summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemi Collet <remi@remirepo.net>2018-10-25 14:36:58 +0200
committerRemi Collet <remi@remirepo.net>2018-10-25 14:36:58 +0200
commit52b25c7338e1b55ba696b281f9227c437f2d0968 (patch)
treef5d7020da0d998d4d6079217f98b975f638c6071
parentfbf98fc73e87bed9a1157d1801dd7de08a649dfc (diff)
add upstream patches for PHP 7.3
-rw-r--r--PHPINFO13
-rw-r--r--php-pecl-v8js.spec17
-rw-r--r--v8js-upstream.patch1022
3 files changed, 1048 insertions, 4 deletions
diff --git a/PHPINFO b/PHPINFO
new file mode 100644
index 0000000..8223a4a
--- /dev/null
+++ b/PHPINFO
@@ -0,0 +1,13 @@
+
+v8js
+
+V8 Javascript Engine => enabled
+V8 Engine Compiled Version => 6.2.91
+V8 Engine Linked Version => 6.2.91
+Version => 2.1.0
+
+Directive => Local Value => Master Value
+v8js.flags => no value => no value
+v8js.icudtl_dat_path => no value => no value
+v8js.use_date => 0 => 0
+v8js.use_array_access => 0 => 0
diff --git a/php-pecl-v8js.spec b/php-pecl-v8js.spec
index 9955bac..f23b7c2 100644
--- a/php-pecl-v8js.spec
+++ b/php-pecl-v8js.spec
@@ -20,19 +20,20 @@
Summary: V8 Javascript Engine for PHP
Name: %{?sub_prefix}php-pecl-%{pecl_name}
Version: 2.1.0
-Release: 2%{?dist}%{!?nophptag:%(%{__php} -r 'echo ".".PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')}
+Release: 3%{?dist}%{!?nophptag:%(%{__php} -r 'echo ".".PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')}
License: PHP
-Group: Development/Languages
URL: http://pecl.php.net/package/%{pecl_name}
Source0: http://pecl.php.net/get/%{pecl_name}-%{version}.tgz
+Patch0: %{pecl_name}-upstream.patch
+
# See http://pkgs.fedoraproject.org/cgit/rpms/v8.git/tree/v8.spec#n74
ExclusiveArch: %{ix86} x86_64 %{arm} ppc mipsel mips64el
BuildRequires: %{?dtsprefix}gcc
BuildRequires: %{?scl_prefix}php-devel > 7
BuildRequires: %{?scl_prefix}php-pear
-# because of https://bugzilla.redhat.com/1378889
-BuildRequires: v8-devel >= 5.2.258-7
+# from config.m4 for full features (else 4.6.76)
+BuildRequires: v8-devel >= 1:6.5.143
Requires: %{?scl_prefix}php(zend-abi) = %{php_zend_api}
Requires: %{?scl_prefix}php(api) = %{php_core_api}
@@ -59,6 +60,10 @@ Obsoletes: php71w-pecl-%{pecl_name} <= %{version}
Obsoletes: php72u-pecl-%{pecl_name} <= %{version}
Obsoletes: php72w-pecl-%{pecl_name} <= %{version}
%endif
+%if "%{php_version}" > "7.3"
+Obsoletes: php73u-pecl-%{pecl_name} <= %{version}
+Obsoletes: php73w-pecl-%{pecl_name} <= %{version}
+%endif
%endif
%if 0%{?fedora} < 20 && 0%{?rhel} < 7
@@ -86,6 +91,7 @@ sed -e 's/role="test"/role="src"/' \
-i package.xml
cd NTS
+%patch0 -p1 -b .upstream
# Sanity check, really often broken
extver=$(sed -n '/#define PHP_V8JS_VERSION/{s/.* "//;s/".*$//;p}' php_v8js_macros.h)
@@ -238,6 +244,9 @@ REPORT_EXIT_STATUS=1 \
%changelog
+* Thu Oct 25 2018 Remi Collet <remi@remirepo.net> - 2.1.0-3
+- add upstream patches for PHP 7.3
+
* Fri Sep 7 2018 Remi Collet <remi@remirepo.net> - 2.1.0-2
- rebuild
diff --git a/v8js-upstream.patch b/v8js-upstream.patch
new file mode 100644
index 0000000..937915d
--- /dev/null
+++ b/v8js-upstream.patch
@@ -0,0 +1,1022 @@
+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 <stesie@brokenpipe.de>
+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--
++<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
++--FILE--
++<?php
++
++$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')";
++ 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<v8::External> data = v8::Local<v8::External>::Cast(info.Data());
+- v8js_ctx *c = static_cast<v8js_ctx*>(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(&params[0], c->modules_base.back());
++ ZVAL_STRING(&params[0], module_base_cstr);
+ ZVAL_STRING(&params[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<v8::String> 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<v8::String> base_path = V8JS_STR(normalised_path);
++ v8::MaybeLocal<v8::Function> 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<v8::Value> *jsArgv = static_cast<v8::Local<v8::Value> *>(alloca(2 * sizeof(v8::Local<v8::Value>)));
++ v8::Local<v8::Value> *jsArgv = static_cast<v8::Local<v8::Value> *>(alloca(3 * sizeof(v8::Local<v8::Value>)));
+ new(&jsArgv[0]) v8::Local<v8::Value>;
+ jsArgv[0] = exports;
+
+ new(&jsArgv[1]) v8::Local<v8::Value>;
+ jsArgv[1] = module;
+
++ new(&jsArgv[2]) v8::Local<v8::Value>;
++ jsArgv[2] = require_fn.ToLocalChecked();
++
+ // actually call the module
+- v8::Local<v8::Function>::Cast(module_function)->Call(exports, 2, jsArgv);
++ v8::Local<v8::Function>::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<v8::ObjectTemplate> 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<v8::String> 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 <stesie@brokenpipe.de>
+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<v8::FunctionTemplate>();
+
+ new(&c->modules_stack) std::vector<char*>();
+- new(&c->modules_base) std::vector<char*>();
+ new(&c->modules_loaded) std::map<char *, v8js_persistent_value_t, cmp_str>;
+
+ new(&c->template_cache) std::map<const zend_string *,v8js_function_tmpl_t>();
+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<char *> modules_stack;
+- std::vector<char *> modules_base;
+ std::map<char *, v8js_persistent_value_t, cmp_str> modules_loaded;
+ std::map<const zend_string *,v8js_function_tmpl_t> 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<v8::Value> 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 <github@ehrhardt.nl>
+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 <github@ehrhardt.nl>
+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<v8::Value> v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate
+ v8::Local<v8::Array> 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<v8::Value> 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 <github@ehrhardt.nl>
+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<v8::ObjectTemplate>, 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<v8::Value> 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<v8::Object> 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<v8::Object> v8js_wrap_array_to_object(v8::Isolate *isolate, zva
+ newobj->Set(static_cast<uint32_t>(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 <github@ehrhardt.nl>
+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--
+-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
++<?php
++require_once(dirname(__FILE__) . '/skipif.inc');
++if (PHP_VERSION_ID >= 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--
++<?php
++require_once(dirname(__FILE__) . '/skipif.inc');
++if (PHP_VERSION_ID < 70300) die('skip Only for php version >= 7.3');
++?>
++--INI--
++date.timezone=UTC
++--FILE--
++<?php
++# Test var_dump of various types
++
++$JS = <<< EOT
++
++print("--- JS var_dump of PHP object ----\\n");
++var_dump(PHP.phptypes);
++
++print("--- JS var_dump of JS object ----\\n");
++var types = {
++ undefined: undefined,
++ null: null,
++ bool: true,
++ string: "string",
++ uint: 1,
++ int: -1,
++ number: 3.141592654,
++ // XXX this gets parsed with local timezone,
++ // which is bad for test repeatability.
++ //date: new Date('September 27, 1976 09:00:00 GMT'),
++ regexp: /regexp/,
++ array: [1,2,3],
++ object: { field: "foo" },
++ function: function id(x) { return x; },
++ phpobject: PHP.obj
++};
++
++var_dump(types);
++print("--- PHP var_dump of JS object ----\\n");
++types;
++EOT;
++
++class Foo {
++ var $field = "php";
++}
++
++$v8 = new V8Js();
++$v8->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) "<required>"
++ }
++ }
++ ["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 <github@ehrhardt.nl>
+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 <stesie@brokenpipe.de>
+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 <stesie@brokenpipe.de>
+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--
++<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
++--FILE--
++<?php
++
++$a = [];
++$a[] = &$a;
++$a[] = 23;
++
++$v8 = new V8Js();
++$v8->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<v8::Value> 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<v8::Value> 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<v8::Value> 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 <stesie@brokenpipe.de>
+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));
++ }
+ }
+ /* }}} */
+