Преглед изворни кода

Merge remote-tracking branch 'rosmo/compile-script-201409', closes #95

Stefan Siegl пре 10 година
родитељ
комит
9cb3711c66
7 измењених фајлова са 276 додато и 22 уклоњено
  1. 7 0
      README.md
  2. 9 0
      php_v8js_macros.h
  3. 4 1
      tests/checkstring.phpt
  4. 21 0
      tests/checkstring_compile.phpt
  5. 46 0
      tests/compile_string.phpt
  6. 46 0
      tests/compile_string_isolate.phpt
  7. 143 21
      v8js.cc

+ 7 - 0
README.md

@@ -97,6 +97,13 @@ PHP API
         // A time limit (milliseconds) and/or memory limit (bytes) can be provided to restrict execution. These options will throw a V8JsTimeLimitException or V8JsMemoryLimitException.
         public mixed V8Js::executeString( string $script [, string $identifier [, int $flags = V8Js::FLAG_NONE [, int $time_limit = 0 [, int $memory_limit = 0]]]])
 
+        // Compiles a script in object's context with optional identifier string.
+        public mixed V8Js::compileString( string $script [, string $identifier ])
+
+        // Executes a precompiled script in object's context.
+        // A time limit (milliseconds) and/or memory limit (bytes) can be provided to restrict execution. These options will throw a V8JsTimeLimitException or V8JsMemoryLimitException.
+        public mixed V8Js::executeScript( resource $script [, int $flags = V8Js::FLAG_NONE [, int $time_limit = 0 [, int $memory_limit = 0]]])
+
         // Returns uncaught pending exception or null if there is no pending exception.
         public V8JsScriptException V8Js::getPendingException( )
 

+ 9 - 0
php_v8js_macros.h

@@ -246,6 +246,7 @@ struct php_v8js_object {
 };
 /* }}} */
 
+/* Resource declaration */
 
 /* Module globals */
 ZEND_BEGIN_MODULE_GLOBALS(v8js)
@@ -285,6 +286,14 @@ ZEND_EXTERN_MODULE_GLOBALS(v8js)
 /* Register builtin methods into passed object */
 void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate>, php_v8js_ctx *c);
 
+typedef struct _php_v8js_script {
+	char *name;
+	v8::Isolate *isolate;	
+	v8::Persistent<v8::Script, v8::CopyablePersistentTraits<v8::Script>> *script;
+} php_v8js_script;
+
+static void php_v8js_script_free(php_v8js_script *res, bool dispose_persistent);
+
 #endif	/* PHP_V8JS_MACROS_H */
 
 /*

+ 4 - 1
tests/checkstring.phpt

@@ -15,7 +15,10 @@ try {
 }
 ?>
 ===EOF===
---EXPECT--
+--EXPECTF--
+Deprecated: Function V8Js::checkString() is deprecated in %s on line %d
 bool(true)
+
+Deprecated: Function V8Js::checkString() is deprecated in %s on line %d
 string(60) "V8Js::checkString():1: SyntaxError: Unexpected token ILLEGAL"
 ===EOF===

+ 21 - 0
tests/checkstring_compile.phpt

@@ -0,0 +1,21 @@
+--TEST--
+Test V8::executeString() : Script validator test using compileString
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+$v8 = new V8Js();
+var_dump($v8->compileString('print("Hello World!");'));
+
+try {
+	var_dump($v8->compileString('print("Hello World!);'));
+} catch (V8JsScriptException $e) {
+	var_dump($e->getMessage());
+}
+?>
+===EOF===
+--EXPECTF--
+resource(%d) of type (V8Js script)
+string(62) "V8Js::compileString():1: SyntaxError: Unexpected token ILLEGAL"
+===EOF===

+ 46 - 0
tests/compile_string.phpt

@@ -0,0 +1,46 @@
+--TEST--
+Test V8::compileString() : Compile and run a script
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php 
+
+$js = <<<'EOT'
+var a = { 'hello' : 'world' }; a;
+EOT;
+
+$js2 = <<<'EOT'
+var a = { 'foo' : 'bar' }; a;
+EOT;
+
+$v8 = new V8Js();
+
+try {
+  $script_a = $v8->compileString($js, 'a.js');
+	var_dump(is_resource($script_a));
+  $script_b = $v8->compileString($js2, 'b.js');
+	var_dump(is_resource($script_b));
+	var_dump($v8->executeScript($script_a));
+	var_dump($v8->executeScript($script_b));
+	var_dump($v8->executeScript($script_a));
+} catch (V8JsScriptException $e) {
+	var_dump($e);
+}
+?>
+===EOF===
+--EXPECT--
+bool(true)
+bool(true)
+object(V8Object)#2 (1) {
+  ["hello"]=>
+  string(5) "world"
+}
+object(V8Object)#2 (1) {
+  ["foo"]=>
+  string(3) "bar"
+}
+object(V8Object)#2 (1) {
+  ["hello"]=>
+  string(5) "world"
+}
+===EOF===

+ 46 - 0
tests/compile_string_isolate.phpt

@@ -0,0 +1,46 @@
+--TEST--
+Test V8::compileString() : Check compiled script isolate processing
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php 
+
+$js = <<<'EOT'
+var a = { 'hello' : 'world' }; a;
+EOT;
+
+$js2 = <<<'EOT'
+var a = { 'foo' : 'bar' }; a;
+EOT;
+
+$v8 = new V8Js();
+$v8two = new V8Js();
+
+try {
+  $script_a = $v8->compileString($js, 'a.js');
+	var_dump($script_a);
+  $script_b = $v8two->compileString($js2, 'b.js');
+	var_dump($script_b);
+	var_dump($v8->executeScript($script_a));
+	var_dump($v8->executeScript($script_b));
+	var_dump($v8->executeScript($script_a));
+} catch (V8JsScriptException $e) {
+	var_dump($e);
+}
+?>
+===EOF===
+--EXPECTF--
+resource(%d) of type (V8Js script)
+resource(%d) of type (V8Js script)
+object(V8Object)#%d (1) {
+  ["hello"]=>
+  string(5) "world"
+}
+
+Warning: Script resource from wrong V8Js object passed in %s on line %d
+bool(false)
+object(V8Object)#%d (1) {
+  ["hello"]=>
+  string(5) "world"
+}
+===EOF===

+ 143 - 21
v8js.cc

@@ -41,6 +41,9 @@ static void php_v8js_create_script_exception(zval *, v8::TryCatch * TSRMLS_DC);
 
 ZEND_DECLARE_MODULE_GLOBALS(v8js)
 
+int le_v8js_script;
+#define PHP_V8JS_SCRIPT_RES_NAME "V8Js script"
+
 /* {{{ INI Settings */
 
 static ZEND_INI_MH(v8js_OnUpdateV8Flags) /* {{{ */
@@ -76,7 +79,7 @@ static ZEND_INI_MH(v8js_OnUpdateUseDate) /* {{{ */
 }
 /* }}} */
 
-ZEND_INI_BEGIN() /* {{{ */
+ZEND_INI_BEGIN() /* {{{ */
 	ZEND_INI_ENTRY("v8js.flags", NULL, ZEND_INI_ALL, v8js_OnUpdateV8Flags)
 	ZEND_INI_ENTRY("v8js.use_date", "0", ZEND_INI_ALL, v8js_OnUpdateUseDate)
 ZEND_INI_END()
@@ -1135,30 +1138,17 @@ static void php_v8js_timer_thread(TSRMLS_D)
 	}
 }
 
-/* {{{ proto mixed V8Js::executeString(string script [, string identifier [, int flags]])
- */
-static PHP_METHOD(V8Js, executeString)
+static void php_v8js_compile_script(zval *this_ptr, const char *str, int str_len, const char *identifier, int identifier_len, php_v8js_script **ret)
 {
-	char *str = NULL, *identifier = NULL, *tz = NULL;
-	int str_len = 0, identifier_len = 0;
-	long flags = V8JS_FLAG_NONE, time_limit = 0, memory_limit = 0;
-
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slll", &str, &str_len, &identifier, &identifier_len, &flags, &time_limit, &memory_limit) == FAILURE) {
-		return;
-	}
-
-	V8JS_BEGIN_CTX(c, getThis())
+	php_v8js_script *res = NULL;
 
-	V8JSG(timer_mutex).lock();
-	c->time_limit_hit = false;
-	c->memory_limit_hit = false;
-	V8JSG(timer_mutex).unlock();
+	V8JS_BEGIN_CTX(c, this_ptr)
 
 	/* Catch JS exceptions */
 	v8::TryCatch try_catch;
 
 	/* Set script identifier */
-	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::compileString()");
 
 	/* 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 = V8JS_STRL(str, str_len);
@@ -1169,6 +1159,36 @@ static PHP_METHOD(V8Js, executeString)
 		php_v8js_throw_script_exception(&try_catch TSRMLS_CC);
 		return;
 	}
+	res = (php_v8js_script *)emalloc(sizeof(php_v8js_script));
+	res->script = new v8::Persistent<v8::Script, v8::CopyablePersistentTraits<v8::Script>>(c->isolate, script);
+
+	v8::String::Utf8Value _sname(sname);
+	res->name = estrndup(ToCString(_sname), _sname.length());
+	res->isolate = c->isolate;
+	*ret = res;
+	return;
+}
+
+static void php_v8js_execute_script(zval *this_ptr, php_v8js_script *res, long flags, long time_limit, long memory_limit, zval **return_value)
+{
+	char *tz = NULL;
+
+	V8JS_BEGIN_CTX(c, this_ptr)
+
+	if (res->isolate != c->isolate) {
+		zend_error(E_WARNING, "Script resource from wrong V8Js object passed");
+		ZVAL_BOOL(*return_value, 0); 
+		return;
+	}
+
+
+	V8JSG(timer_mutex).lock();
+	c->time_limit_hit = false;
+	c->memory_limit_hit = false;
+	V8JSG(timer_mutex).unlock();
+
+	/* Catch JS exceptions */
+	v8::TryCatch try_catch;
 
 	/* Set flags for runtime use */
 	V8JS_GLOBAL_SET_FLAGS(isolate, flags);
@@ -1215,6 +1235,7 @@ static PHP_METHOD(V8Js, executeString)
 
 	/* Execute script */
 	c->in_execution++;
+	v8::Local<v8::Script> script = v8::Local<v8::Script>::New(c->isolate, *res->script);
 	v8::Local<v8::Value> result = script->Run();
 	c->in_execution--;
 
@@ -1282,9 +1303,72 @@ static PHP_METHOD(V8Js, executeString)
 
 	/* Convert V8 value to PHP value */
 	if (!result.IsEmpty()) {
-		v8js_to_zval(result, return_value, flags, c->isolate TSRMLS_CC);
+		v8js_to_zval(result, *return_value, flags, c->isolate TSRMLS_CC);
 	}
 }
+
+/* {{{ proto mixed V8Js::executeString(string script [, string identifier [, int flags]])
+ */
+static PHP_METHOD(V8Js, executeString)
+{
+	char *str = NULL, *identifier = NULL, *tz = NULL;
+	int str_len = 0, identifier_len = 0;
+	long flags = V8JS_FLAG_NONE, time_limit = 0, memory_limit = 0;
+	php_v8js_script *res = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slll", &str, &str_len, &identifier, &identifier_len, &flags, &time_limit, &memory_limit) == FAILURE) {
+		return;
+	}
+
+	php_v8js_compile_script(getThis(), str, str_len, identifier, identifier_len, &res);
+	if (!res) {
+		RETURN_FALSE;
+	}
+	php_v8js_execute_script(getThis(), res, flags, time_limit, memory_limit, &return_value);
+	php_v8js_script_free(res, true);
+
+}
+/* }}} */
+
+
+/* {{{ proto mixed V8Js::compileString(string script [, string identifier])
+ */
+static PHP_METHOD(V8Js, compileString)
+{
+	char *str = NULL, *identifier = NULL;
+	int str_len = 0, identifier_len = 0;
+	php_v8js_script *res = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &identifier, &identifier_len) == FAILURE) {
+		return;
+	}
+
+	php_v8js_compile_script(getThis(), str, str_len, identifier, identifier_len, &res);
+	if (res) {
+		ZEND_REGISTER_RESOURCE(return_value, res, le_v8js_script);
+	}
+	return;
+}
+
+/* }}} */
+
+/* {{{ proto mixed V8Js::executeScript(resource script [, int flags]])
+ */
+static PHP_METHOD(V8Js, executeScript)
+{
+	long flags = V8JS_FLAG_NONE, time_limit = 0, memory_limit = 0;
+	zval *zscript;
+	php_v8js_script *res;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lll", &zscript, &flags, &time_limit, &memory_limit) == FAILURE) {
+		return;
+	}
+
+	ZEND_FETCH_RESOURCE(res, php_v8js_script*, &zscript, -1, PHP_V8JS_SCRIPT_RES_NAME, le_v8js_script);
+	ZEND_VERIFY_RESOURCE(res);
+
+	php_v8js_execute_script(getThis(), res, flags, time_limit, memory_limit, &return_value);
+}
 /* }}} */
 
 /* {{{ proto mixed V8Js::checkString(string script)
@@ -1446,7 +1530,29 @@ static void php_v8js_persistent_zval_dtor(zval **p) /* {{{ */
 }
 /* }}} */
 
-static int php_v8js_register_extension(char *name, uint name_len, char *source, uint source_len, zval *deps_arr, zend_bool auto_enable TSRMLS_DC) /* {{{ */
+static void php_v8js_script_free(php_v8js_script *res, bool dispose_persistent)
+{
+	if (res->name) {
+		efree(res->name);
+		res->name = NULL;
+	}
+	if (dispose_persistent) {
+		res->script->~Persistent(); // does Reset()
+		res->script = NULL;
+	}
+}
+
+static void php_v8js_script_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
+{
+	php_v8js_script *res = (php_v8js_script *)rsrc->ptr;
+	if (res) {
+		php_v8js_script_free(res, false);
+		efree(res);
+	}
+}
+/* }}} */
+
+static int php_v8js_register_extension(char *name, uint name_len, char *source, uint source_len, zval *deps_arr, zend_bool auto_enable TSRMLS_DC) /* {{{ */
 {
 	php_v8js_jsext *jsext = NULL;
 
@@ -1575,6 +1681,18 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_executestring, 0, 0, 1)
 	ZEND_ARG_INFO(0, memory_limit)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_compilestring, 0, 0, 1)
+	ZEND_ARG_INFO(0, script)
+	ZEND_ARG_INFO(0, identifier)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_executescript, 0, 0, 1)
+	ZEND_ARG_INFO(0, script)
+	ZEND_ARG_INFO(0, flags)
+	ZEND_ARG_INFO(0, time_limit)
+	ZEND_ARG_INFO(0, memory_limit)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_checkstring, 0, 0, 1)
 	ZEND_ARG_INFO(0, script)
 ZEND_END_ARG_INFO()
@@ -1626,7 +1744,9 @@ static const zend_function_entry v8_function_methods[] = { /* {{{ */
 static const zend_function_entry v8js_methods[] = { /* {{{ */
 	PHP_ME(V8Js,	__construct,			arginfo_v8js_construct,				ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
 	PHP_ME(V8Js,	executeString,			arginfo_v8js_executestring,			ZEND_ACC_PUBLIC)
-	PHP_ME(V8Js,    checkString,			arginfo_v8js_checkstring,			ZEND_ACC_PUBLIC)
+	PHP_ME(V8Js,	compileString,			arginfo_v8js_compilestring,			ZEND_ACC_PUBLIC)
+	PHP_ME(V8Js,    executeScript,			arginfo_v8js_executescript,			ZEND_ACC_PUBLIC)
+	PHP_ME(V8Js,    checkString,			arginfo_v8js_checkstring,			ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	PHP_ME(V8Js,	getPendingException,	arginfo_v8js_getpendingexception,	ZEND_ACC_PUBLIC)
 	PHP_ME(V8Js,	setModuleLoader,		arginfo_v8js_setmoduleloader,		ZEND_ACC_PUBLIC)
 	PHP_ME(V8Js,	registerExtension,		arginfo_v8js_registerextension,		ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
@@ -1884,6 +2004,8 @@ static PHP_MINIT_FUNCTION(v8js)
 	php_ce_v8js_memory_limit_exception = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
 	php_ce_v8js_memory_limit_exception->ce_flags |= ZEND_ACC_FINAL;
 
+	le_v8js_script = zend_register_list_destructors_ex(php_v8js_script_dtor, NULL, PHP_V8JS_SCRIPT_RES_NAME, module_number);
+
 	REGISTER_INI_ENTRIES();
 
 	return SUCCESS;