0
0
mirror of https://github.com/phpv8/v8js.git synced 2025-03-11 20:08:43 +00:00

fix merge conflict

This commit is contained in:
Patrick Reilly 2013-05-09 15:55:30 -07:00
commit 6f44220328
33 changed files with 934 additions and 202 deletions

View File

@ -1,30 +1,29 @@
V8Js
====
This is a PHP extension for Google's V8 Javascript engine
V8Js is a PHP extension for Google's V8 Javascript engine.
The extension allows you to execute Javascript code in a secure sandbox from PHP. The executed code can be restricted using a time limit and/or memory limit. This provides the possibility to execute untrusted code with confidence.
Minimum requirements
--------------------
- V8 JavaScript Engine library version 2.5.8 <http://code.google.com/p/v8/> (trunk)
- V8 Javascript Engine library (libv8) version 3.2.4 or above <http://code.google.com/p/v8/> (trunk)
V8 is Google's open source JavaScript engine.
V8 is Google's open source Javascript engine.
V8 is written in C++ and is used in Google Chrome, the open source browser from Google.
V8 implements ECMAScript as specified in ECMA-262, 5th edition, and runs on Windows (XP or newer),
Mac OS X (10.5 or newer), and Linux systems that use IA-32, x64, or ARM processors.
V8 can run standalone, or can be embedded into any C++ application.
You can find more information here:
<http://code.google.com/p/v8/>
V8 implements ECMAScript as specified in ECMA-262, 5th edition.
This extension makes use of V8 isolates to ensure separation between multiple V8Js instances, hence the need for 3.2.4 or above.
- PHP 5.3.3+ (non-ZTS build preferred)
Note: V8 engine is not natively thread safe and this extension
has not been designed to work around it either yet and might or
might not work properly with ZTS enabled PHP. :)
- PHP 5.3.3+
This embedded implementation of the V8 engine uses thread locking so it should work with ZTS enabled.
However, this has not been tested yet.
API
===
PHP API
=======
class V8Js
{
@ -37,26 +36,31 @@ API
/* Methods */
// Initializes and starts V8 engine and Returns new V8Js object with it's own V8 context.
public __construct ( [string object_name = "PHP" [, array variables = NULL [, array extensions = NULL [, bool report_uncaught_exceptions = TRUE]]] )
public __construct ( [ string $object_name = "PHP" [, array $variables = NULL [, array $extensions = NULL [, bool $report_uncaught_exceptions = TRUE ] ] ] )
// Provide a function or method to be used to load required modules. This can be any valid PHP callable.
// The loader function will receive the normalised module path and should return Javascript code to be executed.
public setModuleLoader ( callable $loader )
// Compiles and executes script in object's context with optional identifier string.
public mixed V8Js::executeString( string script [, string identifier [, int flags = V8Js::FLAG_NONE]])
// 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]]]])
// Returns uncaught pending exception or null if there is no pending exception.
public V8JsException V8Js::getPendingException( void )
public V8JsScriptException V8Js::getPendingException( )
/** Static methods **/
// Registers persistent context independent global Javascript extension.
// NOTE! These extensions exist until PHP is shutdown and they need to be registered before V8 is initialized.
// For best performance V8 is initialized only once per process thus this call has to be done before any V8Js objects are created!
public static bool V8Js::registerExtension(string ext_name, string script [, array deps [, bool auto_enable = FALSE]])
public static bool V8Js::registerExtension( string $extension_name, string $code [, array $dependenciess [, bool $auto_enable = FALSE ] ] )
// Returns extensions successfully registered with V8Js::registerExtension().
public static array V8Js::getExtensions( void )
public static array V8Js::getExtensions( )
}
final class V8JsException extends Exception
final class V8JsScriptException extends Exception
{
/* Properties */
protected string JsFileName = NULL;
@ -65,10 +69,33 @@ API
protected string JsTrace = NULL;
/* Methods */
final public string getJsFileName( void )
final public int getJsLineNumber( void )
final public string getJsSourceLine( void )
final public string getJsTrace( void )
final public string getJsFileName( )
final public int getJsLineNumber( )
final public string getJsSourceLine( )
final public string getJsTrace( )
}
final class V8JsTimeLimitException extends Exception
{
}
final class V8JsMemoryLimitException extends Exception
{
}
Javascript API
==============
// Print a string.
print(string);
// Dump the contents of a variable.
var_dump(value);
// Terminate Javascript execution immediately.
exit();
// CommonJS Module support to require external code.
// This makes use of the PHP module loader provided via V8Js::setModuleLoader (see PHP API above).
require("path/to/module");

View File

@ -67,7 +67,7 @@ LDFLAGS=$old_LDFLAGS
AC_DEFINE_UNQUOTED([PHP_V8_VERSION], "$ac_cv_v8_version", [ ])
fi
PHP_NEW_EXTENSION(v8js, v8js.cc v8js_convert.cc v8js_methods.cc v8js_variables.cc, $ext_shared)
PHP_NEW_EXTENSION(v8js, v8js.cc v8js_convert.cc v8js_methods.cc v8js_variables.cc v8js_commonjs.cc, $ext_shared, , "-std=c++0x")
PHP_ADD_MAKEFILE_FRAGMENT
fi

View File

@ -22,8 +22,20 @@
#ifndef PHP_V8JS_MACROS_H
#define PHP_V8JS_MACROS_H
extern "C" {
#include "php.h"
#include "php_v8js.h"
}
#include <v8.h>
#include <chrono>
#include <stack>
#include <thread>
#include <map>
#include <vector>
/* V8Js Version */
#define V8JS_VERSION "0.1.3"
@ -80,22 +92,76 @@ extern zend_class_entry *php_ce_v8_object;
extern zend_class_entry *php_ce_v8_function;
/* Create PHP V8 object */
void php_v8js_create_v8(zval *, v8::Handle<v8::Value>, int TSRMLS_DC);
void php_v8js_create_v8(zval *, v8::Handle<v8::Value>, int, v8::Isolate * TSRMLS_DC);
/* Fetch V8 object properties */
int php_v8js_v8_get_properties_hash(v8::Handle<v8::Value>, HashTable *, int TSRMLS_DC);
int php_v8js_v8_get_properties_hash(v8::Handle<v8::Value>, HashTable *, int, v8::Isolate * TSRMLS_DC);
/* Convert zval into V8 value */
v8::Handle<v8::Value> zval_to_v8js(zval * TSRMLS_DC);
v8::Handle<v8::Value> zval_to_v8js(zval *, v8::Isolate * TSRMLS_DC);
/* Convert V8 value into zval */
int v8js_to_zval(v8::Handle<v8::Value>, zval *, int TSRMLS_DC);
/* Register builtin methods into passed object */
void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate>);
int v8js_to_zval(v8::Handle<v8::Value>, zval *, int, v8::Isolate * TSRMLS_DC);
/* Register accessors into passed object */
void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate>, zval * TSRMLS_DC);
void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate>, zval *, v8::Isolate * TSRMLS_DC);
/* {{{ Context container */
struct php_v8js_ctx {
zend_object std;
v8::Persistent<v8::String> object_name;
v8::Persistent<v8::Context> context;
zend_bool report_uncaught;
zval *pending_exception;
int in_execution;
v8::Isolate *isolate;
bool time_limit_hit;
bool memory_limit_hit;
v8::Persistent<v8::FunctionTemplate> global_template;
zval *module_loader;
std::vector<char *> modules_stack;
std::vector<char *> modules_base;
};
/* }}} */
// Timer context
struct php_v8js_timer_ctx
{
long time_limit;
long memory_limit;
std::chrono::time_point<std::chrono::high_resolution_clock> time_point;
php_v8js_ctx *v8js_ctx;
};
/* Module globals */
ZEND_BEGIN_MODULE_GLOBALS(v8js)
int v8_initialized;
HashTable *extensions;
int disposed_contexts; /* Disposed contexts since last time V8 did GC */
/* Ini globals */
char *v8_flags; /* V8 command line flags */
int max_disposed_contexts; /* Max disposed context allowed before forcing V8 GC */
// Timer thread globals
std::stack<php_v8js_timer_ctx *> timer_stack;
std::thread *timer_thread;
std::mutex timer_mutex;
bool timer_stop;
std::map<char *, v8::Handle<v8::Object> > modules_loaded;
ZEND_END_MODULE_GLOBALS(v8js)
extern zend_v8js_globals v8js_globals;
#ifdef ZTS
# define V8JSG(v) TSRMG(v8js_globals_id, zend_v8js_globals *, v)
#else
# define V8JSG(v) (v8js_globals.v)
#endif
/* Register builtin methods into passed object */
void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate>, php_v8js_ctx *c);
#endif /* PHP_V8JS_MACROS_H */

View File

@ -69,6 +69,6 @@ try {
$blaa->executeString('PHP.obj.foo(1,2,3);', "call_test1 #8.js");
echo "------------\n";
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}

View File

@ -6,6 +6,6 @@ $a->func = function ($a) { echo "Closure..\n"; };
try {
$a->executeString("print(PHP.func); PHP.func(1);", "closure_test.js");
$a->executeString("print(PHP.func); PHP.func(1);", "closure_test.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}

View File

@ -4,6 +4,6 @@ $a = new V8Js();
try {
var_dump($a->executeString("date = new Date('September 8, 1975 09:00:00'); print(date + '\\n'); date;", "test.js"));
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}

View File

@ -54,6 +54,6 @@ try {
$a->executeString("var patt1=/[^a-h]/g; var_dump(patt1);", "call_test5.js");
$a->executeString("var_dump(Math.PI, Infinity, null, undefined);", "call_test6.js");
// $a->executeString($JS);
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}

View File

@ -19,7 +19,7 @@
try {
$foo = new Foo();
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
}

View File

@ -27,7 +27,7 @@
try {
$foo = new Foo();
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo "PHP Exception: ", $e->getMessage(), "\n";
}

View File

@ -18,6 +18,6 @@ echo $a;
try {
$a->executeString("PHP.mytest(PHP.foo, PHP.my_private, PHP.my_protected);", "test7.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}

View File

@ -23,13 +23,13 @@ $a->executeString("PHP.myobj.mytest(3.14, 42, null);", "test3.js");
// Invalid parameters
try {
$a->executeString("PHP.myobj.mytest();", "test4.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}
try {
$a->executeString("PHP.myobj.mytest('arg1', 'arg2', 'arg3', 'extra_arg');", "test5.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}
@ -37,12 +37,12 @@ try {
try {
// date_default_timezone_set("UTC");
$a->executeString("date = new Date('September 8, 1975 09:00:00'); PHP.print(date); PHP.myobj.mytest(date, PHP.myobj, new Array(1,2,3));", "test6.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
try {
$a->executeString("PHP.myobj.mytest(PHP.myobj, new Array(1,2,3), new Array('foo', 'bar', PHP.myobj));", "test7.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}

View File

@ -139,7 +139,7 @@ $a->executeString("bigarray()", "test1.js");
try {
echo($a->executeString($jstparser, "test2.js")), "\n";
var_dump($a->executeString($jsontemplate, "test1.js"));
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage();
}
@ -168,6 +168,6 @@ var_dump($b->executeString("print('foobar\\n');"));
try {
$b->executeString("foobar; foo();", "extest.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e, $e->getJsFileName(), $e->getJsLineNumber(), $e->getJsSourceLine(), $e->getJsTrace());
}

View File

@ -14,7 +14,7 @@ $v8 = new V8Js();
try {
var_dump($v8->executeString($JS, 'basic.js'));
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
?>

View File

@ -10,7 +10,7 @@ $a->func = function ($arg) { echo "Hello {$arg}, I'm Closure!\n"; };
try {
$a->executeString('print(PHP.func + "\n"); PHP.func("foobar");', "closure_test.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}
?>

View File

@ -19,7 +19,7 @@ $a->func = function ($arg) use ($b) { return call_user_func($b, $arg); };
try {
$a->executeString('print(PHP.func + "\n"); print(PHP.func("world") + "\n");', "closure_test.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}
?>

View File

@ -0,0 +1,33 @@
--TEST--
Test V8Js::setModuleLoader : CommonJS modules
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$JS = <<< EOT
require("path/to/module1");
EOT;
$v8 = new V8Js();
$v8->setModuleLoader(function($module) {
switch ($module) {
case 'path/to/module1':
return 'print(' . json_encode($module . PHP_EOL) . ');require("./module2");';
case 'path/to/module2':
return 'print(' . json_encode($module . PHP_EOL) . ');require("../../module3");';
default:
return 'print(' . json_encode($module . PHP_EOL) . ');';
}
});
$v8->executeString($JS, 'module.js');
?>
===EOF===
--EXPECT--
path/to/module1
path/to/module2
module3
===EOF===

View File

@ -14,7 +14,7 @@ $v8->__construct();
try {
$v8->executeString($JS, 'basic.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
?>

View File

@ -27,7 +27,7 @@ $a->ctx = '#1';
try {
echo '1. ';
$a->executeString($JS_set, 'set.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
@ -38,7 +38,7 @@ $b->ctx = '#2';
try {
echo '2. ';
$b->executeString($JS_change, 'change.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
@ -46,7 +46,7 @@ try {
try {
echo '3. ';
$a->executeString($JS_read, 'read.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
@ -54,7 +54,7 @@ try {
try {
echo '4. ';
$a->executeString($JS_change, 'change.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
@ -62,7 +62,7 @@ try {
try {
echo '5. ';
$a->executeString($JS_read, 'read.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
?>

View File

@ -14,7 +14,7 @@ $a->foo = 'from first.js';
try {
$a->executeString($JS, 'first.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
@ -25,7 +25,7 @@ $b->foo = 'from second.js';
try {
$b->executeString($JS, 'second.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}

View File

@ -1,5 +1,5 @@
--TEST--
Test V8::executeString() : V8JsException
Test V8::executeString() : V8JsScriptException
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
@ -13,13 +13,13 @@ $v8 = new V8Js();
try {
$v8->executeString($JS, 'exception.js');
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
?>
===EOF===
--EXPECTF--
object(V8JsException)#2 (11) {
object(V8JsScriptException)#2 (11) {
["message":protected]=>
string(75) "exception.js:1: ReferenceError: this_function_does_not_exist is not defined"
["string":"Exception":private]=>

View File

@ -27,7 +27,7 @@ class Foo {
try {
$foo = new Foo();
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
}
?>

View File

@ -27,13 +27,13 @@ class Foo {
try {
$foo = new Foo();
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
}
?>
===EOF===
--EXPECTF--
object(V8JsException)#3 (11) {
object(V8JsScriptException)#3 (11) {
["message":protected]=>
string(49) "throw_0:1: ReferenceError: fooobar is not defined"
["string":"Exception":private]=>

View File

@ -26,7 +26,7 @@ class Foo {
try {
$foo = new Foo();
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo "PHP Exception: ", $e->getMessage(), "\n";
}
?>

View File

@ -15,7 +15,7 @@ try {
var_dump($v8->executeString($js, 'assoc_no_flags.js'));
echo "---\n";
var_dump($v8->executeString($js, 'assoc_force_to_array.js', V8Js::FLAG_FORCE_ARRAY));
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
?>

View File

@ -17,7 +17,7 @@ try {
$v8->executeString($js, 'no_flags.js');
echo "---\n";
$v8->executeString($js, 'force_to_array.js', V8Js::FLAG_FORCE_ARRAY);
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}
?>

72
tests/memory_limit.phpt Normal file
View File

@ -0,0 +1,72 @@
--TEST--
Test V8::executeString() : Time limit
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$JS = <<< EOT
var text = "abcdefghijklmnopqrstuvwyxz0123456789";
var memory = "";
for (var i = 0; i < 1000000; ++i) {
memory += text;
}
EOT;
$v8 = new V8Js();
try {
var_dump($v8->executeString($JS, 'basic.js', V8Js::FLAG_NONE, 0, 10000000));
} catch (V8JsMemoryLimitException $e) {
var_dump($e);
}
?>
===EOF===
--EXPECT--
object(V8JsMemoryLimitException)#2 (7) {
["message":protected]=>
string(46) "Script memory limit of 10000000 bytes exceeded"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(36) "/var/www/v8js/tests/memory_limit.php"
["line":protected]=>
int(13)
["trace":"Exception":private]=>
array(1) {
[0]=>
array(6) {
["file"]=>
string(36) "/var/www/v8js/tests/memory_limit.php"
["line"]=>
int(13)
["function"]=>
string(13) "executeString"
["class"]=>
string(4) "V8Js"
["type"]=>
string(2) "->"
["args"]=>
array(5) {
[0]=>
string(125) "var text = "abcdefghijklmnopqrstuvwyxz0123456789";
var memory = "";
for (var i = 0; i < 1000000; ++i) {
memory += text;
}"
[1]=>
string(8) "basic.js"
[2]=>
int(1)
[3]=>
int(0)
[4]=>
int(10000000)
}
}
}
["previous":"Exception":private]=>
NULL
}
===EOF===

View File

@ -36,13 +36,13 @@ $a->executeString("PHP.myobj.mytest(3.14, 42, null);", "test3.js");
// Invalid parameters
try {
$a->executeString("PHP.myobj.mytest();", "test4.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}
try {
$a->executeString("PHP.myobj.mytest('arg1', 'arg2', 'arg3', 'extra_arg');", "test5.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}
@ -50,14 +50,14 @@ try {
echo "\nTEST: Javascript Date -> PHP DateTime\n";
echo "======================================\n";
$a->executeString("date = new Date('September 8, 1975 09:00:00 GMT'); print(date.toUTCString() + '\\n'); PHP.myobj.mydatetest(date, 'foo');", "test6.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
echo $e->getMessage(), "\n";
}
// Array / Object
try {
$a->executeString("PHP.myobj.mytest(PHP.myobj, new Array(1,2,3), new Array('foo', 'bar', PHP.myobj));", "test7.js");
} catch (V8JsException $e) {
} catch (V8JsScriptException $e) {
var_dump($e);
}

69
tests/time_limit.phpt Normal file
View File

@ -0,0 +1,69 @@
--TEST--
Test V8::executeString() : Time limit
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$JS = <<< EOT
var text = "abcdefghijklmnopqrstuvwyxz0123456789";
for (var i = 0; i < 10000000; ++i) {
var encoded = encodeURI(text);
}
EOT;
$v8 = new V8Js();
try {
var_dump($v8->executeString($JS, 'basic.js', V8Js::FLAG_NONE, 1000));
} catch (V8JsTimeLimitException $e) {
var_dump($e);
}
?>
===EOF===
--EXPECT--
object(V8JsTimeLimitException)#2 (7) {
["message":protected]=>
string(47) "Script time limit of 1000 milliseconds exceeded"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(34) "/var/www/v8js/tests/time_limit.php"
["line":protected]=>
int(13)
["trace":"Exception":private]=>
array(1) {
[0]=>
array(6) {
["file"]=>
string(34) "/var/www/v8js/tests/time_limit.php"
["line"]=>
int(13)
["function"]=>
string(13) "executeString"
["class"]=>
string(4) "V8Js"
["type"]=>
string(2) "->"
["args"]=>
array(4) {
[0]=>
string(124) "var text = "abcdefghijklmnopqrstuvwyxz0123456789";
for (var i = 0; i < 10000000; ++i) {
var encoded = encodeURI(text);
}"
[1]=>
string(8) "basic.js"
[2]=>
int(1)
[3]=>
int(1000)
}
}
}
["previous":"Exception":private]=>
NULL
}
===EOF===

370
v8js.cc
View File

@ -25,40 +25,19 @@
#include "config.h"
#endif
#include "php_v8js_macros.h"
extern "C" {
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_smart_str.h"
#include "zend_exceptions.h"
#include "php_v8js.h"
}
#include <v8.h>
#include "php_v8js_macros.h"
/* Forward declarations */
static void php_v8js_throw_exception(v8::TryCatch * TSRMLS_DC);
static void php_v8js_create_exception(zval *, v8::TryCatch * TSRMLS_DC);
/* Module globals */
ZEND_BEGIN_MODULE_GLOBALS(v8js)
int v8_initialized;
HashTable *extensions;
v8::Persistent<v8::FunctionTemplate> global_template;
int disposed_contexts; /* Disposed contexts since last time V8 did GC */
/* Ini globals */
char *v8_flags; /* V8 command line flags */
int max_disposed_contexts; /* Max disposed context allowed before forcing V8 GC */
ZEND_END_MODULE_GLOBALS(v8js)
#ifdef ZTS
# define V8JSG(v) TSRMG(v8js_globals_id, zend_v8js_globals *, v)
#else
# define V8JSG(v) (v8js_globals.v)
#endif
static void php_v8js_throw_script_exception(v8::TryCatch * TSRMLS_DC);
static void php_v8js_create_script_exception(zval *, v8::TryCatch * TSRMLS_DC);
ZEND_DECLARE_MODULE_GLOBALS(v8js)
@ -107,7 +86,9 @@ ZEND_INI_END()
zend_class_entry *php_ce_v8_object;
zend_class_entry *php_ce_v8_function;
static zend_class_entry *php_ce_v8js;
static zend_class_entry *php_ce_v8js_exception;
static zend_class_entry *php_ce_v8js_script_exception;
static zend_class_entry *php_ce_v8js_time_limit_exception;
static zend_class_entry *php_ce_v8js_memory_limit_exception;
/* }}} */
/* {{{ Object Handlers */
@ -126,22 +107,12 @@ struct php_v8js_jsext {
};
/* }}} */
/* {{{ Context container */
struct php_v8js_ctx {
zend_object std;
v8::Persistent<v8::String> object_name;
v8::Persistent<v8::Context> context;
zend_bool report_uncaught;
zval *pending_exception;
int in_execution;
};
/* }}} */
/* {{{ Object container */
struct php_v8js_object {
zend_object std;
v8::Persistent<v8::Value> v8obj;
int flags;
v8::Isolate *isolate;
};
/* }}} */
@ -177,7 +148,7 @@ static zval *php_v8js_v8_read_property(zval *object, zval *member, int type ZEND
MAKE_STD_ZVAL(retval);
}
if (v8js_to_zval(jsVal, retval, obj->flags TSRMLS_CC) == SUCCESS) {
if (v8js_to_zval(jsVal, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) {
return retval;
}
}
@ -194,7 +165,7 @@ 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);
if (obj->v8obj->IsObject() && !obj->v8obj->IsFunction()) {
obj->v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value TSRMLS_CC));
obj->v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, obj->isolate TSRMLS_CC));
}
}
/* }}} */
@ -209,7 +180,7 @@ static void php_v8js_v8_unset_property(zval *object, zval *member ZEND_HASH_KEY_
}
/* }}} */
int php_v8js_v8_get_properties_hash(v8::Handle<v8::Value> jsValue, HashTable *retval, int flags TSRMLS_DC) /* {{{ */
int php_v8js_v8_get_properties_hash(v8::Handle<v8::Value> jsValue, HashTable *retval, int flags, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
{
v8::Local<v8::Object> jsObj = jsValue->ToObject();
@ -232,7 +203,7 @@ int php_v8js_v8_get_properties_hash(v8::Handle<v8::Value> jsValue, HashTable *re
MAKE_STD_ZVAL(value);
if (v8js_to_zval(jsVal, value, flags TSRMLS_CC) == FAILURE) {
if (v8js_to_zval(jsVal, value, flags, isolate TSRMLS_CC) == FAILURE) {
zval_ptr_dtor(&value);
return FAILURE;
}
@ -253,15 +224,17 @@ static HashTable *php_v8js_v8_get_properties(zval *object TSRMLS_DC) /* {{{ */
{
php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC);
HashTable *retval;
ALLOC_HASHTABLE(retval);
zend_hash_init(retval, 0, NULL, ZVAL_PTR_DTOR, 0);
v8::HandleScope local_scope;
v8::Locker locker(obj->isolate);
v8::Isolate::Scope isolate_scope(obj->isolate);
v8::HandleScope local_scope(obj->isolate);
v8::Handle<v8::Context> temp_context = v8::Context::New();
v8::Context::Scope temp_scope(temp_context);
if (php_v8js_v8_get_properties_hash(obj->v8obj, retval, obj->flags TSRMLS_CC) == SUCCESS) {
if (php_v8js_v8_get_properties_hash(obj->v8obj, retval, obj->flags, obj->isolate TSRMLS_CC) == SUCCESS) {
return retval;
}
return NULL;
@ -332,7 +305,7 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) /
v8::Local<v8::Value> js_retval;
for (i = 0; i < argc; i++) {
jsArgv[i] = v8::Local<v8::Value>::New(zval_to_v8js(*argv[i] TSRMLS_CC));
jsArgv[i] = v8::Local<v8::Value>::New(zval_to_v8js(*argv[i], obj->isolate TSRMLS_CC));
}
js_retval = cb->Call(V8JS_GLOBAL, argc, jsArgv);
@ -344,7 +317,7 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) /
}
if (return_value_used) {
return v8js_to_zval(js_retval, return_value, obj->flags TSRMLS_CC);
return v8js_to_zval(js_retval, return_value, obj->flags, obj->isolate TSRMLS_CC);
}
return SUCCESS;
@ -407,7 +380,7 @@ static zend_object_value php_v8js_v8_new(zend_class_entry *ce TSRMLS_DC) /* {{{
}
/* }}} */
void php_v8js_create_v8(zval *res, v8::Handle<v8::Value> value, int flags TSRMLS_DC) /* {{{ */
void php_v8js_create_v8(zval *res, v8::Handle<v8::Value> value, int flags, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
{
php_v8js_object *c;
@ -417,6 +390,7 @@ void php_v8js_create_v8(zval *res, v8::Handle<v8::Value> value, int flags TSRMLS
c->v8obj = v8::Persistent<v8::Value>::New(value);
c->flags = flags;
c->isolate = isolate;
}
/* }}} */
@ -544,9 +518,6 @@ static void php_v8js_init(TSRMLS_D) /* {{{ */
/* Initialize V8 */
v8::V8::Initialize();
/* Redirect fatal errors to PHP error handler */
v8::V8::SetFatalErrorHandler(php_v8js_fatal_error_handler);
/* Run only once */
V8JSG(v8_initialized) = 1;
}
@ -579,6 +550,10 @@ static PHP_METHOD(V8Js, __construct)
c->report_uncaught = report_uncaught;
c->pending_exception = NULL;
c->in_execution = 0;
c->isolate = v8::Isolate::New();
c->time_limit_hit = false;
c->memory_limit_hit = false;
c->module_loader = NULL;
/* Initialize V8 */
php_v8js_init(TSRMLS_C);
@ -597,20 +572,29 @@ static PHP_METHOD(V8Js, __construct)
/* Declare configuration for extensions */
v8::ExtensionConfiguration extension_conf(exts_count, exts);
// Isolate execution
v8::Locker locker(c->isolate);
v8::Isolate::Scope isolate_scope(c->isolate);
/* Handle scope */
v8::HandleScope handle_scope;
v8::HandleScope handle_scope(c->isolate);
/* Redirect fatal errors to PHP error handler */
// This needs to be done within the context isolate
v8::V8::SetFatalErrorHandler(php_v8js_fatal_error_handler);
/* Create global template for global object */
if (V8JSG(global_template).IsEmpty()) {
V8JSG(global_template) = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
V8JSG(global_template)->SetClassName(V8JS_SYM("V8Js"));
// Now we are using multiple isolates this needs to be created for every context
/* Register builtin methods */
php_v8js_register_methods(V8JSG(global_template)->InstanceTemplate());
}
c->global_template = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
c->global_template->SetClassName(V8JS_SYM("V8Js"));
/* Register builtin methods */
php_v8js_register_methods(c->global_template->InstanceTemplate(), c);
/* Create context */
c->context = v8::Context::New(&extension_conf, V8JSG(global_template)->InstanceTemplate());
c->context = v8::Context::New(&extension_conf, c->global_template->InstanceTemplate());
c->context->SetAlignedPointerInEmbedderData(1, c);
if (exts) {
_php_v8js_free_ext_strarr(exts, exts_count);
@ -640,10 +624,10 @@ static PHP_METHOD(V8Js, __construct)
if (free) {
efree(class_name);
}
/* Register Get accessor for passed variables */
if (vars_arr && zend_hash_num_elements(Z_ARRVAL_P(vars_arr)) > 0) {
php_v8js_register_accessors(php_obj_t->InstanceTemplate(), vars_arr TSRMLS_CC);
php_v8js_register_accessors(php_obj_t->InstanceTemplate(), vars_arr, c->isolate TSRMLS_CC);
}
/* Set name for the PHP JS object */
@ -663,26 +647,118 @@ static PHP_METHOD(V8Js, __construct)
} \
\
(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::Context::Scope context_scope((ctx)->context);
static void php_v8js_timer_push(long time_limit, long memory_limit, php_v8js_ctx *c)
{
V8JSG(timer_mutex).lock();
// Create context for this timer
php_v8js_timer_ctx *timer_ctx = (php_v8js_timer_ctx *)emalloc(sizeof(php_v8js_timer_ctx));
// Calculate the time point when the time limit is exceeded
std::chrono::milliseconds duration(time_limit);
std::chrono::time_point<std::chrono::high_resolution_clock> from = std::chrono::high_resolution_clock::now();
// Push the timer context
timer_ctx->time_limit = time_limit;
timer_ctx->memory_limit = memory_limit;
timer_ctx->time_point = from + duration;
timer_ctx->v8js_ctx = c;
V8JSG(timer_stack).push(timer_ctx);
V8JSG(timer_mutex).unlock();
}
static void php_v8js_timer_pop()
{
V8JSG(timer_mutex).lock();
if (V8JSG(timer_stack).size()) {
// Free the timer context memory
php_v8js_timer_ctx *timer_ctx = V8JSG(timer_stack).top();
efree(timer_ctx);
// Remove the timer context from the stack
V8JSG(timer_stack).pop();
}
V8JSG(timer_mutex).unlock();
}
static void php_v8js_terminate_execution(php_v8js_ctx *c)
{
// Forcefully terminate the current thread of V8 execution in the isolate
v8::V8::TerminateExecution(c->isolate);
// Remove this timer from the stack
php_v8js_timer_pop();
}
static void php_v8js_timer_thread()
{
while (!V8JSG(timer_stop)) {
v8::Locker locker;
std::chrono::time_point<std::chrono::high_resolution_clock> now = std::chrono::high_resolution_clock::now();
v8::HeapStatistics hs;
if (V8JSG(timer_stack).size()) {
// Get the current timer context
php_v8js_timer_ctx *timer_ctx = V8JSG(timer_stack).top();
php_v8js_ctx *c = timer_ctx->v8js_ctx;
// Get memory usage statistics for the isolate
c->isolate->GetHeapStatistics(&hs);
if (timer_ctx->time_limit > 0 && now > timer_ctx->time_point) {
php_v8js_terminate_execution(c);
V8JSG(timer_mutex).lock();
c->time_limit_hit = true;
V8JSG(timer_mutex).unlock();
}
if (timer_ctx->memory_limit > 0 && hs.used_heap_size() > timer_ctx->memory_limit) {
php_v8js_terminate_execution(c);
V8JSG(timer_mutex).lock();
c->memory_limit_hit = true;
V8JSG(timer_mutex).unlock();
}
}
// Sleep for 10ms
std::chrono::milliseconds duration(10);
std::this_thread::sleep_for(duration);
}
}
/* {{{ proto mixed V8Js::executeString(string script [, string identifier [, int flags]])
*/
static PHP_METHOD(V8Js, executeString)
{
char *str = NULL, *identifier = NULL;
int str_len = 0, identifier_len = 0;
long flags = V8JS_FLAG_NONE;
long flags = V8JS_FLAG_NONE, time_limit = 0, memory_limit = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sl", &str, &str_len, &identifier, &identifier_len, &flags) == FAILURE) {
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())
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;
v8::HandleScope handle_scope;
v8::HandleScope handle_scope(c->isolate);
/* Set script identifier */
v8::Local<v8::String> sname = identifier_len ? V8JS_SYML(identifier, identifier_len) : V8JS_SYM("V8Js::executeString()");
@ -693,21 +769,50 @@ static PHP_METHOD(V8Js, executeString)
/* Compile errors? */
if (script.IsEmpty()) {
php_v8js_throw_exception(&try_catch TSRMLS_CC);
php_v8js_throw_script_exception(&try_catch TSRMLS_CC);
return;
}
/* Set flags for runtime use */
V8JS_GLOBAL_SET_FLAGS(flags);
if (time_limit > 0 || memory_limit > 0) {
// If timer thread is not running then start it
if (!V8JSG(timer_thread)) {
// If not, start timer thread
V8JSG(timer_thread) = new std::thread(php_v8js_timer_thread);
}
php_v8js_timer_push(time_limit, memory_limit, c);
}
/* Execute script */
c->in_execution++;
v8::Local<v8::Value> result = script->Run();
c->in_execution--;
/* Script possibly terminated, return immediately */
if (time_limit > 0) {
php_v8js_timer_pop();
}
char exception_string[64];
if (c->time_limit_hit) {
// Execution has been terminated due to time limit
sprintf(exception_string, "Script time limit of %lu milliseconds exceeded", time_limit);
zend_throw_exception(php_ce_v8js_time_limit_exception, exception_string, 0 TSRMLS_CC);
return;
}
if (c->memory_limit_hit) {
// Execution has been terminated due to memory limit
sprintf(exception_string, "Script memory limit of %lu bytes exceeded", memory_limit);
zend_throw_exception(php_ce_v8js_memory_limit_exception, exception_string, 0 TSRMLS_CC);
return;
}
if (!try_catch.CanContinue()) {
/* TODO: throw PHP exception here? */
// At this point we can't re-throw the exception
return;
}
@ -725,14 +830,14 @@ static PHP_METHOD(V8Js, executeString)
/* Report immediately if report_uncaught is true */
if (c->report_uncaught) {
php_v8js_throw_exception(&try_catch TSRMLS_CC);
php_v8js_throw_script_exception(&try_catch TSRMLS_CC);
return;
}
/* Exception thrown from JS, preserve it for future execution */
if (result.IsEmpty()) {
MAKE_STD_ZVAL(c->pending_exception);
php_v8js_create_exception(c->pending_exception, &try_catch TSRMLS_CC);
php_v8js_create_script_exception(c->pending_exception, &try_catch TSRMLS_CC);
}
}
@ -743,7 +848,7 @@ static PHP_METHOD(V8Js, executeString)
/* Convert V8 value to PHP value */
if (!result.IsEmpty()) {
v8js_to_zval(result, return_value, flags TSRMLS_CC);
v8js_to_zval(result, return_value, flags, c->isolate TSRMLS_CC);
}
}
/* }}} */
@ -766,6 +871,24 @@ static PHP_METHOD(V8Js, getPendingException)
}
/* }}} */
/* {{{ proto void V8Js::setModuleLoader(string module)
*/
static PHP_METHOD(V8Js, setModuleLoader)
{
php_v8js_ctx *c;
zval *callable;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &callable) == FAILURE) {
return;
}
c = (php_v8js_ctx *) zend_object_store_get_object(getThis() TSRMLS_CC);
c->module_loader = callable;
Z_ADDREF_P(c->module_loader);
}
/* }}} */
static void php_v8js_persistent_zval_ctor(zval **p) /* {{{ */
{
zval *orig_ptr = *p;
@ -920,11 +1043,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_executestring, 0, 0, 1)
ZEND_ARG_INFO(0, script)
ZEND_ARG_INFO(0, identifier)
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(arginfo_v8js_getpendingexception, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_setmoduleloader, 0, 0, 1)
ZEND_ARG_INFO(0, callable)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_registerextension, 0, 0, 2)
ZEND_ARG_INFO(0, extension_name)
ZEND_ARG_INFO(0, script)
@ -935,7 +1064,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_v8js_getextensions, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_v8jsexception_no_args, 0)
ZEND_BEGIN_ARG_INFO(arginfo_v8jsscriptexception_no_args, 0)
ZEND_END_ARG_INFO()
/* }}} */
@ -943,6 +1072,7 @@ 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, 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)
PHP_ME(V8Js, getExtensions, arginfo_v8js_getextensions, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
{NULL, NULL, NULL}
@ -955,13 +1085,13 @@ static void php_v8js_write_property(zval *object, zval *member, zval *value ZEND
{
V8JS_BEGIN_CTX(c, object)
v8::HandleScope handle_scope;
v8::HandleScope handle_scope(c->isolate);
/* Global PHP JS object */
v8::Local<v8::Object> jsobj = V8JS_GLOBAL->Get(c->object_name)->ToObject();
/* Write value to PHP JS object */
jsobj->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value TSRMLS_CC), v8::ReadOnly);
jsobj->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, c->isolate TSRMLS_CC), v8::ReadOnly);
/* Write value to PHP object */
std_object_handlers.write_property(object, member, value ZEND_HASH_KEY_CC TSRMLS_CC);
@ -972,7 +1102,7 @@ static void php_v8js_unset_property(zval *object, zval *member ZEND_HASH_KEY_DC
{
V8JS_BEGIN_CTX(c, object)
v8::HandleScope handle_scope;
v8::HandleScope handle_scope(c->isolate);
/* Global PHP JS object */
v8::Local<v8::Object> jsobj = V8JS_GLOBAL->Get(c->object_name)->ToObject();
@ -987,9 +1117,9 @@ static void php_v8js_unset_property(zval *object, zval *member ZEND_HASH_KEY_DC
/* }}} V8Js */
/* {{{ Class: V8JsException */
/* {{{ Class: V8JsScriptException */
static void php_v8js_create_exception(zval *return_value, v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
static void php_v8js_create_script_exception(zval *return_value, v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
{
v8::String::Utf8Value exception(try_catch->Exception());
const char *exception_string = ToCString(exception);
@ -998,10 +1128,10 @@ static void php_v8js_create_exception(zval *return_value, v8::TryCatch *try_catc
char *message_string;
int linenum, message_len;
object_init_ex(return_value, php_ce_v8js_exception);
object_init_ex(return_value, php_ce_v8js_script_exception);
#define PHPV8_EXPROP(type, name, value) \
zend_update_property##type(php_ce_v8js_exception, return_value, #name, sizeof(#name) - 1, value TSRMLS_CC);
zend_update_property##type(php_ce_v8js_script_exception, return_value, #name, sizeof(#name) - 1, value TSRMLS_CC);
if (tc_message.IsEmpty()) {
message_len = spprintf(&message_string, 0, "%s", exception_string);
@ -1034,66 +1164,86 @@ static void php_v8js_create_exception(zval *return_value, v8::TryCatch *try_catc
}
/* }}} */
static void php_v8js_throw_exception(v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
static void php_v8js_throw_script_exception(v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
{
v8::String::Utf8Value exception(try_catch->Exception());
const char *exception_string = ToCString(exception);
zval *zexception = NULL;
if (try_catch->Message().IsEmpty()) {
zend_throw_exception(php_ce_v8js_exception, (char *) exception_string, 0 TSRMLS_CC);
zend_throw_exception(php_ce_v8js_script_exception, (char *) exception_string, 0 TSRMLS_CC);
} else {
MAKE_STD_ZVAL(zexception);
php_v8js_create_exception(zexception, try_catch TSRMLS_CC);
php_v8js_create_script_exception(zexception, try_catch TSRMLS_CC);
zend_throw_exception_object(zexception TSRMLS_CC);
}
}
/* }}} */
#define V8JS_EXCEPTION_METHOD(property) \
static PHP_METHOD(V8JsException, get##property) \
static PHP_METHOD(V8JsScriptException, get##property) \
{ \
zval *value; \
\
if (zend_parse_parameters_none() == FAILURE) { \
return; \
} \
value = zend_read_property(php_ce_v8js_exception, getThis(), #property, sizeof(#property) - 1, 0 TSRMLS_CC); \
value = zend_read_property(php_ce_v8js_script_exception, getThis(), #property, sizeof(#property) - 1, 0 TSRMLS_CC); \
*return_value = *value; \
zval_copy_ctor(return_value); \
INIT_PZVAL(return_value); \
}
/* {{{ proto string V8JsException::getJsFileName()
/* {{{ proto string V8JsEScriptxception::getJsFileName()
*/
V8JS_EXCEPTION_METHOD(JsFileName);
/* }}} */
/* {{{ proto string V8JsException::getJsLineNumber()
/* {{{ proto string V8JsScriptException::getJsLineNumber()
*/
V8JS_EXCEPTION_METHOD(JsLineNumber);
/* }}} */
/* {{{ proto string V8JsException::getJsSourceLine()
/* {{{ proto string V8JsScriptException::getJsSourceLine()
*/
V8JS_EXCEPTION_METHOD(JsSourceLine);
/* }}} */
/* {{{ proto string V8JsException::getJsTrace()
/* {{{ proto string V8JsScriptException::getJsTrace()
*/
V8JS_EXCEPTION_METHOD(JsTrace);
/* }}} */
static const zend_function_entry v8js_exception_methods[] = { /* {{{ */
PHP_ME(V8JsException, getJsFileName, arginfo_v8jsexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(V8JsException, getJsLineNumber, arginfo_v8jsexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(V8JsException, getJsSourceLine, arginfo_v8jsexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(V8JsException, getJsTrace, arginfo_v8jsexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
static const zend_function_entry v8js_script_exception_methods[] = { /* {{{ */
PHP_ME(V8JsScriptException, getJsFileName, arginfo_v8jsscriptexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(V8JsScriptException, getJsLineNumber, arginfo_v8jsscriptexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(V8JsScriptException, getJsSourceLine, arginfo_v8jsscriptexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(V8JsScriptException, getJsTrace, arginfo_v8jsscriptexception_no_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
{NULL, NULL, NULL}
};
/* }}} */
/* }}} V8JsException */
/* }}} V8JsScriptException */
/* {{{ Class: V8JsTimeLimitException */
static const zend_function_entry v8js_time_limit_exception_methods[] = { /* {{{ */
{NULL, NULL, NULL}
};
/* }}} */
/* }}} V8JsTimeLimitException */
/* {{{ Class: V8JsMemoryLimitException */
static const zend_function_entry v8js_memory_limit_exception_methods[] = { /* {{{ */
{NULL, NULL, NULL}
};
/* }}} */
/* }}} V8JsMemoryLimitException */
/* {{{ PHP_MINIT_FUNCTION
*/
@ -1145,16 +1295,26 @@ static PHP_MINIT_FUNCTION(v8js)
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("FLAG_NONE"), V8JS_FLAG_NONE TSRMLS_CC);
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("FLAG_FORCE_ARRAY"), V8JS_FLAG_FORCE_ARRAY TSRMLS_CC);
/* V8JsException Class */
INIT_CLASS_ENTRY(ce, "V8JsException", v8js_exception_methods);
php_ce_v8js_exception = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
php_ce_v8js_exception->ce_flags |= ZEND_ACC_FINAL;
/* V8JsScriptException Class */
INIT_CLASS_ENTRY(ce, "V8JsScriptException", v8js_script_exception_methods);
php_ce_v8js_script_exception = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
php_ce_v8js_script_exception->ce_flags |= ZEND_ACC_FINAL;
/* Add custom JS specific properties */
zend_declare_property_null(php_ce_v8js_exception, ZEND_STRL("JsFileName"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_ce_v8js_exception, ZEND_STRL("JsLineNumber"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_ce_v8js_exception, ZEND_STRL("JsSourceLine"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_ce_v8js_exception, ZEND_STRL("JsTrace"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_ce_v8js_script_exception, ZEND_STRL("JsFileName"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_ce_v8js_script_exception, ZEND_STRL("JsLineNumber"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_ce_v8js_script_exception, ZEND_STRL("JsSourceLine"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_ce_v8js_script_exception, ZEND_STRL("JsTrace"), ZEND_ACC_PROTECTED TSRMLS_CC);
/* V8JsTimeLimitException Class */
INIT_CLASS_ENTRY(ce, "V8JsTimeLimitException", v8js_time_limit_exception_methods);
php_ce_v8js_time_limit_exception = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
php_ce_v8js_time_limit_exception->ce_flags |= ZEND_ACC_FINAL;
/* V8JsMemoryLimitException Class */
INIT_CLASS_ENTRY(ce, "V8JsMemoryLimitException", v8js_memory_limit_exception_methods);
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;
REGISTER_INI_ENTRIES();
@ -1190,8 +1350,6 @@ static PHP_MSHUTDOWN_FUNCTION(v8js)
{
UNREGISTER_INI_ENTRIES();
v8::V8::Dispose();
if (V8JSG(extensions)) {
zend_hash_destroy(V8JSG(extensions));
free(V8JSG(extensions));
@ -1211,6 +1369,12 @@ static PHP_MSHUTDOWN_FUNCTION(v8js)
*/
static PHP_RSHUTDOWN_FUNCTION(v8js)
{
// If the timer thread is running then stop it
if (V8JSG(timer_thread)) {
V8JSG(timer_stop) = true;
V8JSG(timer_thread)->join();
}
#if V8JS_DEBUG
v8::HeapStatistics stats;
v8::V8::GetHeapStatistics(&stats);
@ -1254,6 +1418,8 @@ static PHP_GINIT_FUNCTION(v8js)
v8js_globals->disposed_contexts = 0;
v8js_globals->v8_initialized = 0;
v8js_globals->v8_flags = NULL;
v8js_globals->timer_thread = NULL;
v8js_globals->timer_stop = false;
}
/* }}} */

111
v8js_commonjs.cc Normal file
View File

@ -0,0 +1,111 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2012 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Simon Best <simonjbest@gmail.com> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
extern "C" {
#include "php.h"
#include "zend_exceptions.h"
}
#include "php_v8js_macros.h"
void php_v8js_commonjs_split_terms(char *identifier, std::vector<char *> &terms)
{
char *term = (char *)malloc(PATH_MAX), *ptr = term;
// Initialise the term string
*term = 0;
while (*identifier > 0) {
if (*identifier == '/') {
if (strlen(term) > 0) {
// Terminate term string and add to terms vector
*ptr++ = 0;
terms.push_back(strdup(term));
// Reset term string
memset(term, 0, strlen(term));
ptr = term;
}
} else {
*ptr++ = *identifier;
}
identifier++;
}
if (strlen(term) > 0) {
// Terminate term string and add to terms vector
*ptr++ = 0;
terms.push_back(strdup(term));
}
if (term > 0) {
free(term);
}
}
void php_v8js_commonjs_normalise_identifier(char *base, char *identifier, char *normalised_path, char *module_name)
{
std::vector<char *> id_terms, terms;
php_v8js_commonjs_split_terms(identifier, id_terms);
// If we have a relative module identifier then include the base terms
if (!strcmp(id_terms.front(), ".") || !strcmp(id_terms.front(), "..")) {
php_v8js_commonjs_split_terms(base, terms);
}
terms.insert(terms.end(), id_terms.begin(), id_terms.end());
std::vector<char *> normalised_terms;
for (std::vector<char *>::iterator it = terms.begin(); it != terms.end(); it++) {
char *term = *it;
if (!strcmp(term, "..")) {
// Ignore parent term (..) if it's the first normalised term
if (normalised_terms.size() > 0) {
// Remove the parent normalized term
normalised_terms.pop_back();
}
} else if (strcmp(term, ".")) {
// Add the term if it's not the current term (.)
normalised_terms.push_back(term);
}
}
// Initialise the normalised path string
*normalised_path = 0;
*module_name = 0;
strcat(module_name, normalised_terms.back());
normalised_terms.pop_back();
for (std::vector<char *>::iterator it = normalised_terms.begin(); it != normalised_terms.end(); it++) {
char *term = *it;
if (strlen(normalised_path) > 0) {
strcat(normalised_path, "/");
}
strcat(normalised_path, term);
}
}

View File

@ -38,6 +38,7 @@ static v8::Handle<v8::Value> php_v8js_php_callback(const v8::Arguments &args) /*
{
v8::Handle<v8::Value> return_value;
zval *value = reinterpret_cast<zval *>(args.This()->GetAlignedPointerFromInternalField(0));
v8::Isolate *isolate = reinterpret_cast<v8::Isolate *>(args.This()->GetAlignedPointerFromInternalField(1));
zend_function *method_ptr;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
@ -99,7 +100,7 @@ static v8::Handle<v8::Value> php_v8js_php_callback(const v8::Arguments &args) /*
argv = (zval **) safe_emalloc(argc, sizeof(zval *), 0);
for (i = 0; i < argc; i++) {
MAKE_STD_ZVAL(argv[i]);
if (v8js_to_zval(args[i], argv[i], flags TSRMLS_CC) == FAILURE) {
if (v8js_to_zval(args[i], argv[i], flags, isolate TSRMLS_CC) == FAILURE) {
fci.param_count++;
error_len = spprintf(&error, 0, "converting parameter #%d passed to %s() failed", i + 1, method_ptr->common.function_name);
return_value = V8JS_THROW(Error, error, error_len);
@ -134,7 +135,7 @@ failure:
}
if (retval_ptr != NULL) {
return_value = zval_to_v8js(retval_ptr TSRMLS_CC);
return_value = zval_to_v8js(retval_ptr, isolate TSRMLS_CC);
zval_ptr_dtor(&retval_ptr);
} else {
return_value = V8JS_NULL;
@ -275,7 +276,7 @@ static v8::Handle<v8::Integer> php_v8js_property_query(v8::Local<v8::String> pro
#define PHP_V8JS_CALLBACK(mptr) \
v8::FunctionTemplate::New(php_v8js_php_callback, v8::External::New(mptr))->GetFunction()
static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value TSRMLS_DC) /* {{{ */
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;
@ -303,7 +304,7 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value TSRMLS_DC) /* {{
/* Object methods */
if (ce) {
new_tpl->SetClassName(V8JS_STRL(ce->name, ce->name_length));
new_tpl->InstanceTemplate()->SetInternalFieldCount(1);
new_tpl->InstanceTemplate()->SetInternalFieldCount(2);
if (ce == zend_ce_closure) {
new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_php_callback);
@ -387,8 +388,9 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value TSRMLS_DC) /* {{
// Increase the reference count of this value because we're storing it internally for use later
// See https://github.com/preillyme/v8js/issues/6
Z_ADDREF_P(value);
newobj->SetAlignedPointerInInternalField(0, (void *) value);
newobj->SetAlignedPointerInInternalField(1, (void *) isolate);
} else {
new_tpl->SetClassName(V8JS_SYM("Array"));
newobj = new_tpl->InstanceTemplate()->NewInstance();
@ -425,9 +427,9 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value TSRMLS_DC) /* {{
}
continue;
}
newobj->Set(V8JS_STRL(key, key_len - 1), zval_to_v8js(*data TSRMLS_CC), v8::ReadOnly);
newobj->Set(V8JS_STRL(key, key_len - 1), zval_to_v8js(*data, isolate TSRMLS_CC), v8::ReadOnly);
} else {
newobj->Set(index, zval_to_v8js(*data TSRMLS_CC));
newobj->Set(index, zval_to_v8js(*data, isolate TSRMLS_CC));
}
if (tmp_ht) {
@ -440,14 +442,14 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value TSRMLS_DC) /* {{
}
/* }}} */
static v8::Handle<v8::Value> php_v8js_hash_to_jsarr(zval *value TSRMLS_DC) /* {{{ */
static v8::Handle<v8::Value> php_v8js_hash_to_jsarr(zval *value, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
{
HashTable *myht = HASH_OF(value);
int i = myht ? zend_hash_num_elements(myht) : 0;
/* Return object if dealing with assoc array */
if (i > 0 && _php_v8js_is_assoc_array(myht TSRMLS_CC)) {
return php_v8js_hash_to_jsobj(value TSRMLS_CC);
return php_v8js_hash_to_jsobj(value, isolate TSRMLS_CC);
}
v8::Local<v8::Array> newarr;
@ -476,7 +478,7 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsarr(zval *value TSRMLS_DC) /* {{
tmp_ht->nApplyCount++;
}
newarr->Set(index++, zval_to_v8js(*data TSRMLS_CC));
newarr->Set(index++, zval_to_v8js(*data, isolate TSRMLS_CC));
if (tmp_ht) {
tmp_ht->nApplyCount--;
@ -487,18 +489,18 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsarr(zval *value TSRMLS_DC) /* {{
}
/* }}} */
v8::Handle<v8::Value> zval_to_v8js(zval *value TSRMLS_DC) /* {{{ */
v8::Handle<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
{
v8::Handle<v8::Value> jsValue;
switch (Z_TYPE_P(value))
{
case IS_ARRAY:
jsValue = php_v8js_hash_to_jsarr(value TSRMLS_CC);
jsValue = php_v8js_hash_to_jsarr(value, isolate TSRMLS_CC);
break;
case IS_OBJECT:
jsValue = php_v8js_hash_to_jsobj(value TSRMLS_CC);
jsValue = php_v8js_hash_to_jsobj(value, isolate TSRMLS_CC);
break;
case IS_STRING:
@ -526,7 +528,7 @@ v8::Handle<v8::Value> zval_to_v8js(zval *value TSRMLS_DC) /* {{{ */
}
/* }}} */
int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags TSRMLS_DC) /* {{{ */
int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
{
if (jsValue->IsString())
{
@ -575,9 +577,9 @@ int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags TS
{
if ((flags & V8JS_FLAG_FORCE_ARRAY) || jsValue->IsArray()) {
array_init(return_value);
return php_v8js_v8_get_properties_hash(jsValue, Z_ARRVAL_P(return_value), flags TSRMLS_CC);
return php_v8js_v8_get_properties_hash(jsValue, Z_ARRVAL_P(return_value), flags, isolate TSRMLS_CC);
} else {
php_v8js_create_v8(return_value, jsValue, flags TSRMLS_CC);
php_v8js_create_v8(return_value, jsValue, flags, isolate TSRMLS_CC);
return SUCCESS;
}
}

View File

@ -23,12 +23,14 @@
#include "config.h"
#endif
#include "php_v8js_macros.h"
extern "C" {
#include "php.h"
#include "zend_exceptions.h"
}
#include "php_v8js_macros.h"
#include <v8.h>
/* Forward declarations */
void php_v8js_commonjs_normalise_identifier(char *base, char *identifier, char *normalised_path, char *module_name);
/* global.exit - terminate execution */
V8JS_METHOD(exit) /* {{{ */
@ -162,17 +164,187 @@ V8JS_METHOD(var_dump) /* {{{ */
for (int i = 0; i < args.Length(); i++) {
_php_v8js_dumper(args[i], 1 TSRMLS_CC);
}
return V8JS_NULL;
}
/* }}} */
void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate> global) /* {{{ */
V8JS_METHOD(require)
{
// Get the extension context
v8::Handle<v8::External> data = v8::Handle<v8::External>::Cast(args.Data());
php_v8js_ctx *c = static_cast<php_v8js_ctx*>(data->Value());
// Check that we have a module loader
if (c->module_loader == NULL) {
return v8::ThrowException(v8::String::New("No module loader"));
}
v8::String::Utf8Value module_id_v8(args[0]);
// Make sure to duplicate the module name string so it doesn't get freed by the V8 garbage collector
char *module_id = estrdup(ToCString(module_id_v8));
char *normalised_path = (char *)emalloc(PATH_MAX);
char *module_name = (char *)emalloc(PATH_MAX);
php_v8js_commonjs_normalise_identifier(c->modules_base.back(), module_id, normalised_path, module_name);
efree(module_id);
char *normalised_module_id = (char *)emalloc(strlen(module_id));
*normalised_module_id = 0;
if (strlen(normalised_path) > 0) {
strcat(normalised_module_id, normalised_path);
strcat(normalised_module_id, "/");
}
strcat(normalised_module_id, module_name);
efree(module_name);
// Check for module cyclic dependencies
for (std::vector<char *>::iterator it = c->modules_stack.begin(); it != c->modules_stack.end(); ++it)
{
if (!strcmp(*it, normalised_module_id)) {
efree(normalised_path);
return v8::ThrowException(v8::String::New("Module cyclic dependency"));
}
}
// If we have already loaded and cached this module then use it
if (V8JSG(modules_loaded).count(normalised_module_id) > 0) {
efree(normalised_path);
return V8JSG(modules_loaded)[normalised_module_id];
}
// Callback to PHP to load the module code
zval module_code;
zval *normalised_path_zend;
MAKE_STD_ZVAL(normalised_path_zend);
ZVAL_STRING(normalised_path_zend, normalised_module_id, 1);
zval* params[] = { normalised_path_zend };
if (FAILURE == call_user_function(EG(function_table), NULL, c->module_loader, &module_code, 1, params TSRMLS_CC)) {
efree(normalised_path);
return v8::ThrowException(v8::String::New("Module loader callback failed"));
}
// Check if an exception was thrown
if (EG(exception)) {
efree(normalised_path);
// Clear the PHP exception and throw it in V8 instead
zend_clear_exception(TSRMLS_CC);
return v8::ThrowException(v8::String::New("Module loader callback exception"));
}
// Convert the return value to string
if (Z_TYPE(module_code) != IS_STRING) {
convert_to_string(&module_code);
}
// Check that some code has been returned
if (!strlen(Z_STRVAL(module_code))) {
efree(normalised_path);
return v8::ThrowException(v8::String::New("Module loader callback did not return code"));
}
// Create a template for the global object and set the built-in global functions
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("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);
// Add the exports object in which the module can return its API
v8::Handle<v8::ObjectTemplate> exports_template = v8::ObjectTemplate::New();
v8::Handle<v8::Object> exports = exports_template->NewInstance();
global->Set(v8::String::New("exports"), exports);
// 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::Object> module = module_template->NewInstance();
module->Set(v8::String::New("id"), v8::String::New(normalised_module_id));
global->Set(v8::String::New("module"), module);
// Each module gets its own context so different modules do not affect each other
v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
// Catch JS exceptions
v8::TryCatch try_catch;
v8::Locker locker(c->isolate);
v8::Isolate::Scope isolate_scope(c->isolate);
v8::HandleScope handle_scope(c->isolate);
// Enter the module context
v8::Context::Scope scope(context);
// Set script identifier
v8::Local<v8::String> sname = V8JS_SYM("require");
v8::Local<v8::String> source = v8::String::New(Z_STRVAL(module_code));
// Create and compile script
v8::Local<v8::Script> script = v8::Script::New(source, sname);
// The script will be empty if there are compile errors
if (script.IsEmpty()) {
efree(normalised_path);
return v8::ThrowException(v8::String::New("Module script compile failed"));
}
// Add this module and path to the stack
c->modules_stack.push_back(normalised_module_id);
c->modules_base.push_back(normalised_path);
// Run script
v8::Local<v8::Value> result = script->Run();
// Remove this module and path from the stack
c->modules_stack.pop_back();
c->modules_base.pop_back();
efree(normalised_path);
// Script possibly terminated, return immediately
if (!try_catch.CanContinue()) {
return v8::ThrowException(v8::String::New("Module script compile failed"));
}
// Handle runtime JS exceptions
if (try_catch.HasCaught()) {
// Rethrow the exception back to JS
return try_catch.ReThrow();
}
// 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
if (module->Has(v8::String::New("exports")) && !module->Get(v8::String::New("exports"))->IsUndefined()) {
// 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());
} else {
// ...otherwise we cache the exports object itself
V8JSG(modules_loaded)[normalised_module_id] = handle_scope.Close(exports);
}
return V8JSG(modules_loaded)[normalised_module_id];
}
void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate> global, php_v8js_ctx *c) /* {{{ */
{
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("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);
global->Set(V8JS_SYM("var_dump"), v8::FunctionTemplate::New(V8JS_MN(var_dump)), v8::ReadOnly);
c->modules_base.push_back("");
global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(V8JS_MN(require), v8::External::New(c)), v8::ReadOnly);
}
/* }}} */

View File

@ -29,26 +29,34 @@ extern "C" {
#include "php_v8js_macros.h"
#include <v8.h>
#include <string>
struct php_v8js_accessor_ctx
{
char *variable_name_string;
uint variable_name_string_len;
v8::Isolate *isolate;
};
static v8::Handle<v8::Value> php_v8js_fetch_php_variable(v8::Local<v8::String> name, const v8::AccessorInfo &info) /* {{{ */
{
v8::String::Utf8Value variable_name(info.Data()->ToString());
const char *variable_name_string = ToCString(variable_name);
uint variable_name_string_len = strlen(variable_name_string);
v8::Handle<v8::External> data = v8::Handle<v8::External>::Cast(info.Data());
php_v8js_accessor_ctx *ctx = static_cast<php_v8js_accessor_ctx *>(data->Value());
zval **variable;
TSRMLS_FETCH();
zend_is_auto_global(variable_name_string, variable_name_string_len TSRMLS_CC);
zend_is_auto_global(ctx->variable_name_string, ctx->variable_name_string_len TSRMLS_CC);
if (zend_hash_find(&EG(symbol_table), variable_name_string, variable_name_string_len + 1, (void **) &variable) == SUCCESS) {
return zval_to_v8js(*variable TSRMLS_CC);
if (zend_hash_find(&EG(symbol_table), ctx->variable_name_string, ctx->variable_name_string_len + 1, (void **) &variable) == SUCCESS) {
return zval_to_v8js(*variable, ctx->isolate TSRMLS_CC);
}
return v8::Undefined();
}
/* }}} */
void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate> php_obj, zval *array TSRMLS_DC) /* {{{ */
void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate> php_obj, zval *array, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
{
char *property_name;
uint property_name_len;
@ -76,8 +84,14 @@ void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate> php_obj, zval *ar
continue; /* Ignore invalid property names */
}
// Create context to store accessor data
php_v8js_accessor_ctx *ctx = (php_v8js_accessor_ctx *)emalloc(sizeof(php_v8js_accessor_ctx));
ctx->variable_name_string = estrdup(Z_STRVAL_PP(item));
ctx->variable_name_string_len = Z_STRLEN_PP(item);
ctx->isolate = isolate;
/* Set the variable fetch callback for given symbol on named property */
php_obj->SetAccessor(V8JS_STRL(property_name, property_name_len - 1), php_v8js_fetch_php_variable, NULL, V8JS_STRL(Z_STRVAL_PP(item), Z_STRLEN_PP(item)), v8::PROHIBITS_OVERWRITING, v8::ReadOnly);
php_obj->SetAccessor(V8JS_STRL(property_name, property_name_len - 1), php_v8js_fetch_php_variable, NULL, v8::External::New(ctx), v8::PROHIBITS_OVERWRITING, v8::ReadOnly);
}
}
/* }}} */