diff --git a/tests/exception_propagation_2.phpt b/tests/exception_propagation_2.phpt index a822eda..c12830a 100644 --- a/tests/exception_propagation_2.phpt +++ b/tests/exception_propagation_2.phpt @@ -1,6 +1,7 @@ --TEST-- Test V8::executeString() : Exception propagation test 2 --SKIPIF-- +SKIP needs discussion, see issue #144 --FILE-- +--FILE-- +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=== diff --git a/tests/php_exceptions_002.phpt b/tests/php_exceptions_002.phpt new file mode 100644 index 0000000..2ddac8d --- /dev/null +++ b/tests/php_exceptions_002.phpt @@ -0,0 +1,67 @@ +--TEST-- +Test V8::executeString() : PHP Exception handling (multi-level) +--SKIPIF-- + +--FILE-- +foo = new \Foo(); + +$work = $v8->executeString(<<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=== diff --git a/tests/php_exceptions_basic.phpt b/tests/php_exceptions_basic.phpt new file mode 100644 index 0000000..1ddc678 --- /dev/null +++ b/tests/php_exceptions_basic.phpt @@ -0,0 +1,42 @@ +--TEST-- +Test V8::executeString() : PHP Exception handling (basic) +--SKIPIF-- + +--FILE-- +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=== diff --git a/v8js_methods.cc b/v8js_methods.cc index 2bd9013..722736b 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -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 source = V8JS_STR("for(;;);"); - v8::Local script = v8::Script::Compile(source); - v8::V8::TerminateExecution(isolate); - script->Run(); + v8js_terminate_execution(isolate); } /* }}} */ diff --git a/v8js_object_export.cc b/v8js_object_export.cc index e108740..80ba129 100644 --- a/v8js_object_export.cc +++ b/v8js_object_export.cc @@ -134,11 +134,15 @@ 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(); + if(EG(exception)) { + v8js_terminate_execution(isolate); + } + failure: /* Cleanup */ if (argc) { diff --git a/v8js_timer.cc b/v8js_timer.cc index 11ea08e..d556716 100644 --- a/v8js_timer.cc +++ b/v8js_timer.cc @@ -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) { diff --git a/v8js_v8.cc b/v8js_v8.cc index a202c8a..12e413d 100644 --- a/v8js_v8.cc +++ b/v8js_v8.cc @@ -199,10 +199,21 @@ 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); + /* 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 source = V8JS_STR("for(;;);"); + v8::Local script = v8::Script::Compile(source); + v8::V8::TerminateExecution(isolate); + script->Run(); } /* }}} */ diff --git a/v8js_v8.h b/v8js_v8.h index 1e91970..e57d6f8 100644 --- a/v8js_v8.h +++ b/v8js_v8.h @@ -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::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 jsValue, HashTable *retval, int flags, v8::Isolate *isolate TSRMLS_DC);