0
0
mirror of https://github.com/phpv8/v8js.git synced 2024-12-22 12:51:52 +00:00

Merge open PRs into php7

This commit is contained in:
Stefan Siegl 2018-11-06 13:51:10 +01:00
commit 85097c1d68
No known key found for this signature in database
GPG Key ID: 73942AF5642F3DDA
23 changed files with 595 additions and 348 deletions

1
.gitignore vendored
View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

4
Vagrantfile vendored
View File

@ -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

View File

@ -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"

View File

@ -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,23 +53,23 @@ 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])
],[])
],[])
@ -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

View File

@ -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');

View File

@ -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)

View File

@ -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');

View File

@ -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);

View File

@ -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 */

View File

@ -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);
}
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);

View File

@ -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) {

View File

@ -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());
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;

View File

@ -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;
}
/* }}} */

View File

@ -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
{
v8::Local<v8::Array> keys = object->GetOwnPropertyNames();
uint32_t length = keys->Length();
php_printf("<empty>\n");
}
}
else
{
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);

View File

@ -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();

View File

@ -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>());

View File

@ -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,42 +262,61 @@ 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);
if (!jsObj.IsEmpty()) {
v8::Local<v8::Array> jsKeys = jsObj->GetPropertyNames();
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;
}
for (unsigned i = 0; i < jsKeys->Length(); i++)
{
v8::Local<v8::String> jsKey = jsKeys->Get(i)->ToString();
v8::Local<v8::Value> jsKeySlot;
v8::Local<v8::String> jsKey;
/* Skip any prototype properties */
if (!jsObj->HasOwnProperty(isolate->GetEnteredContext(), jsKey).FromMaybe(false)
&& !jsObj->HasRealNamedProperty(jsKey)
&& !jsObj->HasRealNamedCallbackProperty(jsKey)) {
if (!jsKeys->Get(v8_context, i).ToLocal(&jsKeySlot)
|| !jsKeySlot->ToString(v8_context).ToLocal(&jsKey)) {
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);
/* 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;
if (!jsObj->Get(v8_context, jsKey).ToLocal(&jsVal)) {
continue;
}
v8::String::Utf8Value cstr(isolate, jsKey);
zend_string *key = zend_string_init(ToCString(cstr), cstr.length(), 0);
zval value;
ZVAL_UNDEF(&value);
if (jsVal->IsObject() && jsVal->ToObject()->InternalFieldCount() == 2) {
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 *>(jsVal->ToObject()->GetAlignedPointerFromInternalField(1));
zend_object *object = reinterpret_cast<zend_object *>(jsValObject->GetAlignedPointerFromInternalField(1));
ZVAL_OBJ(&value, object);
Z_ADDREF_P(&value);
}
@ -318,8 +337,6 @@ int v8js_get_properties_hash(v8::Local<v8::Value> jsValue, HashTable *retval, in
}
return SUCCESS;
}
return FAILURE;
}
/* }}} */

View File

@ -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 */

View File

@ -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() || !v8obj->ToObject(v8_context).ToLocal(&jsObj)) {
return false;
}
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;
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;
v8::Local<v8::String> jsKey = V8JS_ZSYM(Z_STR_P(member));
/* Skip any prototype properties */
if (jsObj->HasRealNamedProperty(jsKey) || jsObj->HasRealNamedCallbackProperty(jsKey)) {
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! */
retval = true;
} else {
return true;
}
/* We need to look at the value. */
jsVal = jsObj->Get(jsKey);
v8::Local<v8::Value> jsVal = jsObj->Get(v8_context, jsKey).ToLocalChecked();
if (has_set_exists == 0 ) {
/* isset(): We make 'undefined' equivalent to 'null' */
retval = !( jsVal->IsNull() || jsVal->IsUndefined() );
} else {
return !( jsVal->IsNull() || jsVal->IsUndefined() );
}
/* empty() */
retval = jsVal->BooleanValue();
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 */
if (jsVal->IsString() && retval) {
v8::Local<v8::String> str = jsVal->ToString();
if (str->Length() == 1) {
v8::Local<v8::String> str;
if (jsVal->IsString() && retval && jsVal->ToString(v8_context).ToLocal(&str)
&& str->Length() == 1) {
uint16_t c = 0;
str->Write(&c, 0, 1);
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();
v8::Local<v8::Object> jsObj;
v8::Local<v8::Value> jsObjSlot;
if (jsObj->Has(jsKey) && jsObj->Get(jsKey)->IsFunction()) {
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);