Browse Source

Merge open PRs into php7

Stefan Siegl 6 years ago
parent
commit
85097c1d68
23 changed files with 588 additions and 341 deletions
  1. 1 0
      .gitignore
  2. 3 3
      .travis.yml
  3. 1 1
      Dockerfile.travis
  4. 1 1
      Makefile.travis
  5. 4 3
      README.md
  6. 2 2
      Vagrantfile
  7. 16 15
      appveyor.yml
  8. 14 15
      config.m4
  9. 4 4
      config.w32
  10. 0 4
      php_v8js_macros.h
  11. 2 0
      tests/timezones.phpt
  12. 13 6
      v8js_array_access.cc
  13. 1 1
      v8js_array_access.h
  14. 50 33
      v8js_class.cc
  15. 34 11
      v8js_convert.cc
  16. 29 19
      v8js_exceptions.cc
  17. 9 6
      v8js_generator_export.cc
  18. 117 35
      v8js_methods.cc
  19. 123 55
      v8js_object_export.cc
  20. 1 1
      v8js_object_export.h
  21. 60 43
      v8js_v8.cc
  22. 7 8
      v8js_v8.h
  23. 96 75
      v8js_v8object_class.cc

+ 1 - 0
.gitignore

@@ -20,6 +20,7 @@ config.status
 config.sub
 configure
 configure.in
+configure.ac
 *~
 install-sh
 libtool

+ 3 - 3
.travis.yml

@@ -5,11 +5,11 @@ services:
   - docker
 
 env:
-  - V8VER=6.6
+  - V8VER=7.1
     PHPVER=7.0
-  - V8VER=6.6
+  - V8VER=7.1
     PHPVER=7.1
-  - V8VER=6.6
+  - V8VER=7.1
     PHPVER=7.2
 
 script: make -f Makefile.travis test

+ 1 - 1
Dockerfile.travis

@@ -9,7 +9,7 @@ RUN apt-get update -q
 RUN apt-get install -y software-properties-common
 
 RUN add-apt-repository ppa:ondrej/php
-RUN add-apt-repository ppa:pinepain/php -y
+RUN add-apt-repository ppa:stesie/libv8 -y
 RUN apt-get update -q
 
 RUN apt-get install -y php$PHPVER-dev libv8-$V8VER-dev

+ 1 - 1
Makefile.travis

@@ -1,5 +1,5 @@
 # Configure and build scripts for travis CI system
-V8VER ?= 6.6
+V8VER ?= 7.1
 
 IMAGENAME ?= v8js-test
 

+ 4 - 3
README.md

@@ -26,7 +26,7 @@ Minimum requirements
 	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 requires V8 4.6.76 or higher.
+	This extension requires V8 6.9 (6.9.427.18 is known to work) or higher.
 
     V8 releases are published rather quickly and the V8 team usually provides security support
     for the version line shipped with the Chrome browser (stable channel) and newer (only).
@@ -44,7 +44,9 @@ For some very first steps, instead of compiling manually you might want to try o
 image](https://registry.hub.docker.com/u/stesie/v8js/).  It has v8, v8js and php-cli pre-installed
 so you can give it a try with PHP in "interactive mode".  There is no Apache, etc. running however.
 
-For Ubuntu @pinepain has PPAs for [various versions of V8](https://launchpad.net/~pinepain).
+For Ubuntu @stesie has a PPA, based on prior work by @pinepain.
+[See here](https://launchpad.net/~stesie/+archive/ubuntu/libv8/), packaging sources are available
+[from ppa-packaging repository](https://github.com/phpv8/ppa-packaging/tree/stesie/) (*stesie* branch).
 You might want to install these and compile V8Js based on them.
 
 There also is a Heroku extension pack that has recent V8Js versions for PHP 7.x, see
@@ -81,7 +83,6 @@ class V8Js
 
     /**
      * Initializes and starts V8 engine and returns new V8Js object with it's own V8 context.
-     * Snapshots are supported by V8 4.3.7 and higher.
      * @param string $object_name
      * @param array $variables
      * @param array $extensions

+ 2 - 2
Vagrantfile

@@ -19,7 +19,7 @@ Vagrant.configure("2") do |config|
   #
   # mass-define "generic" Ubuntu boxes
   #
-  %w{6.3 6.4 6.5}.each { |version|
+  %w{7.1}.each { |version|
     config.vm.define "v8-#{version}" do |i|
       i.vm.synced_folder ".", "/data/v8js"
 
@@ -31,7 +31,7 @@ Vagrant.configure("2") do |config|
       apt-get install -y software-properties-common gdb tmux git tig curl apache2-utils lcov
 
       add-apt-repository ppa:ondrej/php
-      add-apt-repository ppa:pinepain/libv8-#{version}
+      add-apt-repository ppa:stesie/libv8
       apt-get update
       apt-get install -y php7.1-dev libv8-#{version}-dbg libv8-#{version}-dev
     SHELL

+ 16 - 15
appveyor.yml

@@ -2,22 +2,22 @@ environment:
   matrix:
   - ARTIFACT_NAME: v8js_vc15_php-7.3_%Platform%_ts.zip
     OUTDIR: Release_TS
-    V8_ASSETS: V8-5.8.283.31-%Platform%.zip
-    PHP_VERSION: 7.3.0alpha3
+    V8_ASSETS: V8-7.0.276.11-vc15-%Platform%.zip
+    PHP_VERSION: 7.3.0RC1
   - ARTIFACT_NAME: v8js_vc15_php-7.3_%Platform%_nts.zip
     OUTDIR: Release
     CONFIGURE_EXTRA: --disable-zts
-    V8_ASSETS: V8-5.8.283.31-%Platform%.zip
-    PHP_VERSION: 7.3.0alpha3
+    V8_ASSETS: V8-7.0.276.11-vc15-%Platform%.zip
+    PHP_VERSION: 7.3.0RC1
   - ARTIFACT_NAME: v8js_vc15_php-7.2_%Platform%_ts.zip
     OUTDIR: Release_TS
-    V8_ASSETS: V8-5.8.283.31-%Platform%.zip
-    PHP_VERSION: 7.2.7
+    V8_ASSETS: V8-7.0.276.11-vc15-%Platform%.zip
+    PHP_VERSION: 7.2.10
   - ARTIFACT_NAME: v8js_vc15_php-7.2_%Platform%_nts.zip
     OUTDIR: Release
     CONFIGURE_EXTRA: --disable-zts
-    V8_ASSETS: V8-5.8.283.31-%Platform%.zip
-    PHP_VERSION: 7.2.7
+    V8_ASSETS: V8-7.0.276.11-vc15-%Platform%.zip
+    PHP_VERSION: 7.2.10
 
   PHP_SDK: c:\projects\php-sdk
 
@@ -30,20 +30,21 @@ platform:
 
 install:
   - cd %PHP_SDK%
-  - curl -fSL -o php-sdk-2.1.7beta1.zip "https://github.com/Microsoft/php-sdk-binary-tools/archive/php-sdk-2.1.7beta1.zip"
-  - 7z.exe x php-sdk-2.1.7beta1.zip
-  - move /y php-sdk-binary-tools-php-sdk-2.1.7beta1\* .
-  - for /R /D %%f in (php-sdk-binary-tools-php-sdk-2.1.7beta1\*) do move /y %%f .
+  - curl -fSL -o php-sdk-2.1.9beta1.zip "https://github.com/Microsoft/php-sdk-binary-tools/archive/php-sdk-2.1.9beta1.zip"
+  - 7z.exe x php-sdk-2.1.9beta1.zip
+  - move /y php-sdk-binary-tools-php-sdk-2.1.9beta1\* .
+  - for /R /D %%f in (php-sdk-binary-tools-php-sdk-2.1.9beta1\*) do move /y %%f .
   - call bin\phpsdk_setvars.bat
   - call bin\phpsdk_buildtree.bat v8js-ci
   - cd v8js-ci\vc15\%Platform%
   - md deps
   - cd deps
-  - curl -fSL -o %V8_ASSETS% "https://s3.amazonaws.com/win-phpv8/%V8_ASSETS%"
+  - curl -fSL -o %V8_ASSETS% "https://phpdev.toolsforresearch.com/%V8_ASSETS%"
   - 7z.exe x %V8_ASSETS%
-  - curl -fSL -o libiconv.zip "https://windows.php.net/downloads/php-sdk/deps/vc14/%Platform%/libiconv-1.15-vc14-%Platform%.zip"
+  - dir
+  - curl -fSL -o libiconv.zip "https://windows.php.net/downloads/php-sdk/deps/vc15/%Platform%/libiconv-1.15-3-vc15-%Platform%.zip"
   - 7z.exe x libiconv.zip -y
-  - curl -fSL -o libxml2.zip "https://windows.php.net/downloads/php-sdk/deps/vc14/%Platform%/libxml2-2.9.8-vc14-%Platform%.zip"
+  - curl -fSL -o libxml2.zip "https://windows.php.net/downloads/php-sdk/deps/vc15/%Platform%/libxml2-2.9.8-vc15-%Platform%.zip"
   - 7z.exe x libxml2.zip -y
   - cd ..
   - curl -fSL -o "php-%PHP_VERSION%.tar.gz" "https://github.com/php/php-src/archive/php-%PHP_VERSION%.tar.gz"

+ 14 - 15
config.m4

@@ -43,7 +43,7 @@ if test "$PHP_V8JS" != "no"; then
     old_CPPFLAGS=$CPPFLAGS
     AC_LANG_PUSH([C++])
     CPPFLAGS="-std="$ac_cv_v8_cstd
-    AC_TRY_RUN([int main() { return 0; }],[],[ac_cv_v8_cstd="c++0x"],[])
+    AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])],[],[ac_cv_v8_cstd="c++0x"],[])
     AC_LANG_POP([C++])
     CPPFLAGS=$old_CPPFLAGS
   ]);
@@ -53,27 +53,27 @@ if test "$PHP_V8JS" != "no"; then
     old_CXXFLAGS=$CXXFLAGS
     AC_LANG_PUSH([C++])
     CXXFLAGS="-std="$ac_cv_v8_cstd
-    AC_TRY_RUN([int main() {
+    AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() {
         struct { unsigned int x; } foo = {-1};
         (void) foo;
         return 0;
-    }], [ ac_cv_v8_narrowing="" ], [
+    }]])],[ac_cv_v8_narrowing=""],[
         CXXFLAGS="-Wno-c++11-narrowing -std="$ac_cv_v8_cstd
-        AC_TRY_RUN([int main() {
+        AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() {
             struct { unsigned int x; } foo = {-1};
             (void) foo;
             return 0;
-        }], [ ac_cv_v8_narrowing="-Wno-c++11-narrowing" ], [
+        }]])],[ac_cv_v8_narrowing="-Wno-c++11-narrowing"],[
             CXXFLAGS="-Wno-narrowing -std="$ac_cv_v8_cstd
-            AC_TRY_RUN([int main() {
+            AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() {
                 struct { unsigned int x; } foo = {-1};
                 (void) foo;
                 return 0;
-            }], [ ac_cv_v8_narrowing="-Wno-narrowing" ], [
+            }]])],[ac_cv_v8_narrowing="-Wno-narrowing"],[
                 AC_MSG_ERROR([cannot compile with narrowing])
-            ], [])
-        ], [])
-    ], [])
+            ],[])
+        ],[])
+    ],[])
     AC_LANG_POP([C++])
     CXXFLAGS=$old_CXXFLAGS
   ]);
@@ -83,8 +83,7 @@ if test "$PHP_V8JS" != "no"; then
   old_LDFLAGS=$LDFLAGS
   old_CPPFLAGS=$CPPFLAGS
 
-  AC_LANG_SAVE
-  AC_LANG_CPLUSPLUS
+  AC_LANG_PUSH([C++])
 
   CPPFLAGS="$CPPFLAGS -I$V8_DIR/include -std=$ac_cv_v8_cstd"
   LDFLAGS="$LDFLAGS -L$V8_DIR/$PHP_LIBDIR"
@@ -117,7 +116,7 @@ if test "$PHP_V8JS" != "no"; then
   dnl
   LIBS="$LIBS $V8JS_SHARED_LIBADD"
   AC_CACHE_CHECK(for V8 version, ac_cv_v8_version, [
-AC_TRY_RUN([#include <v8.h>
+AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <v8.h>
 #include <iostream>
 #include <fstream>
 using namespace std;
@@ -132,7 +131,7 @@ int main ()
 		return 0;
 	}
 	return 1;
-}], [ac_cv_v8_version=`cat ./conftestval|awk '{print $1}'`], [ac_cv_v8_version=NONE], [ac_cv_v8_version=NONE])
+}]])],[ac_cv_v8_version=`cat ./conftestval|awk '{print $1}'`],[ac_cv_v8_version=NONE],[ac_cv_v8_version=NONE])
 ])
 
   if test "$ac_cv_v8_version" != "NONE"; then
@@ -197,7 +196,7 @@ int main ()
               [Define unless v8::ArrayBuffer::Allocator::NewDefaultAllocator is usable.])
   fi
 
-  AC_LANG_RESTORE
+  AC_LANG_POP([C++])
   LIBS=$old_LIBS
   LDFLAGS="$old_LDFLAGS"
   CPPFLAGS=$old_CPPFLAGS

+ 4 - 4
config.w32

@@ -15,10 +15,10 @@ if (PHP_V8JS != "no") {
 		ADD_FLAG("CFLAGS_V8JS", "/D __STDC_LIMIT_MACROS");
 
 		// defaults
-		var v8major = 5;
-		var v8minor = 8;
-		var v8build = 301;
-		var v8patch = 0;
+		var v8major = 7;
+		var v8minor = 0;
+		var v8build = 276;
+		var v8patch = 11;
 		var v8pinc = search_paths("v8-version.h", php_usual_include_suspects, null);
 		if (typeof(v8pinc) == "string") {
 			var v8versionh = file_get_contents(v8pinc + '\\v8-version.h');

+ 0 - 4
php_v8js_macros.h

@@ -65,10 +65,6 @@ extern "C" {
 /* V8Js Version */
 #define PHP_V8JS_VERSION "2.1.0"
 
-/* Helper macros */
-#define V8JS_GET_CLASS_NAME(var, obj) \
-	v8::String::Utf8Value var(obj->GetConstructorName());
-
 /* Options */
 #define V8JS_FLAG_NONE			(1<<0)
 #define V8JS_FLAG_FORCE_ARRAY	(1<<1)

+ 2 - 0
tests/timezones.phpt

@@ -1,6 +1,8 @@
 --TEST--
 Test V8::executeString() : Check timezone handling
 --SKIPIF--
+SKIP test currently broken, see #378
+
 <?php
 if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
 	die('SKIP TZ not handled by v8 on Windows');

+ 13 - 6
v8js_array_access.cc

@@ -210,20 +210,22 @@ void v8js_array_access_enumerator(const v8::PropertyCallbackInfo<v8::Array>& inf
 
 	for(int j = 0; j < length; j ++) {
 		if(v8js_array_access_isset_p(object, j)) {
-			result->Set(i ++, V8JS_INT(j));
+			result->Set(isolate->GetEnteredContext(), i ++, V8JS_INT(j));
 		}
 	}
 
-	result->Set(V8JS_STR("length"), V8JS_INT(i));
+	result->Set(isolate->GetEnteredContext(), V8JS_SYM("length"), V8JS_INT(i));
 	info.GetReturnValue().Set(result);
 }
 /* }}} */
 
 
 
-void v8js_array_access_named_getter(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
+void v8js_array_access_named_getter(v8::Local<v8::Name> property_name, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
 {
-	v8::String::Utf8Value cstr(property);
+	v8::Local<v8::String> property = v8::Local<v8::String>::Cast(property_name);
+	v8::Isolate *isolate = info.GetIsolate();
+	v8::String::Utf8Value cstr(isolate, property);
 	const char *name = ToCString(cstr);
 
 	if(strcmp(name, "length") == 0) {
@@ -234,7 +236,6 @@ void v8js_array_access_named_getter(v8::Local<v8::String> property, const v8::Pr
 	v8::Local<v8::Value> ret_value = v8js_named_property_callback(property, info, V8JS_PROP_GETTER);
 
 	if(ret_value.IsEmpty()) {
-		v8::Isolate *isolate = info.GetIsolate();
 		v8::Local<v8::Array> arr = v8::Array::New(isolate);
 		v8::Local<v8::Value> prototype = arr->GetPrototype();
 
@@ -243,7 +244,13 @@ void v8js_array_access_named_getter(v8::Local<v8::String> property, const v8::Pr
 			info.GetReturnValue().Set(ret_value);
 		}
 
-		ret_value = prototype->ToObject()->Get(property);
+		v8::Local<v8::Object> prototype_object;
+		if(!prototype->ToObject(isolate->GetEnteredContext()).ToLocal(&prototype_object)) {
+			/* ehh?  Array.prototype not an object? strange, stop. */
+			info.GetReturnValue().Set(ret_value);
+		}
+
+		prototype_object->Get(isolate->GetEnteredContext(), property).ToLocal(&ret_value);
 	}
 
 	info.GetReturnValue().Set(ret_value);

+ 1 - 1
v8js_array_access.h

@@ -25,7 +25,7 @@ void v8js_array_access_query(uint32_t index,
 				 const v8::PropertyCallbackInfo<v8::Integer>& info);
 
 /* Named Property Handlers */
-void v8js_array_access_named_getter(v8::Local<v8::String> property,
+void v8js_array_access_named_getter(v8::Local<v8::Name> property,
 					const v8::PropertyCallbackInfo<v8::Value> &info);
 
 #endif /* V8JS_ARRAY_ACCESS_H */

+ 50 - 33
v8js_class.cc

@@ -102,7 +102,7 @@ static void v8js_free_storage(zend_object *object) /* {{{ */
 		v8::Local<v8::Context> v8_context = v8::Local<v8::Context>::New(c->isolate, c->context);
 		v8::Context::Scope context_scope(v8_context);
 		v8::Local<v8::String> object_name_js = v8::Local<v8::String>::New(c->isolate, c->object_name);
-		V8JS_GLOBAL(c->isolate)->Delete(object_name_js);
+		V8JS_GLOBAL(c->isolate)->Delete(v8_context, object_name_js);
 	}
 
 	c->object_name.Reset();
@@ -494,8 +494,7 @@ static PHP_METHOD(V8Js, __construct)
 			return;
 		}
 
-		object_name_js = v8::String::NewFromUtf8(isolate, ZSTR_VAL(object_name),
-			v8::String::kInternalizedString, static_cast<int>(ZSTR_LEN(object_name)));
+		object_name_js = V8JS_ZSYM(object_name);
 	}
 	else {
 		object_name_js = V8JS_SYM("PHP");
@@ -505,7 +504,7 @@ static PHP_METHOD(V8Js, __construct)
 
 	/* Add the PHP object into global object */
 	php_obj_t->InstanceTemplate()->SetInternalFieldCount(2);
-	v8::Local<v8::Object> php_obj = php_obj_t->InstanceTemplate()->NewInstance();
+	v8::Local<v8::Object> php_obj = php_obj_t->InstanceTemplate()->NewInstance(context).ToLocalChecked();
 	V8JS_GLOBAL(isolate)->DefineOwnProperty(context, object_name_js, php_obj, v8::ReadOnly);
 
 	/* Export public property values */
@@ -524,8 +523,7 @@ static PHP_METHOD(V8Js, __construct)
 				return;
 			}
 
-			v8::Local<v8::Name> key = v8::String::NewFromUtf8(isolate, ZSTR_VAL(member),
-				v8::String::kInternalizedString, static_cast<int>(ZSTR_LEN(member)));
+			v8::Local<v8::Name> key = V8JS_ZSYM(member);
 
 			/* Write value to PHP JS object */
 			value = OBJ_PROP(Z_OBJ_P(getThis()), property_info->offset);
@@ -587,24 +585,16 @@ static PHP_METHOD(V8Js, __construct)
 			return;
 		}
 
-		v8::Local<v8::String> method_name = v8::String::NewFromUtf8(isolate,
-			ZSTR_VAL(method_ptr->common.function_name), v8::String::kInternalizedString,
-			static_cast<int>(ZSTR_LEN(method_ptr->common.function_name)));
+		v8::Local<v8::String> method_name = V8JS_ZSYM(method_ptr->common.function_name);
 		v8::Local<v8::FunctionTemplate> ft;
 
-		/*try {
-			ft = v8::Local<v8::FunctionTemplate>::New
-				(isolate, c->method_tmpls.at(method_ptr));
-		}
-		catch (const std::out_of_range &) */ {
-			ft = v8::FunctionTemplate::New(isolate, v8js_php_callback,
-					v8::External::New((isolate), method_ptr));
-			// @fixme add/check Signature v8::Signature::New((isolate), tmpl));
-			v8js_function_tmpl_t *persistent_ft = &c->method_tmpls[method_ptr];
-			persistent_ft->Reset(isolate, ft);
-		}
+		ft = v8::FunctionTemplate::New(isolate, v8js_php_callback,
+				v8::External::New((isolate), method_ptr));
+		// @fixme add/check Signature v8::Signature::New((isolate), tmpl));
+		v8js_function_tmpl_t *persistent_ft = &c->method_tmpls[method_ptr];
+		persistent_ft->Reset(isolate, ft);
 
-		php_obj->CreateDataProperty(context, method_name, ft->GetFunction());
+		php_obj->CreateDataProperty(context, method_name, ft->GetFunction(context).ToLocalChecked());
 	} ZEND_HASH_FOREACH_END();
 }
 /* }}} */
@@ -646,18 +636,18 @@ static void v8js_compile_script(zval *this_ptr, const zend_string *str, const ze
 	}
 
 	v8::Local<v8::String> sname = identifier
-		? v8::String::NewFromUtf8(isolate, ZSTR_VAL(identifier), v8::String::kNormalString, static_cast<int>(ZSTR_LEN(identifier)))
+		? V8JS_ZSTR(identifier)
 		: V8JS_SYM("V8Js::compileString()");
+	v8::ScriptOrigin origin(sname);
 
-	/* Compiles a string context independently. TODO: Add a php function which calls this and returns the result as resource which can be executed later. */
 	if (ZSTR_LEN(str) > std::numeric_limits<int>::max()) {
 		zend_throw_exception(php_ce_v8js_exception,
 			"Script source exceeds maximum supported length", 0);
 		return;
 	}
 
-	v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, ZSTR_VAL(str), v8::String::kNormalString, static_cast<int>(ZSTR_LEN(str)));
-	v8::Local<v8::Script> script = v8::Script::Compile(source, sname);
+	v8::Local<v8::String> source = V8JS_ZSTR(str);
+	v8::MaybeLocal<v8::Script> script = v8::Script::Compile(v8::Local<v8::Context>::New(isolate, c->context), source, &origin);
 
 	/* Compile errors? */
 	if (script.IsEmpty()) {
@@ -665,9 +655,9 @@ static void v8js_compile_script(zval *this_ptr, const zend_string *str, const ze
 		return;
 	}
 	res = (v8js_script *)emalloc(sizeof(v8js_script));
-	res->script = new v8::Persistent<v8::Script, v8::CopyablePersistentTraits<v8::Script>>(c->isolate, script);
+	res->script = new v8::Persistent<v8::Script, v8::CopyablePersistentTraits<v8::Script>>(c->isolate, script.ToLocalChecked());
 
-	v8::String::Utf8Value _sname(sname);
+	v8::String::Utf8Value _sname(isolate, sname);
 	res->name = estrndup(ToCString(_sname), _sname.length());
 	res->ctx = c;
 	*ret = res;
@@ -695,9 +685,9 @@ static void v8js_execute_script(zval *this_ptr, v8js_script *res, long flags, lo
 	/* std::function relies on its dtor to be executed, otherwise it leaks
 	 * some memory on bailout. */
 	{
-		std::function< v8::Local<v8::Value>(v8::Isolate *) > v8_call = [res](v8::Isolate *isolate) {
+		std::function< v8::MaybeLocal<v8::Value>(v8::Isolate *) > v8_call = [c, res](v8::Isolate *isolate) {
 			v8::Local<v8::Script> script = v8::Local<v8::Script>::New(isolate, *res->script);
-			return script->Run();
+			return script->Run(v8::Local<v8::Context>::New(isolate, c->context));
 		};
 
 		v8js_v8_call(c, return_value, flags, time_limit, memory_limit, v8_call);
@@ -1153,6 +1143,31 @@ static PHP_METHOD(V8Js, getExtensions)
 }
 /* }}} */
 
+static v8::StartupData createSnapshotDataBlob(v8::SnapshotCreator *snapshot_creator, zend_string *str) /* {{{ */
+{
+	v8::Isolate *isolate = snapshot_creator->GetIsolate();
+
+	{
+		v8::HandleScope scope(isolate);
+		v8::Local<v8::Context> context = v8::Context::New(isolate);
+
+		v8::Context::Scope context_scope(context);
+		v8::TryCatch try_catch(isolate);
+
+		v8::Local<v8::String> source = V8JS_ZSTR(str);
+		v8::MaybeLocal<v8::Script> script = v8::Script::Compile(context, source);
+
+		if (script.IsEmpty() || script.ToLocalChecked()->Run(context).IsEmpty())
+		{
+			return {nullptr, 0};
+		}
+
+		snapshot_creator->SetDefaultContext(context);
+	}
+
+	return snapshot_creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
+} /* }}} */
+
 
 /* {{{ proto string|bool V8Js::createSnapshot(string embed_source)
  */
@@ -1172,7 +1187,9 @@ static PHP_METHOD(V8Js, createSnapshot)
 	/* Initialize V8, if not already done. */
 	v8js_v8_init();
 
-	v8::StartupData snapshot_blob = v8::V8::CreateSnapshotDataBlob(ZSTR_VAL(script));
+	v8::Isolate *isolate = v8::Isolate::Allocate();
+	v8::SnapshotCreator snapshot_creator(isolate);
+	v8::StartupData snapshot_blob = createSnapshotDataBlob(&snapshot_creator, script);
 
 	if (!snapshot_blob.data) {
 		php_error_docref(NULL, E_WARNING, "Failed to create V8 heap snapshot.  Check $embed_source for errors.");
@@ -1303,7 +1320,7 @@ static void v8js_write_property(zval *object, zval *member, zval *value, void **
 		(property_info->flags & ZEND_ACC_PUBLIC))) {
 		/* Global PHP JS object */
 		v8::Local<v8::String> object_name_js = v8::Local<v8::String>::New(isolate, c->object_name);
-		v8::Local<v8::Object> jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject();
+		v8::Local<v8::Object> jsobj = V8JS_GLOBAL(isolate)->Get(v8_context, object_name_js).ToLocalChecked()->ToObject(v8_context).ToLocalChecked();
 
 		if (Z_STRLEN_P(member) > std::numeric_limits<int>::max()) {
 				zend_throw_exception(php_ce_v8js_exception,
@@ -1327,7 +1344,7 @@ static void v8js_unset_property(zval *object, zval *member, void **cache_slot) /
 
 	/* Global PHP JS object */
 	v8::Local<v8::String> object_name_js = v8::Local<v8::String>::New(isolate, c->object_name);
-	v8::Local<v8::Object> jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject();
+	v8::Local<v8::Object> jsobj = V8JS_GLOBAL(isolate)->Get(v8_context, object_name_js).ToLocalChecked()->ToObject(v8_context).ToLocalChecked();
 
 	if (Z_STRLEN_P(member) > std::numeric_limits<int>::max()) {
 			zend_throw_exception(php_ce_v8js_exception,
@@ -1337,7 +1354,7 @@ static void v8js_unset_property(zval *object, zval *member, void **cache_slot) /
 
 	/* Delete value from PHP JS object */
 	v8::Local<v8::Value> key = V8JS_SYML(Z_STRVAL_P(member), static_cast<int>(Z_STRLEN_P(member)));
-	jsobj->Delete(key);
+	jsobj->Delete(v8_context, key);
 
 	/* Unset from PHP object */
 	std_object_handlers.unset_property(object, member, NULL);

+ 34 - 11
v8js_convert.cc

@@ -79,6 +79,7 @@ static v8::Local<v8::Value> v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate
 		return V8JS_NULL;
 	}
 
+	v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
 	newarr = v8::Array::New(isolate, i);
 
 	if (i > 0)
@@ -96,7 +97,7 @@ static v8::Local<v8::Value> v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate
 		}
 
 		ZEND_HASH_FOREACH_VAL(myht, data) {
-			newarr->Set(index++, zval_to_v8js(data, isolate));
+			newarr->Set(v8_context, index++, zval_to_v8js(data, isolate));
 		} ZEND_HASH_FOREACH_END();
 
 #if PHP_VERSION_ID >= 70300
@@ -114,7 +115,7 @@ static v8::Local<v8::Value> v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate
 
 v8::Local<v8::Value> zend_long_to_v8js(zend_long v, v8::Isolate *isolate) /* {{{ */
 {
-	if (v < - std::numeric_limits<int32_t>::min() || v > std::numeric_limits<int32_t>::max()) {
+	if (v < std::numeric_limits<int32_t>::min() || v > std::numeric_limits<int32_t>::max()) {
 		return V8JS_FLOAT(static_cast<double>(v));
 	} else {
 		return V8JS_INT(static_cast<int32_t>(v));
@@ -148,7 +149,7 @@ v8::Local<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate) /* {{{ */
 				 if (instanceof_function(Z_OBJCE_P(value), ce)) {
 					 zval dtval;
 					 zend_call_method_with_0_params(value, NULL, NULL, "getTimestamp", &dtval);
-					 jsValue = V8JS_DATE(((double)Z_LVAL(dtval) * 1000.0));
+					 v8::Date::New(isolate->GetEnteredContext(), ((double)Z_LVAL(dtval) * 1000.0)).ToLocal(&jsValue);
 					 zval_dtor(&dtval);
 				 } else
 					 jsValue = v8js_hash_to_jsobj(value, isolate);
@@ -164,7 +165,7 @@ v8::Local<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate) /* {{{ */
 				break;
 			}
 
-			jsValue = v8::String::NewFromUtf8(isolate, ZSTR_VAL(value_str), v8::String::kNormalString, static_cast<int>(ZSTR_LEN(value_str)));
+			jsValue = V8JS_ZSTR(value_str);
 			break;
 
 		case IS_LONG:
@@ -200,27 +201,45 @@ v8::Local<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate) /* {{{ */
 
 int v8js_to_zval(v8::Local<v8::Value> jsValue, zval *return_value, int flags, v8::Isolate *isolate) /* {{{ */
 {
+	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+	v8::Local<v8::Context> v8_context = v8::Local<v8::Context>::New(isolate, ctx->context);
+
 	if (jsValue->IsString())
 	{
-		v8::String::Utf8Value str(jsValue);
+		v8::String::Utf8Value str(isolate, jsValue);
 		const char *cstr = ToCString(str);
-		RETVAL_STRINGL(cstr, jsValue->ToString()->Utf8Length());
+		RETVAL_STRINGL(cstr, str.length());
 	}
 	else if (jsValue->IsBoolean())
 	{
-		RETVAL_BOOL(jsValue->Uint32Value());
+		v8::Maybe<bool> value = jsValue->BooleanValue(v8_context);
+		if (value.IsNothing())
+		{
+			return FAILURE;
+		}
+		RETVAL_BOOL(value.ToChecked());
 	}
 	else if (jsValue->IsInt32() || jsValue->IsUint32())
 	{
-		RETVAL_LONG((long) jsValue->IntegerValue());
+		v8::Maybe<int64_t> value = jsValue->IntegerValue(v8_context);
+		if (value.IsNothing())
+		{
+			return FAILURE;
+		}
+		RETVAL_LONG((long) value.ToChecked());
 	}
 	else if (jsValue->IsNumber())
 	{
-		RETVAL_DOUBLE(jsValue->NumberValue());
+		v8::Maybe<double> value = jsValue->NumberValue(v8_context);
+		if (value.IsNothing())
+		{
+			return FAILURE;
+		}
+		RETVAL_DOUBLE(value.ToChecked());
 	}
 	else if (jsValue->IsDate())	/* Return as a PHP DateTime object */
 	{
-		v8::String::Utf8Value str(jsValue);
+		v8::String::Utf8Value str(isolate, jsValue);
 		const char *cstr = ToCString(str);
 
 		/* cstr has two timezone specifications:
@@ -253,7 +272,11 @@ int v8js_to_zval(v8::Local<v8::Value> jsValue, zval *return_value, int flags, v8
 	}
 	else if (jsValue->IsObject())
 	{
-		v8::Local<v8::Object> self = jsValue->ToObject();
+		v8::Local<v8::Object> self;
+		if (!jsValue->ToObject(v8_context).ToLocal(&self))
+		{
+			return FAILURE;
+		}
 
 		// if this is a wrapped PHP object, then just unwrap it.
 		if (self->InternalFieldCount() == 2) {

+ 29 - 19
v8js_exceptions.cc

@@ -39,12 +39,14 @@ zend_class_entry *php_ce_v8js_memory_limit_exception;
 
 void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::TryCatch *try_catch) /* {{{ */
 {
-	v8::String::Utf8Value exception(try_catch->Exception());
+	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+	v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate, ctx->context);
+
+	v8::String::Utf8Value exception(isolate, try_catch->Exception());
 	const char *exception_string = ToCString(exception);
 	v8::Local<v8::Message> tc_message = try_catch->Message();
 	const char *filename_string, *sourceline_string;
 	char *message_string;
-	int linenum, start_col;
 
 	object_init_ex(return_value, php_ce_v8js_script_exception);
 
@@ -56,35 +58,43 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::
 	}
 	else
 	{
-		v8::String::Utf8Value filename(tc_message->GetScriptResourceName());
+		v8::String::Utf8Value filename(isolate, tc_message->GetScriptResourceName());
 		filename_string = ToCString(filename);
 		PHPV8_EXPROP(_string, JsFileName, filename_string);
 
-		v8::String::Utf8Value sourceline(tc_message->GetSourceLine());
-		sourceline_string = ToCString(sourceline);
-		PHPV8_EXPROP(_string, JsSourceLine, sourceline_string);
+		v8::MaybeLocal<v8::String> maybe_sourceline = tc_message->GetSourceLine(context);
+		if (!maybe_sourceline.IsEmpty()) {
+			v8::String::Utf8Value sourceline(isolate, maybe_sourceline.ToLocalChecked());
+			sourceline_string = ToCString(sourceline);
+			PHPV8_EXPROP(_string, JsSourceLine, sourceline_string);
+		}
 
-		linenum = tc_message->GetLineNumber();
-		PHPV8_EXPROP(_long, JsLineNumber, linenum);
+		v8::Maybe<int> linenum = tc_message->GetLineNumber(context);
+		if (linenum.IsJust()) {
+			PHPV8_EXPROP(_long, JsLineNumber, linenum.FromJust());
+		}
 
-		start_col = tc_message->GetStartColumn();
-		PHPV8_EXPROP(_long, JsStartColumn, start_col);
+		v8::Maybe<int> start_col = tc_message->GetStartColumn(context);
+		if (start_col.IsJust()) {
+			PHPV8_EXPROP(_long, JsStartColumn, start_col.FromJust());
+		}
 
-		v8::Maybe<int> end_col = tc_message->GetEndColumn(isolate->GetEnteredContext());
+		v8::Maybe<int> end_col = tc_message->GetEndColumn(context);
 		if (end_col.IsJust()) {
 			PHPV8_EXPROP(_long, JsEndColumn, end_col.FromJust());
 		}
 
-		spprintf(&message_string, 0, "%s:%d: %s", filename_string, linenum, exception_string);
+		spprintf(&message_string, 0, "%s:%d: %s", filename_string, linenum.FromMaybe(0), exception_string);
 
-		v8::String::Utf8Value stacktrace(try_catch->StackTrace());
-		if (stacktrace.length() > 0) {
-			const char* stacktrace_string = ToCString(stacktrace);
-			PHPV8_EXPROP(_string, JsTrace, stacktrace_string);
+		v8::MaybeLocal<v8::Value> maybe_stacktrace = try_catch->StackTrace(context);
+		if (!maybe_stacktrace.IsEmpty()) {
+			v8::String::Utf8Value stacktrace(isolate, maybe_stacktrace.ToLocalChecked());
+			PHPV8_EXPROP(_string, JsTrace, ToCString(stacktrace));
 		}
 
-		if(try_catch->Exception()->IsObject() && try_catch->Exception()->ToObject()->InternalFieldCount() == 2) {
-			zend_object *php_exception = reinterpret_cast<zend_object *>(try_catch->Exception()->ToObject()->GetAlignedPointerFromInternalField(1));
+		v8::Local<v8::Object> error_object;
+		if(try_catch->Exception()->IsObject() && try_catch->Exception()->ToObject(context).ToLocal(&error_object) && error_object->InternalFieldCount() == 2) {
+			zend_object *php_exception = reinterpret_cast<zend_object *>(error_object->GetAlignedPointerFromInternalField(1));
 
 			zend_class_entry *exception_ce = zend_exception_get_default();
 			if (instanceof_function(php_exception->ce, exception_ce)) {
@@ -106,7 +116,7 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::
 
 void v8js_throw_script_exception(v8::Isolate *isolate, v8::TryCatch *try_catch) /* {{{ */
 {
-	v8::String::Utf8Value exception(try_catch->Exception());
+	v8::String::Utf8Value exception(isolate, try_catch->Exception());
 	const char *exception_string = ToCString(exception);
 	zval zexception;
 

+ 9 - 6
v8js_generator_export.cc

@@ -24,8 +24,11 @@ v8::Local<v8::Value> v8js_wrap_generator(v8::Isolate *isolate, v8::Local<v8::Val
 	assert(!wrapped_object.IsEmpty());
 	assert(wrapped_object->IsObject());
 
+	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+    v8::Local<v8::Context> v8_context = v8::Local<v8::Context>::New(isolate, ctx->context);
+
 	v8::TryCatch try_catch(isolate);
-	v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, "(\
+	v8::Local<v8::String> source = V8JS_SYM("(\
 function(wrapped_object) {					\
 	return (function*() {					\
 		for(;;) {							\
@@ -37,27 +40,27 @@ function(wrapped_object) {					\
 		}									\
 	})();									\
 })");
-	v8::Local<v8::Script> script = v8::Script::Compile(source);
+	v8::MaybeLocal<v8::Script> script = v8::Script::Compile(v8_context, source);
 
 	if(script.IsEmpty()) {
 		zend_error(E_ERROR, "Failed to compile Generator object wrapper");
 		return result;
 	}
 
-	v8::Local<v8::Value> wrapper_fn_val = script->Run();
+	v8::MaybeLocal<v8::Value> wrapper_fn_val = script.ToLocalChecked()->Run(v8_context);
 
-	if(wrapper_fn_val.IsEmpty() || !wrapper_fn_val->IsFunction()) {
+	if(wrapper_fn_val.IsEmpty() || !wrapper_fn_val.ToLocalChecked()->IsFunction()) {
 		zend_error(E_ERROR, "Failed to create Generator object wrapper function");
 		return result;
 	}
 
-	v8::Local<v8::Function> wrapper_fn = v8::Local<v8::Function>::Cast(wrapper_fn_val);
+	v8::Local<v8::Function> wrapper_fn = v8::Local<v8::Function>::Cast(wrapper_fn_val.ToLocalChecked());
 	v8::Local<v8::Value> *jsArgv = static_cast<v8::Local<v8::Value> *>(alloca(sizeof(v8::Local<v8::Value>)));
 
 	new(&jsArgv[0]) v8::Local<v8::Value>;
 	jsArgv[0] = v8::Local<v8::Value>::New(isolate, wrapped_object);
 
-	result = wrapper_fn->Call(V8JS_GLOBAL(isolate), 1, jsArgv);
+	wrapper_fn->Call(v8_context, V8JS_GLOBAL(isolate), 1, jsArgv).ToLocal(&result);
 	return result;
 }
 /* }}} */

+ 117 - 35
v8js_methods.cc

@@ -35,7 +35,14 @@ V8JS_METHOD(exit) /* {{{ */
 /* global.sleep - sleep for passed seconds */
 V8JS_METHOD(sleep) /* {{{ */
 {
-	php_sleep(info[0]->Int32Value());
+	v8::Isolate *isolate = info.GetIsolate();
+	v8js_ctx *c = (v8js_ctx *) isolate->GetData(0);
+
+	v8::Maybe<int32_t> t = info[0]->Int32Value(v8::Local<v8::Context>::New(isolate, c->context));
+
+	if (t.IsJust()) {
+		php_sleep(t.FromJust());
+	}
 }
 /* }}} */
 
@@ -46,7 +53,7 @@ V8JS_METHOD(print) /* {{{ */
 	zend_long ret = 0;
 
 	for (int i = 0; i < info.Length(); i++) {
-		v8::String::Utf8Value str(info[i]);
+		v8::String::Utf8Value str(isolate, info[i]);
 		const char *cstr = ToCString(str);
 		ret = PHPWRITE(cstr, strlen(cstr));
 	}
@@ -57,6 +64,9 @@ V8JS_METHOD(print) /* {{{ */
 
 static void v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int level) /* {{{ */
 {
+	v8js_ctx *c = (v8js_ctx *) isolate->GetData(0);
+	v8::Local<v8::Context> v8_context = v8::Local<v8::Context>::New(isolate, c->context);
+
 	if (level > 1) {
 		php_printf("%*c", (level - 1) * 2, ' ');
 	}
@@ -73,22 +83,54 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int leve
 	}
 	if (var->IsInt32())
 	{
-		php_printf("int(%ld)\n", (long) var->IntegerValue());
+		v8::Maybe<int64_t> value = var->IntegerValue(v8_context);
+		if (value.IsNothing())
+		{
+			php_printf("<empty>\n");
+		}
+		else
+		{
+			php_printf("int(%ld)\n", (long) value.FromJust());
+		}
 		return;
 	}
 	if (var->IsUint32())
 	{
-		php_printf("int(%lu)\n", (unsigned long) var->IntegerValue());
+		v8::Maybe<uint32_t> value = var->Uint32Value(v8_context);
+		if (value.IsNothing())
+		{
+			php_printf("<empty>\n");
+		}
+		else
+		{
+			php_printf("int(%lu)\n", (unsigned long) value.FromJust());
+		}
 		return;
 	}
 	if (var->IsNumber())
 	{
-		php_printf("float(%f)\n", var->NumberValue());
+		v8::Maybe<double> value = var->NumberValue(v8_context);
+		if (value.IsNothing())
+		{
+			php_printf("<empty>\n");
+		}
+		else
+		{
+			php_printf("float(%f)\n", value.FromJust());
+		}
 		return;
 	}
 	if (var->IsBoolean())
 	{
-		php_printf("bool(%s)\n", var->BooleanValue() ? "true" : "false");
+		v8::Maybe<bool> value = var->BooleanValue(v8_context);
+		if (value.IsNothing())
+		{
+			php_printf("<empty>\n");
+		}
+		else
+		{
+			php_printf("bool(%s)\n", value.FromJust() ? "true" : "false");
+		}
 		return;
 	}
 
@@ -100,16 +142,16 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int leve
 		details = re->GetSource();
 	}
 	else {
-		details = var->ToDetailString(isolate->GetEnteredContext()).FromMaybe(v8::Local<v8::String>());
+		details = var->ToDetailString(v8_context).FromMaybe(v8::Local<v8::String>());
 
 		if (try_catch.HasCaught()) {
 			details = V8JS_SYM("<toString threw exception>");
 		}
 	}
 
-	v8::String::Utf8Value str(details);
+	v8::String::Utf8Value str(isolate, details);
 	const char *valstr = ToCString(str);
-	size_t valstr_len = details->ToString()->Utf8Length();
+	size_t valstr_len = str.length();
 
 	if (var->IsString())
 	{
@@ -135,7 +177,16 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int leve
 
 		for (unsigned i = 0; i < length; i++) {
 			php_printf("%*c[%d] =>\n", level * 2, ' ', i);
-			v8js_dumper(isolate, array->Get(i), level + 1);
+
+			v8::MaybeLocal<v8::Value> value = array->Get(v8_context, i);
+			if (value.IsEmpty())
+			{
+				php_printf("<empty>\n");
+			}
+			else
+			{
+				v8js_dumper(isolate, value.ToLocalChecked(), level + 1);
+			}
 		}
 
 		if (level > 1) {
@@ -147,18 +198,28 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int leve
 	else if (var->IsObject())
 	{
 		v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(var);
-		V8JS_GET_CLASS_NAME(cname, object);
+		v8::String::Utf8Value cname(isolate, object->GetConstructorName());
 		int hash = object->GetIdentityHash();
 
 		if (var->IsFunction() && strcmp(ToCString(cname), "Closure") != 0)
 		{
-			v8::String::Utf8Value csource(object->ToString());
-			php_printf("object(Closure)#%d {\n%*c%s\n", hash, level * 2 + 2, ' ', ToCString(csource));
+			php_printf("object(Closure)#%d {\n%*c", hash, level * 2 + 2, ' ');
+
+			v8::Local<v8::String> source;
+			if (object->ToString(v8_context).ToLocal(&source))
+			{
+				v8::String::Utf8Value csource(isolate, source);
+				php_printf("%s\n",  ToCString(csource));
+			}
+			else
+			{
+				php_printf("<empty>\n");
+			}
 		}
 		else
 		{
-			v8::Local<v8::Array> keys = object->GetOwnPropertyNames();
-			uint32_t length = keys->Length();
+			v8::MaybeLocal<v8::Array> keys = object->GetOwnPropertyNames(v8_context);
+			uint32_t length = keys.IsEmpty() ? 0 : keys.ToLocalChecked()->Length();
 
 			if (strcmp(ToCString(cname), "Array") == 0 ||
 				strcmp(ToCString(cname), "V8Object") == 0) {
@@ -169,10 +230,26 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int leve
 			php_printf(" (%d) {\n", length);
 
 			for (unsigned i = 0; i < length; i++) {
-				v8::Local<v8::String> key = keys->Get(i)->ToString();
-				v8::String::Utf8Value kname(key);
-				php_printf("%*c[\"%s\"] =>\n", level * 2, ' ', ToCString(kname));
-				v8js_dumper(isolate, object->Get(key), level + 1);
+				v8::MaybeLocal<v8::Value> key_slot = keys.ToLocalChecked()->Get(v8_context, i);
+				v8::Local<v8::String> key;
+
+				if (key_slot.IsEmpty() || !key_slot.ToLocalChecked()->ToString(v8_context).ToLocal(&key))
+				{
+					key = V8JS_SYM("<empty>");
+				}
+
+				v8::String::Utf8Value key_name(isolate, key);
+				php_printf("%*c[\"%s\"] =>\n", level * 2, ' ', ToCString(key_name));
+
+				v8::MaybeLocal<v8::Value> value = object->Get(v8_context, key);
+				if (value.IsEmpty())
+				{
+					php_printf("<empty>\n");
+				}
+				else
+				{
+					v8js_dumper(isolate, value.ToLocalChecked(), level + 1);
+				}
 			}
 		}
 
@@ -207,7 +284,7 @@ V8JS_METHOD(require)
 	v8::Isolate *isolate = info.GetIsolate();
 	v8js_ctx *c = (v8js_ctx *) isolate->GetData(0);
 
-	v8::String::Utf8Value module_base(info.Data());
+	v8::String::Utf8Value module_base(isolate, info.Data());
 	const char *module_base_cstr = ToCString(module_base);
 
 	// Check that we have a module loader
@@ -216,7 +293,7 @@ V8JS_METHOD(require)
 		return;
 	}
 
-	v8::String::Utf8Value module_id_v8(info[0]);
+	v8::String::Utf8Value module_id_v8(isolate, info[0]);
 	const char *module_id = ToCString(module_id_v8);
 	char *normalised_path, *module_name;
 
@@ -343,7 +420,9 @@ V8JS_METHOD(require)
 	if (c->modules_loaded.count(normalised_module_id) > 0) {
 		v8::Persistent<v8::Value> newobj;
 		newobj.Reset(isolate, c->modules_loaded[normalised_module_id]);
-		info.GetReturnValue().Set(newobj);
+
+		// TODO store v8::Global in c->modules_loaded directly!?
+		info.GetReturnValue().Set(v8::Global<v8::Value>(isolate, newobj));
 
 		efree(normalised_module_id);
 		efree(normalised_path);
@@ -407,7 +486,7 @@ V8JS_METHOD(require)
 	}
 
 	if(Z_TYPE(module_code) == IS_OBJECT) {
-		v8::Local<v8::Object> newobj = zval_to_v8js(&module_code, isolate)->ToObject();
+		v8::Local<v8::Object> newobj = zval_to_v8js(&module_code, isolate)->ToObject(isolate->GetEnteredContext()).ToLocalChecked();
 		c->modules_loaded[normalised_module_id].Reset(isolate, newobj);
 		info.GetReturnValue().Set(newobj);
 
@@ -435,6 +514,7 @@ V8JS_METHOD(require)
 
 	// Set script identifier
 	v8::Local<v8::String> sname = V8JS_STR(normalised_module_id);
+	v8::ScriptOrigin origin(sname);
 
 	if (Z_STRLEN(module_code) > std::numeric_limits<int>::max()) {
 		zend_throw_exception(php_ce_v8js_exception,
@@ -445,11 +525,11 @@ 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, require) {"), source);
-	source = v8::String::Concat(source, V8JS_SYM("\n});"));
+	source = v8::String::Concat(isolate, V8JS_SYM("(function (exports, module, require) {"), source);
+	source = v8::String::Concat(isolate, source, V8JS_SYM("\n});"));
 
 	// Create and compile script
-	v8::Local<v8::Script> script = v8::Script::Compile(source, sname);
+	v8::MaybeLocal<v8::Script> script = v8::Script::Compile(context, source, &origin);
 
 	// The script will be empty if there are compile errors
 	if (script.IsEmpty()) {
@@ -460,7 +540,7 @@ V8JS_METHOD(require)
 	}
 
 	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();
+	v8::MaybeLocal<v8::Function> require_fn = v8::FunctionTemplate::New(isolate, V8JS_MN(require), base_path)->GetFunction(context);
 
 	if (require_fn.IsEmpty()) {
 		efree(normalised_path);
@@ -474,16 +554,16 @@ V8JS_METHOD(require)
 	c->modules_stack.push_back(normalised_module_id);
 
 	// Run script to evaluate closure
-	v8::Local<v8::Value> module_function = script->Run();
+	v8::MaybeLocal<v8::Value> module_function = script.ToLocalChecked()->Run(context);
 
 	// Prepare exports & module object
 	v8::Local<v8::Object> exports = v8::Object::New(isolate);
 
 	v8::Local<v8::Object> module = v8::Object::New(isolate);
-	module->Set(V8JS_SYM("id"), V8JS_STR(normalised_module_id));
-	module->Set(V8JS_SYM("exports"), exports);
+	module->Set(context, V8JS_SYM("id"), V8JS_STR(normalised_module_id));
+	module->Set(context, V8JS_SYM("exports"), exports);
 
-	if (module_function->IsFunction()) {
+	if (!module_function.IsEmpty() && module_function.ToLocalChecked()->IsFunction()) {
 		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;
@@ -495,7 +575,7 @@ V8JS_METHOD(require)
 		jsArgv[2] = require_fn.ToLocalChecked();
 
 		// actually call the module
-		v8::Local<v8::Function>::Cast(module_function)->Call(exports, 3, jsArgv);
+		v8::Local<v8::Function>::Cast(module_function.ToLocalChecked())->Call(context, exports, 3, jsArgv);
 	}
 
 	// Remove this module and path from the stack
@@ -503,7 +583,7 @@ V8JS_METHOD(require)
 
 	efree(normalised_path);
 
-	if (!module_function->IsFunction()) {
+	if (module_function.IsEmpty() || !module_function.ToLocalChecked()->IsFunction()) {
 		info.GetReturnValue().Set(isolate->ThrowException(V8JS_SYM("Wrapped module script failed to return function")));
 		efree(normalised_module_id);
 		return;
@@ -528,8 +608,10 @@ 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(V8JS_SYM("exports"))) {
-		newobj = module->Get(V8JS_SYM("exports"));
+	v8::Local<v8::String> sym_exports = V8JS_SYM("exports");
+	if (module->Has(context, sym_exports).FromMaybe(false))
+	{
+		module->Get(context, sym_exports).ToLocal(&newobj);
 	}
 
 	c->modules_loaded[normalised_module_id].Reset(isolate, newobj);

+ 123 - 55
v8js_object_export.cc

@@ -35,8 +35,10 @@ extern "C" {
 static void v8js_weak_object_callback(const v8::WeakCallbackInfo<zend_object> &data);
 
 /* Callback for PHP methods and functions */
-static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v8::Isolate *isolate, const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
+static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 {
+	v8::Isolate *isolate = info.GetIsolate();
+	v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
 	v8::Local<v8::Value> return_value = V8JS_NULL;
 	zend_fcall_info fci;
 	zend_fcall_info_cache fcc;
@@ -97,16 +99,24 @@ static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v
 	}
 
 	/* Convert parameters passed from V8 */
-	if (argc) {
+	if (argc)
+	{
 		fci.params = (zval *) safe_emalloc(argc, sizeof(zval), 0);
-		for (i = 0; i < argc; i++) {
-			if (info[i]->IsObject() && info[i]->ToObject()->InternalFieldCount() == 2) {
+		for (i = 0; i < argc; i++)
+		{
+			v8::Local<v8::Object> param_object;
+
+			if (info[i]->IsObject() && info[i]->ToObject(v8_context).ToLocal(&param_object) && param_object->InternalFieldCount() == 2)
+			{
 				/* This is a PHP object, passed to JS and back. */
-				zend_object *object = reinterpret_cast<zend_object *>(info[i]->ToObject()->GetAlignedPointerFromInternalField(1));
+				zend_object *object = reinterpret_cast<zend_object *>(param_object->GetAlignedPointerFromInternalField(1));
 				ZVAL_OBJ(&fci.params[i], object);
 				Z_ADDREF_P(&fci.params[i]);
-			} else {
-				if (v8js_to_zval(info[i], &fci.params[i], ctx->flags, isolate) == FAILURE) {
+			}
+			else
+			{
+				if (v8js_to_zval(info[i], &fci.params[i], ctx->flags, isolate) == FAILURE)
+				{
 					error_len = spprintf(&error, 0, "converting parameter #%d passed to %s() failed", i + 1, method_ptr->common.function_name);
 
 					if (error_len > std::numeric_limits<int>::max()) {
@@ -190,7 +200,6 @@ failure:
 /* Callback for PHP methods and functions */
 void v8js_php_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 {
-	v8::Isolate *isolate = info.GetIsolate();
 	v8::Local<v8::Object> self = info.Holder();
 
 	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
@@ -203,7 +212,7 @@ void v8js_php_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ *
 		method_ptr = zend_get_closure_invoke_method(object);
 	}
 
-	return v8js_call_php_func(object, method_ptr, isolate, info);
+	return v8js_call_php_func(object, method_ptr, info);
 }
 
 /* Callback for PHP constructor calls */
@@ -220,8 +229,16 @@ static void v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& i
 	}
 
 	v8::Local<v8::Array> cons_data = v8::Local<v8::Array>::Cast(info.Data());
-	v8::Local<v8::External> ext_tmpl = v8::Local<v8::External>::Cast(cons_data->Get(0));
-	v8::Local<v8::External> ext_ce =  v8::Local<v8::External>::Cast(cons_data->Get(1));
+	v8::Local<v8::Value> cons_tmpl, cons_ce;
+
+	if (!cons_data->Get(isolate->GetEnteredContext(), 0).ToLocal(&cons_tmpl)
+			||!cons_data->Get(isolate->GetEnteredContext(), 1).ToLocal(&cons_ce))
+	{
+		return;
+	}
+
+	v8::Local<v8::External> ext_tmpl = v8::Local<v8::External>::Cast(cons_tmpl);
+	v8::Local<v8::External> ext_ce =  v8::Local<v8::External>::Cast(cons_ce);
 
 	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
 
@@ -257,7 +274,7 @@ static void v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& i
 
 		// Call __construct function
 		if (ctor_ptr != NULL) {
-			v8js_call_php_func(Z_OBJ(value), ctor_ptr, isolate, info);
+			v8js_call_php_func(Z_OBJ(value), ctor_ptr, info);
 		}
 	}
 
@@ -311,13 +328,15 @@ static void v8js_weak_closure_callback(const v8::WeakCallbackInfo<v8js_function_
 	 !strncasecmp(ZSTR_VAL(key), mname, ZSTR_LEN(key)))
 
 #define PHP_V8JS_CALLBACK(isolate, mptr, tmpl)										\
-	v8::FunctionTemplate::New((isolate), v8js_php_callback, v8::External::New((isolate), mptr), v8::Signature::New((isolate), tmpl))->GetFunction()
+	(v8::FunctionTemplate::New((isolate), v8js_php_callback, v8::External::New((isolate), mptr), v8::Signature::New((isolate), tmpl))->GetFunction(isolate->GetEnteredContext()).ToLocalChecked())
 
 
 static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo<v8::Array> &info) /* {{{ */
 {
 	// note: 'special' properties like 'constructor' are not enumerated.
 	v8::Isolate *isolate = info.GetIsolate();
+	v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
+
 	v8::Local<v8::Object> self = info.Holder();
 	v8::Local<v8::Array> result = v8::Array::New(isolate, 0);
 	uint32_t result_len = 0;
@@ -372,7 +391,7 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo<v8::Ar
 			method_name = V8JS_STRL(ZSTR_VAL(method_ptr->common.function_name), static_cast<int>(ZSTR_LEN(method_ptr->common.function_name)));
 		}
 
-		result->Set(result_len++, method_name);
+		result->Set(v8_context, result_len++, method_name);
 	} ZEND_HASH_FOREACH_END();
 
 	/* enumerate all properties */
@@ -400,11 +419,11 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo<v8::Ar
 			char *prefixed = static_cast<char *>(emalloc(ZSTR_LEN(key) + 2));
 			prefixed[0] = '$';
 			strncpy(prefixed + 1, ZSTR_VAL(key), ZSTR_LEN(key) + 1);
-			result->Set(result_len++, V8JS_STRL(prefixed, static_cast<int>(ZSTR_LEN(key) + 1)));
+			result->Set(v8_context, result_len++, V8JS_STRL(prefixed, static_cast<int>(ZSTR_LEN(key) + 1)));
 			efree(prefixed);
 		} else {
 			// even numeric indices are enumerated as strings in JavaScript
-			result->Set(result_len++, V8JS_FLOAT((double) index)->ToString());
+			result->Set(v8_context, result_len++, V8JS_FLOAT((double) index)->ToString(v8_context).ToLocalChecked());
 		}
 	} ZEND_HASH_FOREACH_END();
 
@@ -416,6 +435,8 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo<v8::Ar
 static void v8js_invoke_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 {
 	v8::Isolate *isolate = info.GetIsolate();
+	v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
+
 	v8::Local<v8::Object> self = info.Holder();
 	v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(info.Data());
 	int argc = info.Length(), i;
@@ -430,9 +451,9 @@ static void v8js_invoke_callback(const v8::FunctionCallbackInfo<v8::Value>& info
 	if (info.IsConstructCall()) {
 		v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
 
-		v8::Local<v8::String> str = self->GetConstructorName()->ToString();
-		v8::String::Utf8Value str_value(str);
-		zend_string *constructor_name = zend_string_init(ToCString(str_value), str->Utf8Length(), 0);
+		v8::Local<v8::String> str = self->GetConstructorName()->ToString(v8_context).ToLocalChecked();
+		v8::String::Utf8Value str_value(isolate, str);
+		zend_string *constructor_name = zend_string_init(ToCString(str_value), str_value.length(), 0);
 		zend_class_entry *ce = zend_lookup_class(constructor_name);
 		zend_string_release(constructor_name);
 
@@ -440,15 +461,13 @@ static void v8js_invoke_callback(const v8::FunctionCallbackInfo<v8::Value>& info
 		new_tpl = v8::Local<v8::FunctionTemplate>::New
 			(isolate, ctx->template_cache.at(ce->name));
 
-		v8::MaybeLocal<v8::Object> maybeResult = new_tpl->GetFunction()->NewInstance(isolate->GetEnteredContext(), argc, argv);
-
-		if (!maybeResult.IsEmpty()) {
-			result = maybeResult.ToLocalChecked();
-		} else {
+		v8::Local<v8::Function> fn;
+		if (!new_tpl->GetFunction(v8_context).ToLocal(&fn) || !fn->NewInstance(v8_context, argc, argv).ToLocal(&result))
+		{
 			result = V8JS_UNDEFINED;
 		}
 	} else {
-		result = cb->Call(self, argc, argv);
+		cb->Call(v8_context, self, argc, argv).ToLocal(&result);
 	}
 
 	info.GetReturnValue().Set(result);
@@ -461,6 +480,8 @@ static void v8js_invoke_callback(const v8::FunctionCallbackInfo<v8::Value>& info
 static void v8js_fake_call_impl(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 {
 	v8::Isolate *isolate = info.GetIsolate();
+	v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
+
 	v8::Local<v8::Object> self = info.Holder();
 	v8::Local<v8::Value> return_value = V8JS_NULL;
 
@@ -529,10 +550,30 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo<v8::Value>& info)
 		return;
 	}
 
-	v8::Local<v8::String> str = info[0]->ToString();
-	v8::String::Utf8Value str_value(str);
+	v8::MaybeLocal<v8::String> str = info[0]->ToString(v8_context);
+
+	if (str.IsEmpty())
+	{
+		error_len = spprintf(&error, 0,
+			"%s::__call expect 1st parameter to be valid function name, toString() invocation failed.",
+			ce->name);
+
+		if (error_len > std::numeric_limits<int>::max()) {
+			zend_throw_exception(php_ce_v8js_exception,
+				"Generated error message length exceeds maximum supported length", 0);
+		}
+		else {
+			return_value = V8JS_THROW(isolate, TypeError, error, static_cast<int>(error_len));
+		}
+
+		efree(error);
+		info.GetReturnValue().Set(return_value);
+		return;
+	}
+
+	v8::String::Utf8Value str_value(isolate, str.ToLocalChecked());
 	const char *method_c_name = ToCString(str_value);
-	zend_string *method_name = zend_string_init(method_c_name, str->Utf8Length(), 0);
+	zend_string *method_name = zend_string_init(method_c_name, str_value.length(), 0);
 
 	// okay, look up the method name and manually invoke it.
 	const zend_object_handlers *h = object->handlers;
@@ -568,22 +609,25 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo<v8::Value>& info)
 	v8::Local<v8::Value> *argv = static_cast<v8::Local<v8::Value> *>(alloca(sizeof(v8::Local<v8::Value>) * argc));
 	for (i=0; i<argc; i++) {
 		new(&argv[i]) v8::Local<v8::Value>;
-		argv[i] = args->Get(i);
+		args->Get(v8_context, i).ToLocal(&argv[i]);
 	}
-	return_value = cb->Call(info.This(), (int) argc, argv);
+	cb->Call(v8_context, info.This(), (int) argc, argv).ToLocal(&return_value);
 	info.GetReturnValue().Set(return_value);
 }
 /* }}} */
 
 /* This method handles named property and method get/set/query/delete. */
 template<typename T>
-v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<T> &info, property_op_t callback_type, v8::Local<v8::Value> set_value) /* {{{ */
+v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::Name> property_name, const v8::PropertyCallbackInfo<T> &info, property_op_t callback_type, v8::Local<v8::Value> set_value) /* {{{ */
 {
+	v8::Local<v8::String> property = v8::Local<v8::String>::Cast(property_name);
+
 	v8::Isolate *isolate = info.GetIsolate();
+	v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
 	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
-	v8::String::Utf8Value cstr(property);
+	v8::String::Utf8Value cstr(isolate, property);
 	const char *name = ToCString(cstr);
-	uint name_len = property->Utf8Length();
+	uint name_len = cstr.length();
 	char *lower = estrndup(name, name_len);
 	zend_string *method_name;
 
@@ -646,9 +690,13 @@ v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> property
 						v8js_function_tmpl_t *persistent_ft = &ctx->call_impls[tmpl_ptr];
 						persistent_ft->Reset(isolate, ft);
 					}
-					v8::Local<v8::Function> cb = ft->GetFunction();
-					cb->SetName(property);
-					ret_value = cb;
+
+					v8::Local<v8::Function> fn;
+					if (ft->GetFunction(v8_context).ToLocal(&fn))
+					{
+						fn->SetName(property);
+						ret_value = fn;
+					}
 				} else {
 					v8::Local<v8::FunctionTemplate> ft;
 					try {
@@ -662,7 +710,7 @@ v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> property
 						v8js_function_tmpl_t *persistent_ft = &ctx->method_tmpls[method_ptr];
 						persistent_ft->Reset(isolate, ft);
 					}
-					ret_value = ft->GetFunction();
+					ft->GetFunction(v8_context).ToLocal(&ret_value);
 				}
 			}
 		} else if (callback_type == V8JS_PROP_QUERY) {
@@ -780,32 +828,44 @@ v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> property
 }
 /* }}} */
 
-static void v8js_named_property_getter(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
+static void v8js_named_property_getter(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
 {
 	info.GetReturnValue().Set(v8js_named_property_callback(property, info, V8JS_PROP_GETTER));
 }
 /* }}} */
 
-static void v8js_named_property_setter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
+static void v8js_named_property_setter(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value> &info) /* {{{ */
 {
 	info.GetReturnValue().Set(v8js_named_property_callback(property, info, V8JS_PROP_SETTER, value));
 }
 /* }}} */
 
-static void v8js_named_property_query(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Integer> &info) /* {{{ */
+static void v8js_named_property_query(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Integer> &info) /* {{{ */
 {
 	v8::Local<v8::Value> r = v8js_named_property_callback(property, info, V8JS_PROP_QUERY);
-	if (!r.IsEmpty()) {
-		info.GetReturnValue().Set(r->ToInteger());
+	if (r.IsEmpty()) {
+		return;
+	}
+
+	v8::Isolate *isolate = info.GetIsolate();
+	v8::MaybeLocal<v8::Integer> value = r->ToInteger(isolate->GetEnteredContext());
+	if (!value.IsEmpty()) {
+		info.GetReturnValue().Set(value.ToLocalChecked());
 	}
 }
 /* }}} */
 
-static void v8js_named_property_deleter(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Boolean> &info) /* {{{ */
+static void v8js_named_property_deleter(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Boolean> &info) /* {{{ */
 {
 	v8::Local<v8::Value> r = v8js_named_property_callback(property, info, V8JS_PROP_DELETER);
-	if (!r.IsEmpty()) {
-		info.GetReturnValue().Set(r->ToBoolean());
+	if (r.IsEmpty()) {
+		return;
+	}
+
+	v8::Isolate *isolate = info.GetIsolate();
+	v8::MaybeLocal<v8::Boolean> value = r->ToBoolean(isolate->GetEnteredContext());
+	if (!value.IsEmpty()) {
+		info.GetReturnValue().Set(value.ToLocalChecked());
 	}
 }
 /* }}} */
@@ -815,6 +875,7 @@ static void v8js_named_property_deleter(v8::Local<v8::String> property, const v8
 static v8::MaybeLocal<v8::Object> v8js_wrap_object(v8::Isolate *isolate, zend_class_entry *ce, zval *value) /* {{{ */
 {
 	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+	v8::Local<v8::Context> v8_context = v8::Local<v8::Context>::New(isolate, ctx->context);
 	v8::Local<v8::FunctionTemplate> new_tpl;
 	v8js_function_tmpl_t *persist_tpl_;
 
@@ -847,8 +908,8 @@ static v8::MaybeLocal<v8::Object> v8js_wrap_object(v8::Isolate *isolate, zend_cl
 			/* We'll free persist_tpl_ when template_cache is destroyed */
 
 			v8::Local<v8::ObjectTemplate> inst_tpl = new_tpl->InstanceTemplate();
-			v8::NamedPropertyGetterCallback getter = v8js_named_property_getter;
-			v8::NamedPropertyEnumeratorCallback enumerator = v8js_named_property_enumerator;
+			v8::GenericNamedPropertyGetterCallback getter = v8js_named_property_getter;
+			v8::GenericNamedPropertyEnumeratorCallback enumerator = v8js_named_property_enumerator;
 
 			/* Check for ArrayAccess object */
 			if (V8JSG(use_array_access) && ce) {
@@ -884,14 +945,14 @@ static v8::MaybeLocal<v8::Object> v8js_wrap_object(v8::Isolate *isolate, zend_cl
 
 
 			// Finish setup of new_tpl
-			inst_tpl->SetNamedPropertyHandler
+			inst_tpl->SetHandler(v8::NamedPropertyHandlerConfiguration
 				(getter, /* getter */
 				 v8js_named_property_setter, /* setter */
 				 v8js_named_property_query, /* query */
 				 v8js_named_property_deleter, /* deleter */
 				 enumerator, /* enumerator */
 				 V8JS_NULL /* data */
-				 );
+				 ));
 			// add __invoke() handler
 			zend_string *invoke_str = zend_string_init
 				(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME) - 1, 0);
@@ -904,14 +965,20 @@ static v8::MaybeLocal<v8::Object> v8js_wrap_object(v8::Isolate *isolate, zend_cl
 			zend_string_release(invoke_str);
 		}
 		v8::Local<v8::Array> call_handler_data = v8::Array::New(isolate, 2);
-		call_handler_data->Set(0, v8::External::New(isolate, persist_tpl_));
-		call_handler_data->Set(1, v8::External::New(isolate, ce));
+		call_handler_data->Set(v8_context, 0, v8::External::New(isolate, persist_tpl_));
+		call_handler_data->Set(v8_context, 1, v8::External::New(isolate, ce));
 		new_tpl->SetCallHandler(v8js_construct_callback, call_handler_data);
 	}
 
 	// Create v8 wrapper object
 	v8::Local<v8::Value> external = v8::External::New(isolate, Z_OBJ_P(value));
-	v8::MaybeLocal<v8::Object> newobj = new_tpl->GetFunction()->NewInstance(isolate->GetEnteredContext(), 1, &external);
+
+	v8::Local<v8::Function> constr;
+	if (!new_tpl->GetFunction(v8_context).ToLocal(&constr)) {
+		return v8::MaybeLocal<v8::Object>();
+	}
+
+	v8::MaybeLocal<v8::Object> newobj = constr->NewInstance(v8_context, 1, &external);
 
 	if (ce == zend_ce_closure && !newobj.IsEmpty()) {
 		// free uncached function template when object is freed
@@ -931,6 +998,7 @@ static v8::Local<v8::Object> v8js_wrap_array_to_object(v8::Isolate *isolate, zva
 	zend_ulong index;
 
 	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+	v8::Local<v8::Context> v8_context = v8::Local<v8::Context>::New(isolate, ctx->context);
 	v8::Local<v8::FunctionTemplate> new_tpl;
 
 	if(ctx->array_tmpl.IsEmpty()) {
@@ -947,7 +1015,7 @@ static v8::Local<v8::Object> v8js_wrap_array_to_object(v8::Isolate *isolate, zva
 		new_tpl = v8::Local<v8::FunctionTemplate>::New(isolate, ctx->array_tmpl);
 	}
 
-	v8::Local<v8::Object> newobj = new_tpl->InstanceTemplate()->NewInstance();
+	v8::Local<v8::Object> newobj = new_tpl->InstanceTemplate()->NewInstance(v8_context).ToLocalChecked();
 
 	HashTable *myht = HASH_OF(value);
 	i = myht ? zend_hash_num_elements(myht) : 0;
@@ -978,7 +1046,7 @@ static v8::Local<v8::Object> v8js_wrap_array_to_object(v8::Isolate *isolate, zva
 					continue;
 				}
 
-				newobj->Set(V8JS_STRL(ZSTR_VAL(key), static_cast<int>(ZSTR_LEN(key))),
+				newobj->Set(v8_context, V8JS_STRL(ZSTR_VAL(key), static_cast<int>(ZSTR_LEN(key))),
 					zval_to_v8js(data, isolate));
 			} else {
 				if (index > std::numeric_limits<uint32_t>::max()) {
@@ -987,7 +1055,7 @@ static v8::Local<v8::Object> v8js_wrap_array_to_object(v8::Isolate *isolate, zva
 					continue;
 				}
 
-				newobj->Set(static_cast<uint32_t>(index), zval_to_v8js(data, isolate));
+				newobj->Set(v8_context, static_cast<uint32_t>(index), zval_to_v8js(data, isolate));
 			}
 
 		} ZEND_HASH_FOREACH_END();

+ 1 - 1
v8js_object_export.h

@@ -25,7 +25,7 @@ typedef enum {
 } property_op_t;
 
 template<typename T>
-v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> property,
+v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::Name> property,
 						      const v8::PropertyCallbackInfo<T> &info,
 						      property_op_t callback_type,
 						      v8::Local<v8::Value> set_value = v8::Local<v8::Value>());

+ 60 - 43
v8js_v8.cc

@@ -108,7 +108,7 @@ void v8js_v8_init() /* {{{ */
  */
 void v8js_v8_call(v8js_ctx *c, zval **return_value,
 				  long flags, long time_limit, size_t memory_limit,
-				  std::function< v8::Local<v8::Value>(v8::Isolate *) >& v8_call) /* {{{ */
+				  std::function< v8::MaybeLocal<v8::Value>(v8::Isolate *) >& v8_call) /* {{{ */
 {
 	char *tz = NULL;
 
@@ -154,7 +154,7 @@ void v8js_v8_call(v8js_ctx *c, zval **return_value,
 
 	/* Execute script */
 	c->in_execution++;
-	v8::Local<v8::Value> result = v8_call(c->isolate);
+	v8::MaybeLocal<v8::Value> result = v8_call(c->isolate);
 	c->in_execution--;
 
 	/* Pop our context from the stack and read (possibly updated) limits
@@ -238,7 +238,7 @@ void v8js_v8_call(v8js_ctx *c, zval **return_value,
 
 		/* Convert V8 value to PHP value */
 		if (return_value && !result.IsEmpty()) {
-			v8js_to_zval(result, *return_value, flags, c->isolate);
+			v8js_to_zval(result.ToLocalChecked(), *return_value, flags, c->isolate);
 		}
 	}
 }
@@ -262,63 +262,80 @@ void v8js_terminate_execution(v8::Isolate *isolate) /* {{{ */
 	v8::Isolate::Scope isolate_scope(isolate);
 	v8::HandleScope handle_scope(isolate);
 
+	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+	v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate, ctx->context);
+
 	v8::Local<v8::String> source = V8JS_STR("for(;;);");
-	v8::Local<v8::Script> script = v8::Script::Compile(source);
+	v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
 	isolate->TerminateExecution();
-	script->Run();
+	script->Run(context);
 }
 /* }}} */
 
 
 int v8js_get_properties_hash(v8::Local<v8::Value> jsValue, HashTable *retval, int flags, v8::Isolate *isolate) /* {{{ */
 {
-	v8::Local<v8::Object> jsObj = jsValue->ToObject();
+	v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+	v8::Local<v8::Context> v8_context = v8::Local<v8::Context>::New(isolate, ctx->context);
+
+	v8::Local<v8::Object> jsObj;
+	v8::Local<v8::Array> jsKeys;
+	if (!jsValue->ToObject(v8_context).ToLocal(&jsObj)
+			|| !jsObj->GetPropertyNames(v8_context).ToLocal(&jsKeys)) {
+		return FAILURE;
+	}
 
-	if (!jsObj.IsEmpty()) {
-		v8::Local<v8::Array> jsKeys = jsObj->GetPropertyNames();
+	for (unsigned i = 0; i < jsKeys->Length(); i++)
+	{
+		v8::Local<v8::Value> jsKeySlot;
+		v8::Local<v8::String> jsKey;
 
-		for (unsigned i = 0; i < jsKeys->Length(); i++)
-		{
-			v8::Local<v8::String> jsKey = jsKeys->Get(i)->ToString();
+		if (!jsKeys->Get(v8_context, i).ToLocal(&jsKeySlot)
+				|| !jsKeySlot->ToString(v8_context).ToLocal(&jsKey)) {
+			continue;
+		}
 
-			/* Skip any prototype properties */
-			if (!jsObj->HasOwnProperty(isolate->GetEnteredContext(), jsKey).FromMaybe(false)
-				&& !jsObj->HasRealNamedProperty(jsKey)
-				&& !jsObj->HasRealNamedCallbackProperty(jsKey)) {
-				continue;
-			}
+		/* Skip any prototype properties */
+		if (!jsObj->HasOwnProperty(isolate->GetEnteredContext(), jsKey).FromMaybe(false)
+			&& !jsObj->HasRealNamedProperty(v8_context, jsKey).FromMaybe(false)
+			&& !jsObj->HasRealNamedCallbackProperty(v8_context, jsKey).FromMaybe(false)) {
+			continue;
+		}
 
-			v8::Local<v8::Value> jsVal = jsObj->Get(jsKey);
-			v8::String::Utf8Value cstr(jsKey);
-			const char *c_key = ToCString(cstr);
-			zend_string *key = zend_string_init(c_key, jsKey->ToString()->Utf8Length(), 0);
-			zval value;
-			ZVAL_UNDEF(&value);
-
-			if (jsVal->IsObject() && jsVal->ToObject()->InternalFieldCount() == 2) {
-				/* This is a PHP object, passed to JS and back. */
-				zend_object *object = reinterpret_cast<zend_object *>(jsVal->ToObject()->GetAlignedPointerFromInternalField(1));
-				ZVAL_OBJ(&value, object);
-				Z_ADDREF_P(&value);
-			}
-			else {
-				if (v8js_to_zval(jsVal, &value, flags, isolate) == FAILURE) {
-					zval_ptr_dtor(&value);
-					return FAILURE;
-				}
-			}
+		v8::Local<v8::Value> jsVal;
+
+		if (!jsObj->Get(v8_context, jsKey).ToLocal(&jsVal)) {
+			continue;
+		}
 
-			if ((flags & V8JS_FLAG_FORCE_ARRAY) || jsValue->IsArray()) {
-				zend_symtable_update(retval, key, &value);
-			} else {
-				zend_hash_update(retval, key, &value);
+		v8::String::Utf8Value cstr(isolate, jsKey);
+		zend_string *key = zend_string_init(ToCString(cstr), cstr.length(), 0);
+		zval value;
+		ZVAL_UNDEF(&value);
+
+		v8::Local<v8::Object> jsValObject;
+		if (jsVal->IsObject() && jsVal->ToObject(v8_context).ToLocal(&jsValObject) && jsValObject->InternalFieldCount() == 2) {
+			/* This is a PHP object, passed to JS and back. */
+			zend_object *object = reinterpret_cast<zend_object *>(jsValObject->GetAlignedPointerFromInternalField(1));
+			ZVAL_OBJ(&value, object);
+			Z_ADDREF_P(&value);
+		}
+		else {
+			if (v8js_to_zval(jsVal, &value, flags, isolate) == FAILURE) {
+				zval_ptr_dtor(&value);
+				return FAILURE;
 			}
+		}
 
-			zend_string_release(key);
+		if ((flags & V8JS_FLAG_FORCE_ARRAY) || jsValue->IsArray()) {
+			zend_symtable_update(retval, key, &value);
+		} else {
+			zend_hash_update(retval, key, &value);
 		}
-		return SUCCESS;
+
+		zend_string_release(key);
 	}
-	return FAILURE;
+	return SUCCESS;
 }
 /* }}} */
 

+ 7 - 8
v8js_v8.h

@@ -17,19 +17,18 @@
 #include <functional>
 
 /* Helper macros */
-#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_ZSYM(v)		v8::String::NewFromUtf8(isolate, ZSTR_VAL(v), v8::String::kInternalizedString, ZSTR_LEN(v))
-#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_ZSTR(v)		v8::String::NewFromUtf8(isolate, ZSTR_VAL(v), v8::String::kNormalString, ZSTR_LEN(v))
+#define V8JS_SYM(v)			(v8::String::NewFromUtf8(isolate, v, v8::NewStringType::kInternalized, sizeof(v) - 1).ToLocalChecked())
+#define V8JS_SYML(v, l)		(v8::String::NewFromUtf8(isolate, v, v8::NewStringType::kInternalized, l).ToLocalChecked())
+#define V8JS_ZSYM(v)		(v8::String::NewFromUtf8(isolate, ZSTR_VAL(v), v8::NewStringType::kInternalized, ZSTR_LEN(v)).ToLocalChecked())
+#define V8JS_STR(v)			(v8::String::NewFromUtf8(isolate, v, v8::NewStringType::kNormal).ToLocalChecked())
+#define V8JS_STRL(v, l)		(v8::String::NewFromUtf8(isolate, v, v8::NewStringType::kNormal, l).ToLocalChecked())
+#define V8JS_ZSTR(v)		(v8::String::NewFromUtf8(isolate, ZSTR_VAL(v), v8::NewStringType::kNormal, ZSTR_LEN(v)).ToLocalChecked())
 #define V8JS_INT(v)			v8::Integer::New(isolate, v)
 #define V8JS_UINT(v)		v8::Integer::NewFromUnsigned(isolate, v)
 #define V8JS_FLOAT(v)		v8::Number::New(isolate, v)
 #define V8JS_BOOL(v)		((v)?v8::True(isolate):v8::False(isolate))
 #define V8JS_TRUE()			v8::True(isolate)
 #define V8JS_FALSE()		v8::False(isolate)
-#define V8JS_DATE(v)		v8::Date::New(isolate, v)
 #define V8JS_NULL			v8::Null(isolate)
 #define V8JS_UNDEFINED		v8::Undefined(isolate)
 #define V8JS_MN(name)		v8js_method_##name
@@ -56,7 +55,7 @@ static inline const char * ToCString(const v8::String::Utf8Value &value) /* {{{
 void v8js_v8_init();
 void v8js_v8_call(v8js_ctx *c, zval **return_value,
 				  long flags, long time_limit, size_t memory_limit,
-				  std::function< v8::Local<v8::Value>(v8::Isolate *) >& v8_call);
+				  std::function< v8::MaybeLocal<v8::Value>(v8::Isolate *) >& v8_call);
 void v8js_terminate_execution(v8::Isolate *isolate);
 
 /* Fetch V8 object properties */

+ 96 - 75
v8js_v8object_class.cc

@@ -59,58 +59,64 @@ static int v8js_v8object_has_property(zval *object, zval *member, int has_set_ex
 	if (!obj->ctx) {
 		zend_throw_exception(php_ce_v8js_exception,
 			"Can't access V8Object after V8Js instance is destroyed!", 0);
-		return retval;
+		return false;
 	}
 
-	V8JS_CTX_PROLOGUE_EX(obj->ctx, retval);
+	V8JS_CTX_PROLOGUE_EX(obj->ctx, false);
 	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
+	v8::Local<v8::Object> jsObj;
 
-	if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject())
-	{
-		if (Z_STRLEN_P(member) > std::numeric_limits<int>::max()) {
-			zend_throw_exception(php_ce_v8js_exception,
-				"Member name length exceeds maximum supported length", 0);
-			return retval;
-		}
+	if (Z_TYPE_P(member) != IS_STRING || !v8obj->IsObject() || !v8obj->ToObject(v8_context).ToLocal(&jsObj)) {
+		return false;
+	}
 
-		v8::Local<v8::Object> jsObj = v8obj->ToObject();
-		v8::Local<v8::String> jsKey = V8JS_STRL(Z_STRVAL_P(member), static_cast<int>(Z_STRLEN_P(member)));
-		v8::Local<v8::Value> jsVal;
+	if (Z_STRLEN_P(member) > std::numeric_limits<int>::max()) {
+		zend_throw_exception(php_ce_v8js_exception,
+			"Member name length exceeds maximum supported length", 0);
+		return false;
+	}
 
-		/* Skip any prototype properties */
-		if (jsObj->HasRealNamedProperty(jsKey) || jsObj->HasRealNamedCallbackProperty(jsKey)) {
-			if (has_set_exists == 2) {
-				/* property_exists(), that's enough! */
-				retval = true;
-			} else {
-				/* We need to look at the value. */
-				jsVal = jsObj->Get(jsKey);
-				if (has_set_exists == 0 ) {
-					/* isset(): We make 'undefined' equivalent to 'null' */
-					retval = !( jsVal->IsNull() || jsVal->IsUndefined() );
-				} else {
-					/* empty() */
-					retval = jsVal->BooleanValue();
-					/* for PHP compatibility, [] should also be empty */
-					if (jsVal->IsArray() && retval) {
-						v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(jsVal);
-						retval = (array->Length() != 0);
-					}
-					/* for PHP compatibility, '0' should also be empty */
-					if (jsVal->IsString() && retval) {
-						v8::Local<v8::String> str = jsVal->ToString();
-						if (str->Length() == 1) {
-							uint16_t c = 0;
-							str->Write(&c, 0, 1);
-							if (c == '0') {
-								retval = false;
-							}
-						}
-					}
-				}
-			}
+	v8::Local<v8::String> jsKey = V8JS_ZSYM(Z_STR_P(member));
+
+	/* Skip any prototype properties */
+	if (!jsObj->HasRealNamedProperty(v8_context, jsKey).FromMaybe(false)
+			&& !jsObj->HasRealNamedCallbackProperty(v8_context, jsKey).FromMaybe(false)) {
+		return false;
+	}
+
+	if (has_set_exists == 2) {
+		/* property_exists(), that's enough! */
+		return true;
+	}
+
+	/* We need to look at the value. */
+	v8::Local<v8::Value> jsVal = jsObj->Get(v8_context, jsKey).ToLocalChecked();
+
+	if (has_set_exists == 0 ) {
+		/* isset(): We make 'undefined' equivalent to 'null' */
+		return !( jsVal->IsNull() || jsVal->IsUndefined() );
+	}
+
+	/* empty() */
+	retval = jsVal->BooleanValue(v8_context).FromMaybe(false);
+
+	/* for PHP compatibility, [] should also be empty */
+	if (jsVal->IsArray() && retval) {
+		v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(jsVal);
+		retval = (array->Length() != 0);
+	}
+
+	/* for PHP compatibility, '0' should also be empty */
+	v8::Local<v8::String> str;
+	if (jsVal->IsString() && retval && jsVal->ToString(v8_context).ToLocal(&str)
+			&& str->Length() == 1) {
+		uint16_t c = 0;
+		str->Write(isolate, &c, 0, 1);
+		if (c == '0') {
+			retval = false;
 		}
 	}
+
 	return retval;
 }
 /* }}} */
@@ -137,15 +143,15 @@ static zval *v8js_v8object_read_property(zval *object, zval *member, int type, v
 			return retval;
 		}
 
-		v8::Local<v8::Object> jsObj = v8obj->ToObject();
-		v8::Local<v8::String> jsKey = V8JS_STRL(Z_STRVAL_P(member), static_cast<int>(Z_STRLEN_P(member)));
-		v8::Local<v8::Value> jsVal;
+		v8::Local<v8::String> jsKey = V8JS_ZSYM(Z_STR_P(member));
+		v8::Local<v8::Object> jsObj = v8obj->ToObject(v8_context).ToLocalChecked();
 
 		/* Skip any prototype properties */
-		if (jsObj->HasRealNamedProperty(jsKey) || jsObj->HasRealNamedCallbackProperty(jsKey)) {
-			jsVal = jsObj->Get(jsKey);
+		if (jsObj->HasRealNamedProperty(v8_context, jsKey).FromMaybe(false)
+				|| jsObj->HasRealNamedCallbackProperty(v8_context, jsKey).FromMaybe(false)) {
+			v8::MaybeLocal<v8::Value> jsVal = jsObj->Get(v8_context, jsKey);
 			
-			if (v8js_to_zval(jsVal, retval, obj->flags, isolate) == SUCCESS) {
+			if (!jsVal.IsEmpty() && v8js_to_zval(jsVal.ToLocalChecked(), retval, obj->flags, isolate) == SUCCESS) {
 				return retval;
 			}
 		}
@@ -166,7 +172,7 @@ static void v8js_v8object_write_property(zval *object, zval *member, zval *value
 	}
 
 	V8JS_CTX_PROLOGUE(obj->ctx);
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
+	v8::Local<v8::Value> v8objHandle = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 	if (Z_STRLEN_P(member) > std::numeric_limits<int>::max()) {
 		zend_throw_exception(php_ce_v8js_exception,
@@ -174,8 +180,9 @@ static void v8js_v8object_write_property(zval *object, zval *member, zval *value
 		return;
 	}
 
-	if (v8obj->IsObject()) {
-		v8obj->ToObject()->CreateDataProperty(v8_context, V8JS_SYML(Z_STRVAL_P(member), static_cast<int>(Z_STRLEN_P(member))), zval_to_v8js(value, isolate));
+	v8::Local<v8::Object> v8obj;
+	if (v8objHandle->IsObject() && v8objHandle->ToObject(v8_context).ToLocal(&v8obj)) {
+		v8obj->CreateDataProperty(v8_context, V8JS_ZSYM(Z_STR_P(member)), zval_to_v8js(value, isolate));
 	}
 }
 /* }}} */
@@ -191,7 +198,7 @@ static void v8js_v8object_unset_property(zval *object, zval *member, void **cach
 	}
 
 	V8JS_CTX_PROLOGUE(obj->ctx);
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
+	v8::Local<v8::Value> v8objHandle = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 	if (Z_STRLEN_P(member) > std::numeric_limits<int>::max()) {
 		zend_throw_exception(php_ce_v8js_exception,
@@ -199,8 +206,9 @@ static void v8js_v8object_unset_property(zval *object, zval *member, void **cach
 		return;
 	}
 
-	if (v8obj->IsObject()) {
-		v8obj->ToObject()->Delete(V8JS_SYML(Z_STRVAL_P(member), static_cast<int>(Z_STRLEN_P(member))));
+	v8::Local<v8::Object> v8obj;
+	if (v8objHandle->IsObject() && v8objHandle->ToObject(v8_context).ToLocal(&v8obj)) {
+		v8obj->Delete(v8_context, V8JS_ZSYM(Z_STR_P(member)));
 	}
 }
 /* }}} */
@@ -276,9 +284,13 @@ static zend_function *v8js_v8object_get_method(zend_object **object_ptr, zend_st
 	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 	if (!obj->v8obj.IsEmpty() && v8obj->IsObject() && !v8obj->IsFunction()) {
-		v8::Local<v8::Object> jsObj = v8obj->ToObject();
-		
-		if (jsObj->Has(jsKey) && jsObj->Get(jsKey)->IsFunction()) {
+		v8::Local<v8::Object> jsObj;
+		v8::Local<v8::Value> jsObjSlot;
+
+		if (v8obj->ToObject(v8_context).ToLocal(&jsObj)
+				&& jsObj->Has(v8_context, jsKey).FromMaybe(false)
+				&& jsObj->Get(v8_context, jsKey).ToLocal(&jsObjSlot)
+				&& jsObjSlot->IsFunction()) {
 			f = (zend_function *) ecalloc(1, sizeof(*f));
 			f->type = ZEND_OVERLOADED_FUNCTION_TEMPORARY;
 			f->common.function_name = zend_string_copy(method);
@@ -321,18 +333,25 @@ static int v8js_v8object_call_method(zend_string *method, zend_object *object, I
 	/* std::function relies on its dtor to be executed, otherwise it leaks
 	 * some memory on bailout. */
 	{
-		std::function< v8::Local<v8::Value>(v8::Isolate *) > v8_call = [obj, method, argc, argv, object, &return_value](v8::Isolate *isolate) {
+		std::function< v8::MaybeLocal<v8::Value>(v8::Isolate *) > v8_call = [obj, method, argc, argv, object, &return_value](v8::Isolate *isolate) {
 			int i = 0;
 
+			v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
 			v8::Local<v8::String> method_name = V8JS_SYML(ZSTR_VAL(method), static_cast<int>(ZSTR_LEN(method)));
-			v8::Local<v8::Object> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj)->ToObject();
+			v8::Local<v8::Object> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj)->ToObject(v8_context).ToLocalChecked();
 			v8::Local<v8::Object> thisObj;
 			v8::Local<v8::Function> cb;
 
-			if (method_name->Equals(V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME))) {
+			if (method_name->Equals(v8_context, V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME)).FromMaybe(false)) {
 				cb = v8::Local<v8::Function>::Cast(v8obj);
 			} else {
-				cb = v8::Local<v8::Function>::Cast(v8obj->Get(method_name));
+				v8::Local<v8::Value> slot;
+
+				if (!v8obj->Get(v8_context, method_name).ToLocal(&slot)) {
+					return v8::MaybeLocal<v8::Value>();
+				}
+
+				cb = v8::Local<v8::Function>::Cast(slot);
 			}
 
 			// If a method is invoked on V8Object, then set the object itself as
@@ -351,13 +370,13 @@ static int v8js_v8object_call_method(zend_string *method, zend_object *object, I
 				jsArgv[i] = v8::Local<v8::Value>::New(isolate, zval_to_v8js(&argv[i], isolate));
 			}
 
-			v8::Local<v8::Value> result = cb->Call(thisObj, argc, jsArgv);
+			v8::MaybeLocal<v8::Value> result = cb->Call(v8_context, thisObj, argc, jsArgv);
 
-			if (obj->std.ce == php_ce_v8object && result->StrictEquals(thisObj)) {
+			if (obj->std.ce == php_ce_v8object && !result.IsEmpty() && result.ToLocalChecked()->StrictEquals(thisObj)) {
 				/* JS code did "return this", retain object identity */
 				ZVAL_OBJ(return_value, object);
 				zval_copy_ctor(return_value);
-				result.Clear();
+				result = v8::MaybeLocal<v8::Value>();
 			}
 
 			return result;
@@ -545,27 +564,29 @@ static void v8js_v8generator_next(v8js_v8generator *g) /* {{{ */
 	/* std::function relies on its dtor to be executed, otherwise it leaks
 	 * some memory on bailout. */
 	{
-		std::function< v8::Local<v8::Value>(v8::Isolate *) > v8_call = [g](v8::Isolate *isolate) {
-			v8::Local<v8::String> method_name = V8JS_STR("next");
-			v8::Local<v8::Object> v8obj = v8::Local<v8::Value>::New(isolate, g->v8obj.v8obj)->ToObject();
-			v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(v8obj->Get(method_name));;
+		std::function< v8::MaybeLocal<v8::Value>(v8::Isolate *) > v8_call = [g](v8::Isolate *isolate) {
+			v8::Local<v8::Context> v8_context = isolate->GetEnteredContext();
+			v8::Local<v8::String> method_name = V8JS_SYM("next");
+			v8::Local<v8::Object> v8obj = v8::Local<v8::Value>::New(isolate, g->v8obj.v8obj)->ToObject(v8_context).ToLocalChecked();
+			v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(v8obj->Get(v8_context, method_name).ToLocalChecked());;
 
-			v8::Local<v8::Value> result = cb->Call(v8obj, 0, NULL);
+			v8::MaybeLocal<v8::Value> result = cb->Call(v8_context, v8obj, 0, NULL);
 
 			if(result.IsEmpty()) {
 				/* cb->Call probably threw (and already threw a zend exception), just return */
 				return V8JS_NULL;
 			}
 
-			if(!result->IsObject()) {
+			if(!result.ToLocalChecked()->IsObject()) {
 				zend_throw_exception(php_ce_v8js_exception,
 					"V8Generator returned non-object on next()", 0);
 				return V8JS_NULL;
 			}
 
-			v8::Local<v8::Object> resultObj = result->ToObject();
-			v8::Local<v8::Value> val = resultObj->Get(V8JS_STR("value"));
-			v8::Local<v8::Value> done = resultObj->Get(V8JS_STR("done"));
+
+			v8::Local<v8::Object> resultObj = result.ToLocalChecked()->ToObject(v8_context).ToLocalChecked();
+			v8::Local<v8::Value> val = resultObj->Get(v8_context, V8JS_SYM("value")).ToLocalChecked();
+			v8::Local<v8::Value> done = resultObj->Get(v8_context, V8JS_SYM("done")).ToLocalChecked();
 
 			zval_ptr_dtor(&g->value);
 			v8js_to_zval(val, &g->value, 0, isolate);