0
0
mirror of https://github.com/phpv8/v8js.git synced 2024-09-19 15:25:19 +00:00

Merge pull request #202 from stesie/issue-183

Export public methods on derived classes to V8
This commit is contained in:
Stefan Siegl 2016-01-10 16:13:58 +01:00
commit 82ffaa2c35
11 changed files with 317 additions and 8 deletions

View File

@ -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<v8::Value> zval_to_v8js(zval *, v8::Isolate * TSRMLS_DC);

30
tests/issue_183_001.phpt Normal file
View File

@ -0,0 +1,30 @@
--TEST--
Test V8::executeString() : Method access on derived classes (protected)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo extends \V8Js
{
protected function hello()
{
print("Hello World\n");
}
}
$JS = <<< EOT
PHP.hello();
EOT;
$v8 = new Foo();
$v8->executeString($JS);
?>
===EOF===
--EXPECTF--
Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s' in %s
Stack trace:
#0 %s: V8Js->executeString('PHP.hello();')
#1 {main}
thrown in %s on line 16

30
tests/issue_183_002.phpt Normal file
View File

@ -0,0 +1,30 @@
--TEST--
Test V8::executeString() : Method access on derived classes (private)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo extends \V8Js
{
private function hello()
{
print("Hello World\n");
}
}
$JS = <<< EOT
PHP.hello();
EOT;
$v8 = new Foo();
$v8->executeString($JS);
?>
===EOF===
--EXPECTF--
Fatal error: Uncaught exception 'V8JsScriptException' with message 'V8Js::compileString():1: TypeError: %s' in %s
Stack trace:
#0 %s: V8Js->executeString('PHP.hello();')
#1 {main}
thrown in %s on line 16

57
tests/issue_183_003.phpt Normal file
View File

@ -0,0 +1,57 @@
--TEST--
Test V8::executeString() : Method access on derived classes (V8Js methods)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo extends \V8Js
{
public function hello()
{
print("Hello World\n");
}
}
$JS = <<< EOT
var_dump(typeof PHP.hello);
var_dump(typeof PHP.executeString);
var_dump(typeof PHP.compileString);
var_dump(typeof PHP.executeScript);
var_dump(typeof PHP.checkString);
var_dump(typeof PHP.getPendingException);
var_dump(typeof PHP.setModuleNormaliser);
var_dump(typeof PHP.setModuleLoader);
var_dump(typeof PHP.registerExtension);
var_dump(typeof PHP.getExtensions);
var_dump(typeof PHP.setTimeLimit);
var_dump(typeof PHP.setMemoryLimit);
try {
PHP.setTimeLimit(100);
}
catch(e) {
var_dump('caught');
}
EOT;
$v8 = new Foo();
$v8->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===

44
tests/issue_183_004.phpt Normal file
View File

@ -0,0 +1,44 @@
--TEST--
Test V8::executeString() : Method access on derived classes (overridden V8Js methods)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo extends \V8Js
{
public function hello()
{
print("Hello World\n");
}
public function executeString($script, $identifier = NULL, $flags = NULL, $time_limit = NULL, $memory_limit = NULL)
{
var_dump("executeString");
return parent::executeString($script);
}
}
$JS = <<< EOT
var_dump(typeof PHP.hello);
var_dump(typeof PHP.executeString);
try {
PHP.executeString('print("blar")');
}
catch(e) {
var_dump('caught');
}
EOT;
$v8 = new Foo();
$v8->executeString($JS);
?>
===EOF===
--EXPECTF--
string(13) "executeString"
string(8) "function"
string(9) "undefined"
string(6) "caught"
===EOF===

19
tests/issue_183_005.phpt Normal file
View File

@ -0,0 +1,19 @@
--TEST--
Test V8::executeString() : Method access on derived classes (__sleep)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo extends \V8Js
{
public function __sleep()
{
var_dump("foo");
}
}
?>
===EOF===
--EXPECTF--
Fatal error: Cannot override final method V8Js::__sleep() in %s

19
tests/issue_183_006.phpt Normal file
View File

@ -0,0 +1,19 @@
--TEST--
Test V8::executeString() : Method access on derived classes (__wakeup)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo extends \V8Js
{
public function __wakeup()
{
var_dump("foo");
}
}
?>
===EOF===
--EXPECTF--
Fatal error: Cannot override final method V8Js::__wakeup() in %s

View File

@ -0,0 +1,27 @@
--TEST--
Test V8::executeString() : Method access on derived classes
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo extends \V8Js
{
public function hello()
{
print("Hello World\n");
}
}
$JS = <<< EOT
PHP.hello();
EOT;
$v8 = new Foo();
$v8->executeString($JS);
?>
===EOF===
--EXPECT--
Hello World
===EOF===

View File

@ -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 <functional>
@ -45,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;
@ -319,6 +323,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 +488,78 @@ 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;
}
const zend_function_entry *fe;
for (fe = v8js_methods; fe->fname; fe ++) {
if (strcmp(fe->fname, method_ptr->common.function_name) == 0) {
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<v8::String> method_name = V8JS_STR(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_tmpl_t *persistent_ft = &c->method_tmpls[method_ptr];
persistent_ft->Reset(isolate, ft);
}
php_obj->ForceSet(method_name, ft->GetFunction());
}
}
/* }}} */
@ -1056,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)

View File

@ -169,7 +169,7 @@ failure:
/* }}} */
/* Callback for PHP methods and functions */
static void v8js_php_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
void v8js_php_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
{
v8::Isolate *isolate = info.GetIsolate();
v8::Local<v8::Object> self = info.Holder();
@ -291,11 +291,6 @@ static void v8js_weak_closure_callback(const v8::WeakCallbackData<v8::Object, v8
ctx->weak_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))

View File

@ -30,4 +30,6 @@ v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> property
property_op_t callback_type,
v8::Local<v8::Value> set_value = v8::Local<v8::Value>());
void v8js_php_callback(const v8::FunctionCallbackInfo<v8::Value>& info);
#endif /* V8JS_OBJECT_EXPORT_H */