From b8920d5d3f67810d22c0fe60db34e5a90fc580fa Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 25 Oct 2016 13:06:04 +0200 Subject: [PATCH] Handle panics. --- Cargo.toml | 2 ++ src/lib.rs | 1 + src/panic.rs | 33 +++++++++++++++++++++++++++++++++ src/rust.rs | 2 ++ tests/panic.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+) create mode 100644 src/panic.rs create mode 100644 tests/panic.rs diff --git a/Cargo.toml b/Cargo.toml index d2c7d18ed..b19174382 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ name = "enumerate" [[test]] name = "evaluate" [[test]] +name = "panic" +[[test]] name = "typedarray" [[test]] name = "stack_limit" diff --git a/src/lib.rs b/src/lib.rs index 084a235a9..af366e41d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,7 @@ pub mod conversions; pub mod error; pub mod glue; pub mod jsval; +pub mod panic; pub mod typedarray; diff --git a/src/panic.rs b/src/panic.rs new file mode 100644 index 000000000..ae1c2cdba --- /dev/null +++ b/src/panic.rs @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::any::Any; +use std::cell::RefCell; +use std::panic::{UnwindSafe, catch_unwind, resume_unwind}; + +thread_local!(static PANIC_RESULT: RefCell>> = RefCell::new(None)); + +/// If there is a pending panic, resume unwinding. +pub fn maybe_resume_unwind() { + if let Some(error) = PANIC_RESULT.with(|result| result.borrow_mut().take()) { + resume_unwind(error); + } +} + +/// Generic wrapper for JS engine callbacks panic-catching +pub fn wrap_panic(function: F, generic_return_type: R) -> R + where F: FnMut() -> R + UnwindSafe +{ + let result = catch_unwind(function); + match result { + Ok(result) => result, + Err(error) => { + PANIC_RESULT.with(|result| { + assert!(result.borrow().is_none()); + *result.borrow_mut() = Some(error); + }); + generic_return_type + } + } +} diff --git a/src/rust.rs b/src/rust.rs index 20b4a3b7b..c9165778f 100644 --- a/src/rust.rs +++ b/src/rust.rs @@ -54,6 +54,7 @@ use jsapi::JS_ResolveStandardClass; use glue::{CreateAutoIdVector, SliceAutoIdVector, DestroyAutoIdVector}; use glue::{CreateAutoObjectVector, CreateCallArgsFromVp, AppendToAutoObjectVector, DeleteAutoObjectVector}; use glue::{NewCompileOptions, DeleteCompileOptions}; +use panic::maybe_resume_unwind; use default_heapsize; // From Gecko: @@ -205,6 +206,7 @@ impl Runtime { unsafe { if !Evaluate2(self.cx(), options.ptr, ptr as *const u16, len as size_t, rval) { debug!("...err!"); + maybe_resume_unwind(); Err(()) } else { // we could return the script result but then we'd have diff --git a/tests/panic.rs b/tests/panic.rs new file mode 100644 index 000000000..548f9ee48 --- /dev/null +++ b/tests/panic.rs @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#[macro_use] +extern crate js; + +use js::jsapi::CompartmentOptions; +use js::jsapi::JSAutoCompartment; +use js::jsapi::JSContext; +use js::jsapi::JS_DefineFunction; +use js::jsapi::JS_NewGlobalObject; +use js::jsapi::OnNewGlobalHookOption; +use js::jsapi::Value; +use js::jsval::UndefinedValue; +use js::panic::wrap_panic; +use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS}; +use std::ptr; +use std::str; + +#[test] +#[should_panic] +fn panic() { + let runtime = Runtime::new(); + let context = runtime.cx(); + let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook; + let c_option = CompartmentOptions::default(); + + unsafe { + let global = JS_NewGlobalObject(context, &SIMPLE_GLOBAL_CLASS, + ptr::null_mut(), h_option, &c_option); + rooted!(in(context) let global_root = global); + let global = global_root.handle(); + let _ac = JSAutoCompartment::new(context, global.get()); + let function = JS_DefineFunction(context, global, + b"test\0".as_ptr() as *const _, + Some(test), 0, 0); + assert!(!function.is_null()); + rooted!(in(context) let mut rval = UndefinedValue()); + let _ = runtime.evaluate_script(global, "test();", "test.js", 0, + rval.handle_mut()); + } +} + +unsafe extern "C" fn test(_cx: *mut JSContext, _argc: u32, _vp: *mut Value) -> bool { + wrap_panic(|| { + panic!() + }, false) +}