diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index ae17ebe355e..fa208682396 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -5,6 +5,7 @@ on: branches: - main - releases/** + - dev/oscars-gc permissions: contents: read diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0fa015031fb..1dfd917b2a6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -5,10 +5,12 @@ on: branches: - main - releases/** + - dev/oscars-gc push: branches: - main - releases/** + - dev/oscars-gc merge_group: types: [checks_requested] workflow_dispatch: diff --git a/.github/workflows/test262.yml b/.github/workflows/test262.yml index 6797efa033d..d569e061ac5 100644 --- a/.github/workflows/test262.yml +++ b/.github/workflows/test262.yml @@ -5,6 +5,7 @@ on: branches: - main - releases/** + - dev/oscars-gc permissions: contents: read diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index f9538775ebd..676a8602965 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -5,10 +5,12 @@ on: branches: - main - releases/** + - dev/oscars-gc push: branches: - main - releases/** + - dev/oscars-gc merge_group: types: [checks_requested] diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index 7b0f9246640..f82bc7f4483 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -320,10 +320,8 @@ impl Eval { compiler.compile_statement_list(body.statements(), true, false); - let code_block = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - compiler.finish(), - ); + let finished = compiler.finish(); + let code_block = Gc::new(&context.gc(), finished); // Strict calls don't need extensions, since all strict eval calls push a new // function environment before evaluating. @@ -350,9 +348,11 @@ impl Eval { { let frame = context.vm.frame_mut(); let global = frame.realm.environment(); - frame - .environments - .push_lexical(lexical_scope.num_bindings_non_local(), global); + frame.environments.push_lexical( + lexical_scope.num_bindings_non_local(), + global, + unsafe { boa_gc::MutationContext::dummy() }, + ); } context diff --git a/core/engine/src/builtins/finalization_registry/mod.rs b/core/engine/src/builtins/finalization_registry/mod.rs index 4ec7ba0c0f3..86395727792 100644 --- a/core/engine/src/builtins/finalization_registry/mod.rs +++ b/core/engine/src/builtins/finalization_registry/mod.rs @@ -158,10 +158,7 @@ impl BuiltInConstructor for FinalizationRegistry { }, ); - let weak_registry = WeakGc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - registry.inner(), - ); + let weak_registry = WeakGc::new(&context.gc(), registry.inner()); { async fn inner_cleanup( @@ -205,7 +202,7 @@ impl FinalizationRegistry { /// [`FinalizationRegistry.prototype.register ( target, heldValue [ , unregisterToken ] )`][spec] /// /// [spec]: https://tc39.es/ecma262/sec-finalization-registry.prototype.register - fn register(this: &JsValue, args: &[JsValue], _context: &mut Context) -> JsResult { + fn register(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let finalizationRegistry be the this value. // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). let this = this.as_object(); @@ -257,10 +254,7 @@ impl FinalizationRegistry { // // TODO: support Symbols let unregister_token = match unregister_token.variant() { - JsVariant::Object(obj) => Some(WeakGc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - obj.inner(), - )), + JsVariant::Object(obj) => Some(WeakGc::new(&context.gc(), obj.inner())), // b. Set unregisterToken to empty. JsVariant::Undefined => None, // a. If unregisterToken is not undefined, throw a TypeError exception. @@ -275,7 +269,7 @@ impl FinalizationRegistry { // 6. Let cell be the Record { [[WeakRefTarget]]: target, [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }. let cell = RegistryCell { target: Ephemeron::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &context.gc(), target_obj.inner(), CleanupSignaler(Cell::new(Some( registry.cleanup_notifier.clone().downgrade(), @@ -295,7 +289,7 @@ impl FinalizationRegistry { /// [`FinalizationRegistry.prototype.unregister ( unregisterToken )`][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-finalization-registry.prototype.unregister - fn unregister(this: &JsValue, args: &[JsValue], _context: &mut Context) -> JsResult { + fn unregister(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let finalizationRegistry be the this value. // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). let this = this.as_object(); @@ -338,20 +332,16 @@ impl FinalizationRegistry { // a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then if let Some(tok) = cell.unregister_token.as_ref() - && let Some(tok) = tok.upgrade(&unsafe { boa_gc::MutationContext::dummy() }) + && let Some(tok) = tok.upgrade(&context.gc()) && Gc::ptr_eq(&tok, unregister_token) { // i. Remove cell from finalizationRegistry.[[Cells]]. let cell = registry.cells.swap_remove(i); - let _key = cell - .target - .key(&unsafe { boa_gc::MutationContext::dummy() }); + let _key = cell.target.key(&context.gc()); // TODO: it might be better to add a special ref for the value that // also preserves the original key instead. - cell.target - .value(&unsafe { boa_gc::MutationContext::dummy() }) - .and_then(|v| v.0.take()); + cell.target.value(&context.gc()).and_then(|v| v.0.take()); // ii. Set removed to true. removed = true; diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index d7585a76994..25bf52b8272 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -1073,7 +1073,9 @@ pub(crate) fn function_call( if has_binding_identifier { let frame = context.vm.frame_mut(); let global = frame.realm.environment(); - let index = frame.environments.push_lexical(1, global); + let index = frame + .environments + .push_lexical(1, global, unsafe { boa_gc::MutationContext::dummy() }); frame.environments.put_lexical_value( BindingLocatorScope::Stack(index), 0, @@ -1091,6 +1093,7 @@ pub(crate) fn function_call( scope, FunctionSlots::new(this, function_object.clone(), None), global, + unsafe { boa_gc::MutationContext::dummy() }, ); } @@ -1181,7 +1184,9 @@ fn function_construct( if has_binding_identifier { let frame = context.vm.frame_mut(); let global = frame.realm.environment(); - let index = frame.environments.push_lexical(1, global); + let index = frame + .environments + .push_lexical(1, global, unsafe { boa_gc::MutationContext::dummy() }); frame.environments.put_lexical_value( BindingLocatorScope::Stack(index), 0, @@ -1210,6 +1215,7 @@ fn function_construct( ), ), global, + unsafe { boa_gc::MutationContext::dummy() }, ); } diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index be01ebb234b..a4387b619c4 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -307,10 +307,8 @@ impl Json { SourcePath::Json, ); compiler.compile_statement_list(script.statements(), true, false); - Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - compiler.finish(), - ) + let finished = compiler.finish(); + Gc::new(&context.gc(), finished) }; let realm = context.realm().clone(); diff --git a/core/engine/src/builtins/promise/mod.rs b/core/engine/src/builtins/promise/mod.rs index e79b52fc256..f03ef9500da 100644 --- a/core/engine/src/builtins/promise/mod.rs +++ b/core/engine/src/builtins/promise/mod.rs @@ -244,7 +244,7 @@ impl PromiseCapability { // 2. NOTE: C is assumed to be a constructor function that supports the parameter conventions of the Promise constructor (see 27.2.3.1). // 3. Let promiseCapability be the PromiseCapability Record { [[Promise]]: undefined, [[Resolve]]: undefined, [[Reject]]: undefined }. let promise_capability = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &context.gc(), GcRefCell::new(RejectResolve { reject: JsValue::undefined(), resolve: JsValue::undefined(), @@ -656,10 +656,7 @@ impl Promise { } // 1. Let values be a new empty List. - let values = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - GcRefCell::new(Vec::new()), - ); + let values = Gc::new(&context.gc(), GcRefCell::new(Vec::new())); // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. let remaining_elements_count = Rc::new(Cell::new(1)); @@ -874,10 +871,7 @@ impl Promise { } // 1. Let values be a new empty List. - let values = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - GcRefCell::new(Vec::new()), - ); + let values = Gc::new(&context.gc(), GcRefCell::new(Vec::new())); // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. let remaining_elements_count = Rc::new(Cell::new(1)); @@ -1244,10 +1238,7 @@ impl Promise { let keys = Rc::new(RefCell::new(Vec::new())); // 3. Let values be a new empty List. - let values = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - GcRefCell::new(Vec::new()), - ); + let values = Gc::new(&context.gc(), GcRefCell::new(Vec::new())); // 4. Let remainingElementsCount be the Record { [[Value]]: 1 }. let remaining_elements_count = Rc::new(Cell::new(1)); @@ -1557,10 +1548,7 @@ impl Promise { } // 1. Let errors be a new empty List. - let errors = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - GcRefCell::new(Vec::new()), - ); + let errors = Gc::new(&context.gc(), GcRefCell::new(Vec::new())); // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. let remaining_elements_count = Rc::new(Cell::new(1)); @@ -2460,10 +2448,7 @@ impl Promise { // 1. Let alreadyResolved be the Record { [[Value]]: false }. // 5. Set resolve.[[Promise]] to promise. // 6. Set resolve.[[AlreadyResolved]] to alreadyResolved. - let promise = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - Cell::new(Some(promise.clone())), - ); + let promise = Gc::new(&context.gc(), Cell::new(Some(promise.clone()))); // 2. Let stepsResolve be the algorithm steps defined in Promise Resolve Functions. // 3. Let lengthResolve be the number of non-optional parameters of the function definition in Promise Resolve Functions. diff --git a/core/engine/src/builtins/weak/weak_ref.rs b/core/engine/src/builtins/weak/weak_ref.rs index 77f136812ac..d72b8c74f00 100644 --- a/core/engine/src/builtins/weak/weak_ref.rs +++ b/core/engine/src/builtins/weak/weak_ref.rs @@ -87,7 +87,7 @@ impl BuiltInConstructor for WeakRef { let weak_ref = JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), prototype, - WeakGc::new(&unsafe { boa_gc::MutationContext::dummy() }, target.inner()), + WeakGc::new(&context.gc(), target.inner()), ); // 4. Perform AddToKeptObjects(target). @@ -124,7 +124,7 @@ impl WeakRef { // https://tc39.es/ecma262/multipage/managing-memory.html#sec-weakrefderef // 1. Let target be weakRef.[[WeakRefTarget]]. // 2. If target is not empty, then - if let Some(object) = weak_ref.upgrade(&unsafe { boa_gc::MutationContext::dummy() }) { + if let Some(object) = weak_ref.upgrade(&context.gc()) { let object = JsObject::from(object); // a. Perform AddToKeptObjects(target). diff --git a/core/engine/src/builtins/weak_map/mod.rs b/core/engine/src/builtins/weak_map/mod.rs index adff36ecbfc..c18050c572d 100644 --- a/core/engine/src/builtins/weak_map/mod.rs +++ b/core/engine/src/builtins/weak_map/mod.rs @@ -97,7 +97,7 @@ impl BuiltInConstructor for WeakMap { let map = JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), prototype, - NativeWeakMap::new(&unsafe { boa_gc::MutationContext::dummy() }), + NativeWeakMap::new(&context.gc()), ) .upcast(); @@ -171,7 +171,7 @@ impl WeakMap { pub(crate) fn get( this: &JsValue, args: &[JsValue], - _context: &mut Context, + context: &mut Context, ) -> JsResult { // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]). @@ -194,7 +194,7 @@ impl WeakMap { // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]]. // 6. Return undefined. if let Some(entry) = map.get(key.inner()) - && let Some(val) = entry.value(&unsafe { boa_gc::MutationContext::dummy() }) + && let Some(val) = entry.value(&context.gc()) { Ok(val.clone()) } else { @@ -298,7 +298,7 @@ impl WeakMap { pub(crate) fn get_or_insert( this: &JsValue, args: &[JsValue], - _context: &mut Context, + context: &mut Context, ) -> JsResult { // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]). @@ -325,7 +325,7 @@ impl WeakMap { // 4. For each Record { [[Key]], [[Value]] } p of M.[[WeakMapData]] if let Some(existing) = map.borrow().data().get(key.inner()) - && let Some(value) = existing.value(&unsafe { boa_gc::MutationContext::dummy() }) + && let Some(value) = existing.value(&context.gc()) { // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]]. return Ok(value.clone()); @@ -387,7 +387,7 @@ impl WeakMap { // 5. For each Record { [[Key]], [[Value]] } p of M.[[WeakMapData]] if let Some(existing) = map.borrow().data().get(key_obj.inner()) - && let Some(value) = existing.value(&unsafe { boa_gc::MutationContext::dummy() }) + && let Some(value) = existing.value(&context.gc()) { // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]]. return Ok(value.clone()); diff --git a/core/engine/src/builtins/weak_set/mod.rs b/core/engine/src/builtins/weak_set/mod.rs index 50647b16881..1d0a64c11e1 100644 --- a/core/engine/src/builtins/weak_set/mod.rs +++ b/core/engine/src/builtins/weak_set/mod.rs @@ -86,7 +86,7 @@ impl BuiltInConstructor for WeakSet { let weak_set = JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), prototype, - NativeWeakSet::new(&unsafe { boa_gc::MutationContext::dummy() }), + NativeWeakSet::new(&context.gc()), ) .upcast(); diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index 78453d26078..d468975e253 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -463,6 +463,20 @@ impl Context { &self.vm.frame().realm } + /// Returns [`boa_gc::MutationContext`] to allocate on the Gc heap + /// (eg. for [`Gc::new`]) + /// + /// # Safety + /// Uses `dummy()` as a temporary bridge during the oscars GC migration. + /// Todo: replace with a real branding token in future + #[inline] + #[must_use] + pub fn gc(&self) -> boa_gc::MutationContext<'static, '_> { + // SAFETY: `MutationContext` is a ZST phantom type, this is sound + // under boa's single-threaded GC invariant until migration is complete + unsafe { boa_gc::MutationContext::dummy() } + } + /// Set the value of trace on the context #[cfg(feature = "trace")] #[inline] diff --git a/core/engine/src/environments/runtime/mod.rs b/core/engine/src/environments/runtime/mod.rs index 8f7b2dd26c6..e15db81bd04 100644 --- a/core/engine/src/environments/runtime/mod.rs +++ b/core/engine/src/environments/runtime/mod.rs @@ -214,13 +214,14 @@ impl EnvironmentStack { &mut self, bindings_count: u32, global: &Gc<'static, DeclarativeEnvironment>, + gc: boa_gc::MutationContext<'static, '_>, ) -> u32 { let (poisoned, with) = self.compute_poisoned_with(global); let index = self.depth; self.push_env(Environment::Declarative(Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &gc, DeclarativeEnvironment::new( DeclarativeEnvironmentKind::Lexical(LexicalEnvironment::new(bindings_count)), poisoned, @@ -237,13 +238,14 @@ impl EnvironmentStack { scope: Scope, function_slots: FunctionSlots, global: &Gc<'static, DeclarativeEnvironment>, + gc: boa_gc::MutationContext<'static, '_>, ) { let num_bindings = scope.num_bindings_non_local(); let (poisoned, with) = self.compute_poisoned_with(global); self.push_env(Environment::Declarative(Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &gc, DeclarativeEnvironment::new( DeclarativeEnvironmentKind::Function(FunctionEnvironment::new( num_bindings, @@ -257,10 +259,10 @@ impl EnvironmentStack { } /// Push a module environment on the environments stack. - pub(crate) fn push_module(&mut self, scope: Scope) { + pub(crate) fn push_module(&mut self, scope: Scope, gc: boa_gc::MutationContext<'static, '_>) { let num_bindings = scope.num_bindings_non_local(); self.push_env(Environment::Declarative(Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &gc, DeclarativeEnvironment::new( DeclarativeEnvironmentKind::Module(ModuleEnvironment::new(num_bindings, scope)), false, diff --git a/core/engine/src/module/mod.rs b/core/engine/src/module/mod.rs index e34f8a8329d..cdba3f7ec17 100644 --- a/core/engine/src/module/mod.rs +++ b/core/engine/src/module/mod.rs @@ -287,7 +287,7 @@ impl Module { Ok(Self { inner: Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &context.gc(), ModuleRepr { realm, namespace: GcRefCell::default(), @@ -319,7 +319,7 @@ impl Module { Self { inner: Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &context.gc(), ModuleRepr { realm, namespace: GcRefCell::default(), @@ -826,10 +826,7 @@ fn into_js_module() { let bar_count = Rc::new(RefCell::new(0)); let dad_count = Rc::new(RefCell::new(0)); - context.insert_data(Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - GcRefCell::new(JsValue::undefined()), - )); + context.insert_data(Gc::new(&context.gc(), GcRefCell::new(JsValue::undefined()))); let module = unsafe { vec![ diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index 80fe607c6d6..b84319f6893 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -1824,17 +1824,17 @@ impl SourceTextModule { compiler.compile_module_item_list(source.items()); ( - Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - compiler.finish(), - ), + { + let finished = compiler.finish(); + Gc::new(&context.gc(), finished) + }, functions, ) }; // 8. Let moduleContext be a new ECMAScript code execution context. let mut envs = EnvironmentStack::new(); - envs.push_module(source.scope().clone()); + envs.push_module(source.scope().clone(), context.gc()); drop(status); // 9. Set the Function of moduleContext to null. diff --git a/core/engine/src/module/synthetic.rs b/core/engine/src/module/synthetic.rs index 613561c9558..a9412b05ed7 100644 --- a/core/engine/src/module/synthetic.rs +++ b/core/engine/src/module/synthetic.rs @@ -338,13 +338,11 @@ impl SyntheticModule { module_scope.escape_all_bindings(); - let cb = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - compiler.finish(), - ); + let finished = compiler.finish(); + let cb = Gc::new(&context.gc(), finished); let mut envs = EnvironmentStack::new(); - envs.push_module(module_scope); + envs.push_module(module_scope, context.gc()); for locator in exports { // b. Perform ! env.InitializeBinding(exportName, undefined). diff --git a/core/engine/src/object/builtins/jspromise.rs b/core/engine/src/object/builtins/jspromise.rs index 85aa1b26b2b..c65e1d1c158 100644 --- a/core/engine/src/object/builtins/jspromise.rs +++ b/core/engine/src/object/builtins/jspromise.rs @@ -1094,7 +1094,7 @@ impl JsPromise { } let state = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &context.gc(), GcRefCell::new(Inner { result: None, task: None, diff --git a/core/engine/src/object/builtins/jsweakmap.rs b/core/engine/src/object/builtins/jsweakmap.rs index e752f696b95..d120be65a48 100644 --- a/core/engine/src/object/builtins/jsweakmap.rs +++ b/core/engine/src/object/builtins/jsweakmap.rs @@ -30,7 +30,7 @@ impl JsWeakMap { inner: JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), context.intrinsics().constructors().weak_map().prototype(), - NativeWeakMap::new(&unsafe { boa_gc::MutationContext::dummy() }), + NativeWeakMap::new(&context.gc()), ) .upcast(), } diff --git a/core/engine/src/object/builtins/jsweakset.rs b/core/engine/src/object/builtins/jsweakset.rs index 13d14095cc8..07a53fd4264 100644 --- a/core/engine/src/object/builtins/jsweakset.rs +++ b/core/engine/src/object/builtins/jsweakset.rs @@ -30,7 +30,7 @@ impl JsWeakSet { inner: JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), context.intrinsics().constructors().weak_set().prototype(), - NativeWeakSet::new(&unsafe { boa_gc::MutationContext::dummy() }), + NativeWeakSet::new(&context.gc()), ) .upcast(), } diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index f11dbc61168..e7d5a36144e 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -105,7 +105,7 @@ impl Script { Ok(Self { inner: Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &context.gc(), Inner { realm: realm.unwrap_or_else(|| context.realm().clone()), phase: GcRefCell::new(ScriptPhase::Ast(code)), @@ -162,10 +162,8 @@ impl Script { compiler.global_declaration_instantiation(source); compiler.compile_statement_list(source.statements(), true, false); - Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - compiler.finish(), - ) + let finished = compiler.finish(); + Gc::new(&context.gc(), finished) }; *self.inner.phase.borrow_mut() = ScriptPhase::Codeblock(cb.clone()); diff --git a/core/engine/src/vm/opcode/await/mod.rs b/core/engine/src/vm/opcode/await/mod.rs index ad2c5fcbd54..fda3a4b1746 100644 --- a/core/engine/src/vm/opcode/await/mod.rs +++ b/core/engine/src/vm/opcode/await/mod.rs @@ -56,10 +56,7 @@ impl Await { let r#gen = GeneratorContext::from_current(context, None); - let captures = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, - Cell::new(Some(r#gen)), - ); + let captures = Gc::new(&context.gc(), Cell::new(Some(r#gen))); // 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called: // 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »). diff --git a/core/engine/src/vm/opcode/push/environment.rs b/core/engine/src/vm/opcode/push/environment.rs index 8f49f65b2c6..251d44d8368 100644 --- a/core/engine/src/vm/opcode/push/environment.rs +++ b/core/engine/src/vm/opcode/push/environment.rs @@ -22,7 +22,9 @@ impl PushScope { let global = frame.realm.environment(); frame .environments - .push_lexical(scope.num_bindings_non_local(), global); + .push_lexical(scope.num_bindings_non_local(), global, unsafe { + boa_gc::MutationContext::dummy() + }); } } @@ -82,7 +84,7 @@ impl PushPrivateEnvironment { let ptr: *const _ = class.as_ref(); let environment = Gc::new( - &unsafe { boa_gc::MutationContext::dummy() }, + &context.gc(), PrivateEnvironment::new(ptr.cast::<()>() as usize, names), ); diff --git a/core/gc/src/cell.rs b/core/gc/src/cell.rs index 674cf86ddb8..a07ce4ce731 100644 --- a/core/gc/src/cell.rs +++ b/core/gc/src/cell.rs @@ -59,13 +59,13 @@ impl BorrowFlag { /// - This method will panic after incrementing if the borrow count overflows. #[inline] fn add_reading(self) -> Self { - assert!(self.borrowed() != BorrowState::Writing); + assert_ne!(self.borrowed(), BorrowState::Writing); let flags = Self(self.0 + 1); // This will fail if the borrow count overflows, which shouldn't happen, // but let's be safe { - assert!(flags.borrowed() == BorrowState::Reading); + assert_eq!(flags.borrowed(), BorrowState::Reading); } flags } @@ -75,7 +75,7 @@ impl BorrowFlag { /// # Panic /// - This method will panic if the current `BorrowState` is not reading. fn sub_reading(self) -> Self { - assert!(self.borrowed() == BorrowState::Reading); + assert_eq!(self.borrowed(), BorrowState::Reading); Self(self.0 - 1) } } @@ -261,7 +261,7 @@ struct BorrowGcRef<'a> { impl Drop for BorrowGcRef<'_> { fn drop(&mut self) { - debug_assert!(self.borrow.get().borrowed() == BorrowState::Reading); + debug_assert_eq!(self.borrow.get().borrowed(), BorrowState::Reading); self.borrow.set(self.borrow.get().sub_reading()); } } @@ -411,7 +411,7 @@ struct BorrowGcRefMut<'a> { impl Drop for BorrowGcRefMut<'_> { fn drop(&mut self) { - debug_assert!(self.borrow.get().borrowed() == BorrowState::Writing); + debug_assert_eq!(self.borrow.get().borrowed(), BorrowState::Writing); self.borrow.set(BorrowFlag(UNUSED)); } } diff --git a/core/gc/src/lib.rs b/core/gc/src/lib.rs index dec09a9c077..0e09c9cde4c 100644 --- a/core/gc/src/lib.rs +++ b/core/gc/src/lib.rs @@ -14,6 +14,10 @@ clippy::redundant_pub_crate, clippy::let_unit_value )] +#![cfg_attr( + feature = "oscars_backend", + allow(unused_crate_dependencies, unused_extern_crates) +)] extern crate self as boa_gc; @@ -49,10 +53,27 @@ pub use internals::GcBox; pub use pointers::{Ephemeron, Gc, GcErased, MutationContext, WeakGc, WeakMap}; #[cfg(feature = "oscars_backend")] -pub use oscars::null_collector_branded::{ +pub use oscars::collectors::null_collector_branded::{ Ephemeron, Finalize, Gc, GcRefCell, MutationContext, Root, Trace, Tracer, WeakGc, }; +#[cfg(feature = "oscars_backend")] +/// Implements an empty `Trace` trait for the specified types +#[macro_export] +macro_rules! empty_trace { + () => { + #[inline] + unsafe fn trace(&self, _tracer: &mut $crate::Tracer<'_>) {} + }; + ($($T:ty),* $(,)?) => { + $( + unsafe impl $crate::Trace for $T { + $crate::empty_trace!(); + } + )* + }; +} + #[cfg(not(feature = "oscars_backend"))] pub(crate) mod boa_allocator; diff --git a/core/interner/src/sym.rs b/core/interner/src/sym.rs index e60e7a3459d..ccd16e36589 100644 --- a/core/interner/src/sym.rs +++ b/core/interner/src/sym.rs @@ -1,4 +1,4 @@ -use boa_gc::{Finalize, Trace, empty_trace}; +use boa_gc::{Finalize, Trace}; use boa_macros::static_syms; use core::num::NonZeroUsize; @@ -13,17 +13,15 @@ use core::num::NonZeroUsize; )] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[allow(clippy::unsafe_derive_deserialize)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Finalize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Finalize, Trace)] +#[boa_gc(unsafe_no_drop)] pub struct Sym { + // SAFETY: `NonZeroUsize` is a constrained `usize`, and all primitive types + // don't need to be traced by the garbage collector. + #[unsafe_ignore_trace] value: NonZeroUsize, } -// SAFETY: `NonZeroUsize` is a constrained `usize`, and all primitive types don't need to be traced -// by the garbage collector. -unsafe impl Trace for Sym { - empty_trace!(); -} - impl Sym { /// Creates a new [`Sym`] from the provided `value`, or returns `None` if `index` is zero. pub(super) fn new(value: usize) -> Option { diff --git a/core/string/src/builder.rs b/core/string/src/builder.rs index b8b426b4aed..843c27861e2 100644 --- a/core/string/src/builder.rs +++ b/core/string/src/builder.rs @@ -771,14 +771,18 @@ impl<'seg, 'ref_str: 'seg> CommonJsStringBuilder<'seg> { let mut builder = Latin1JsStringBuilder::new(); for seg in &self.segments { match seg { - Segment::String(s) => { + Segment::String(s) => + { + #[allow(clippy::question_mark)] if let Some(data) = s.as_str().as_latin1() { builder.extend_from_slice(data); } else { return None; } } - Segment::Str(s) => { + Segment::Str(s) => + { + #[allow(clippy::question_mark)] if let Some(data) = s.as_latin1() { builder.extend_from_slice(data); } else { diff --git a/core/string/src/tests.rs b/core/string/src/tests.rs index 2315a558937..0a4f80a602b 100644 --- a/core/string/src/tests.rs +++ b/core/string/src/tests.rs @@ -402,7 +402,7 @@ fn clone_builder() { // clone_from(empty) == origin(empty) let mut cloned_from = Latin1JsStringBuilder::new(); cloned_from.clone_from(&empty_origin); - assert!(cloned_from.capacity() == 0); + assert_eq!(cloned_from.capacity(), 0); assert_eq!(empty_origin, cloned_from); // utf16 builder -- test @@ -432,7 +432,7 @@ fn clone_builder() { // clone_from(empty) == origin(empty) let mut cloned_from = Utf16JsStringBuilder::new(); cloned_from.clone_from(&empty_origin); - assert!(cloned_from.capacity() == 0); + assert_eq!(cloned_from.capacity(), 0); assert_eq!(empty_origin, cloned_from); }