diff --git a/Cargo.lock b/Cargo.lock index cd7c9bed29a..c52afe1eb88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5202,7 +5202,7 @@ dependencies = [ [[package]] name = "mozjs" version = "0.14.1" -source = "git+https://github.com/servo/mozjs?rev=94fb7eb92b7f027fa102c69c7eac55526f1ce124#94fb7eb92b7f027fa102c69c7eac55526f1ce124" +source = "git+https://github.com/servo/mozjs?rev=ccc1423c5dedd86c34c74b7eda7d62be1b26b7ee#ccc1423c5dedd86c34c74b7eda7d62be1b26b7ee" dependencies = [ "bindgen 0.71.1", "cc", @@ -5214,7 +5214,7 @@ dependencies = [ [[package]] name = "mozjs_sys" version = "0.128.13-1" -source = "git+https://github.com/servo/mozjs?rev=94fb7eb92b7f027fa102c69c7eac55526f1ce124#94fb7eb92b7f027fa102c69c7eac55526f1ce124" +source = "git+https://github.com/servo/mozjs?rev=ccc1423c5dedd86c34c74b7eda7d62be1b26b7ee#ccc1423c5dedd86c34c74b7eda7d62be1b26b7ee" dependencies = [ "bindgen 0.71.1", "cc", diff --git a/Cargo.toml b/Cargo.toml index 2ed440ec19f..4e89a037433 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,7 @@ imsz = "0.2" indexmap = { version = "2.10.0", features = ["std"] } ipc-channel = "0.20" itertools = "0.14" -js = { package = "mozjs", git = "https://github.com/servo/mozjs", rev = "94fb7eb92b7f027fa102c69c7eac55526f1ce124" } # branch = for_debugger +js = { package = "mozjs", git = "https://github.com/servo/mozjs", rev = "ccc1423c5dedd86c34c74b7eda7d62be1b26b7ee" } # branch = for_debugger keyboard-types = "0.7" kurbo = "0.11" libc = "0.2" diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 8b5244cc0b0..19029bf7101 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -90,6 +90,7 @@ static JOB_QUEUE_TRAPS: JobQueueTraps = JobQueueTraps { empty: Some(empty), pushNewInterruptQueue: Some(push_new_interrupt_queue), popInterruptQueue: Some(pop_interrupt_queue), + dropInterruptQueues: Some(drop_interrupt_queues), }; static SECURITY_CALLBACKS: JSSecurityCallbacks = JSSecurityCallbacks { @@ -251,34 +252,40 @@ unsafe extern "C" fn empty(extra: *const c_void) -> bool { } #[allow(unsafe_code)] -unsafe extern "C" fn push_new_interrupt_queue(interrupt_queues: *mut c_void) -> *mut c_void { - let mut result = std::ptr::null_mut(); +unsafe extern "C" fn push_new_interrupt_queue(interrupt_queues: *mut c_void) -> *const c_void { + let mut result = std::ptr::null(); wrap_panic(&mut || { - let interrupt_queues = &mut *(interrupt_queues as *mut Vec>); + let mut interrupt_queues = Box::from_raw(interrupt_queues as *mut Vec>); let mut new_queue = Rc::new(MicrotaskQueue::default()); - // TODO: SAFETY: is this ok? It subverts the Rc::get_mut() check - result = Rc::get_mut(&mut new_queue).expect("Guaranteed by usage of `new_queue`") as *mut _ - as *mut c_void; + result = Rc::as_ptr(&new_queue) as *const c_void; interrupt_queues.push(new_queue.clone()); + std::mem::forget(interrupt_queues); }); result } #[allow(unsafe_code)] -unsafe extern "C" fn pop_interrupt_queue(interrupt_queues: *mut c_void) -> *mut c_void { - let mut result = std::ptr::null_mut(); +unsafe extern "C" fn pop_interrupt_queue(interrupt_queues: *mut c_void) -> *const c_void { + let mut result = std::ptr::null(); wrap_panic(&mut || { - let interrupt_queues = &mut *(interrupt_queues as *mut Vec>); + let mut interrupt_queues = Box::from_raw(interrupt_queues as *mut Vec>); let mut popped_queue: Rc = interrupt_queues.pop().expect("Guaranteed by SpiderMonkey?"); // Dangling, but jsglue.cpp will only use this for pointer comparison. - // TODO: SAFETY: is this ok? It subverts the Rc::get_mut() check - result = Rc::get_mut(&mut popped_queue).expect("Guaranteed by usage of `popped_queue`") - as *mut _ as *mut c_void; + result = Rc::as_ptr(&popped_queue) as *const c_void; + std::mem::forget(interrupt_queues); }); result } +#[allow(unsafe_code)] +unsafe extern "C" fn drop_interrupt_queues(interrupt_queues: *mut c_void) { + wrap_panic(&mut || { + let interrupt_queues = Box::from_raw(interrupt_queues as *mut Vec>); + drop(interrupt_queues); + }); +} + /// SM callback for promise job resolution. Adds a promise callback to the current /// global's microtask queue. #[allow(unsafe_code)] @@ -507,8 +514,6 @@ pub(crate) struct Runtime { rt: RustRuntime, /// Our actual microtask queue, which is preserved and untouched by the debugger when running debugger scripts. pub(crate) microtask_queue: Rc, - /// Extra microtask queues created for debugger scripts via AutoDebuggerJobQueueInterruption and saveJobQueue(). - interrupt_microtask_queues: Rc>>, job_queue: *mut JobQueue, networking_task_src: Option>, } @@ -615,15 +620,16 @@ impl Runtime { InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error)); let microtask_queue = Rc::new(MicrotaskQueue::default()); - let mut interrupt_microtask_queues: Rc>> = Rc::new(vec![]); - // TODO: SAFETY: is this ok? It subverts the Rc::get_mut() check - let interrupt_queues_raw = Rc::get_mut(&mut interrupt_microtask_queues) - .expect("Guaranteed by usage of `new_queue`") - as *mut _ as *mut c_void; + + // Extra queues for debugger scripts (“interrupts”) via AutoDebuggerJobQueueInterruption and saveJobQueue(). + // Moved indefinitely to mozjs via CreateJobQueue(), borrowed from mozjs via JobQueueTraps, and moved back from + // mozjs for dropping via DeleteJobQueue(). + let mut interrupt_queues: Box>> = Box::new(vec![]); + let job_queue = CreateJobQueue( &JOB_QUEUE_TRAPS, &*microtask_queue as *const _ as *const c_void, - interrupt_queues_raw, + Box::into_raw(interrupt_queues) as *mut c_void, ); SetJobQueue(cx, job_queue); SetPromiseRejectionTrackerCallback(cx, Some(promise_rejection_tracker), ptr::null_mut()); @@ -768,7 +774,6 @@ impl Runtime { Runtime { rt: runtime, microtask_queue, - interrupt_microtask_queues, job_queue, networking_task_src: (!networking_task_src_ptr.is_null()) .then(|| Box::from_raw(networking_task_src_ptr)), @@ -783,10 +788,10 @@ impl Runtime { impl Drop for Runtime { #[allow(unsafe_code)] fn drop(&mut self) { - // Clear our microtask_queue, but do not touch any interrupt_microtask_queues. - // Those are under SpiderMonkey’s control, even though we are responsible for their lifetimes. + // Clear our main microtask_queue. self.microtask_queue.clear(); + // Delete the RustJobQueue in mozjs, which will destroy our interrupt queues. unsafe { DeleteJobQueue(self.job_queue); }