mirror of
https://github.com/servo/servo.git
synced 2025-08-02 12:10:29 +01:00
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:
parent
a74ce64539
commit
b8853554db
4 changed files with 58 additions and 11 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue