|
@@ -32,6 +32,8 @@ extern "C" {
|
|
|
|
|
|
#include "php_v8js_macros.h"
|
|
|
#include <v8.h>
|
|
|
+#include <map>
|
|
|
+#include <stdexcept>
|
|
|
|
|
|
/* Callback for PHP methods and functions */
|
|
|
static v8::Handle<v8::Value> php_v8js_php_callback(const v8::Arguments &args) /* {{{ */
|
|
@@ -278,7 +280,6 @@ static v8::Handle<v8::Integer> php_v8js_property_query(v8::Local<v8::String> pro
|
|
|
|
|
|
static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
|
|
|
{
|
|
|
- v8::Local<v8::FunctionTemplate> new_tpl = v8::FunctionTemplate::New();
|
|
|
v8::Local<v8::Object> newobj;
|
|
|
int i;
|
|
|
char *key = NULL;
|
|
@@ -303,75 +304,95 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value, v8::Isolate *is
|
|
|
|
|
|
/* Object methods */
|
|
|
if (ce) {
|
|
|
- new_tpl->SetClassName(V8JS_STRL(ce->name, ce->name_length));
|
|
|
- new_tpl->InstanceTemplate()->SetInternalFieldCount(2);
|
|
|
+ v8::Handle<v8::FunctionTemplate> new_tpl;
|
|
|
+ static std::map<const char *, v8::Persistent<v8::FunctionTemplate> > tpl_map;
|
|
|
+ std::map<const char *, v8::Persistent<v8::FunctionTemplate> >::iterator it;
|
|
|
|
|
|
- if (ce == zend_ce_closure) {
|
|
|
- new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_php_callback);
|
|
|
- newobj = new_tpl->InstanceTemplate()->NewInstance();
|
|
|
- } else {
|
|
|
- zend_hash_internal_pointer_reset_ex(&ce->function_table, &pos);
|
|
|
- for (;; zend_hash_move_forward_ex(&ce->function_table, &pos)) {
|
|
|
- if (zend_hash_get_current_key_ex(&ce->function_table, &key, &key_len, &index, 0, &pos) != HASH_KEY_IS_STRING ||
|
|
|
- zend_hash_get_current_data_ex(&ce->function_table, (void **) &method_ptr, &pos) == FAILURE
|
|
|
- ) {
|
|
|
- break;
|
|
|
- }
|
|
|
+ try {
|
|
|
+ new_tpl = tpl_map.at(ce->name);
|
|
|
+ }
|
|
|
+ catch (const std::out_of_range &) {
|
|
|
+ /* No cached v8::FunctionTemplate available as of yet, create one. */
|
|
|
+ new_tpl = v8::FunctionTemplate::New();
|
|
|
+
|
|
|
+ new_tpl->SetClassName(V8JS_STRL(ce->name, ce->name_length));
|
|
|
+ new_tpl->InstanceTemplate()->SetInternalFieldCount(2);
|
|
|
+
|
|
|
+ if (ce == zend_ce_closure) {
|
|
|
+ /* Got a closure, mustn't cache ... */
|
|
|
+ new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_php_callback);
|
|
|
+ } else {
|
|
|
+ /* Add new v8::FunctionTemplate to tpl_map, as long as it is not a closure. */
|
|
|
+ tpl_map[ce->name] = v8::Persistent<v8::FunctionTemplate>::New(isolate, new_tpl);
|
|
|
+
|
|
|
+ /* Attach object methods to the instance template. */
|
|
|
+ zend_hash_internal_pointer_reset_ex(&ce->function_table, &pos);
|
|
|
+ for (;; zend_hash_move_forward_ex(&ce->function_table, &pos)) {
|
|
|
+ if (zend_hash_get_current_key_ex(&ce->function_table, &key, &key_len, &index, 0, &pos) != HASH_KEY_IS_STRING ||
|
|
|
+ zend_hash_get_current_data_ex(&ce->function_table, (void **) &method_ptr, &pos) == FAILURE
|
|
|
+ ) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- if ((method_ptr->common.fn_flags & ZEND_ACC_PUBLIC) && /* Allow only public methods */
|
|
|
- (method_ptr->common.fn_flags & ZEND_ACC_CTOR) == 0 && /* ..and no __construct() */
|
|
|
- (method_ptr->common.fn_flags & ZEND_ACC_DTOR) == 0 && /* ..or __destruct() */
|
|
|
- (method_ptr->common.fn_flags & ZEND_ACC_CLONE) == 0 /* ..or __clone() functions */
|
|
|
- ) {
|
|
|
- /* Override native toString() with __tostring() if it is set in passed object */
|
|
|
- if (IS_MAGIC_FUNC(ZEND_TOSTRING_FUNC_NAME)) {
|
|
|
- new_tpl->InstanceTemplate()->Set(V8JS_SYM("toString"), PHP_V8JS_CALLBACK(method_ptr));
|
|
|
- /* TODO: __set(), __unset() disabled as JS is not allowed to modify the passed PHP object yet.
|
|
|
- * __sleep(), __wakeup(), __set_state() are always ignored */
|
|
|
- } else if (
|
|
|
- IS_MAGIC_FUNC(ZEND_CALLSTATIC_FUNC_NAME)|| /* TODO */
|
|
|
- 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_SET_FUNC_NAME) ||
|
|
|
- IS_MAGIC_FUNC(ZEND_UNSET_FUNC_NAME)
|
|
|
+ if ((method_ptr->common.fn_flags & ZEND_ACC_PUBLIC) && /* Allow only public methods */
|
|
|
+ (method_ptr->common.fn_flags & ZEND_ACC_CTOR) == 0 && /* ..and no __construct() */
|
|
|
+ (method_ptr->common.fn_flags & ZEND_ACC_DTOR) == 0 && /* ..or __destruct() */
|
|
|
+ (method_ptr->common.fn_flags & ZEND_ACC_CLONE) == 0 /* ..or __clone() functions */
|
|
|
) {
|
|
|
- /* Register all magic function as hidden with lowercase name */
|
|
|
- } else if (IS_MAGIC_FUNC(ZEND_GET_FUNC_NAME)) {
|
|
|
- get_ptr = method_ptr;
|
|
|
- } else if (IS_MAGIC_FUNC(ZEND_CALL_FUNC_NAME)) {
|
|
|
- call_ptr = method_ptr;
|
|
|
- } else if (IS_MAGIC_FUNC(ZEND_INVOKE_FUNC_NAME)) {
|
|
|
- invoke_ptr = method_ptr;
|
|
|
- } else if (IS_MAGIC_FUNC(ZEND_ISSET_FUNC_NAME)) {
|
|
|
- isset_ptr = method_ptr;
|
|
|
- } else {
|
|
|
- new_tpl->InstanceTemplate()->Set(V8JS_STR(method_ptr->common.function_name), PHP_V8JS_CALLBACK(method_ptr), v8::ReadOnly);
|
|
|
+ /* Override native toString() with __tostring() if it is set in passed object */
|
|
|
+ if (IS_MAGIC_FUNC(ZEND_TOSTRING_FUNC_NAME)) {
|
|
|
+ new_tpl->InstanceTemplate()->Set(V8JS_SYM("toString"), PHP_V8JS_CALLBACK(method_ptr));
|
|
|
+ /* TODO: __set(), __unset() disabled as JS is not allowed to modify the passed PHP object yet.
|
|
|
+ * __sleep(), __wakeup(), __set_state() are always ignored */
|
|
|
+ } else if (
|
|
|
+ IS_MAGIC_FUNC(ZEND_CALLSTATIC_FUNC_NAME)|| /* TODO */
|
|
|
+ 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_SET_FUNC_NAME) ||
|
|
|
+ IS_MAGIC_FUNC(ZEND_UNSET_FUNC_NAME)
|
|
|
+ ) {
|
|
|
+ /* Register all magic function as hidden with lowercase name */
|
|
|
+ } else if (IS_MAGIC_FUNC(ZEND_GET_FUNC_NAME)) {
|
|
|
+ get_ptr = method_ptr;
|
|
|
+ } else if (IS_MAGIC_FUNC(ZEND_CALL_FUNC_NAME)) {
|
|
|
+ call_ptr = method_ptr;
|
|
|
+ } else if (IS_MAGIC_FUNC(ZEND_INVOKE_FUNC_NAME)) {
|
|
|
+ invoke_ptr = method_ptr;
|
|
|
+ } else if (IS_MAGIC_FUNC(ZEND_ISSET_FUNC_NAME)) {
|
|
|
+ isset_ptr = method_ptr;
|
|
|
+ } else {
|
|
|
+ new_tpl->InstanceTemplate()->Set(V8JS_STR(method_ptr->common.function_name), PHP_V8JS_CALLBACK(method_ptr), v8::ReadOnly);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- /* Only register getter, etc. when they're set in PHP side */
|
|
|
- if (call_ptr || get_ptr || isset_ptr)
|
|
|
- {
|
|
|
- /* Set __get() handler which acts also as __call() proxy */
|
|
|
- new_tpl->InstanceTemplate()->SetNamedPropertyHandler(
|
|
|
- php_v8js_property_getter, /* getter */
|
|
|
- 0, /* setter */
|
|
|
- isset_ptr ? php_v8js_property_query : 0, /* query */
|
|
|
- 0, /* deleter */
|
|
|
- 0, /* enumerator */
|
|
|
- V8JS_BOOL(call_ptr ? true : false)
|
|
|
- );
|
|
|
- }
|
|
|
|
|
|
- /* __invoke() handler */
|
|
|
- if (invoke_ptr) {
|
|
|
- new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_property_caller, V8JS_SYM(ZEND_INVOKE_FUNC_NAME));
|
|
|
+ /* Only register getter, etc. when they're set in PHP side */
|
|
|
+ if (call_ptr || get_ptr || isset_ptr)
|
|
|
+ {
|
|
|
+ /* Set __get() handler which acts also as __call() proxy */
|
|
|
+ new_tpl->InstanceTemplate()->SetNamedPropertyHandler(
|
|
|
+ php_v8js_property_getter, /* getter */
|
|
|
+ 0, /* setter */
|
|
|
+ isset_ptr ? php_v8js_property_query : 0, /* query */
|
|
|
+ 0, /* deleter */
|
|
|
+ 0, /* enumerator */
|
|
|
+ V8JS_BOOL(call_ptr ? true : false)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /* __invoke() handler */
|
|
|
+ if (invoke_ptr) {
|
|
|
+ new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_property_caller, V8JS_SYM(ZEND_INVOKE_FUNC_NAME));
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- newobj = new_tpl->InstanceTemplate()->NewInstance();
|
|
|
+ newobj = new_tpl->InstanceTemplate()->NewInstance();
|
|
|
|
|
|
+ if (ce != zend_ce_closure) { // @fixme all of those get lost, when using cached templates
|
|
|
if (call_ptr) {
|
|
|
newobj->SetHiddenValue(V8JS_SYM(ZEND_CALL_FUNC_NAME), PHP_V8JS_CALLBACK(call_ptr));
|
|
|
}
|
|
@@ -392,6 +413,8 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value, v8::Isolate *is
|
|
|
newobj->SetAlignedPointerInInternalField(0, (void *) value);
|
|
|
newobj->SetAlignedPointerInInternalField(1, (void *) isolate);
|
|
|
} else {
|
|
|
+ v8::Local<v8::FunctionTemplate> new_tpl = v8::FunctionTemplate::New(); // @todo re-use template likewise
|
|
|
+
|
|
|
new_tpl->SetClassName(V8JS_SYM("Array"));
|
|
|
newobj = new_tpl->InstanceTemplate()->NewInstance();
|
|
|
}
|