Browse Source

Merge pull request #61 from cscott/misc-cleanup

A week's worth of cleanup
Patrick Reilly 11 years ago
parent
commit
ed8fd24f69
6 changed files with 514 additions and 127 deletions
  1. 10 10
      php_v8js_macros.h
  2. 5 4
      tests/return_value.phpt
  3. 315 0
      tests/var_dump.phpt
  4. 91 69
      v8js.cc
  5. 17 3
      v8js_convert.cc
  6. 76 41
      v8js_methods.cc

+ 10 - 10
php_v8js_macros.h

@@ -35,15 +35,15 @@ extern "C" {
 #define V8JS_VERSION "0.1.3"
 #define V8JS_VERSION "0.1.3"
 
 
 /* Helper macros */
 /* Helper macros */
-#define V8JS_SYM(v)			v8::String::NewSymbol(v, sizeof(v) - 1)
-#define V8JS_SYML(v, l)		v8::String::NewSymbol(v, l)
-#define V8JS_STR(v)			v8::String::New(v)
-#define V8JS_STRL(v, l)		v8::String::New(v, l)
-#define V8JS_INT(v)			v8::Integer::New(v)
-#define V8JS_FLOAT(v)		v8::Number::New(v)
-#define V8JS_BOOL(v)		v8::Boolean::New(v)
-#define V8JS_NULL			v8::Null()
-#define V8JS_UNDEFINED		v8::Undefined()
+#define V8JS_SYM(v)			v8::String::NewFromUtf8(isolate, v, v8::String::kInternalizedString, sizeof(v) - 1)
+#define V8JS_SYML(v, l)		v8::String::NewFromUtf8(isolate, v, v8::String::kInternalizedString, l)
+#define V8JS_STR(v)			v8::String::NewFromUtf8(isolate, v)
+#define V8JS_STRL(v, l)		v8::String::NewFromUtf8(isolate, v, v8::String::kNormalString, l)
+#define V8JS_INT(v)			v8::Integer::New(v, isolate)
+#define V8JS_FLOAT(v)		v8::Number::New(isolate, v)
+#define V8JS_BOOL(v)		((v)?v8::True(isolate):v8::False(isolate))
+#define V8JS_NULL			v8::Null(isolate)
+#define V8JS_UNDEFINED		v8::Undefined(isolate)
 #define V8JS_MN(name)		v8js_method_##name
 #define V8JS_MN(name)		v8js_method_##name
 #define V8JS_METHOD(name)	void V8JS_MN(name)(const v8::FunctionCallbackInfo<v8::Value>& info)
 #define V8JS_METHOD(name)	void V8JS_MN(name)(const v8::FunctionCallbackInfo<v8::Value>& info)
 #define V8JS_THROW(type, message, message_len)	v8::ThrowException(v8::Exception::type(V8JS_STRL(message, message_len)))
 #define V8JS_THROW(type, message, message_len)	v8::ThrowException(v8::Exception::type(V8JS_STRL(message, message_len)))
@@ -81,7 +81,7 @@ extern "C" {
 #define V8JS_DEBUG_AUTO_BREAK_ALWAYS	2
 #define V8JS_DEBUG_AUTO_BREAK_ALWAYS	2
 
 
 /* Extracts a C string from a V8 Utf8Value. */
 /* Extracts a C string from a V8 Utf8Value. */
-static const char * ToCString(const v8::String::Utf8Value &value) /* {{{ */
+static inline const char * ToCString(const v8::String::Utf8Value &value) /* {{{ */
 {
 {
 	return *value ? *value : "<string conversion failed>";
 	return *value ? *value : "<string conversion failed>";
 }
 }

+ 5 - 4
tests/return_value.phpt

@@ -43,12 +43,13 @@ var_dump($a->executeString("test(false);", "test9.js"));
 ===EOF===
 ===EOF===
 --EXPECT--
 --EXPECT--
 NULL
 NULL
-object(V8Object)#3 (2) {
-  ["mytest"]=>
-  object(V8Function)#4 (0) {
-  }
+object(Testing)#2 (3) {
   ["foo"]=>
   ["foo"]=>
   string(8) "ORIGINAL"
   string(8) "ORIGINAL"
+  ["my_private":"Testing":private]=>
+  string(3) "arf"
+  ["my_protected":protected]=>
+  string(4) "argh"
 }
 }
 array(3) {
 array(3) {
   [0]=>
   [0]=>

+ 315 - 0
tests/var_dump.phpt

@@ -0,0 +1,315 @@
+--TEST--
+Test V8::executeString() : var_dump
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+# Test var_dump of various types
+
+$JS = <<< EOT
+
+print("--- JS var_dump of PHP object ----\\n");
+var_dump(PHP.phptypes);
+
+print("--- JS var_dump of JS object ----\\n");
+var types = {
+	undefined: undefined,
+	null: null,
+	bool: true,
+	string: "string",
+	uint: 1,
+	int: -1,
+	number: 3.141592654,
+	// XXX this gets parsed with local timezone,
+	//     which is bad for test repeatability.
+	//date: new Date('September 27, 1976 09:00:00 GMT'),
+	regexp: /regexp/,
+	array: [1,2,3],
+	object: { field: "foo" },
+	function: function id(x) { return x; },
+	phpobject: PHP.obj
+};
+
+var_dump(types);
+print("--- PHP var_dump of JS object ----\\n");
+types;
+EOT;
+
+class Foo {
+	  var $field = "php";
+}
+
+$v8 = new V8Js();
+$v8->obj = new Foo;
+
+$phptypes = $v8->phptypes = array(
+	"null" => NULL,
+	"bool" => true,
+	"string" => "string",
+	"uint" => 1,
+	"int" => -1,
+	"number" => 3.141592654,
+	"date" => new DateTime('September 27, 1976 09:00:00 UTC', new DateTimeZone('UTC')),
+	//"regexp" => new Regexp('/regexp/'), /* no native PHP regex type */
+	"array" => array(1,2,3),
+	"object" => array( "field" => "foo" ),
+	"function" => (function ($x) { return $x; }),
+	"phpobject" => new Foo
+);
+
+echo "---- PHP var_dump of PHP object ----\n";
+var_dump($phptypes);
+
+try {
+	var_dump($v8->executeString($JS, 'var_dump.js'));
+} catch (V8JsScriptException $e) {
+	echo "Error!\n";
+	var_dump($e);
+}
+?>
+===EOF===
+--EXPECTF--
+---- PHP var_dump of PHP object ----
+array(11) {
+  ["null"]=>
+  NULL
+  ["bool"]=>
+  bool(true)
+  ["string"]=>
+  string(6) "string"
+  ["uint"]=>
+  int(1)
+  ["int"]=>
+  int(-1)
+  ["number"]=>
+  float(3.141592654)
+  ["date"]=>
+  object(DateTime)#%d (3) {
+    ["date"]=>
+    string(19) "1976-09-27 09:00:00"
+    ["timezone_type"]=>
+    int(3)
+    ["timezone"]=>
+    string(3) "UTC"
+  }
+  ["array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["object"]=>
+  array(1) {
+    ["field"]=>
+    string(3) "foo"
+  }
+  ["function"]=>
+  object(Closure)#%d (1) {
+    ["parameter"]=>
+    array(1) {
+      ["$x"]=>
+      string(10) "<required>"
+    }
+  }
+  ["phpobject"]=>
+  object(Foo)#%d (1) {
+    ["field"]=>
+    string(3) "php"
+  }
+}
+--- JS var_dump of PHP object ----
+array (11) {
+  ["null"] =>
+  NULL
+  ["bool"] =>
+  bool(true)
+  ["string"] =>
+  string(6) "string"
+  ["uint"] =>
+  int(1)
+  ["int"] =>
+  int(-1)
+  ["number"] =>
+  float(3.141593)
+  ["date"] =>
+  object(DateTime)#%d (18) {
+    ["createFromFormat"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["getLastErrors"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["format"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["modify"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["add"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["sub"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["getTimezone"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["setTimezone"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["getOffset"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["setTime"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["setDate"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["setISODate"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["setTimestamp"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["getTimestamp"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["diff"] =>
+    object(Closure)#%d {
+        function () { [native code] }
+    }
+    ["date"] =>
+    string(19) "1976-09-27 09:00:00"
+    ["timezone_type"] =>
+    int(3)
+    ["timezone"] =>
+    string(3) "UTC"
+  }
+  ["array"] =>
+  array(3) {
+    [0] =>
+    int(1)
+    [1] =>
+    int(2)
+    [2] =>
+    int(3)
+  }
+  ["object"] =>
+  array (1) {
+    ["field"] =>
+    string(3) "foo"
+  }
+  ["function"] =>
+  object(Closure)#%d (0) {
+  }
+  ["phpobject"] =>
+  object(Foo)#%d (1) {
+    ["field"] =>
+    string(3) "php"
+  }
+}
+--- JS var_dump of JS object ----
+object(Object)#%d (12) {
+  ["undefined"] =>
+  NULL
+  ["null"] =>
+  NULL
+  ["bool"] =>
+  bool(true)
+  ["string"] =>
+  string(6) "string"
+  ["uint"] =>
+  int(1)
+  ["int"] =>
+  int(-1)
+  ["number"] =>
+  float(3.141593)
+  ["regexp"] =>
+  regexp(/regexp/)
+  ["array"] =>
+  array(3) {
+    [0] =>
+    int(1)
+    [1] =>
+    int(2)
+    [2] =>
+    int(3)
+  }
+  ["object"] =>
+  object(Object)#%d (1) {
+    ["field"] =>
+    string(3) "foo"
+  }
+  ["function"] =>
+  object(Closure)#%d {
+      function id(x) { return x; }
+  }
+  ["phpobject"] =>
+  object(Foo)#%d (1) {
+    ["field"] =>
+    string(3) "php"
+  }
+}
+--- PHP var_dump of JS object ----
+object(V8Object)#%d (12) {
+  ["undefined"]=>
+  NULL
+  ["null"]=>
+  NULL
+  ["bool"]=>
+  bool(true)
+  ["string"]=>
+  string(6) "string"
+  ["uint"]=>
+  int(1)
+  ["int"]=>
+  int(-1)
+  ["number"]=>
+  float(3.141592654)
+  ["regexp"]=>
+  object(V8Object)#%d (0) {
+  }
+  ["array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["object"]=>
+  object(V8Object)#%d (1) {
+    ["field"]=>
+    string(3) "foo"
+  }
+  ["function"]=>
+  object(V8Function)#%d (0) {
+  }
+  ["phpobject"]=>
+  object(Foo)#%d (1) {
+    ["field"]=>
+    string(3) "php"
+  }
+}
+===EOF===

+ 91 - 69
v8js.cc

@@ -128,13 +128,14 @@ static int php_v8js_v8_has_property(zval *object, zval *member, int has_set_exis
 	int retval = false;
 	int retval = false;
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
 
 
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 
 	if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction())
 	if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction())
 	{
 	{
@@ -186,13 +187,14 @@ static zval *php_v8js_v8_read_property(zval *object, zval *member, int type ZEND
 	zval *retval = NULL;
 	zval *retval = NULL;
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
 
 
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 
 	if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction())
 	if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction())
 	{
 	{
@@ -212,7 +214,7 @@ static zval *php_v8js_v8_read_property(zval *object, zval *member, int type ZEND
 				MAKE_STD_ZVAL(retval);
 				MAKE_STD_ZVAL(retval);
 			}
 			}
 
 
-			if (v8js_to_zval(jsVal, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) {
+			if (v8js_to_zval(jsVal, retval, obj->flags, isolate TSRMLS_CC) == SUCCESS) {
 				return retval;
 				return retval;
 			}
 			}
 		}
 		}
@@ -228,16 +230,17 @@ static void php_v8js_v8_write_property(zval *object, zval *member, zval *value Z
 {
 {
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
 
 
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 
 	if (v8obj->IsObject() && !v8obj->IsFunction()) {
 	if (v8obj->IsObject() && !v8obj->IsFunction()) {
-		v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, obj->isolate TSRMLS_CC));
+		v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, isolate TSRMLS_CC));
 	}
 	}
 }
 }
 /* }}} */
 /* }}} */
@@ -246,13 +249,14 @@ static void php_v8js_v8_unset_property(zval *object, zval *member ZEND_HASH_KEY_
 {
 {
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
 
 
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 
 	if (v8obj->IsObject() && !v8obj->IsFunction()) {
 	if (v8obj->IsObject() && !v8obj->IsFunction()) {
 		v8obj->ToObject()->ForceDelete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)));
 		v8obj->ToObject()->ForceDelete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)));
@@ -317,14 +321,15 @@ static HashTable *php_v8js_v8_get_properties(zval *object TSRMLS_DC) /* {{{ */
 	ALLOC_HASHTABLE(retval);
 	ALLOC_HASHTABLE(retval);
 	zend_hash_init(retval, 0, NULL, ZVAL_PTR_DTOR, 0);
 	zend_hash_init(retval, 0, NULL, ZVAL_PTR_DTOR, 0);
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 
-	if (php_v8js_v8_get_properties_hash(v8obj, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) {
+	if (php_v8js_v8_get_properties_hash(v8obj, retval, obj->flags, isolate TSRMLS_CC) == SUCCESS) {
 		return retval;
 		return retval;
 	}
 	}
 
 
@@ -344,13 +349,14 @@ static zend_function *php_v8js_v8_get_method(zval **object_ptr, char *method, in
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(*object_ptr TSRMLS_CC);
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(*object_ptr TSRMLS_CC);
 	zend_function *f;
 	zend_function *f;
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Local<v8::String> jsKey = V8JS_STRL(method, method_len);
 	v8::Local<v8::String> jsKey = V8JS_STRL(method, method_len);
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 
 	if (!obj->v8obj.IsEmpty() && v8obj->IsObject() && !v8obj->IsFunction()) {
 	if (!obj->v8obj.IsEmpty() && v8obj->IsObject() && !v8obj->IsFunction()) {
 		v8::Local<v8::Object> jsObj = v8obj->ToObject();
 		v8::Local<v8::Object> jsObj = v8obj->ToObject();
@@ -389,14 +395,15 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) /
 		zend_get_parameters_array_ex(argc, argv);
 		zend_get_parameters_array_ex(argc, argv);
 	}
 	}
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
 
 
 	v8::Local<v8::String> method_name = V8JS_SYML(method, strlen(method));
 	v8::Local<v8::String> method_name = V8JS_SYML(method, strlen(method));
-	v8::Local<v8::Object> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj)->ToObject();
+	v8::Local<v8::Object> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj)->ToObject();
 	v8::Local<v8::Function> cb;
 	v8::Local<v8::Function> cb;
 
 
 	if (method_name->Equals(V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME))) {
 	if (method_name->Equals(V8JS_SYM(V8JS_V8_INVOKE_FUNC_NAME))) {
@@ -405,11 +412,11 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) /
 		cb = v8::Local<v8::Function>::Cast(v8obj->Get(method_name));
 		cb = v8::Local<v8::Function>::Cast(v8obj->Get(method_name));
 	}
 	}
 
 
-	v8::Local<v8::Value> *jsArgv = new v8::Local<v8::Value>[argc];
+	v8::Local<v8::Value> jsArgv[argc];
 	v8::Local<v8::Value> js_retval;
 	v8::Local<v8::Value> js_retval;
 
 
 	for (i = 0; i < argc; i++) {
 	for (i = 0; i < argc; i++) {
-		jsArgv[i] = v8::Local<v8::Value>::New(obj->isolate, zval_to_v8js(*argv[i], obj->isolate TSRMLS_CC));
+		jsArgv[i] = v8::Local<v8::Value>::New(isolate, zval_to_v8js(*argv[i], isolate TSRMLS_CC));
 	}
 	}
 
 
 	js_retval = cb->Call(V8JS_GLOBAL, argc, jsArgv);
 	js_retval = cb->Call(V8JS_GLOBAL, argc, jsArgv);
@@ -421,7 +428,7 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) /
 	}
 	}
 
 
 	if (return_value_used) {
 	if (return_value_used) {
-		return v8js_to_zval(js_retval, return_value, obj->flags, obj->isolate TSRMLS_CC);
+		return v8js_to_zval(js_retval, return_value, obj->flags, isolate TSRMLS_CC);
 	}
 	}
 
 
 	return SUCCESS;
 	return SUCCESS;
@@ -434,12 +441,13 @@ static int php_v8js_v8_get_closure(zval *object, zend_class_entry **ce_ptr, zend
 
 
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 	php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
 
 
-	v8::Locker locker(obj->isolate);
-	v8::Isolate::Scope isolate_scope(obj->isolate);
-	v8::HandleScope local_scope(obj->isolate);
-	v8::Local<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Isolate *isolate = obj->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
+	v8::HandleScope local_scope(isolate);
+	v8::Local<v8::Context> temp_context = v8::Context::New(isolate);
 	v8::Context::Scope temp_scope(temp_context);
 	v8::Context::Scope temp_scope(temp_context);
-	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(isolate, obj->v8obj);
 
 
 	if (!v8obj->IsFunction()) {
 	if (!v8obj->IsFunction()) {
 		return FAILURE;
 		return FAILURE;
@@ -467,9 +475,7 @@ static void php_v8js_v8_free_storage(void *object, zend_object_handle handle TSR
 
 
 	zend_object_std_dtor(&c->std TSRMLS_CC);
 	zend_object_std_dtor(&c->std TSRMLS_CC);
 
 
-	if (!c->v8obj.IsEmpty()) {
-		c->v8obj.Dispose();
-	}
+	c->v8obj.Reset();
 
 
 	efree(object);
 	efree(object);
 }
 }
@@ -519,19 +525,24 @@ static void php_v8js_free_storage(void *object TSRMLS_DC) /* {{{ */
 		zval_ptr_dtor(&c->pending_exception);
 		zval_ptr_dtor(&c->pending_exception);
 	}
 	}
 	
 	
-	c->object_name.Dispose();
+	c->object_name.Reset();
+	c->object_name.~Persistent();
+	c->global_template.Reset();
+	c->global_template.~Persistent();
 
 
 	/* Clear global object, dispose context */
 	/* Clear global object, dispose context */
 	if (!c->context.IsEmpty()) {
 	if (!c->context.IsEmpty()) {
-		c->context.Dispose();
-		c->context.Clear();
+		c->context.Reset();
 		V8JSG(disposed_contexts) = v8::V8::ContextDisposedNotification();
 		V8JSG(disposed_contexts) = v8::V8::ContextDisposedNotification();
 #if V8JS_DEBUG
 #if V8JS_DEBUG
 		fprintf(stderr, "Context dispose notification sent (%d)\n", V8JSG(disposed_contexts));
 		fprintf(stderr, "Context dispose notification sent (%d)\n", V8JSG(disposed_contexts));
 		fflush(stderr);
 		fflush(stderr);
 #endif
 #endif
 	}
 	}
+	c->context.~Persistent();
 
 
+	c->modules_stack.~vector();
+	c->modules_base.~vector();
 	efree(object);
 	efree(object);
 }
 }
 /* }}} */
 /* }}} */
@@ -543,6 +554,7 @@ static zend_object_value php_v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */
 
 
 	c = (php_v8js_ctx *) ecalloc(1, sizeof(*c));
 	c = (php_v8js_ctx *) ecalloc(1, sizeof(*c));
 	zend_object_std_init(&c->std, ce TSRMLS_CC);
 	zend_object_std_init(&c->std, ce TSRMLS_CC);
+
 #if PHP_VERSION_ID >= 50400
 #if PHP_VERSION_ID >= 50400
 	object_properties_init(&c->std, ce);
 	object_properties_init(&c->std, ce);
 #else
 #else
@@ -551,6 +563,13 @@ static zend_object_value php_v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */
 				   (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
 				   (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
 #endif
 #endif
 
 
+	new(&c->object_name) v8::Persistent<v8::String>();
+	new(&c->context) v8::Persistent<v8::Context>();
+	new(&c->global_template) v8::Persistent<v8::FunctionTemplate>();
+
+	new(&c->modules_stack) std::vector<char*>();
+	new(&c->modules_base) std::vector<char*>();
+
 	retval.handle = zend_objects_store_put(c, NULL, (zend_objects_free_object_storage_t) php_v8js_free_storage, NULL TSRMLS_CC);
 	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;
 	retval.handlers = &v8js_object_handlers;
 
 
@@ -711,11 +730,12 @@ static PHP_METHOD(V8Js, __construct)
 	v8::ExtensionConfiguration extension_conf(exts_count, exts);
 	v8::ExtensionConfiguration extension_conf(exts_count, exts);
 
 
 	// Isolate execution
 	// Isolate execution
-	v8::Locker locker(c->isolate);
-	v8::Isolate::Scope isolate_scope(c->isolate);
+	v8::Isolate *isolate = c->isolate;
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
 
 
 	/* Handle scope */
 	/* Handle scope */
-	v8::HandleScope handle_scope(c->isolate);
+	v8::HandleScope handle_scope(isolate);
 
 
 	/* Redirect fatal errors to PHP error handler */
 	/* Redirect fatal errors to PHP error handler */
 	// This needs to be done within the context isolate
 	// This needs to be done within the context isolate
@@ -726,15 +746,15 @@ static PHP_METHOD(V8Js, __construct)
 
 
 	v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New();
 	v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New();
 	tpl->SetClassName(V8JS_SYM("V8Js"));
 	tpl->SetClassName(V8JS_SYM("V8Js"));
-	c->global_template.Reset(c->isolate, tpl);
+	c->global_template.Reset(isolate, tpl);
 
 
 	/* Register builtin methods */
 	/* Register builtin methods */
 	php_v8js_register_methods(tpl->InstanceTemplate(), c);
 	php_v8js_register_methods(tpl->InstanceTemplate(), c);
 
 
 	/* Create context */
 	/* Create context */
-	v8::Local<v8::Context> context = v8::Context::New(c->isolate, &extension_conf, tpl->InstanceTemplate());
+	v8::Local<v8::Context> context = v8::Context::New(isolate, &extension_conf, tpl->InstanceTemplate());
 	context->SetAlignedPointerInEmbedderData(1, c);
 	context->SetAlignedPointerInEmbedderData(1, c);
-	c->context.Reset(c->isolate, context);
+	c->context.Reset(isolate, context);
 
 
 	if (exts) {
 	if (exts) {
 		_php_v8js_free_ext_strarr(exts, exts_count);
 		_php_v8js_free_ext_strarr(exts, exts_count);
@@ -767,12 +787,12 @@ static PHP_METHOD(V8Js, __construct)
 
 
 	/* Register Get accessor for passed variables */
 	/* Register Get accessor for passed variables */
 	if (vars_arr && zend_hash_num_elements(Z_ARRVAL_P(vars_arr)) > 0) {
 	if (vars_arr && zend_hash_num_elements(Z_ARRVAL_P(vars_arr)) > 0) {
-		php_v8js_register_accessors(php_obj_t->InstanceTemplate(), vars_arr, c->isolate TSRMLS_CC);
+		php_v8js_register_accessors(php_obj_t->InstanceTemplate(), vars_arr, isolate TSRMLS_CC);
 	}
 	}
 
 
 	/* Set name for the PHP JS object */
 	/* Set name for the PHP JS object */
 	v8::Local<v8::String> object_name_js = (object_name_len) ? V8JS_SYML(object_name, object_name_len) : V8JS_SYM("PHP");
 	v8::Local<v8::String> object_name_js = (object_name_len) ? V8JS_SYML(object_name, object_name_len) : V8JS_SYM("PHP");
-	c->object_name.Reset(c->isolate, object_name_js);
+	c->object_name.Reset(isolate, object_name_js);
 
 
 	/* Add the PHP object into global object */
 	/* Add the PHP object into global object */
 	v8::Local<v8::Object> php_obj = php_obj_t->InstanceTemplate()->NewInstance();
 	v8::Local<v8::Object> php_obj = php_obj_t->InstanceTemplate()->NewInstance();
@@ -799,7 +819,7 @@ static PHP_METHOD(V8Js, __construct)
 		zend_property_info *property_info = zend_get_property_info(c->std.ce, &zmember, 1 TSRMLS_CC);
 		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) {
 		if(property_info && property_info->flags & ZEND_ACC_PUBLIC) {
 			/* Write value to PHP JS object */
 			/* Write value to PHP JS object */
-			php_obj->ForceSet(V8JS_SYML(member, member_len - 1), zval_to_v8js(*value, c->isolate TSRMLS_CC), v8::ReadOnly);
+			php_obj->ForceSet(V8JS_SYML(member, member_len - 1), zval_to_v8js(*value, isolate TSRMLS_CC), v8::ReadOnly);
 		}
 		}
 	}
 	}
 
 
@@ -816,10 +836,11 @@ static PHP_METHOD(V8Js, __construct)
 	} \
 	} \
 	\
 	\
 	(ctx) = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); \
 	(ctx) = (php_v8js_ctx *) zend_object_store_get_object(object TSRMLS_CC); \
-	v8::Locker locker((ctx)->isolate); \
-	v8::Isolate::Scope isolate_scope((ctx)->isolate); \
-	v8::HandleScope handle_scope((ctx)->isolate); \
-	v8::Context::Scope context_scope((ctx)->isolate, (ctx)->context);
+	v8::Isolate *isolate = (ctx)->isolate; \
+	v8::Locker locker(isolate); \
+	v8::Isolate::Scope isolate_scope(isolate); \
+	v8::HandleScope handle_scope(isolate); \
+	v8::Context::Scope context_scope(isolate, (ctx)->context);
 
 
 static void php_v8js_timer_push(long time_limit, long memory_limit, php_v8js_ctx *c TSRMLS_DC)
 static void php_v8js_timer_push(long time_limit, long memory_limit, php_v8js_ctx *c TSRMLS_DC)
 {
 {
@@ -930,7 +951,7 @@ static PHP_METHOD(V8Js, executeString)
 	v8::Local<v8::String> sname = identifier_len ? V8JS_SYML(identifier, identifier_len) : V8JS_SYM("V8Js::executeString()");
 	v8::Local<v8::String> sname = identifier_len ? V8JS_SYML(identifier, identifier_len) : V8JS_SYM("V8Js::executeString()");
 
 
 	/* Compiles a string context independently. TODO: Add a php function which calls this and returns the result as resource which can be executed later. */
 	/* Compiles a string context independently. TODO: Add a php function which calls this and returns the result as resource which can be executed later. */
-	v8::Local<v8::String> source = v8::String::New(str, str_len);
+	v8::Local<v8::String> source = V8JS_STRL(str, str_len);
 	v8::Local<v8::Script> script = v8::Script::New(source, sname);
 	v8::Local<v8::Script> script = v8::Script::New(source, sname);
 
 
 	/* Compile errors? */
 	/* Compile errors? */
@@ -1015,6 +1036,7 @@ static PHP_METHOD(V8Js, executeString)
 			if (result.IsEmpty()) {
 			if (result.IsEmpty()) {
 				MAKE_STD_ZVAL(c->pending_exception);
 				MAKE_STD_ZVAL(c->pending_exception);
 				php_v8js_create_script_exception(c->pending_exception, &try_catch TSRMLS_CC);
 				php_v8js_create_script_exception(c->pending_exception, &try_catch TSRMLS_CC);
+				return;
 			}
 			}
 		}
 		}
 
 
@@ -1341,11 +1363,11 @@ static void php_v8js_write_property(zval *object, zval *member, zval *value ZEND
 	zend_property_info *property_info = zend_get_property_info(c->std.ce, member, 1 TSRMLS_CC);
 	zend_property_info *property_info = zend_get_property_info(c->std.ce, member, 1 TSRMLS_CC);
 	if(property_info->flags & ZEND_ACC_PUBLIC) {
 	if(property_info->flags & ZEND_ACC_PUBLIC) {
 		/* Global PHP JS object */
 		/* Global PHP JS object */
-		v8::Local<v8::String> object_name_js = v8::Local<v8::String>::New(c->isolate, c->object_name);
+		v8::Local<v8::String> object_name_js = v8::Local<v8::String>::New(isolate, c->object_name);
 		v8::Local<v8::Object> jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject();
 		v8::Local<v8::Object> jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject();
 
 
 		/* Write value to PHP JS object */
 		/* Write value to PHP JS object */
-		jsobj->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, c->isolate TSRMLS_CC), v8::ReadOnly);
+		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 */
 	/* Write value to PHP object */
@@ -1358,7 +1380,7 @@ static void php_v8js_unset_property(zval *object, zval *member ZEND_HASH_KEY_DC
 	V8JS_BEGIN_CTX(c, object)
 	V8JS_BEGIN_CTX(c, object)
 
 
 	/* Global PHP JS object */
 	/* Global PHP JS object */
-	v8::Local<v8::String> object_name_js = v8::Local<v8::String>::New(c->isolate, c->object_name);
+	v8::Local<v8::String> object_name_js = v8::Local<v8::String>::New(isolate, c->object_name);
 	v8::Local<v8::Object> jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject();
 	v8::Local<v8::Object> jsobj = V8JS_GLOBAL->Get(object_name_js)->ToObject();
 	
 	
 	/* Delete value from PHP JS object */
 	/* Delete value from PHP JS object */

+ 17 - 3
v8js_convert.cc

@@ -28,6 +28,8 @@ extern "C" {
 #include <v8.h>
 #include <v8.h>
 #include <stdexcept>
 #include <stdexcept>
 
 
+#define PHPJS_OBJECT_KEY "phpjs::object"
+
 #if PHP_V8_API_VERSION < 3022000
 #if PHP_V8_API_VERSION < 3022000
 /* CopyablePersistentTraits is only part of V8 from 3.22.0 on,
 /* CopyablePersistentTraits is only part of V8 from 3.22.0 on,
    to be compatible with lower versions add our own (compatible) version. */
    to be compatible with lower versions add our own (compatible) version. */
@@ -194,7 +196,7 @@ static void php_v8js_php_callback(const v8::FunctionCallbackInfo<v8::Value>& inf
 /* Callback for PHP constructor calls */
 /* Callback for PHP constructor calls */
 static void php_v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 static void php_v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 {
 {
-	v8::Isolate *isolate = v8::Isolate::GetCurrent();
+	v8::Isolate *isolate = info.GetIsolate();
 	info.GetReturnValue().Set(V8JS_UNDEFINED);
 	info.GetReturnValue().Set(V8JS_UNDEFINED);
 
 
 	// @todo assert constructor call
 	// @todo assert constructor call
@@ -216,7 +218,7 @@ static void php_v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value
 
 
 		// Check access on __construct function, if any
 		// Check access on __construct function, if any
 		if (ctor_ptr != NULL && (ctor_ptr->common.fn_flags & ZEND_ACC_PUBLIC) == 0) {
 		if (ctor_ptr != NULL && (ctor_ptr->common.fn_flags & ZEND_ACC_PUBLIC) == 0) {
-			info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Call to protected __construct() not allowed")));
+			info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Call to protected __construct() not allowed")));
 			return;
 			return;
 		}
 		}
 
 
@@ -231,6 +233,7 @@ static void php_v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value
 
 
 	newobj->SetAlignedPointerInInternalField(0, value);
 	newobj->SetAlignedPointerInInternalField(0, value);
 	newobj->SetAlignedPointerInInternalField(1, (void *) isolate);
 	newobj->SetAlignedPointerInInternalField(1, (void *) isolate);
+	newobj->SetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY), V8JS_BOOL(true));
 }
 }
 /* }}} */
 /* }}} */
 
 
@@ -259,6 +262,7 @@ static int _php_v8js_is_assoc_array(HashTable *myht TSRMLS_DC) /* {{{ */
 static void php_v8js_property_caller(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 static void php_v8js_property_caller(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ */
 {
 {
 	v8::Local<v8::Object> self = info.Holder();
 	v8::Local<v8::Object> self = info.Holder();
+	v8::Isolate *isolate = reinterpret_cast<v8::Isolate *>(self->GetAlignedPointerFromInternalField(1));
 	v8::Local<v8::String> cname = info.Callee()->GetName()->ToString();
 	v8::Local<v8::String> cname = info.Callee()->GetName()->ToString();
 	v8::Local<v8::Value> value;
 	v8::Local<v8::Value> value;
 	v8::Local<v8::String> cb_func = v8::Local<v8::String>::Cast(info.Data());
 	v8::Local<v8::String> cb_func = v8::Local<v8::String>::Cast(info.Data());
@@ -268,7 +272,7 @@ static void php_v8js_property_caller(const v8::FunctionCallbackInfo<v8::Value>&
 	if (!value.IsEmpty() && value->IsFunction())
 	if (!value.IsEmpty() && value->IsFunction())
 	{
 	{
 		int argc = info.Length(), i = 0;
 		int argc = info.Length(), i = 0;
-		v8::Local<v8::Value> *argv = new v8::Local<v8::Value>[argc];
+		v8::Local<v8::Value> argv[argc];
 		v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(value);
 		v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(value);
 
 
 		if (cb_func->Equals(V8JS_SYM(ZEND_INVOKE_FUNC_NAME))) {
 		if (cb_func->Equals(V8JS_SYM(ZEND_INVOKE_FUNC_NAME))) {
@@ -316,6 +320,8 @@ static void php_v8js_property_getter(v8::Local<v8::String> property, const v8::P
 		return;
 		return;
 	}
 	}
 
 
+	v8::Isolate *isolate = reinterpret_cast<v8::Isolate *>(self->GetAlignedPointerFromInternalField(1));
+
 	/* If __get() is set for PHP object, call it */
 	/* If __get() is set for PHP object, call it */
 	value = self->GetHiddenValue(V8JS_SYM(ZEND_GET_FUNC_NAME));
 	value = self->GetHiddenValue(V8JS_SYM(ZEND_GET_FUNC_NAME));
 	if (!value.IsEmpty() && value->IsFunction()) {
 	if (!value.IsEmpty() && value->IsFunction()) {
@@ -340,6 +346,7 @@ static void php_v8js_property_getter(v8::Local<v8::String> property, const v8::P
 static void php_v8js_property_query(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Integer> &info) /* {{{ */
 static void php_v8js_property_query(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Integer> &info) /* {{{ */
 {
 {
 	v8::Local<v8::Object> self = info.Holder();
 	v8::Local<v8::Object> self = info.Holder();
+	v8::Isolate *isolate = reinterpret_cast<v8::Isolate *>(self->GetAlignedPointerFromInternalField(1));
 	v8::Local<v8::Value> value;
 	v8::Local<v8::Value> value;
 
 
 	/* Return early if property is set in JS object */
 	/* Return early if property is set in JS object */
@@ -734,6 +741,13 @@ int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags, v
 	}
 	}
 	else if (jsValue->IsObject())
 	else if (jsValue->IsObject())
 	{
 	{
+		v8::Handle<v8::Object> self = v8::Handle<v8::Object>::Cast(jsValue);
+		// if this is a wrapped PHP object, then just unwrap it.
+		if (!self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY)).IsEmpty()) {
+			zval *object = reinterpret_cast<zval *>(self->GetAlignedPointerFromInternalField(0));
+			RETVAL_ZVAL(object, 1, 0);
+			return SUCCESS;
+		}
 		if ((flags & V8JS_FLAG_FORCE_ARRAY) || jsValue->IsArray()) {
 		if ((flags & V8JS_FLAG_FORCE_ARRAY) || jsValue->IsArray()) {
 			array_init(return_value);
 			array_init(return_value);
 			return php_v8js_v8_get_properties_hash(jsValue, Z_ARRVAL_P(return_value), flags, isolate TSRMLS_CC);
 			return php_v8js_v8_get_properties_hash(jsValue, Z_ARRVAL_P(return_value), flags, isolate TSRMLS_CC);

+ 76 - 41
v8js_methods.cc

@@ -43,6 +43,7 @@ V8JS_METHOD(sleep) /* {{{ */
 /* global.print - php print() */
 /* global.print - php print() */
 V8JS_METHOD(print) /* {{{ */
 V8JS_METHOD(print) /* {{{ */
 {
 {
+	v8::Isolate *isolate = info.GetIsolate();
 	int ret = 0;
 	int ret = 0;
 	TSRMLS_FETCH();
 	TSRMLS_FETCH();
 
 
@@ -55,40 +56,65 @@ V8JS_METHOD(print) /* {{{ */
 }
 }
 /* }}} */
 /* }}} */
 
 
-static void _php_v8js_dumper(v8::Local<v8::Value> var, int level TSRMLS_DC) /* {{{ */
+static void _php_v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int level TSRMLS_DC) /* {{{ */
 {
 {
-	v8::String::Utf8Value str(var->ToDetailString());
-	const char *valstr = ToCString(str);
-	size_t valstr_len = (valstr) ? strlen(valstr) : 0;
-
 	if (level > 1) {
 	if (level > 1) {
 		php_printf("%*c", (level - 1) * 2, ' ');
 		php_printf("%*c", (level - 1) * 2, ' ');
 	}
 	}
 
 
-	if (var->IsString())
+	if (var.IsEmpty())
 	{
 	{
-		php_printf("string(%zu) \"%s\"\n", valstr_len, valstr);
+		php_printf("<empty>\n");
+		return;
+	}
+	if (var->IsNull() || var->IsUndefined() /* PHP compat */)
+	{
+		php_printf("NULL\n");
+		return;
+	}
+	if (var->IsInt32())
+	{
+		php_printf("int(%ld)\n", (long) var->IntegerValue());
+		return;
+	}
+	if (var->IsUint32())
+	{
+		php_printf("int(%lu)\n", (unsigned long) var->IntegerValue());
+		return;
 	}
 	}
-	else if (var->IsBoolean())
+	if (var->IsNumber())
 	{
 	{
-		php_printf("bool(%s)\n", valstr);
+		php_printf("float(%f)\n", var->NumberValue());
+		return;
 	}
 	}
-	else if (var->IsInt32() || var->IsUint32())
+	if (var->IsBoolean())
 	{
 	{
-		php_printf("int(%s)\n", valstr);
+		php_printf("bool(%s)\n", var->BooleanValue() ? "true" : "false");
+		return;
 	}
 	}
-	else if (var->IsNumber())
+
+	v8::TryCatch try_catch; /* object.toString() can throw an exception */
+	v8::Local<v8::String> details = var->ToDetailString();
+	if (try_catch.HasCaught()) {
+		details = V8JS_SYM("<toString threw exception>");
+	}
+	v8::String::Utf8Value str(details);
+	const char *valstr = ToCString(str);
+	size_t valstr_len = (valstr) ? strlen(valstr) : 0;
+
+	if (var->IsString())
 	{
 	{
-		php_printf("float(%s)\n", valstr);
+		php_printf("string(%zu) \"%s\"\n", valstr_len, valstr);
 	}
 	}
 	else if (var->IsDate())
 	else if (var->IsDate())
 	{
 	{
+		// fake the fields of a PHP DateTime
 		php_printf("Date(%s)\n", valstr);
 		php_printf("Date(%s)\n", valstr);
 	}
 	}
 #if PHP_V8_API_VERSION >= 2003007
 #if PHP_V8_API_VERSION >= 2003007
 	else if (var->IsRegExp())
 	else if (var->IsRegExp())
 	{
 	{
-		php_printf("RegExp(%s)\n", valstr);
+		php_printf("regexp(%s)\n", valstr);
 	}
 	}
 #endif
 #endif
 	else if (var->IsArray())
 	else if (var->IsArray())
@@ -100,7 +126,7 @@ static void _php_v8js_dumper(v8::Local<v8::Value> var, int level TSRMLS_DC) /* {
 
 
 		for (unsigned i = 0; i < length; i++) {
 		for (unsigned i = 0; i < length; i++) {
 			php_printf("%*c[%d] =>\n", level * 2, ' ', i);
 			php_printf("%*c[%d] =>\n", level * 2, ' ', i);
-			_php_v8js_dumper(array->Get(i), level + 1 TSRMLS_CC);
+			_php_v8js_dumper(isolate, array->Get(i), level + 1 TSRMLS_CC);
 		}
 		}
 
 
 		if (level > 1) {
 		if (level > 1) {
@@ -113,24 +139,31 @@ static void _php_v8js_dumper(v8::Local<v8::Value> var, int level TSRMLS_DC) /* {
 	{
 	{
 		v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(var);
 		v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(var);
 		V8JS_GET_CLASS_NAME(cname, object);
 		V8JS_GET_CLASS_NAME(cname, object);
+		int hash = object->GetIdentityHash();
 
 
 		if (var->IsFunction())
 		if (var->IsFunction())
 		{
 		{
 			v8::String::Utf8Value csource(object->ToString());
 			v8::String::Utf8Value csource(object->ToString());
-			php_printf("object(%s)#%d {\n%*c%s\n", ToCString(cname), object->GetIdentityHash(), level * 2 + 2, ' ', ToCString(csource));
+			php_printf("object(Closure)#%d {\n%*c%s\n", hash, level * 2 + 2, ' ', ToCString(csource));
 		}
 		}
 		else
 		else
 		{
 		{
-			v8::Local<v8::Array> keys = object->GetPropertyNames();
+			v8::Local<v8::Array> keys = object->GetOwnPropertyNames();
 			uint32_t length = keys->Length();
 			uint32_t length = keys->Length();
 
 
-			php_printf("object(%s)#%d (%d) {\n", ToCString(cname), object->GetIdentityHash(), length);
+			if (strcmp(ToCString(cname), "Array") == 0 ||
+				strcmp(ToCString(cname), "V8Object") == 0) {
+				php_printf("array");
+			} else {
+				php_printf("object(%s)#%d", ToCString(cname), hash);
+			}
+			php_printf(" (%d) {\n", length);
 
 
 			for (unsigned i = 0; i < length; i++) {
 			for (unsigned i = 0; i < length; i++) {
 				v8::Local<v8::String> key = keys->Get(i)->ToString();
 				v8::Local<v8::String> key = keys->Get(i)->ToString();
 				v8::String::Utf8Value kname(key);
 				v8::String::Utf8Value kname(key);
 				php_printf("%*c[\"%s\"] =>\n", level * 2, ' ', ToCString(kname));
 				php_printf("%*c[\"%s\"] =>\n", level * 2, ' ', ToCString(kname));
-				_php_v8js_dumper(object->Get(key), level + 1 TSRMLS_CC);
+				_php_v8js_dumper(isolate, object->Get(key), level + 1 TSRMLS_CC);
 			}
 			}
 		}
 		}
 
 
@@ -150,11 +183,11 @@ static void _php_v8js_dumper(v8::Local<v8::Value> var, int level TSRMLS_DC) /* {
 /* global.var_dump - Dump JS values */
 /* global.var_dump - Dump JS values */
 V8JS_METHOD(var_dump) /* {{{ */
 V8JS_METHOD(var_dump) /* {{{ */
 {
 {
-	int i;
+	v8::Isolate *isolate = info.GetIsolate();
 	TSRMLS_FETCH();
 	TSRMLS_FETCH();
 
 
 	for (int i = 0; i < info.Length(); i++) {
 	for (int i = 0; i < info.Length(); i++) {
-		_php_v8js_dumper(info[i], 1 TSRMLS_CC);
+		_php_v8js_dumper(isolate, info[i], 1 TSRMLS_CC);
 	}
 	}
 
 
 	info.GetReturnValue().Set(V8JS_NULL);
 	info.GetReturnValue().Set(V8JS_NULL);
@@ -163,6 +196,7 @@ V8JS_METHOD(var_dump) /* {{{ */
 
 
 V8JS_METHOD(require)
 V8JS_METHOD(require)
 {
 {
+	v8::Isolate *isolate = info.GetIsolate();
 	TSRMLS_FETCH();
 	TSRMLS_FETCH();
 
 
 	// Get the extension context
 	// Get the extension context
@@ -171,7 +205,7 @@ V8JS_METHOD(require)
 
 
 	// Check that we have a module loader
 	// Check that we have a module loader
 	if (c->module_loader == NULL) {
 	if (c->module_loader == NULL) {
-		info.GetReturnValue().Set(v8::ThrowException(v8::String::New("No module loader")));
+		info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("No module loader")));
 		return;
 		return;
 	}
 	}
 
 
@@ -185,7 +219,7 @@ V8JS_METHOD(require)
 	php_v8js_commonjs_normalise_identifier(c->modules_base.back(), module_id, normalised_path, module_name);
 	php_v8js_commonjs_normalise_identifier(c->modules_base.back(), module_id, normalised_path, module_name);
 	efree(module_id);
 	efree(module_id);
 
 
-	char *normalised_module_id = (char *)emalloc(strlen(module_id));
+	char *normalised_module_id = (char *)emalloc(strlen(normalised_path)+1+strlen(module_name)+1);
 	*normalised_module_id = 0;
 	*normalised_module_id = 0;
 
 
 	if (strlen(normalised_path) > 0) {
 	if (strlen(normalised_path) > 0) {
@@ -202,7 +236,7 @@ V8JS_METHOD(require)
     	if (!strcmp(*it, normalised_module_id)) {
     	if (!strcmp(*it, normalised_module_id)) {
     		efree(normalised_path);
     		efree(normalised_path);
 
 
-		info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module cyclic dependency")));
+		info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module cyclic dependency")));
 		return;
 		return;
     	}
     	}
     }
     }
@@ -227,7 +261,7 @@ V8JS_METHOD(require)
 	if (FAILURE == call_user_function(EG(function_table), NULL, c->module_loader, &module_code, 1, params TSRMLS_CC)) {
 	if (FAILURE == call_user_function(EG(function_table), NULL, c->module_loader, &module_code, 1, params TSRMLS_CC)) {
 		efree(normalised_path);
 		efree(normalised_path);
 
 
-		info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module loader callback failed")));
+		info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module loader callback failed")));
 		return;
 		return;
 	}
 	}
 
 
@@ -237,7 +271,7 @@ V8JS_METHOD(require)
 
 
 		// Clear the PHP exception and throw it in V8 instead
 		// Clear the PHP exception and throw it in V8 instead
 		zend_clear_exception(TSRMLS_C);
 		zend_clear_exception(TSRMLS_C);
-		info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module loader callback exception")));
+		info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module loader callback exception")));
 		return;
 		return;
 	}
 	}
 
 
@@ -250,44 +284,44 @@ V8JS_METHOD(require)
 	if (!strlen(Z_STRVAL(module_code))) {
 	if (!strlen(Z_STRVAL(module_code))) {
 		efree(normalised_path);
 		efree(normalised_path);
 
 
-		info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module loader callback did not return code")));
+		info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module loader callback did not return code")));
 		return;
 		return;
 	}
 	}
 
 
 	// Create a template for the global object and set the built-in global functions
 	// Create a template for the global object and set the built-in global functions
 	v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
 	v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
-	global->Set(v8::String::New("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);
+	global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);
 	global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly);
 	global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly);
-	global->Set(v8::String::New("require"), v8::FunctionTemplate::New(V8JS_MN(require), v8::External::New(c)), v8::ReadOnly);
+	global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(V8JS_MN(require), v8::External::New(c)), v8::ReadOnly);
 
 
 	// Add the exports object in which the module can return its API
 	// Add the exports object in which the module can return its API
 	v8::Handle<v8::ObjectTemplate> exports_template = v8::ObjectTemplate::New();
 	v8::Handle<v8::ObjectTemplate> exports_template = v8::ObjectTemplate::New();
 	v8::Handle<v8::Object> exports = exports_template->NewInstance();
 	v8::Handle<v8::Object> exports = exports_template->NewInstance();
-	global->Set(v8::String::New("exports"), exports);
+	global->Set(V8JS_SYM("exports"), exports);
 
 
 	// Add the module object in which the module can have more fine-grained control over what it can return
 	// Add the module object in which the module can have more fine-grained control over what it can return
 	v8::Handle<v8::ObjectTemplate> module_template = v8::ObjectTemplate::New();
 	v8::Handle<v8::ObjectTemplate> module_template = v8::ObjectTemplate::New();
 	v8::Handle<v8::Object> module = module_template->NewInstance();
 	v8::Handle<v8::Object> module = module_template->NewInstance();
-	module->Set(v8::String::New("id"), v8::String::New(normalised_module_id));
-	global->Set(v8::String::New("module"), module);
+	module->Set(V8JS_SYM("id"), V8JS_STR(normalised_module_id));
+	global->Set(V8JS_SYM("module"), module);
 
 
 	// Each module gets its own context so different modules do not affect each other
 	// Each module gets its own context so different modules do not affect each other
-	v8::Local<v8::Context> context = v8::Local<v8::Context>::New(c->isolate, v8::Context::New(c->isolate, NULL, global));
+	v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate, v8::Context::New(isolate, NULL, global));
 
 
 	// Catch JS exceptions
 	// Catch JS exceptions
 	v8::TryCatch try_catch;
 	v8::TryCatch try_catch;
 
 
-	v8::Locker locker(c->isolate);
-	v8::Isolate::Scope isolate_scope(c->isolate);
+	v8::Locker locker(isolate);
+	v8::Isolate::Scope isolate_scope(isolate);
 
 
-	v8::HandleScope handle_scope(c->isolate);
+	v8::HandleScope handle_scope(isolate);
 
 
 	// Enter the module context
 	// Enter the module context
 	v8::Context::Scope scope(context);
 	v8::Context::Scope scope(context);
 	// Set script identifier
 	// Set script identifier
 	v8::Local<v8::String> sname = V8JS_SYM("require");
 	v8::Local<v8::String> sname = V8JS_SYM("require");
 
 
-	v8::Local<v8::String> source = v8::String::New(Z_STRVAL(module_code));
+	v8::Local<v8::String> source = V8JS_STR(Z_STRVAL(module_code));
 
 
 	// Create and compile script
 	// Create and compile script
 	v8::Local<v8::Script> script = v8::Script::New(source, sname);
 	v8::Local<v8::Script> script = v8::Script::New(source, sname);
@@ -295,7 +329,7 @@ V8JS_METHOD(require)
 	// The script will be empty if there are compile errors
 	// The script will be empty if there are compile errors
 	if (script.IsEmpty()) {
 	if (script.IsEmpty()) {
 		efree(normalised_path);
 		efree(normalised_path);
-		info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module script compile failed")));
+		info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module script compile failed")));
 		return;
 		return;
 	}
 	}
 
 
@@ -315,7 +349,7 @@ V8JS_METHOD(require)
 
 
 	// Script possibly terminated, return immediately
 	// Script possibly terminated, return immediately
 	if (!try_catch.CanContinue()) {
 	if (!try_catch.CanContinue()) {
-		info.GetReturnValue().Set(v8::ThrowException(v8::String::New("Module script compile failed")));
+		info.GetReturnValue().Set(v8::ThrowException(V8JS_SYM("Module script compile failed")));
 		return;
 		return;
 	}
 	}
 
 
@@ -328,9 +362,9 @@ V8JS_METHOD(require)
 
 
 	// Cache the module so it doesn't need to be compiled and run again
 	// Cache the module so it doesn't need to be compiled and run again
 	// Ensure compatibility with CommonJS implementations such as NodeJS by playing nicely with module.exports and exports
 	// Ensure compatibility with CommonJS implementations such as NodeJS by playing nicely with module.exports and exports
-	if (module->Has(v8::String::New("exports")) && !module->Get(v8::String::New("exports"))->IsUndefined()) {
+	if (module->Has(V8JS_SYM("exports")) && !module->Get(V8JS_SYM("exports"))->IsUndefined()) {
 		// If module.exports has been set then we cache this arbitrary value...
 		// If module.exports has been set then we cache this arbitrary value...
-		V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(module->Get(v8::String::New("exports"))->ToObject());
+		V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(module->Get(V8JS_SYM("exports"))->ToObject());
 	} else {
 	} else {
 		// ...otherwise we cache the exports object itself
 		// ...otherwise we cache the exports object itself
 		V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(exports);
 		V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(exports);
@@ -341,6 +375,7 @@ V8JS_METHOD(require)
 
 
 void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate> global, php_v8js_ctx *c) /* {{{ */
 void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate> global, php_v8js_ctx *c) /* {{{ */
 {
 {
+	v8::Isolate *isolate = c->isolate;
 	global->Set(V8JS_SYM("exit"), v8::FunctionTemplate::New(V8JS_MN(exit)), v8::ReadOnly);
 	global->Set(V8JS_SYM("exit"), v8::FunctionTemplate::New(V8JS_MN(exit)), v8::ReadOnly);
 	global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly);
 	global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly);
 	global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);
 	global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);