Some selected patches from https://github.com/phpv8/v8js/compare/2.1.0...php7 to fix compatibility with upcoming PHP 7.3 From 48a763d6dbd0543ef49e275c6a5d4de4e25af24a Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Thu, 1 Feb 2018 19:38:47 +0100 Subject: [PATCH 02/18] pass module_base directly via FunctionTemplate, closes #349 --- tests/issue_349_basic.phpt | 82 ++++++++++++++++++++++++++++++++++++++ v8js_methods.cc | 34 +++++++++++----- 2 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 tests/issue_349_basic.phpt diff --git a/tests/issue_349_basic.phpt b/tests/issue_349_basic.phpt new file mode 100644 index 0000000..f7a6d7d --- /dev/null +++ b/tests/issue_349_basic.phpt @@ -0,0 +1,82 @@ +--TEST-- +Test V8Js::setModuleNormaliser : Custom normalisation #005 +--SKIPIF-- + +--FILE-- +setModuleNormaliser(function ($base, $moduleName) { + var_dump($base, $moduleName); + if ($base == '' && $moduleName == './tags') { + return ['./tags', 'index.js']; + } + if ($base == './tags' && $moduleName == './if.js') { + return ['./tags', 'if.js']; + } + return [$base, $moduleName]; +}); + +$v8->setModuleLoader(function ($moduleName) { + print("setModuleLoader called for ".$moduleName."\n"); + switch ($moduleName) { + case './app.js': + return "require('./tags')"; + case './tags/index.js': + return "require('./if.js')"; + } +}); + +$v8->executeString("require('./app.js')"); + +echo "------------------------------------------------\n"; + +$v8 = new V8Js(); + +$v8->setModuleNormaliser(function ($base, $moduleName) { + var_dump($base, $moduleName); + if ($base == '' && $moduleName == './tags') { + return ['./tags', 'index.js']; + } + if ($base == './tags' && $moduleName == './if.js') { + return ['./tags', 'if.js']; + } + return [$base, $moduleName]; +}); + +$v8->setModuleLoader(function ($moduleName) { + print("setModuleLoader called for ".$moduleName."\n"); + switch ($moduleName) { + case './app.js': + return "require('./tags')()"; // different + case './tags/index.js': + return "module.exports = function() {require('./if.js')}"; // different + } +}); + +$v8->executeString("require('./app.js')"); + +?> +===EOF=== +--EXPECT-- +string(0) "" +string(8) "./app.js" +setModuleLoader called for ./app.js +string(0) "" +string(6) "./tags" +setModuleLoader called for ./tags/index.js +string(6) "./tags" +string(7) "./if.js" +setModuleLoader called for ./tags/if.js +------------------------------------------------ +string(0) "" +string(8) "./app.js" +setModuleLoader called for ./app.js +string(0) "" +string(6) "./tags" +setModuleLoader called for ./tags/index.js +string(6) "./tags" +string(7) "./if.js" +setModuleLoader called for ./tags/if.js +===EOF=== diff --git a/v8js_methods.cc b/v8js_methods.cc index 947d348..644c8c2 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -205,10 +205,10 @@ V8JS_METHOD(var_dump) /* {{{ */ V8JS_METHOD(require) { v8::Isolate *isolate = info.GetIsolate(); + v8js_ctx *c = (v8js_ctx *) isolate->GetData(0); - // Get the extension context - v8::Local data = v8::Local::Cast(info.Data()); - v8js_ctx *c = static_cast(data->Value()); + v8::String::Utf8Value module_base(info.Data()); + const char *module_base_cstr = ToCString(module_base); // Check that we have a module loader if(Z_TYPE(c->module_loader) == IS_NULL) { @@ -225,7 +225,7 @@ V8JS_METHOD(require) normalised_path = (char *)emalloc(PATH_MAX); module_name = (char *)emalloc(PATH_MAX); - v8js_commonjs_normalise_identifier(c->modules_base.back(), module_id, normalised_path, module_name); + v8js_commonjs_normalise_identifier(module_base_cstr, module_id, normalised_path, module_name); } else { // Call custom normaliser @@ -238,7 +238,7 @@ V8JS_METHOD(require) isolate->Exit(); v8::Unlocker unlocker(isolate); - ZVAL_STRING(¶ms[0], c->modules_base.back()); + ZVAL_STRING(¶ms[0], module_base_cstr); ZVAL_STRING(¶ms[1], module_id); call_result = call_user_function_ex(EG(function_table), NULL, &c->module_normaliser, @@ -445,7 +445,7 @@ V8JS_METHOD(require) v8::Local source = V8JS_ZSTR(Z_STR(module_code)); zval_ptr_dtor(&module_code); - source = v8::String::Concat(V8JS_SYM("(function (exports, module) {"), source); + source = v8::String::Concat(V8JS_SYM("(function (exports, module, require) {"), source); source = v8::String::Concat(source, V8JS_SYM("\n});")); // Create and compile script @@ -459,6 +459,17 @@ V8JS_METHOD(require) return; } + v8::Local base_path = V8JS_STR(normalised_path); + v8::MaybeLocal require_fn = v8::FunctionTemplate::New(isolate, V8JS_MN(require), base_path)->GetFunction(); + + if (require_fn.IsEmpty()) { + efree(normalised_path); + efree(normalised_module_id); + info.GetReturnValue().Set(isolate->ThrowException(V8JS_SYM("Failed to create require method"))); + return; + } + + // Add this module and path to the stack c->modules_stack.push_back(normalised_module_id); c->modules_base.push_back(normalised_path); @@ -474,15 +485,18 @@ V8JS_METHOD(require) module->Set(V8JS_SYM("exports"), exports); if (module_function->IsFunction()) { - v8::Local *jsArgv = static_cast *>(alloca(2 * sizeof(v8::Local))); + v8::Local *jsArgv = static_cast *>(alloca(3 * sizeof(v8::Local))); new(&jsArgv[0]) v8::Local; jsArgv[0] = exports; new(&jsArgv[1]) v8::Local; jsArgv[1] = module; + new(&jsArgv[2]) v8::Local; + jsArgv[2] = require_fn.ToLocalChecked(); + // actually call the module - v8::Local::Cast(module_function)->Call(exports, 2, jsArgv); + v8::Local::Cast(module_function)->Call(exports, 3, jsArgv); } // Remove this module and path from the stack @@ -532,8 +546,8 @@ void v8js_register_methods(v8::Local global, v8js_ctx *c) /* global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(isolate, V8JS_MN(print)), v8::ReadOnly); global->Set(V8JS_SYM("var_dump"), v8::FunctionTemplate::New(isolate, V8JS_MN(var_dump)), v8::ReadOnly); - c->modules_base.push_back(""); - global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(isolate, V8JS_MN(require), v8::External::New(isolate, c)), v8::ReadOnly); + v8::Local base_path = V8JS_STRL("", 0); + global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(isolate, V8JS_MN(require), base_path), v8::ReadOnly); } /* }}} */ From 313ad1e258542e2db64a0a38f64eb789c963bd03 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Thu, 1 Feb 2018 19:43:36 +0100 Subject: [PATCH 04/18] cleanup: remove no longer needed modules_base from ctx, refs #349 --- v8js_class.cc | 2 -- v8js_class.h | 1 - v8js_methods.cc | 2 -- 3 files changed, 5 deletions(-) diff --git a/v8js_class.cc b/v8js_class.cc index a572864..dc366aa 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -200,7 +200,6 @@ static void v8js_free_storage(zend_object *object) /* {{{ */ } c->modules_stack.~vector(); - c->modules_base.~vector(); zval_ptr_dtor(&c->zval_snapshot_blob); @@ -226,7 +225,6 @@ static zend_object* v8js_new(zend_class_entry *ce) /* {{{ */ new(&c->array_tmpl) v8::Persistent(); new(&c->modules_stack) std::vector(); - new(&c->modules_base) std::vector(); new(&c->modules_loaded) std::map; new(&c->template_cache) std::map(); diff --git a/v8js_class.h b/v8js_class.h index 4886754..cd06fc5 100644 --- a/v8js_class.h +++ b/v8js_class.h @@ -57,7 +57,6 @@ struct v8js_ctx { zval module_loader; std::vector modules_stack; - std::vector modules_base; std::map modules_loaded; std::map template_cache; diff --git a/v8js_methods.cc b/v8js_methods.cc index 644c8c2..4bee095 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -472,7 +472,6 @@ V8JS_METHOD(require) // Add this module and path to the stack c->modules_stack.push_back(normalised_module_id); - c->modules_base.push_back(normalised_path); // Run script to evaluate closure v8::Local module_function = script->Run(); @@ -501,7 +500,6 @@ V8JS_METHOD(require) // Remove this module and path from the stack c->modules_stack.pop_back(); - c->modules_base.pop_back(); efree(normalised_path); From c7019e3c1a311e6ed2cccda01445e21fa5a17969 Mon Sep 17 00:00:00 2001 From: Jan-E Date: Thu, 5 Jul 2018 11:22:17 +0200 Subject: [PATCH 05/18] Remove GC_G(gc_active) check --- v8js_v8object_class.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v8js_v8object_class.cc b/v8js_v8object_class.cc index 49669ac..abc32d3 100644 --- a/v8js_v8object_class.cc +++ b/v8js_v8object_class.cc @@ -210,10 +210,12 @@ static HashTable *v8js_v8object_get_properties(zval *object) /* {{{ */ v8js_v8object *obj = Z_V8JS_V8OBJECT_OBJ_P(object); if (obj->properties == NULL) { +#if PHP_VERSION_ID < 70300 if (GC_G(gc_active)) { /* the garbage collector is running, don't create more zvals */ return NULL; } +#endif ALLOC_HASHTABLE(obj->properties); zend_hash_init(obj->properties, 0, NULL, ZVAL_PTR_DTOR, 0); From 826aaa689cd420d1ea0961109607a8641de31031 Mon Sep 17 00:00:00 2001 From: Jan-E Date: Thu, 5 Jul 2018 11:30:20 +0200 Subject: [PATCH 06/18] Change to GC_IS_RECURSIVE for PHP 7.3 --- v8js_convert.cc | 4 ++++ v8js_exceptions.cc | 4 ++++ v8js_object_export.cc | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/v8js_convert.cc b/v8js_convert.cc index 056e31b..4091cf0 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -70,7 +70,11 @@ static v8::Local v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate v8::Local newarr; /* Prevent recursion */ +#if PHP_VERSION_ID >= 70300 + if (myht && GC_IS_RECURSIVE(myht)) { +#else if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) { +#endif return V8JS_NULL; } diff --git a/v8js_exceptions.cc b/v8js_exceptions.cc index 42fe7b1..967cae8 100644 --- a/v8js_exceptions.cc +++ b/v8js_exceptions.cc @@ -88,7 +88,11 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8:: zend_class_entry *exception_ce = zend_exception_get_default(); if (instanceof_function(php_exception->ce, exception_ce)) { +#ifdef GC_ADDREF + GC_ADDREF(php_exception); +#else ++GC_REFCOUNT(php_exception); +#endif zend_exception_set_previous(Z_OBJ_P(return_value), php_exception); } } diff --git a/v8js_object_export.cc b/v8js_object_export.cc index 4d61e76..85bd701 100644 --- a/v8js_object_export.cc +++ b/v8js_object_export.cc @@ -137,7 +137,9 @@ static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v zend_try { /* zend_fcall_info_cache */ +#if PHP_VERSION_ID < 70300 fcc.initialized = 1; +#endif fcc.function_handler = method_ptr; fcc.calling_scope = object->ce; fcc.called_scope = object->ce; @@ -1013,7 +1015,11 @@ v8::Local v8js_hash_to_jsobj(zval *value, v8::Isolate *isolate) /* {{ } /* Prevent recursion */ +#if PHP_VERSION_ID >= 70300 + if (myht && GC_IS_RECURSIVE(myht)) { +#else if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) { +#endif return V8JS_NULL; } From 0c2b55d8028cbdc603a84136a883edee277fed99 Mon Sep 17 00:00:00 2001 From: Jan-E Date: Thu, 5 Jul 2018 11:41:32 +0200 Subject: [PATCH 07/18] Add GC_(UN)PROTECT_RECURSION, move (un)protecting outside loops --- php_v8js_macros.h | 11 +++++++++++ v8js_convert.cc | 27 ++++++++++++++++----------- v8js_object_export.cc | 29 +++++++++++++++++------------ 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/php_v8js_macros.h b/php_v8js_macros.h index e95ca55..2503663 100644 --- a/php_v8js_macros.h +++ b/php_v8js_macros.h @@ -166,6 +166,17 @@ extern struct _v8js_process_globals v8js_process_globals; /* Register builtin methods into passed object */ void v8js_register_methods(v8::Local, v8js_ctx *c); +#ifdef ZEND_HASH_INC_APPLY_COUNT +#ifndef GC_PROTECT_RECURSION +# define GC_PROTECT_RECURSION(ht) ZEND_HASH_INC_APPLY_COUNT(ht) +#endif +#endif +#ifdef ZEND_HASH_DEC_APPLY_COUNT +#ifndef GC_UNPROTECT_RECURSION +# define GC_UNPROTECT_RECURSION(ht) ZEND_HASH_DEC_APPLY_COUNT(ht) +#endif +#endif + #endif /* PHP_V8JS_MACROS_H */ /* diff --git a/v8js_convert.cc b/v8js_convert.cc index 4091cf0..b701154 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -84,21 +84,26 @@ static v8::Local v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate { zval *data; ulong index = 0; - HashTable *tmp_ht; - ZEND_HASH_FOREACH_VAL(myht, data) { - tmp_ht = HASH_OF(data); - - if (tmp_ht) { - ZEND_HASH_INC_APPLY_COUNT(myht); - } +#if PHP_VERSION_ID >= 70300 + if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { +#else + if (myht) { +#endif + GC_PROTECT_RECURSION(myht); + } + ZEND_HASH_FOREACH_VAL(myht, data) { newarr->Set(index++, zval_to_v8js(data, isolate)); - - if (tmp_ht) { - ZEND_HASH_DEC_APPLY_COUNT(myht); - } } ZEND_HASH_FOREACH_END(); + +#if PHP_VERSION_ID >= 70300 + if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { +#else + if (myht) { +#endif + GC_UNPROTECT_RECURSION(myht); + } } return newarr; } diff --git a/v8js_object_export.cc b/v8js_object_export.cc index 85bd701..3568871 100644 --- a/v8js_object_export.cc +++ b/v8js_object_export.cc @@ -955,21 +955,20 @@ static v8::Local v8js_wrap_array_to_object(v8::Isolate *isolate, zva if (i > 0) { zval *data; - HashTable *tmp_ht; - ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, data) { - tmp_ht = HASH_OF(data); +#if PHP_VERSION_ID >= 70300 + if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { +#else + if (myht) { +#endif + GC_PROTECT_RECURSION(myht); + } - if (tmp_ht) { - ZEND_HASH_INC_APPLY_COUNT(tmp_ht); - } + ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, data) { if (key) { if (ZSTR_VAL(key)[0] == '\0' && Z_TYPE_P(value) == IS_OBJECT) { /* Skip protected and private members. */ - if (tmp_ht) { - ZEND_HASH_DEC_APPLY_COUNT(tmp_ht); - } continue; } @@ -991,10 +990,16 @@ static v8::Local v8js_wrap_array_to_object(v8::Isolate *isolate, zva newobj->Set(static_cast(index), zval_to_v8js(data, isolate)); } - if (tmp_ht) { - ZEND_HASH_DEC_APPLY_COUNT(tmp_ht); - } } ZEND_HASH_FOREACH_END(); + +#if PHP_VERSION_ID >= 70300 + if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { +#else + if (myht) { +#endif + GC_UNPROTECT_RECURSION(myht); + } + } return newobj; From c87ddb8ebf9b249663f32b9e94b6fad698b3e2c1 Mon Sep 17 00:00:00 2001 From: Jan-E Date: Thu, 5 Jul 2018 11:45:03 +0200 Subject: [PATCH 08/18] Fix tests for added method DateTime::createFromImmutable() --- tests/var_dump.phpt | 5 +- tests/var_dump_73.phpt | 322 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 tests/var_dump_73.phpt diff --git a/tests/var_dump.phpt b/tests/var_dump.phpt index 9ce0896..7a5d7d8 100644 --- a/tests/var_dump.phpt +++ b/tests/var_dump.phpt @@ -1,7 +1,10 @@ --TEST-- Test V8::executeString() : var_dump --SKIPIF-- - += 70300) die('skip Only for php version < 7.3'); +?> --INI-- date.timezone=UTC --FILE-- diff --git a/tests/var_dump_73.phpt b/tests/var_dump_73.phpt new file mode 100644 index 0000000..553febb --- /dev/null +++ b/tests/var_dump_73.phpt @@ -0,0 +1,322 @@ +--TEST-- +Test V8::executeString() : var_dump +--SKIPIF-- += 7.3'); +?> +--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(%d) "1976-09-27 09:00:00%r(\.0+)?%r" + ["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 (19) { + ["createFromImmutable"] => + object(Closure)#%d { + function () { [native code] } + } + ["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(%d) "1976-09-27 09:00:00%r(\.0+)?%r" + ["$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=== From 61f98e2a4a01ae429390768e92c7af1cb2418f4d Mon Sep 17 00:00:00 2001 From: Jan-E Date: Sat, 7 Jul 2018 05:18:31 +0200 Subject: [PATCH 11/18] extensions_error.phpt runs fine on Windows --- tests/extensions_error.phpt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/extensions_error.phpt b/tests/extensions_error.phpt index f59b0b7..b11054a 100644 --- a/tests/extensions_error.phpt +++ b/tests/extensions_error.phpt @@ -9,12 +9,6 @@ phpinfo(INFO_MODULES); $minfo = ob_get_contents(); ob_end_clean(); -if(strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { - // On Windows the "Fatal error" happens to appear before the error - // message output by V8 itself. - echo "skip Windows"; -} - if(preg_match("/V8 Engine Linked Version => (.*)/", $minfo, $matches)) { $version = explode('.', $matches[1]); if($version[0] < 5 || ($version[0] == 5 && $version[1] < 7)) { From 4762dd1f0ae799add559f18307032d697b05497e Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 13 Jul 2018 14:21:45 +0200 Subject: [PATCH 13/18] fix null-ptr deref --- v8js_class.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/v8js_class.cc b/v8js_class.cc index 8f66f7c..524e55e 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -273,9 +273,14 @@ static void v8js_jsext_free_storage(v8js_jsext *jsext) /* {{{ */ v8js_free_ext_strarr(jsext->deps, jsext->deps_count); } delete jsext->extension; + // Free the persisted non-interned strings we allocated. - zend_string_release(jsext->name); - zend_string_release(jsext->source); + if (jsext->name) { + zend_string_release(jsext->name); + } + if (jsext->source) { + zend_string_release(jsext->source); + } free(jsext); } From 90b6b31f06a7ff990c462d6ec012b28c54a31faf Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 13 Jul 2018 15:41:41 +0200 Subject: [PATCH 14/18] break recursion immediately on PHP <= 7.2 as well --- tests/array_recursive.phpt | 25 +++++++++++++++++++++++++ v8js_convert.cc | 15 +++++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 tests/array_recursive.phpt diff --git a/tests/array_recursive.phpt b/tests/array_recursive.phpt new file mode 100644 index 0000000..47df7ea --- /dev/null +++ b/tests/array_recursive.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test V8::executeString() : export of recursive array +--SKIPIF-- + +--FILE-- +foo = $a; +$v8->executeString('var_dump(PHP.foo);'); + +?> +===EOF=== +--EXPECT-- +array(2) { + [0] => + NULL + [1] => + int(23) +} +===EOF=== diff --git a/v8js_convert.cc b/v8js_convert.cc index b701154..3f91dd1 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -71,10 +71,11 @@ static v8::Local v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate /* Prevent recursion */ #if PHP_VERSION_ID >= 70300 - if (myht && GC_IS_RECURSIVE(myht)) { + if (myht && GC_IS_RECURSIVE(myht)) #else - if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) { + if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) #endif + { return V8JS_NULL; } @@ -86,10 +87,11 @@ static v8::Local v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate ulong index = 0; #if PHP_VERSION_ID >= 70300 - if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { + if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) #else - if (myht) { + if (myht) #endif + { GC_PROTECT_RECURSION(myht); } @@ -98,10 +100,11 @@ static v8::Local v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate } ZEND_HASH_FOREACH_END(); #if PHP_VERSION_ID >= 70300 - if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { + if (myht && !(GC_FLAGS(myht) & GC_IMMUTABLE)) #else - if (myht) { + if (myht) #endif + { GC_UNPROTECT_RECURSION(myht); } } From 18b79d90046a63209cedf58ef24e2f4f0281f7a1 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Wed, 18 Jul 2018 23:36:13 +0200 Subject: [PATCH 18/18] don't free interned strings, only persistent ones --- v8js_class.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/v8js_class.cc b/v8js_class.cc index a9cda70..9f67992 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -991,7 +991,10 @@ static void v8js_persistent_zval_ctor(zval *p) /* {{{ */ static void v8js_persistent_zval_dtor(zval *p) /* {{{ */ { assert(Z_TYPE_P(p) == IS_STRING); - free(Z_STR_P(p)); + + if (!ZSTR_IS_INTERNED(Z_STR_P(p))) { + free(Z_STR_P(p)); + } } /* }}} */