diff --git a/php_v8js_macros.h b/php_v8js_macros.h index 2503663..903a6d6 100644 --- a/php_v8js_macros.h +++ b/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) diff --git a/v8js_array_access.cc b/v8js_array_access.cc index cabba33..bf7289a 100644 --- a/v8js_array_access.cc +++ b/v8js_array_access.cc @@ -221,9 +221,11 @@ void v8js_array_access_enumerator(const v8::PropertyCallbackInfo& inf -void v8js_array_access_named_getter(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ +void v8js_array_access_named_getter(v8::Local property_name, const v8::PropertyCallbackInfo &info) /* {{{ */ { - v8::String::Utf8Value cstr(property); + v8::Local property = v8::Local::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 property, const v8::Pr v8::Local ret_value = v8js_named_property_callback(property, info, V8JS_PROP_GETTER); if(ret_value.IsEmpty()) { - v8::Isolate *isolate = info.GetIsolate(); v8::Local arr = v8::Array::New(isolate); v8::Local prototype = arr->GetPrototype(); @@ -243,7 +244,7 @@ void v8js_array_access_named_getter(v8::Local property, const v8::Pr info.GetReturnValue().Set(ret_value); } - ret_value = prototype->ToObject()->Get(property); + ret_value = prototype->ToObject(isolate)->Get(property); } info.GetReturnValue().Set(ret_value); diff --git a/v8js_array_access.h b/v8js_array_access.h index ef5416a..283e1cb 100644 --- a/v8js_array_access.h +++ b/v8js_array_access.h @@ -25,7 +25,7 @@ void v8js_array_access_query(uint32_t index, const v8::PropertyCallbackInfo& info); /* Named Property Handlers */ -void v8js_array_access_named_getter(v8::Local property, +void v8js_array_access_named_getter(v8::Local property, const v8::PropertyCallbackInfo &info); #endif /* V8JS_ARRAY_ACCESS_H */ diff --git a/v8js_class.cc b/v8js_class.cc index 9f67992..8430348 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -648,8 +648,8 @@ static void v8js_compile_script(zval *this_ptr, const zend_string *str, const ze v8::Local sname = identifier ? v8::String::NewFromUtf8(isolate, ZSTR_VAL(identifier), v8::String::kNormalString, static_cast(ZSTR_LEN(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::max()) { zend_throw_exception(php_ce_v8js_exception, "Script source exceeds maximum supported length", 0); @@ -657,7 +657,7 @@ static void v8js_compile_script(zval *this_ptr, const zend_string *str, const ze } v8::Local source = v8::String::NewFromUtf8(isolate, ZSTR_VAL(str), v8::String::kNormalString, static_cast(ZSTR_LEN(str))); - v8::Local script = v8::Script::Compile(source, sname); + v8::MaybeLocal script = v8::Script::Compile(v8::Local::New(isolate, c->context), source, &origin); /* Compile errors? */ if (script.IsEmpty()) { @@ -665,9 +665,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>(c->isolate, script); + res->script = new v8::Persistent>(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 +695,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::Isolate *) > v8_call = [res](v8::Isolate *isolate) { + std::function< v8::MaybeLocal(v8::Isolate *) > v8_call = [c, res](v8::Isolate *isolate) { v8::Local script = v8::Local::New(isolate, *res->script); - return script->Run(); + return script->Run(v8::Local::New(isolate, c->context)); }; v8js_v8_call(c, return_value, flags, time_limit, memory_limit, v8_call); @@ -1153,6 +1153,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 context = v8::Context::New(isolate); + + v8::Context::Scope context_scope(context); + v8::TryCatch try_catch(isolate); + + v8::Local source = v8::String::NewFromUtf8(isolate, ZSTR_VAL(str), v8::String::kNormalString, static_cast(ZSTR_LEN(str))); + v8::MaybeLocal 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 +1197,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 +1330,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 object_name_js = v8::Local::New(isolate, c->object_name); - v8::Local jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject(); + v8::Local jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject(isolate); if (Z_STRLEN_P(member) > std::numeric_limits::max()) { zend_throw_exception(php_ce_v8js_exception, @@ -1327,7 +1354,7 @@ static void v8js_unset_property(zval *object, zval *member, void **cache_slot) / /* Global PHP JS object */ v8::Local object_name_js = v8::Local::New(isolate, c->object_name); - v8::Local jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject(); + v8::Local jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject(isolate); if (Z_STRLEN_P(member) > std::numeric_limits::max()) { zend_throw_exception(php_ce_v8js_exception, diff --git a/v8js_convert.cc b/v8js_convert.cc index 3f91dd1..3271685 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -200,27 +200,41 @@ v8::Local zval_to_v8js(zval *value, v8::Isolate *isolate) /* {{{ */ int v8js_to_zval(v8::Local jsValue, zval *return_value, int flags, v8::Isolate *isolate) /* {{{ */ { + v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0); + 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, jsValue->ToString(isolate)->Utf8Length(isolate)); } else if (jsValue->IsBoolean()) { - RETVAL_BOOL(jsValue->Uint32Value()); + v8::Maybe value = jsValue->BooleanValue(v8::Local::New(isolate, ctx->context)); + if (value.IsNothing()) { + return FAILURE; + } + RETVAL_BOOL(value.ToChecked()); } else if (jsValue->IsInt32() || jsValue->IsUint32()) { - RETVAL_LONG((long) jsValue->IntegerValue()); + v8::Maybe value = jsValue->IntegerValue(v8::Local::New(isolate, ctx->context)); + if (value.IsNothing()) { + return FAILURE; + } + RETVAL_LONG((long) value.ToChecked()); } else if (jsValue->IsNumber()) { - RETVAL_DOUBLE(jsValue->NumberValue()); + v8::Maybe value = jsValue->NumberValue(v8::Local::New(isolate, ctx->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 +267,7 @@ int v8js_to_zval(v8::Local jsValue, zval *return_value, int flags, v8 } else if (jsValue->IsObject()) { - v8::Local self = jsValue->ToObject(); + v8::Local self = jsValue->ToObject(isolate); // if this is a wrapped PHP object, then just unwrap it. if (self->InternalFieldCount() == 2) { diff --git a/v8js_exceptions.cc b/v8js_exceptions.cc index 967cae8..2947198 100644 --- a/v8js_exceptions.cc +++ b/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 context = v8::Local::New(isolate, ctx->context); + + v8::String::Utf8Value exception(isolate, try_catch->Exception()); const char *exception_string = ToCString(exception); v8::Local 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,42 @@ 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 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 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 start_col = tc_message->GetStartColumn(context); + if (start_col.IsJust()) { + PHPV8_EXPROP(_long, JsStartColumn, start_col.FromJust()); + } - v8::Maybe end_col = tc_message->GetEndColumn(isolate->GetEnteredContext()); + v8::Maybe 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 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(try_catch->Exception()->ToObject()->GetAlignedPointerFromInternalField(1)); + if(try_catch->Exception()->IsObject() && try_catch->Exception()->ToObject(isolate)->InternalFieldCount() == 2) { + zend_object *php_exception = reinterpret_cast(try_catch->Exception()->ToObject(isolate)->GetAlignedPointerFromInternalField(1)); zend_class_entry *exception_ce = zend_exception_get_default(); if (instanceof_function(php_exception->ce, exception_ce)) { @@ -106,7 +115,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; diff --git a/v8js_generator_export.cc b/v8js_generator_export.cc index a27fa93..2d5f1ce 100644 --- a/v8js_generator_export.cc +++ b/v8js_generator_export.cc @@ -24,6 +24,9 @@ v8::Local v8js_wrap_generator(v8::Isolate *isolate, v8::LocalIsObject()); + v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0); + v8::Local context = v8::Local::New(isolate, ctx->context); + v8::TryCatch try_catch(isolate); v8::Local source = v8::String::NewFromUtf8(isolate, "(\ function(wrapped_object) { \ @@ -37,21 +40,21 @@ function(wrapped_object) { \ } \ })(); \ })"); - v8::Local script = v8::Script::Compile(source); + v8::MaybeLocal script = v8::Script::Compile(context, source); if(script.IsEmpty()) { zend_error(E_ERROR, "Failed to compile Generator object wrapper"); return result; } - v8::Local wrapper_fn_val = script->Run(); + v8::MaybeLocal wrapper_fn_val = script.ToLocalChecked()->Run(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 wrapper_fn = v8::Local::Cast(wrapper_fn_val); + v8::Local wrapper_fn = v8::Local::Cast(wrapper_fn_val.ToLocalChecked()); v8::Local *jsArgv = static_cast *>(alloca(sizeof(v8::Local))); new(&jsArgv[0]) v8::Local; diff --git a/v8js_methods.cc b/v8js_methods.cc index 4bee095..cd761bd 100644 --- a/v8js_methods.cc +++ b/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 t = info[0]->Int32Value(v8::Local::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 var, int level) /* {{{ */ { + v8js_ctx *c = (v8js_ctx *) isolate->GetData(0); + v8::Local context = v8::Local::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 var, int leve } if (var->IsInt32()) { - php_printf("int(%ld)\n", (long) var->IntegerValue()); + v8::Maybe value = var->IntegerValue(context); + if (value.IsNothing()) + { + php_printf("\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 value = var->Uint32Value(context); + if (value.IsNothing()) + { + php_printf("\n"); + } + else + { + php_printf("int(%lu)\n", (unsigned long) value.FromJust()); + } return; } if (var->IsNumber()) { - php_printf("float(%f)\n", var->NumberValue()); + v8::Maybe value = var->NumberValue(context); + if (value.IsNothing()) + { + php_printf("\n"); + } + else + { + php_printf("float(%f)\n", value.FromJust()); + } return; } if (var->IsBoolean()) { - php_printf("bool(%s)\n", var->BooleanValue() ? "true" : "false"); + v8::Maybe value = var->BooleanValue(context); + if (value.IsNothing()) + { + php_printf("\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 var, int leve details = re->GetSource(); } else { - details = var->ToDetailString(isolate->GetEnteredContext()).FromMaybe(v8::Local()); + details = var->ToDetailString(context).FromMaybe(v8::Local()); if (try_catch.HasCaught()) { details = V8JS_SYM(""); } } - 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 = details->ToString(isolate)->Utf8Length(isolate); if (var->IsString()) { @@ -147,12 +189,12 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local var, int leve else if (var->IsObject()) { v8::Local object = v8::Local::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()); + v8::String::Utf8Value csource(isolate, object->ToString(isolate)); php_printf("object(Closure)#%d {\n%*c%s\n", hash, level * 2 + 2, ' ', ToCString(csource)); } else @@ -169,8 +211,8 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local var, int leve php_printf(" (%d) {\n", length); for (unsigned i = 0; i < length; i++) { - v8::Local key = keys->Get(i)->ToString(); - v8::String::Utf8Value kname(key); + v8::Local key = keys->Get(i)->ToString(isolate); + v8::String::Utf8Value kname(isolate, key); php_printf("%*c[\"%s\"] =>\n", level * 2, ' ', ToCString(kname)); v8js_dumper(isolate, object->Get(key), level + 1); } @@ -207,7 +249,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 +258,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; @@ -407,7 +449,7 @@ V8JS_METHOD(require) } if(Z_TYPE(module_code) == IS_OBJECT) { - v8::Local newobj = zval_to_v8js(&module_code, isolate)->ToObject(); + v8::Local newobj = zval_to_v8js(&module_code, isolate)->ToObject(isolate); c->modules_loaded[normalised_module_id].Reset(isolate, newobj); info.GetReturnValue().Set(newobj); @@ -435,6 +477,7 @@ V8JS_METHOD(require) // Set script identifier v8::Local sname = V8JS_STR(normalised_module_id); + v8::ScriptOrigin origin(sname); if (Z_STRLEN(module_code) > std::numeric_limits::max()) { zend_throw_exception(php_ce_v8js_exception, @@ -445,11 +488,11 @@ V8JS_METHOD(require) v8::Local source = V8JS_ZSTR(Z_STR(module_code)); zval_ptr_dtor(&module_code); - source = v8::String::Concat(V8JS_SYM("(function (exports, module, 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 script = v8::Script::Compile(source, sname); + v8::MaybeLocal script = v8::Script::Compile(context, source, &origin); // The script will be empty if there are compile errors if (script.IsEmpty()) { @@ -474,7 +517,7 @@ V8JS_METHOD(require) c->modules_stack.push_back(normalised_module_id); // Run script to evaluate closure - v8::Local module_function = script->Run(); + v8::MaybeLocal module_function = script.ToLocalChecked()->Run(context); // Prepare exports & module object v8::Local exports = v8::Object::New(isolate); @@ -483,7 +526,7 @@ V8JS_METHOD(require) module->Set(V8JS_SYM("id"), V8JS_STR(normalised_module_id)); module->Set(V8JS_SYM("exports"), exports); - if (module_function->IsFunction()) { + if (!module_function.IsEmpty() && module_function.ToLocalChecked()->IsFunction()) { v8::Local *jsArgv = static_cast *>(alloca(3 * sizeof(v8::Local))); new(&jsArgv[0]) v8::Local; jsArgv[0] = exports; @@ -495,7 +538,7 @@ V8JS_METHOD(require) jsArgv[2] = require_fn.ToLocalChecked(); // actually call the module - v8::Local::Cast(module_function)->Call(exports, 3, jsArgv); + v8::Local::Cast(module_function.ToLocalChecked())->Call(exports, 3, jsArgv); } // Remove this module and path from the stack @@ -503,7 +546,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; diff --git a/v8js_object_export.cc b/v8js_object_export.cc index 3568871..6a01009 100644 --- a/v8js_object_export.cc +++ b/v8js_object_export.cc @@ -100,9 +100,9 @@ static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v 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) { + if (info[i]->IsObject() && info[i]->ToObject(isolate)->InternalFieldCount() == 2) { /* This is a PHP object, passed to JS and back. */ - zend_object *object = reinterpret_cast(info[i]->ToObject()->GetAlignedPointerFromInternalField(1)); + zend_object *object = reinterpret_cast(info[i]->ToObject(isolate)->GetAlignedPointerFromInternalField(1)); ZVAL_OBJ(&fci.params[i], object); Z_ADDREF_P(&fci.params[i]); } else { @@ -404,7 +404,7 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfoSet(result_len++, V8JS_FLOAT((double) index)->ToString()); + result->Set(result_len++, V8JS_FLOAT((double) index)->ToString(isolate)); } } ZEND_HASH_FOREACH_END(); @@ -430,9 +430,9 @@ static void v8js_invoke_callback(const v8::FunctionCallbackInfo& info if (info.IsConstructCall()) { v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0); - v8::Local str = self->GetConstructorName()->ToString(); - v8::String::Utf8Value str_value(str); - zend_string *constructor_name = zend_string_init(ToCString(str_value), str->Utf8Length(), 0); + v8::MaybeLocal str = self->GetConstructorName()->ToString(isolate); + v8::String::Utf8Value str_value(isolate, str.ToLocalChecked()); + zend_string *constructor_name = zend_string_init(ToCString(str_value), str.ToLocalChecked()->Utf8Length(isolate), 0); zend_class_entry *ce = zend_lookup_class(constructor_name); zend_string_release(constructor_name); @@ -529,10 +529,30 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo& info) return; } - v8::Local str = info[0]->ToString(); - v8::String::Utf8Value str_value(str); + v8::MaybeLocal str = info[0]->ToString(isolate); + + 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::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(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.ToLocalChecked()->Utf8Length(isolate), 0); // okay, look up the method name and manually invoke it. const zend_object_handlers *h = object->handlers; @@ -577,13 +597,15 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo& info) /* This method handles named property and method get/set/query/delete. */ template -v8::Local v8js_named_property_callback(v8::Local property, const v8::PropertyCallbackInfo &info, property_op_t callback_type, v8::Local set_value) /* {{{ */ +v8::Local v8js_named_property_callback(v8::Local property_name, const v8::PropertyCallbackInfo &info, property_op_t callback_type, v8::Local set_value) /* {{{ */ { + v8::Local property = v8::Local::Cast(property_name); + v8::Isolate *isolate = info.GetIsolate(); 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 = property->Utf8Length(isolate); char *lower = estrndup(name, name_len); zend_string *method_name; @@ -780,32 +802,44 @@ v8::Local v8js_named_property_callback(v8::Local property } /* }}} */ -static void v8js_named_property_getter(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ +static void v8js_named_property_getter(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ { info.GetReturnValue().Set(v8js_named_property_callback(property, info, V8JS_PROP_GETTER)); } /* }}} */ -static void v8js_named_property_setter(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo &info) /* {{{ */ +static void v8js_named_property_setter(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo &info) /* {{{ */ { info.GetReturnValue().Set(v8js_named_property_callback(property, info, V8JS_PROP_SETTER, value)); } /* }}} */ -static void v8js_named_property_query(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ +static void v8js_named_property_query(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ { v8::Local 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 value = r->ToInteger(isolate->GetEnteredContext()); + if (!value.IsEmpty()) { + info.GetReturnValue().Set(value.ToLocalChecked()); } } /* }}} */ -static void v8js_named_property_deleter(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ +static void v8js_named_property_deleter(v8::Local property, const v8::PropertyCallbackInfo &info) /* {{{ */ { v8::Local 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 value = r->ToBoolean(isolate->GetEnteredContext()); + if (!value.IsEmpty()) { + info.GetReturnValue().Set(value.ToLocalChecked()); } } /* }}} */ @@ -847,8 +881,8 @@ static v8::MaybeLocal v8js_wrap_object(v8::Isolate *isolate, zend_cl /* We'll free persist_tpl_ when template_cache is destroyed */ v8::Local 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 +918,14 @@ static v8::MaybeLocal 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); diff --git a/v8js_object_export.h b/v8js_object_export.h index fda29a7..3bbc840 100644 --- a/v8js_object_export.h +++ b/v8js_object_export.h @@ -25,7 +25,7 @@ typedef enum { } property_op_t; template -v8::Local v8js_named_property_callback(v8::Local property, +v8::Local v8js_named_property_callback(v8::Local property, const v8::PropertyCallbackInfo &info, property_op_t callback_type, v8::Local set_value = v8::Local()); diff --git a/v8js_v8.cc b/v8js_v8.cc index 607fb42..6abc0a4 100644 --- a/v8js_v8.cc +++ b/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::Isolate *) >& v8_call) /* {{{ */ + std::function< v8::MaybeLocal(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 result = v8_call(c->isolate); + v8::MaybeLocal 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,24 +262,27 @@ 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 context = v8::Local::New(isolate, ctx->context); + v8::Local source = V8JS_STR("for(;;);"); - v8::Local script = v8::Script::Compile(source); + v8::Local script = v8::Script::Compile(context, source).ToLocalChecked(); isolate->TerminateExecution(); - script->Run(); + script->Run(context); } /* }}} */ int v8js_get_properties_hash(v8::Local jsValue, HashTable *retval, int flags, v8::Isolate *isolate) /* {{{ */ { - v8::Local jsObj = jsValue->ToObject(); + v8::Local jsObj = jsValue->ToObject(isolate); if (!jsObj.IsEmpty()) { v8::Local jsKeys = jsObj->GetPropertyNames(); for (unsigned i = 0; i < jsKeys->Length(); i++) { - v8::Local jsKey = jsKeys->Get(i)->ToString(); + v8::Local jsKey = jsKeys->Get(i)->ToString(isolate); /* Skip any prototype properties */ if (!jsObj->HasOwnProperty(isolate->GetEnteredContext(), jsKey).FromMaybe(false) @@ -289,15 +292,15 @@ int v8js_get_properties_hash(v8::Local jsValue, HashTable *retval, in } v8::Local jsVal = jsObj->Get(jsKey); - v8::String::Utf8Value cstr(jsKey); + v8::String::Utf8Value cstr(isolate, jsKey); const char *c_key = ToCString(cstr); - zend_string *key = zend_string_init(c_key, jsKey->ToString()->Utf8Length(), 0); + zend_string *key = zend_string_init(c_key, jsKey->ToString(isolate)->Utf8Length(isolate), 0); zval value; ZVAL_UNDEF(&value); - if (jsVal->IsObject() && jsVal->ToObject()->InternalFieldCount() == 2) { + if (jsVal->IsObject() && jsVal->ToObject(isolate)->InternalFieldCount() == 2) { /* This is a PHP object, passed to JS and back. */ - zend_object *object = reinterpret_cast(jsVal->ToObject()->GetAlignedPointerFromInternalField(1)); + zend_object *object = reinterpret_cast(jsVal->ToObject(isolate)->GetAlignedPointerFromInternalField(1)); ZVAL_OBJ(&value, object); Z_ADDREF_P(&value); } diff --git a/v8js_v8.h b/v8js_v8.h index 16d35f8..e8ebbb6 100644 --- a/v8js_v8.h +++ b/v8js_v8.h @@ -56,7 +56,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::Isolate *) >& v8_call); + std::function< v8::MaybeLocal(v8::Isolate *) >& v8_call); void v8js_terminate_execution(v8::Isolate *isolate); /* Fetch V8 object properties */ diff --git a/v8js_v8object_class.cc b/v8js_v8object_class.cc index abc32d3..58f249b 100644 --- a/v8js_v8object_class.cc +++ b/v8js_v8object_class.cc @@ -73,7 +73,7 @@ static int v8js_v8object_has_property(zval *object, zval *member, int has_set_ex return retval; } - v8::Local jsObj = v8obj->ToObject(); + v8::Local jsObj = v8obj->ToObject(isolate); v8::Local jsKey = V8JS_STRL(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))); v8::Local jsVal; @@ -90,7 +90,7 @@ static int v8js_v8object_has_property(zval *object, zval *member, int has_set_ex retval = !( jsVal->IsNull() || jsVal->IsUndefined() ); } else { /* empty() */ - retval = jsVal->BooleanValue(); + retval = jsVal->BooleanValue(v8_context).FromMaybe(false); /* for PHP compatibility, [] should also be empty */ if (jsVal->IsArray() && retval) { v8::Local array = v8::Local::Cast(jsVal); @@ -98,10 +98,10 @@ static int v8js_v8object_has_property(zval *object, zval *member, int has_set_ex } /* for PHP compatibility, '0' should also be empty */ if (jsVal->IsString() && retval) { - v8::Local str = jsVal->ToString(); - if (str->Length() == 1) { + v8::MaybeLocal str = jsVal->ToString(isolate); + if (!str.IsEmpty() && str.ToLocalChecked()->Length() == 1) { uint16_t c = 0; - str->Write(&c, 0, 1); + str.ToLocalChecked()->Write(isolate, &c, 0, 1); if (c == '0') { retval = false; } @@ -137,7 +137,7 @@ static zval *v8js_v8object_read_property(zval *object, zval *member, int type, v return retval; } - v8::Local jsObj = v8obj->ToObject(); + v8::Local jsObj = v8obj->ToObject(isolate); v8::Local jsKey = V8JS_STRL(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))); v8::Local jsVal; @@ -175,7 +175,7 @@ static void v8js_v8object_write_property(zval *object, zval *member, zval *value } if (v8obj->IsObject()) { - v8obj->ToObject()->CreateDataProperty(v8_context, V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))), zval_to_v8js(value, isolate)); + v8obj->ToObject(isolate)->CreateDataProperty(v8_context, V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))), zval_to_v8js(value, isolate)); } } /* }}} */ @@ -200,7 +200,7 @@ static void v8js_v8object_unset_property(zval *object, zval *member, void **cach } if (v8obj->IsObject()) { - v8obj->ToObject()->Delete(V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member)))); + v8obj->ToObject(isolate)->Delete(V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member)))); } } /* }}} */ @@ -276,7 +276,7 @@ static zend_function *v8js_v8object_get_method(zend_object **object_ptr, zend_st v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (!obj->v8obj.IsEmpty() && v8obj->IsObject() && !v8obj->IsFunction()) { - v8::Local jsObj = v8obj->ToObject(); + v8::Local jsObj = v8obj->ToObject(isolate); if (jsObj->Has(jsKey) && jsObj->Get(jsKey)->IsFunction()) { f = (zend_function *) ecalloc(1, sizeof(*f)); @@ -321,15 +321,16 @@ 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::Isolate *) > v8_call = [obj, method, argc, argv, object, &return_value](v8::Isolate *isolate) { + std::function< v8::MaybeLocal(v8::Isolate *) > v8_call = [obj, method, argc, argv, object, &return_value](v8::Isolate *isolate) { int i = 0; + v8::Local context = v8::Local::New(isolate, obj->ctx->context); v8::Local method_name = V8JS_SYML(ZSTR_VAL(method), static_cast(ZSTR_LEN(method))); - v8::Local v8obj = v8::Local::New(isolate, obj->v8obj)->ToObject(); + v8::Local v8obj = v8::Local::New(isolate, obj->v8obj)->ToObject(isolate); v8::Local thisObj; v8::Local cb; - if (method_name->Equals(V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME))) { + if (method_name->Equals(context, V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME)).FromMaybe(false)) { cb = v8::Local::Cast(v8obj); } else { cb = v8::Local::Cast(v8obj->Get(method_name)); @@ -545,9 +546,9 @@ 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::Isolate *) > v8_call = [g](v8::Isolate *isolate) { + std::function< v8::MaybeLocal(v8::Isolate *) > v8_call = [g](v8::Isolate *isolate) { v8::Local method_name = V8JS_STR("next"); - v8::Local v8obj = v8::Local::New(isolate, g->v8obj.v8obj)->ToObject(); + v8::Local v8obj = v8::Local::New(isolate, g->v8obj.v8obj)->ToObject(isolate); v8::Local cb = v8::Local::Cast(v8obj->Get(method_name));; v8::Local result = cb->Call(v8obj, 0, NULL); @@ -563,7 +564,7 @@ static void v8js_v8generator_next(v8js_v8generator *g) /* {{{ */ return V8JS_NULL; } - v8::Local resultObj = result->ToObject(); + v8::Local resultObj = result->ToObject(isolate); v8::Local val = resultObj->Get(V8JS_STR("value")); v8::Local done = resultObj->Get(V8JS_STR("done"));