Wrap executions of Rust code called from JS in catch_unwind. Propagate the interrupted panic to the origin of the JS execution via TLS before resuming. Fix #6462.

This commit is contained in:
Josh Matthews 2016-06-20 13:25:42 -04:00
parent a74ce64539
commit b8853554db
4 changed files with 58 additions and 11 deletions

View file

@ -2145,7 +2145,7 @@ class CGAbstractMethod(CGThing):
""" """
def __init__(self, descriptor, name, returnType, args, inline=False, def __init__(self, descriptor, name, returnType, args, inline=False,
alwaysInline=False, extern=False, pub=False, templateArgs=None, alwaysInline=False, extern=False, pub=False, templateArgs=None,
unsafe=False, docs=None): unsafe=False, docs=None, doesNotPanic=False):
CGThing.__init__(self) CGThing.__init__(self)
self.descriptor = descriptor self.descriptor = descriptor
self.name = name self.name = name
@ -2157,6 +2157,7 @@ class CGAbstractMethod(CGThing):
self.pub = pub self.pub = pub
self.unsafe = unsafe self.unsafe = unsafe
self.docs = docs self.docs = docs
self.catchPanic = self.extern and not doesNotPanic
def _argstring(self): def _argstring(self):
return ', '.join([a.declare() for a in self.args]) return ', '.join([a.declare() for a in self.args])
@ -2199,6 +2200,19 @@ class CGAbstractMethod(CGThing):
if self.unsafe and not self.extern: if self.unsafe and not self.extern:
body = CGWrapper(CGIndenter(body), pre="unsafe {\n", post="\n}") body = CGWrapper(CGIndenter(body), pre="unsafe {\n", post="\n}")
if self.catchPanic:
body = CGWrapper(CGIndenter(body),
pre="let result = panic::catch_unwind(AssertUnwindSafe(|| {\n",
post=("""}));
match result {
Ok(result) => result,
Err(error) => {
store_panic_result(error);
return%s;
}
}
""" % ("" if self.returnType == "void" else " false")))
return CGWrapper(CGIndenter(body), return CGWrapper(CGIndenter(body),
pre=self.definition_prologue(), pre=self.definition_prologue(),
post=self.definition_epilogue()).define() post=self.definition_epilogue()).define()
@ -2447,9 +2461,9 @@ class CGAbstractExternMethod(CGAbstractMethod):
Abstract base class for codegen of implementation-only (no Abstract base class for codegen of implementation-only (no
declaration) static methods. declaration) static methods.
""" """
def __init__(self, descriptor, name, returnType, args): def __init__(self, descriptor, name, returnType, args, doesNotPanic=False):
CGAbstractMethod.__init__(self, descriptor, name, returnType, args, CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
inline=False, extern=True) inline=False, extern=True, doesNotPanic=doesNotPanic)
class PropertyArrays(): class PropertyArrays():
@ -4830,7 +4844,7 @@ return true;""" % (getIndexedOrExpando, getNamed)
class CGDOMJSProxyHandler_className(CGAbstractExternMethod): class CGDOMJSProxyHandler_className(CGAbstractExternMethod):
def __init__(self, descriptor): def __init__(self, descriptor):
args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_proxy')] args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_proxy')]
CGAbstractExternMethod.__init__(self, descriptor, "className", "*const i8", args) CGAbstractExternMethod.__init__(self, descriptor, "className", "*const i8", args, doesNotPanic=True)
self.descriptor = descriptor self.descriptor = descriptor
def getBody(self): def getBody(self):
@ -4845,7 +4859,7 @@ class CGAbstractClassHook(CGAbstractExternMethod):
Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
'this' unwrapping as it assumes that the unwrapped type is always known. 'this' unwrapping as it assumes that the unwrapped type is always known.
""" """
def __init__(self, descriptor, name, returnType, args): def __init__(self, descriptor, name, returnType, args, doesNotPanic=False):
CGAbstractExternMethod.__init__(self, descriptor, name, returnType, CGAbstractExternMethod.__init__(self, descriptor, name, returnType,
args) args)
@ -4905,7 +4919,7 @@ class CGClassTraceHook(CGAbstractClassHook):
def __init__(self, descriptor): def __init__(self, descriptor):
args = [Argument('*mut JSTracer', 'trc'), Argument('*mut JSObject', 'obj')] args = [Argument('*mut JSTracer', 'trc'), Argument('*mut JSObject', 'obj')]
CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void', CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
args) args, doesNotPanic=True)
self.traceGlobal = descriptor.isGlobal() self.traceGlobal = descriptor.isGlobal()
def generate_code(self): def generate_code(self):
@ -5597,11 +5611,13 @@ class CGBindingRoot(CGThing):
'mem::heap_size_of_raw_self_and_children', 'mem::heap_size_of_raw_self_and_children',
'libc', 'libc',
'util::prefs', 'util::prefs',
'script_runtime::{store_panic_result, maybe_take_panic_result}',
'std::borrow::ToOwned', 'std::borrow::ToOwned',
'std::cmp', 'std::cmp',
'std::mem', 'std::mem',
'std::num', 'std::num',
'std::os', 'std::os',
'std::panic::{self, AssertUnwindSafe}',
'std::ptr', 'std::ptr',
'std::str', 'std::str',
'std::rc', 'std::rc',
@ -6088,6 +6104,9 @@ class CallbackMethod(CallbackMember):
" length_: ${argc} as ::libc::size_t,\n" " length_: ${argc} as ::libc::size_t,\n"
" elements_: ${argv}\n" " elements_: ${argv}\n"
" }, rval.handle_mut());\n" " }, rval.handle_mut());\n"
"if let Some(error) = maybe_take_panic_result() {\n"
" panic::resume_unwind(error);\n"
"}\n"
"if !ok {\n" "if !ok {\n"
" return Err(JSFailed);\n" " return Err(JSFailed);\n"
"}\n").substitute(replacements) "}\n").substitute(replacements)

View file

@ -61,7 +61,7 @@ use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflo
use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
use script_layout_interface::rpc::{MarginStyleResponse, ResolvedStyleResponse}; use script_layout_interface::rpc::{MarginStyleResponse, ResolvedStyleResponse};
use script_runtime::{ScriptChan, ScriptPort}; use script_runtime::{ScriptChan, ScriptPort, maybe_take_panic_result};
use script_thread::SendableMainThreadScriptChan; use script_thread::SendableMainThreadScriptChan;
use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper}; use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper};
use script_traits::{ConstellationControlMsg, UntrustedNodeAddress}; use script_traits::{ConstellationControlMsg, UntrustedNodeAddress};
@ -74,6 +74,7 @@ use std::collections::{HashMap, HashSet};
use std::default::Default; use std::default::Default;
use std::ffi::CString; use std::ffi::CString;
use std::io::{Write, stderr, stdout}; use std::io::{Write, stderr, stdout};
use std::panic;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use std::sync::mpsc::TryRecvError::{Disconnected, Empty};
@ -914,6 +915,10 @@ impl<'a, T: Reflectable> ScriptHelpers for &'a T {
report_pending_exception(cx, globalhandle.get()); report_pending_exception(cx, globalhandle.get());
} }
} }
if let Some(error) = maybe_take_panic_result() {
panic::resume_unwind(error);
}
} }
) )
} }

View file

@ -28,11 +28,12 @@ use msg::constellation_msg::{PipelineId, ReferrerPolicy, PanicMsg};
use net_traits::{LoadContext, ResourceThreads, load_whole_resource}; use net_traits::{LoadContext, ResourceThreads, load_whole_resource};
use net_traits::{RequestSource, LoadOrigin, CustomResponseSender, IpcSend}; use net_traits::{RequestSource, LoadOrigin, CustomResponseSender, IpcSend};
use profile_traits::{mem, time}; use profile_traits::{mem, time};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, maybe_take_panic_result};
use script_traits::ScriptMsg as ConstellationMsg; use script_traits::ScriptMsg as ConstellationMsg;
use script_traits::{MsDuration, TimerEvent, TimerEventId, TimerEventRequest, TimerSource}; use script_traits::{MsDuration, TimerEvent, TimerEventId, TimerEventRequest, TimerSource};
use std::cell::Cell; use std::cell::Cell;
use std::default::Default; use std::default::Default;
use std::panic;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -320,8 +321,14 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
} }
}; };
match self.runtime.evaluate_script( let result = self.runtime.evaluate_script(
self.reflector().get_jsobject(), &source, url.as_str(), 1, rval.handle_mut()) { self.reflector().get_jsobject(), &source, url.as_str(), 1, rval.handle_mut());
if let Some(error) = maybe_take_panic_result() {
panic::resume_unwind(error);
}
match result {
Ok(_) => (), Ok(_) => (),
Err(_) => { Err(_) => {
println!("evaluate_script failed"); println!("evaluate_script failed");

View file

@ -19,7 +19,8 @@ use js::jsapi::{JSObject, RuntimeOptionsRef, SetPreserveWrapperCallback};
use js::rust::Runtime; use js::rust::Runtime;
use profile_traits::mem::{Report, ReportKind, ReportsChan}; use profile_traits::mem::{Report, ReportKind, ReportsChan};
use script_thread::{Runnable, STACK_ROOTS, trace_thread}; use script_thread::{Runnable, STACK_ROOTS, trace_thread};
use std::cell::Cell; use std::any::Any;
use std::cell::{RefCell, Cell};
use std::io::{Write, stdout}; use std::io::{Write, stdout};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::os; use std::os;
@ -321,6 +322,21 @@ pub fn get_reports(cx: *mut JSContext, path_seg: String) -> Vec<Report> {
reports reports
} }
thread_local!(static PANIC_RESULT: RefCell<Option<Box<Any + Send>>> = RefCell::new(None));
pub fn store_panic_result(error: Box<Any + Send>) {
PANIC_RESULT.with(|result| {
assert!(result.borrow().is_none());
*result.borrow_mut() = Some(error);
});
}
pub fn maybe_take_panic_result() -> Option<Box<Any + Send>> {
PANIC_RESULT.with(|result| {
result.borrow_mut().take()
})
}
thread_local!(static GC_CYCLE_START: Cell<Option<Tm>> = Cell::new(None)); thread_local!(static GC_CYCLE_START: Cell<Option<Tm>> = Cell::new(None));
thread_local!(static GC_SLICE_START: Cell<Option<Tm>> = Cell::new(None)); thread_local!(static GC_SLICE_START: Cell<Option<Tm>> = Cell::new(None));