From 0d5c99e2a36f236704a4b5eafcfa3614b0f2fbcc Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Tue, 12 May 2015 13:44:16 +0200 Subject: [PATCH] Also allow returning the original ecmascript object from PHP --- jsobject.cpp | 11 +++++++++++ jsobject.h | 7 +++++++ object.cpp | 44 ++++++++++++++++++++++++++++---------------- object.h | 6 ++++++ value.cpp | 51 ++++++++++++++++++++++++++++++++++++++------------- 5 files changed, 90 insertions(+), 29 deletions(-) diff --git a/jsobject.cpp b/jsobject.cpp index 102662d..508f13c 100644 --- a/jsobject.cpp +++ b/jsobject.cpp @@ -172,6 +172,17 @@ Php::Iterator *JSObject::getIterator() return new Iterator(this, _object); } +/** + * Retrieve the original ecmascript value + * + * @return original ecmascript value + */ +v8::Local JSObject::object() const +{ + // the stack has the original object + return _object; +} + /** * End namespace */ diff --git a/jsobject.h b/jsobject.h index 79ca233..f7ffab6 100644 --- a/jsobject.h +++ b/jsobject.h @@ -145,6 +145,13 @@ class JSObject : * @return The iterator */ Php::Iterator *getIterator() override; + + /** + * Retrieve the original ecmascript value + * + * @return original ecmascript value + */ + v8::Local object() const; }; /** diff --git a/object.cpp b/object.cpp index 7419bea..4c7b55d 100644 --- a/object.cpp +++ b/object.cpp @@ -59,11 +59,9 @@ static uint32_t count(const Php::Object &object) */ static void callback(const v8::FunctionCallbackInfo &info) { - // create a local handle, so properties "fall out of scope" + // create a local handle, so properties "fall out of scope" and retrieve a handle to the original object v8::HandleScope scope(isolate()); - - // retrieve handle to the original object - Handle handle(info.Data()); + Handle handle(info.Holder()->GetInternalField(0)); // an array to hold all the arguments Php::Array arguments; @@ -96,7 +94,7 @@ static void indexed_enumerator(const v8::PropertyCallbackInfo &info) { // create a local handle, so properties "fall out of scope" and retrieve the original object v8::HandleScope scope(isolate()); - Handle handle(info.Data()); + Handle handle(info.Holder()->GetInternalField(0)); // create a new array to store all the properties v8::Local properties(v8::Array::New(isolate())); @@ -128,7 +126,7 @@ static void named_enumerator(const v8::PropertyCallbackInfo &info) { // create a local handle, so properties "fall out of scope" and retrieve the original object v8::HandleScope scope(isolate()); - Handle handle(info.Data()); + Handle handle(info.Holder()->GetInternalField(0)); // create a new array to store all the properties v8::Local properties(v8::Array::New(isolate())); @@ -161,7 +159,7 @@ static void getter(uint32_t index, const v8::PropertyCallbackInfo &in { // create a local handle, so properties "fall out of scope" and retrieve the original object v8::HandleScope scope(isolate()); - Handle handle(info.Data()); + Handle handle(info.Holder()->GetInternalField(0)); // check if we have an item at the requested offset if (handle->call("offsetExists", static_cast(index))) @@ -188,7 +186,7 @@ static void getter(v8::Local property, const v8::PropertyCallbackInf v8::HandleScope scope(isolate()); // retrieve handle to the original object and the property name - Handle handle(info.Data()); + Handle handle(info.Holder()->GetInternalField(0)); v8::String::Utf8Value name(property); /** @@ -261,8 +259,9 @@ static void getter(v8::Local property, const v8::PropertyCallbackInf */ static void setter(uint32_t index, v8::Local input, const v8::PropertyCallbackInfo& info) { - // retrieve handle to the original object - Handle handle(info.Data()); + // create a local handle, so properties "fall out of scope" and retrieve the original object + v8::HandleScope scope(isolate()); + Handle handle(info.Holder()->GetInternalField(0)); // store the property inside the object handle->call("offsetSet", static_cast(index), value(input)); @@ -277,8 +276,9 @@ static void setter(uint32_t index, v8::Local input, const v8::Propert */ static void setter(v8::Local property, v8::Local input, const v8::PropertyCallbackInfo& info) { - // retrieve handle to the original object and convert the requested property - Handle handle(info.Data()); + // create a local handle, so properties "fall out of scope" and retrieve the original object + v8::HandleScope scope(isolate()); + Handle handle(info.Holder()->GetInternalField(0)); v8::String::Utf8Value name(property); // if the object is not implementing ArrayAccess or has the given property as a member @@ -301,16 +301,22 @@ static void setter(v8::Local property, v8::Local input, c * @param object The object to wrap */ Object::Object(Php::Object object) : - _template(v8::ObjectTemplate::New()) + _template(v8::ObjectTemplate::New()), + _object(object) { + // TODO: check whether it saves memory and time to re-use object templates + + // reserve space to store the handle to the object as an external reference + _template->SetInternalFieldCount(1); + // if the object can be invoked as a function, we register the callback if (object.isCallable()) _template->SetCallAsFunctionHandler(callback, Handle(object)); // register the property handlers - _template->SetNamedPropertyHandler(getter, setter, nullptr, nullptr, named_enumerator, Handle(object)); + _template->SetNamedPropertyHandler(getter, setter, nullptr, nullptr, named_enumerator); // if the object implements the ArrayAccess interface, we should also set the indexed property handler - if (object.instanceOf("ArrayAccess")) _template->SetIndexedPropertyHandler(getter, setter, nullptr, nullptr, indexed_enumerator, Handle(object)); + if (object.instanceOf("ArrayAccess")) _template->SetIndexedPropertyHandler(getter, setter, nullptr, nullptr, indexed_enumerator); } /** @@ -322,7 +328,13 @@ Object::Object(Php::Object object) : Object::operator v8::Local () { // create a new object based on the template - return _template->NewInstance(); + auto instance = _template->NewInstance(); + + // attach the object to the instance + instance->SetInternalField(0, Handle(_object)); + + // return the instance + return instance; } /** diff --git a/object.h b/object.h index 57eef89..d293063 100644 --- a/object.h +++ b/object.h @@ -37,6 +37,12 @@ class Object * @var Stack */ Stack _template; + + /** + * The PHP object we wrap + * @var Php::Object + */ + Php::Object _object; public: /** * Constructor diff --git a/value.cpp b/value.cpp index db08fd4..e906250 100644 --- a/value.cpp +++ b/value.cpp @@ -72,19 +72,28 @@ v8::Handle value(const Php::Value &input) // the result value we are assigning v8::Local result; - // the value can be of many types - switch (input.type()) + // are we dealing with a value originally from ecmascript? + if (input.instanceOf("JS\\Object")) { - case Php::Type::Null: /* don't set anything, let it be empty */ break; - case Php::Type::Numeric: result = v8::Integer::New(isolate(), input); break; - case Php::Type::Float: result = v8::Number::New(isolate(), input); break; - case Php::Type::Bool: result = v8::Boolean::New(isolate(), input); break; - case Php::Type::String: result = v8::String::NewFromUtf8(isolate(), input); break; - case Php::Type::Object: result = Object(input); break; - case Php::Type::Callable: result = v8::FunctionTemplate::New(isolate(), callback, Handle(input))->GetFunction(); break; - case Php::Type::Array: result = Array(input); break; - default: - break; + // cast the input to the original object + result = static_cast(input.implementation())->object(); + } + else + { + // the value can be of many types + switch (input.type()) + { + case Php::Type::Null: /* don't set anything, let it be empty */ break; + case Php::Type::Numeric: result = v8::Integer::New(isolate(), input); break; + case Php::Type::Float: result = v8::Number::New(isolate(), input); break; + case Php::Type::Bool: result = v8::Boolean::New(isolate(), input); break; + case Php::Type::String: result = v8::String::NewFromUtf8(isolate(), input); break; + case Php::Type::Object: result = Object(input); break; + case Php::Type::Callable: result = v8::FunctionTemplate::New(isolate(), callback, Handle(input))->GetFunction(); break; + case Php::Type::Array: result = Array(input); break; + default: + break; + } } // return the value by "escaping" it @@ -177,8 +186,24 @@ Php::Value value(v8::Handle input) // or perhaps an object if (input->IsObject()) { + // retrieve the object and the first internal field + auto object = input.As(); + auto field = object->GetInternalField(0); + + // does it have an internal field and is it external? we are converting back + // an original PHP object, just retrieve the original thing that came from PHP + if (!field.IsEmpty() && field->IsExternal()) + { + // the PHP value is stored in the first internal field, + // retrieve it and create the handle around it + Handle handle(field); + + // dereference and return it + return *handle; + } + // create a new js object and convert it to userspace - return Php::Object("JS\\Object", new JSObject(input.As())); + return Php::Object("JS\\Object", new JSObject(object)); } // we sadly don't support this type of value