0
0
mirror of https://github.com/phpv8/v8js.git synced 2024-11-08 13:48:40 +00:00

Merge pull request #156 from stesie/php-exception-behaviour

PHP->JS exception propagation
This commit is contained in:
Stefan Siegl 2015-09-23 19:40:23 +02:00
commit 83f51e5021
21 changed files with 478 additions and 49 deletions

View File

@ -52,6 +52,7 @@ class V8Js
const FLAG_NONE = 1;
const FLAG_FORCE_ARRAY = 2;
const FLAG_PROPAGATE_PHP_EXCEPTIONS = 4;
const DEBUG_AUTO_BREAK_NEVER = 1;
const DEBUG_AUTO_BREAK_ONCE = 2;
@ -301,3 +302,28 @@ PHP Objects implementing ArrayAccess, Countable
The above rule that PHP objects are generally converted to JavaScript objects also applies to PHP objects of `ArrayObject` type or other classes, that implement both the `ArrayAccess` and the `Countable` interface -- even so they behave like PHP arrays.
This behaviour can be changed by enabling the php.ini flag `v8js.use_array_access`. If set, objects of PHP classes that implement the aforementioned interfaces are converted to JavaScript Array-like objects. This is by-index access of this object results in immediate calls to the `offsetGet` or `offsetSet` PHP methods (effectively this is live-binding of JavaScript against the PHP object). Such an Array-esque object also supports calling every attached public method of the PHP object + methods of JavaScript's native Array.prototype methods (as long as they are not overloaded by PHP methods).
Exceptions
==========
If the JavaScript code throws (without catching), causes errors or doesn't
compile, `V8JsScriptException` exceptions are thrown unless the `V8Js` object
is constructed with `report_uncaught_exceptions` set `FALSE`.
PHP exceptions that occur due to calls from JavaScript code by default are
*not* re-thrown into JavaScript context but cause the JavaScript execution to
be stopped immediately and then are reported at the location calling the JS code.
This behaviour can be changed by setting the `FLAG_PROPAGATE_PHP_EXCEPTIONS`
flag. If it is set, PHP exception (objects) are converted to JavaScript
objects obeying the above rules and re-thrown in JavaScript context. If they
are not caught by JavaScript code the execution stops and a
`V8JsScriptException` is thrown, which has the original PHP exception accessible
via `getPrevious` method.
V8Js versions 0.2.4 and before did not stop JS code execution on PHP exceptions,
but silently ignored them (even so succeeding PHP calls from within the same piece
of JS code were not executed by the PHP engine). This behaviour is considered as
a bug and hence was fixed with 0.2.5 release. Nevertheless there is a
compatibility php.ini switch (`v8js.compat_php_exceptions`) which turns previous
behaviour back on.

View File

@ -80,13 +80,10 @@ extern "C" {
# define V8JS_CONST (char *)
#endif
/* Global flags */
#define V8JS_GLOBAL_SET_FLAGS(isolate,flags) V8JS_GLOBAL(isolate)->SetHiddenValue(V8JS_SYM("__php_flags__"), V8JS_INT(flags))
#define V8JS_GLOBAL_GET_FLAGS(isolate) V8JS_GLOBAL(isolate)->GetHiddenValue(V8JS_SYM("__php_flags__"))->IntegerValue();
/* Options */
#define V8JS_FLAG_NONE (1<<0)
#define V8JS_FLAG_FORCE_ARRAY (1<<1)
#define V8JS_FLAG_PROPAGATE_PHP_EXCEPTIONS (1<<2)
#define V8JS_DEBUG_AUTO_BREAK_NEVER 0
#define V8JS_DEBUG_AUTO_BREAK_ONCE 1
@ -124,6 +121,7 @@ ZEND_BEGIN_MODULE_GLOBALS(v8js)
char *v8_flags; /* V8 command line flags */
bool use_date; /* Generate JS Date objects instead of PHP DateTime */
bool use_array_access; /* Convert ArrayAccess, Countable objects to array-like objects */
bool compat_php_exceptions; /* Don't stop JS execution on PHP exception */
// Timer thread globals
std::deque<v8js_timer_ctx *> timer_stack;

View File

@ -14,6 +14,9 @@ class Foo {
$this->v8->foo = $this;
$this->v8->executeString('fooobar', 'throw_0');
var_dump($this->v8->getPendingException());
// the exception is not cleared before the next executeString call,
// hence the next *exiting* executeString will throw.
// In this case this is the executeString call in bar() function.
$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " caught!\n"); }', 'trycatch1');
$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " caught!\n"); }', 'trycatch2');
}
@ -21,6 +24,8 @@ class Foo {
public function bar()
{
echo "To Bar!\n";
// This executeString call throws a PHP exception, not propagated
// to JS, hence immediately triggering the top-level catch handler.
$this->v8->executeString('throw new Error();', 'throw_1');
}
}
@ -71,7 +76,7 @@ object(V8JsScriptException)#%d (13) {
["file"]=>
string(%d) "%s"
["line"]=>
int(24)
int(29)
["function"]=>
string(11) "__construct"
["class"]=>
@ -100,6 +105,5 @@ object(V8JsScriptException)#%d (13) {
at throw_0:1:1"
}
To Bar!
Error caught!
PHP Exception: throw_0:1: ReferenceError: fooobar is not defined
===EOF===

33
tests/issue_156_001.phpt Normal file
View File

@ -0,0 +1,33 @@
--TEST--
Test V8::executeString() : Backwards compatibility for issue #156
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--INI--
v8js.compat_php_exceptions = 1
--FILE--
<?php
$v8 = new V8Js();
$v8->throwPHPException = function () {
echo "throwing PHP exception now ...\n";
throw new \Exception('foo');
};
$JS = <<< EOT
PHP.throwPHPException();
print("... old behaviour was to not stop JS execution on PHP exceptions\\n");
EOT;
try {
$v8->executeString($JS, 'issue_156_001.js');
} catch(Exception $e) {
var_dump($e->getMessage());
}
?>
===EOF===
--EXPECT--
throwing PHP exception now ...
... old behaviour was to not stop JS execution on PHP exceptions
string(3) "foo"
===EOF===

View File

@ -0,0 +1,50 @@
--TEST--
Test V8::executeString() : PHP Exception handling (repeated)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo {
function throwException() {
throw new \Exception("Test-Exception");
}
}
$v8 = new V8Js();
$v8->foo = new \Foo();
$JS = <<< EOT
try {
PHP.foo.throwException();
// the exception should abort further execution,
// hence the print must not pop up
print("after throwException\\n");
} catch(e) {
// JS should not catch in default mode
print("JS caught exception");
}
EOT;
for($i = 0; $i < 5; $i ++) {
var_dump($i);
try {
$v8->executeString($JS);
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
?>
===EOF===
--EXPECTF--
int(0)
string(14) "Test-Exception"
int(1)
string(14) "Test-Exception"
int(2)
string(14) "Test-Exception"
int(3)
string(14) "Test-Exception"
int(4)
string(14) "Test-Exception"
===EOF===

View File

@ -0,0 +1,67 @@
--TEST--
Test V8::executeString() : PHP Exception handling (multi-level)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo {
function throwException() {
throw new \Exception("Test-Exception");
}
function recurse($i) {
echo "recurse[$i] ...\n";
global $work;
$work($i);
}
}
$v8 = new V8Js();
$v8->foo = new \Foo();
$work = $v8->executeString(<<<EOT
var work = function(level) {
if(level--) {
PHP.foo.recurse(level);
}
else {
PHP.foo.throwException();
}
};
work;
EOT
);
for($i = 0; $i < 5; $i ++) {
var_dump($i);
try {
$work($i);
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
?>
===EOF===
--EXPECT--
int(0)
string(14) "Test-Exception"
int(1)
recurse[0] ...
string(14) "Test-Exception"
int(2)
recurse[1] ...
recurse[0] ...
string(14) "Test-Exception"
int(3)
recurse[2] ...
recurse[1] ...
recurse[0] ...
string(14) "Test-Exception"
int(4)
recurse[3] ...
recurse[2] ...
recurse[1] ...
recurse[0] ...
string(14) "Test-Exception"
===EOF===

View File

@ -0,0 +1,36 @@
--TEST--
Test V8::executeString() : PHP Exception handling (basic JS propagation)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo {
function throwException() {
throw new \Exception("Test-Exception");
}
}
$v8 = new V8Js();
$v8->foo = new \Foo();
$JS = <<< EOT
try {
PHP.foo.throwException();
// the exception should abort further execution,
// hence the print must not pop up
print("after throwException\\n");
} catch(e) {
print("JS caught exception!\\n");
var_dump(e.getMessage());
}
EOT;
$v8->executeString($JS, 'php_exceptions_003', V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
?>
===EOF===
--EXPECTF--
JS caught exception!
string(14) "Test-Exception"
===EOF===

View File

@ -0,0 +1,36 @@
--TEST--
Test V8::executeString() : PHP Exception handling (PHP->JS->PHP back propagation)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo {
function throwException() {
throw new \Exception("Test-Exception");
}
}
$v8 = new V8Js();
$v8->foo = new \Foo();
$JS = <<< EOT
PHP.foo.throwException();
// the exception should abort further execution,
// hence the print must not pop up
print("after throwException\\n");
EOT;
try {
$v8->executeString($JS, 'php_exceptions_004', V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
}
catch(V8JsScriptException $e) {
echo "Got V8JsScriptException\n";
var_dump($e->getPrevious()->getMessage());
}
?>
===EOF===
--EXPECTF--
Got V8JsScriptException
string(14) "Test-Exception"
===EOF===

View File

@ -0,0 +1,43 @@
--TEST--
Test V8::executeString() : PHP Exception handling (JS throw PHP-exception)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo {
function getException() {
return new \Exception("Test-Exception");
}
}
$v8 = new V8Js();
$v8->foo = new \Foo();
$JS = <<< EOT
var ex = PHP.foo.getException();
print("after getException\\n");
throw ex;
print("after throw\\n");
EOT;
try {
$v8->executeString($JS, 'php_exceptions_005');
}
catch(V8JsScriptException $e) {
echo "Got V8JsScriptException\n";
var_dump($e->getMessage());
var_dump($e->getPrevious()->getMessage());
}
?>
===EOF===
--EXPECTF--
after getException
Got V8JsScriptException
string(%d) "php_exceptions_005:3: exception 'Exception' with message 'Test-Exception' in %s
Stack trace:
#0 [internal function]: Foo->getException()
#1 %s: V8Js->executeString('var ex = PHP.fo...', 'php_exceptions_...')
#2 {main}"
string(14) "Test-Exception"
===EOF===

View File

@ -0,0 +1,40 @@
--TEST--
Test V8::executeString() : PHP Exception handling (JS throws normal PHP-object)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo {
function getNonExceptionObject() {
return new \Foo();
}
}
$v8 = new V8Js();
$v8->foo = new \Foo();
$JS = <<< EOT
var ex = PHP.foo.getNonExceptionObject();
print("after getNonExceptionObject\\n");
throw ex;
print("after throw\\n");
EOT;
try {
$v8->executeString($JS, 'php_exceptions_006');
}
catch(V8JsScriptException $e) {
echo "Got V8JsScriptException\n";
var_dump($e->getMessage());
// previous exception should be NULL, as it is *not* a php exception
var_dump($e->getPrevious());
}
?>
===EOF===
--EXPECTF--
after getNonExceptionObject
Got V8JsScriptException
string(34) "php_exceptions_006:3: [object Foo]"
NULL
===EOF===

View File

@ -0,0 +1,42 @@
--TEST--
Test V8::executeString() : PHP Exception handling (basic)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class Foo {
function throwException() {
throw new \Exception("Test-Exception");
}
}
$v8 = new V8Js();
$v8->foo = new \Foo();
$JS = <<< EOT
try {
PHP.foo.throwException();
// the exception should abort further execution,
// hence the print must not pop up
print("after throwException\\n");
} catch(e) {
// JS should not catch in default mode
print("JS caught exception");
}
EOT;
try {
$v8->executeString($JS);
} catch (Exception $e) {
var_dump($e->getMessage());
var_dump($e->getFile());
var_dump($e->getLine());
}
?>
===EOF===
--EXPECTF--
string(14) "Test-Exception"
string(%d) "%sphp_exceptions_basic.php"
int(5)
===EOF===

18
v8js.cc
View File

@ -82,10 +82,28 @@ static ZEND_INI_MH(v8js_OnUpdateUseArrayAccess) /* {{{ */
}
/* }}} */
static ZEND_INI_MH(v8js_OnUpdateCompatExceptions) /* {{{ */
{
bool value;
if (new_value_length==2 && strcasecmp("on", new_value)==0) {
value = (bool) 1;
} else if (new_value_length==3 && strcasecmp("yes", new_value)==0) {
value = (bool) 1;
} else if (new_value_length==4 && strcasecmp("true", new_value)==0) {
value = (bool) 1;
} else {
value = (bool) atoi(new_value);
}
V8JSG(compat_php_exceptions) = value;
return SUCCESS;
}
/* }}} */
ZEND_INI_BEGIN() /* {{{ */
ZEND_INI_ENTRY("v8js.flags", NULL, ZEND_INI_ALL, v8js_OnUpdateV8Flags)
ZEND_INI_ENTRY("v8js.use_date", "0", ZEND_INI_ALL, v8js_OnUpdateUseDate)
ZEND_INI_ENTRY("v8js.use_array_access", "0", ZEND_INI_ALL, v8js_OnUpdateUseArrayAccess)
ZEND_INI_ENTRY("v8js.compat_php_exceptions", "0", ZEND_INI_ALL, v8js_OnUpdateCompatExceptions)
ZEND_INI_END()
/* }}} */

View File

@ -2,12 +2,13 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
| Copyright (c) 1997-2015 The PHP Group |
+----------------------------------------------------------------------+
| http://www.opensource.org/licenses/mit-license.php MIT License |
+----------------------------------------------------------------------+
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
| Author: Patrick Reilly <preilly@php.net> |
| Author: Stefan Siegl <stesie@php.net> |
+----------------------------------------------------------------------+
*/
@ -517,7 +518,7 @@ static void v8js_compile_script(zval *this_ptr, const char *str, int str_len, co
/* Compile errors? */
if (script.IsEmpty()) {
v8js_throw_script_exception(&try_catch TSRMLS_CC);
v8js_throw_script_exception(c->isolate, &try_catch TSRMLS_CC);
return;
}
res = (v8js_script *)emalloc(sizeof(v8js_script));
@ -1097,6 +1098,7 @@ PHP_MINIT_FUNCTION(v8js_class) /* {{{ */
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);
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("FLAG_PROPAGATE_PHP_EXCEPTIONS"), V8JS_FLAG_PROPAGATE_PHP_EXCEPTIONS TSRMLS_CC);
#ifdef ENABLE_DEBUGGER_SUPPORT
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("DEBUG_AUTO_BREAK_NEVER"), V8JS_DEBUG_AUTO_BREAK_NEVER TSRMLS_CC);

View File

@ -40,6 +40,8 @@ struct v8js_ctx {
int in_execution;
v8::Isolate *isolate;
long flags;
long time_limit;
bool time_limit_hit;
long memory_limit;

View File

@ -2,12 +2,13 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
| Copyright (c) 1997-2015 The PHP Group |
+----------------------------------------------------------------------+
| http://www.opensource.org/licenses/mit-license.php MIT License |
+----------------------------------------------------------------------+
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
| Author: Patrick Reilly <preilly@php.net> |
| Author: Stefan Siegl <stesie@php.net> |
+----------------------------------------------------------------------+
*/
@ -38,7 +39,7 @@ zend_class_entry *php_ce_v8js_memory_limit_exception;
/* {{{ Class: V8JsScriptException */
void v8js_create_script_exception(zval *return_value, v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
{
v8::String::Utf8Value exception(try_catch->Exception());
const char *exception_string = ToCString(exception);
@ -81,6 +82,21 @@ void v8js_create_script_exception(zval *return_value, v8::TryCatch *try_catch TS
const char* stacktrace_string = ToCString(stacktrace);
PHPV8_EXPROP(_string, JsTrace, stacktrace_string);
}
if(try_catch->Exception()->IsObject()) {
v8::Local<v8::Value> php_ref = try_catch->Exception()->ToObject()->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
if(!php_ref.IsEmpty()) {
assert(php_ref->IsExternal());
zval *php_exception = reinterpret_cast<zval *>(v8::External::Cast(*php_ref)->Value());
zend_class_entry *exception_ce = zend_exception_get_default(TSRMLS_C);
if (Z_TYPE_P(php_exception) == IS_OBJECT && instanceof_function(Z_OBJCE_P(php_exception), exception_ce TSRMLS_CC)) {
zend_exception_set_previous(return_value, php_exception TSRMLS_CC);
}
}
}
}
PHPV8_EXPROP(_string, message, message_string);
@ -89,7 +105,7 @@ void v8js_create_script_exception(zval *return_value, v8::TryCatch *try_catch TS
}
/* }}} */
void v8js_throw_script_exception(v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
void v8js_throw_script_exception(v8::Isolate *isolate, v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
{
v8::String::Utf8Value exception(try_catch->Exception());
const char *exception_string = ToCString(exception);
@ -99,7 +115,7 @@ void v8js_throw_script_exception(v8::TryCatch *try_catch TSRMLS_DC) /* {{{ */
zend_throw_exception(php_ce_v8js_script_exception, (char *) exception_string, 0 TSRMLS_CC);
} else {
MAKE_STD_ZVAL(zexception);
v8js_create_script_exception(zexception, try_catch TSRMLS_CC);
v8js_create_script_exception(zexception, isolate, try_catch TSRMLS_CC);
zend_throw_exception_object(zexception TSRMLS_CC);
}
}

View File

@ -2,12 +2,13 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
| Copyright (c) 1997-2015 The PHP Group |
+----------------------------------------------------------------------+
| http://www.opensource.org/licenses/mit-license.php MIT License |
+----------------------------------------------------------------------+
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
| Author: Patrick Reilly <preilly@php.net> |
| Author: Stefan Siegl <stesie@php.net> |
+----------------------------------------------------------------------+
*/
@ -19,8 +20,8 @@ extern zend_class_entry *php_ce_v8js_script_exception;
extern zend_class_entry *php_ce_v8js_time_limit_exception;
extern zend_class_entry *php_ce_v8js_memory_limit_exception;
void v8js_create_script_exception(zval *return_value, v8::TryCatch *try_catch TSRMLS_DC);
void v8js_throw_script_exception(v8::TryCatch *try_catch TSRMLS_DC);
void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::TryCatch *try_catch TSRMLS_DC);
void v8js_throw_script_exception(v8::Isolate *isolate, v8::TryCatch *try_catch TSRMLS_DC);
PHP_MINIT_FUNCTION(v8js_exceptions);

View File

@ -26,20 +26,7 @@ extern "C" {
V8JS_METHOD(exit) /* {{{ */
{
v8::Isolate *isolate = info.GetIsolate();
/* Unfortunately just calling TerminateExecution on the isolate is not
* enough, since v8 just marks the thread as "to be aborted" and doesn't
* immediately do so. Hence we enter an endless loop after signalling
* termination, so we definitely don't execute JS code after the exit()
* statement. */
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> source = V8JS_STR("for(;;);");
v8::Local<v8::Script> script = v8::Script::Compile(source);
v8::V8::TerminateExecution(isolate);
script->Run();
v8js_terminate_execution(isolate);
}
/* }}} */

View File

@ -21,6 +21,7 @@ extern "C" {
#include "ext/standard/php_string.h"
#include "zend_interfaces.h"
#include "zend_closures.h"
#include "zend_exceptions.h"
}
#include "php_v8js_macros.h"
@ -33,13 +34,13 @@ static void v8js_weak_object_callback(const v8::WeakCallbackData<v8::Object, zva
/* Callback for PHP methods and functions */
static void v8js_call_php_func(zval *value, zend_class_entry *ce, zend_function *method_ptr, v8::Isolate *isolate, const v8::FunctionCallbackInfo<v8::Value>& info TSRMLS_DC) /* {{{ */
{
v8::Handle<v8::Value> return_value;
v8::Handle<v8::Value> return_value = V8JS_NULL;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zval fname, *retval_ptr = NULL, **argv = NULL;
zend_uint argc = info.Length(), min_num_args = 0, max_num_args = 0;
char *error;
int error_len, i, flags = V8JS_FLAG_NONE;
int error_len, i;
v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
@ -84,7 +85,6 @@ static void v8js_call_php_func(zval *value, zend_class_entry *ce, zend_function
/* Convert parameters passed from V8 */
if (argc) {
flags = V8JS_GLOBAL_GET_FLAGS(isolate);
fci.params = (zval ***) safe_emalloc(argc, sizeof(zval **), 0);
argv = (zval **) safe_emalloc(argc, sizeof(zval *), 0);
for (i = 0; i < argc; i++) {
@ -98,7 +98,7 @@ static void v8js_call_php_func(zval *value, zend_class_entry *ce, zend_function
Z_ADDREF_P(argv[i]);
} else {
MAKE_STD_ZVAL(argv[i]);
if (v8js_to_zval(info[i], argv[i], flags, isolate TSRMLS_CC) == FAILURE) {
if (v8js_to_zval(info[i], argv[i], ctx->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(isolate, Error, error, error_len);
@ -134,7 +134,7 @@ static void v8js_call_php_func(zval *value, zend_class_entry *ce, zend_function
isolate->Enter();
}
zend_catch {
v8::V8::TerminateExecution(isolate);
v8js_terminate_execution(isolate);
V8JSG(fatal_error_abort) = 1;
}
zend_end_try();
@ -149,11 +149,19 @@ failure:
efree(fci.params);
}
if (retval_ptr != NULL) {
if(EG(exception) && !V8JSG(compat_php_exceptions)) {
if(ctx->flags & V8JS_FLAG_PROPAGATE_PHP_EXCEPTIONS) {
return_value = isolate->ThrowException(zval_to_v8js(EG(exception), isolate TSRMLS_CC));
zend_clear_exception(TSRMLS_C);
} else {
v8js_terminate_execution(isolate);
}
} else if (retval_ptr != NULL) {
return_value = zval_to_v8js(retval_ptr, isolate TSRMLS_CC);
}
if (retval_ptr != NULL) {
zval_ptr_dtor(&retval_ptr);
} else {
return_value = V8JS_NULL;
}
info.GetReturnValue().Set(return_value);
@ -526,6 +534,8 @@ inline v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> p
const char *method_name;
uint method_name_len;
v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
v8::Local<v8::Object> self = info.Holder();
v8::Local<v8::Value> ret_value;
v8::Local<v8::Function> cb;
@ -669,9 +679,8 @@ inline v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> p
zval_ptr_dtor(&php_value);
}
} else if (callback_type == V8JS_PROP_SETTER) {
int flags = V8JS_GLOBAL_GET_FLAGS(isolate);
MAKE_STD_ZVAL(php_value);
if (v8js_to_zval(set_value, php_value, flags, isolate TSRMLS_CC) != SUCCESS) {
if (v8js_to_zval(set_value, php_value, ctx->flags, isolate TSRMLS_CC) != SUCCESS) {
ret_value = v8::Handle<v8::Value>();
}
else {

View File

@ -55,7 +55,7 @@ static void v8js_timer_interrupt_handler(v8::Isolate *isolate, void *data) { /*
if (timer_ctx->memory_limit > 0 && hs.used_heap_size() > timer_ctx->memory_limit) {
timer_ctx->killed = true;
v8js_terminate_execution(c TSRMLS_CC);
v8::V8::TerminateExecution(c->isolate);
c->memory_limit_hit = true;
}
}
@ -80,7 +80,7 @@ void v8js_timer_thread(TSRMLS_D) /* {{{ */
}
else if(timer_ctx->time_limit > 0 && now > timer_ctx->time_point) {
timer_ctx->killed = true;
v8js_terminate_execution(c TSRMLS_CC);
v8::V8::TerminateExecution(c->isolate);
c->time_limit_hit = true;
}
else if (timer_ctx->memory_limit > 0) {

View File

@ -2,12 +2,13 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
| Copyright (c) 1997-2015 The PHP Group |
+----------------------------------------------------------------------+
| http://www.opensource.org/licenses/mit-license.php MIT License |
+----------------------------------------------------------------------+
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
| Author: Patrick Reilly <preilly@php.net> |
| Author: Stefan Siegl <stesie@php.net> |
+----------------------------------------------------------------------+
*/
@ -77,7 +78,7 @@ void v8js_v8_call(v8js_ctx *c, zval **return_value,
v8::TryCatch try_catch;
/* Set flags for runtime use */
V8JS_GLOBAL_SET_FLAGS(isolate, flags);
c->flags = flags;
/* Check if timezone has been changed and notify V8 */
tz = getenv("TZ");
@ -175,14 +176,14 @@ void v8js_v8_call(v8js_ctx *c, zval **return_value,
/* Report immediately if report_uncaught is true */
if (c->report_uncaught) {
v8js_throw_script_exception(&try_catch TSRMLS_CC);
v8js_throw_script_exception(c->isolate, &try_catch TSRMLS_CC);
return;
}
/* Exception thrown from JS, preserve it for future execution */
if (result.IsEmpty()) {
MAKE_STD_ZVAL(c->pending_exception);
v8js_create_script_exception(c->pending_exception, &try_catch TSRMLS_CC);
v8js_create_script_exception(c->pending_exception, c->isolate, &try_catch TSRMLS_CC);
return;
}
}
@ -199,10 +200,28 @@ void v8js_v8_call(v8js_ctx *c, zval **return_value,
}
/* }}} */
void v8js_terminate_execution(v8js_ctx *c TSRMLS_DC) /* {{{ */
void v8js_terminate_execution(v8::Isolate *isolate) /* {{{ */
{
// Forcefully terminate the current thread of V8 execution in the isolate
v8::V8::TerminateExecution(c->isolate);
if(v8::V8::IsExecutionTerminating(isolate)) {
/* Execution already terminating, needn't trigger it again and
* especially must not execute the spinning loop (which would cause
* crashes in V8 itself, at least with 4.2 and 4.3 version lines). */
return;
}
/* Unfortunately just calling TerminateExecution on the isolate is not
* enough, since v8 just marks the thread as "to be aborted" and doesn't
* immediately do so. Hence we enter an endless loop after signalling
* termination, so we definitely don't execute JS code after the exit()
* statement. */
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> source = V8JS_STR("for(;;);");
v8::Local<v8::Script> script = v8::Script::Compile(source);
v8::V8::TerminateExecution(isolate);
script->Run();
}
/* }}} */

View File

@ -45,7 +45,7 @@ void v8js_v8_init(TSRMLS_D);
void v8js_v8_call(v8js_ctx *c, zval **return_value,
long flags, long time_limit, long memory_limit,
std::function< v8::Local<v8::Value>(v8::Isolate *) >& v8_call TSRMLS_DC);
void v8js_terminate_execution(v8js_ctx *c TSRMLS_DC);
void v8js_terminate_execution(v8::Isolate *isolate);
/* Fetch V8 object properties */
int v8js_get_properties_hash(v8::Handle<v8::Value> jsValue, HashTable *retval, int flags, v8::Isolate *isolate TSRMLS_DC);