aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2024-10-16 01:11:31 -0700
committerGitHub <noreply@github.com>2024-10-16 08:11:31 +0000
commit30abb99287790c2653821cc469ecd891b89ab57c (patch)
tree9d9187cd6a0ca7133096315044b24dcf75871a65 /components/script
parented959d7a1a2a584803f689ce8d92f7d0c3c0d48b (diff)
downloadservo-30abb99287790c2653821cc469ecd891b89ab57c.tar.gz
servo-30abb99287790c2653821cc469ecd891b89ab57c.zip
clippy: Add safety documentation and clean up unsafe methods (#33748)
This change: 1. Adds safety documentation where it was missing. 2. Limits the scope of unsafe code in some cases to where it is actually unsafe. 3. Converts some free functions to associated functions and methods, thereby making them more likely to be called safely. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/reflector.rs8
-rw-r--r--components/script/dom/bindings/trace.rs11
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs6
-rw-r--r--components/script/dom/serviceworkerglobalscope.rs5
-rw-r--r--components/script/dom/workerglobalscope.rs7
-rw-r--r--components/script/dom/worklet.rs4
-rw-r--r--components/script/layout_dom/element.rs12
-rw-r--r--components/script/layout_dom/node.rs5
-rw-r--r--components/script/layout_dom/shadow_root.rs6
-rw-r--r--components/script/script_runtime.rs614
-rw-r--r--components/script/script_thread.rs16
11 files changed, 377 insertions, 317 deletions
diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs
index 261a317e360..9d3d27141ef 100644
--- a/components/script/dom/bindings/reflector.rs
+++ b/components/script/dom/bindings/reflector.rs
@@ -76,6 +76,10 @@ impl Reflector {
}
/// Initialize the reflector. (May be called only once.)
+ ///
+ /// # Safety
+ ///
+ /// The provided [`JSObject`] pointer must point to a valid [`JSObject`].
pub unsafe fn set_jsobject(&self, object: *mut JSObject) {
assert!(self.object.get().is_null());
assert!(!object.is_null());
@@ -123,6 +127,10 @@ impl DomObject for Reflector {
/// A trait to initialize the `Reflector` for a DOM object.
pub trait MutDomObject: DomObject {
/// Initializes the Reflector
+ ///
+ /// # Safety
+ ///
+ /// The provided [`JSObject`] pointer must point to a valid [`JSObject`].
unsafe fn init_reflector(&self, obj: *mut JSObject);
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 544aaa229fc..06ec5b92d89 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -69,8 +69,19 @@ use crate::script_thread::IncompleteParserContexts;
use crate::task::TaskBox;
/// A trait to allow tracing only DOM sub-objects.
+///
+/// # Safety
+///
+/// This trait is unsafe; if it is implemented incorrectly, the GC may end up collecting objects
+/// that are still reachable.
pub unsafe trait CustomTraceable {
/// Trace `self`.
+ ///
+ /// # Safety
+ ///
+ /// The `JSTracer` argument must point to a valid `JSTracer` in memory. In addition,
+ /// implementors of this method must ensure that all active objects are properly traced
+ /// or else the garbage collector may end up collecting objects that are still reachable.
unsafe fn trace(&self, trc: *mut JSTracer);
}
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index 6550671a3d3..4b0b064bb35 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -55,8 +55,8 @@ use crate::fetch::load_whole_resource;
use crate::realms::{enter_realm, AlreadyInRealm, InRealm};
use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent;
use crate::script_runtime::{
- new_child_runtime, CanGc, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan,
- ScriptPort, ThreadSafeJSContext,
+ CanGc, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan, ScriptPort,
+ ThreadSafeJSContext,
};
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
use crate::task_source::networking::NetworkingTaskSource;
@@ -381,7 +381,7 @@ impl DedicatedWorkerGlobalScope {
}),
pipeline_id,
);
- new_child_runtime(parent, Some(task_source))
+ Runtime::new_with_parent(Some(parent), Some(task_source))
};
let context_for_interrupt = runtime.thread_safe_js_context();
diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs
index 651d383337a..65975c5755b 100644
--- a/components/script/dom/serviceworkerglobalscope.rs
+++ b/components/script/dom/serviceworkerglobalscope.rs
@@ -45,8 +45,7 @@ use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::fetch::load_whole_resource;
use crate::realms::{enter_realm, AlreadyInRealm, InRealm};
use crate::script_runtime::{
- new_rt_and_cx, CanGc, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan,
- ThreadSafeJSContext,
+ CanGc, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan, ThreadSafeJSContext,
};
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
use crate::task_source::TaskSourceName;
@@ -310,7 +309,7 @@ impl ServiceWorkerGlobalScope {
.name(format!("SW:{}", script_url.debug_compact()))
.spawn(move || {
thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
- let runtime = new_rt_and_cx(None);
+ let runtime = Runtime::new(None);
let context_for_interrupt = runtime.thread_safe_js_context();
let _ = context_sender.send(context_for_interrupt);
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index 91d21feb945..2e77790cdcf 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -54,9 +54,7 @@ use crate::dom::workerlocation::WorkerLocation;
use crate::dom::workernavigator::WorkerNavigator;
use crate::fetch;
use crate::realms::{enter_realm, InRealm};
-use crate::script_runtime::{
- get_reports, CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptPort,
-};
+use crate::script_runtime::{CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptPort};
use crate::task::TaskCanceller;
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
use crate::task_source::file_reading::FileReadingTaskSource;
@@ -539,8 +537,7 @@ impl WorkerGlobalScope {
CommonScriptMsg::Task(_, task, _, _) => task.run_box(),
CommonScriptMsg::CollectReports(reports_chan) => {
let cx = self.get_cx();
- let path_seg = format!("url({})", self.get_url());
- let reports = unsafe { get_reports(*cx, path_seg) };
+ let reports = cx.get_reports(format!("url({})", self.get_url()));
reports_chan.send(reports);
},
}
diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs
index d2ee1ef965c..a796bb98071 100644
--- a/components/script/dom/worklet.rs
+++ b/components/script/dom/worklet.rs
@@ -49,7 +49,7 @@ use crate::dom::workletglobalscope::{
};
use crate::fetch::load_whole_resource;
use crate::realms::InRealm;
-use crate::script_runtime::{new_rt_and_cx, CommonScriptMsg, Runtime, ScriptThreadEventCategory};
+use crate::script_runtime::{CommonScriptMsg, Runtime, ScriptThreadEventCategory};
use crate::script_thread::{MainThreadScriptMsg, ScriptThread};
use crate::task::TaskBox;
use crate::task_source::TaskSourceName;
@@ -490,7 +490,7 @@ impl WorkletThread {
global_init: init.global_init,
global_scopes: HashMap::new(),
control_buffer: None,
- runtime: new_rt_and_cx(None),
+ runtime: Runtime::new(None),
should_gc: false,
gc_threshold: MIN_GC_THRESHOLD,
});
diff --git a/components/script/layout_dom/element.rs b/components/script/layout_dom/element.rs
index 4ecd3ca2de1..05349205c82 100644
--- a/components/script/layout_dom/element.rs
+++ b/components/script/layout_dom/element.rs
@@ -89,12 +89,24 @@ impl<'dom> ServoLayoutElement<'dom> {
self.as_node().style_data()
}
+ /// Unset the snapshot flags on the underlying DOM object for this element.
+ ///
+ /// # Safety
+ ///
+ /// This function accesses and modifies the underlying DOM object and should
+ /// not be used by more than a single thread at once.
pub unsafe fn unset_snapshot_flags(&self) {
self.as_node()
.node
.set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false);
}
+ /// Unset the snapshot flags on the underlying DOM object for this element.
+ ///
+ /// # Safety
+ ///
+ /// This function accesses and modifies the underlying DOM object and should
+ /// not be used by more than a single thread at once.
pub unsafe fn set_has_snapshot(&self) {
self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true);
}
diff --git a/components/script/layout_dom/node.rs b/components/script/layout_dom/node.rs
index efb05a2f121..0b2b939cda4 100644
--- a/components/script/layout_dom/node.rs
+++ b/components/script/layout_dom/node.rs
@@ -76,6 +76,11 @@ impl<'dom> ServoLayoutNode<'dom> {
ServoLayoutNode { node: n }
}
+ /// Create a new [`ServoLayoutNode`] for this given [`TrustedNodeAddress`].
+ ///
+ /// # Safety
+ ///
+ /// The address pointed to by `address` should point to a valid node in memory.
pub unsafe fn new(address: &TrustedNodeAddress) -> Self {
ServoLayoutNode::from_layout_js(LayoutDom::from_trusted_node_address(*address))
}
diff --git a/components/script/layout_dom/shadow_root.rs b/components/script/layout_dom/shadow_root.rs
index 9137e863972..0763e150eff 100644
--- a/components/script/layout_dom/shadow_root.rs
+++ b/components/script/layout_dom/shadow_root.rs
@@ -48,6 +48,12 @@ impl<'dom> ServoShadowRoot<'dom> {
ServoShadowRoot { shadow_root }
}
+ /// Flush the stylesheets for the underlying shadow root.
+ ///
+ /// # Safety
+ ///
+ /// This modifies a DOM object, so should care should be taken that only one
+ /// thread has a reference to this object.
pub unsafe fn flush_stylesheets(
&self,
stylist: &mut Stylist,
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index aeee9c1553b..0e3228ac73c 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -448,6 +448,258 @@ pub struct Runtime {
}
impl Runtime {
+ /// Create a new runtime, optionally with the given [`NetworkingTaskSource`].
+ ///
+ /// # Safety
+ ///
+ /// If panicking does not abort the program, any threads with child runtimes will continue
+ /// executing after the thread with the parent runtime panics, but they will be in an
+ /// invalid and undefined state.
+ ///
+ /// This, like many calls to SpiderMoney API, is unsafe.
+ #[allow(unsafe_code)]
+ pub(crate) fn new(networking_task_source: Option<NetworkingTaskSource>) -> Runtime {
+ unsafe { Self::new_with_parent(None, networking_task_source) }
+ }
+
+ /// Create a new runtime, optionally with the given [`ParentRuntime`] and [`NetworkingTaskSource`].
+ ///
+ /// # Safety
+ ///
+ /// If panicking does not abort the program, any threads with child runtimes will continue
+ /// executing after the thread with the parent runtime panics, but they will be in an
+ /// invalid and undefined state.
+ ///
+ /// The `parent` pointer in the [`ParentRuntime`] argument must point to a valid object in memory.
+ ///
+ /// This, like many calls to the SpiderMoney API, is unsafe.
+ #[allow(unsafe_code)]
+ pub(crate) unsafe fn new_with_parent(
+ parent: Option<ParentRuntime>,
+ networking_task_source: Option<NetworkingTaskSource>,
+ ) -> Runtime {
+ LiveDOMReferences::initialize();
+ let (cx, runtime) = if let Some(parent) = parent {
+ let runtime = RustRuntime::create_with_parent(parent);
+ let cx = runtime.cx();
+ (cx, runtime)
+ } else {
+ let runtime = RustRuntime::new(JS_ENGINE.lock().unwrap().as_ref().unwrap().clone());
+ (runtime.cx(), runtime)
+ };
+
+ JS_AddExtraGCRootsTracer(cx, Some(trace_rust_roots), ptr::null_mut());
+
+ JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
+
+ JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
+ JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
+
+ // Needed for debug assertions about whether GC is running.
+ if cfg!(debug_assertions) {
+ JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
+ }
+
+ if opts::get().debug.gc_profile {
+ SetGCSliceCallback(cx, Some(gc_slice_callback));
+ }
+
+ unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
+ true
+ }
+ unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
+ // fixme: return true when the Drop impl for a DOM object has been invoked
+ false
+ }
+ SetDOMCallbacks(cx, &DOM_CALLBACKS);
+ SetPreserveWrapperCallbacks(
+ cx,
+ Some(empty_wrapper_callback),
+ Some(empty_has_released_callback),
+ );
+ // Pre barriers aren't working correctly at the moment
+ DisableIncrementalGC(cx);
+
+ unsafe extern "C" fn dispatch_to_event_loop(
+ closure: *mut c_void,
+ dispatchable: *mut JSRunnable,
+ ) -> bool {
+ let networking_task_src: &NetworkingTaskSource =
+ &*(closure as *mut NetworkingTaskSource);
+ let runnable = Runnable(dispatchable);
+ let task = task!(dispatch_to_event_loop_message: move || {
+ runnable.run(RustRuntime::get(), Dispatchable_MaybeShuttingDown::NotShuttingDown);
+ });
+
+ networking_task_src.queue_unconditionally(task).is_ok()
+ }
+
+ let mut networking_task_src_ptr = std::ptr::null_mut();
+ if let Some(source) = networking_task_source {
+ networking_task_src_ptr = Box::into_raw(Box::new(source));
+ InitDispatchToEventLoop(
+ cx,
+ Some(dispatch_to_event_loop),
+ networking_task_src_ptr as *mut c_void,
+ );
+ }
+
+ InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
+
+ let microtask_queue = Rc::new(MicrotaskQueue::default());
+ let job_queue = CreateJobQueue(
+ &JOB_QUEUE_TRAPS,
+ &*microtask_queue as *const _ as *const c_void,
+ );
+ SetJobQueue(cx, job_queue);
+ SetPromiseRejectionTrackerCallback(cx, Some(promise_rejection_tracker), ptr::null_mut());
+
+ EnsureModuleHooksInitialized(runtime.rt());
+
+ set_gc_zeal_options(cx);
+
+ // Enable or disable the JITs.
+ let cx_opts = &mut *ContextOptionsRef(cx);
+ JS_SetGlobalJitCompilerOption(
+ cx,
+ JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
+ pref!(js.baseline_interpreter.enabled) as u32,
+ );
+ JS_SetGlobalJitCompilerOption(
+ cx,
+ JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
+ pref!(js.baseline_jit.enabled) as u32,
+ );
+ JS_SetGlobalJitCompilerOption(
+ cx,
+ JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
+ pref!(js.ion.enabled) as u32,
+ );
+ cx_opts.compileOptions_.asmJSOption_ = if pref!(js.asmjs.enabled) {
+ AsmJSOption::Enabled
+ } else {
+ AsmJSOption::DisabledByAsmJSPref
+ };
+ let wasm_enabled = pref!(js.wasm.enabled);
+ cx_opts.set_wasm_(wasm_enabled);
+ if wasm_enabled {
+ // If WASM is enabled without setting the buildIdOp,
+ // initializing a module will report an out of memory error.
+ // https://dxr.mozilla.org/mozilla-central/source/js/src/wasm/WasmTypes.cpp#458
+ SetProcessBuildIdOp(Some(servo_build_id));
+ }
+ cx_opts.set_wasmBaseline_(pref!(js.wasm.baseline.enabled));
+ cx_opts.set_wasmIon_(pref!(js.wasm.ion.enabled));
+ // TODO: handle js.throw_on_asmjs_validation_failure (needs new Spidermonkey)
+ JS_SetGlobalJitCompilerOption(
+ cx,
+ JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
+ pref!(js.native_regex.enabled) as u32,
+ );
+ JS_SetParallelParsingEnabled(cx, pref!(js.parallel_parsing.enabled));
+ JS_SetOffthreadIonCompilationEnabled(cx, pref!(js.offthread_compilation.enabled));
+ JS_SetGlobalJitCompilerOption(
+ cx,
+ JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
+ if pref!(js.baseline_jit.unsafe_eager_compilation.enabled) {
+ 0
+ } else {
+ u32::MAX
+ },
+ );
+ JS_SetGlobalJitCompilerOption(
+ cx,
+ JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
+ if pref!(js.ion.unsafe_eager_compilation.enabled) {
+ 0
+ } else {
+ u32::MAX
+ },
+ );
+ // TODO: handle js.discard_system_source.enabled
+ // TODO: handle js.asyncstack.enabled (needs new Spidermonkey)
+ // TODO: handle js.throw_on_debugee_would_run (needs new Spidermonkey)
+ // TODO: handle js.dump_stack_on_debugee_would_run (needs new Spidermonkey)
+ // TODO: handle js.shared_memory.enabled
+ JS_SetGCParameter(
+ cx,
+ JSGCParamKey::JSGC_MAX_BYTES,
+ in_range(pref!(js.mem.max), 1, 0x100)
+ .map(|val| (val * 1024 * 1024) as u32)
+ .unwrap_or(u32::MAX),
+ );
+ // NOTE: This is disabled above, so enabling it here will do nothing for now.
+ JS_SetGCParameter(
+ cx,
+ JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
+ pref!(js.mem.gc.incremental.enabled) as u32,
+ );
+ JS_SetGCParameter(
+ cx,
+ JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
+ pref!(js.mem.gc.per_zone.enabled) as u32,
+ );
+ if let Some(val) = in_range(pref!(js.mem.gc.incremental.slice_ms), 0, 100_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
+ }
+ JS_SetGCParameter(
+ cx,
+ JSGCParamKey::JSGC_COMPACTING_ENABLED,
+ pref!(js.mem.gc.compacting.enabled) as u32,
+ );
+
+ if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_time_limit_ms), 0, 10_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
+ }
+ if let Some(val) = in_range(pref!(js.mem.gc.low_frequency_heap_growth), 0, 10_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
+ }
+ if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_heap_growth_min), 0, 10_000) {
+ JS_SetGCParameter(
+ cx,
+ JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
+ val as u32,
+ );
+ }
+ if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_heap_growth_max), 0, 10_000) {
+ JS_SetGCParameter(
+ cx,
+ JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
+ val as u32,
+ );
+ }
+ if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_low_limit_mb), 0, 10_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
+ }
+ if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_high_limit_mb), 0, 10_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
+ }
+ /*if let Some(val) = in_range(pref!(js.mem.gc.allocation_threshold_factor), 0, 10_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_NON_INCREMENTAL_FACTOR, val as u32);
+ }*/
+ /*
+ // JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
+ pref("javascript.options.mem.gc_small_heap_incremental_limit", 140);
+
+ // JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
+ pref("javascript.options.mem.gc_large_heap_incremental_limit", 110);
+ */
+ if let Some(val) = in_range(pref!(js.mem.gc.empty_chunk_count_min), 0, 10_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
+ }
+ if let Some(val) = in_range(pref!(js.mem.gc.empty_chunk_count_max), 0, 10_000) {
+ JS_SetGCParameter(cx, JSGCParamKey::JSGC_MAX_EMPTY_CHUNK_COUNT, val as u32);
+ }
+
+ Runtime {
+ rt: runtime,
+ microtask_queue,
+ job_queue,
+ networking_task_src: (!networking_task_src_ptr.is_null())
+ .then(|| Box::from_raw(networking_task_src_ptr)),
+ }
+ }
+
pub(crate) fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
self.rt.thread_safe_js_context()
}
@@ -495,245 +747,6 @@ impl Drop for JSEngineSetup {
static JS_ENGINE: Mutex<Option<JSEngineHandle>> = Mutex::new(None);
-#[allow(unsafe_code)]
-pub unsafe fn new_child_runtime(
- parent: ParentRuntime,
- networking_task_source: Option<NetworkingTaskSource>,
-) -> Runtime {
- new_rt_and_cx_with_parent(Some(parent), networking_task_source)
-}
-
-#[allow(unsafe_code)]
-pub fn new_rt_and_cx(networking_task_source: Option<NetworkingTaskSource>) -> Runtime {
- unsafe { new_rt_and_cx_with_parent(None, networking_task_source) }
-}
-
-#[allow(unsafe_code)]
-unsafe fn new_rt_and_cx_with_parent(
- parent: Option<ParentRuntime>,
- networking_task_source: Option<NetworkingTaskSource>,
-) -> Runtime {
- LiveDOMReferences::initialize();
- let (cx, runtime) = if let Some(parent) = parent {
- let runtime = RustRuntime::create_with_parent(parent);
- let cx = runtime.cx();
- (cx, runtime)
- } else {
- let runtime = RustRuntime::new(JS_ENGINE.lock().unwrap().as_ref().unwrap().clone());
- (runtime.cx(), runtime)
- };
-
- JS_AddExtraGCRootsTracer(cx, Some(trace_rust_roots), ptr::null_mut());
-
- JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
-
- JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
- JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
-
- // Needed for debug assertions about whether GC is running.
- if cfg!(debug_assertions) {
- JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
- }
-
- if opts::get().debug.gc_profile {
- SetGCSliceCallback(cx, Some(gc_slice_callback));
- }
-
- unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
- true
- }
- unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
- // fixme: return true when the Drop impl for a DOM object has been invoked
- false
- }
- SetDOMCallbacks(cx, &DOM_CALLBACKS);
- SetPreserveWrapperCallbacks(
- cx,
- Some(empty_wrapper_callback),
- Some(empty_has_released_callback),
- );
- // Pre barriers aren't working correctly at the moment
- DisableIncrementalGC(cx);
-
- unsafe extern "C" fn dispatch_to_event_loop(
- closure: *mut c_void,
- dispatchable: *mut JSRunnable,
- ) -> bool {
- let networking_task_src: &NetworkingTaskSource = &*(closure as *mut NetworkingTaskSource);
- let runnable = Runnable(dispatchable);
- let task = task!(dispatch_to_event_loop_message: move || {
- runnable.run(RustRuntime::get(), Dispatchable_MaybeShuttingDown::NotShuttingDown);
- });
-
- networking_task_src.queue_unconditionally(task).is_ok()
- }
-
- let mut networking_task_src_ptr = std::ptr::null_mut();
- if let Some(source) = networking_task_source {
- networking_task_src_ptr = Box::into_raw(Box::new(source));
- InitDispatchToEventLoop(
- cx,
- Some(dispatch_to_event_loop),
- networking_task_src_ptr as *mut c_void,
- );
- }
-
- InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
-
- let microtask_queue = Rc::new(MicrotaskQueue::default());
- let job_queue = CreateJobQueue(
- &JOB_QUEUE_TRAPS,
- &*microtask_queue as *const _ as *const c_void,
- );
- SetJobQueue(cx, job_queue);
- SetPromiseRejectionTrackerCallback(cx, Some(promise_rejection_tracker), ptr::null_mut());
-
- EnsureModuleHooksInitialized(runtime.rt());
-
- set_gc_zeal_options(cx);
-
- // Enable or disable the JITs.
- let cx_opts = &mut *ContextOptionsRef(cx);
- JS_SetGlobalJitCompilerOption(
- cx,
- JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
- pref!(js.baseline_interpreter.enabled) as u32,
- );
- JS_SetGlobalJitCompilerOption(
- cx,
- JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
- pref!(js.baseline_jit.enabled) as u32,
- );
- JS_SetGlobalJitCompilerOption(
- cx,
- JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
- pref!(js.ion.enabled) as u32,
- );
- cx_opts.compileOptions_.asmJSOption_ = if pref!(js.asmjs.enabled) {
- AsmJSOption::Enabled
- } else {
- AsmJSOption::DisabledByAsmJSPref
- };
- let wasm_enabled = pref!(js.wasm.enabled);
- cx_opts.set_wasm_(wasm_enabled);
- if wasm_enabled {
- // If WASM is enabled without setting the buildIdOp,
- // initializing a module will report an out of memory error.
- // https://dxr.mozilla.org/mozilla-central/source/js/src/wasm/WasmTypes.cpp#458
- SetProcessBuildIdOp(Some(servo_build_id));
- }
- cx_opts.set_wasmBaseline_(pref!(js.wasm.baseline.enabled));
- cx_opts.set_wasmIon_(pref!(js.wasm.ion.enabled));
- // TODO: handle js.throw_on_asmjs_validation_failure (needs new Spidermonkey)
- JS_SetGlobalJitCompilerOption(
- cx,
- JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
- pref!(js.native_regex.enabled) as u32,
- );
- JS_SetParallelParsingEnabled(cx, pref!(js.parallel_parsing.enabled));
- JS_SetOffthreadIonCompilationEnabled(cx, pref!(js.offthread_compilation.enabled));
- JS_SetGlobalJitCompilerOption(
- cx,
- JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
- if pref!(js.baseline_jit.unsafe_eager_compilation.enabled) {
- 0
- } else {
- u32::MAX
- },
- );
- JS_SetGlobalJitCompilerOption(
- cx,
- JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
- if pref!(js.ion.unsafe_eager_compilation.enabled) {
- 0
- } else {
- u32::MAX
- },
- );
- // TODO: handle js.discard_system_source.enabled
- // TODO: handle js.asyncstack.enabled (needs new Spidermonkey)
- // TODO: handle js.throw_on_debugee_would_run (needs new Spidermonkey)
- // TODO: handle js.dump_stack_on_debugee_would_run (needs new Spidermonkey)
- // TODO: handle js.shared_memory.enabled
- JS_SetGCParameter(
- cx,
- JSGCParamKey::JSGC_MAX_BYTES,
- in_range(pref!(js.mem.max), 1, 0x100)
- .map(|val| (val * 1024 * 1024) as u32)
- .unwrap_or(u32::MAX),
- );
- // NOTE: This is disabled above, so enabling it here will do nothing for now.
- JS_SetGCParameter(
- cx,
- JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
- pref!(js.mem.gc.incremental.enabled) as u32,
- );
- JS_SetGCParameter(
- cx,
- JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
- pref!(js.mem.gc.per_zone.enabled) as u32,
- );
- if let Some(val) = in_range(pref!(js.mem.gc.incremental.slice_ms), 0, 100_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
- }
- JS_SetGCParameter(
- cx,
- JSGCParamKey::JSGC_COMPACTING_ENABLED,
- pref!(js.mem.gc.compacting.enabled) as u32,
- );
-
- if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_time_limit_ms), 0, 10_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
- }
- if let Some(val) = in_range(pref!(js.mem.gc.low_frequency_heap_growth), 0, 10_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
- }
- if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_heap_growth_min), 0, 10_000) {
- JS_SetGCParameter(
- cx,
- JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
- val as u32,
- );
- }
- if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_heap_growth_max), 0, 10_000) {
- JS_SetGCParameter(
- cx,
- JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
- val as u32,
- );
- }
- if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_low_limit_mb), 0, 10_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
- }
- if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_high_limit_mb), 0, 10_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
- }
- /*if let Some(val) = in_range(pref!(js.mem.gc.allocation_threshold_factor), 0, 10_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_NON_INCREMENTAL_FACTOR, val as u32);
- }*/
- /*
- // JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
- pref("javascript.options.mem.gc_small_heap_incremental_limit", 140);
-
- // JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
- pref("javascript.options.mem.gc_large_heap_incremental_limit", 110);
- */
- if let Some(val) = in_range(pref!(js.mem.gc.empty_chunk_count_min), 0, 10_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
- }
- if let Some(val) = in_range(pref!(js.mem.gc.empty_chunk_count_max), 0, 10_000) {
- JS_SetGCParameter(cx, JSGCParamKey::JSGC_MAX_EMPTY_CHUNK_COUNT, val as u32);
- }
-
- Runtime {
- rt: runtime,
- microtask_queue,
- job_queue,
- networking_task_src: (!networking_task_src_ptr.is_null())
- .then(|| Box::from_raw(networking_task_src_ptr)),
- }
-}
-
fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
if val < min || val >= max {
None
@@ -758,63 +771,6 @@ unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
}
}
-#[allow(unsafe_code)]
-pub unsafe fn get_reports(cx: *mut RawJSContext, path_seg: String) -> Vec<Report> {
- let mut reports = vec![];
-
- let mut stats = ::std::mem::zeroed();
- if CollectServoSizes(cx, &mut stats, Some(get_size)) {
- let mut report = |mut path_suffix, kind, size| {
- let mut path = path![path_seg, "js"];
- path.append(&mut path_suffix);
- reports.push(Report { path, kind, size })
- };
-
- // A note about possibly confusing terminology: the JS GC "heap" is allocated via
- // mmap/VirtualAlloc, which means it's not on the malloc "heap", so we use
- // `ExplicitNonHeapSize` as its kind.
-
- report(
- path!["gc-heap", "used"],
- ReportKind::ExplicitNonHeapSize,
- stats.gcHeapUsed,
- );
-
- report(
- path!["gc-heap", "unused"],
- ReportKind::ExplicitNonHeapSize,
- stats.gcHeapUnused,
- );
-
- report(
- path!["gc-heap", "admin"],
- ReportKind::ExplicitNonHeapSize,
- stats.gcHeapAdmin,
- );
-
- report(
- path!["gc-heap", "decommitted"],
- ReportKind::ExplicitNonHeapSize,
- stats.gcHeapDecommitted,
- );
-
- // SpiderMonkey uses the system heap, not jemalloc.
- report(
- path!["malloc-heap"],
- ReportKind::ExplicitSystemHeapSize,
- stats.mallocHeap,
- );
-
- report(
- path!["non-heap"],
- ReportKind::ExplicitNonHeapSize,
- stats.nonHeap,
- );
- }
-
- reports
-}
-
thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
@@ -923,9 +879,73 @@ pub struct JSContext(*mut RawJSContext);
#[allow(unsafe_code)]
impl JSContext {
- pub unsafe fn from_ptr(raw_js_context: *mut RawJSContext) -> Self {
+ /// Create a new [`JSContext`] object from the given raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// The `RawJSContext` argument must point to a valid `RawJSContext` in memory.
+ pub(crate) unsafe fn from_ptr(raw_js_context: *mut RawJSContext) -> Self {
JSContext(raw_js_context)
}
+
+ #[allow(unsafe_code)]
+ pub(crate) fn get_reports(&self, path_seg: String) -> Vec<Report> {
+ let stats = unsafe {
+ let mut stats = ::std::mem::zeroed();
+ if !CollectServoSizes(self.0, &mut stats, Some(get_size)) {
+ return vec![];
+ }
+ stats
+ };
+
+ let mut reports = vec![];
+ let mut report = |mut path_suffix, kind, size| {
+ let mut path = path![path_seg, "js"];
+ path.append(&mut path_suffix);
+ reports.push(Report { path, kind, size })
+ };
+
+ // A note about possibly confusing terminology: the JS GC "heap" is allocated via
+ // mmap/VirtualAlloc, which means it's not on the malloc "heap", so we use
+ // `ExplicitNonHeapSize` as its kind.
+ report(
+ path!["gc-heap", "used"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapUsed,
+ );
+
+ report(
+ path!["gc-heap", "unused"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapUnused,
+ );
+
+ report(
+ path!["gc-heap", "admin"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapAdmin,
+ );
+
+ report(
+ path!["gc-heap", "decommitted"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapDecommitted,
+ );
+
+ // SpiderMonkey uses the system heap, not jemalloc.
+ report(
+ path!["malloc-heap"],
+ ReportKind::ExplicitSystemHeapSize,
+ stats.mallocHeap,
+ );
+
+ report(
+ path!["non-heap"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.nonHeap,
+ );
+ reports
+ }
}
#[allow(unsafe_code)]
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index ae765b9768d..c0dd80b141b 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -145,8 +145,8 @@ use crate::microtask::{Microtask, MicrotaskQueue};
use crate::realms::enter_realm;
use crate::script_module::ScriptFetchOptions;
use crate::script_runtime::{
- get_reports, new_rt_and_cx, CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptPort,
- ScriptThreadEventCategory, ThreadSafeJSContext,
+ CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptPort, ScriptThreadEventCategory,
+ ThreadSafeJSContext,
};
use crate::task_manager::TaskManager;
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
@@ -170,6 +170,11 @@ pub type ImageCacheMsg = (PipelineId, PendingImageResponse);
thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = const { Cell::new(None) });
+/// # Safety
+///
+/// The `JSTracer` argument must point to a valid `JSTracer` in memory. In addition,
+/// implementors of this method must ensure that all active objects are properly traced
+/// or else the garbage collector may end up collecting objects that are still reachable.
pub unsafe fn trace_thread(tr: *mut JSTracer) {
SCRIPT_THREAD_ROOT.with(|root| {
if let Some(script_thread) = root.get() {
@@ -1291,7 +1296,7 @@ impl ScriptThread {
let boxed_script_sender = Box::new(MainThreadScriptChan(chan.clone()));
- let runtime = new_rt_and_cx(Some(NetworkingTaskSource(
+ let runtime = Runtime::new(Some(NetworkingTaskSource(
boxed_script_sender.clone(),
state.id,
)));
@@ -2944,11 +2949,8 @@ impl ScriptThread {
fn collect_reports(&self, reports_chan: ReportsChan) {
let documents = self.documents.borrow();
let urls = itertools::join(documents.iter().map(|(_, d)| d.url().to_string()), ", ");
- let path_seg = format!("url({})", urls);
-
- let mut reports = vec![];
- reports.extend(unsafe { get_reports(*self.get_cx(), path_seg) });
+ let mut reports = self.get_cx().get_reports(format!("url({})", urls));
for (_, document) in documents.iter() {
document.window().layout().collect_reports(&mut reports);
}