From 40b0ca6ab393bd03a312f2fb6ec8f364c3dd4b02 Mon Sep 17 00:00:00 2001 From: yjiq150 Date: Wed, 13 Sep 2017 13:33:20 +0900 Subject: [PATCH] PHP7 basic support - Test case successes [8/12] - registerFunction with closure is not currently working --- .gitignore | 44 +++++++ php_spidermonkey.h | 14 +- spidermonkey.cc | 257 ++++++++++++++++++------------------- spidermonkey_context.cc | 119 ++++++++--------- spidermonkey_external.cc | 228 +++++++++++++++----------------- spidermonkey_streams.cc | 10 +- tests/js_context_info.phpt | 10 ++ tests/js_evaluate.phpt | 11 ++ tests/js_functions.phpt | 4 +- tests/js_objects.phpt | 4 +- 10 files changed, 362 insertions(+), 339 deletions(-) create mode 100644 tests/js_context_info.phpt create mode 100644 tests/js_evaluate.phpt diff --git a/.gitignore b/.gitignore index 65e3544..9a07414 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,47 @@ *~ /.project /.settings +.deps +acinclude.m4 +aclocal.m4 +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +install-sh +libtool +ltmain.sh +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +missing +mkinstalldirs +run-tests.php +spidermonkey.loT +autom4te.cache/output.0 +autom4te.cache/requests +autom4te.cache/traces.0 +build/ax_check_compile_flag.m4 +build/libtool.m4 +build/mkdep.awk +build/scan_makefile_in.awk +build/shtool +.vscode +*.diff +*.log +*.exp +tests/*.sh +tests/*.out +tests/*.php +*.so +*.o +*.lo +*.la +.libs +tmp-php.ini diff --git a/php_spidermonkey.h b/php_spidermonkey.h index 612c7f2..23c79b9 100644 --- a/php_spidermonkey.h +++ b/php_spidermonkey.h @@ -67,12 +67,6 @@ ZEND_END_MODULE_GLOBALS(spidermonkey) #define PHPJS_START(cx) JS_BeginRequest(cx) #define PHPJS_END(cx) JS_EndRequest(cx) -// useful for iterating on php hashtables -#define PHPJS_FOREACH(ht) for (zend_hash_internal_pointer_reset(ht); zend_hash_has_more_elements(ht) == SUCCESS; zend_hash_move_forward(ht)) -#define PHPJS_FOREACH_ENTRY(ht,dest) if (zend_hash_get_current_data(ht,(void**)& dest) == FAILURE) {\ - continue;\ -} - /* Used by JSContext to store callbacks */ typedef struct _php_callback { zend_fcall_info fci; @@ -112,10 +106,10 @@ extern zend_class_entry *php_spidermonkey_jsc_entry; /* this method defined in spidermonkey.c allow us to convert a jsval * to a zval for PHP use */ -void php_jsobject_set_property(JSContext *ctx, JSObject *obj, char *property_name, zval *val TSRMLS_DC); -#define jsval_to_zval(rval, ctx, jval) _jsval_to_zval(rval, ctx, jval, NULL TSRMLS_CC) -void _jsval_to_zval(zval *return_value, JSContext *ctx, JS::MutableHandle rval, php_jsparent *parent TSRMLS_DC); -void zval_to_jsval(zval *val, JSContext *ctx, jsval *jval TSRMLS_DC); +void php_jsobject_set_property(JSContext *ctx, JSObject *obj, char *property_name, zval *val); +#define jsval_to_zval(rval, ctx, jval) _jsval_to_zval(rval, ctx, jval, NULL) +void _jsval_to_zval(zval *return_value, JSContext *ctx, JS::MutableHandle rval, php_jsparent *parent); +void zval_to_jsval(zval *val, JSContext *ctx, jsval *jval); /* init/shutdown functions */ PHP_MINIT_FUNCTION(spidermonkey); diff --git a/spidermonkey.cc b/spidermonkey.cc index 06dcec2..d732c7a 100644 --- a/spidermonkey.cc +++ b/spidermonkey.cc @@ -77,9 +77,13 @@ zend_function_entry php_spidermonkey_jsc_functions[] = { static zend_object_handlers jscontext_object_handlers; -static void php_jscontext_object_free_storage(void *object TSRMLS_DC) +static inline php_jscontext_object* php_jscontext_fetch_object(zend_object *obj) { + return (php_jscontext_object *)((char *)obj - XtOffsetOf(php_jscontext_object, zo)); +} + +static void php_jscontext_object_free_storage(zend_object *object) { - php_jscontext_object *intern = (php_jscontext_object *)object; + php_jscontext_object *intern = php_jscontext_fetch_object(object); // if a context is found ( which should be the case ) // destroy it @@ -94,24 +98,15 @@ static void php_jscontext_object_free_storage(void *object TSRMLS_DC) FREE_HASHTABLE(intern->ec_ht); } - zend_object_std_dtor(&intern->zo TSRMLS_CC); - efree(object); + zend_object_std_dtor(&intern->zo); } -static zend_object_value php_jscontext_object_new_ex(zend_class_entry *class_type, php_jscontext_object **ptr TSRMLS_DC) +static zend_object* php_jscontext_object_new(zend_class_entry *ce) { - zval *tmp; - zend_object_value retval; php_jscontext_object *intern; /* Allocate memory for it */ - intern = (php_jscontext_object *) emalloc(sizeof(php_jscontext_object)); - memset(intern, 0, sizeof(php_jscontext_object)); - - if (ptr) - { - *ptr = intern; - } + intern = (php_jscontext_object *) ecalloc(1, sizeof(php_jscontext_object) + zend_object_properties_size(ce)); /* if no runtime is found create one */ if (SPIDERMONKEY_G(rt) == NULL) @@ -124,7 +119,7 @@ static zend_object_value php_jscontext_object_new_ex(zend_class_entry *class_typ zend_hash_init(intern->ec_ht, 20, NULL, NULL, 0); /* prepare hashtable for callback storage */ - intern->jsref = (php_jsobject_ref*)emalloc(sizeof(php_jsobject_ref)); + intern->jsref = (php_jsobject_ref*)ecalloc(1, sizeof(php_jsobject_ref)); /* create callback hashtable */ ALLOC_HASHTABLE(intern->jsref->ht); zend_hash_init(intern->jsref->ht, 50, NULL, NULL, 0); @@ -159,16 +154,16 @@ static zend_object_value php_jscontext_object_new_ex(zend_class_entry *class_typ JSAutoRequest ar(intern->ct); /* says that our script runs in global scope */ - JS_SetOptions(intern->ct, JSOPTION_VAROBJFIX | JSOPTION_ASMJS); - + JS_SetOptions(intern->ct, JS_GetOptions(intern->ct) | JSOPTION_VAROBJFIX | JSOPTION_ASMJS); + /* set the error callback */ JS_SetErrorReporter(intern->ct, reportError); /* create global object for execution */ intern->obj = JS_NewGlobalObject(intern->ct, &intern->global_class, nullptr); - intern->cpt = JS_EnterCompartment(intern->ct, intern->obj); JS_SetGlobalObject(intern->ct, intern->obj); + intern->cpt = JS_EnterCompartment(intern->ct, intern->obj); /* initialize standard JS classes */ JS_InitStandardClasses(intern->ct, intern->obj); @@ -177,17 +172,24 @@ static zend_object_value php_jscontext_object_new_ex(zend_class_entry *class_typ JS_SetPrivate(intern->obj, intern->jsref); /* create zend object */ - zend_object_std_init(&intern->zo, class_type TSRMLS_CC); + zend_object_std_init(&intern->zo, ce); + object_properties_init(&intern->zo, ce); + + intern->zo.handlers = &jscontext_object_handlers; - retval.handle = zend_objects_store_put(intern, NULL, (zend_objects_free_object_storage_t) php_jscontext_object_free_storage, NULL TSRMLS_CC); - retval.handlers = (zend_object_handlers *) &jscontext_object_handlers; PHPJS_END(intern->ct); - return retval; + return &intern->zo; } -static zend_object_value php_jscontext_object_new(zend_class_entry *class_type TSRMLS_DC) +static HashTable * get_gc(zval *object, zval **gc_data, int *gc_count)/*{{{*/ +{ + *gc_data = NULL; + *gc_count = 0; + return zend_std_get_properties(object); +} + +static void php_spidermonkey_init_globals(zend_spidermonkey_globals *hello_globals) { - return php_jscontext_object_new_ex(class_type, NULL TSRMLS_CC); } /** @@ -218,23 +220,25 @@ PHP_MINIT_FUNCTION(spidermonkey) REGISTER_LONG_CONSTANT("JSVERSION_DEFAULT", JSVERSION_DEFAULT, CONST_CS | CONST_PERSISTENT); // CLASS INIT -#ifdef ZTS - //ts_allocate_id(&spidermonkey_globals_id, sizeof(spidermonkey_globals), NULL, NULL); - ZEND_INIT_MODULE_GLOBALS(spidermonkey, NULL, NULL); -#endif + ZEND_DECLARE_MODULE_GLOBALS(spidermonkey); + ZEND_INIT_MODULE_GLOBALS(spidermonkey, php_spidermonkey_init_globals, NULL); SPIDERMONKEY_G(rt) = NULL; // here we set handlers to zero, meaning that we have no handlers set memcpy(&jscontext_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + jscontext_object_handlers.offset = XtOffsetOf(php_jscontext_object, zo); + jscontext_object_handlers.get_gc = get_gc; + jscontext_object_handlers.clone_obj = NULL; + jscontext_object_handlers.free_obj = php_jscontext_object_free_storage; // init JSContext class INIT_CLASS_ENTRY(ce, PHP_SPIDERMONKEY_JSC_NAME, php_spidermonkey_jsc_functions); // this function will be called when the object is created by php ce.create_object = php_jscontext_object_new; // register class in PHP - php_spidermonkey_jsc_entry = zend_register_internal_class(&ce TSRMLS_CC); - + php_spidermonkey_jsc_entry = zend_register_internal_class(&ce); + return SUCCESS; } @@ -269,12 +273,12 @@ PHP_MINFO_FUNCTION(spidermonkey) PHP_INI_MH(spidermonkey_ini_update) { // if the runtime is already here, emit a warning if (SPIDERMONKEY_G(rt) != NULL) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "JS runtime is already started, update of spidermonkey.gc_mem_threshold ignored."); + php_error_docref(NULL, E_WARNING, "JS runtime is already started, update of spidermonkey.gc_mem_threshold ignored."); } } /* convert a given jsval in a context to a zval, for PHP access */ -void _jsval_to_zval(zval *return_value, JSContext *ctx, JS::MutableHandle rval, php_jsparent *parent TSRMLS_DC) +void _jsval_to_zval(zval *return_value, JSContext *ctx, JS::MutableHandle rval, php_jsparent *parent) { PHPJS_START(ctx); @@ -303,7 +307,7 @@ void _jsval_to_zval(zval *return_value, JSContext *ctx, JS::MutableHandle 0) { /* then we retrieve the pointer to the string */ char *txt = JS_EncodeString(ctx, str); - RETVAL_STRINGL(txt, strlen(txt), 1); + RETVAL_STRINGL(txt, strlen(txt)); JS_free(ctx, txt); } else @@ -347,7 +351,7 @@ void _jsval_to_zval(zval *return_value, JSContext *ctx, JS::MutableHandleobj == NULL) + if ((jsref = (php_jsobject_ref*)JS_GetInstancePrivate(ctx, obj, &intern->script_class, NULL)) == NULL || jsref->obj == NULL) { zobj = NULL; while (parent != NULL) { @@ -461,20 +459,19 @@ void _jsval_to_zval(zval *return_value, JSContext *ctx, JS::MutableHandlesetString(jstr); break; - case IS_BOOL: - *jval = BOOLEAN_TO_JSVAL(Z_BVAL_P(val)); + case IS_TRUE: + *jval = BOOLEAN_TO_JSVAL(1); + break; + case IS_FALSE: + *jval = BOOLEAN_TO_JSVAL(0); break; - case IS_RESOURCE: + case IS_RESOURCE: { + intern = (php_jscontext_object*)JS_GetContextPrivate(ctx); /* create JSObject */ jobj = JS_NewObject(ctx, &intern->script_class, NULL, NULL); jsref = (php_jsobject_ref*)emalloc(sizeof(php_jsobject_ref)); /* store pointer to object */ - SEPARATE_ARG_IF_REF(val); + // todo: is this necessary? this causes zend_object memory leak. + // SEPARATE_ARG_IF_REF(val); jsref->ht = NULL; jsref->obj = val; /* auto define functions for stream */ - php_stream *stream; - php_stream_from_zval_no_verify(stream, &val); + php_stream_from_zval_no_verify(stream, val); if (stream != NULL) { /* set a bunch of constants */ @@ -563,7 +562,10 @@ void zval_to_jsval(zval *val, JSContext *ctx, jsval *jval TSRMLS_DC) *jval = OBJECT_TO_JSVAL(jobj); break; - case IS_OBJECT: + + } + case IS_OBJECT: { + intern = (php_jscontext_object*)JS_GetContextPrivate(ctx); /* create JSObject */ jobj = JS_NewObject(ctx, &intern->script_class, NULL, NULL); @@ -573,7 +575,9 @@ void zval_to_jsval(zval *val, JSContext *ctx, jsval *jval TSRMLS_DC) ALLOC_HASHTABLE(jsref->ht); zend_hash_init(jsref->ht, 50, NULL, NULL, 0); - SEPARATE_ARG_IF_REF(val); + // todo: is this necessary? this causes zend_object memory leak. + // on FinalizePHP callback, ref count of val had changed to weird number. + //SEPARATE_ARG_IF_REF(val); /* store pointer to object */ jsref->obj = val; @@ -582,55 +586,50 @@ void zval_to_jsval(zval *val, JSContext *ctx, jsval *jval TSRMLS_DC) /* retrieve class entry */ ce = Z_OBJCE_P(val); + /* get function table */ - ht = &ce->function_table; + HashTable *func_ht = &ce->function_table; + /* foreach functions */ - for(zend_hash_internal_pointer_reset(ht); zend_hash_has_more_elements(ht) == SUCCESS; zend_hash_move_forward(ht)) - { - char *key; - uint keylen; - php_callback cb; - zval *z_fname; - - /* retrieve current key */ - zend_hash_get_current_key_ex(ht, &key, &keylen, 0, 0, NULL); - if (zend_hash_get_current_data(ht, (void**)&fptr) == FAILURE) { - /* Should never actually fail - * since the key is known to exist. */ - continue; - } - - /* store the function name as a zval */ - MAKE_STD_ZVAL(z_fname); - ZVAL_STRING(z_fname, fptr->common.function_name, 1); - + zend_string *key; + ulong num_key; + zval* zval_func; + + ZEND_HASH_FOREACH_KEY_VAL(func_ht, num_key, key, zval_func) { + + php_callback *callback = (php_callback*)ecalloc(1, sizeof(php_callback)); + + zend_function* fptr = Z_FUNC_P(zval_func); + + zval function_name; + ZVAL_STR(&function_name, fptr->common.function_name); + /* then build the zend_fcall_info and cache */ - cb.fci.size = sizeof(cb.fci); - cb.fci.function_table = &ce->function_table; - cb.fci.function_name = z_fname; - cb.fci.symbol_table = NULL; - cb.fci.object_ptr = val; - cb.fci.retval_ptr_ptr = NULL; - cb.fci.param_count = fptr->common.num_args; - cb.fci.params = NULL; - cb.fci.no_separation = 1; - - cb.fci_cache.initialized = 1; - cb.fci_cache.function_handler = fptr; - cb.fci_cache.calling_scope = ce; - cb.fci_cache.object_ptr = val; + callback->fci.size = sizeof(callback->fci); + callback->fci.function_name = function_name; + callback->fci.retval = NULL; + callback->fci.params = NULL; + callback->fci.object = Z_OBJ_P(val); + callback->fci.no_separation = 1; + + callback->fci_cache.initialized = 1; + callback->fci_cache.function_handler = fptr; + callback->fci_cache.calling_scope = ce; + callback->fci_cache.object = Z_OBJ_P(val); /* store them */ - zend_hash_add(jsref->ht, fptr->common.function_name, strlen(fptr->common.function_name), &cb, sizeof(cb), NULL); + zend_hash_add_new_ptr(jsref->ht, fptr->common.function_name, callback); /* define the function */ - JS_DefineFunction(ctx, jobj, fptr->common.function_name, generic_call, 1, 0); - } + JS_DefineFunction(ctx, jobj, ZSTR_VAL(fptr->common.function_name), generic_call, 1, 0); + } ZEND_HASH_FOREACH_END(); + *jval = OBJECT_TO_JSVAL(jobj); break; - case IS_ARRAY: + } + case IS_ARRAY: { /* retrieve the array hash table */ - ht = HASH_OF(val); + HashTable *array_ht = HASH_OF(val); /* create JSObject */ jobj = JS_NewArrayObject(ctx, 0, nullptr); @@ -638,47 +637,37 @@ void zval_to_jsval(zval *val, JSContext *ctx, jsval *jval TSRMLS_DC) // prevent GC JS_AddObjectRoot(ctx, &jobj); - /* foreach item */ - for(zend_hash_internal_pointer_reset(ht); zend_hash_has_more_elements(ht) == SUCCESS; zend_hash_move_forward(ht)) - { - char *key; - uint keylen; - ulong idx; - int type; - zval **ppzval; - char intIdx[25]; - - /* retrieve current key */ - type = zend_hash_get_current_key_ex(ht, &key, &keylen, &idx, 0, NULL); - if (zend_hash_get_current_data(ht, (void**)&ppzval) == FAILURE) { - /* Should never actually fail - * since the key is known to exist. */ - continue; - } + zend_string *string_key = NULL; + zend_ulong num_key = 0; + zval *z_value; - if (type == HASH_KEY_IS_LONG) + ZEND_HASH_FOREACH_KEY_VAL(array_ht, num_key, string_key, z_value) { + + if (string_key) + { + php_jsobject_set_property(ctx, jobj, ZSTR_VAL(string_key), z_value); + } + else { - //sprintf(intIdx, "%ld", idx); - //php_jsobject_set_property(ctx, jobj, intIdx, *ppzval TSRMLS_CC); jsval jarrval; - + /* first convert zval to jsval */ - zval_to_jsval(*ppzval, ctx, &jarrval TSRMLS_CC); + zval_to_jsval(z_value, ctx, &jarrval); /* no ref behavior, just set a property */ //JSBool res = JS_SetProperty(ctx, obj, property_name, &jval); //JSBool res = JS_SetElement(ctx, jobj, idx, &jarrval); - JSBool res = JS_DefineElement(ctx, jobj, idx, jarrval, nullptr, nullptr, 0); - + JSBool res = JS_DefineElement(ctx, jobj, num_key, jarrval, nullptr, nullptr, 0); } - else - { - php_jsobject_set_property(ctx, jobj, key, *ppzval TSRMLS_CC); - } - } + + } ZEND_HASH_FOREACH_END(); + *jval = OBJECT_TO_JSVAL(jobj); + + JS_RemoveObjectRoot(ctx, &jobj); break; + } case IS_NULL: *jval = JSVAL_NULL; break; diff --git a/spidermonkey_context.cc b/spidermonkey_context.cc index be07465..57676c2 100644 --- a/spidermonkey_context.cc +++ b/spidermonkey_context.cc @@ -27,35 +27,42 @@ */ zend_class_entry *php_spidermonkey_jsc_entry; +// todo: might use zend_closure to fix closure error on test cases. +// https://github.com/php/php-src/blob/1189fe572944932b4d517aa8039596fb1892d5b7/Zend/zend_closures.c +/* +PHP_METHOD(JSContext, registerClosure) +{ +} +*/ + /* {{{ proto public bool JSContext::registerFunction(string name, callback function) Register a PHP function in a Javascript context allowing a script to call it*/ PHP_METHOD(JSContext, registerFunction) { - char *name = NULL; - int name_len = 0; - php_callback callback; + zend_string *name = NULL; + php_callback *callback; php_jscontext_object *intern; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|s", &callback.fci, &callback.fci_cache, &name, &name_len) == FAILURE) { + // efree(callback) on JS_FinalizePHP + callback = (php_callback*)ecalloc(1, sizeof(php_callback)); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "f|S", &callback->fci, &callback->fci_cache, &name) == FAILURE) { RETURN_NULL(); } /* retrieve this class from the store */ - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); PHPJS_START(intern->ct); - Z_ADDREF_P(callback.fci.function_name); - /* TODO: error management is needed here, we should throw an exception if the "name" entry * already exists */ if (name == NULL) { - name = Z_STRVAL_P(callback.fci.function_name); - name_len = Z_STRLEN_P(callback.fci.function_name); + name = Z_STR(callback->fci.function_name); } - zend_hash_add(intern->jsref->ht, name, name_len, &callback, sizeof(callback), NULL); - JS_DefineFunction(intern->ct, intern->obj, name, generic_call, 1, 0); + zend_hash_add_new_ptr(intern->jsref->ht, name, callback); + JS_DefineFunction(intern->ct, intern->obj, ZSTR_VAL(name), generic_call, 1, 0); PHPJS_END(intern->ct); } @@ -65,45 +72,43 @@ PHP_METHOD(JSContext, registerFunction) Register a PHP function in a Javascript context allowing a script to call it*/ PHP_METHOD(JSContext, registerClass) { - char *class_name = NULL; - int class_name_len = 0; - char *exported_name = NULL; - int exported_name_len = 0; + zend_string *class_name = NULL; + zend_string *exported_name = NULL; php_jscontext_object *intern; zend_class_entry *ce = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &class_name, &class_name_len, &exported_name, &exported_name_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &class_name, &exported_name) == FAILURE) { RETURN_NULL(); } /* retrieve this class from the store */ - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); PHPJS_START(intern->ct); - if (class_name_len) { - zend_class_entry **pce; - if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC) == FAILURE) { - if (!EG(exception)) { - PHPJS_END(intern->ct); - zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Class %s doesn't exists !", class_name); - return; - } + if (ZSTR_LEN(class_name)) { + ce = zend_lookup_class(class_name); + if (ce == NULL) { + PHPJS_END(intern->ct); + zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0, "Class %s doesn't exists !", ZSTR_VAL(class_name)); + return; } - ce = *pce; } JSClass *reClass = (JSClass*)emalloc(sizeof(JSClass)); memcpy(reClass, &intern->script_class, sizeof(intern->script_class)); if (exported_name != NULL) { - zend_hash_add(intern->ec_ht, exported_name, exported_name_len, &ce, sizeof(zend_class_entry**), NULL); - reClass->name = exported_name; + zend_hash_add_new_ptr(intern->ec_ht, exported_name, ce); + reClass->name = ZSTR_VAL(exported_name); } else { - zend_hash_add(intern->ec_ht, class_name, class_name_len, &ce, sizeof(zend_class_entry**), NULL); - reClass->name = class_name; + zend_hash_add_new_ptr(intern->ec_ht, class_name, ce); + reClass->name = ZSTR_VAL(class_name); } JS_InitClass(intern->ct, intern->obj, nullptr, reClass, generic_constructor, 1, nullptr, nullptr, nullptr, nullptr); + + efree(reClass); + /* TODO: error management is needed here, we should throw an exception if the "name" entry * already exists */ /*if (exported_name != NULL) { @@ -125,18 +130,17 @@ PHP_METHOD(JSContext, registerClass) Register a value in Javascript's global scope*/ PHP_METHOD(JSContext, assign) { - char *name; - int name_len; - zval *val = NULL; + zend_string *name; + zval *val; php_jscontext_object *intern; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &name_len, &val) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &name, &val) == FAILURE) { RETURN_NULL(); } /* retrieve this class from the store */ - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); - php_jsobject_set_property(intern->ct, intern->obj, name, val TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); + php_jsobject_set_property(intern->ct, intern->obj, ZSTR_VAL(name), val); } /* }}} */ @@ -147,23 +151,21 @@ PHP_METHOD(JSContext, assign) in any JSObject in the same context. */ PHP_METHOD(JSContext, evaluateScript) { - char *script; - char *script_name = NULL; - int script_len, script_name_len = 0; + zend_string *script; + php_jscontext_object *intern; jsval rval; /* retrieve script */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "s|s", &script, &script_len, &script_name, &script_name_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &script) == FAILURE) { RETURN_FALSE; } - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); PHPJS_START(intern->ct); - - if (JS_EvaluateScript(intern->ct, intern->obj, script, script_len, script_name, 0, &rval) == JS_TRUE) + + if (JS_EvaluateScript(intern->ct, intern->obj, ZSTR_VAL(script), ZSTR_LEN(script), "evaluateScript", 0, &rval) == JS_TRUE) { if (!rval.isNullOrUndefined()) { @@ -192,12 +194,11 @@ PHP_METHOD(JSContext, setOptions) long options; long old_options; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "l", &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &options) == FAILURE) { RETURN_NULL(); } - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); old_options = JS_SetOptions(intern->ct, options); if (JS_GetOptions(intern->ct) == options) @@ -220,12 +221,11 @@ PHP_METHOD(JSContext, toggleOptions) long options; long old_options; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "l", &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &options) == FAILURE) { RETURN_NULL(); } - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); old_options = JS_ToggleOptions(intern->ct, options); if (JS_GetOptions(intern->ct) == (old_options ^ options)) @@ -245,7 +245,7 @@ PHP_METHOD(JSContext, getOptions) { php_jscontext_object *intern; - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); RETVAL_LONG(JS_GetOptions(intern->ct)); } @@ -260,12 +260,11 @@ PHP_METHOD(JSContext, setVersion) long version; long old_version; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "l", &version) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &version) == FAILURE) { RETURN_NULL(); } - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); // old_version = JS_SetVersion(intern->ct, version); if (JS_GetVersion(intern->ct) == version) @@ -285,7 +284,7 @@ PHP_METHOD(JSContext, getVersion) { php_jscontext_object *intern; - intern = (php_jscontext_object *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = (php_jscontext_object *) Z_OBJ_P(getThis()); RETVAL_LONG(JS_GetVersion(intern->ct)); } @@ -296,18 +295,14 @@ PHP_METHOD(JSContext, getVersion) PHP_METHOD(JSContext, getVersionString) { const char *version_str; - int l; - long version; + zend_long version; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "l", &version) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &version) == FAILURE) { RETURN_NULL(); } version_str = JS_VersionToString((JSVersion)version); - l = strlen(version_str); - - RETVAL_STRINGL(estrndup(version_str, l), l, 0); + RETVAL_STRINGL(version_str, strlen(version_str)); } /* }}} */ diff --git a/spidermonkey_external.cc b/spidermonkey_external.cc index fe631ca..06057e2 100644 --- a/spidermonkey_external.cc +++ b/spidermonkey_external.cc @@ -22,6 +22,7 @@ #include "php_spidermonkey.h" #include "zend_exceptions.h" + /* The error reporter callback. */ /* TODO: change that to an exception */ void reportError(JSContext *cx, const char *message, JSErrorReport *report) @@ -30,22 +31,22 @@ void reportError(JSContext *cx, const char *message, JSErrorReport *report) if ((report->flags & JSREPORT_WARNING) || (report->flags & JSREPORT_STRICT)) { // emit a warning - php_error_docref(NULL TSRMLS_CC, report->flags & JSREPORT_WARNING ? E_WARNING : E_STRICT, message); + php_error_docref(NULL, report->flags & JSREPORT_WARNING ? E_WARNING : E_STRICT, message); } else { /* throw error */ - zend_throw_exception(zend_exception_get_default(TSRMLS_C), (char *) message, report->errorNumber TSRMLS_CC); + zend_throw_exception(zend_exception_get_default(TSRMLS_C), (char *) message, report->errorNumber); } } /* this function set a property on an object */ -void php_jsobject_set_property(JSContext *ctx, JSObject *obj, char *property_name, zval *val TSRMLS_DC) +void php_jsobject_set_property(JSContext *ctx, JSObject *obj, char *property_name, zval *val) { jsval jval; PHPJS_START(ctx); /* first convert zval to jsval */ - zval_to_jsval(val, ctx, &jval TSRMLS_CC); + zval_to_jsval(val, ctx, &jval); /* no ref behavior, just set a property */ JSBool res = JS_SetProperty(ctx, obj, property_name, &jval); @@ -53,6 +54,8 @@ void php_jsobject_set_property(JSContext *ctx, JSObject *obj, char *property_nam PHPJS_END(ctx); } + +// call "PHP function" from JS and convert "return value of PHP func" to jsval /* all function calls are mapped through this unique function */ JSBool generic_call(JSContext *cx, unsigned argc, jsval *vp) { @@ -60,8 +63,9 @@ JSBool generic_call(JSContext *cx, unsigned argc, jsval *vp) JSFunction *func; JSString *jfunc_name; JSClass *jclass; - char *func_name; - zval ***params, *retval_ptr = NULL; + zend_string *func_name; + zval *params = NULL; + zval retval; php_callback *callback; php_jscontext_object *intern; php_jsobject_ref *jsref; @@ -77,7 +81,11 @@ JSBool generic_call(JSContext *cx, unsigned argc, jsval *vp) func = JS_ValueToFunction(cx, argv.calleev()); jfunc_name = JS_GetFunctionId(func); /* because version 1.8.5 supports unicode, we must encode strings */ - func_name = JS_EncodeString(cx, jfunc_name); + + char * jsString = JS_EncodeString(cx, jfunc_name); + func_name = zend_string_init(jsString, strlen(jsString), 0); + /* free function name */ + JS_free(cx, jsString); intern = (php_jscontext_object*)JS_GetContextPrivate(cx); jclass = &intern->script_class; @@ -91,55 +99,56 @@ JSBool generic_call(JSContext *cx, unsigned argc, jsval *vp) jclass =&intern->global_class; } - if ((jsref = (php_jsobject_ref*)JS_GetPrivate(obj)) == nullptr) + if ((jsref = (php_jsobject_ref*)JS_GetInstancePrivate(cx, obj, jclass, NULL)) == NULL) { - zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Failed to retrieve function table", 0 TSRMLS_CC); + zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Failed to retrieve function table", 0); } /* search for function callback */ - if (zend_hash_find(jsref->ht, func_name, strlen(func_name), (void**)&callback) == FAILURE) { - zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Failed to retrieve function callback", 0 TSRMLS_CC); + callback = (php_callback*)zend_hash_find_ptr(jsref->ht, func_name); + if (callback == NULL) { + zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Failed to retrieve function callback", 0); } - - /* free function name */ - JS_free(cx, func_name); - + /* ready parameters */ - params = (zval***)emalloc(argc * sizeof(zval**)); + params = (zval*)ecalloc(1, argc * sizeof(zval)); for (i = 0; i < argc; i++) { - zval **val = (zval**)emalloc(sizeof(zval*)); - MAKE_STD_ZVAL(*val); - jsval_to_zval(*val, cx, JS::MutableHandleValue::fromMarkedLocation(&argv[i])); - params[i] = val; + zval *val = &(params[i]); + jsval_to_zval(val, cx, JS::MutableHandleValue::fromMarkedLocation(&argv[i])); + } + + if (argc == 0) { + callback->fci.params = NULL; + } else { + callback->fci.params = params; } - callback->fci.params = params; callback->fci.param_count = argc; - callback->fci.retval_ptr_ptr = &retval_ptr; + callback->fci.retval = &retval; - zend_call_function(&callback->fci, NULL TSRMLS_CC); + zend_call_function(&callback->fci, &callback->fci_cache); /* call ended, clean */ for (i = 0; i < argc; i++) { - zval **eval; - eval = params[i]; - zval_ptr_dtor(eval); - efree(eval); + zval *val = &(params[i]); + zval_dtor(val); } - if (retval_ptr != NULL) - { - zval_to_jsval(retval_ptr, cx, argv.rval().address() TSRMLS_CC); - zval_ptr_dtor(&retval_ptr); - } - else + if(Z_TYPE(retval) == IS_NULL) { argv.rval().get().setNull(); + } + else + { + // convert "return value of PHP func" to jsval + zval_to_jsval(&retval, cx, argv.rval().address()); } - + efree(params); + zval_dtor(&retval); + zend_string_release(func_name); return JS_TRUE; } @@ -150,10 +159,10 @@ JSBool generic_constructor(JSContext *cx, unsigned argc, jsval *vp) TSRMLS_FETCH(); JSFunction *jclass; JSString *jclass_name; - char *class_name; - zval ***params, *retval_ptr; - zend_class_entry *ce, **pce; - zval *cobj; + zend_string *class_name; + zval *params, retval; + zend_class_entry *ce; + zval cobj; /*JSObject *obj = JS_THIS_OBJECT(cx, vp); jsval *argv = JS_ARGV(cx,vp); jsval *rval = &JS_RVAL(cx,vp);*/ @@ -166,77 +175,63 @@ JSBool generic_constructor(JSContext *cx, unsigned argc, jsval *vp) jclass = JS_ValueToFunction(cx, argv.calleev()); jclass_name = JS_GetFunctionId(jclass); /* because version 1.8.5 supports unicode, we must encode strings */ - class_name = JS_EncodeString(cx, jclass_name); + char * jsString = JS_EncodeString(cx, jclass_name); + class_name = zend_string_init(jsString, strlen(jsString), 0); + /* free class name */ + JS_free(cx, jsString); intern = (php_jscontext_object*)JS_GetContextPrivate(cx); - + /* search for class entry */ - if (zend_hash_find(intern->ec_ht, class_name, strlen(class_name), (void**)&pce) == FAILURE) { - zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Failed to retrieve function callback", 0 TSRMLS_CC); + ce = (zend_class_entry*)zend_hash_find_ptr(intern->ec_ht, class_name); + if (ce == NULL) { + zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Failed to retrieve function callback", 0); } - /* free class name */ - JS_free(cx, class_name); - - /* retrieve pointer to ce */ - ce = *pce; - - /* create object */ - MAKE_STD_ZVAL(cobj); - if (ce->constructor) { zend_fcall_info fci; zend_fcall_info_cache fcc; if (!(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { - zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Access to non-public constructor"); + zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0, "Access to non-public constructor"); } - object_init_ex(cobj, ce); + object_init_ex(&cobj, ce); /* ready parameters */ - params = (zval***)emalloc(argc * sizeof(zval**)); + params = (zval*)emalloc(argc * sizeof(zval)); for (i = 0; i < argc; i++) { - zval *val; - MAKE_STD_ZVAL(val); + zval *val = &(params[i]); jsval_to_zval(val, cx, JS::MutableHandleValue::fromMarkedLocation(&argv[i])); - SEPARATE_ARG_IF_REF(val); - params[i] = &val; } fci.size = sizeof(fci); - fci.function_table = EG(function_table); - fci.function_name = NULL; - fci.symbol_table = NULL; - fci.object_ptr = cobj; - fci.retval_ptr_ptr = &retval_ptr; + fci.object = Z_OBJ(cobj); + fci.retval = &retval; fci.params = params; fci.param_count = argc; fci.no_separation = 1; fcc.initialized = 1; fcc.function_handler= ce->constructor; - fcc.calling_scope = EG(scope); - fcc.called_scope = Z_OBJCE_P(cobj); - fcc.object_ptr = cobj; + fcc.calling_scope = zend_get_executed_scope(); + fcc.called_scope = Z_OBJCE(cobj); + //fcc.called_scope = ce; // todo: any difference to Z_OBJCE(cobj)? + fcc.object = Z_OBJ(cobj); - if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) + if (zend_call_function(&fci, &fcc) == FAILURE) { /* call ended, clean */ for (i = 0; i < argc; i++) { - zval *eval; - eval = *params[i]; - zval_ptr_dtor(&eval); - efree(eval); - } - if (retval_ptr) { - zval_ptr_dtor(&retval_ptr); + zval *eval = &(params[i]); + zval_dtor(eval); } + efree(params); - zval_ptr_dtor(&cobj); + zval_dtor(&cobj); /* TODO: failed */ argv.rval().setNull(); return JS_FALSE; @@ -245,31 +240,28 @@ JSBool generic_constructor(JSContext *cx, unsigned argc, jsval *vp) /* call ended, clean */ for (i = 0; i < argc; i++) { - zval *eval; - eval = *params[i]; - zval_ptr_dtor(&eval); - efree(eval); - } - - if (retval_ptr) - { - zval_ptr_dtor(&retval_ptr); + zval *eval = &(params[i]); + zval_dtor(eval); } - zval_to_jsval(cobj, cx, argv.rval().address() TSRMLS_CC); + zval_dtor(&retval); + zval_to_jsval(&cobj, cx, argv.rval().address()); efree(params); + zval_dtor(&cobj); } else { - object_init_ex(cobj, ce); - zval_to_jsval(cobj, cx, argv.rval().address() TSRMLS_CC); + object_init_ex(&cobj, ce); + zval_to_jsval(&cobj, cx, argv.rval().address()); + zval_dtor(&cobj); } - zval_ptr_dtor(&cobj); + zend_string_free(class_name); return JS_TRUE; } + JSBool JS_ResolvePHP(JSContext *cx, JS::Handle obj, JS::Handle id) { /* always return true, as PHP doesn't use any resolver */ @@ -297,7 +289,7 @@ JSBool JS_PropertySetterPHP(JSContext *cx, JS::Handle obj, JS::Handle if (jsref->obj != NULL && Z_TYPE_P(jsref->obj) == IS_OBJECT) { JSString *str; char *prop_name; - zval *val; + zval val; /* 1.8.5 uses reals jsid for id, we need to convert it */ jsval rid; @@ -306,11 +298,10 @@ JSBool JS_PropertySetterPHP(JSContext *cx, JS::Handle obj, JS::Handle /* because version 1.8.5 supports unicode, we must encode strings */ prop_name = JS_EncodeString(cx, str); - MAKE_STD_ZVAL(val); - jsval_to_zval(val, cx, vp); + jsval_to_zval(&val, cx, vp); - zend_update_property(Z_OBJCE_P(jsref->obj), jsref->obj, prop_name, strlen(prop_name), val TSRMLS_CC); - zval_ptr_dtor(&val); + zend_update_property(Z_OBJCE_P(jsref->obj), jsref->obj, prop_name, strlen(prop_name), &val); + zval_dtor(&val); /* free prop name */ JS_free(cx, prop_name); @@ -334,9 +325,11 @@ JSBool JS_PropertyGetterPHP(JSContext *cx, JS::Handle obj, JS::Handle if (obj == intern->obj) { jclass =&intern->global_class; } + jsref = (php_jsobject_ref*)JS_GetInstancePrivate(cx, obj, &intern->script_class, NULL); - if (jsref != NULL) { + if (jsref != NULL) + { if (jsref->obj != NULL && Z_TYPE_P(jsref->obj) == IS_OBJECT) { JSString *str; char *prop_name; @@ -359,15 +352,14 @@ JSBool JS_PropertyGetterPHP(JSContext *cx, JS::Handle obj, JS::Handle } } - val = zend_read_property(Z_OBJCE_P(jsref->obj), jsref->obj, prop_name, strlen(prop_name), 1 TSRMLS_CC); + val = zend_read_property(Z_OBJCE_P(jsref->obj), jsref->obj, prop_name, strlen(prop_name), 1, NULL); /* free prop name */ JS_free(cx, prop_name); - if (val != EG(uninitialized_zval_ptr)) { - zval_add_ref(&val); - zval_to_jsval(val, cx, vp.address() TSRMLS_CC); - zval_ptr_dtor(&val); + + if (val != &EG(uninitialized_zval)) { + zval_to_jsval(val, cx, vp.address()); return JS_TRUE; } } @@ -383,10 +375,8 @@ void JS_FinalizePHP(JSFreeOp *fop, JSObject *obj) php_jsobject_ref *jsref; php_jscontext_object *intern; - //intern = (php_jscontext_object*)JS_GetContextPrivate(JS_GetCon); - //jsref = (php_jsobject_ref*)JS_GetInstancePrivate(cx, obj, &intern->script_class, NULL); + // it is safe to use JS_GetPrivate in a finalizer. jsref = (php_jsobject_ref*)JS_GetPrivate(obj); - //JS_GetPrivate() /* destroy ref object */ if (jsref != NULL) @@ -394,27 +384,17 @@ void JS_FinalizePHP(JSFreeOp *fop, JSObject *obj) /* free the functions hash table */ if (jsref->ht != NULL) { - /* because we made a zval for each function callback, parse the whole - * hashtable back and free them */ - for(zend_hash_internal_pointer_reset(jsref->ht); zend_hash_has_more_elements(jsref->ht) == SUCCESS; zend_hash_move_forward(jsref->ht)) - { - char *key; - uint keylen; - ulong idx; - int type; - php_callback *callback; - - /* retrieve current key */ - type = zend_hash_get_current_key_ex(jsref->ht, &key, &keylen, &idx, 0, NULL); - if (zend_hash_get_current_data(jsref->ht, (void**)&callback) == FAILURE) { - /* Should never actually fail - * since the key is known to exist. */ - continue; - } - - /* free the string used for the function name */ - zval_ptr_dtor(&callback->fci.function_name); - } + HashTable* ht = jsref->ht; + zend_string *string_key = NULL; + zend_ulong num_key = 0; + zval *zval_callback; + + ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, zval_callback) { + + php_callback *callback = (php_callback*)Z_PTR_P(zval_callback); + efree(callback); + + } ZEND_HASH_FOREACH_END(); /* destroy hashtable */ zend_hash_destroy(jsref->ht); FREE_HASHTABLE(jsref->ht); @@ -423,7 +403,7 @@ void JS_FinalizePHP(JSFreeOp *fop, JSObject *obj) /* remove reference to object and call ptr dtor */ if (jsref->obj != NULL) { - zval_ptr_dtor(&jsref->obj); + zval_dtor(jsref->obj); } /* then destroy JSRef */ diff --git a/spidermonkey_streams.cc b/spidermonkey_streams.cc index 98b13de..c7b8d26 100644 --- a/spidermonkey_streams.cc +++ b/spidermonkey_streams.cc @@ -61,7 +61,7 @@ JSBool js_stream_read(JSContext *ctx, unsigned argc, JS::Value *vp) } /* fetch php_stream */ - php_stream_from_zval_no_verify(stream, &jsref->obj); + php_stream_from_zval_no_verify(stream, jsref->obj); if (stream == NULL) { reportError(ctx, "Failed to read stream", NULL); @@ -128,7 +128,7 @@ JSBool js_stream_getline(JSContext *ctx, unsigned argc, JS::Value *vp) buf_len = 4096; } // fetch php_stream - php_stream_from_zval_no_verify(stream, &jsref->obj); + php_stream_from_zval_no_verify(stream, jsref->obj); if (stream == NULL) { reportError(ctx, "Failed to read stream", NULL); @@ -187,7 +187,7 @@ JSBool js_stream_seek(JSContext *ctx, unsigned argc, JS::Value *vp) int whence; // fetch php_stream - php_stream_from_zval_no_verify(stream, &jsref->obj); + php_stream_from_zval_no_verify(stream, jsref->obj); if (stream == NULL) { reportError(ctx, "Failed to access stream", NULL); @@ -244,7 +244,7 @@ JSBool js_stream_write(JSContext *ctx, unsigned argc, JS::Value *vp) size_t buf_len, nbytes; // fetch php_stream - php_stream_from_zval_no_verify(stream, &jsref->obj); + php_stream_from_zval_no_verify(stream, jsref->obj); if (stream == NULL) { reportError(ctx, "Failed to write to stream", NULL); @@ -310,7 +310,7 @@ JSBool js_stream_tell(JSContext *ctx, unsigned argc, JS::Value *vp) off_t file_pos; // fetch php_stream - php_stream_from_zval_no_verify(stream, &jsref->obj); + php_stream_from_zval_no_verify(stream, jsref->obj); if (stream == NULL) { reportError(ctx, "Failed to fetch stream", NULL); diff --git a/tests/js_context_info.phpt b/tests/js_context_info.phpt new file mode 100644 index 0000000..1401623 --- /dev/null +++ b/tests/js_context_info.phpt @@ -0,0 +1,10 @@ +--TEST-- +Check JSContext->getVersionString() +--FILE-- +getVersionString(160); + +?> +--EXPECTF-- +1.6 \ No newline at end of file diff --git a/tests/js_evaluate.phpt b/tests/js_evaluate.phpt new file mode 100644 index 0000000..7a927c1 --- /dev/null +++ b/tests/js_evaluate.phpt @@ -0,0 +1,11 @@ +--TEST-- +Check JSContext->evaluete() +--FILE-- +evaluateScript('var a = 1;'); +echo $js->evaluateScript('a'); + +?> +--EXPECTF-- +1 \ No newline at end of file diff --git a/tests/js_functions.phpt b/tests/js_functions.phpt index 2e03bc1..04b24bb 100644 --- a/tests/js_functions.phpt +++ b/tests/js_functions.phpt @@ -13,11 +13,13 @@ class C { } } +function cl() { echo "bar\n"; } + $js = new JSContext(); $js->registerFunction('printf'); $js->registerFunction('a', 'b'); $js->registerFunction(array(new C(), 'F') , 'CF'); -$js->registerFunction(function() { echo "bar\n"; } , 'closure'); +$js->registerFunction('cl' , 'closure'); $script = << --EXPECTF-- - - meh - +meh string(33) "Trying to reference global object"