diff --git a/php_v8js_macros.h b/php_v8js_macros.h index 4f689ce..cdca2e1 100644 --- a/php_v8js_macros.h +++ b/php_v8js_macros.h @@ -35,15 +35,15 @@ extern "C" { #define V8JS_VERSION "0.1.3" /* Helper macros */ -#define V8JS_SYM(v) v8::String::NewSymbol(v, sizeof(v) - 1) -#define V8JS_SYML(v, l) v8::String::NewSymbol(v, l) -#define V8JS_STR(v) v8::String::New(v) -#define V8JS_STRL(v, l) v8::String::New(v, l) -#define V8JS_INT(v) v8::Integer::New(v) -#define V8JS_FLOAT(v) v8::Number::New(v) -#define V8JS_BOOL(v) v8::Boolean::New(v) -#define V8JS_NULL v8::Null() -#define V8JS_UNDEFINED v8::Undefined() +#define V8JS_SYM(v) v8::String::NewFromUtf8(isolate, v, v8::String::kInternalizedString, sizeof(v) - 1) +#define V8JS_SYML(v, l) v8::String::NewFromUtf8(isolate, v, v8::String::kInternalizedString, l) +#define V8JS_STR(v) v8::String::NewFromUtf8(isolate, v) +#define V8JS_STRL(v, l) v8::String::NewFromUtf8(isolate, v, v8::String::kNormalString, l) +#define V8JS_INT(v) v8::Integer::New(v, isolate) +#define V8JS_FLOAT(v) v8::Number::New(isolate, v) +#define V8JS_BOOL(v) ((v)?v8::True(isolate):v8::False(isolate)) +#define V8JS_NULL v8::Null(isolate) +#define V8JS_UNDEFINED v8::Undefined(isolate) #define V8JS_MN(name) v8js_method_##name #define V8JS_METHOD(name) void V8JS_MN(name)(const v8::FunctionCallbackInfo& info) #define V8JS_THROW(type, message, message_len) v8::ThrowException(v8::Exception::type(V8JS_STRL(message, message_len))) @@ -81,7 +81,7 @@ extern "C" { #define V8JS_DEBUG_AUTO_BREAK_ALWAYS 2 /* Extracts a C string from a V8 Utf8Value. */ -static const char * ToCString(const v8::String::Utf8Value &value) /* {{{ */ +static inline const char * ToCString(const v8::String::Utf8Value &value) /* {{{ */ { return *value ? *value : ""; } diff --git a/tests/return_value.phpt b/tests/return_value.phpt index 910cf88..5ee036a 100644 --- a/tests/return_value.phpt +++ b/tests/return_value.phpt @@ -43,12 +43,13 @@ var_dump($a->executeString("test(false);", "test9.js")); ===EOF=== --EXPECT-- NULL -object(V8Object)#3 (2) { - ["mytest"]=> - object(V8Function)#4 (0) { - } +object(Testing)#2 (3) { ["foo"]=> string(8) "ORIGINAL" + ["my_private":"Testing":private]=> + string(3) "arf" + ["my_protected":protected]=> + string(4) "argh" } array(3) { [0]=> diff --git a/tests/var_dump.phpt b/tests/var_dump.phpt new file mode 100644 index 0000000..0aacb43 --- /dev/null +++ b/tests/var_dump.phpt @@ -0,0 +1,315 @@ +--TEST-- +Test V8::executeString() : var_dump +--SKIPIF-- + +--INI-- +date.timezone=UTC +--FILE-- +obj = new Foo; + +$phptypes = $v8->phptypes = array( + "null" => NULL, + "bool" => true, + "string" => "string", + "uint" => 1, + "int" => -1, + "number" => 3.141592654, + "date" => new DateTime('September 27, 1976 09:00:00 UTC', new DateTimeZone('UTC')), + //"regexp" => new Regexp('/regexp/'), /* no native PHP regex type */ + "array" => array(1,2,3), + "object" => array( "field" => "foo" ), + "function" => (function ($x) { return $x; }), + "phpobject" => new Foo +); + +echo "---- PHP var_dump of PHP object ----\n"; +var_dump($phptypes); + +try { + var_dump($v8->executeString($JS, 'var_dump.js')); +} catch (V8JsScriptException $e) { + echo "Error!\n"; + var_dump($e); +} +?> +===EOF=== +--EXPECTF-- +---- PHP var_dump of PHP object ---- +array(11) { + ["null"]=> + NULL + ["bool"]=> + bool(true) + ["string"]=> + string(6) "string" + ["uint"]=> + int(1) + ["int"]=> + int(-1) + ["number"]=> + float(3.141592654) + ["date"]=> + object(DateTime)#%d (3) { + ["date"]=> + string(19) "1976-09-27 09:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["array"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["object"]=> + array(1) { + ["field"]=> + string(3) "foo" + } + ["function"]=> + object(Closure)#%d (1) { + ["parameter"]=> + array(1) { + ["$x"]=> + string(10) "" + } + } + ["phpobject"]=> + object(Foo)#%d (1) { + ["field"]=> + string(3) "php" + } +} +--- JS var_dump of PHP object ---- +array (11) { + ["null"] => + NULL + ["bool"] => + bool(true) + ["string"] => + string(6) "string" + ["uint"] => + int(1) + ["int"] => + int(-1) + ["number"] => + float(3.141593) + ["date"] => + object(DateTime)#%d (18) { + ["createFromFormat"] => + object(Closure)#%d { + function () { [native code] } + } + ["getLastErrors"] => + object(Closure)#%d { + function () { [native code] } + } + ["format"] => + object(Closure)#%d { + function () { [native code] } + } + ["modify"] => + object(Closure)#%d { + function () { [native code] } + } + ["add"] => + object(Closure)#%d { + function () { [native code] } + } + ["sub"] => + object(Closure)#%d { + function () { [native code] } + } + ["getTimezone"] => + object(Closure)#%d { + function () { [native code] } + } + ["setTimezone"] => + object(Closure)#%d { + function () { [native code] } + } + ["getOffset"] => + object(Closure)#%d { + function () { [native code] } + } + ["setTime"] => + object(Closure)#%d { + function () { [native code] } + } + ["setDate"] => + object(Closure)#%d { + function () { [native code] } + } + ["setISODate"] => + object(Closure)#%d { + function () { [native code] } + } + ["setTimestamp"] => + object(Closure)#%d { + function () { [native code] } + } + ["getTimestamp"] => + object(Closure)#%d { + function () { [native code] } + } + ["diff"] => + object(Closure)#%d { + function () { [native code] } + } + ["date"] => + string(19) "1976-09-27 09:00:00" + ["timezone_type"] => + int(3) + ["timezone"] => + string(3) "UTC" + } + ["array"] => + array(3) { + [0] => + int(1) + [1] => + int(2) + [2] => + int(3) + } + ["object"] => + array (1) { + ["field"] => + string(3) "foo" + } + ["function"] => + object(Closure)#%d (0) { + } + ["phpobject"] => + object(Foo)#%d (1) { + ["field"] => + string(3) "php" + } +} +--- JS var_dump of JS object ---- +object(Object)#%d (12) { + ["undefined"] => + NULL + ["null"] => + NULL + ["bool"] => + bool(true) + ["string"] => + string(6) "string" + ["uint"] => + int(1) + ["int"] => + int(-1) + ["number"] => + float(3.141593) + ["regexp"] => + regexp(/regexp/) + ["array"] => + array(3) { + [0] => + int(1) + [1] => + int(2) + [2] => + int(3) + } + ["object"] => + object(Object)#%d (1) { + ["field"] => + string(3) "foo" + } + ["function"] => + object(Closure)#%d { + function id(x) { return x; } + } + ["phpobject"] => + object(Foo)#%d (1) { + ["field"] => + string(3) "php" + } +} +--- PHP var_dump of JS object ---- +object(V8Object)#%d (12) { + ["undefined"]=> + NULL + ["null"]=> + NULL + ["bool"]=> + bool(true) + ["string"]=> + string(6) "string" + ["uint"]=> + int(1) + ["int"]=> + int(-1) + ["number"]=> + float(3.141592654) + ["regexp"]=> + object(V8Object)#%d (0) { + } + ["array"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["object"]=> + object(V8Object)#%d (1) { + ["field"]=> + string(3) "foo" + } + ["function"]=> + object(V8Function)#%d (0) { + } + ["phpobject"]=> + object(Foo)#%d (1) { + ["field"]=> + string(3) "php" + } +} +===EOF=== diff --git a/v8js.cc b/v8js.cc index d5de3e3..c7ba662 100644 --- a/v8js.cc +++ b/v8js.cc @@ -128,13 +128,14 @@ static int php_v8js_v8_has_property(zval *object, zval *member, int has_set_exis int retval = false; php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC); - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction()) { @@ -186,13 +187,14 @@ static zval *php_v8js_v8_read_property(zval *object, zval *member, int type ZEND zval *retval = NULL; php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC); - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction()) { @@ -212,7 +214,7 @@ static zval *php_v8js_v8_read_property(zval *object, zval *member, int type ZEND MAKE_STD_ZVAL(retval); } - if (v8js_to_zval(jsVal, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) { + if (v8js_to_zval(jsVal, retval, obj->flags, isolate TSRMLS_CC) == SUCCESS) { return retval; } } @@ -228,16 +230,17 @@ static void php_v8js_v8_write_property(zval *object, zval *member, zval *value Z { php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC); - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (v8obj->IsObject() && !v8obj->IsFunction()) { - v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, obj->isolate TSRMLS_CC)); + v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, isolate TSRMLS_CC)); } } /* }}} */ @@ -246,13 +249,14 @@ static void php_v8js_v8_unset_property(zval *object, zval *member ZEND_HASH_KEY_ { php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC); - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (v8obj->IsObject() && !v8obj->IsFunction()) { v8obj->ToObject()->ForceDelete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); @@ -317,14 +321,15 @@ static HashTable *php_v8js_v8_get_properties(zval *object TSRMLS_DC) /* {{{ */ ALLOC_HASHTABLE(retval); zend_hash_init(retval, 0, NULL, ZVAL_PTR_DTOR, 0); - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); - if (php_v8js_v8_get_properties_hash(v8obj, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) { + if (php_v8js_v8_get_properties_hash(v8obj, retval, obj->flags, isolate TSRMLS_CC) == SUCCESS) { return retval; } @@ -344,13 +349,14 @@ static zend_function *php_v8js_v8_get_method(zval **object_ptr, char *method, in php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(*object_ptr TSRMLS_CC); zend_function *f; - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); v8::Local jsKey = V8JS_STRL(method, method_len); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (!obj->v8obj.IsEmpty() && v8obj->IsObject() && !v8obj->IsFunction()) { v8::Local jsObj = v8obj->ToObject(); @@ -389,14 +395,15 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) / zend_get_parameters_array_ex(argc, argv); } - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); v8::Local method_name = V8JS_SYML(method, strlen(method)); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj)->ToObject(); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj)->ToObject(); v8::Local cb; if (method_name->Equals(V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME))) { @@ -405,11 +412,11 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) / cb = v8::Local::Cast(v8obj->Get(method_name)); } - v8::Local *jsArgv = new v8::Local[argc]; + v8::Local jsArgv[argc]; v8::Local js_retval; for (i = 0; i < argc; i++) { - jsArgv[i] = v8::Local::New(obj->isolate, zval_to_v8js(*argv[i], obj->isolate TSRMLS_CC)); + jsArgv[i] = v8::Local::New(isolate, zval_to_v8js(*argv[i], isolate TSRMLS_CC)); } js_retval = cb->Call(V8JS_GLOBAL, argc, jsArgv); @@ -421,7 +428,7 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) / } if (return_value_used) { - return v8js_to_zval(js_retval, return_value, obj->flags, obj->isolate TSRMLS_CC); + return v8js_to_zval(js_retval, return_value, obj->flags, isolate TSRMLS_CC); } return SUCCESS; @@ -434,12 +441,13 @@ static int php_v8js_v8_get_closure(zval *object, zend_class_entry **ce_ptr, zend php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC); - v8::Locker locker(obj->isolate); - v8::Isolate::Scope isolate_scope(obj->isolate); - v8::HandleScope local_scope(obj->isolate); - v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Isolate *isolate = obj->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope local_scope(isolate); + v8::Local temp_context = v8::Context::New(isolate); v8::Context::Scope temp_scope(temp_context); - v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (!v8obj->IsFunction()) { return FAILURE; @@ -467,9 +475,7 @@ static void php_v8js_v8_free_storage(void *object, zend_object_handle handle TSR zend_object_std_dtor(&c->std TSRMLS_CC); - if (!c->v8obj.IsEmpty()) { - c->v8obj.Dispose(); - } + c->v8obj.Reset(); efree(object); } @@ -519,19 +525,24 @@ static void php_v8js_free_storage(void *object TSRMLS_DC) /* {{{ */ zval_ptr_dtor(&c->pending_exception); } - c->object_name.Dispose(); + c->object_name.Reset(); + c->object_name.~Persistent(); + c->global_template.Reset(); + c->global_template.~Persistent(); /* Clear global object, dispose context */ if (!c->context.IsEmpty()) { - c->context.Dispose(); - c->context.Clear(); + c->context.Reset(); V8JSG(disposed_contexts) = v8::V8::ContextDisposedNotification(); #if V8JS_DEBUG fprintf(stderr, "Context dispose notification sent (%d)\n", V8JSG(disposed_contexts)); fflush(stderr); #endif } + c->context.~Persistent(); + c->modules_stack.~vector(); + c->modules_base.~vector(); efree(object); } /* }}} */ @@ -543,6 +554,7 @@ static zend_object_value php_v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */ c = (php_v8js_ctx *) ecalloc(1, sizeof(*c)); zend_object_std_init(&c->std, ce TSRMLS_CC); + #if PHP_VERSION_ID >= 50400 object_properties_init(&c->std, ce); #else @@ -551,6 +563,13 @@ static zend_object_value php_v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */ (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); #endif + new(&c->object_name) v8::Persistent(); + new(&c->context) v8::Persistent(); + new(&c->global_template) v8::Persistent(); + + new(&c->modules_stack) std::vector(); + new(&c->modules_base) std::vector(); + retval.handle = zend_objects_store_put(c, NULL, (zend_objects_free_object_storage_t) php_v8js_free_storage, NULL TSRMLS_CC); retval.handlers = &v8js_object_handlers; @@ -711,11 +730,12 @@ static PHP_METHOD(V8Js, __construct) v8::ExtensionConfiguration extension_conf(exts_count, exts); // Isolate execution - v8::Locker locker(c->isolate); - v8::Isolate::Scope isolate_scope(c->isolate); + v8::Isolate *isolate = c->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); /* Handle scope */ - v8::HandleScope handle_scope(c->isolate); + v8::HandleScope handle_scope(isolate); /* Redirect fatal errors to PHP error handler */ // This needs to be done within the context isolate @@ -726,15 +746,15 @@ static PHP_METHOD(V8Js, __construct) v8::Local tpl = v8::FunctionTemplate::New(); tpl->SetClassName(V8JS_SYM("V8Js")); - c->global_template.Reset(c->isolate, tpl); + c->global_template.Reset(isolate, tpl); /* Register builtin methods */ php_v8js_register_methods(tpl->InstanceTemplate(), c); /* Create context */ - v8::Local context = v8::Context::New(c->isolate, &extension_conf, tpl->InstanceTemplate()); + v8::Local context = v8::Context::New(isolate, &extension_conf, tpl->InstanceTemplate()); context->SetAlignedPointerInEmbedderData(1, c); - c->context.Reset(c->isolate, context); + c->context.Reset(isolate, context); if (exts) { _php_v8js_free_ext_strarr(exts, exts_count); @@ -767,12 +787,12 @@ static PHP_METHOD(V8Js, __construct) /* Register Get accessor for passed variables */ if (vars_arr && zend_hash_num_elements(Z_ARRVAL_P(vars_arr)) > 0) { - php_v8js_register_accessors(php_obj_t->InstanceTemplate(), vars_arr, c->isolate TSRMLS_CC); + php_v8js_register_accessors(php_obj_t->InstanceTemplate(), vars_arr, isolate TSRMLS_CC); } /* Set name for the PHP JS object */ v8::Local object_name_js = (object_name_len) ? V8JS_SYML(object_name, object_name_len) : V8JS_SYM("PHP"); - c->object_name.Reset(c->isolate, object_name_js); + c->object_name.Reset(isolate, object_name_js); /* Add the PHP object into global object */ v8::Local php_obj = php_obj_t->InstanceTemplate()->NewInstance(); @@ -799,7 +819,7 @@ static PHP_METHOD(V8Js, __construct) zend_property_info *property_info = zend_get_property_info(c->std.ce, &zmember, 1 TSRMLS_CC); if(property_info && property_info->flags & ZEND_ACC_PUBLIC) { /* Write value to PHP JS object */ - php_obj->ForceSet(V8JS_SYML(member, member_len - 1), zval_to_v8js(*value, c->isolate TSRMLS_CC), v8::ReadOnly); + php_obj->ForceSet(V8JS_SYML(member, member_len - 1), zval_to_v8js(*value, isolate TSRMLS_CC), v8::ReadOnly); } } @@ -816,10 +836,11 @@ static PHP_METHOD(V8Js, __construct) } \ \ (ctx) = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); \ - v8::Locker locker((ctx)->isolate); \ - v8::Isolate::Scope isolate_scope((ctx)->isolate); \ - v8::HandleScope handle_scope((ctx)->isolate); \ - v8::Context::Scope context_scope((ctx)->isolate, (ctx)->context); + v8::Isolate *isolate = (ctx)->isolate; \ + v8::Locker locker(isolate); \ + v8::Isolate::Scope isolate_scope(isolate); \ + v8::HandleScope handle_scope(isolate); \ + v8::Context::Scope context_scope(isolate, (ctx)->context); static void php_v8js_timer_push(long time_limit, long memory_limit, php_v8js_ctx *c TSRMLS_DC) { @@ -930,7 +951,7 @@ static PHP_METHOD(V8Js, executeString) v8::Local sname = identifier_len ? V8JS_SYML(identifier, identifier_len) : V8JS_SYM("V8Js::executeString()"); /* Compiles a string context independently. TODO: Add a php function which calls this and returns the result as resource which can be executed later. */ - v8::Local source = v8::String::New(str, str_len); + v8::Local source = V8JS_STRL(str, str_len); v8::Local script = v8::Script::New(source, sname); /* Compile errors? */ @@ -1015,6 +1036,7 @@ static PHP_METHOD(V8Js, executeString) if (result.IsEmpty()) { MAKE_STD_ZVAL(c->pending_exception); php_v8js_create_script_exception(c->pending_exception, &try_catch TSRMLS_CC); + return; } } @@ -1341,11 +1363,11 @@ static void php_v8js_write_property(zval *object, zval *member, zval *value ZEND zend_property_info *property_info = zend_get_property_info(c->std.ce, member, 1 TSRMLS_CC); if(property_info->flags & ZEND_ACC_PUBLIC) { /* Global PHP JS object */ - v8::Local object_name_js = v8::Local::New(c->isolate, c->object_name); + v8::Local object_name_js = v8::Local::New(isolate, c->object_name); v8::Local jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject(); /* Write value to PHP JS object */ - jsobj->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, c->isolate TSRMLS_CC), v8::ReadOnly); + jsobj->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, isolate TSRMLS_CC), v8::ReadOnly); } /* Write value to PHP object */ @@ -1358,7 +1380,7 @@ static void php_v8js_unset_property(zval *object, zval *member ZEND_HASH_KEY_DC V8JS_BEGIN_CTX(c, object) /* Global PHP JS object */ - v8::Local object_name_js = v8::Local::New(c->isolate, c->object_name); + v8::Local object_name_js = v8::Local::New(isolate, c->object_name); v8::Local jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject(); /* Delete value from PHP JS object */ diff --git a/v8js_convert.cc b/v8js_convert.cc index 4efa331..b9fa190 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -28,6 +28,8 @@ extern "C" { #include #include +#define PHPJS_OBJECT_KEY "phpjs::object" + #if PHP_V8_API_VERSION < 3022000 /* CopyablePersistentTraits is only part of V8 from 3.22.0 on, to be compatible with lower versions add our own (compatible) version. */ @@ -194,7 +196,7 @@ static void php_v8js_php_callback(const v8::FunctionCallbackInfo& inf /* Callback for PHP constructor calls */ static void php_v8js_construct_callback(const v8::FunctionCallbackInfo& info) /* {{{ */ { - v8::Isolate *isolate = v8::Isolate::GetCurrent(); + v8::Isolate *isolate = info.GetIsolate(); info.GetReturnValue().Set(V8JS_UNDEFINED); // @todo assert constructor call @@ -216,7 +218,7 @@ static void php_v8js_construct_callback(const v8::FunctionCallbackInfocommon.fn_flags & ZEND_ACC_PUBLIC) == 0) { - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Call to protected __construct() not allowed"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Call to protected __construct() not allowed"))); return; } @@ -231,6 +233,7 @@ static void php_v8js_construct_callback(const v8::FunctionCallbackInfoSetAlignedPointerInInternalField(0, value); newobj->SetAlignedPointerInInternalField(1, (void *) isolate); + newobj->SetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY), V8JS_BOOL(true)); } /* }}} */ @@ -259,6 +262,7 @@ static int _php_v8js_is_assoc_array(HashTable *myht TSRMLS_DC) /* {{{ */ static void php_v8js_property_caller(const v8::FunctionCallbackInfo& info) /* {{{ */ { v8::Local self = info.Holder(); + v8::Isolate *isolate = reinterpret_cast(self->GetAlignedPointerFromInternalField(1)); v8::Local cname = info.Callee()->GetName()->ToString(); v8::Local value; v8::Local cb_func = v8::Local::Cast(info.Data()); @@ -268,7 +272,7 @@ static void php_v8js_property_caller(const v8::FunctionCallbackInfo& if (!value.IsEmpty() && value->IsFunction()) { int argc = info.Length(), i = 0; - v8::Local *argv = new v8::Local[argc]; + v8::Local argv[argc]; v8::Local cb = v8::Local::Cast(value); if (cb_func->Equals(V8JS_SYM(ZEND_INVOKE_FUNC_NAME))) { @@ -316,6 +320,8 @@ static void php_v8js_property_getter(v8::Local property, const v8::P return; } + v8::Isolate *isolate = reinterpret_cast(self->GetAlignedPointerFromInternalField(1)); + /* If __get() is set for PHP object, call it */ value = self->GetHiddenValue(V8JS_SYM(ZEND_GET_FUNC_NAME)); if (!value.IsEmpty() && value->IsFunction()) { @@ -340,6 +346,7 @@ static void php_v8js_property_getter(v8::Local property, const v8::P static void php_v8js_property_query(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ { v8::Local self = info.Holder(); + v8::Isolate *isolate = reinterpret_cast(self->GetAlignedPointerFromInternalField(1)); v8::Local value; /* Return early if property is set in JS object */ @@ -734,6 +741,13 @@ int v8js_to_zval(v8::Handle jsValue, zval *return_value, int flags, v } else if (jsValue->IsObject()) { + v8::Handle self = v8::Handle::Cast(jsValue); + // if this is a wrapped PHP object, then just unwrap it. + if (!self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY)).IsEmpty()) { + zval *object = reinterpret_cast(self->GetAlignedPointerFromInternalField(0)); + RETVAL_ZVAL(object, 1, 0); + return SUCCESS; + } if ((flags & V8JS_FLAG_FORCE_ARRAY) || jsValue->IsArray()) { array_init(return_value); return php_v8js_v8_get_properties_hash(jsValue, Z_ARRVAL_P(return_value), flags, isolate TSRMLS_CC); diff --git a/v8js_methods.cc b/v8js_methods.cc index 761a40f..dc763d7 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -43,6 +43,7 @@ V8JS_METHOD(sleep) /* {{{ */ /* global.print - php print() */ V8JS_METHOD(print) /* {{{ */ { + v8::Isolate *isolate = info.GetIsolate(); int ret = 0; TSRMLS_FETCH(); @@ -55,40 +56,65 @@ V8JS_METHOD(print) /* {{{ */ } /* }}} */ -static void _php_v8js_dumper(v8::Local var, int level TSRMLS_DC) /* {{{ */ +static void _php_v8js_dumper(v8::Isolate *isolate, v8::Local var, int level TSRMLS_DC) /* {{{ */ { - v8::String::Utf8Value str(var->ToDetailString()); - const char *valstr = ToCString(str); - size_t valstr_len = (valstr) ? strlen(valstr) : 0; - if (level > 1) { php_printf("%*c", (level - 1) * 2, ' '); } + if (var.IsEmpty()) + { + php_printf("\n"); + return; + } + if (var->IsNull() || var->IsUndefined() /* PHP compat */) + { + php_printf("NULL\n"); + return; + } + if (var->IsInt32()) + { + php_printf("int(%ld)\n", (long) var->IntegerValue()); + return; + } + if (var->IsUint32()) + { + php_printf("int(%lu)\n", (unsigned long) var->IntegerValue()); + return; + } + if (var->IsNumber()) + { + php_printf("float(%f)\n", var->NumberValue()); + return; + } + if (var->IsBoolean()) + { + php_printf("bool(%s)\n", var->BooleanValue() ? "true" : "false"); + return; + } + + v8::TryCatch try_catch; /* object.toString() can throw an exception */ + v8::Local details = var->ToDetailString(); + if (try_catch.HasCaught()) { + details = V8JS_SYM(""); + } + v8::String::Utf8Value str(details); + const char *valstr = ToCString(str); + size_t valstr_len = (valstr) ? strlen(valstr) : 0; + if (var->IsString()) { php_printf("string(%zu) \"%s\"\n", valstr_len, valstr); } - else if (var->IsBoolean()) - { - php_printf("bool(%s)\n", valstr); - } - else if (var->IsInt32() || var->IsUint32()) - { - php_printf("int(%s)\n", valstr); - } - else if (var->IsNumber()) - { - php_printf("float(%s)\n", valstr); - } else if (var->IsDate()) { + // fake the fields of a PHP DateTime php_printf("Date(%s)\n", valstr); } #if PHP_V8_API_VERSION >= 2003007 else if (var->IsRegExp()) { - php_printf("RegExp(%s)\n", valstr); + php_printf("regexp(%s)\n", valstr); } #endif else if (var->IsArray()) @@ -100,7 +126,7 @@ static void _php_v8js_dumper(v8::Local var, int level TSRMLS_DC) /* { for (unsigned i = 0; i < length; i++) { php_printf("%*c[%d] =>\n", level * 2, ' ', i); - _php_v8js_dumper(array->Get(i), level + 1 TSRMLS_CC); + _php_v8js_dumper(isolate, array->Get(i), level + 1 TSRMLS_CC); } if (level > 1) { @@ -113,24 +139,31 @@ static void _php_v8js_dumper(v8::Local var, int level TSRMLS_DC) /* { { v8::Local object = v8::Local::Cast(var); V8JS_GET_CLASS_NAME(cname, object); + int hash = object->GetIdentityHash(); if (var->IsFunction()) { v8::String::Utf8Value csource(object->ToString()); - php_printf("object(%s)#%d {\n%*c%s\n", ToCString(cname), object->GetIdentityHash(), level * 2 + 2, ' ', ToCString(csource)); + php_printf("object(Closure)#%d {\n%*c%s\n", hash, level * 2 + 2, ' ', ToCString(csource)); } else { - v8::Local keys = object->GetPropertyNames(); + v8::Local keys = object->GetOwnPropertyNames(); uint32_t length = keys->Length(); - php_printf("object(%s)#%d (%d) {\n", ToCString(cname), object->GetIdentityHash(), length); + if (strcmp(ToCString(cname), "Array") == 0 || + strcmp(ToCString(cname), "V8Object") == 0) { + php_printf("array"); + } else { + php_printf("object(%s)#%d", ToCString(cname), hash); + } + php_printf(" (%d) {\n", length); for (unsigned i = 0; i < length; i++) { v8::Local key = keys->Get(i)->ToString(); v8::String::Utf8Value kname(key); php_printf("%*c[\"%s\"] =>\n", level * 2, ' ', ToCString(kname)); - _php_v8js_dumper(object->Get(key), level + 1 TSRMLS_CC); + _php_v8js_dumper(isolate, object->Get(key), level + 1 TSRMLS_CC); } } @@ -150,11 +183,11 @@ static void _php_v8js_dumper(v8::Local var, int level TSRMLS_DC) /* { /* global.var_dump - Dump JS values */ V8JS_METHOD(var_dump) /* {{{ */ { - int i; + v8::Isolate *isolate = info.GetIsolate(); TSRMLS_FETCH(); for (int i = 0; i < info.Length(); i++) { - _php_v8js_dumper(info[i], 1 TSRMLS_CC); + _php_v8js_dumper(isolate, info[i], 1 TSRMLS_CC); } info.GetReturnValue().Set(V8JS_NULL); @@ -163,6 +196,7 @@ V8JS_METHOD(var_dump) /* {{{ */ V8JS_METHOD(require) { + v8::Isolate *isolate = info.GetIsolate(); TSRMLS_FETCH(); // Get the extension context @@ -171,7 +205,7 @@ V8JS_METHOD(require) // Check that we have a module loader if (c->module_loader == NULL) { - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("No module loader"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("No module loader"))); return; } @@ -185,7 +219,7 @@ V8JS_METHOD(require) php_v8js_commonjs_normalise_identifier(c->modules_base.back(), module_id, normalised_path, module_name); efree(module_id); - char *normalised_module_id = (char *)emalloc(strlen(module_id)); + char *normalised_module_id = (char *)emalloc(strlen(normalised_path)+1+strlen(module_name)+1); *normalised_module_id = 0; if (strlen(normalised_path) > 0) { @@ -202,7 +236,7 @@ V8JS_METHOD(require) if (!strcmp(*it, normalised_module_id)) { efree(normalised_path); - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module cyclic dependency"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module cyclic dependency"))); return; } } @@ -227,7 +261,7 @@ V8JS_METHOD(require) if (FAILURE == call_user_function(EG(function_table), NULL, c->module_loader, &module_code, 1, params TSRMLS_CC)) { efree(normalised_path); - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module loader callback failed"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module loader callback failed"))); return; } @@ -237,7 +271,7 @@ V8JS_METHOD(require) // Clear the PHP exception and throw it in V8 instead zend_clear_exception(TSRMLS_C); - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module loader callback exception"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module loader callback exception"))); return; } @@ -250,44 +284,44 @@ V8JS_METHOD(require) if (!strlen(Z_STRVAL(module_code))) { efree(normalised_path); - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module loader callback did not return code"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module loader callback did not return code"))); return; } // Create a template for the global object and set the built-in global functions v8::Handle global = v8::ObjectTemplate::New(); - global->Set(v8::String::New("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly); + global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly); global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly); - global->Set(v8::String::New("require"), v8::FunctionTemplate::New(V8JS_MN(require), v8::External::New(c)), v8::ReadOnly); + global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(V8JS_MN(require), v8::External::New(c)), v8::ReadOnly); // Add the exports object in which the module can return its API v8::Handle exports_template = v8::ObjectTemplate::New(); v8::Handle exports = exports_template->NewInstance(); - global->Set(v8::String::New("exports"), exports); + global->Set(V8JS_SYM("exports"), exports); // Add the module object in which the module can have more fine-grained control over what it can return v8::Handle module_template = v8::ObjectTemplate::New(); v8::Handle module = module_template->NewInstance(); - module->Set(v8::String::New("id"), v8::String::New(normalised_module_id)); - global->Set(v8::String::New("module"), module); + module->Set(V8JS_SYM("id"), V8JS_STR(normalised_module_id)); + global->Set(V8JS_SYM("module"), module); // Each module gets its own context so different modules do not affect each other - v8::Local context = v8::Local::New(c->isolate, v8::Context::New(c->isolate, NULL, global)); + v8::Local context = v8::Local::New(isolate, v8::Context::New(isolate, NULL, global)); // Catch JS exceptions v8::TryCatch try_catch; - v8::Locker locker(c->isolate); - v8::Isolate::Scope isolate_scope(c->isolate); + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope(c->isolate); + v8::HandleScope handle_scope(isolate); // Enter the module context v8::Context::Scope scope(context); // Set script identifier v8::Local sname = V8JS_SYM("require"); - v8::Local source = v8::String::New(Z_STRVAL(module_code)); + v8::Local source = V8JS_STR(Z_STRVAL(module_code)); // Create and compile script v8::Local script = v8::Script::New(source, sname); @@ -295,7 +329,7 @@ V8JS_METHOD(require) // The script will be empty if there are compile errors if (script.IsEmpty()) { efree(normalised_path); - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module script compile failed"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module script compile failed"))); return; } @@ -315,7 +349,7 @@ V8JS_METHOD(require) // Script possibly terminated, return immediately if (!try_catch.CanContinue()) { - info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module script compile failed"))); + info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module script compile failed"))); return; } @@ -328,9 +362,9 @@ V8JS_METHOD(require) // Cache the module so it doesn't need to be compiled and run again // Ensure compatibility with CommonJS implementations such as NodeJS by playing nicely with module.exports and exports - if (module->Has(v8::String::New("exports")) && !module->Get(v8::String::New("exports"))->IsUndefined()) { + if (module->Has(V8JS_SYM("exports")) && !module->Get(V8JS_SYM("exports"))->IsUndefined()) { // If module.exports has been set then we cache this arbitrary value... - V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(module->Get(v8::String::New("exports"))->ToObject()); + V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(module->Get(V8JS_SYM("exports"))->ToObject()); } else { // ...otherwise we cache the exports object itself V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(exports); @@ -341,6 +375,7 @@ V8JS_METHOD(require) void php_v8js_register_methods(v8::Handle global, php_v8js_ctx *c) /* {{{ */ { + v8::Isolate *isolate = c->isolate; global->Set(V8JS_SYM("exit"), v8::FunctionTemplate::New(V8JS_MN(exit)), v8::ReadOnly); global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly); global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);