From d31960342fdecebad4848c60fc457d81d8476a90 Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) \"Kassimo\" Qian" Date: Tue, 31 Dec 2019 03:11:43 -0800 Subject: [PATCH] isolate: add termination related methods (#157) --- src/binding.cc | 12 ++++++++++++ src/isolate.rs | 38 +++++++++++++++++++++++++++++++++++ tests/test_api.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/src/binding.cc b/src/binding.cc index d0798861..af27a091 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -149,6 +149,18 @@ v8::Value* v8__Isolate__ThrowException(v8::Isolate& isolate, return local_to_ptr(isolate.ThrowException(ptr_to_local(exception))); } +void v8__Isolate__TerminateExecution(v8::Isolate& isolate) { + isolate.TerminateExecution(); +} + +bool v8__Isolate__IsExecutionTerminating(v8::Isolate& isolate) { + return isolate.IsExecutionTerminating(); +} + +void v8__Isolate__CancelTerminateExecution(v8::Isolate& isolate) { + isolate.CancelTerminateExecution(); +} + v8::Isolate::CreateParams* v8__Isolate__CreateParams__NEW() { return new v8::Isolate::CreateParams(); } diff --git a/src/isolate.rs b/src/isolate.rs index 2302bf70..29208aa3 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -93,6 +93,9 @@ extern "C" { isolate: &Isolate, exception: &Value, ) -> *mut Value; + fn v8__Isolate__TerminateExecution(isolate: &Isolate); + fn v8__Isolate__IsExecutionTerminating(isolate: &Isolate) -> bool; + fn v8__Isolate__CancelTerminateExecution(isolate: &Isolate); fn v8__Isolate__CreateParams__NEW() -> *mut CreateParams; fn v8__Isolate__CreateParams__DELETE(this: &mut CreateParams); @@ -244,6 +247,41 @@ impl Isolate { } } + /// Forcefully terminate the current thread of JavaScript execution + /// in the given isolate. + /// + /// This method can be used by any thread even if that thread has not + /// acquired the V8 lock with a Locker object. + pub fn terminate_execution(&self) { + unsafe { v8__Isolate__TerminateExecution(self) } + } + + /// Is V8 terminating JavaScript execution. + /// + /// Returns true if JavaScript execution is currently terminating + /// because of a call to TerminateExecution. In that case there are + /// still JavaScript frames on the stack and the termination + /// exception is still active. + pub fn is_execution_terminating(&self) -> bool { + unsafe { v8__Isolate__IsExecutionTerminating(self) } + } + + /// Resume execution capability in the given isolate, whose execution + /// was previously forcefully terminated using TerminateExecution(). + /// + /// When execution is forcefully terminated using TerminateExecution(), + /// the isolate can not resume execution until all JavaScript frames + /// have propagated the uncatchable exception which is generated. This + /// method allows the program embedding the engine to handle the + /// termination event and resume execution capability, even if + /// JavaScript frames remain on the stack. + /// + /// This method can be used by any thread even if that thread has not + /// acquired the V8 lock with a Locker object. + pub fn cancel_terminate_execution(&self) { + unsafe { v8__Isolate__CancelTerminateExecution(self) } + } + /// Disposes the isolate. The isolate must not be entered by any /// thread to be disposable. pub unsafe fn dispose(&mut self) { diff --git a/tests/test_api.rs b/tests/test_api.rs index 73b12782..5d05cbae 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -386,6 +386,56 @@ fn throw_exception() { } } +#[test] +fn terminate_execution() { + let g = setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator(v8::new_default_allocator()); + let isolate = v8::Isolate::new(params); + let mut locker = v8::Locker::new(&isolate); + // Originally run fine. + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); + let mut context = v8::Context::new(scope); + context.enter(); + let result = eval(scope, context, "true").unwrap(); + let true_val = v8::new_true(scope).into(); + assert!(result.same_value(true_val)); + context.exit(); + } + // Terminate. + isolate.terminate_execution(); + // Below run should fail with terminated knowledge. + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); + let mut context = v8::Context::new(scope); + context.enter(); + let mut try_catch = v8::TryCatch::new(scope); + let tc = try_catch.enter(); + let _ = eval(scope, context, "true"); + assert!(tc.has_caught()); + assert!(tc.has_terminated()); + context.exit(); + } + // Cancel termination. + isolate.cancel_terminate_execution(); + // Works again. + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); + let mut context = v8::Context::new(scope); + context.enter(); + let result = eval(scope, context, "true").unwrap(); + let true_val = v8::new_true(scope).into(); + assert!(result.same_value(true_val)); + context.exit(); + } + drop(locker); + drop(g); +} + #[test] fn add_message_listener() { let g = setup();