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:
commit
6f44220328
77
README.md
77
README.md
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
} catch (V8JsScriptException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
} catch (V8JsScriptException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
4
test.php
4
test.php
@ -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());
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ $v8 = new V8Js();
|
||||
|
||||
try {
|
||||
var_dump($v8->executeString($JS, 'basic.js'));
|
||||
} catch (V8JsException $e) {
|
||||
} catch (V8JsScriptException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
|
@ -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";
|
||||
}
|
||||
?>
|
||||
|
@ -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";
|
||||
}
|
||||
?>
|
||||
|
33
tests/commonjs_modules.phpt
Normal file
33
tests/commonjs_modules.phpt
Normal 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===
|
@ -14,7 +14,7 @@ $v8->__construct();
|
||||
|
||||
try {
|
||||
$v8->executeString($JS, 'basic.js');
|
||||
} catch (V8JsException $e) {
|
||||
} catch (V8JsScriptException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
|
@ -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);
|
||||
}
|
||||
?>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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]=>
|
||||
|
@ -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);
|
||||
}
|
||||
?>
|
||||
|
@ -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]=>
|
||||
|
@ -26,7 +26,7 @@ class Foo {
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
} catch (V8JsScriptException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n";
|
||||
}
|
||||
?>
|
||||
|
@ -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);
|
||||
}
|
||||
?>
|
||||
|
@ -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
72
tests/memory_limit.phpt
Normal 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===
|
@ -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
69
tests/time_limit.phpt
Normal 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
370
v8js.cc
@ -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
111
v8js_commonjs.cc
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
182
v8js_methods.cc
182
v8js_methods.cc
@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
Loading…
x
Reference in New Issue
Block a user