From 81d44187e9dbe176d4debcf41a1311adc459c11b Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 29 Oct 2013 13:28:16 -0400 Subject: [PATCH] Issue #72: Simplify export of V8Js properties to JavaScript. Instead of using special JavaScript accessors for the `PHP` object, we just reuse the standard PHP object wrapping. To control which properties are visible to JavaScript, we use a separate PHP V8UserProperties object for the JavaScript-visible stuff. We need to use __get/__set/etc magic methods (instead of the object handler equivalents) in order to make things work properly with derived classes. --- config.m4 | 2 +- php_v8js_macros.h | 15 +--- v8js.cc | 195 ++++++++++++++++++++++++---------------------- v8js_variables.cc | 104 ------------------------- 4 files changed, 102 insertions(+), 214 deletions(-) delete mode 100644 v8js_variables.cc diff --git a/config.m4 b/config.m4 index 4baa05a7..f5e2509b 100644 --- a/config.m4 +++ b/config.m4 @@ -100,7 +100,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], CPPFLAGS=$old_CPPFLAGS ]); - PHP_NEW_EXTENSION(v8js, v8js.cc v8js_convert.cc v8js_methods.cc v8js_variables.cc v8js_commonjs.cc, $ext_shared, , "-std="$ac_cv_v8_cstd) + PHP_NEW_EXTENSION(v8js, v8js.cc v8js_convert.cc v8js_methods.cc v8js_commonjs.cc, $ext_shared, , "-std="$ac_cv_v8_cstd) PHP_ADD_MAKEFILE_FRAGMENT fi diff --git a/php_v8js_macros.h b/php_v8js_macros.h index d0e8cf7f..59dec68c 100644 --- a/php_v8js_macros.h +++ b/php_v8js_macros.h @@ -142,22 +142,10 @@ v8::Handle zval_to_v8js(zval *, v8::Isolate * TSRMLS_DC); /* Convert V8 value into zval */ int v8js_to_zval(v8::Handle, zval *, int, v8::Isolate * TSRMLS_DC); -struct php_v8js_accessor_ctx -{ - char *variable_name_string; - uint variable_name_string_len; - v8::Isolate *isolate; -}; - -void php_v8js_accessor_ctx_dtor(php_v8js_accessor_ctx * TSRMLS_DC); - -/* Register accessors into passed object */ -void php_v8js_register_accessors(std::vector *accessor_list, v8::Local, zval *, v8::Isolate * TSRMLS_DC); - - /* {{{ Context container */ struct php_v8js_ctx { zend_object std; + zval user_properties; v8::Persistent object_name; v8::Persistent context; zend_bool report_uncaught; @@ -171,7 +159,6 @@ struct php_v8js_ctx { std::vector modules_stack; std::vector modules_base; std::map template_cache; - std::vector accessor_list; #ifdef ZTS void ***zts_ctx; #endif diff --git a/v8js.cc b/v8js.cc index ec56e004..a0ae2b3e 100644 --- a/v8js.cc +++ b/v8js.cc @@ -67,6 +67,7 @@ ZEND_INI_END() zend_class_entry *php_ce_v8_object; zend_class_entry *php_ce_v8_function; static zend_class_entry *php_ce_v8js; +static zend_class_entry *php_ce_v8js_user_properties; static zend_class_entry *php_ce_v8js_script_exception; static zend_class_entry *php_ce_v8js_time_limit_exception; static zend_class_entry *php_ce_v8js_memory_limit_exception; @@ -303,7 +304,6 @@ int php_v8js_v8_get_properties_hash(v8::Handle jsValue, HashTable *re static HashTable *php_v8js_v8_get_properties(zval *object TSRMLS_DC) /* {{{ */ { php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC); - HashTable *retval; if (obj->properties == NULL) { if (GC_G(gc_active)) { @@ -522,6 +522,7 @@ static void php_v8js_free_storage(void *object TSRMLS_DC) /* {{{ */ php_v8js_ctx *c = (php_v8js_ctx *) object; zend_object_std_dtor(&c->std TSRMLS_CC); + zval_dtor(&c->user_properties); if (c->pending_exception) { zval_ptr_dtor(&c->pending_exception); @@ -554,13 +555,6 @@ static void php_v8js_free_storage(void *object TSRMLS_DC) /* {{{ */ } c->template_cache.~map(); - /* Clear contexts */ - for (std::vector::iterator it = c->accessor_list.begin(); - it != c->accessor_list.end(); ++it) { - php_v8js_accessor_ctx_dtor(*it TSRMLS_CC); - } - c->accessor_list.~vector(); - /* Clear global object, dispose context */ if (!c->context.IsEmpty()) { c->context.Reset(); @@ -591,7 +585,8 @@ static zend_object_value php_v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */ c = (php_v8js_ctx *) ecalloc(1, sizeof(*c)); zend_object_std_init(&c->std, ce TSRMLS_CC); TSRMLS_SET_CTX(c->zts_ctx); - + INIT_ZVAL(c->user_properties); + object_init_ex(&c->user_properties, php_ce_v8js_user_properties); #if PHP_VERSION_ID >= 50400 object_properties_init(&c->std, ce); #else @@ -607,7 +602,6 @@ static zend_object_value php_v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */ new(&c->modules_stack) std::vector(); new(&c->modules_base) std::vector(); new(&c->template_cache) std::map(); - new(&c->accessor_list) std::vector(); retval.handle = zend_objects_store_put(c, NULL, (zend_objects_free_object_storage_t) php_v8js_free_storage, NULL TSRMLS_CC); retval.handlers = &v8js_object_handlers; @@ -718,13 +712,63 @@ static void php_v8js_init(TSRMLS_D) /* {{{ */ } /* }}} */ +static PHP_METHOD(V8Js, __get) +{ + zval *object = getThis(), *value; + char *name; + int name_length; + php_v8js_ctx *ctx = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_length) == FAILURE) { + return; + } + value = zend_read_property(NULL, &ctx->user_properties, name, name_length, 0 TSRMLS_CC); + RETURN_ZVAL(value, 1, 0); +} + +static PHP_METHOD(V8Js, __set) +{ + zval *object = getThis(), *value; + char *name; + int name_length; + php_v8js_ctx *ctx = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &name_length, &value) == FAILURE) { + return; + } + object = &ctx->user_properties; + zend_update_property(NULL, &ctx->user_properties, name, name_length, value TSRMLS_CC); + RETURN_TRUE; +} + +static PHP_METHOD(V8Js, __unset) +{ + zval *object = getThis(), *member, *userprop; + php_v8js_ctx *ctx = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &member) == FAILURE) { + return; + } + userprop = &ctx->user_properties; + Z_OBJ_HT_P(userprop)->unset_property(userprop, member ZEND_HASH_KEY_NULL TSRMLS_CC); + RETURN_TRUE; +} + +static PHP_METHOD(V8Js, __isset) +{ + zval *object = getThis(), *member, *userprop; + php_v8js_ctx *ctx = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &member) == FAILURE) { + return; + } + userprop = &ctx->user_properties; + bool r = Z_OBJ_HT_P(userprop)->has_property(userprop, member, 0 ZEND_HASH_KEY_NULL TSRMLS_CC); + RETURN_BOOL(r); +} + /* {{{ proto void V8Js::__construct([string object_name [, array variables [, array extensions [, bool report_uncaught_exceptions]]]) __construct for V8Js */ static PHP_METHOD(V8Js, __construct) { - char *object_name = NULL, *class_name = NULL; - int object_name_len = 0, free = 0; - zend_uint class_name_len = 0; + char *object_name = NULL; + int object_name_len = 0; zend_bool report_uncaught = 1; zval *vars_arr = NULL, *exts_arr = NULL; const char **exts = NULL; @@ -754,6 +798,13 @@ static PHP_METHOD(V8Js, __construct) c->memory_limit_hit = false; c->module_loader = NULL; + if (vars_arr) { + HashTable *vartable = HASH_OF(vars_arr); + zend_hash_merge(Z_OBJPROP_P(&c->user_properties), HASH_OF(vars_arr), + (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), + 0); + } + /* Include extensions used by this context */ /* Note: Extensions registered with auto_enable do not need to be added separately like this. */ if (exts_arr) @@ -809,60 +860,16 @@ static PHP_METHOD(V8Js, __construct) /* Enter context */ v8::Context::Scope context_scope(context); - /* Create the PHP container object's function template */ - v8::Local php_obj_t = v8::FunctionTemplate::New(); - - /* Set class name for PHP object */ -#if PHP_VERSION_ID >= 50400 - free = !zend_get_object_classname(getThis(), const_cast(&class_name), &class_name_len TSRMLS_CC); -#else - free = !zend_get_object_classname(getThis(), &class_name, &class_name_len TSRMLS_CC); -#endif - php_obj_t->SetClassName(V8JS_SYML(class_name, class_name_len)); - - if (free) { - efree(class_name); - } - - /* Register Get accessor for passed variables */ - if (vars_arr && zend_hash_num_elements(Z_ARRVAL_P(vars_arr)) > 0) { - php_v8js_register_accessors(&c->accessor_list, php_obj_t, vars_arr, isolate TSRMLS_CC); - } + /* Create the PHP container object */ + v8::Local php_obj = zval_to_v8js(&c->user_properties, isolate TSRMLS_CC); /* Set name for the PHP JS object */ v8::Local object_name_js = (object_name_len) ? V8JS_SYML(object_name, object_name_len) : V8JS_SYM("PHP"); c->object_name.Reset(isolate, object_name_js); /* Add the PHP object into global object */ - v8::Local php_obj = php_obj_t->InstanceTemplate()->NewInstance(); V8JS_GLOBAL->Set(object_name_js, php_obj, v8::ReadOnly); - /* Export public property values */ - HashTable *properties = zend_std_get_properties(getThis() TSRMLS_CC); - HashPosition pos; - zval **value; - ulong index; - char *member; - uint member_len; - - for(zend_hash_internal_pointer_reset_ex(properties, &pos); - zend_hash_get_current_data_ex(properties, (void **) &value, &pos) == SUCCESS; - zend_hash_move_forward_ex(properties, &pos)) { - if(zend_hash_get_current_key_ex(properties, &member, &member_len, &index, 0, &pos) != HASH_KEY_IS_STRING) { - continue; - } - - zval zmember; - ZVAL_STRING(&zmember, member, 0); - - zend_property_info *property_info = zend_get_property_info(c->std.ce, &zmember, 1 TSRMLS_CC); - if(property_info && property_info->flags & ZEND_ACC_PUBLIC) { - /* Write value to PHP JS object */ - php_obj->ForceSet(V8JS_SYML(member, member_len - 1), zval_to_v8js(*value, isolate TSRMLS_CC), v8::ReadOnly); - } - } - - } /* }}} */ @@ -1339,6 +1346,23 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_construct, 0, 0, 0) ZEND_ARG_INFO(0, report_uncaught_exceptions) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_get, 0, 0, 1) + ZEND_ARG_INFO(0, member) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_set, 0, 0, 2) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_isset, 0, 0, 1) + ZEND_ARG_INFO(0, member) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_unset, 0, 0, 1) + ZEND_ARG_INFO(0, member) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_executestring, 0, 0, 1) ZEND_ARG_INFO(0, script) ZEND_ARG_INFO(0, identifier) @@ -1381,6 +1405,10 @@ ZEND_END_ARG_INFO() static const zend_function_entry v8js_methods[] = { /* {{{ */ PHP_ME(V8Js, __construct, arginfo_v8js_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(V8Js, __get, arginfo_v8js_get, ZEND_ACC_PUBLIC) + PHP_ME(V8Js, __set, arginfo_v8js_set, ZEND_ACC_PUBLIC) + PHP_ME(V8Js, __unset, arginfo_v8js_unset, ZEND_ACC_PUBLIC) + PHP_ME(V8Js, __isset, arginfo_v8js_isset, ZEND_ACC_PUBLIC) PHP_ME(V8Js, executeString, arginfo_v8js_executestring, ZEND_ACC_PUBLIC) PHP_ME(V8Js, getPendingException, arginfo_v8js_getpendingexception, ZEND_ACC_PUBLIC) PHP_ME(V8Js, setModuleLoader, arginfo_v8js_setmoduleloader, ZEND_ACC_PUBLIC) @@ -1396,41 +1424,11 @@ static const zend_function_entry v8js_methods[] = { /* {{{ */ /* V8Js object handlers */ -static void php_v8js_write_property(zval *object, zval *member, zval *value ZEND_HASH_KEY_DC TSRMLS_DC) /* {{{ */ -{ - V8JS_BEGIN_CTX(c, object) - - /* Check whether member is public, if so, export to V8. */ - zend_property_info *property_info = zend_get_property_info(c->std.ce, member, 1 TSRMLS_CC); - if(property_info->flags & ZEND_ACC_PUBLIC) { - /* Global PHP JS object */ - v8::Local object_name_js = v8::Local::New(isolate, c->object_name); - v8::Local jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject(); - - /* Write value to PHP JS object */ - jsobj->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, isolate TSRMLS_CC), v8::ReadOnly); - } - - /* Write value to PHP object */ - std_object_handlers.write_property(object, member, value ZEND_HASH_KEY_CC TSRMLS_CC); -} -/* }}} */ - -static void php_v8js_unset_property(zval *object, zval *member ZEND_HASH_KEY_DC TSRMLS_DC) /* {{{ */ -{ - V8JS_BEGIN_CTX(c, object) - - /* Global PHP JS object */ - v8::Local object_name_js = v8::Local::New(isolate, c->object_name); - v8::Local jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject(); - - /* Delete value from PHP JS object */ - jsobj->ForceDelete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); - - /* Unset from PHP object */ - std_object_handlers.unset_property(object, member ZEND_HASH_KEY_CC TSRMLS_CC); +static HashTable * php_v8js_get_properties(zval *object TSRMLS_DC) { + /* Use the properties of the V8UserProperties object */ + php_v8js_ctx *ctx = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); + return Z_OBJPROP(ctx->user_properties); } -/* }}} */ /* }}} V8Js */ @@ -1596,6 +1594,12 @@ static PHP_MINIT_FUNCTION(v8js) v8_object_handlers.get_debug_info = php_v8js_v8_get_debug_info; v8_object_handlers.get_closure = php_v8js_v8_get_closure; + /* V8UserProperties Class */ + // These are the user properties set on the V8Js object, which are + // exported to JavaScript. + INIT_CLASS_ENTRY(ce, "V8JsUserProperties", NULL); + php_ce_v8js_user_properties = zend_register_internal_class(&ce TSRMLS_CC); + /* V8Js Class */ INIT_CLASS_ENTRY(ce, "V8Js", v8js_methods); php_ce_v8js = zend_register_internal_class(&ce TSRMLS_CC); @@ -1604,8 +1608,9 @@ static PHP_MINIT_FUNCTION(v8js) /* V8Js handlers */ memcpy(&v8js_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); v8js_object_handlers.clone_obj = NULL; - v8js_object_handlers.write_property = php_v8js_write_property; - v8js_object_handlers.unset_property = php_v8js_unset_property; +#if 1 + v8js_object_handlers.get_properties = php_v8js_get_properties; +#endif /* V8Js Class Constants */ zend_declare_class_constant_string(php_ce_v8js, ZEND_STRL("V8_VERSION"), PHP_V8_VERSION TSRMLS_CC); diff --git a/v8js_variables.cc b/v8js_variables.cc deleted file mode 100644 index 3e6ddf30..00000000 --- a/v8js_variables.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 5 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2013 The PHP Group | - +----------------------------------------------------------------------+ - | http://www.opensource.org/licenses/mit-license.php MIT License | - +----------------------------------------------------------------------+ - | Author: Jani Taskinen | - | Author: Patrick Reilly | - +----------------------------------------------------------------------+ -*/ - -/* $Id:$ */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -extern "C" { -#include "php.h" -} - -#include "php_v8js_macros.h" -#include -#include - -static void php_v8js_fetch_php_variable(v8::Local name, const v8::PropertyCallbackInfo& info) /* {{{ */ -{ - v8::Handle data = v8::Handle::Cast(info.Data()); - php_v8js_accessor_ctx *ctx = static_cast(data->Value()); - v8::Isolate *isolate = ctx->isolate; - zval **variable; - - V8JS_TSRMLS_FETCH(); - - zend_is_auto_global(ctx->variable_name_string, ctx->variable_name_string_len TSRMLS_CC); - - if (zend_hash_find(&EG(symbol_table), ctx->variable_name_string, ctx->variable_name_string_len + 1, (void **) &variable) == SUCCESS) { - info.GetReturnValue().Set(zval_to_v8js(*variable, isolate TSRMLS_CC)); - return; - } -} -/* }}} */ - -void php_v8js_accessor_ctx_dtor(php_v8js_accessor_ctx *ctx TSRMLS_DC) /* {{{ */ -{ - efree(ctx->variable_name_string); - efree(ctx); -} -/* }}} */ - -void php_v8js_register_accessors(std::vector *accessor_list, v8::Local php_obj_t, zval *array, v8::Isolate *isolate TSRMLS_DC) /* {{{ */ -{ - char *property_name; - uint property_name_len; - ulong index; - zval **item; - v8::Local php_obj = php_obj_t->InstanceTemplate(); - - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(array)); - zend_hash_get_current_data(Z_ARRVAL_P(array), (void **) &item) != FAILURE; - zend_hash_move_forward(Z_ARRVAL_P(array)) - ) { - switch (Z_TYPE_PP(item)) - { - /* - case IS_OBJECT: - case IS_ARRAY: - */ - case IS_STRING: - break; - - default: - continue; /* Ignore invalid values */ - } - - if (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &property_name, &property_name_len, &index, 0, NULL) != HASH_KEY_IS_STRING) { - continue; /* Ignore invalid property names */ - } - - // Create context to store accessor data - php_v8js_accessor_ctx *ctx = (php_v8js_accessor_ctx *)emalloc(sizeof(php_v8js_accessor_ctx)); - ctx->variable_name_string = estrdup(Z_STRVAL_PP(item)); - ctx->variable_name_string_len = Z_STRLEN_PP(item); - ctx->isolate = isolate; - - /* Set the variable fetch callback for given symbol on named property */ - php_obj->SetAccessor(V8JS_STRL(property_name, property_name_len - 1), php_v8js_fetch_php_variable, NULL, v8::External::New(ctx), v8::PROHIBITS_OVERWRITING, v8::ReadOnly, v8::AccessorSignature::New(php_obj_t)); - - /* record the context so we can free it later */ - accessor_list->push_back(ctx); - } -} -/* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */