From 0945749e7cf1d3d6f23cc3c7f976dcb0f55915e3 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Thu, 7 Jan 2016 13:44:40 +0100 Subject: [PATCH 1/8] add basic test on issue #183 --- tests/issue_183_basic.phpt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/issue_183_basic.phpt diff --git a/tests/issue_183_basic.phpt b/tests/issue_183_basic.phpt new file mode 100644 index 0000000..3a0cca5 --- /dev/null +++ b/tests/issue_183_basic.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test V8::executeString() : Method access on derived classes +--SKIPIF-- + +--FILE-- +executeString($JS); + +?> +===EOF=== +--EXPECT-- +Hello World +===EOF=== From 3508f0c8e7dd005e6cb8e645755cb30853eb9941 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 8 Jan 2016 15:42:43 +0100 Subject: [PATCH 2/8] export public methods of classes derived from \V8Js, closes #183 --- php_v8js_macros.h | 7 +++++ v8js_class.cc | 63 +++++++++++++++++++++++++++++++++++++++++++ v8js_object_export.cc | 7 +---- v8js_object_export.h | 2 ++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/php_v8js_macros.h b/php_v8js_macros.h index 9a2b9e8..f7a0836 100644 --- a/php_v8js_macros.h +++ b/php_v8js_macros.h @@ -85,6 +85,13 @@ extern "C" { #define V8JS_FLAG_FORCE_ARRAY (1<<1) #define V8JS_FLAG_PROPAGATE_PHP_EXCEPTIONS (1<<2) + +/* These are not defined by Zend */ +#define ZEND_WAKEUP_FUNC_NAME "__wakeup" +#define ZEND_SLEEP_FUNC_NAME "__sleep" +#define ZEND_SET_STATE_FUNC_NAME "__set_state" + + /* Convert zval into V8 value */ v8::Handle zval_to_v8js(zval *, v8::Isolate * TSRMLS_DC); diff --git a/v8js_class.cc b/v8js_class.cc index f538a82..27f2457 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -30,6 +30,7 @@ extern "C" { #include "v8js_v8.h" #include "v8js_exceptions.h" #include "v8js_v8object_class.h" +#include "v8js_object_export.h" #include "v8js_timer.h" #include @@ -319,6 +320,10 @@ static void v8js_fatal_error_handler(const char *location, const char *message) } /* }}} */ +#define IS_MAGIC_FUNC(mname) \ + ((key_len == sizeof(mname)) && \ + !strncasecmp(key, mname, key_len - 1)) + /* {{{ proto void V8Js::__construct([string object_name [, array variables [, array extensions [, bool report_uncaught_exceptions]]]) __construct for V8Js */ static PHP_METHOD(V8Js, __construct) @@ -480,7 +485,65 @@ static PHP_METHOD(V8Js, __construct) } } + /* Add pointer to zend object */ + php_obj->SetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY), v8::External::New(isolate, getThis())); + /* Export public methods */ + zend_function *method_ptr; + char *key = NULL; + uint key_len; + + zend_hash_internal_pointer_reset_ex(&c->std.ce->function_table, &pos); + for (;; zend_hash_move_forward_ex(&c->std.ce->function_table, &pos)) { + if (zend_hash_get_current_key_ex(&c->std.ce->function_table, &key, &key_len, &index, 0, &pos) != HASH_KEY_IS_STRING || + zend_hash_get_current_data_ex(&c->std.ce->function_table, (void **) &method_ptr, &pos) == FAILURE + ) { + break; + } + + if ((method_ptr->common.fn_flags & ZEND_ACC_PUBLIC) == 0) { + /* Allow only public methods */ + continue; + } + + if ((method_ptr->common.fn_flags & (ZEND_ACC_CTOR|ZEND_ACC_DTOR|ZEND_ACC_CLONE)) != 0) { + /* no __construct, __destruct(), or __clone() functions */ + continue; + } + + /* hide (do not export) other PHP magic functions */ + if (IS_MAGIC_FUNC(ZEND_CALLSTATIC_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_SLEEP_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_WAKEUP_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_SET_STATE_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_GET_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_SET_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_UNSET_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_CALL_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_INVOKE_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_TOSTRING_FUNC_NAME) || + IS_MAGIC_FUNC(ZEND_ISSET_FUNC_NAME)) { + continue; + } + + v8::Local method_name = V8JS_STR(method_ptr->common.function_name); + v8::Local ft; + + try { + ft = v8::Local::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_tmpl_t *persistent_ft = &c->method_tmpls[method_ptr]; + persistent_ft->Reset(isolate, ft); + } + + + php_obj->ForceSet(method_name, ft->GetFunction()); + } } /* }}} */ diff --git a/v8js_object_export.cc b/v8js_object_export.cc index 04befee..949888f 100644 --- a/v8js_object_export.cc +++ b/v8js_object_export.cc @@ -169,7 +169,7 @@ failure: /* }}} */ /* Callback for PHP methods and functions */ -static void v8js_php_callback(const v8::FunctionCallbackInfo& info) /* {{{ */ +void v8js_php_callback(const v8::FunctionCallbackInfo& info) /* {{{ */ { v8::Isolate *isolate = info.GetIsolate(); v8::Local self = info.Holder(); @@ -291,11 +291,6 @@ static void v8js_weak_closure_callback(const v8::WeakCallbackDataweak_closures.erase(persist_tpl_); }; -/* These are not defined by Zend */ -#define ZEND_WAKEUP_FUNC_NAME "__wakeup" -#define ZEND_SLEEP_FUNC_NAME "__sleep" -#define ZEND_SET_STATE_FUNC_NAME "__set_state" - #define IS_MAGIC_FUNC(mname) \ ((key_len == sizeof(mname)) && \ !strncasecmp(key, mname, key_len - 1)) diff --git a/v8js_object_export.h b/v8js_object_export.h index 72c48a7..ea697b4 100644 --- a/v8js_object_export.h +++ b/v8js_object_export.h @@ -30,4 +30,6 @@ v8::Local v8js_named_property_callback(v8::Local property property_op_t callback_type, v8::Local set_value = v8::Local()); +void v8js_php_callback(const v8::FunctionCallbackInfo& info); + #endif /* V8JS_OBJECT_EXPORT_H */ From e0f990bfa1ad7c2aa799cfecd88ad4c5813b678c Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 8 Jan 2016 15:47:24 +0100 Subject: [PATCH 3/8] Add test with private/protected methods, refs #183 --- tests/issue_183_001.phpt | 30 ++++++++++++++++++++++++++++++ tests/issue_183_002.phpt | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/issue_183_001.phpt create mode 100644 tests/issue_183_002.phpt diff --git a/tests/issue_183_001.phpt b/tests/issue_183_001.phpt new file mode 100644 index 0000000..8469940 --- /dev/null +++ b/tests/issue_183_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test V8::executeString() : Method access on derived classes (protected) +--SKIPIF-- + +--FILE-- +executeString($JS); + +?> +===EOF=== +--EXPECTF-- +Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: PHP.hello is not a function' in %s +Stack trace: +#0 %s: V8Js->executeString('PHP.hello();') +#1 {main} + thrown in %s on line 16 diff --git a/tests/issue_183_002.phpt b/tests/issue_183_002.phpt new file mode 100644 index 0000000..fa8704b --- /dev/null +++ b/tests/issue_183_002.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test V8::executeString() : Method access on derived classes (private) +--SKIPIF-- + +--FILE-- +executeString($JS); + +?> +===EOF=== +--EXPECTF-- +Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: PHP.hello is not a function' in %s +Stack trace: +#0 %s: V8Js->executeString('PHP.hello();') +#1 {main} + thrown in %s on line 16 From 1e86e2c9f79ea46f6990f1cef62137a247e902f2 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 9 Jan 2016 19:02:58 +0100 Subject: [PATCH 4/8] Don't export methods of V8Js object to V8 --- tests/issue_183_003.phpt | 57 ++++++++++++++++++++++++++++++++++++++++ v8js_class.cc | 18 ++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/issue_183_003.phpt diff --git a/tests/issue_183_003.phpt b/tests/issue_183_003.phpt new file mode 100644 index 0000000..1a96c3f --- /dev/null +++ b/tests/issue_183_003.phpt @@ -0,0 +1,57 @@ +--TEST-- +Test V8::executeString() : Method access on derived classes (V8Js methods) +--SKIPIF-- + +--FILE-- +executeString($JS); + +?> +===EOF=== +--EXPECTF-- +string(8) "function" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(9) "undefined" +string(6) "caught" +===EOF=== diff --git a/v8js_class.cc b/v8js_class.cc index 27f2457..5b121ae 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -46,6 +46,9 @@ static zend_class_entry *php_ce_v8js; static zend_object_handlers v8js_object_handlers; /* }}} */ +/* Forward declare v8js_methods, actually "static" but not possible in C++ */ +extern const zend_function_entry v8js_methods[]; + typedef struct _v8js_script { char *name; v8js_ctx *ctx; @@ -526,6 +529,19 @@ static PHP_METHOD(V8Js, __construct) continue; } + const zend_function_entry *fe; + for (fe = v8js_methods; fe->fname; fe ++) { + if (fe->fname == method_ptr->common.function_name) { + break; + } + } + + if(fe->fname) { + /* Method belongs to \V8Js class itself, never export to V8, even if + * it is overriden in a derived class. */ + continue; + } + v8::Local method_name = V8JS_STR(method_ptr->common.function_name); v8::Local ft; @@ -1119,7 +1135,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_setmemorylimit, 0, 0, 1) ZEND_END_ARG_INFO() -static const zend_function_entry v8js_methods[] = { /* {{{ */ +const zend_function_entry v8js_methods[] = { /* {{{ */ PHP_ME(V8Js, __construct, arginfo_v8js_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(V8Js, __sleep, arginfo_v8js_sleep, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) PHP_ME(V8Js, __wakeup, arginfo_v8js_sleep, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) From d438624a3d30db4bb8019a91dc0cf30276a9df3b Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 9 Jan 2016 19:11:30 +0100 Subject: [PATCH 5/8] Don't export V8Js methods even if overwritten --- tests/issue_183_004.phpt | 44 ++++++++++++++++++++++++++++++++++++++++ v8js_class.cc | 4 ++-- 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/issue_183_004.phpt diff --git a/tests/issue_183_004.phpt b/tests/issue_183_004.phpt new file mode 100644 index 0000000..e4d2f17 --- /dev/null +++ b/tests/issue_183_004.phpt @@ -0,0 +1,44 @@ +--TEST-- +Test V8::executeString() : Method access on derived classes (overridden V8Js methods) +--SKIPIF-- + +--FILE-- +executeString($JS); + +?> +===EOF=== +--EXPECTF-- +string(13) "executeString" +string(8) "function" +string(9) "undefined" +string(6) "caught" +===EOF=== diff --git a/v8js_class.cc b/v8js_class.cc index 5b121ae..a472209 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -531,7 +531,7 @@ static PHP_METHOD(V8Js, __construct) const zend_function_entry *fe; for (fe = v8js_methods; fe->fname; fe ++) { - if (fe->fname == method_ptr->common.function_name) { + if (strcmp(fe->fname, method_ptr->common.function_name) == 0) { break; } } @@ -1186,7 +1186,7 @@ static void v8js_unset_property(zval *object, zval *member ZEND_HASH_KEY_DC TSRM /* 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(); - + /* Delete value from PHP JS object */ jsobj->Delete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); From 4a8a8b812fc6dbb2d5249200a25f47e079461b0f Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 9 Jan 2016 19:15:12 +0100 Subject: [PATCH 6/8] Add test that V8Js::__sleep and V8Js::__wakeup are final --- tests/issue_183_005.phpt | 19 +++++++++++++++++++ tests/issue_183_006.phpt | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/issue_183_005.phpt create mode 100644 tests/issue_183_006.phpt diff --git a/tests/issue_183_005.phpt b/tests/issue_183_005.phpt new file mode 100644 index 0000000..2cff423 --- /dev/null +++ b/tests/issue_183_005.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test V8::executeString() : Method access on derived classes (__sleep) +--SKIPIF-- + +--FILE-- + +===EOF=== +--EXPECTF-- +Fatal error: Cannot override final method V8Js::__sleep() in %s diff --git a/tests/issue_183_006.phpt b/tests/issue_183_006.phpt new file mode 100644 index 0000000..395e905 --- /dev/null +++ b/tests/issue_183_006.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test V8::executeString() : Method access on derived classes (__wakeup) +--SKIPIF-- + +--FILE-- + +===EOF=== +--EXPECTF-- +Fatal error: Cannot override final method V8Js::__wakeup() in %s From 796ae85cb62e71a3e14918ebb7d351d174de10aa Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 9 Jan 2016 21:36:30 +0100 Subject: [PATCH 7/8] Adapt tests to different V8 versions --- tests/issue_183_001.phpt | 2 +- tests/issue_183_002.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/issue_183_001.phpt b/tests/issue_183_001.phpt index 8469940..2896e1b 100644 --- a/tests/issue_183_001.phpt +++ b/tests/issue_183_001.phpt @@ -23,7 +23,7 @@ $v8->executeString($JS); ?> ===EOF=== --EXPECTF-- -Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: PHP.hello is not a function' in %s +Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s is not a function' in %s Stack trace: #0 %s: V8Js->executeString('PHP.hello();') #1 {main} diff --git a/tests/issue_183_002.phpt b/tests/issue_183_002.phpt index fa8704b..f79d5e8 100644 --- a/tests/issue_183_002.phpt +++ b/tests/issue_183_002.phpt @@ -23,7 +23,7 @@ $v8->executeString($JS); ?> ===EOF=== --EXPECTF-- -Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: PHP.hello is not a function' in %s +Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s is not a function' in %s Stack trace: #0 %s: V8Js->executeString('PHP.hello();') #1 {main} From 3808f69deac8586cb2996f1d32fbc84dd1a35aea Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 9 Jan 2016 21:45:10 +0100 Subject: [PATCH 8/8] Adapt tests V8 3.24 also --- tests/issue_183_001.phpt | 2 +- tests/issue_183_002.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/issue_183_001.phpt b/tests/issue_183_001.phpt index 2896e1b..921fec6 100644 --- a/tests/issue_183_001.phpt +++ b/tests/issue_183_001.phpt @@ -23,7 +23,7 @@ $v8->executeString($JS); ?> ===EOF=== --EXPECTF-- -Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s is not a function' in %s +Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s' in %s Stack trace: #0 %s: V8Js->executeString('PHP.hello();') #1 {main} diff --git a/tests/issue_183_002.phpt b/tests/issue_183_002.phpt index f79d5e8..1745fdf 100644 --- a/tests/issue_183_002.phpt +++ b/tests/issue_183_002.phpt @@ -23,7 +23,7 @@ $v8->executeString($JS); ?> ===EOF=== --EXPECTF-- -Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s is not a function' in %s +Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s' in %s Stack trace: #0 %s: V8Js->executeString('PHP.hello();') #1 {main}