From 619231913c1c2199be64a216e3bebc2a0936efda Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Thu, 3 Oct 2013 16:27:04 +0200 Subject: [PATCH 1/3] Adapt to new v8::Persistent API, support V8 >= 3.21.12 --- README.md | 4 +- config.m4 | 5 +-- v8js.cc | 101 +++++++++++++++++++++++++++++++----------------- v8js_convert.cc | 39 +++++++++++++------ v8js_methods.cc | 2 +- 5 files changed, 98 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index c8482ec..1ff458b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Minimum requirements V8 is Google's open source Javascript engine. V8 is written in C++ and is used in Google Chrome, the open source browser from Google. V8 implements ECMAScript as specified in ECMA-262, 5th edition. - This extension makes use of V8 isolates to ensure separation between multiple V8Js instances, hence the need for 3.17.11 or above. (See .) + This extension makes use of V8 isolates to ensure separation between multiple V8Js instances and already uses the new persistence API, hence the need for 3.21.12 or above. - PHP 5.3.3+ @@ -51,7 +51,7 @@ cd /tmp git clone https://github.com/preillyme/v8js.git cd v8js phpize -./configure CXXFLAGS="-DV8_USE_UNSAFE_HANDLES=1" +./configure make sudo make install ``` diff --git a/config.m4 b/config.m4 index 0b42979..e7bb871 100644 --- a/config.m4 +++ b/config.m4 @@ -67,9 +67,8 @@ CPPFLAGS=$old_CPPFLAGS set $ac_cv_v8_version IFS=$ac_IFS V8_API_VERSION=`expr [$]1 \* 1000000 + [$]2 \* 1000 + [$]3` - if test "$V8_API_VERSION" -lt 3017011 ; then - # see https://github.com/preillyme/v8js/issues/12 - AC_MSG_ERROR([libv8 must be version 3.17.11 or greater]) + if test "$V8_API_VERSION" -lt 3021012 ; then + AC_MSG_ERROR([libv8 must be version 3.21.12 or greater]) fi AC_DEFINE_UNQUOTED([PHP_V8_API_VERSION], $V8_API_VERSION, [ ]) AC_DEFINE_UNQUOTED([PHP_V8_VERSION], "$ac_cv_v8_version", [ ]) diff --git a/v8js.cc b/v8js.cc index 1c7d09f..2f87130 100644 --- a/v8js.cc +++ b/v8js.cc @@ -116,15 +116,18 @@ 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); - if (Z_TYPE_P(member) == IS_STRING && obj->v8obj->IsObject() && !obj->v8obj->IsFunction()) - { - 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::Context::Scope temp_scope(temp_context); + 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::Context::Scope temp_scope(temp_context); - v8::Local jsObj = obj->v8obj->ToObject(); + v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + + if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction()) + { + + v8::Local jsObj = v8obj->ToObject(); v8::Local jsKey = V8JS_STRL(Z_STRVAL_P(member), Z_STRLEN_P(member)); v8::Local jsVal; @@ -155,8 +158,16 @@ 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); - if (obj->v8obj->IsObject() && !obj->v8obj->IsFunction()) { - obj->v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, obj->isolate 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::Context::Scope temp_scope(temp_context); + + v8::Local v8obj = v8::Local::New(obj->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)); } } /* }}} */ @@ -165,8 +176,16 @@ 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); - if (obj->v8obj->IsObject() && !obj->v8obj->IsFunction()) { - obj->v8obj->ToObject()->ForceDelete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); + 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::Context::Scope temp_scope(temp_context); + + v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + + if (v8obj->IsObject() && !v8obj->IsFunction()) { + v8obj->ToObject()->ForceDelete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); } } /* }}} */ @@ -224,8 +243,9 @@ static HashTable *php_v8js_v8_get_properties(zval *object TSRMLS_DC) /* {{{ */ v8::HandleScope local_scope(obj->isolate); v8::Local temp_context = v8::Context::New(obj->isolate); v8::Context::Scope temp_scope(temp_context); + v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); - if (php_v8js_v8_get_properties_hash(obj->v8obj, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) { + if (php_v8js_v8_get_properties_hash(v8obj, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) { return retval; } @@ -251,9 +271,10 @@ static zend_function *php_v8js_v8_get_method(zval **object_ptr, char *method, in v8::Local temp_context = v8::Context::New(obj->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); - if (!obj->v8obj.IsEmpty() && obj->v8obj->IsObject() && !obj->v8obj->IsFunction()) { - v8::Local jsObj = obj->v8obj->ToObject(); + if (!obj->v8obj.IsEmpty() && v8obj->IsObject() && !v8obj->IsFunction()) { + v8::Local jsObj = v8obj->ToObject(); if (jsObj->Has(jsKey) && jsObj->Get(jsKey)->IsFunction()) { f = (zend_function *) ecalloc(1, sizeof(*f)); @@ -296,7 +317,7 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) / v8::Context::Scope temp_scope(temp_context); v8::Local method_name = V8JS_SYML(method, strlen(method)); - v8::Local v8obj = obj->v8obj->ToObject(); + v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj)->ToObject(); v8::Local cb; if (method_name->Equals(V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME))) { @@ -309,7 +330,7 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) / v8::Local js_retval; for (i = 0; i < argc; i++) { - jsArgv[i] = v8::Local::New(zval_to_v8js(*argv[i], obj->isolate TSRMLS_CC)); + jsArgv[i] = v8::Local::New(obj->isolate, zval_to_v8js(*argv[i], obj->isolate TSRMLS_CC)); } js_retval = cb->Call(V8JS_GLOBAL, argc, jsArgv); @@ -334,7 +355,14 @@ 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); - if (!obj->v8obj->IsFunction()) { + 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::Context::Scope temp_scope(temp_context); + v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + + if (!v8obj->IsFunction()) { return FAILURE; } @@ -392,7 +420,7 @@ void php_v8js_create_v8(zval *res, v8::Handle value, int flags, v8::I c = (php_v8js_object *) zend_object_store_get_object(res TSRMLS_CC); - c->v8obj = v8::Persistent::New(isolate, value); + c->v8obj.Reset(isolate, value); c->flags = flags; c->isolate = isolate; } @@ -555,6 +583,7 @@ static PHP_METHOD(V8Js, __construct) c->pending_exception = NULL; c->in_execution = 0; c->isolate = v8::Isolate::New(); + c->isolate->SetData(c); c->time_limit_hit = false; c->memory_limit_hit = false; c->module_loader = NULL; @@ -590,15 +619,17 @@ static PHP_METHOD(V8Js, __construct) /* Create global template for global object */ // Now we are using multiple isolates this needs to be created for every context - c->global_template = v8::Persistent::New(c->isolate, v8::FunctionTemplate::New()); - c->global_template->SetClassName(V8JS_SYM("V8Js")); + v8::Local tpl = v8::FunctionTemplate::New(); + tpl->SetClassName(V8JS_SYM("V8Js")); + c->global_template.Reset(c->isolate, tpl); /* Register builtin methods */ - php_v8js_register_methods(c->global_template->InstanceTemplate(), c); + php_v8js_register_methods(tpl->InstanceTemplate(), c); /* Create context */ - c->context = v8::Persistent::New(c->isolate, v8::Context::New(c->isolate, &extension_conf, c->global_template->InstanceTemplate())); - c->context->SetAlignedPointerInEmbedderData(1, c); + v8::Local context = v8::Context::New(c->isolate, &extension_conf, tpl->InstanceTemplate()); + context->SetAlignedPointerInEmbedderData(1, c); + c->context.Reset(c->isolate, context); if (exts) { _php_v8js_free_ext_strarr(exts, exts_count); @@ -612,7 +643,7 @@ static PHP_METHOD(V8Js, __construct) } /* Enter context */ - v8::Context::Scope context_scope(c->context); + v8::Context::Scope context_scope(context); /* Create the PHP container object's function template */ v8::Local php_obj_t = v8::FunctionTemplate::New(); @@ -635,10 +666,11 @@ static PHP_METHOD(V8Js, __construct) } /* Set name for the PHP JS object */ - c->object_name = v8::Persistent::New(c->isolate, (object_name_len) ? V8JS_SYML(object_name, object_name_len) : V8JS_SYM("PHP")); + 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); /* Add the PHP object into global object */ - V8JS_GLOBAL->Set(c->object_name, php_obj_t->InstanceTemplate()->NewInstance(), v8::ReadOnly); + V8JS_GLOBAL->Set(object_name_js, php_obj_t->InstanceTemplate()->NewInstance(), v8::ReadOnly); } /* }}} */ @@ -653,7 +685,8 @@ 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::Context::Scope context_scope((ctx)->context); + v8::HandleScope handle_scope((ctx)->isolate); \ + v8::Context::Scope context_scope((ctx)->isolate, (ctx)->context); static void php_v8js_timer_push(long time_limit, long memory_limit, php_v8js_ctx *c TSRMLS_DC) { @@ -762,8 +795,6 @@ static PHP_METHOD(V8Js, executeString) /* Catch JS exceptions */ v8::TryCatch try_catch; - v8::HandleScope handle_scope(c->isolate); - /* Set script identifier */ v8::Local sname = identifier_len ? V8JS_SYML(identifier, identifier_len) : V8JS_SYM("V8Js::executeString()"); @@ -1089,10 +1120,9 @@ static void php_v8js_write_property(zval *object, zval *member, zval *value ZEND { V8JS_BEGIN_CTX(c, object) - v8::HandleScope handle_scope(c->isolate); - /* Global PHP JS object */ - v8::Local jsobj = V8JS_GLOBAL->Get(c->object_name)->ToObject(); + v8::Local object_name_js = v8::Local::New(c->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); @@ -1106,10 +1136,9 @@ static void php_v8js_unset_property(zval *object, zval *member ZEND_HASH_KEY_DC { V8JS_BEGIN_CTX(c, object) - v8::HandleScope handle_scope(c->isolate); - /* Global PHP JS object */ - v8::Local jsobj = V8JS_GLOBAL->Get(c->object_name)->ToObject(); + v8::Local object_name_js = v8::Local::New(c->isolate, c->object_name); + v8::Local jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject(); /* Delete value from PHP JS object */ jsobj->ForceDelete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); diff --git a/v8js_convert.cc b/v8js_convert.cc index 5ba0d2d..8e2dba6 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -26,11 +26,25 @@ extern "C" { #include "php_v8js_macros.h" #include -#include #include -typedef std::pair TemplateCacheKey; -typedef std::map > TemplateCache; +namespace v8 { + template + struct CopyablePersistentTraits { + typedef Persistent > CopyablePersistent; + static const bool kResetInDestructor = true; + template + V8_INLINE(static void Copy(const Persistent& source, + CopyablePersistent* dest)) { + // do nothing, just allow copy + } + }; +} + + +typedef std::pair TemplateCacheKey; +typedef v8::Persistent > TemplateCacheEntry; +typedef std::map TemplateCache; /* Callback for PHP methods and functions */ static void php_v8js_call_php_func(zval *value, zend_class_entry *ce, zend_function *method_ptr, v8::Isolate *isolate, const v8::FunctionCallbackInfo& info) /* {{{ */ @@ -373,14 +387,18 @@ static v8::Handle php_v8js_hash_to_jsobj(zval *value, v8::Isolate *is /* Object methods */ if (ce == php_ce_v8_function) { php_v8js_object *c = (php_v8js_object *) zend_object_store_get_object(value TSRMLS_CC); - return c->v8obj; + v8::Local v8obj = v8::Local::New(isolate, c->v8obj); + + return v8obj; } else if (ce) { - v8::Handle new_tpl; + php_v8js_ctx *ctx = (php_v8js_ctx *) isolate->GetData(); + v8::Local new_tpl; bool cached_tpl = true; static TemplateCache tpl_map; try { - new_tpl = tpl_map.at(std::make_pair(isolate, ce->name)); + new_tpl = v8::Local::New + (isolate, tpl_map.at(std::make_pair(ctx, ce->name))); } catch (const std::out_of_range &) { cached_tpl = false; @@ -399,8 +417,8 @@ static v8::Handle php_v8js_hash_to_jsobj(zval *value, v8::Isolate *is new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_php_callback); } else { /* Add new v8::FunctionTemplate to tpl_map, as long as it is not a closure. */ - tpl_map[std::make_pair(isolate, ce->name)] = - v8::Persistent::New(isolate, new_tpl); + TemplateCacheEntry tce(isolate, new_tpl); + tpl_map[std::make_pair(ctx, ce->name)] = tce; } } @@ -477,15 +495,14 @@ static v8::Handle php_v8js_hash_to_jsobj(zval *value, v8::Isolate *is // decides to dispose the JS object, we add a weak persistent handle and register // a callback function that removes the reference. v8::Handle external = v8::External::New(value); - v8::Persistent persist_newobj = v8::Persistent::New - (isolate, new_tpl->GetFunction()->NewInstance(1, &external)); + v8::Persistent persist_newobj(isolate, new_tpl->GetFunction()->NewInstance(1, &external)); persist_newobj.MakeWeak(value, php_v8js_weak_object_callback); // Just tell v8 that we're allocating some external memory // (for the moment we just always tell 1k instead of trying to find out actual values) v8::V8::AdjustAmountOfExternalAllocatedMemory(1024); - newobj = persist_newobj; + newobj = v8::Local::New(isolate, persist_newobj); if (ce != zend_ce_closure) { // These unfortunately cannot be attached to the template, hence we have to put them diff --git a/v8js_methods.cc b/v8js_methods.cc index c8dd73d..463e2bd 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -270,7 +270,7 @@ V8JS_METHOD(require) global->Set(v8::String::New("module"), module); // Each module gets its own context so different modules do not affect each other - v8::Persistent context = v8::Persistent::New(c->isolate, v8::Context::New(c->isolate, NULL, global)); + v8::Local context = v8::Local::New(c->isolate, v8::Context::New(c->isolate, NULL, global)); // Catch JS exceptions v8::TryCatch try_catch; From 386465570d42a81ce937ed8ba40b6b3add03f570 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Thu, 3 Oct 2013 16:27:41 +0200 Subject: [PATCH 2/3] Use v8's CopyablePersistentTraits for v8 >= 3.22.0 --- v8js_convert.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/v8js_convert.cc b/v8js_convert.cc index 8e2dba6..3ca93a7 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -28,6 +28,9 @@ extern "C" { #include #include +#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. */ namespace v8 { template struct CopyablePersistentTraits { @@ -40,7 +43,7 @@ namespace v8 { } }; } - +#endif typedef std::pair TemplateCacheKey; typedef v8::Persistent > TemplateCacheEntry; From 12e5e0192038d164d0ee39d147bf2f5bc3149b97 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Thu, 3 Oct 2013 16:57:18 +0200 Subject: [PATCH 3/3] Remove Locker from timer thread --- v8js.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/v8js.cc b/v8js.cc index 2f87130..57bb9c5 100644 --- a/v8js.cc +++ b/v8js.cc @@ -737,8 +737,6 @@ static void php_v8js_terminate_execution(php_v8js_ctx *c TSRMLS_DC) static void php_v8js_timer_thread(TSRMLS_D) { while (!V8JSG(timer_stop)) { - v8::Locker locker; - std::chrono::time_point now = std::chrono::high_resolution_clock::now(); v8::HeapStatistics hs;