diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d7ff04fe..0f179421c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -321,7 +321,7 @@ this is not a trivial piece of work. Right now a function in the engine might look like this: ```rs -fn call(agent: &mut Agent, obj: Value, mut gc: Gc) -> JsResult { +fn call<'gc>(agent: &mut Agent, obj: Value, mut gc: GcScope<'gc, '_>) -> JsResult> { if !obj.is_object() { return Err(agent.throw_error(agent, "Not object", gc)); } @@ -411,7 +411,7 @@ But what about when we call some mutable function and need to keep a reference to a stack value past that call? This is how that would look: ```rs -fn call(agent: &mut Agent, value: Value, mut gc: Gc) -> JsResult { +fn call<'gc>(agent: &mut Agent, value: Value, mut gc: GcScope<'gc, '_>) -> JsResult> { let value = unsafe { value.bind(agent) }; let kept_value: Global> = value.make_global(value); other_call(agent, gc.reborrow(), value.into_register())?; diff --git a/nova_cli/src/helper.rs b/nova_cli/src/helper.rs index b7d3260c5..f7d41c6cf 100644 --- a/nova_cli/src/helper.rs +++ b/nova_cli/src/helper.rs @@ -1,18 +1,30 @@ -use nova_vm::ecmascript::{ - builtins::{create_builtin_function, ArgumentsList, Behaviour, BuiltinFunctionArgs}, - execution::{agent::ExceptionType, Agent, JsResult}, - types::{ - InternalMethods, IntoValue, Object, OrdinaryObject, PropertyDescriptor, PropertyKey, - String, Value, +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use nova_vm::{ + ecmascript::{ + builtins::{create_builtin_function, ArgumentsList, Behaviour, BuiltinFunctionArgs}, + execution::{agent::ExceptionType, Agent, JsResult}, + types::{ + InternalMethods, IntoValue, Object, OrdinaryObject, PropertyDescriptor, PropertyKey, + String, Value, + }, }, + engine::context::{Bindable, GcScope}, }; -use nova_vm::engine::context::GcScope; use oxc_diagnostics::OxcDiagnostic; /// Initialize the global object with the built-in functions. pub fn initialize_global_object(agent: &mut Agent, global: Object, mut gc: GcScope) { + let global = global.scope(agent, gc.nogc()); // `print` function - fn print(agent: &mut Agent, _this: Value, args: ArgumentsList, gc: GcScope) -> JsResult { + fn print<'gc>( + agent: &mut Agent, + _this: Value, + args: ArgumentsList, + gc: GcScope<'gc, '_>, + ) -> JsResult> { if args.len() == 0 { println!(); } else { @@ -22,12 +34,12 @@ pub fn initialize_global_object(agent: &mut Agent, global: Object, mut gc: GcSco } // 'readTextFile' function - fn read_text_file( + fn read_text_file<'gc>( agent: &mut Agent, _: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { if args.len() != 1 { return Err(agent.throw_exception_with_static_message( ExceptionType::Error, @@ -45,7 +57,7 @@ pub fn initialize_global_object(agent: &mut Agent, global: Object, mut gc: GcSco let file = std::fs::read_to_string(path.as_str(agent)) .map_err(|e| agent.throw_exception(ExceptionType::Error, e.to_string(), gc.nogc()))?; - Ok(String::from_string(agent, file, gc.nogc()).into_value()) + Ok(String::from_string(agent, file, gc.into_nogc()).into_value()) } let function = create_builtin_function( @@ -56,11 +68,12 @@ pub fn initialize_global_object(agent: &mut Agent, global: Object, mut gc: GcSco ); let property_key = PropertyKey::from_static_str(agent, "print", gc.nogc()).unbind(); global + .get(agent) .internal_define_own_property( agent, property_key, PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), writable: Some(true), enumerable: Some(false), configurable: Some(true), @@ -78,11 +91,12 @@ pub fn initialize_global_object(agent: &mut Agent, global: Object, mut gc: GcSco ); let property_key = PropertyKey::from_static_str(agent, "readTextFile", gc.nogc()).unbind(); global + .get(agent) .internal_define_own_property( agent, property_key, PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), writable: Some(true), enumerable: Some(false), configurable: Some(true), @@ -95,12 +109,12 @@ pub fn initialize_global_object(agent: &mut Agent, global: Object, mut gc: GcSco pub fn initialize_global_object_with_internals(agent: &mut Agent, global: Object, mut gc: GcScope) { // `detachArrayBuffer` function - fn detach_array_buffer( + fn detach_array_buffer<'gc>( agent: &mut Agent, _this: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let Value::ArrayBuffer(array_buffer) = args.get(0) else { return Err(agent.throw_exception_with_static_message( ExceptionType::Error, @@ -112,12 +126,12 @@ pub fn initialize_global_object_with_internals(agent: &mut Agent, global: Object Ok(Value::Undefined) } - fn create_realm( + fn create_realm<'gc>( agent: &mut Agent, _this: Value, _args: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { let create_global_object: Option fn(&mut Agent, GcScope<'a, '_>) -> Object<'a>> = None; let create_global_this_value: Option< @@ -128,7 +142,7 @@ pub fn initialize_global_object_with_internals(agent: &mut Agent, global: Object create_global_this_value, Some(initialize_global_object_with_internals), ); - Ok(realm.global_object(agent).into_value()) + Ok(realm.global_object(agent).into_value().unbind()) } initialize_global_object(agent, global, gc.reborrow()); @@ -163,7 +177,7 @@ pub fn initialize_global_object_with_internals(agent: &mut Agent, global: Object agent, property_key, PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), writable: Some(true), enumerable: Some(false), configurable: Some(true), @@ -186,7 +200,7 @@ pub fn initialize_global_object_with_internals(agent: &mut Agent, global: Object agent, property_key, PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), writable: Some(true), enumerable: Some(false), configurable: Some(true), diff --git a/nova_cli/src/main.rs b/nova_cli/src/main.rs index 29fd28e84..a52415b8f 100644 --- a/nova_cli/src/main.rs +++ b/nova_cli/src/main.rs @@ -15,12 +15,12 @@ use nova_vm::{ ecmascript::{ execution::{ agent::{GcAgent, HostHooks, Job, Options}, - Agent, + Agent, JsResult, }, scripts_and_modules::script::{parse_script, script_evaluation}, - types::{Object, String as JsString}, + types::{Object, String as JsString, Value}, }, - engine::context::GcScope, + engine::context::{Bindable, GcScope}, }; use oxc_parser::Parser; use oxc_semantic::{SemanticBuilder, SemanticBuilderReturn}; @@ -90,6 +90,10 @@ impl Debug for CliHostHooks { } impl CliHostHooks { + fn has_promise_jobs(&self) -> bool { + !self.promise_job_queue.borrow().is_empty() + } + fn pop_promise_job(&self) -> Option { self.promise_job_queue.borrow_mut().pop_front() } @@ -186,17 +190,34 @@ fn main() -> Result<(), Box> { exit_with_parse_errors(errors, &path, source_text) } }; - let mut result = script_evaluation(agent, script, gc.reborrow()); + let result = script_evaluation(agent, script, gc.reborrow()); - if result.is_ok() { - while let Some(job) = host_hooks.pop_promise_job() { - if let Err(err) = job.run(agent, gc.reborrow()) { - result = Err(err); - break; + fn run_microtask_queue<'gc>( + agent: &mut Agent, + host_hooks: &CliHostHooks, + result: JsResult, + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + match result.bind(gc.nogc()) { + Ok(result) => { + let ok_result = result.unbind().scope(agent, gc.nogc()); + while let Some(job) = host_hooks.pop_promise_job() { + job.run(agent, gc.reborrow())?; + } + Ok(ok_result.get(agent).bind(gc.into_nogc())) } + Err(_) => result.bind(gc.into_nogc()), } } + let result = if host_hooks.has_promise_jobs() { + run_microtask_queue(agent, host_hooks, result.unbind(), gc.reborrow()) + .unbind() + .bind(gc.nogc()) + } else { + result + }; + match result { Ok(result) => { if verbose { diff --git a/nova_vm/src/ecmascript/abstract_operations/operations_on_iterator_objects.rs b/nova_vm/src/ecmascript/abstract_operations/operations_on_iterator_objects.rs index c8e2d6aa9..f59f9493d 100644 --- a/nova_vm/src/ecmascript/abstract_operations/operations_on_iterator_objects.rs +++ b/nova_vm/src/ecmascript/abstract_operations/operations_on_iterator_objects.rs @@ -11,12 +11,18 @@ use crate::{ type_conversion::to_boolean, }, builtins::{ordinary::ordinary_object_create_with_intrinsics, ArgumentsList}, - execution::{agent::ExceptionType, Agent, JsResult, ProtoIntrinsics}, - types::{Function, Object, PropertyDescriptor, PropertyKey, Value, BUILTIN_STRING_MEMORY}, + execution::{ + agent::{ExceptionType, JsError}, + Agent, JsResult, ProtoIntrinsics, + }, + types::{ + Function, IntoValue, Object, PropertyDescriptor, PropertyKey, Value, + BUILTIN_STRING_MEMORY, + }, }, engine::{ - context::{GcScope, NoGcScope}, - TryResult, + context::{Bindable, GcScope, NoGcScope}, + Scoped, TryResult, }, heap::{CompactionLists, HeapMarkAndSweep, WellKnownSymbolIndexes, WorkQueues}, }; @@ -28,7 +34,7 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorRecord { pub(crate) iterator: Object<'static>, - pub(crate) next_method: Value, + pub(crate) next_method: Value<'static>, pub(crate) done: bool, } @@ -44,9 +50,12 @@ pub(crate) fn get_iterator_from_method( method: Function, mut gc: GcScope, ) -> JsResult { + let obj = obj.bind(gc.nogc()); let method = method.bind(gc.nogc()); // 1. Let iterator be ? Call(method, obj). - let iterator = call(agent, method.into(), obj, None, gc.reborrow())?; + let iterator = call_function(agent, method.unbind(), obj.unbind(), None, gc.reborrow())? + .unbind() + .bind(gc.nogc()); // 2. If iterator is not an Object, throw a TypeError exception. let Ok(iterator) = Object::try_from(iterator) else { @@ -56,7 +65,6 @@ pub(crate) fn get_iterator_from_method( gc.nogc(), )); }; - let iterator = iterator.bind(gc.nogc()); let scoped_iterator = iterator.scope(agent, gc.nogc()); // 3. Let nextMethod be ? Get(iterator, "next"). @@ -71,7 +79,7 @@ pub(crate) fn get_iterator_from_method( // 5. Return iteratorRecord. Ok(IteratorRecord { iterator: scoped_iterator.get(agent).unbind(), - next_method, + next_method: next_method.unbind(), done: false, }) } @@ -87,12 +95,14 @@ pub(crate) fn get_iterator( is_async: bool, mut gc: GcScope, ) -> JsResult { + let obj = obj.bind(gc.nogc()); + let scoped_obj = obj.scope(agent, gc.nogc()); // 1. If kind is async, then let method = if is_async { // a. Let method be ? GetMethod(obj, @@asyncIterator). let method = get_method( agent, - obj, + obj.unbind(), PropertyKey::Symbol(WellKnownSymbolIndexes::AsyncIterator.into()), gc.reborrow(), )?; @@ -102,7 +112,7 @@ pub(crate) fn get_iterator( // i. Let syncMethod be ? GetMethod(obj, @@iterator). let Some(sync_method) = get_method( agent, - obj, + scoped_obj.get(agent), PropertyKey::Symbol(WellKnownSymbolIndexes::Iterator.into()), gc.reborrow(), )? @@ -116,8 +126,12 @@ pub(crate) fn get_iterator( }; // iii. Let syncIteratorRecord be ? GetIteratorFromMethod(obj, syncMethod). - let _sync_iterator_record = - get_iterator_from_method(agent, obj, sync_method.unbind(), gc.reborrow())?; + let _sync_iterator_record = get_iterator_from_method( + agent, + scoped_obj.get(agent), + sync_method.unbind(), + gc.reborrow(), + )?; // iv. Return CreateAsyncFromSyncIterator(syncIteratorRecord). todo!("Implement create_async_from_sync_iterator(sync_iterator_record)") @@ -129,7 +143,7 @@ pub(crate) fn get_iterator( // a. Let method be ? GetMethod(obj, @@iterator). get_method( agent, - obj, + obj.unbind(), PropertyKey::Symbol(WellKnownSymbolIndexes::Iterator.into()), gc.reborrow(), )? @@ -145,7 +159,7 @@ pub(crate) fn get_iterator( }; // 4. Return ? GetIteratorFromMethod(obj, method). - get_iterator_from_method(agent, obj, method.unbind(), gc.reborrow()) + get_iterator_from_method(agent, scoped_obj.get(agent), method.unbind(), gc.reborrow()) } /// ### [7.4.4 IteratorNext ( iteratorRecord [ , value ] )](https://tc39.es/ecma262/#sec-iteratornext) @@ -157,7 +171,9 @@ pub(crate) fn get_iterator( pub(crate) fn iterator_next<'a>( agent: &mut Agent, iterator_record: &IteratorRecord, - value: Option, + // SAFETY: The value is immediately passed to Call and never used again: + // We don't need to bind/unbind/worry about its lifetime. + value: Option>, mut gc: GcScope<'a, '_>, ) -> JsResult> { // 1. If value is not present, then @@ -172,7 +188,10 @@ pub(crate) fn iterator_next<'a>( .as_ref() .map(|data| ArgumentsList(core::slice::from_ref(data))), gc.reborrow(), - )?; + )? + .unbind(); + let gc = gc.into_nogc(); + let result = result.bind(gc); // 3. If result is not an Object, throw a TypeError exception. // 4. Return result. @@ -181,7 +200,7 @@ pub(crate) fn iterator_next<'a>( .or(Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "The iterator result was not an object", - gc.nogc(), + gc, ))) } @@ -210,18 +229,13 @@ pub(crate) fn iterator_complete( /// The abstract operation IteratorValue takes argument iterResult (an /// Object) and returns either a normal completion containing an ECMAScript /// language value or a throw completion. -pub(crate) fn iterator_value( +pub(crate) fn iterator_value<'a>( agent: &mut Agent, iter_result: Object, - mut gc: GcScope, -) -> JsResult { + gc: GcScope<'a, '_>, +) -> JsResult> { // 1. Return ? Get(iterResult, "value"). - get( - agent, - iter_result, - BUILTIN_STRING_MEMORY.value.into(), - gc.reborrow(), - ) + get(agent, iter_result, BUILTIN_STRING_MEMORY.value.into(), gc) } /// ### [7.4.7 IteratorStep ( iteratorRecord )](https://tc39.es/ecma262/#sec-iteratorstep) @@ -267,11 +281,11 @@ pub(crate) fn iterator_step<'a>( /// iteratorRecord.[\[NextMethod\]] and returns either done indicating that the /// iterator has reached its end or the value from the IteratorResult object if /// a next value is available. -pub(crate) fn iterator_step_value( +pub(crate) fn iterator_step_value<'a>( agent: &mut Agent, iterator_record: &mut IteratorRecord, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'a, '_>, +) -> JsResult>> { // 1. Let result be Completion(IteratorNext(iteratorRecord)). let result = iterator_next(agent, iterator_record, None, gc.reborrow()); @@ -319,7 +333,7 @@ pub(crate) fn iterator_step_value( agent, scoped_result.get(agent), BUILTIN_STRING_MEMORY.value.into(), - gc.reborrow(), + gc, ); // 9. If value is a throw completion, then @@ -390,6 +404,42 @@ pub(crate) fn iterator_close( Ok(completion) } +pub(crate) fn iterator_close_with_error( + agent: &mut Agent, + iterator_record: &IteratorRecord, + completion: JsError, + mut gc: GcScope, +) -> JsError { + // 1. Assert: iteratorRecord.[[Iterator]] is an Object. + // 2. Let iterator be iteratorRecord.[[Iterator]]. + let iterator = iterator_record.iterator; + // 3. Let innerResult be Completion(GetMethod(iterator, "return")). + let inner_result = get_method( + agent, + iterator.into_value(), + BUILTIN_STRING_MEMORY.r#return.into(), + gc.reborrow(), + ); + // 4. If innerResult.[[Type]] is normal, then + if let Ok(r#return) = inner_result { + // a. Let return be innerResult.[[Value]]. + // b. If return is undefined, return ? completion. + let Some(r#return) = r#return else { + return completion; + }; + // c. Set innerResult to Completion(Call(return, iterator)). + let _ = call_function( + agent, + r#return.unbind(), + iterator.into_value(), + None, + gc.reborrow(), + ); + } + // 5. If completion.[[Type]] is throw, return ? completion. + completion +} + /// ### [7.4.9 IteratorClose ( iteratorRecord, completion )](https://tc39.es/ecma262/#sec-iteratorclose) /// /// The abstract operation IteratorClose takes arguments iteratorRecord (an @@ -436,27 +486,29 @@ pub(crate) fn try_iterator_close( } } -/// ### [7.4.10 IfAbruptCloseIterator ( value, iteratorRecord )](https://tc39.es/ecma262/#sec-ifabruptcloseiterator) -/// -/// IfAbruptCloseIterator is a shorthand for a sequence of algorithm steps that -/// use an Iterator Record. -#[inline(always)] -pub(crate) fn if_abrupt_close_iterator( - agent: &mut Agent, - value: JsResult, - iterator_record: &IteratorRecord, - gc: GcScope, -) -> JsResult { - // 1. Assert: value is a Completion Record. - // 2. If value is an abrupt completion, return ? IteratorClose(iteratorRecord, value). - if value.is_err() { - iterator_close(agent, iterator_record, value, gc) - } else { - // 3. Else, set value to value.[[Value]]. - value - } +macro_rules! if_abrupt_close_iterator { + ($agent:ident, $value:ident, $iterator_record:ident, $gc:ident) => { + // 1. Assert: value is a Completion Record. + // 2. If value is an abrupt completion, return ? IteratorClose(iteratorRecord, value). + if let Err(err) = $value { + return Err( + crate::ecmascript::abstract_operations::operations_on_iterator_objects::iterator_close_with_error( + $agent, + &$iterator_record, + err, + $gc + ) + ); + } else if let Ok(value) = $value { + value.unbind().bind($gc.nogc()) + } else { + unreachable!(); + } + }; } +pub(crate) use if_abrupt_close_iterator; + /// ### [7.4.11 AsyncIteratorClose ( iteratorRecord, completion )](https://tc39.es/ecma262/#sec-asynciteratorclose) /// /// The abstract operation AsyncIteratorClose takes arguments iteratorRecord @@ -464,12 +516,12 @@ pub(crate) fn if_abrupt_close_iterator( /// Completion Record. It is used to notify an async iterator that it should /// perform any actions it would normally perform when it has reached its /// completed state. -pub(crate) fn async_iterator_close( +pub(crate) fn async_iterator_close<'a>( _agent: &mut Agent, _iterator_record: &IteratorRecord, _completion: JsResult, - _gc: GcScope, -) -> JsResult { + _gc: GcScope<'a, '_>, +) -> JsResult> { // 1. Assert: iteratorRecord.[[Iterator]] is an Object. // 2. Let iterator be iteratorRecord.[[Iterator]]. // 3. Let innerResult be Completion(GetMethod(iterator, "return")). @@ -522,11 +574,11 @@ pub(crate) fn create_iter_result_object<'a>( /// of ECMAScript language values) and returns an Iterator Record. It creates /// an Iterator (27.1.1.2) object record whose next method returns the /// successive elements of list. -pub(crate) fn create_list_iterator_record( +pub(crate) fn create_list_iterator_record<'a>( _agent: &mut Agent, _list: &[Value], - _gc: GcScope, -) -> JsResult { + _gc: GcScope<'a, '_>, +) -> JsResult> { // 1. Let closure be a new Abstract Closure with no parameters that captures list and performs the following steps when called: // a. For each element E of list, do // i. Perform ? GeneratorYield(CreateIterResultObject(E, false)). @@ -541,11 +593,11 @@ pub(crate) fn create_list_iterator_record( /// The abstract operation IteratorToList takes argument iteratorRecord (an /// Iterator Record) and returns either a normal completion containing a List /// of ECMAScript language values or a throw completion. -pub(crate) fn iterator_to_list( +pub(crate) fn iterator_to_list<'b>( agent: &mut Agent, iterator_record: &IteratorRecord, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'_, 'b>, +) -> JsResult>>> { // 1. Let values be a new empty List. let mut values = Vec::new(); @@ -556,7 +608,12 @@ pub(crate) fn iterator_to_list( while let Some(next) = iterator_step(agent, iterator_record, gc.reborrow())? { // i. Let nextValue be ? IteratorValue(next). // ii. Append nextValue to values. - values.push(iterator_value(agent, next.unbind(), gc.reborrow())?); + values.push( + iterator_value(agent, next.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()) + .scope(agent, gc.nogc()), + ); } // 4. Return values. diff --git a/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs b/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs index 5f52c6d89..e84f6a23b 100644 --- a/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs +++ b/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs @@ -14,9 +14,9 @@ use super::{ }, }; use crate::{ - ecmascript::types::{bind_property_keys, scope_property_keys, unbind_property_keys}, + ecmascript::types::scope_property_keys, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::Rootable, unwrap_try, Scoped, TryResult, }, @@ -78,12 +78,12 @@ pub(crate) fn make_basic_object(_agent: &mut Agent, _internal_slots_list: ()) -> /// language value or a throw completion. It is used to retrieve the value of a /// specific property of an object. #[inline] -pub(crate) fn get<'a>( +pub(crate) fn get<'a, 'b>( agent: &mut Agent, - o: impl IntoObject<'a>, + o: impl IntoObject<'b>, p: PropertyKey, - gc: GcScope, -) -> JsResult { + gc: GcScope<'a, '_>, +) -> JsResult> { let p = p.bind(gc.nogc()); // 1. Return ? O.[[Get]](P, O). o.into_object() @@ -97,12 +97,12 @@ pub(crate) fn get<'a>( /// language value or a throw completion. It is used to retrieve the value of a /// specific property of an object. #[inline] -pub(crate) fn try_get<'a>( +pub(crate) fn try_get<'a, 'gc>( agent: &mut Agent, o: impl IntoObject<'a>, p: PropertyKey, - gc: NoGcScope, -) -> TryResult { + gc: NoGcScope<'gc, '_>, +) -> TryResult> { // 1. Return ? O.[[Get]](P, O). o.into_object().try_get(agent, p, o.into_value(), gc) } @@ -115,19 +115,19 @@ pub(crate) fn try_get<'a>( /// to retrieve the value of a specific property of an ECMAScript language /// value. If the value is not an object, the property lookup is performed /// using a wrapper object appropriate for the type of the value. -pub(crate) fn get_v(agent: &mut Agent, v: Value, p: PropertyKey, gc: GcScope) -> JsResult { - let mut p = p.bind(gc.nogc()); +pub(crate) fn get_v<'gc>( + agent: &mut Agent, + v: Value, + p: PropertyKey, + gc: GcScope<'gc, '_>, +) -> JsResult> { + let v = v.bind(gc.nogc()); + let p = p.bind(gc.nogc()); // 1. Let O be ? ToObject(V). - let o = if let Ok(o) = Object::try_from(v) { - o.bind(gc.nogc()) - } else { - let scoped_p = p.scope(agent, gc.nogc()); - let o = to_object(agent, v, gc.nogc())?; - p = scoped_p.get(agent).bind(gc.nogc()); - o - }; + let o = to_object(agent, v, gc.nogc())?; // 2. Return ? O.[[Get]](P, V). - o.unbind().internal_get(agent, p.unbind(), o.into(), gc) + o.unbind() + .internal_get(agent, p.unbind(), o.unbind().into(), gc) } /// ### Try [7.3.3 GetV ( V, P )](https://tc39.es/ecma262/#sec-getv) @@ -138,12 +138,12 @@ pub(crate) fn get_v(agent: &mut Agent, v: Value, p: PropertyKey, gc: GcScope) -> /// to retrieve the value of a specific property of an ECMAScript language /// value. If the value is not an object, the property lookup is performed /// using a wrapper object appropriate for the type of the value. -pub(crate) fn try_get_v( +pub(crate) fn try_get_v<'gc>( agent: &mut Agent, v: Value, p: PropertyKey, - gc: NoGcScope, -) -> TryResult> { + gc: NoGcScope<'gc, '_>, +) -> TryResult>> { // 1. Let O be ? ToObject(V). let o = match to_object(agent, v, gc) { Ok(o) => o, @@ -239,7 +239,7 @@ pub(crate) fn try_create_data_property<'a>( ) -> TryResult { // 1. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }. let new_desc = PropertyDescriptor { - value: Some(value), + value: Some(value.unbind()), writable: Some(true), get: None, set: None, @@ -272,7 +272,7 @@ pub(crate) fn create_data_property<'a>( let property_key = property_key.bind(gc.nogc()); // 1. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }. let new_desc = PropertyDescriptor { - value: Some(value), + value: Some(value.unbind()), writable: Some(true), get: None, set: None, @@ -520,7 +520,7 @@ pub(crate) fn get_method<'a>( let p = p.bind(gc.nogc()); // 1. Let func be ? GetV(V, P). let func = get_v(agent, v, p.unbind(), gc.reborrow())?; - get_method_internal(agent, func, gc.into_nogc()) + get_method_internal(agent, func.unbind(), gc.into_nogc()) } /// ### [7.3.11 GetMethod ( V, P )](https://tc39.es/ecma262/#sec-getmethod) @@ -538,8 +538,12 @@ pub(crate) fn get_object_method<'a>( ) -> JsResult>> { let p = p.bind(gc.nogc()); // 1. Let func be ? GetV(V, P). - let func = o.internal_get(agent, p.unbind(), o.into_value(), gc.reborrow())?; - get_method_internal(agent, func, gc.into_nogc()) + let func = o + .internal_get(agent, p.unbind(), o.into_value(), gc.reborrow())? + .unbind(); + let gc = gc.into_nogc(); + let func = func.bind(gc); + get_method_internal(agent, func, gc) } fn get_method_internal<'a>( @@ -651,13 +655,13 @@ pub(crate) fn has_own_property( /// the this value of the [[Call]], and argumentsList is the value passed to /// the corresponding argument of the internal method. If argumentsList is not /// present, a new empty List is used as its value. -pub(crate) fn call( +pub(crate) fn call<'gc>( agent: &mut Agent, f: Value, v: Value, arguments_list: Option, - gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { // 1. If argumentsList is not present, set argumentsList to a new empty List. let arguments_list = arguments_list.unwrap_or_default(); // 2. If IsCallable(F) is false, throw a TypeError exception. @@ -721,7 +725,7 @@ pub(crate) fn set_integrity_level( } // 3. Let keys be ? O.[[OwnPropertyKeys]](). let keys = o.internal_own_property_keys(agent, gc.reborrow())?; - let keys = bind_property_keys(unbind_property_keys(keys), gc.nogc()); + let keys = keys.unbind().bind(gc.nogc()); // 4. If level is SEALED, then if T::LEVEL == IntegrityLevel::Sealed { // a. For each element k of keys, do @@ -869,7 +873,7 @@ pub(crate) fn test_integrity_level( // 4. Let keys be ? O.[[OwnPropertyKeys]](). let keys = o.internal_own_property_keys(agent, gc.reborrow())?; - let keys = bind_property_keys(unbind_property_keys(keys), gc.nogc()); + let keys = keys.unbind().bind(gc.nogc()); let mut broke = false; let mut i = 0; @@ -1005,7 +1009,7 @@ pub(crate) fn length_of_array_like( PropertyKey::from(BUILTIN_STRING_MEMORY.length), gc.reborrow(), )?; - to_length(agent, property, gc) + to_length(agent, property.unbind(), gc) } /// ### [7.3.18 LengthOfArrayLike ( obj )](https://tc39.es/ecma262/#sec-lengthofarraylike) @@ -1044,17 +1048,20 @@ pub(crate) fn try_length_of_array_like( /// for element values of the List that is created. /// /// NOTE: This implementation doesn't yet support `elementTypes`. -pub(crate) fn create_list_from_array_like( +pub(crate) fn create_list_from_array_like<'gc>( agent: &mut Agent, obj: Value, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'gc, '_>, +) -> JsResult>> { match obj { - Value::Array(array) => Ok(array - .as_slice(agent) - .iter() - .map(|el| el.unwrap_or(Value::Undefined)) - .collect()), + Value::Array(array) => { + let gc = gc.into_nogc(); + Ok(array + .as_slice(agent) + .iter() + .map(|el| el.unwrap_or(Value::Undefined).bind(gc)) + .collect()) + } // TODO: TypedArrays _ if obj.is_object() => { let object = Object::try_from(obj).unwrap(); @@ -1075,11 +1082,12 @@ pub(crate) fn create_list_from_array_like( gc.reborrow(), )?; // d. Append next to list. - list.push(next); + list.push(next.unbind().bind(gc.nogc()).scope(agent, gc.nogc())); // e. Set index to index + 1. } // 7. Return list. - Ok(list) + let gc = gc.into_nogc(); + Ok(list.into_iter().map(|v| v.get(agent).bind(gc)).collect()) } // 2. If obj is not an Object, throw a TypeError exception. _ => Err(agent.throw_exception_with_static_message( @@ -1128,16 +1136,23 @@ pub(crate) fn create_property_key_list_from_array_like<'a>( scoped_object.get(agent).unbind(), PropertyKey::Integer(SmallInteger::try_from(index as u64).unwrap()).unbind(), gc.reborrow(), - )?; + )? + .unbind() + .bind(gc.nogc()); match next { Value::String(_) | Value::SmallString(_) => { let string_value = String::try_from(next).unwrap(); - let scoped_property_key = - unwrap_try(to_property_key_simple(agent, string_value, gc.nogc())) - .scope(agent, gc.nogc()); + let scoped_property_key = unwrap_try(to_property_key_simple( + agent, + string_value.unbind(), + gc.nogc(), + )) + .scope(agent, gc.nogc()); list.push(scoped_property_key); } - Value::Symbol(sym) => list.push(PropertyKey::Symbol(sym).scope(agent, gc.nogc())), + Value::Symbol(sym) => { + list.push(PropertyKey::Symbol(sym.unbind()).scope(agent, gc.nogc())) + } _ => { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -1152,13 +1167,13 @@ pub(crate) fn create_property_key_list_from_array_like<'a>( } /// Abstract operation Call specialized for a Function. -pub(crate) fn call_function( +pub(crate) fn call_function<'gc>( agent: &mut Agent, f: Function, v: Value, arguments_list: Option, - gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { let f = f.bind(gc.nogc()); let arguments_list = arguments_list.unwrap_or_default(); let current_stack_size = agent.stack_refs.borrow().len(); @@ -1193,19 +1208,42 @@ pub(crate) fn construct<'a>( /// the lookup point for the property and the this value of the call. /// argumentsList is the list of arguments values passed to the method. If /// argumentsList is not present, a new empty List is used as its value. -pub(crate) fn invoke( +pub(crate) fn invoke<'gc>( agent: &mut Agent, v: Value, p: PropertyKey, arguments_list: Option, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { + let v = v.bind(gc.nogc()); + let p = p.bind(gc.nogc()); // 1. If argumentsList is not present, set argumentsList to a new empty List. let arguments_list = arguments_list.unwrap_or_default(); // 2. Let func be ? GetV(V, P). - let func = get_v(agent, v, p, gc.reborrow())?; // 3. Return ? Call(func, V, argumentsList). - call(agent, func, v, Some(arguments_list), gc) + if let TryResult::Continue(func) = try_get_v(agent, v, p, gc.nogc()) { + call(agent, func?.unbind(), v.unbind(), Some(arguments_list), gc) + } else { + // We couldn't get the func without calling into Javascript: No + // choice, we must allocate the arguments onto the heap. + let scoped_v = v.scope(agent, gc.nogc()); + let scoped_arguments = arguments_list + .iter() + .map(|v| v.scope(agent, gc.nogc())) + .collect::>(); + let func = get_v(agent, v.unbind(), p.unbind(), gc.reborrow())?; + let arguments = scoped_arguments + .into_iter() + .map(|v| v.get(agent)) + .collect::>(); + call( + agent, + func.unbind(), + scoped_v.get(agent), + Some(ArgumentsList(&arguments)), + gc, + ) + } } /// ### [7.3.21 OrdinaryHasInstance ( C, O )](https://tc39.es/ecma262/#sec-ordinaryhasinstance) @@ -1218,14 +1256,13 @@ pub(crate) fn invoke( pub(crate) fn ordinary_has_instance<'a, 'b>( agent: &mut Agent, c: impl TryInto>, - o: impl IntoValue, + o: impl IntoValue<'b>, mut gc: GcScope, ) -> JsResult { // 1. If IsCallable(C) is false, return false. let Some(c) = is_callable(c, gc.nogc()) else { return Ok(false); }; - let c = c.bind(gc.nogc()); // 2. If C has a [[BoundTargetFunction]] internal slot, then if let Function::BoundFunction(c) = c { // a. Let BC be C.[[BoundTargetFunction]]. @@ -1253,7 +1290,7 @@ pub(crate) fn ordinary_has_instance<'a, 'b>( )); }; // 6. Repeat, - is_prototype_of_loop(agent, p, o, gc) + is_prototype_of_loop(agent, p.unbind(), o.unbind(), gc) } pub(crate) fn is_prototype_of_loop( @@ -1367,23 +1404,21 @@ pub(crate) fn scoped_enumerable_own_keys<'a>( .collect()); } // 1. Let ownKeys be ? O.[[OwnPropertyKeys]](). - let own_string_keys = bind_property_keys( - unbind_property_keys( - o.get(agent) - .internal_own_property_keys(agent, gc.reborrow())?, - ), - gc.nogc(), - ) - .into_iter() - // 1. If key is a String, then - .filter_map(|key| { - if key.is_string() { - Some(key.scope(agent, gc.nogc())) - } else { - None - } - }) - .collect::>(); + let own_string_keys = o + .get(agent) + .internal_own_property_keys(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()) + .into_iter() + // 1. If key is a String, then + .filter_map(|key| { + if key.is_string() { + Some(key.scope(agent, gc.nogc())) + } else { + None + } + }) + .collect::>(); // 2. Let results be a new empty List. // 3. For each element key of ownKeys, do @@ -1422,16 +1457,27 @@ pub(crate) fn scoped_enumerable_own_keys<'a>( /// Object) and kind (KEY, VALUE, or KEY+VALUE) and returns either a normal /// completion containing a List of ECMAScript language values or a throw /// completion. -pub(crate) fn enumerable_own_properties( +pub(crate) fn enumerable_own_properties<'gc, Kind: EnumerablePropertiesKind>( agent: &mut Agent, o: Object, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'gc, '_>, +) -> JsResult>> { + let mut o = o.bind(gc.nogc()); + let mut scoped_o = None; // 1. Let ownKeys be ? O.[[OwnPropertyKeys]](). - let mut own_keys = bind_property_keys( - unbind_property_keys(o.internal_own_property_keys(agent, gc.reborrow())?), - gc.nogc(), - ); + let mut own_keys = + if let TryResult::Continue(own_keys) = o.try_own_property_keys(agent, gc.nogc()) { + own_keys + } else { + scoped_o = Some(o.scope(agent, gc.nogc())); + let result = o + .unbind() + .internal_own_property_keys(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()); + o = scoped_o.as_ref().unwrap().get(agent).bind(gc.nogc()); + result + }; // 2. Let results be a new empty List. let mut results: Vec = Vec::with_capacity(own_keys.len()); // 3. For each element key of ownKeys, do @@ -1517,21 +1563,27 @@ pub(crate) fn enumerable_own_properties( if broke { // drop the keys we already got. let _ = own_keys.drain(..i); - let own_keys = unbind_property_keys(own_keys); - enumerable_own_properties_slow::(agent, o, own_keys, results, gc) + let scoped_o = scoped_o.unwrap_or_else(|| o.scope(agent, gc.nogc())); + enumerable_own_properties_slow::( + agent, + scoped_o, + own_keys.unbind(), + results.unbind(), + gc, + ) } else { // 4. Return results. - Ok(results) + Ok(results.unbind().bind(gc.into_nogc())) } } -fn enumerable_own_properties_slow( +fn enumerable_own_properties_slow<'gc, Kind: EnumerablePropertiesKind>( agent: &mut Agent, - o: Object, + o: Scoped<'_, Object<'static>>, own_keys: Vec, results: Vec, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'gc, '_>, +) -> JsResult>> { let own_keys = scope_property_keys(agent, own_keys, gc.nogc()); let mut results = results .into_iter() @@ -1539,11 +1591,13 @@ fn enumerable_own_properties_slow( .collect::>(); for scoped_key in own_keys { let key = scoped_key.get(agent).bind(gc.nogc()); - if let PropertyKey::Symbol(_) = key { + if key.is_symbol() { continue; } // i. Let desc be ? O.[[GetOwnProperty]](key). - let desc = o.internal_get_own_property(agent, key.unbind(), gc.reborrow())?; + let desc = o + .get(agent) + .internal_get_own_property(agent, key.unbind(), gc.reborrow())?; // ii. If desc is not undefined and desc.[[Enumerable]] is true, then let Some(desc) = desc else { continue; @@ -1554,23 +1608,18 @@ fn enumerable_own_properties_slow( // 1. If kind is KEY, then if Kind::KIND == EnumPropKind::Key { // a. Append key to results. - let key_value = match scoped_key.get(agent).bind(gc.nogc()) { - PropertyKey::Symbol(_) => { - unreachable!(); - } - PropertyKey::Integer(int) => { - let int = int.into_i64(); - String::from_string(agent, int.to_string(), gc.nogc()) - } - PropertyKey::SmallString(str) => str.into(), - PropertyKey::String(str) => str.into(), - }; - results.push(key_value.into_value().scope(agent, gc.nogc())); + let key_value = scoped_key + .get(agent) + .bind(gc.nogc()) + .convert_to_value(agent, gc.nogc()); + results.push(key_value.scope(agent, gc.nogc())); } else { // 2. Else, // a. Let value be ? Get(O, key). let key = scoped_key.get(agent).bind(gc.nogc()); - let value = get(agent, o, key.unbind(), gc.reborrow())?; + let value = get(agent, o.get(agent), key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); // b. If kind is VALUE, then if Kind::KIND == EnumPropKind::Value { // i. Append value to results. @@ -1579,20 +1628,19 @@ fn enumerable_own_properties_slow( // c. Else, // i. Assert: kind is KEY+VALUE. debug_assert_eq!(Kind::KIND, EnumPropKind::KeyValue); - let key_value = match scoped_key.get(agent).bind(gc.nogc()) { - PropertyKey::Symbol(_) => { - unreachable!(); - } - PropertyKey::Integer(int) => { - let int = int.into_i64(); - String::from_string(agent, int.to_string(), gc.nogc()) - } - PropertyKey::SmallString(str) => str.into(), - PropertyKey::String(str) => str.into(), - }; + let key_value = String::try_from( + scoped_key + .get(agent) + .bind(gc.nogc()) + .convert_to_value(agent, gc.nogc()), + ) + .unwrap(); // ii. Let entry be CreateArrayFromList(« key, value »). - let entry = - create_array_from_list(agent, &[key_value.into_value(), value], gc.nogc()); + let entry = create_array_from_list( + agent, + &[key_value.into_value().unbind(), value.unbind()], + gc.nogc(), + ); // iii. Append entry to results. results.push(entry.into_value().scope(agent, gc.nogc())); } @@ -1667,13 +1715,11 @@ pub(crate) fn copy_data_properties( } else { scoped_target = Some(target.scope(agent, gc.nogc())); scoped_from = Some(from.scope(agent, gc.nogc())); - let keys = bind_property_keys( - unbind_property_keys( - from.unbind() - .internal_own_property_keys(agent, gc.reborrow())?, - ), - gc.nogc(), - ); + let keys = from + .unbind() + .internal_own_property_keys(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()); target = scoped_target.as_ref().unwrap().get(agent).bind(gc.nogc()); from = scoped_from.as_ref().unwrap().get(agent).bind(gc.nogc()); keys @@ -1723,10 +1769,9 @@ pub(crate) fn copy_data_properties( if broke { let _ = keys.drain(..i); - let keys = unbind_property_keys(keys); let target = scoped_target.unwrap_or_else(|| target.scope(agent, gc.nogc())); let from = scoped_from.unwrap_or_else(|| from.scope(agent, gc.nogc())); - copy_data_properties_slow(agent, target, from, keys, gc) + copy_data_properties_slow(agent, target, from, keys.unbind(), gc) } else { // 5. Return UNUSED. Ok(()) @@ -1750,16 +1795,17 @@ fn copy_data_properties_slow( { if dest.enumerable.unwrap() { // 1. Let propValue be ? Get(from, nextKey). - let prop_value = get(agent, from.get(agent), next_key.get(agent), gc.reborrow())?; + let prop_value = get(agent, from.get(agent), next_key.get(agent), gc.reborrow())? + .unbind() + .bind(gc.nogc()); // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue). - create_data_property( + unwrap_try(try_create_data_property( agent, target.get(agent), next_key.get(agent), prop_value, - gc.reborrow(), - ) - .unwrap(); + gc.nogc(), + )); } } } @@ -1838,14 +1884,17 @@ pub(crate) fn copy_data_properties_into_object<'a, 'b>( excluded_items: &AHashSet>, mut gc: GcScope<'a, '_>, ) -> JsResult> { - let from = source.into_object(); + let from = source.into_object().bind(gc.nogc()); + let scoped_from = from.scope(agent, gc.nogc()); let mut entries = Vec::new(); // 3. Let keys be ? from.[[OwnPropertyKeys]](). - let mut keys = bind_property_keys( - unbind_property_keys(from.internal_own_property_keys(agent, gc.reborrow())?), - gc.nogc(), - ); + let mut keys = from + .unbind() + .internal_own_property_keys(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()); + let from = scoped_from.get(agent).bind(gc.nogc()); // 4. For each element nextKey of keys, do let mut broke = false; let mut i = 0; @@ -1872,7 +1921,8 @@ pub(crate) fn copy_data_properties_into_object<'a, 'b>( if let Some(desc) = desc { if desc.enumerable.unwrap() { // 1. Let propValue be ? Get(from, nextKey). - let TryResult::Continue(prop_value) = try_get(agent, from, next_key, gc.nogc()) + let TryResult::Continue(prop_value) = + try_get(agent, from.unbind(), next_key, gc.nogc()) else { broke = true; break; @@ -1897,9 +1947,9 @@ pub(crate) fn copy_data_properties_into_object<'a, 'b>( let _ = keys.drain(..i); Ok(copy_data_properties_into_object_slow( agent, - from, + scoped_from, excluded_items, - unbind_property_keys(keys), + keys.unbind(), object.unbind(), gc.reborrow(), )? @@ -1912,14 +1962,14 @@ pub(crate) fn copy_data_properties_into_object<'a, 'b>( fn copy_data_properties_into_object_slow<'a>( agent: &mut Agent, - from: Object, + from: Scoped<'_, Object<'static>>, excluded_items: &AHashSet>, keys: Vec>, object: OrdinaryObject<'a>, mut gc: GcScope<'a, '_>, ) -> JsResult> { - let keys = bind_property_keys(keys, gc.nogc()); - let object = object.bind(gc.nogc()).scope(agent, gc.nogc()); + let keys = keys.bind(gc.nogc()); + let object = object.scope(agent, gc.nogc()); // We need to collect the excluded items into a vector, as we cannot hash // scoped items: The same item can be scoped multiple times. let excluded_items = excluded_items @@ -1944,21 +1994,24 @@ fn copy_data_properties_into_object_slow<'a>( // i. Let desc be ? from.[[GetOwnProperty]](nextKey). // ii. If desc is not undefined and desc.[[Enumerable]] is true, then if let Some(desc) = - from.internal_get_own_property(agent, next_key.unbind(), gc.reborrow())? + from.get(agent) + .internal_get_own_property(agent, next_key.unbind(), gc.reborrow())? { if desc.enumerable.unwrap() { // 1. Let propValue be ? Get(from, nextKey). let next_key = scoped_key.get(agent).bind(gc.nogc()); - let prop_value = get(agent, from, next_key.unbind(), gc.reborrow())?; + let prop_value = get(agent, from.get(agent), next_key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue). let next_key = scoped_key.get(agent).bind(gc.nogc()); - create_data_property_or_throw( + unwrap_try(try_create_data_property_or_throw( agent, object.get(agent), next_key.unbind(), - prop_value, - gc.reborrow(), - ) + prop_value.unbind(), + gc.nogc(), + )) .unwrap(); } } @@ -1977,6 +2030,7 @@ pub(crate) fn initialize_instance_elements( constructor: BuiltinConstructorFunction, gc: GcScope, ) -> JsResult<()> { + let o = o.bind(gc.nogc()); let constructor = constructor.bind(gc.nogc()); // 1. Let methods be the value of constructor.[[PrivateMethods]]. // 2. For each PrivateElement method of methods, do @@ -1994,7 +2048,7 @@ pub(crate) fn initialize_instance_elements( // the ECMAScript class constructors directly, so our code only needs // to work for builtin constructors. // Third, the spec defines the initializers as individual functions - // run one after the other. Instea we compile all of the initializers + // run one after the other. Instead we compile all of the initializers // into a single bytecode executable associated with the constructor. // The problem then becomes how to run this executable as an ECMAScript // function. @@ -2028,12 +2082,15 @@ pub(crate) fn initialize_instance_elements( /// The abstract operation AddValueToKeyedGroup takes arguments groups (a List of Records with fields /// [[Key]] (an ECMAScript language value) and [[Elements]] (a List of ECMAScript language values)), /// key (an ECMAScript language value), and value (an ECMAScript language value) and returns UNUSED. -pub(crate) fn add_value_to_keyed_group<'a, K: 'static + Rootable + Copy + Into>( +pub(crate) fn add_value_to_keyed_group< + 'scope, + K: 'static + Rootable + Copy + Into>, +>( agent: &mut Agent, - groups: &mut Vec>, + groups: &mut Vec>, key: K, - value: Value, - gc: NoGcScope<'_, 'a>, + value: Scoped<'scope, Value<'static>>, + gc: NoGcScope<'_, 'scope>, ) -> JsResult<()> { // 1. For each Record { [[Key]], [[Elements]] } g of groups, do for g in groups.iter_mut() { @@ -2041,7 +2098,7 @@ pub(crate) fn add_value_to_keyed_group<'a, K: 'static + Rootable + Copy + Into> { - pub(crate) key: Scoped<'a, K>, - pub(crate) elements: Vec>, +pub(crate) struct GroupByRecord<'scope, K: 'static + Rootable + Copy + Into>> { + pub(crate) key: Scoped<'scope, K>, + pub(crate) elements: Vec>>, } /// ### [7.3.35 GroupBy ( items, callback, keyCoercion )](https://tc39.es/ecma262/#sec-groupby) @@ -2076,12 +2133,14 @@ pub(crate) struct GroupByRecord<'a, K: 'static + Rootable + Copy + Into> /// value) and [[Elements]] (a List of ECMAScript language values), or a throw completion. /// /// Note: This version is for "property" keyCoercion. -pub(crate) fn group_by_property<'a, 'b>( +pub(crate) fn group_by_property<'gc, 'scope>( agent: &mut Agent, items: Value, callback_fn: Value, - mut gc: GcScope<'a, 'b>, -) -> JsResult>>> { + mut gc: GcScope<'gc, 'scope>, +) -> JsResult>>> { + let items = items.bind(gc.nogc()); + let callback_fn = callback_fn.bind(gc.nogc()); // 1. Perform ? RequireObjectCoercible(iterable). require_object_coercible(agent, items, gc.nogc())?; @@ -2093,13 +2152,13 @@ pub(crate) fn group_by_property<'a, 'b>( gc.into_nogc(), )); }; - let callback_fn = callback_fn.bind(gc.nogc()).scope(agent, gc.nogc()); + let callback_fn = callback_fn.scope(agent, gc.nogc()); // 3. Let groups be a new empty List. - let mut groups: Vec>> = vec![]; + let mut groups: Vec>> = vec![]; // 4. Let iteratorRecord be ? GetIterator(iterable). - let mut iterator_record = get_iterator(agent, items, false, gc.reborrow())?; + let mut iterator_record = get_iterator(agent, items.unbind(), false, gc.reborrow())?; // 5. Let k be 0. let mut k = 0; @@ -2117,7 +2176,7 @@ pub(crate) fn group_by_property<'a, 'b>( ); // ii. Return ? IteratorClose(iteratorRecord, error). - return iterator_close(agent, &iterator_record, Err(error), gc.reborrow()); + return iterator_close(agent, &iterator_record, Err(error), gc); } // b. Let next be ? IteratorStepValue(iteratorRecord). @@ -2130,7 +2189,8 @@ pub(crate) fn group_by_property<'a, 'b>( }; // d. Let value be next. - let value = next; + let value = next.unbind().bind(gc.nogc()); + let scoped_value = value.scope(agent, gc.nogc()); // 𝔽(k) let fk = Number::try_from(k).unwrap().into_value(); @@ -2140,22 +2200,22 @@ pub(crate) fn group_by_property<'a, 'b>( agent, callback_fn.get(agent), Value::Undefined, - Some(ArgumentsList(&[value, fk])), + Some(ArgumentsList(&[value.unbind(), fk])), gc.reborrow(), ); // f. IfAbruptCloseIterator(key, iteratorRecord). - let key = if_abrupt_close_iterator(agent, key, &iterator_record, gc.reborrow())?; + let key = if_abrupt_close_iterator!(agent, key, iterator_record, gc); // g. If keyCoercion is property, then // i. Set key to Completion(ToPropertyKey(key)). - let key = to_property_key(agent, key, gc.reborrow()).map(|pk| pk.unbind()); + let key = to_property_key(agent, key.unbind(), gc.reborrow()).map(|pk| pk.unbind()); // ii. IfAbruptCloseIterator(key, iteratorRecord). - let key = if_abrupt_close_iterator(agent, key, &iterator_record, gc.reborrow())?; + let key = if_abrupt_close_iterator!(agent, key, iterator_record, gc); // i. Perform AddValueToKeyedGroup(groups, key, value). - add_value_to_keyed_group(agent, &mut groups, key.unbind(), value, gc.nogc())?; + add_value_to_keyed_group(agent, &mut groups, key.unbind(), scoped_value, gc.nogc())?; // j. Set k to k + 1. k += 1; @@ -2170,12 +2230,14 @@ pub(crate) fn group_by_property<'a, 'b>( /// value) and [[Elements]] (a List of ECMAScript language values), or a throw completion. /// /// Note: This version is for "collection" keyCoercion. -pub(crate) fn group_by_collection<'a>( +pub(crate) fn group_by_collection<'gc, 'scope>( agent: &mut Agent, items: Value, callback_fn: Value, - mut gc: GcScope<'_, 'a>, -) -> JsResult>> { + mut gc: GcScope<'gc, 'scope>, +) -> JsResult>>> { + let items = items.bind(gc.nogc()); + let callback_fn = callback_fn.bind(gc.nogc()); // 1. Perform ? RequireObjectCoercible(iterable). require_object_coercible(agent, items, gc.nogc())?; @@ -2187,13 +2249,13 @@ pub(crate) fn group_by_collection<'a>( gc.into_nogc(), )); }; - let callback_fn = callback_fn.bind(gc.nogc()).scope(agent, gc.nogc()); + let callback_fn = callback_fn.scope(agent, gc.nogc()); // 3. Let groups be a new empty List. - let mut groups: Vec> = vec![]; + let mut groups: Vec>> = vec![]; // 4. Let iteratorRecord be ? GetIterator(iterable). - let mut iterator_record = get_iterator(agent, items, false, gc.reborrow())?; + let mut iterator_record = get_iterator(agent, items.unbind(), false, gc.reborrow())?; // 5. Let k be 0. let mut k = 0; @@ -2211,7 +2273,7 @@ pub(crate) fn group_by_collection<'a>( ); // ii. Return ? IteratorClose(iteratorRecord, error). - return iterator_close(agent, &iterator_record, Err(error), gc.reborrow()); + return iterator_close(agent, &iterator_record, Err(error), gc); } // b. Let next be ? IteratorStepValue(iteratorRecord). @@ -2224,7 +2286,8 @@ pub(crate) fn group_by_collection<'a>( }; // d. Let value be next. - let value = next; + let value = next.unbind().bind(gc.nogc()); + let scoped_value = value.scope(agent, gc.nogc()); // 𝔽(k) let fk = Number::try_from(k).unwrap().into_value(); @@ -2234,12 +2297,13 @@ pub(crate) fn group_by_collection<'a>( agent, callback_fn.get(agent), Value::Undefined, - Some(ArgumentsList(&[value, fk])), + Some(ArgumentsList(&[value.unbind(), fk])), gc.reborrow(), - ); + ) + .map(|key| key.unbind()); // f. IfAbruptCloseIterator(key, iteratorRecord). - let key = if_abrupt_close_iterator(agent, key, &iterator_record, gc.reborrow())?; + let key = if_abrupt_close_iterator!(agent, key, iterator_record, gc); // h. Else, // i. Assert: keyCoercion is collection. @@ -2247,7 +2311,7 @@ pub(crate) fn group_by_collection<'a>( let key = canonicalize_keyed_collection_key(agent, key); // i. Perform AddValueToKeyedGroup(groups, key, value). - add_value_to_keyed_group(agent, &mut groups, key, value, gc.nogc())?; + add_value_to_keyed_group(agent, &mut groups, key.unbind(), scoped_value, gc.nogc())?; // j. Set k to k + 1. k += 1; diff --git a/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs b/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs index d06e55f4b..05d6115bf 100644 --- a/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs +++ b/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs @@ -9,7 +9,7 @@ use crate::ecmascript::builtins::proxy::abstract_operations::{ validate_non_revoked_proxy, NonRevokedProxy, }; use crate::ecmascript::types::{InternalSlots, Numeric, Primitive, PropertyKey}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::heap::WellKnownSymbolIndexes; use crate::{ @@ -34,11 +34,11 @@ use super::type_conversion::{ /// containing an ECMAScript language value or a throw completion. It throws an /// error if argument is a value that cannot be converted to an Object using /// ToObject. It is defined by [Table 14](https://tc39.es/ecma262/#table-requireobjectcoercible-results): -pub(crate) fn require_object_coercible( +pub(crate) fn require_object_coercible<'gc>( agent: &mut Agent, argument: Value, - gc: NoGcScope, -) -> JsResult { + gc: NoGcScope<'gc, '_>, +) -> JsResult> { if argument.is_undefined() || argument.is_null() { Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -46,7 +46,7 @@ pub(crate) fn require_object_coercible( gc, )) } else { - Ok(argument) + Ok(argument.bind(gc)) } } @@ -55,12 +55,12 @@ pub(crate) fn require_object_coercible( /// The abstract operation IsArray takes argument argument (an ECMAScript /// language value) and returns either a normal completion containing a Boolean /// or a throw completion. -pub(crate) fn is_array( +pub(crate) fn is_array<'a>( agent: &mut Agent, - argument: impl IntoValue, + argument: impl IntoValue<'a>, gc: NoGcScope, ) -> JsResult { - let argument = argument.into_value(); + let argument = argument.into_value().bind(gc); match argument { // 1. If argument is not an Object, return false. @@ -187,7 +187,10 @@ pub(crate) fn is_extensible(agent: &mut Agent, o: Object, gc: GcScope) -> JsResu o.internal_is_extensible(agent, gc) } -pub(crate) fn is_same_type, V2: Copy + Into>(x: V1, y: V2) -> bool { +pub(crate) fn is_same_type<'a, V1: Copy + Into>, V2: Copy + Into>>( + x: V1, + y: V2, +) -> bool { (x.into().is_undefined() && y.into().is_undefined()) || (x.into().is_null() && y.into().is_null()) || (x.into().is_boolean() && y.into().is_boolean()) @@ -199,10 +202,9 @@ pub(crate) fn is_same_type, V2: Copy + Into>(x: V1 } /// ### [7.2.6 IsIntegralNumber ( argument )](https://tc39.es/ecma262/#sec-isintegralnumber) -pub(crate) fn is_integral_number( +pub(crate) fn is_integral_number<'a>( agent: &mut Agent, - argument: impl Copy + Into, - gc: GcScope, + argument: impl Copy + Into>, ) -> bool { let argument = argument.into(); @@ -226,11 +228,11 @@ pub(crate) fn is_integral_number( // 4. Return true. // NOTE: Checking if the fractional component is 0.0 is the same as the // specification's operation. - argument.into_value().to_real(agent, gc).unwrap().fract() == 0.0 + argument.to_real(agent).fract() == 0.0 } /// ### [7.2.10 SameValue ( x, y )](https://tc39.es/ecma262/#sec-samevalue) -pub(crate) fn same_value, V2: Copy + Into>( +pub(crate) fn same_value<'a, V1: Copy + Into>, V2: Copy + Into>>( agent: &impl PrimitiveHeapIndexable, x: V1, y: V2, @@ -259,10 +261,10 @@ pub(crate) fn same_value, V2: Copy + Into>( /// It determines whether or not the two arguments are the same value (ignoring /// the difference between +0𝔽 and -0𝔽). It performs the following steps when /// called: -pub(crate) fn same_value_zero( +pub(crate) fn same_value_zero<'a>( agent: &impl PrimitiveHeapIndexable, - x: impl Copy + Into, - y: impl Copy + Into, + x: impl Copy + Into>, + y: impl Copy + Into>, ) -> bool { let (x, y) = (Into::::into(x), Into::::into(y)); @@ -284,7 +286,7 @@ pub(crate) fn same_value_zero( } /// ### [7.2.12 SameValueNonNumber ( x, y )](https://tc39.es/ecma262/#sec-samevaluenonnumber) -pub(crate) fn same_value_non_number>( +pub(crate) fn same_value_non_number<'a, T: Copy + Into>>( agent: &impl PrimitiveHeapIndexable, x: T, y: T, @@ -303,7 +305,7 @@ pub(crate) fn same_value_non_number>( // 3. If x is a BigInt, then if let (Ok(x), Ok(y)) = (BigInt::try_from(x), BigInt::try_from(y)) { // a. Return BigInt::equal(x, y). - return BigInt::equal(agent, x, y); + return BigInt::equal(agent, x.unbind(), y.unbind()); } // 4. If x is a String, then @@ -337,10 +339,10 @@ pub(crate) fn same_value_non_number>( /// corresponds to an expression that occurs to the left of the y parameter's /// corresponding expression. If LeftFirst is false, the reverse is the case /// and operations must be performed upon y before x. -pub(crate) fn is_less_than( +pub(crate) fn is_less_than<'a, const LEFT_FIRST: bool>( agent: &mut Agent, - x: impl Into + Copy, - y: impl Into + Copy, + x: impl Into> + Copy, + y: impl Into> + Copy, mut gc: GcScope, ) -> JsResult> { let (px, py, gc) = match (Primitive::try_from(x.into()), Primitive::try_from(y.into())) { @@ -541,10 +543,10 @@ pub(crate) fn is_less_than( /// language value) and y (an ECMAScript language value) and returns either a /// normal completion containing a Boolean or a throw completion. It provides /// the semantics for the == operator. -pub(crate) fn is_loosely_equal( +pub(crate) fn is_loosely_equal<'a>( agent: &mut Agent, - x: impl Into + Copy, - y: impl Into + Copy, + x: impl Into> + Copy, + y: impl Into> + Copy, mut gc: GcScope, ) -> JsResult { let x: Value = x.into(); @@ -675,10 +677,10 @@ pub(crate) fn is_loosely_equal( /// The abstract operation IsStrictlyEqual takes arguments x (an ECMAScript /// language value) and y (an ECMAScript language value) and returns a Boolean. /// It provides the semantics for the === operator. -pub(crate) fn is_strictly_equal( +pub(crate) fn is_strictly_equal<'a>( agent: &Agent, - x: impl Into + Copy, - y: impl Into + Copy, + x: impl Into> + Copy, + y: impl Into> + Copy, ) -> bool { let (x, y) = (x.into(), y.into()); diff --git a/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs b/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs index 23ea54804..ac9dcef38 100644 --- a/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs +++ b/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs @@ -17,7 +17,7 @@ use num_bigint::Sign; use crate::ecmascript::types::IntoPrimitive; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -62,80 +62,34 @@ pub enum PreferredType { /// > this specification only Dates (see 21.4.4.45) and Symbol objects (see /// > 20.4.3.5) over-ride the default ToPrimitive behaviour. Dates treat the /// > absence of a hint as if the hint were STRING. -pub(crate) fn to_primitive<'a>( +pub(crate) fn to_primitive<'a, 'gc>( agent: &mut Agent, - input: impl IntoValue, + input: impl IntoValue<'a>, preferred_type: Option, - mut gc: GcScope<'a, '_>, -) -> JsResult> { - let input = input.into_value(); + gc: GcScope<'gc, '_>, +) -> JsResult> { + let input = input.into_value().bind(gc.nogc()); // 1. If input is an Object, then if let Ok(input) = Object::try_from(input) { - // a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). - let exotic_to_prim = get_method( - agent, - input.into_value(), - PropertyKey::Symbol(WellKnownSymbolIndexes::ToPrimitive.into()), - gc.reborrow(), - )?; - // b. If exoticToPrim is not undefined, then - if let Some(exotic_to_prim) = exotic_to_prim { - let hint = match preferred_type { - // i. If preferredType is not present, then - // 1. Let hint be "default". - None => BUILTIN_STRING_MEMORY.default, - // ii. Else if preferredType is STRING, then - // 1. Let hint be "string". - Some(PreferredType::String) => BUILTIN_STRING_MEMORY.string, - // iii. Else, - // 1. Assert: preferredType is NUMBER. - // 2. Let hint be "number". - Some(PreferredType::Number) => BUILTIN_STRING_MEMORY.number, - }; - // iv. Let result be ? Call(exoticToPrim, input, « hint »). - let result: Value = call_function( - agent, - exotic_to_prim.unbind(), - input.into(), - Some(ArgumentsList(&[hint.into()])), - gc.reborrow(), - )?; - // v. If result is not an Object, return result. - Primitive::try_from(result).map_err(|_| { - // vi. Throw a TypeError exception. - agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "Invalid toPrimitive return value", - gc.nogc(), - ) - }) - } else { - // c. If preferredType is not present, let preferredType be NUMBER. - // d. Return ? OrdinaryToPrimitive(input, preferredType). - ordinary_to_primitive( - agent, - input, - preferred_type.unwrap_or(PreferredType::Number), - gc, - ) - } + to_primitive_object(agent, input.unbind(), preferred_type, gc) } else { // 2. Return input. - Ok(Primitive::try_from(input).unwrap()) + Ok(Primitive::try_from(input.unbind().bind(gc.into_nogc())).unwrap()) } } -pub(crate) fn to_primitive_object<'a, 'b>( +pub(crate) fn to_primitive_object<'a, 'gc>( agent: &mut Agent, - input: impl IntoObject<'b>, + input: impl IntoObject<'a>, preferred_type: Option, - mut gc: GcScope<'a, '_>, -) -> JsResult> { - let input = input.into_object(); + mut gc: GcScope<'gc, '_>, +) -> JsResult> { + let input = input.into_object().bind(gc.nogc()); // a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). + let scoped_input = input.scope(agent, gc.nogc()); let exotic_to_prim = get_method( agent, - input.into_value(), + input.into_value().unbind(), PropertyKey::Symbol(WellKnownSymbolIndexes::ToPrimitive.into()), gc.reborrow(), )?; @@ -154,20 +108,23 @@ pub(crate) fn to_primitive_object<'a, 'b>( Some(PreferredType::Number) => BUILTIN_STRING_MEMORY.number, }; // iv. Let result be ? Call(exoticToPrim, input, « hint »). - let result: Value = call_function( + let result = call_function( agent, exotic_to_prim.unbind(), - input.into(), + scoped_input.get(agent).into_value().unbind(), Some(ArgumentsList(&[hint.into()])), gc.reborrow(), - )?; + )? + .unbind(); + let gc = gc.into_nogc(); + let result = result.bind(gc); // v. If result is not an Object, return result. Primitive::try_from(result).map_err(|_| { // vi. Throw a TypeError exception. agent.throw_exception_with_static_message( ExceptionType::TypeError, "Invalid toPrimitive return value", - gc.nogc(), + gc, ) }) } else { @@ -175,7 +132,7 @@ pub(crate) fn to_primitive_object<'a, 'b>( // d. Return ? OrdinaryToPrimitive(input, preferredType). ordinary_to_primitive( agent, - input, + scoped_input.get(agent), preferred_type.unwrap_or(PreferredType::Number), gc, ) @@ -187,12 +144,13 @@ pub(crate) fn to_primitive_object<'a, 'b>( /// The abstract operation OrdinaryToPrimitive takes arguments O (an Object) /// and hint (STRING or NUMBER) and returns either a normal completion /// containing an ECMAScript language value or a throw completion. -pub(crate) fn ordinary_to_primitive<'a>( +pub(crate) fn ordinary_to_primitive<'gc>( agent: &mut Agent, o: Object, hint: PreferredType, - mut gc: GcScope<'a, '_>, -) -> JsResult> { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { + let mut o = o.bind(gc.nogc()); let to_string_key = PropertyKey::from(BUILTIN_STRING_MEMORY.toString); let value_of_key = PropertyKey::from(BUILTIN_STRING_MEMORY.valueOf); let method_names = match hint { @@ -208,19 +166,28 @@ pub(crate) fn ordinary_to_primitive<'a>( } }; // 3. For each element name of methodNames, do + let scoped_o = o.scope(agent, gc.nogc()); for name in method_names { // a. Let method be ? Get(O, name). - let method = get(agent, o, name, gc.reborrow())?; + let method = get(agent, o.unbind(), name, gc.reborrow())? + .unbind() + .bind(gc.nogc()); // b. If IsCallable(method) is true, then if let Some(method) = is_callable(method, gc.nogc()) { // i. Let result be ? Call(method, O). - let result: Value = - call_function(agent, method.unbind(), o.into(), None, gc.reborrow())?; + let result: Value = call_function( + agent, + method.unbind(), + scoped_o.get(agent).into_value(), + None, + gc.reborrow(), + )?; // ii. If result is not an Object, return result. if let Ok(result) = Primitive::try_from(result) { - return Ok(result); + return Ok(result.unbind().bind(gc.into_nogc())); } } + o = scoped_o.get(agent).bind(gc.nogc()); } // 4. Throw a TypeError exception. Err(agent.throw_exception_with_static_message( @@ -256,11 +223,11 @@ pub(crate) fn to_boolean(agent: &Agent, argument: Value) -> bool { } /// ### [7.1.3 ToNumeric ( value )](https://tc39.es/ecma262/#sec-tonumeric) -pub(crate) fn to_numeric<'a>( +pub(crate) fn to_numeric<'a, 'gc>( agent: &mut Agent, - value: impl IntoValue, - mut gc: GcScope<'a, '_>, -) -> JsResult> { + value: impl IntoValue<'a>, + mut gc: GcScope<'gc, '_>, +) -> JsResult> { // 1. Let primValue be ? ToPrimitive(value, number). let prim_value = to_primitive(agent, value, Some(PreferredType::Number), gc.reborrow())?.unbind(); @@ -285,12 +252,12 @@ pub(crate) fn to_numeric_primitive<'a>( to_number_primitive(agent, prim_value, gc).map(|n| n.into_numeric()) } -pub(crate) fn try_to_number<'gc>( +pub(crate) fn try_to_number<'a, 'gc>( agent: &mut Agent, - argument: impl IntoValue, + argument: impl IntoValue<'a>, gc: NoGcScope<'gc, '_>, ) -> Option>> { - let argument = argument.into_value(); + let argument = argument.into_value().unbind().bind(gc); if let Ok(argument) = Primitive::try_from(argument) { Some(to_number_primitive(agent, argument, gc)) } else { @@ -299,21 +266,25 @@ pub(crate) fn try_to_number<'gc>( } /// ### [7.1.4 ToNumber ( argument )](https://tc39.es/ecma262/#sec-tonumber) -pub(crate) fn to_number<'gc>( +pub(crate) fn to_number<'a, 'gc>( agent: &mut Agent, - argument: impl IntoValue, + argument: impl IntoValue<'a>, mut gc: GcScope<'gc, '_>, ) -> JsResult> { - let argument = argument.into_value(); + let argument = argument.into_value().unbind().bind(gc.nogc()); if let Ok(argument) = Primitive::try_from(argument) { - to_number_primitive(agent, argument, gc.into_nogc()) + to_number_primitive(agent, argument.unbind(), gc.into_nogc()) } else { // 7. Assert: argument is an Object. let argument = Object::try_from(argument).unwrap(); // 8. Let primValue be ? ToPrimitive(argument, number). - let prim_value = - to_primitive_object(agent, argument, Some(PreferredType::Number), gc.reborrow())? - .unbind(); + let prim_value = to_primitive_object( + agent, + argument.unbind(), + Some(PreferredType::Number), + gc.reborrow(), + )? + .unbind(); let gc = gc.into_nogc(); let prim_value = prim_value.bind(gc); // 9. Assert: primValue is not an Object. @@ -325,7 +296,7 @@ pub(crate) fn to_number<'gc>( /// ### [7.1.4 ToNumber ( argument )](https://tc39.es/ecma262/#sec-tonumber) pub(crate) fn to_number_primitive<'gc>( agent: &mut Agent, - argument: Primitive<'gc>, + argument: Primitive, gc: NoGcScope<'gc, '_>, ) -> JsResult> { match argument { @@ -345,7 +316,7 @@ pub(crate) fn to_number_primitive<'gc>( gc, )), // 1. If argument is a Number, return argument. - Primitive::Number(idx) => Ok(idx.into()), + Primitive::Number(idx) => Ok(idx.unbind().bind(gc).into()), Primitive::Integer(idx) => Ok(idx.into()), Primitive::SmallF64(idx) => Ok(idx.into()), Primitive::BigInt(_) | Primitive::SmallBigInt(_) => Err(agent @@ -996,12 +967,12 @@ pub(crate) fn to_big_uint64_big_int(agent: &mut Agent, n: BigInt) -> u64 { } } -pub(crate) fn try_to_string<'gc>( +pub(crate) fn try_to_string<'a, 'gc>( agent: &mut Agent, - argument: impl IntoValue, + argument: impl IntoValue<'a>, gc: NoGcScope<'gc, '_>, ) -> TryResult>> { - let argument = argument.into_value(); + let argument = argument.into_value().unbind().bind(gc); if let Ok(argument) = Primitive::try_from(argument) { TryResult::Continue(to_string_primitive(agent, argument, gc)) } else { @@ -1010,21 +981,26 @@ pub(crate) fn try_to_string<'gc>( } /// ### [7.1.17 ToString ( argument )](https://tc39.es/ecma262/#sec-tostring) -pub(crate) fn to_string<'gc>( +pub(crate) fn to_string<'a, 'gc>( agent: &mut Agent, - argument: impl IntoValue, + argument: impl IntoValue<'a>, mut gc: GcScope<'gc, '_>, ) -> JsResult> { - let argument = argument.into_value(); + let argument = argument.into_value().unbind().bind(gc.nogc()); // 1. If argument is a String, return argument. if let Ok(argument) = Primitive::try_from(argument) { - to_string_primitive(agent, argument, gc.into_nogc()) + to_string_primitive(agent, argument.unbind(), gc.into_nogc()) } else { // 9. Assert: argument is an Object. assert!(Object::try_from(argument).is_ok()); // 10. Let primValue be ? ToPrimitive(argument, string). - let prim_value = - to_primitive(agent, argument, Some(PreferredType::String), gc.reborrow())?.unbind(); + let prim_value = to_primitive( + agent, + argument.unbind(), + Some(PreferredType::String), + gc.reborrow(), + )? + .unbind(); let gc = gc.into_nogc(); let prim_value = prim_value.bind(gc); // 11. Assert: primValue is not an Object. @@ -1084,6 +1060,7 @@ pub(crate) fn to_object<'a>( argument: Value, gc: NoGcScope<'a, '_>, ) -> JsResult> { + let argument = argument.bind(gc); match argument { Value::Undefined | Value::Null => Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -1103,7 +1080,7 @@ pub(crate) fn to_object<'a>( .heap .create(PrimitiveObjectHeapData { object_index: None, - data: PrimitiveObjectData::String(str), + data: PrimitiveObjectData::String(str.unbind()), }) .into_object()), Value::SmallString(str) => Ok(agent @@ -1118,7 +1095,7 @@ pub(crate) fn to_object<'a>( .heap .create(PrimitiveObjectHeapData { object_index: None, - data: PrimitiveObjectData::Symbol(symbol), + data: PrimitiveObjectData::Symbol(symbol.unbind()), }) .into_object()), // Return a new Number object whose [[NumberData]] internal slot is set to argument. @@ -1126,7 +1103,7 @@ pub(crate) fn to_object<'a>( .heap .create(PrimitiveObjectHeapData { object_index: None, - data: PrimitiveObjectData::Number(number), + data: PrimitiveObjectData::Number(number.unbind()), }) .into_object()), Value::Integer(integer) => Ok(agent @@ -1148,7 +1125,7 @@ pub(crate) fn to_object<'a>( .heap .create(PrimitiveObjectHeapData { object_index: None, - data: PrimitiveObjectData::BigInt(bigint), + data: PrimitiveObjectData::BigInt(bigint.unbind()), }) .into_object()), Value::SmallBigInt(bigint) => Ok(agent @@ -1163,11 +1140,11 @@ pub(crate) fn to_object<'a>( } /// ### [7.1.19 ToPropertyKey ( argument )](https://tc39.es/ecma262/#sec-topropertykey) -pub(crate) fn to_property_key<'a>( +pub(crate) fn to_property_key<'a, 'gc>( agent: &mut Agent, - argument: impl IntoValue, - gc: GcScope<'a, '_>, -) -> JsResult> { + argument: impl IntoValue<'a>, + gc: GcScope<'gc, '_>, +) -> JsResult> { // Note: Fast path and non-standard special case combined. Usually the // argument is already a valid property key. We also need to parse integer // strings back into integer property keys. @@ -1201,12 +1178,12 @@ pub(crate) fn to_property_key<'a>( /// /// If a complex case is found, the function returns None to indicate that the /// caller should handle the uncommon case. -pub(crate) fn to_property_key_simple<'a>( +pub(crate) fn to_property_key_simple<'a, 'gc>( agent: &Agent, - argument: impl IntoValue, - _: NoGcScope<'a, '_>, -) -> TryResult> { - let argument = argument.into_value(); + argument: impl IntoValue<'a>, + gc: NoGcScope<'gc, '_>, +) -> TryResult> { + let argument = argument.into_value().unbind().bind(gc); match argument { Value::String(_) | Value::SmallString(_) => { let (str, string_key) = match &argument { @@ -1243,11 +1220,11 @@ pub(crate) fn to_property_key_simple<'a>( } } -pub(crate) fn to_property_key_complex<'a>( +pub(crate) fn to_property_key_complex<'a, 'gc>( agent: &mut Agent, - argument: impl IntoValue, - mut gc: GcScope<'a, '_>, -) -> JsResult> { + argument: impl IntoValue<'a>, + mut gc: GcScope<'gc, '_>, +) -> JsResult> { // 1. Let key be ? ToPrimitive(argument, hint String). let key = to_primitive(agent, argument, Some(PreferredType::String), gc.reborrow())?.unbind(); let gc = gc.into_nogc(); @@ -1352,19 +1329,24 @@ pub(crate) fn canonical_numeric_index_string<'gc>( None } +/// 2. If integer is not in the inclusive interval from 0 to 2**53 - 1, throw a +/// RangeError exception. +pub(crate) fn validate_index(agent: &mut Agent, value: i64, gc: NoGcScope) -> JsResult { + if !(0..=(SmallInteger::MAX_NUMBER)).contains(&value) { + return Err(agent.throw_exception_with_static_message( + ExceptionType::RangeError, + "Index is out of range", + gc, + )); + } + Ok(value as u64) +} + /// ### [7.1.22 ToIndex ( value )](https://tc39.es/ecma262/#sec-toindex) pub(crate) fn to_index(agent: &mut Agent, argument: Value, mut gc: GcScope) -> JsResult { // Fast path: A safe integer is already an integer. if let Value::Integer(integer) = argument { - let integer = integer.into_i64(); - if !(0..=(SmallInteger::MAX_NUMBER)).contains(&integer) { - return Err(agent.throw_exception_with_static_message( - ExceptionType::RangeError, - "Index is out of range", - gc.nogc(), - )); - } - return Ok(integer); + return validate_index(agent, integer.into_i64(), gc.into_nogc()).map(|i| i as i64); } // TODO: This can be heavily optimized by inlining `to_integer_or_infinity`. @@ -1372,16 +1354,8 @@ pub(crate) fn to_index(agent: &mut Agent, argument: Value, mut gc: GcScope) -> J let integer = to_integer_or_infinity(agent, argument, gc.reborrow())?.into_i64(); // 2. If integer is not in the inclusive interval from 0 to 2**53 - 1, throw a RangeError exception. - if !(0..=(SmallInteger::MAX_NUMBER)).contains(&integer) { - return Err(agent.throw_exception_with_static_message( - ExceptionType::RangeError, - "Index is out of range", - gc.nogc(), - )); - } - // 3. Return integer. - Ok(integer) + validate_index(agent, integer, gc.into_nogc()).map(|i| i as i64) } /// ### [7.1.22 ToIndex ( value )](https://tc39.es/ecma262/#sec-toindex) @@ -1392,15 +1366,9 @@ pub(crate) fn try_to_index( ) -> TryResult> { // Fast path: A safe integer is already an integer. if let Value::Integer(integer) = argument { - let integer = integer.into_i64(); - if !(0..=(SmallInteger::MAX_NUMBER)).contains(&integer) { - return TryResult::Continue(Err(agent.throw_exception_with_static_message( - ExceptionType::RangeError, - "Index is out of range", - gc, - ))); - } - return TryResult::Continue(Ok(integer)); + return TryResult::Continue( + validate_index(agent, integer.into_i64(), gc).map(|i| i as i64), + ); } // TODO: This can be heavily optimized by inlining `to_integer_or_infinity`. @@ -1411,16 +1379,8 @@ pub(crate) fn try_to_index( }; // 2. If integer is not in the inclusive interval from 0 to 2**53 - 1, throw a RangeError exception. - if !(0..=(SmallInteger::MAX_NUMBER)).contains(&integer) { - return TryResult::Continue(Err(agent.throw_exception_with_static_message( - ExceptionType::RangeError, - "Index is out of range", - gc, - ))); - } - // 3. Return integer. - TryResult::Continue(Ok(integer)) + TryResult::Continue(validate_index(agent, integer, gc).map(|i| i as i64)) } /// Helper function to check if a `char` is trimmable. diff --git a/nova_vm/src/ecmascript/builders/builtin_function_builder.rs b/nova_vm/src/ecmascript/builders/builtin_function_builder.rs index 9f9ad183e..644f1207f 100644 --- a/nova_vm/src/ecmascript/builders/builtin_function_builder.rs +++ b/nova_vm/src/ecmascript/builders/builtin_function_builder.rs @@ -14,6 +14,7 @@ use crate::{ OrdinaryObject, PropertyKey, String, Value, BUILTIN_STRING_MEMORY, }, }, + engine::context::Bindable, heap::{ element_array::ElementDescriptor, indexes::{BuiltinFunctionIndex, ObjectIndex}, @@ -54,7 +55,7 @@ pub struct CreatorProperties( Vec<( PropertyKey<'static>, Option, - Option, + Option>, )>, ); @@ -324,7 +325,7 @@ impl<'agent, P, B> BuiltinFunctionBuilder<'agent, P, CreatorLength, CreatorName, pub fn with_data_property( self, key: PropertyKey<'static>, - value: Value, + value: Value<'static>, ) -> BuiltinFunctionBuilder<'agent, P, CreatorLength, CreatorName, B, CreatorProperties> { let object_index = Some(self.object_index.unwrap_or_else(|| { self.agent.heap.objects.push(None); @@ -339,9 +340,9 @@ impl<'agent, P, B> BuiltinFunctionBuilder<'agent, P, CreatorLength, CreatorName, ( PropertyKey::from(BUILTIN_STRING_MEMORY.name), Some(ElementDescriptor::ReadOnlyUnenumerableConfigurableData), - Some(self.name.0.into()), + Some(self.name.0.unbind().into()), ), - (key, None, Some(value)), + (key.unbind(), None, Some(value.unbind())), ]; BuiltinFunctionBuilder { agent: self.agent, @@ -364,7 +365,7 @@ impl<'agent, P, B> BuiltinFunctionBuilder<'agent, P, CreatorLength, CreatorName, ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ), ) -> BuiltinFunctionBuilder<'agent, P, CreatorLength, CreatorName, B, CreatorProperties> { let object_index = Some(self.object_index.unwrap_or_else(|| { @@ -384,7 +385,7 @@ impl<'agent, P, B> BuiltinFunctionBuilder<'agent, P, CreatorLength, CreatorName, ( PropertyKey::from(BUILTIN_STRING_MEMORY.name), Some(ElementDescriptor::ReadOnlyUnenumerableConfigurableData), - Some(self.name.0.into()), + Some(self.name.0.unbind().into()), ), property, ]; @@ -407,9 +408,9 @@ impl<'agent, P, L, N, B> BuiltinFunctionBuilder<'agent, P, L, N, B, CreatorPrope pub fn with_data_property( mut self, key: PropertyKey<'static>, - value: Value, + value: Value<'static>, ) -> BuiltinFunctionBuilder<'agent, P, L, N, B, CreatorProperties> { - self.properties.0.push((key, None, Some(value))); + self.properties.0.push((key, None, Some(value.unbind()))); BuiltinFunctionBuilder { agent: self.agent, this: self.this, @@ -431,7 +432,7 @@ impl<'agent, P, L, N, B> BuiltinFunctionBuilder<'agent, P, L, N, B, CreatorPrope ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ), ) -> BuiltinFunctionBuilder<'agent, P, L, N, B, CreatorProperties> { let builder = PropertyBuilder::new(self.agent); diff --git a/nova_vm/src/ecmascript/builders/ordinary_object_builder.rs b/nova_vm/src/ecmascript/builders/ordinary_object_builder.rs index c53f84769..f6a10efcb 100644 --- a/nova_vm/src/ecmascript/builders/ordinary_object_builder.rs +++ b/nova_vm/src/ecmascript/builders/ordinary_object_builder.rs @@ -33,7 +33,7 @@ pub struct CreatorProperties( Vec<( PropertyKey<'static>, Option, - Option, + Option>, )>, ); @@ -128,7 +128,7 @@ impl<'agent, P> OrdinaryObjectBuilder<'agent, P, NoProperties> { impl

OrdinaryObjectBuilder<'_, P, CreatorProperties> { #[must_use] - pub fn with_data_property(mut self, key: PropertyKey<'static>, value: Value) -> Self { + pub fn with_data_property(mut self, key: PropertyKey<'static>, value: Value<'static>) -> Self { self.properties.0.push((key, None, Some(value))); OrdinaryObjectBuilder { agent: self.agent, @@ -148,7 +148,7 @@ impl

OrdinaryObjectBuilder<'_, P, CreatorProperties> { ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ), ) -> Self { let builder = PropertyBuilder::new(self.agent); @@ -165,7 +165,7 @@ impl

OrdinaryObjectBuilder<'_, P, CreatorProperties> { } #[must_use] - pub fn with_constructor_property(mut self, constructor: BuiltinFunction) -> Self { + pub fn with_constructor_property(mut self, constructor: BuiltinFunction<'static>) -> Self { let property = PropertyBuilder::new(self.agent) .with_enumerable(false) .with_key(BUILTIN_STRING_MEMORY.constructor.into()) diff --git a/nova_vm/src/ecmascript/builders/property_builder.rs b/nova_vm/src/ecmascript/builders/property_builder.rs index 71dd01b8f..4dda35e62 100644 --- a/nova_vm/src/ecmascript/builders/property_builder.rs +++ b/nova_vm/src/ecmascript/builders/property_builder.rs @@ -38,10 +38,10 @@ pub struct CreatorGetSetAccessor { } #[derive(Clone, Copy)] -pub struct CreatorValue(Value); +pub struct CreatorValue(Value<'static>); #[derive(Clone, Copy)] -pub struct CreatorReadOnlyValue(Value); +pub struct CreatorReadOnlyValue(Value<'static>); pub struct PropertyBuilder<'agent, K: 'static, D> { pub(crate) agent: &'agent mut Agent, @@ -76,7 +76,7 @@ impl<'agent, D> PropertyBuilder<'agent, NoKey, D> { } impl<'agent, K> PropertyBuilder<'agent, K, NoDefinition> { - pub fn with_value(self, value: Value) -> PropertyBuilder<'agent, K, CreatorValue> { + pub fn with_value(self, value: Value<'static>) -> PropertyBuilder<'agent, K, CreatorValue> { PropertyBuilder { agent: self.agent, key: self.key, @@ -88,7 +88,7 @@ impl<'agent, K> PropertyBuilder<'agent, K, NoDefinition> { pub fn with_value_readonly( self, - value: Value, + value: Value<'static>, ) -> PropertyBuilder<'agent, K, CreatorReadOnlyValue> { PropertyBuilder { agent: self.agent, @@ -101,7 +101,7 @@ impl<'agent, K> PropertyBuilder<'agent, K, NoDefinition> { pub fn with_value_creator( self, - creator: impl FnOnce(&mut Agent) -> Value, + creator: impl FnOnce(&mut Agent) -> Value<'static>, ) -> PropertyBuilder<'agent, K, CreatorValue> { let value = creator(self.agent); PropertyBuilder { @@ -115,7 +115,7 @@ impl<'agent, K> PropertyBuilder<'agent, K, NoDefinition> { pub fn with_value_creator_readonly( self, - creator: impl FnOnce(&mut Agent) -> Value, + creator: impl FnOnce(&mut Agent) -> Value<'static>, ) -> PropertyBuilder<'agent, K, CreatorReadOnlyValue> { let value = creator(self.agent); PropertyBuilder { @@ -248,7 +248,7 @@ impl PropertyBuilder<'_, CreatorKey, CreatorValue> { ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ) { ( self.key.0, @@ -264,7 +264,7 @@ impl PropertyBuilder<'_, CreatorKey, CreatorReadOnlyValue> { ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ) { ( self.key.0, @@ -280,7 +280,7 @@ impl PropertyBuilder<'_, CreatorKey, CreatorGetAccessor> { ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ) { ( self.key.0, @@ -300,7 +300,7 @@ impl PropertyBuilder<'_, CreatorKey, CreatorSetAccess> { ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ) { ( self.key.0, @@ -320,7 +320,7 @@ impl PropertyBuilder<'_, CreatorKey, CreatorGetSetAccessor> { ) -> ( PropertyKey<'static>, Option, - Option, + Option>, ) { ( self.key.0, diff --git a/nova_vm/src/ecmascript/builtins/arguments.rs b/nova_vm/src/ecmascript/builtins/arguments.rs index e1f84df5f..6f0c8190b 100644 --- a/nova_vm/src/ecmascript/builtins/arguments.rs +++ b/nova_vm/src/ecmascript/builtins/arguments.rs @@ -26,7 +26,7 @@ //! //! ECMAScript implementations of arguments exotic objects have historically contained an accessor property named "caller". Prior to ECMAScript 2017, this specification included the definition of a throwing "caller" property on ordinary arguments objects. Since implementations do not contain this extension any longer, ECMAScript 2017 dropped the requirement for a throwing "caller" accessor. -use crate::engine::context::NoGcScope; +use crate::engine::context::{Bindable, NoGcScope}; use crate::engine::unwrap_try; use crate::{ ecmascript::{ @@ -129,7 +129,9 @@ pub(crate) fn create_unmapped_arguments_object<'a>( ) -> Object<'a> { // 1. Let len be the number of elements in argumentsList. let len = arguments_list.len(); - let len = Number::from_f64(agent, len as f64, gc).into_value(); + let len = Number::from_f64(agent, len as f64, gc) + .into_value() + .unbind(); // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »). let obj = ordinary_object_create_with_intrinsics(agent, Some(ProtoIntrinsics::Object), None, gc); diff --git a/nova_vm/src/ecmascript/builtins/array.rs b/nova_vm/src/ecmascript/builtins/array.rs index a769aebfb..136a0c480 100644 --- a/nova_vm/src/ecmascript/builtins/array.rs +++ b/nova_vm/src/ecmascript/builtins/array.rs @@ -28,7 +28,7 @@ use crate::{ }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, unwrap_try, Scoped, TryResult, }, @@ -134,13 +134,13 @@ impl<'a> Array<'a> { } #[inline] - fn try_get_backing( + fn try_get_backing<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { if let Some(object_index) = self.get_backing_object(agent) { // If backing object exists, then we might have properties there object_index.try_get(agent, property_key, receiver, gc) @@ -154,13 +154,13 @@ impl<'a> Array<'a> { } #[inline] - fn internal_get_backing( + fn internal_get_backing<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let property_key = property_key.bind(gc.nogc()); if let Some(object_index) = self.get_backing_object(agent) { // If backing object exists, then we might have properties there @@ -178,20 +178,20 @@ impl<'a> Array<'a> { } #[inline] - pub(crate) fn as_slice(self, arena: &impl ArrayHeapIndexable<'a>) -> &[Option] { + pub(crate) fn as_slice(self, arena: &impl ArrayHeapIndexable<'a>) -> &[Option>] { let elements = arena[self].elements; &arena.as_ref()[elements] } #[inline] - pub(crate) fn as_mut_slice(self, agent: &mut Agent) -> &mut [Option] { + pub(crate) fn as_mut_slice(self, agent: &mut Agent) -> &mut [Option>] { let elements = agent[self].elements; &mut agent[elements] } } -impl IntoValue for Array<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Array<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -214,16 +214,16 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: Array) -> Self { - Self::Array(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Array<'a>) -> Self { + Self::Array(value) } } -impl TryFrom for Array<'_> { +impl<'a> TryFrom> for Array<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Array(data) => Ok(data), _ => Err(()), @@ -503,13 +503,13 @@ impl<'a> InternalMethods<'a> for Array<'a> { Ok(false) } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { TryResult::Continue(self.len(agent).into()) } else if let PropertyKey::Integer(index) = property_key { @@ -559,13 +559,13 @@ impl<'a> InternalMethods<'a> for Array<'a> { } } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - mut gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let property_key = property_key.bind(gc.nogc()); if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { Ok(self.len(agent).into()) @@ -573,12 +573,7 @@ impl<'a> InternalMethods<'a> for Array<'a> { let index = index.into_i64(); if !ARRAY_INDEX_RANGE.contains(&index) { // Negative indexes and indexes over 2^32 - 2 go into backing store - return self.internal_get_backing( - agent, - property_key.unbind(), - receiver, - gc.reborrow(), - ); + return self.internal_get_backing(agent, property_key.unbind(), receiver, gc); } let index = index as u32; let elements = agent[self].elements; @@ -616,7 +611,7 @@ impl<'a> InternalMethods<'a> for Array<'a> { } } } else { - self.internal_get_backing(agent, property_key.unbind(), receiver, gc.reborrow()) + self.internal_get_backing(agent, property_key.unbind(), receiver, gc) } } @@ -797,7 +792,7 @@ fn ordinary_define_own_property_for_array( // 2. If current is undefined, then if current_descriptor.is_none() && current_value.is_none() { - // Hole + // Holegc // a. If extensible is false, return false. if !elements.writable() { diff --git a/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs index be1a7fa42..c45c735a8 100644 --- a/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs @@ -3,7 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::type_conversion::to_uint32_number; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -97,9 +97,10 @@ pub(crate) fn array_species_create<'a>( length: usize, mut gc: GcScope<'a, '_>, ) -> JsResult> { - let original_array = original_array.bind(gc.nogc()); + let nogc = gc.nogc(); + let original_array = original_array.bind(nogc); // 1. Let isArray be ? IsArray(originalArray). - let original_is_array = is_array(agent, original_array.into_value(), gc.nogc())?; + let original_is_array = is_array(agent, original_array, nogc)?; // 2. If isArray is false, return ? ArrayCreate(length). if !original_is_array { let new_array = array_create(agent, length, length, None, gc.into_nogc())?; @@ -111,7 +112,9 @@ pub(crate) fn array_species_create<'a>( original_array.unbind(), BUILTIN_STRING_MEMORY.constructor.into(), gc.reborrow(), - )?; + )? + .unbind() + .bind(gc.nogc()); // 4. If IsConstructor(C) is true, then if let Some(c_func) = is_constructor(agent, c) { // a. Let thisRealm be the current Realm Record. @@ -131,10 +134,12 @@ pub(crate) fn array_species_create<'a>( // a. Set C to ? Get(C, @@species). c = get( agent, - c_obj, + c_obj.unbind(), WellKnownSymbolIndexes::Species.into(), gc.reborrow(), - )?; + )? + .unbind() + .bind(gc.nogc()); // b. If C is null, set C to undefined. if c.is_null() { c = Value::Undefined; @@ -155,7 +160,13 @@ pub(crate) fn array_species_create<'a>( }; // 8. Return ? Construct(C, « 𝔽(length) »). let length = Value::from_f64(agent, length as f64, gc.nogc()); - construct(agent, c, Some(ArgumentsList(&[length])), None, gc) + construct( + agent, + c.unbind(), + Some(ArgumentsList(&[length.unbind()])), + None, + gc, + ) } /// ### [10.4.2.4 ArraySetLength ( A, Desc )](https://tc39.es/ecma262/#sec-arraysetlength) diff --git a/nova_vm/src/ecmascript/builtins/array_buffer.rs b/nova_vm/src/ecmascript/builtins/array_buffer.rs index 25b90389d..2757ad275 100644 --- a/nova_vm/src/ecmascript/builtins/array_buffer.rs +++ b/nova_vm/src/ecmascript/builtins/array_buffer.rs @@ -14,7 +14,7 @@ use crate::{ }, }, engine::{ - context::NoGcScope, + context::{Bindable, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, Scoped, }, @@ -158,16 +158,16 @@ impl<'a> IntoObject<'a> for ArrayBuffer<'a> { } } -impl IntoValue for ArrayBuffer<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for ArrayBuffer<'a> { + fn into_value(self) -> Value<'a> { self.into() } } -impl TryFrom for ArrayBuffer<'_> { +impl<'a> TryFrom> for ArrayBuffer<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::ArrayBuffer(base_index) => Ok(base_index), _ => Err(()), @@ -193,9 +193,9 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: ArrayBuffer) -> Self { - Self::ArrayBuffer(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: ArrayBuffer<'a>) -> Self { + Self::ArrayBuffer(value) } } diff --git a/nova_vm/src/ecmascript/builtins/array_buffer/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/array_buffer/abstract_operations.rs index 2ac766362..e8bf13c98 100644 --- a/nova_vm/src/ecmascript/builtins/array_buffer/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/array_buffer/abstract_operations.rs @@ -5,15 +5,12 @@ use super::{ArrayBuffer, ArrayBufferHeapData}; use crate::ecmascript::abstract_operations::type_conversion::to_index; use crate::ecmascript::types::{Numeric, Viewable}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_objects::get, execution::{agent::ExceptionType, Agent, JsResult}, - types::{ - DataBlock, Function, IntoFunction, Number, Object, PropertyKey, Value, - BUILTIN_STRING_MEMORY, - }, + types::{DataBlock, Function, IntoFunction, Number, Object, Value, BUILTIN_STRING_MEMORY}, }, heap::indexes::ArrayBufferIndex, Heap, @@ -202,6 +199,7 @@ pub(crate) fn get_array_buffer_max_byte_length_option( options: Value, mut gc: GcScope, ) -> JsResult> { + let options = options.bind(gc.nogc()); // 1. If options is not an Object, return EMPTY. let options = if let Ok(options) = Object::try_from(options) { options @@ -209,14 +207,18 @@ pub(crate) fn get_array_buffer_max_byte_length_option( return Ok(None); }; // 2. Let maxByteLength be ? Get(options, "maxByteLength"). - let property = PropertyKey::from(BUILTIN_STRING_MEMORY.maxByteLength); - let max_byte_length = get(agent, options, property, gc.reborrow())?; + let max_byte_length = get( + agent, + options.unbind(), + BUILTIN_STRING_MEMORY.maxByteLength.into(), + gc.reborrow(), + )?; // 3. If maxByteLength is undefined, return EMPTY. if max_byte_length.is_undefined() { return Ok(None); } // 4. Return ? ToIndex(maxByteLength). - to_index(agent, max_byte_length, gc).map(Some) + to_index(agent, max_byte_length.unbind(), gc).map(Some) } /// ### [25.1.3.7 HostResizeArrayBuffer ( buffer, newByteLength )](https://tc39.es/ecma262/#sec-hostresizearraybuffer) diff --git a/nova_vm/src/ecmascript/builtins/bound_function.rs b/nova_vm/src/ecmascript/builtins/bound_function.rs index 4fdf10c2e..72cea8520 100644 --- a/nova_vm/src/ecmascript/builtins/bound_function.rs +++ b/nova_vm/src/ecmascript/builtins/bound_function.rs @@ -5,7 +5,7 @@ use core::ops::{Index, IndexMut}; use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::rootable::{HeapRootData, HeapRootRef, Rootable}; use crate::engine::{unwrap_try, Scoped, TryResult}; use crate::{ @@ -80,8 +80,8 @@ impl BoundFunction<'_> { } } -impl IntoValue for BoundFunction<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for BoundFunction<'a> { + fn into_value(self) -> Value<'a> { Value::BoundFunction(self.unbind()) } } @@ -148,7 +148,7 @@ pub(crate) fn bound_function_create<'a>( object_index: None, length: 0, bound_target_function: target_function.unbind(), - bound_this, + bound_this: bound_this.unbind(), bound_arguments: elements, name: None, }; @@ -239,23 +239,23 @@ impl<'a> InternalMethods<'a> for BoundFunction<'a> { function_internal_has_property(self, agent, property_key, gc) } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { function_try_get(self, agent, property_key, receiver, gc) } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { function_internal_get(self, agent, property_key, receiver, gc) } @@ -305,13 +305,13 @@ impl<'a> InternalMethods<'a> for BoundFunction<'a> { /// argumentsList (a List of ECMAScript language values) and returns either /// a normal completion containing an ECMAScript language value or a throw /// completion. - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, _: Value, arguments_list: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let target be F.[[BoundTargetFunction]]. let target = agent[self].bound_target_function; // 2. Let boundThis be F.[[BoundThis]]. diff --git a/nova_vm/src/ecmascript/builtins/builtin_constructor.rs b/nova_vm/src/ecmascript/builtins/builtin_constructor.rs index 9c1b27f87..1ed334c9d 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_constructor.rs @@ -7,7 +7,7 @@ use core::ops::{Index, IndexMut}; use oxc_span::Span; use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::rootable::{HeapRootData, HeapRootRef, Rootable}; use crate::engine::{Scoped, TryResult}; use crate::{ @@ -98,8 +98,8 @@ impl<'a> From> for BuiltinConstructorFunction<'a> { } } -impl IntoValue for BuiltinConstructorFunction<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for BuiltinConstructorFunction<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -116,9 +116,9 @@ impl<'a> IntoFunction<'a> for BuiltinConstructorFunction<'a> { } } -impl From> for Value { - fn from(value: BuiltinConstructorFunction) -> Self { - Value::BuiltinConstructorFunction(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: BuiltinConstructorFunction<'a>) -> Self { + Value::BuiltinConstructorFunction(value) } } @@ -134,10 +134,10 @@ impl<'a> From> for Function<'a> { } } -impl TryFrom for BuiltinConstructorFunction<'_> { +impl<'a> TryFrom> for BuiltinConstructorFunction<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::BuiltinConstructorFunction(data) => Ok(data), _ => Err(()), @@ -220,10 +220,7 @@ impl<'a> InternalSlots<'a> for BuiltinConstructorFunction<'a> { } fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) { - assert!(agent[self] - .object_index - .replace(backing_object.unbind()) - .is_none()); + assert!(agent[self].object_index.replace(backing_object).is_none()); } fn create_backing_object(self, agent: &mut Agent) -> OrdinaryObject<'static> { @@ -279,23 +276,23 @@ impl<'a> InternalMethods<'a> for BuiltinConstructorFunction<'a> { function_internal_has_property(self, agent, property_key, gc) } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { function_try_get(self, agent, property_key, receiver, gc) } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { function_internal_get(self, agent, property_key, receiver, gc) } @@ -345,13 +342,13 @@ impl<'a> InternalMethods<'a> for BuiltinConstructorFunction<'a> { /// (a List of ECMAScript language values) and returns either a normal /// completion containing an ECMAScript language value or a throw /// completion. - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, _: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). // ii. If NewTarget is undefined, throw a TypeError exception. Err(agent.throw_exception_with_static_message( diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index ad5a8a3af..dc7c12096 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -5,7 +5,7 @@ use core::ops::{Deref, Index, IndexMut}; use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::rootable::{HeapRootData, HeapRootRef, Rootable}; use crate::engine::{Scoped, TryResult}; use crate::{ @@ -31,10 +31,10 @@ use crate::{ }; #[derive(Debug, Clone, Copy, Default)] -pub struct ArgumentsList<'a>(pub(crate) &'a [Value]); +pub struct ArgumentsList<'a>(pub(crate) &'a [Value<'static>]); impl<'a> Deref for ArgumentsList<'a> { - type Target = &'a [Value]; + type Target = &'a [Value<'static>]; fn deref(&self) -> &Self::Target { &self.0 @@ -48,9 +48,15 @@ impl ArgumentsList<'_> { } } -pub type RegularFn = fn(&mut Agent, Value, ArgumentsList, GcScope) -> JsResult; -pub type ConstructorFn = - fn(&mut Agent, Value, ArgumentsList, Option, GcScope) -> JsResult; +pub type RegularFn = + for<'gc> fn(&mut Agent, Value, ArgumentsList, GcScope<'gc, '_>) -> JsResult>; +pub type ConstructorFn = for<'gc> fn( + &mut Agent, + Value, + ArgumentsList, + Option, + GcScope<'gc, '_>, +) -> JsResult>; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Behaviour { @@ -165,8 +171,8 @@ impl<'a> From> for BuiltinFunction<'a> { } } -impl IntoValue for BuiltinFunction<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for BuiltinFunction<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -183,9 +189,9 @@ impl<'a> IntoFunction<'a> for BuiltinFunction<'a> { } } -impl From> for Value { - fn from(value: BuiltinFunction) -> Self { - Value::BuiltinFunction(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: BuiltinFunction<'a>) -> Self { + Value::BuiltinFunction(value) } } @@ -313,23 +319,23 @@ impl<'a> InternalMethods<'a> for BuiltinFunction<'a> { function_internal_has_property(self, agent, property_key, gc) } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { function_try_get(self, agent, property_key, receiver, gc) } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { function_internal_get(self, agent, property_key, receiver, gc) } @@ -379,13 +385,13 @@ impl<'a> InternalMethods<'a> for BuiltinFunction<'a> { /// (a List of ECMAScript language values) and returns either a normal /// completion containing an ECMAScript language value or a throw /// completion. - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, this_argument: Value, arguments_list: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). builtin_call_or_construct(agent, self, Some(this_argument), arguments_list, None, gc) } @@ -416,14 +422,14 @@ impl<'a> InternalMethods<'a> for BuiltinFunction<'a> { /// uninitialized), argumentsList (a List of ECMAScript language values), and /// newTarget (a constructor or undefined) and returns either a normal /// completion containing an ECMAScript language value or a throw completion. -pub(crate) fn builtin_call_or_construct( +pub(crate) fn builtin_call_or_construct<'gc>( agent: &mut Agent, f: BuiltinFunction, this_argument: Option, arguments_list: ArgumentsList, new_target: Option, - gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { let f = f.bind(gc.nogc()); let new_target = new_target.map(|f| f.bind(gc.nogc())); // 1. Let callerContext be the running execution context. diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_constructor.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_constructor.rs index 7f70b2629..54ff0dfd4 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_constructor.rs @@ -29,13 +29,13 @@ impl BuiltinIntrinsicConstructor for AsyncFunctionConstructor { } impl AsyncFunctionConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 2. If bodyArg is not present, set bodyArg to the empty String. let (parameter_args, body_arg) = if arguments.is_empty() { (&[] as &[Value], String::EMPTY_STRING.into_value()) diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_prototype.rs index 2a89a38a6..dbf86129d 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/async_function_prototype.rs @@ -6,7 +6,7 @@ use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, execution::{Agent, RealmIdentifier}, - types::BUILTIN_STRING_MEMORY, + types::{IntoValue, BUILTIN_STRING_MEMORY}, }, heap::WellKnownSymbolIndexes, }; diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs index b9e2c65d1..ec9c1b734 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs @@ -7,7 +7,7 @@ use core::{ ops::{Index, IndexMut}, }; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ builtins::{ @@ -61,6 +61,7 @@ impl AwaitReactionIdentifier { value: Value, mut gc: GcScope, ) { + let value = value.bind(gc.nogc()); // [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await) // 3. c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. let execution_context = agent[self].execution_context.take().unwrap(); @@ -74,13 +75,13 @@ impl AwaitReactionIdentifier { PromiseReactionType::Fulfill => vm.resume( agent, async_function.get_executable(agent), - value, + value.unbind(), gc.reborrow(), ), PromiseReactionType::Reject => vm.resume_throw( agent, async_function.get_executable(agent), - value, + value.unbind(), gc.reborrow(), ), }; @@ -96,7 +97,7 @@ impl AwaitReactionIdentifier { // i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). agent[self] .return_promise_capability - .resolve(agent, result, gc); + .resolve(agent, result.unbind(), gc); } ExecutionResult::Throw(err) => { // [27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )](https://tc39.es/ecma262/#sec-asyncblockstart) @@ -118,7 +119,7 @@ impl AwaitReactionIdentifier { // which resume execution of the function. let handler = PromiseReactionHandler::Await(self); // 2. Let promise be ? PromiseResolve(%Promise%, value). - let promise = Promise::resolve(agent, awaited_value, gc.reborrow()) + let promise = Promise::resolve(agent, awaited_value.unbind(), gc.reborrow()) .unbind() .bind(gc.nogc()); // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected). diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_function_objects/async_generator_function_constructor.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_function_objects/async_generator_function_constructor.rs index 4e309ca79..d2bbfffd6 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_function_objects/async_generator_function_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_function_objects/async_generator_function_constructor.rs @@ -30,13 +30,13 @@ impl BuiltinIntrinsicConstructor for AsyncGeneratorFunctionConstructor { } impl AsyncGeneratorFunctionConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 2. If bodyArg is not present, set bodyArg to the empty String. let (parameter_args, body_arg) = if arguments.is_empty() { (&[] as &[Value], String::EMPTY_STRING.into_value()) diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs index 690c646b5..9840b885b 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs @@ -26,7 +26,7 @@ use crate::{ }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, Executable, ExecutionResult, Scoped, SuspendedVm, }, @@ -247,15 +247,16 @@ impl AsyncGenerator<'_> { value: Value, mut gc: GcScope, ) { + let value = value.bind(gc.nogc()); if self.is_draining_queue(agent) { // We're coming here because return was called. match reaction_type { PromiseReactionType::Fulfill => { // AsyncGeneratorAwaitReturn onFulfilled - async_generator_await_return_on_fulfilled(agent, self, value, gc); + async_generator_await_return_on_fulfilled(agent, self, value.unbind(), gc); } PromiseReactionType::Reject => { - async_generator_await_return_on_rejected(agent, self, value, gc); + async_generator_await_return_on_rejected(agent, self, value.unbind(), gc); } } return; @@ -278,10 +279,10 @@ impl AsyncGenerator<'_> { let executable = agent[self].executable.unwrap(); match reaction_type { PromiseReactionType::Fulfill => { - vm.resume(agent, executable, value, gc.reborrow()) + vm.resume(agent, executable, value.unbind(), gc.reborrow()) } PromiseReactionType::Reject => { - vm.resume_throw(agent, executable, value, gc.reborrow()) + vm.resume_throw(agent, executable, value.unbind(), gc.reborrow()) } } } @@ -291,11 +292,11 @@ impl AsyncGenerator<'_> { // ? Yield ( ? Await ( Value ) ), so Yield doesn't get // performed at all and value is just thrown. let executable = agent[self].executable.unwrap(); - vm.resume_throw(agent, executable, value, gc.reborrow()) + vm.resume_throw(agent, executable, value.unbind(), gc.reborrow()) } else { async_generator_yield( agent, - value, + value.unbind(), scoped_generator.clone(), vm, gc.reborrow(), @@ -308,7 +309,7 @@ impl AsyncGenerator<'_> { // 3. If awaited is a throw completion, return ? awaited. if reaction_type == PromiseReactionType::Reject { let executable = agent[self].executable.unwrap(); - vm.resume_throw(agent, executable, value, gc.reborrow()) + vm.resume_throw(agent, executable, value.unbind(), gc.reborrow()) } else { // TODO: vm.resume_return(agent, executable, value, gc.reborrow()) // 4. Assert: awaited is a normal completion. @@ -317,12 +318,12 @@ impl AsyncGenerator<'_> { } } }; - resume_handle_result(agent, execution_result, scoped_generator, gc); + resume_handle_result(agent, execution_result.unbind(), scoped_generator, gc); } } -impl IntoValue for AsyncGenerator<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for AsyncGenerator<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -333,9 +334,9 @@ impl<'a> IntoObject<'a> for AsyncGenerator<'a> { } } -impl From> for Value { - fn from(val: AsyncGenerator) -> Self { - Value::AsyncGenerator(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: AsyncGenerator<'a>) -> Self { + Value::AsyncGenerator(value) } } @@ -345,10 +346,10 @@ impl<'a> From> for Object<'a> { } } -impl TryFrom for AsyncGenerator<'_> { +impl<'a> TryFrom> for AsyncGenerator<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { if let Value::AsyncGenerator(value) = value { Ok(value) } else { @@ -362,7 +363,7 @@ impl<'a> TryFrom> for AsyncGenerator<'a> { fn try_from(value: Object) -> Result { if let Object::AsyncGenerator(value) = value { - Ok(value) + Ok(value.unbind()) } else { Err(()) } @@ -447,7 +448,7 @@ pub(crate) enum AsyncGeneratorAwaitKind { #[derive(Debug)] pub(crate) enum AsyncGeneratorState { SuspendedStart { - arguments: Box<[Value]>, + arguments: Box<[Value<'static>]>, execution_context: ExecutionContext, queue: VecDeque, }, @@ -511,16 +512,39 @@ impl AsyncGeneratorState { #[derive(Debug)] pub(crate) struct AsyncGeneratorRequest { /// \[\[Completion]] - pub(crate) completion: AsyncGeneratorRequestCompletion, + pub(crate) completion: AsyncGeneratorRequestCompletion<'static>, /// \[\[Capability]] pub(crate) capability: PromiseCapability, } #[derive(Debug, Clone, Copy)] -pub(crate) enum AsyncGeneratorRequestCompletion { - Ok(Value), +pub(crate) enum AsyncGeneratorRequestCompletion<'a> { + Ok(Value<'a>), Err(JsError), - Return(Value), + Return(Value<'a>), +} + +// SAFETY: Property implemented as a lifetime transmute. +unsafe impl Bindable for AsyncGeneratorRequestCompletion<'_> { + type Of<'a> = AsyncGeneratorRequestCompletion<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + match self { + Self::Ok(value) => AsyncGeneratorRequestCompletion::Ok(value.unbind()), + Self::Err(js_error) => AsyncGeneratorRequestCompletion::Err(js_error), + Self::Return(value) => AsyncGeneratorRequestCompletion::Return(value.unbind()), + } + } + + #[inline(always)] + fn bind<'a>(self, gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + match self { + Self::Ok(value) => AsyncGeneratorRequestCompletion::Ok(value.bind(gc)), + Self::Err(js_error) => AsyncGeneratorRequestCompletion::Err(js_error), + Self::Return(value) => AsyncGeneratorRequestCompletion::Return(value.bind(gc)), + } + } } impl Rootable for AsyncGenerator<'_> { diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs index 0f97574fb..587dae7e0 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs @@ -20,10 +20,10 @@ use crate::{ agent::{ExceptionType, JsError}, Agent, JsResult, RealmIdentifier, }, - types::Value, + types::{IntoValue, Value}, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, unwrap_try, ExecutionResult, Scoped, SuspendedVm, Vm, }, }; @@ -43,6 +43,7 @@ pub(crate) fn async_generator_start_result( mut gc: GcScope, ) { let generator = generator.bind(gc.nogc()); + let result = result.bind(gc.nogc()); let scoped_generator = generator.scope(agent, gc.nogc()); // f. Remove acGenContext from the execution context stack and restore the // execution context that is at the top of the execution context stack @@ -64,7 +65,7 @@ pub(crate) fn async_generator_start_result( gc.nogc(), ); // k. Perform AsyncGeneratorDrainQueue(acGenerator). - async_generator_drain_queue(agent, scoped_generator.get(agent), gc.reborrow()); + async_generator_drain_queue(agent, scoped_generator, gc.reborrow()); // l. Return undefined. } @@ -83,7 +84,7 @@ pub(super) fn async_generator_validate<'a>( // 4. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception. // 5. Return unused. if let Value::AsyncGenerator(generator) = generator { - Ok(generator) + Ok(generator.unbind()) } else { Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -106,7 +107,7 @@ pub(super) fn async_generator_enqueue( ) { // 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }. let request = AsyncGeneratorRequest { - completion, + completion: completion.unbind(), capability: promise_capability, }; // 2. Append request to generator.[[AsyncGeneratorQueue]]. @@ -184,7 +185,9 @@ pub(super) fn async_generator_resume( completion: AsyncGeneratorRequestCompletion, mut gc: GcScope, ) { - let generator = generator.bind(gc.nogc()); + let nogc = gc.nogc(); + let generator = generator.bind(nogc); + let completion = completion.bind(nogc); // 1. Assert: generator.[[AsyncGeneratorState]] is either suspended-start or suspended-yield. // 2. Let genContext be generator.[[AsyncGeneratorContext]]. // 5. Set generator.[[AsyncGeneratorState]] to executing. @@ -197,7 +200,7 @@ pub(super) fn async_generator_resume( // the running execution context. agent.execution_context_stack.push(gen_context); - let scoped_generator = generator.scope(agent, gc.nogc()); + let scoped_generator = generator.scope(agent, nogc); // 7. Resume the suspended evaluation of genContext using completion as the // result of the operation that suspended it. Let result be the @@ -210,14 +213,14 @@ pub(super) fn async_generator_resume( let AsyncGeneratorRequestCompletion::Ok(value) = completion else { unreachable!() }; - vm.resume(agent, executable, value, gc.reborrow()) + vm.resume(agent, executable, value.unbind(), gc.reborrow()) } }; // 8. Assert: result is never an abrupt completion. // 9. Assert: When we return here, genContext has already been removed from // the execution context stack and callerContext is the currently // running execution context. - resume_handle_result(agent, execution_result, scoped_generator, gc); + resume_handle_result(agent, execution_result.unbind(), scoped_generator, gc); // 10. Return unused. } @@ -247,8 +250,7 @@ pub(super) fn resume_handle_result( gc.nogc(), ); // k. Perform AsyncGeneratorDrainQueue(acGenerator). - let generator = scoped_generator.get(agent).bind(gc.nogc()); - async_generator_drain_queue(agent, generator.unbind(), gc.reborrow()); + async_generator_drain_queue(agent, scoped_generator, gc.reborrow()); // l. Return undefined. } ExecutionResult::Throw(err) => { @@ -268,7 +270,7 @@ pub(super) fn resume_handle_result( gc.nogc(), ); // k. Perform AsyncGeneratorDrainQueue(acGenerator). - async_generator_drain_queue(agent, generator.unbind(), gc.reborrow()); + async_generator_drain_queue(agent, scoped_generator, gc.reborrow()); // l. Return undefined. } ExecutionResult::Yield { vm, yielded_value } => { @@ -331,18 +333,19 @@ fn async_generator_unwrap_yield_resumption( resumption_value: AsyncGeneratorRequestCompletion, mut gc: GcScope, ) { + let resumption_value = resumption_value.bind(gc.nogc()); // 1. If resumptionValue is not a return completion, return ? resumptionValue. let execution_result = match resumption_value { AsyncGeneratorRequestCompletion::Ok(v) => vm.resume( agent, generator.get(agent).get_executable(agent), - v, + v.unbind(), gc.reborrow(), ), AsyncGeneratorRequestCompletion::Err(e) => vm.resume_throw( agent, generator.get(agent).get_executable(agent), - e.value(), + e.value().unbind(), gc.reborrow(), ), AsyncGeneratorRequestCompletion::Return(value) => { @@ -351,14 +354,14 @@ fn async_generator_unwrap_yield_resumption( agent, generator, vm, - value, + value.unbind(), AsyncGeneratorAwaitKind::Return, gc.reborrow(), ); return; } }; - resume_handle_result(agent, execution_result, generator, gc); + resume_handle_result(agent, execution_result.unbind(), generator, gc); } /// ### [27.6.3.8 AsyncGeneratorYield ( value )](https://tc39.es/ecma262/#sec-asyncgeneratoryield) @@ -444,10 +447,10 @@ pub(super) fn async_generator_yield( /// (an AsyncGenerator) and returns unused. pub(super) fn async_generator_await_return( agent: &mut Agent, - generator: AsyncGenerator, + scoped_generator: Scoped<'_, AsyncGenerator<'static>>, mut gc: GcScope, ) { - let generator = generator.bind(gc.nogc()); + let generator = scoped_generator.get(agent).bind(gc.nogc()); // 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue. assert!(generator.is_draining_queue(agent)); // 2. Let queue be generator.[[AsyncGeneratorQueue]]. @@ -462,7 +465,6 @@ pub(super) fn async_generator_await_return( unreachable!() }; // 7. Let promiseCompletion be Completion(PromiseResolve(%Promise%, completion.[[Value]])). - let generator = generator.scope(agent, gc.nogc()); // 8. If promiseCompletion is an abrupt completion, then // a. Perform AsyncGeneratorCompleteStep(generator, promiseCompletion, true). // b. Perform AsyncGeneratorDrainQueue(generator). @@ -475,7 +477,7 @@ pub(super) fn async_generator_await_return( // 13. ... onRejected ... // 14. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »). // 15. Perform PerformPromiseThen(promise, onFulfilled, onRejected). - let handler = PromiseReactionHandler::AsyncGenerator(generator.get(agent)); + let handler = PromiseReactionHandler::AsyncGenerator(scoped_generator.get(agent)); inner_promise_then(agent, promise, handler, handler, None); // 16. Return unused. } @@ -505,7 +507,7 @@ pub(crate) fn async_generator_await_return_on_fulfilled( gc.nogc(), ); // d. Perform AsyncGeneratorDrainQueue(generator). - async_generator_drain_queue(agent, scoped_generator.get(agent).unbind(), gc); + async_generator_drain_queue(agent, scoped_generator, gc); // e. Return undefined. } @@ -528,13 +530,13 @@ pub(crate) fn async_generator_await_return_on_rejected( async_generator_complete_step( agent, generator.unbind(), - AsyncGeneratorRequestCompletion::Err(JsError::new(value)), + AsyncGeneratorRequestCompletion::Err(JsError::new(value.unbind())), true, None, gc.nogc(), ); // d. Perform AsyncGeneratorDrainQueue(generator). - async_generator_drain_queue(agent, scoped_generator.get(agent).unbind(), gc); + async_generator_drain_queue(agent, scoped_generator, gc); // e. Return undefined. } @@ -544,7 +546,12 @@ pub(crate) fn async_generator_await_return_on_rejected( /// (an AsyncGenerator) and returns unused. It drains the generator's /// AsyncGeneratorQueue until it encounters an AsyncGeneratorRequest which /// holds a return completion. -fn async_generator_drain_queue(agent: &mut Agent, generator: AsyncGenerator, mut gc: GcScope) { +fn async_generator_drain_queue( + agent: &mut Agent, + scoped_generator: Scoped<'_, AsyncGenerator<'static>>, + mut gc: GcScope, +) { + let generator = scoped_generator.get(agent).bind(gc.nogc()); // Assert: generator.[[AsyncGeneratorState]] is draining-queue. // 2. Let queue be generator.[[AsyncGeneratorQueue]]. let Some(AsyncGeneratorState::DrainingQueue(queue)) = @@ -563,9 +570,8 @@ fn async_generator_drain_queue(agent: &mut Agent, generator: AsyncGenerator, mut } // 4. Let done be false. - let mut done = false; // 5. Repeat, while done is false, - while !done { + loop { // a. Let next be the first element of queue. let next = generator.peek_first(agent); // b. Let completion be Completion(next.[[Completion]]). @@ -573,9 +579,9 @@ fn async_generator_drain_queue(agent: &mut Agent, generator: AsyncGenerator, mut // c. If completion is a return completion, then if let AsyncGeneratorRequestCompletion::Return(_) = completion { // i. Perform AsyncGeneratorAwaitReturn(generator). - async_generator_await_return(agent, generator, gc.reborrow()); + async_generator_await_return(agent, scoped_generator, gc.reborrow()); // ii. Set done to true. - done = true; + return; } else { // d. Else, // i. If completion is a normal completion, then @@ -599,7 +605,7 @@ fn async_generator_drain_queue(agent: &mut Agent, generator: AsyncGenerator, mut .async_generator_state .replace(AsyncGeneratorState::Completed); // 2. Set done to true. - done = true + return; } } } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs index bccb7ede9..15d68629e 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs @@ -6,7 +6,7 @@ use crate::ecmascript::abstract_operations::operations_on_iterator_objects::crea use crate::ecmascript::builtins::async_generator_objects::AsyncGeneratorState; use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_capability_records::{if_abrupt_reject_promise, PromiseCapability}; use crate::ecmascript::execution::agent::JsError; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -52,24 +52,25 @@ impl Builtin for AsyncGeneratorPrototypeThrow { impl AsyncGeneratorPrototype { /// ### [27.6.1.2 %AsyncGeneratorPrototype%.next ( value )](https://tc39.es/ecma262/#sec-asyncgenerator-prototype-next) - fn next( + fn next<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { - let value = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let value = arguments.get(0).bind(gc.nogc()); // 1. Let generator be the this value. - let generator = this_value; + let generator = this_value.bind(gc.nogc()); // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). let promise_capability = PromiseCapability::new(agent); // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). let result = async_generator_validate(agent, generator, (), gc.nogc()); // 4. IfAbruptRejectPromise(result, promiseCapability). - let generator = match if_abrupt_reject_promise(agent, result, promise_capability) { + let generator = match if_abrupt_reject_promise(agent, result, promise_capability, gc.nogc()) + { Ok(g) => g, Err(p) => { - return Ok(p.into_value()); + return Ok(p.into_value().unbind()); } }; // 5. Let state be generator.[[AsyncGeneratorState]]. @@ -80,7 +81,7 @@ impl AsyncGeneratorPrototype { let iterator_result = create_iter_result_object(agent, Value::Undefined, true, gc.nogc()); // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »). - promise_capability.resolve(agent, iterator_result.into_value(), gc); + promise_capability.resolve(agent, iterator_result.into_value().unbind(), gc); // c. Return promiseCapability.[[Promise]]. return Ok(promise_capability.promise().into_value()); } @@ -93,7 +94,7 @@ impl AsyncGeneratorPrototype { // 9. If state is either suspended-start or suspended-yield, then if state_is_suspended { // a. Perform AsyncGeneratorResume(generator, completion). - async_generator_resume(agent, generator.unbind(), completion, gc); + async_generator_resume(agent, generator.unbind(), completion.unbind(), gc); } else { // 10. Else, // a. Assert: state is either executing or draining-queue. @@ -104,12 +105,12 @@ impl AsyncGeneratorPrototype { } /// ### [27.6.1.3 %AsyncGeneratorPrototype%.return ( value )](https://tc39.es/ecma262/#sec-asyncgenerator-prototype-return) - fn r#return( + fn r#return<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let value = arguments.get(0).bind(gc.nogc()); // 1. Let generator be the this value. let generator = this_value.bind(gc.nogc()); @@ -119,10 +120,11 @@ impl AsyncGeneratorPrototype { // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). let result = async_generator_validate(agent, generator, (), gc.nogc()); // 4. IfAbruptRejectPromise(result, promiseCapability). - let generator = match if_abrupt_reject_promise(agent, result, promise_capability) { + let generator = match if_abrupt_reject_promise(agent, result, promise_capability, gc.nogc()) + { Ok(g) => g, Err(p) => { - return Ok(p.into_value()); + return Ok(p.into_value().unbind()); } }; // 5. Let completion be ReturnCompletion(value). @@ -136,14 +138,20 @@ impl AsyncGeneratorPrototype { // a. Set generator.[[AsyncGeneratorState]] to draining-queue. generator.transition_to_draining_queue(agent); // b. Perform AsyncGeneratorAwaitReturn(generator). - async_generator_await_return(agent, generator.unbind(), gc.reborrow()); + let generator = generator.scope(agent, gc.nogc()); + async_generator_await_return(agent, generator, gc.reborrow()); // 11. Return promiseCapability.[[Promise]]. Ok(promise.get(agent).into_value()) } else if generator.is_suspended_yield(agent) { // 9. Else if state is suspended-yield, then let promise = promise.scope(agent, gc.nogc()); // a. Perform AsyncGeneratorResume(generator, completion). - async_generator_resume(agent, generator.unbind(), completion, gc.reborrow()); + async_generator_resume( + agent, + generator.unbind(), + completion.unbind(), + gc.reborrow(), + ); // 11. Return promiseCapability.[[Promise]]. Ok(promise.get(agent).into_value()) } else { @@ -151,17 +159,17 @@ impl AsyncGeneratorPrototype { // a. Assert: state is either executing or draining-queue. assert!(generator.is_draining_queue(agent) || generator.is_executing(agent)); // 11. Return promiseCapability.[[Promise]]. - Ok(promise.into_value()) + Ok(promise.into_value().unbind()) } } /// ### [27.6.1.4 %AsyncGeneratorPrototype%.throw ( exception )](https://tc39.es/ecma262/#sec-asyncgenerator-prototype-throw) - fn throw( + fn throw<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let exception = arguments.get(0).bind(gc.nogc()); // 1. Let generator be the this value. let generator = this_value.bind(gc.nogc()); @@ -171,10 +179,11 @@ impl AsyncGeneratorPrototype { // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). let result = async_generator_validate(agent, generator, (), gc.nogc()); // 4. IfAbruptRejectPromise(result, promiseCapability). - let generator = match if_abrupt_reject_promise(agent, result, promise_capability) { + let generator = match if_abrupt_reject_promise(agent, result, promise_capability, gc.nogc()) + { Ok(g) => g, Err(p) => { - return Ok(p.into_value()); + return Ok(p.into_value().unbind()); } }; // 5. Let state be generator.[[AsyncGeneratorState]]. @@ -191,17 +200,23 @@ impl AsyncGeneratorPrototype { // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »). promise_capability.reject(agent, exception); // b. Return promiseCapability.[[Promise]]. - return Ok(promise.into_value()); + return Ok(promise.into_value().unbind()); } // 8. Let completion be ThrowCompletion(exception). - let completion = AsyncGeneratorRequestCompletion::Err(JsError::new(exception)); + let completion = + AsyncGeneratorRequestCompletion::Err(JsError::new(exception.unbind())).bind(gc.nogc()); // 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). async_generator_enqueue(agent, generator, completion, promise_capability); // 10. If state is suspended-yield, then if generator.is_suspended_yield(agent) { // a. Perform AsyncGeneratorResume(generator, completion). let scoped_promise = promise.scope(agent, gc.nogc()); - async_generator_resume(agent, generator.unbind(), completion, gc.reborrow()); + async_generator_resume( + agent, + generator.unbind(), + completion.unbind(), + gc.reborrow(), + ); promise = scoped_promise.get(agent).bind(gc.nogc()); } else { // 11. Else, @@ -209,7 +224,7 @@ impl AsyncGeneratorPrototype { assert!(generator.is_executing(agent) || generator.is_draining_queue(agent)); } // 12. Return promiseCapability.[[Promise]]. - Ok(promise.into_value()) + Ok(promise.into_value().unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_function_objects/generator_function_constructor.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_function_objects/generator_function_constructor.rs index a0c088326..803e68bbc 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_function_objects/generator_function_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_function_objects/generator_function_constructor.rs @@ -3,7 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::operations_on_objects::try_define_property_or_throw; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::engine::unwrap_try; use crate::{ ecmascript::{ @@ -37,33 +37,38 @@ impl BuiltinIntrinsicConstructor for GeneratorFunctionConstructor { } impl GeneratorFunctionConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let new_target = new_target.bind(gc.nogc()); // 2. If bodyArg is not present, set bodyArg to the empty String. let (parameter_args, body_arg) = if arguments.is_empty() { (&[] as &[Value], String::EMPTY_STRING.into_value()) } else { let (last, others) = arguments.split_last().unwrap(); - (others, *last) + (others, last.bind(gc.nogc())) }; let constructor = if let Some(new_target) = new_target { Function::try_from(new_target).unwrap() } else { - agent.running_execution_context().function.unwrap() + agent + .running_execution_context() + .function + .unwrap() + .bind(gc.nogc()) }; // 3. Return ? CreateDynamicFunction(C, NewTarget, generator, parameterArgs, bodyArg). let f = create_dynamic_function( agent, - constructor, + constructor.unbind(), DynamicFunctionKind::Generator, parameter_args, - body_arg, + body_arg.unbind(), gc.reborrow(), )? .unbind(); @@ -90,7 +95,7 @@ impl GeneratorFunctionConstructor { f, BUILTIN_STRING_MEMORY.prototype.to_property_key(), PropertyDescriptor { - value: Some(prototype.into_value()), + value: Some(prototype.into_value().unbind()), writable: Some(true), get: None, set: None, diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs index b7e9a1614..a81dfcfef 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs @@ -4,7 +4,7 @@ use core::ops::{Index, IndexMut}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_iterator_objects::create_iter_result_object, @@ -73,6 +73,7 @@ impl Generator<'_> { value: Value, mut gc: GcScope<'a, '_>, ) -> JsResult> { + let value = value.bind(gc.nogc()); let generator = self.bind(gc.nogc()); // 1. Let state be ? GeneratorValidate(generator, generatorBrand). match agent[generator].generator_state.as_ref().unwrap() { @@ -125,10 +126,13 @@ impl Generator<'_> { VmOrArguments::Arguments(args) => { Vm::execute(agent, executable, Some(&args), gc.reborrow()) } - VmOrArguments::Vm(vm) => vm.resume(agent, executable, value, gc.reborrow()), + VmOrArguments::Vm(vm) => vm.resume(agent, executable, value.unbind(), gc.reborrow()), }; - let generator = saved.get(agent).bind(gc.nogc()); + let execution_result = execution_result.unbind(); + let gc = gc.into_nogc(); + let generator = saved.get(agent).bind(gc); + let execution_result = execution_result.bind(gc); // GeneratorStart: 4.f. Remove acGenContext from the execution context stack and restore the // execution context that is at the top of the execution context stack as the running @@ -153,12 +157,7 @@ impl Generator<'_> { // j. Else if result is a return completion, then // i. Let resultValue be result.[[Value]]. // l. Return CreateIterResultObject(resultValue, true). - Ok(create_iter_result_object( - agent, - result_value, - true, - gc.into_nogc(), - )) + Ok(create_iter_result_object(agent, result_value, true, gc)) } ExecutionResult::Throw(err) => { // GeneratorStart step 4: @@ -185,12 +184,7 @@ impl Generator<'_> { })); // 8. Resume callerContext passing NormalCompletion(iterNextObj). ... // NOTE: `callerContext` here is the `GeneratorResume` execution context. - Ok(create_iter_result_object( - agent, - yielded_value, - false, - gc.into_nogc(), - )) + Ok(create_iter_result_object(agent, yielded_value, false, gc)) } ExecutionResult::Await { .. } => unreachable!(), } @@ -204,6 +198,7 @@ impl Generator<'_> { value: Value, mut gc: GcScope<'a, '_>, ) -> JsResult> { + let value = value.bind(gc.nogc()); // 1. Let state be ? GeneratorValidate(generator, generatorBrand). match agent[self].generator_state.as_ref().unwrap() { GeneratorState::Suspended(SuspendedGeneratorState { @@ -220,7 +215,7 @@ impl Generator<'_> { // 3. If state is completed, then // b. Return ? abruptCompletion. - return Err(JsError::new(value)); + return Err(JsError::new(value.unbind())); } GeneratorState::Suspended { .. } => { // 4. Assert: state is suspended-yield. @@ -229,13 +224,13 @@ impl Generator<'_> { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "The generator is currently running", - gc.nogc(), + gc.into_nogc(), )); } GeneratorState::Completed => { // 3. If state is completed, then // b. Return ? abruptCompletion. - return Err(JsError::new(value)); + return Err(JsError::new(value.unbind())); } }; @@ -261,7 +256,11 @@ impl Generator<'_> { // 10. Resume the suspended evaluation of genContext using NormalCompletion(value) as the // result of the operation that suspended it. Let result be the value returned by the // resumed computation. - let execution_result = vm.resume_throw(agent, executable, value, gc.reborrow()); + let execution_result = vm + .resume_throw(agent, executable, value.unbind(), gc.reborrow()) + .unbind(); + let gc = gc.into_nogc(); + let execution_result = execution_result.bind(gc); // GeneratorStart: 4.f. Remove acGenContext from the execution context stack and restore the // execution context that is at the top of the execution context stack as the running @@ -276,12 +275,7 @@ impl Generator<'_> { match execution_result { ExecutionResult::Return(result) => { agent[self].generator_state = Some(GeneratorState::Completed); - Ok(create_iter_result_object( - agent, - result, - true, - gc.into_nogc(), - )) + Ok(create_iter_result_object(agent, result, true, gc)) } ExecutionResult::Throw(err) => { agent[self].generator_state = Some(GeneratorState::Completed); @@ -294,20 +288,15 @@ impl Generator<'_> { executable, execution_context, })); - Ok(create_iter_result_object( - agent, - yielded_value, - false, - gc.into_nogc(), - )) + Ok(create_iter_result_object(agent, yielded_value, false, gc)) } ExecutionResult::Await { .. } => unreachable!(), } } } -impl IntoValue for Generator<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Generator<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -318,9 +307,9 @@ impl<'a> IntoObject<'a> for Generator<'a> { } } -impl From> for Value { - fn from(val: Generator) -> Self { - Value::Generator(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Generator<'a>) -> Self { + Value::Generator(value) } } @@ -330,10 +319,10 @@ impl<'a> From> for Object<'a> { } } -impl TryFrom for Generator<'_> { +impl<'a> TryFrom> for Generator<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { if let Value::Generator(value) = value { Ok(value) } else { @@ -350,10 +339,7 @@ impl<'a> InternalSlots<'a> for Generator<'a> { } fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) { - assert!(agent[self] - .object_index - .replace(backing_object.unbind()) - .is_none()); + assert!(agent[self].object_index.replace(backing_object).is_none()); } } @@ -432,7 +418,7 @@ pub struct GeneratorHeapData { #[derive(Debug)] pub(crate) enum VmOrArguments { Vm(SuspendedVm), - Arguments(Box<[Value]>), + Arguments(Box<[Value<'static>]>), } #[derive(Debug)] diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_prototype.rs index 429adfdf2..64b09ba0b 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_prototype.rs @@ -48,12 +48,12 @@ impl Builtin for GeneratorPrototypeThrow { } impl GeneratorPrototype { - fn next( + fn next<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // GeneratorResume: 1. Let state be ? GeneratorValidate(generator, generatorBrand). let Value::Generator(generator) = this_value else { return Err(agent.throw_exception_with_static_message( @@ -67,12 +67,12 @@ impl GeneratorPrototype { Ok(generator.resume(agent, arguments.get(0), gc)?.into_value()) } - fn r#return( + fn r#return<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); // 1. Let g be the this value. // 2. Let C be Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. @@ -124,12 +124,12 @@ impl GeneratorPrototype { Ok(create_iter_result_object(agent, arguments.get(0), true, gc).into_value()) } - fn throw( + fn throw<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // GeneratorResumeAbrupt: 1. Let state be ? GeneratorValidate(generator, generatorBrand). let Value::Generator(generator) = this_value else { return Err(agent.throw_exception_with_static_message( diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_prototype.rs index 9b7631d83..398d0ff6a 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_prototype.rs @@ -34,30 +34,30 @@ impl Builtin for AsyncFromSyncIteratorPrototypeThrow { } impl AsyncFromSyncIteratorPrototype { - fn next( + fn next<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn r#return( + fn r#return<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn throw( + fn throw<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_iterator_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_iterator_prototype.rs index b9a4ee123..8a84eab25 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_iterator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_iterator_prototype.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -25,13 +25,13 @@ impl Builtin for AsyncIteratorPrototypeIterator { } impl AsyncIteratorPrototype { - fn iterator( + fn iterator<'gc>( _agent: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(this_value) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/iterator_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/iterator_prototype.rs index 7d8a774d3..ecddd303b 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/iterator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/iterator_prototype.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -25,13 +25,13 @@ impl Builtin for IteratorPrototypeIterator { } impl IteratorPrototype { - fn iterator( + fn iterator<'gc>( _agent: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(this_value) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs index e6921b9ec..a534e25ab 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs @@ -5,7 +5,7 @@ //! ## [27.2.1.1 PromiseCapability Records]() use crate::ecmascript::abstract_operations::operations_on_objects::try_get; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -97,7 +97,7 @@ impl PromiseCapability { // 5. Set promise.[[PromiseRejectReactions]] to undefined. // 6. Set promise.[[PromiseState]] to FULFILLED. *promise_state = PromiseState::Fulfilled { - promise_result: value, + promise_result: value.unbind(), }; // 7. Perform TriggerPromiseReactions(reactions, value) if let Some(reactions) = reactions { @@ -123,7 +123,7 @@ impl PromiseCapability { // NOTE: [[PromiseIsHandled]] for pending promises corresponds to // whether [[PromiseRejectReactions]] is not empty. *promise_state = PromiseState::Rejected { - promise_result: reason, + promise_result: reason.unbind(), is_handled: reactions.is_some(), }; @@ -158,11 +158,13 @@ impl PromiseCapability { if resolution == self.promise.into_value() { // a. Let selfResolutionError be a newly created TypeError object. // b. Perform RejectPromise(promise, selfResolutionError). - let exception = agent.create_exception_with_static_message( - ExceptionType::TypeError, - "Tried to resolve a promise with itself.", - gc.nogc(), - ); + let exception = agent + .create_exception_with_static_message( + ExceptionType::TypeError, + "Tried to resolve a promise with itself.", + gc.nogc(), + ) + .unbind(); self.internal_reject(agent, exception); // c. Return undefined. return; @@ -233,11 +235,13 @@ impl PromiseCapability { if resolution == self.promise.into_value() { // a. Let selfResolutionError be a newly created TypeError object. // b. Perform RejectPromise(promise, selfResolutionError). - let exception = agent.create_exception_with_static_message( - ExceptionType::TypeError, - "Tried to resolve a promise with itself.", - gc, - ); + let exception = agent + .create_exception_with_static_message( + ExceptionType::TypeError, + "Tried to resolve a promise with itself.", + gc, + ) + .unbind(); self.internal_reject(agent, exception); // c. Return undefined. return TryResult::Continue(()); @@ -337,16 +341,17 @@ impl HeapMarkAndSweep for PromiseCapability { /// a. Set value to ! value. /// ``` #[inline(always)] -pub(crate) fn if_abrupt_reject_promise( +pub(crate) fn if_abrupt_reject_promise<'gc, T: 'gc>( agent: &mut Agent, value: JsResult, capability: PromiseCapability, -) -> Result { + gc: NoGcScope<'gc, '_>, +) -> Result> { value.map_err(|err| { capability.reject(agent, err.value()); // Note: We return an error here so that caller gets to call this // function with the ? operator - capability.promise() + capability.promise().bind(gc) }) } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs index 6ff504d5b..21e1ae5e5 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs @@ -4,7 +4,7 @@ //! ## [27.2.2 Promise Jobs](https://tc39.es/ecma262/#sec-promise-jobs) -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::engine::Global; use crate::{ ecmascript::{ @@ -112,13 +112,13 @@ pub(crate) fn new_promise_resolve_thenable_job( #[derive(Debug)] pub(crate) struct PromiseReactionJob { reaction: Global, - argument: Global, + argument: Global>, } impl PromiseReactionJob { pub(crate) fn run(self, agent: &mut Agent, mut gc: GcScope) -> JsResult<()> { let Self { reaction, argument } = self; let reaction = reaction.take(agent); - let argument = argument.take(agent).bind(gc.nogc()); + let argument = argument.take(agent).bind(gc.nogc()).unbind(); // The following are substeps of point 1 in NewPromiseReactionJob. let handler_result = match agent[reaction].handler { PromiseReactionHandler::Empty => match agent[reaction].reaction_type { @@ -174,7 +174,7 @@ impl PromiseReactionJob { // i. Else, Ok(value) => { // i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »). - promise_capability.resolve(agent, value, gc) + promise_capability.resolve(agent, value.unbind(), gc) } }; Ok(()) @@ -215,7 +215,7 @@ pub(crate) fn new_promise_reaction_job( // 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }. let reaction = Global::new(agent, reaction); - let argument = Global::new(agent, argument); + let argument = Global::new(agent, argument.unbind()); Job { realm: handler_realm, inner: InnerJob::PromiseReaction(PromiseReactionJob { reaction, argument }), diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs index c814326b4..daa3c7daf 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs @@ -5,7 +5,7 @@ use core::ops::{Index, IndexMut}; use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{ GcScope, NoGcScope}; use crate::engine::rootable::{HeapRootData, HeapRootRef, Rootable}; use crate::engine::{Scoped, TryResult}; use crate::{ @@ -118,14 +118,14 @@ impl<'a> IntoObject<'a> for BuiltinPromiseResolvingFunction<'a> { } } -impl From> for Value { - fn from(value: BuiltinPromiseResolvingFunction) -> Self { - Self::BuiltinPromiseResolvingFunction(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: BuiltinPromiseResolvingFunction<'a>) -> Self { + Self::BuiltinPromiseResolvingFunction(value) } } -impl IntoValue for BuiltinPromiseResolvingFunction<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for BuiltinPromiseResolvingFunction<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -149,10 +149,7 @@ impl<'a> InternalSlots<'a> for BuiltinPromiseResolvingFunction<'a> { } fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) { - assert!(agent[self] - .object_index - .replace(backing_object.unbind()) - .is_none()); + assert!(agent[self].object_index.replace(backing_object).is_none()); } fn create_backing_object(self, agent: &mut Agent) -> OrdinaryObject<'static> { @@ -208,23 +205,23 @@ impl<'a> InternalMethods<'a> for BuiltinPromiseResolvingFunction<'a> { function_internal_has_property(self, agent, property_key, gc) } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { function_try_get(self, agent, property_key, receiver, gc) } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { function_internal_get(self, agent, property_key, receiver, gc) } @@ -267,13 +264,13 @@ impl<'a> InternalMethods<'a> for BuiltinPromiseResolvingFunction<'a> { TryResult::Continue(function_internal_own_property_keys(self, agent, gc)) } - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, _this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let arg = args.get(0); let promise_capability = agent[self].promise_capability; match agent[self].resolve_type { diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs index c3e0bd2a6..a75697c57 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -102,13 +102,13 @@ impl Builtin for PromiseGetSpecies { impl BuiltinGetter for PromiseGetSpecies {} impl PromiseConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, args: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let executor = args.get(0); // 1. If NewTarget is undefined, throw a TypeError exception. @@ -191,46 +191,47 @@ impl PromiseConstructor { Ok(scoped_promise.get(agent).into_value()) } - fn all( + fn all<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn all_settled( + fn all_settled<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn any( + fn any<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn race( + fn race<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn reject( + fn reject<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - _: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let r = arguments.get(0).bind(gc.nogc()); // We currently don't support Promise subclassing. assert_eq!( this_value, @@ -246,19 +247,19 @@ impl PromiseConstructor { let promise = agent.heap.create(PromiseHeapData { object_index: None, promise_state: PromiseState::Rejected { - promise_result: arguments.get(0), + promise_result: r.unbind(), is_handled: false, }, }); Ok(promise.into_value()) } - fn resolve( + fn resolve<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // We currently don't support Promise subclassing. assert_eq!( this_value, @@ -270,12 +271,12 @@ impl PromiseConstructor { } /// Defined in the [`Promise.try` proposal](https://tc39.es/proposal-promise-try) - fn r#try( + fn r#try<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let C be the this value. // 2. If C is not an Object, throw a TypeError exception. if is_constructor(agent, this_value).is_none() { @@ -316,19 +317,19 @@ impl PromiseConstructor { // 6. Else, Ok(result) => { // a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »). - Promise::resolve(agent, result, gc) + Promise::resolve(agent, result.unbind(), gc) } }; // 7. Return promiseCapability.[[Promise]]. Ok(promise.into_value()) } - fn with_resolvers( + fn with_resolvers<'gc>( agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // Step 2 will throw if `this_value` is not a constructor. if is_constructor(agent, this_value).is_none() { return Err(agent.throw_exception_with_static_message( @@ -393,13 +394,13 @@ impl PromiseConstructor { Ok(obj.into_value()) } - fn get_species( + fn get_species<'gc>( _: &mut Agent, this_value: Value, _: ArgumentsList, - _: GcScope, - ) -> JsResult { - Ok(this_value) + _: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_prototype.rs index 8ab760321..f417f9bf4 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_prototype.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_objects::invoke, @@ -53,17 +53,17 @@ impl Builtin for PromisePrototypeThen { } impl PromisePrototype { - fn catch( + fn catch<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let promise be the this value. // 2. Return ? Invoke(promise, "then", « undefined, onRejected »). // TODO: Add a fast path that calls `perform_promise_then` if we know // `this.then` is this realm's creation-time `Promise.prototype.then`. - let on_rejected = args.get(0); + let on_rejected = args.get(0).unbind(); invoke( agent, this_value, @@ -73,21 +73,21 @@ impl PromisePrototype { ) } - fn finally( + fn finally<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn then( + fn then<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let promise be the this value. // 2. If IsPromise(promise) is false, throw a TypeError exception. let Value::Promise(promise) = this_value else { @@ -154,7 +154,7 @@ pub(crate) fn perform_promise_then( // TODO: Add the HostMakeJobCallback host hook. Leaving it for later, since in implementations // other than browsers, [[HostDefined]] must be EMPTY. let on_fulfilled_job_callback = match Function::try_from(on_fulfilled) { - Ok(callback) => PromiseReactionHandler::JobCallback(callback), + Ok(callback) => PromiseReactionHandler::JobCallback(callback.unbind()), Err(_) => PromiseReactionHandler::Empty, }; // 5. If IsCallable(onRejected) is false, then @@ -162,7 +162,7 @@ pub(crate) fn perform_promise_then( // 6. Else, // a. Let onRejectedJobCallback be HostMakeJobCallback(onRejected). let on_rejected_job_callback = match Function::try_from(on_rejected) { - Ok(callback) => PromiseReactionHandler::JobCallback(callback), + Ok(callback) => PromiseReactionHandler::JobCallback(callback.unbind()), Err(_) => PromiseReactionHandler::Empty, }; diff --git a/nova_vm/src/ecmascript/builtins/data_view.rs b/nova_vm/src/ecmascript/builtins/data_view.rs index b6e548446..1da56c212 100644 --- a/nova_vm/src/ecmascript/builtins/data_view.rs +++ b/nova_vm/src/ecmascript/builtins/data_view.rs @@ -113,8 +113,8 @@ impl<'a> IntoBaseIndex<'a, DataViewHeapData> for DataView<'a> { } } -impl IntoValue for DataView<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for DataView<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -125,15 +125,15 @@ impl<'a> IntoObject<'a> for DataView<'a> { } } -impl From> for Value { - fn from(val: DataView) -> Self { - Value::DataView(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: DataView<'a>) -> Self { + Value::DataView(value) } } impl<'a> From> for Object<'a> { - fn from(val: DataView) -> Self { - Object::DataView(val.unbind()) + fn from(value: DataView<'a>) -> Self { + Object::DataView(value) } } @@ -191,10 +191,7 @@ impl<'a> InternalSlots<'a> for DataView<'a> { } fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) { - assert!(agent[self] - .object_index - .replace(backing_object.unbind()) - .is_none()); + assert!(agent[self].object_index.replace(backing_object).is_none()); } } diff --git a/nova_vm/src/ecmascript/builtins/data_view/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/data_view/abstract_operations.rs index 0f34179fb..c88da2cdd 100644 --- a/nova_vm/src/ecmascript/builtins/data_view/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/data_view/abstract_operations.rs @@ -250,15 +250,15 @@ pub(crate) fn get_view_value<'gc, T: Viewable>( /// (an ECMAScript language value) and returns either a normal completion /// containing undefined or a throw completion. It is used by functions on /// DataView instances to store values into the view's buffer. -pub(crate) fn set_view_value( +pub(crate) fn set_view_value<'gc, T: Viewable>( agent: &mut Agent, view: Value, request_index: Value, // 6. Set isLittleEndian to ToBoolean(isLittleEndian). is_little_endian: bool, value: Value, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { // 1. Perform ? RequireInternalSlot(view, [[DataView]]). // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. let mut view = require_internal_slot_data_view(agent, view, gc.nogc())?; diff --git a/nova_vm/src/ecmascript/builtins/date.rs b/nova_vm/src/ecmascript/builtins/date.rs index dc11ba317..602f6b46a 100644 --- a/nova_vm/src/ecmascript/builtins/date.rs +++ b/nova_vm/src/ecmascript/builtins/date.rs @@ -67,15 +67,15 @@ impl Date<'_> { } } -impl IntoValue for Date<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Date<'a> { + fn into_value(self) -> Value<'a> { self.into() } } -impl From> for Value { - fn from(value: Date) -> Self { - Value::Date(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Date<'a>) -> Self { + Value::Date(value) } } @@ -91,10 +91,10 @@ impl<'a> From> for Object<'a> { } } -impl TryFrom for Date<'_> { +impl<'a> TryFrom> for Date<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Date(idx) => Ok(idx), _ => Err(()), @@ -122,10 +122,7 @@ impl<'a> InternalSlots<'a> for Date<'a> { } fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) { - assert!(agent[self] - .object_index - .replace(backing_object.unbind()) - .is_none()); + assert!(agent[self].object_index.replace(backing_object).is_none()); } } diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index 6689d4124..9c61dd4ab 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -49,7 +49,7 @@ use crate::{ types::{function_try_get, function_try_has_property, function_try_set}, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, Scoped, TryResult, }, @@ -75,8 +75,8 @@ impl<'a> From> for ECMAScriptFunction<'a> { } } -impl IntoValue for ECMAScriptFunction<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for ECMAScriptFunction<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -93,10 +93,10 @@ impl<'a> IntoFunction<'a> for ECMAScriptFunction<'a> { } } -impl TryFrom for ECMAScriptFunction<'_> { +impl<'a> TryFrom> for ECMAScriptFunction<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { if let Value::ECMAScriptFunction(function) = value { Ok(function) } else { @@ -129,15 +129,15 @@ impl<'a> TryFrom> for ECMAScriptFunction<'a> { } } -impl From> for Value { - fn from(val: ECMAScriptFunction) -> Self { - Value::ECMAScriptFunction(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: ECMAScriptFunction<'a>) -> Self { + Value::ECMAScriptFunction(value) } } impl<'a> From> for Object<'a> { - fn from(val: ECMAScriptFunction) -> Self { - Object::ECMAScriptFunction(val.unbind()) + fn from(value: ECMAScriptFunction<'a>) -> Self { + Object::ECMAScriptFunction(value) } } @@ -434,23 +434,23 @@ impl<'a> InternalMethods<'a> for ECMAScriptFunction<'a> { function_internal_has_property(self, agent, property_key, gc) } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { function_try_get(self, agent, property_key, receiver, gc) } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { function_internal_get(self, agent, property_key, receiver, gc) } @@ -500,13 +500,13 @@ impl<'a> InternalMethods<'a> for ECMAScriptFunction<'a> { /// `argumentsList` (a List of ECMAScript language values) and returns /// either a normal completion containing an ECMAScript language value or a /// throw completion. - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, this_argument: Value, arguments_list: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let callerContext be the running execution context. let _ = agent.running_execution_context(); // 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined). @@ -636,7 +636,7 @@ impl<'a> InternalMethods<'a> for ECMAScriptFunction<'a> { // 10. If result is a return completion, then // a. If result.[[Value]] is an Object, return result.[[Value]]. if let Ok(value) = Object::try_from(value) { - Ok(value) + Ok(value.unbind()) } else // b. If kind is base, return thisArgument. if is_base { @@ -646,7 +646,10 @@ impl<'a> InternalMethods<'a> for ECMAScriptFunction<'a> { if !value.is_undefined() { let message = format!( "derived class constructor returned invalid value {}", - value.string_repr(agent, gc.reborrow()).as_str(agent) + value + .unbind() + .string_repr(agent, gc.reborrow()) + .as_str(agent) ); let message = String::from_string(agent, message, gc.nogc()); Err(agent.throw_exception_with_message(ExceptionType::TypeError, message)) @@ -660,7 +663,7 @@ impl<'a> InternalMethods<'a> for ECMAScriptFunction<'a> { }; // 14. Return thisBinding. - Ok(this_binding) + Ok(this_binding.unbind()) } } } @@ -776,12 +779,12 @@ pub(crate) fn ordinary_call_bind_this( /// (an ECMAScript function object) and `argumentsList` (a List of ECMAScript /// language values) and returns either a normal completion containing an /// ECMAScript language value or an abrupt completion. -pub(crate) fn evaluate_body( +pub(crate) fn evaluate_body<'gc>( agent: &mut Agent, function_object: ECMAScriptFunction, arguments_list: ArgumentsList, - gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { let function_object = function_object.bind(gc.nogc()); let function_heap_data = &agent[function_object].ecmascript_function; let heap_data = function_heap_data; @@ -847,12 +850,12 @@ pub(crate) fn evaluate_body( /// ECMAScript function object) and `argumentsList` (a List of ECMAScript /// language values) and returns either a normal completion containing an /// ECMAScript language value or an abrupt completion. -pub(crate) fn ordinary_call_evaluate_body( +pub(crate) fn ordinary_call_evaluate_body<'gc>( agent: &mut Agent, f: ECMAScriptFunction, arguments_list: ArgumentsList, - gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { // 1. Return ? EvaluateBody of F.[[ECMAScriptCode]] with arguments F and argumentsList. evaluate_body(agent, f, arguments_list, gc) } @@ -1028,7 +1031,7 @@ pub(crate) fn make_constructor<'a>( agent, key, PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), writable: Some(writable_prototype), enumerable: Some(false), configurable: Some(true), @@ -1047,7 +1050,7 @@ pub(crate) fn make_constructor<'a>( agent, key, PropertyDescriptor { - value: Some(prototype.into_value()), + value: Some(prototype.into_value().unbind()), writable: Some(writable_prototype), enumerable: Some(false), configurable: Some(false), diff --git a/nova_vm/src/ecmascript/builtins/embedder_object.rs b/nova_vm/src/ecmascript/builtins/embedder_object.rs index 7e15515c1..0583dc3c0 100644 --- a/nova_vm/src/ecmascript/builtins/embedder_object.rs +++ b/nova_vm/src/ecmascript/builtins/embedder_object.rs @@ -64,8 +64,8 @@ impl EmbedderObject<'_> { } } -impl IntoValue for EmbedderObject<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for EmbedderObject<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -76,15 +76,15 @@ impl<'a> IntoObject<'a> for EmbedderObject<'a> { } } -impl From> for Value { - fn from(val: EmbedderObject) -> Self { - Value::EmbedderObject(val.unbind().unbind()) +impl<'a> From> for Value<'a> { + fn from(value: EmbedderObject<'a>) -> Self { + Value::EmbedderObject(value.unbind()) } } impl<'a> From> for Object<'a> { - fn from(val: EmbedderObject) -> Self { - Object::EmbedderObject(val.unbind()) + fn from(value: EmbedderObject<'a>) -> Self { + Object::EmbedderObject(value) } } diff --git a/nova_vm/src/ecmascript/builtins/error.rs b/nova_vm/src/ecmascript/builtins/error.rs index 1d024a489..d0c672053 100644 --- a/nova_vm/src/ecmascript/builtins/error.rs +++ b/nova_vm/src/ecmascript/builtins/error.rs @@ -8,7 +8,7 @@ use core::ops::{Index, IndexMut}; pub(crate) use data::ErrorHeapData; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::rootable::{HeapRootData, HeapRootRef, Rootable}; use crate::engine::{unwrap_try, Scoped, TryResult}; use crate::{ @@ -67,15 +67,15 @@ impl<'a> Error<'a> { } } -impl IntoValue for Error<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Error<'a> { + fn into_value(self) -> Value<'a> { self.into() } } -impl From> for Value { - fn from(value: Error) -> Self { - Value::Error(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Error<'a>) -> Self { + Value::Error(value) } } @@ -91,10 +91,10 @@ impl<'a> From> for Object<'a> { } } -impl TryFrom for Error<'_> { +impl<'a> TryFrom> for Error<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Error(idx) => Ok(idx), _ => Err(()), @@ -268,13 +268,13 @@ impl<'a> InternalMethods<'a> for Error<'a> { } } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { match self.get_backing_object(agent) { Some(backing_object) => backing_object.try_get(agent, property_key, receiver, gc), None => { @@ -298,13 +298,13 @@ impl<'a> InternalMethods<'a> for Error<'a> { } } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let property_key = property_key.bind(gc.nogc()); match self.get_backing_object(agent) { Some(backing_object) => { @@ -352,10 +352,10 @@ impl<'a> InternalMethods<'a> for Error<'a> { if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) && value.is_string() { - agent[self].message = Some(String::try_from(value).unwrap()); + agent[self].message = Some(String::try_from(value.unbind()).unwrap()); TryResult::Continue(true) } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.cause) { - agent[self].cause = Some(value); + agent[self].cause = Some(value.unbind()); TryResult::Continue(true) } else { let backing_object = self.create_backing_object(agent); @@ -382,10 +382,10 @@ impl<'a> InternalMethods<'a> for Error<'a> { if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) && value.is_string() { - agent[self].message = Some(String::try_from(value).unwrap()); + agent[self].message = Some(String::try_from(value.unbind()).unwrap()); Ok(true) } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.cause) { - agent[self].cause = Some(value); + agent[self].cause = Some(value.unbind()); Ok(true) } else { let backing_object = self.create_backing_object(agent); diff --git a/nova_vm/src/ecmascript/builtins/error/data.rs b/nova_vm/src/ecmascript/builtins/error/data.rs index d06798174..9919e405a 100644 --- a/nova_vm/src/ecmascript/builtins/error/data.rs +++ b/nova_vm/src/ecmascript/builtins/error/data.rs @@ -15,7 +15,7 @@ pub struct ErrorHeapData { pub(crate) object_index: Option>, pub(crate) kind: ExceptionType, pub(crate) message: Option>, - pub(crate) cause: Option, + pub(crate) cause: Option>, // TODO: stack? name? } @@ -23,7 +23,7 @@ impl ErrorHeapData { pub(crate) fn new( kind: ExceptionType, message: Option>, - cause: Option, + cause: Option>, ) -> Self { Self { object_index: None, diff --git a/nova_vm/src/ecmascript/builtins/finalization_registry.rs b/nova_vm/src/ecmascript/builtins/finalization_registry.rs index 5eef803d6..e7a060c48 100644 --- a/nova_vm/src/ecmascript/builtins/finalization_registry.rs +++ b/nova_vm/src/ecmascript/builtins/finalization_registry.rs @@ -68,8 +68,8 @@ impl FinalizationRegistry<'_> { } } -impl IntoValue for FinalizationRegistry<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for FinalizationRegistry<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -80,15 +80,15 @@ impl<'a> IntoObject<'a> for FinalizationRegistry<'a> { } } -impl From> for Value { - fn from(val: FinalizationRegistry) -> Self { - Value::FinalizationRegistry(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: FinalizationRegistry<'a>) -> Self { + Value::FinalizationRegistry(value) } } impl<'a> From> for Object<'a> { - fn from(val: FinalizationRegistry) -> Self { - Object::FinalizationRegistry(val.unbind()) + fn from(value: FinalizationRegistry<'a>) -> Self { + Object::FinalizationRegistry(value) } } @@ -101,10 +101,7 @@ impl<'a> InternalSlots<'a> for FinalizationRegistry<'a> { } fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) { - assert!(agent[self] - .object_index - .replace(backing_object.unbind()) - .is_none()); + assert!(agent[self].object_index.replace(backing_object).is_none()); } } diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_constructor.rs index 1cf71fe05..c993c61aa 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_constructor.rs @@ -38,13 +38,13 @@ impl BuiltinIntrinsicConstructor for BooleanConstructor { } impl BooleanConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let value = arguments.get(0); let b = to_boolean(agent, value); let Some(new_target) = new_target else { diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_prototype.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_prototype.rs index feb117555..13010da41 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/boolean_objects/boolean_prototype.rs @@ -7,7 +7,7 @@ use crate::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, builtins::{ primitive_objects::{PrimitiveObjectData, PrimitiveObjectHeapData}, - ArgumentsList, Builtin, + ArgumentsList, Behaviour, Builtin, }, execution::{agent::ExceptionType, Agent, JsResult, RealmIdentifier}, types::{String, Value, BUILTIN_STRING_MEMORY}, @@ -23,8 +23,7 @@ impl Builtin for BooleanPrototypeToString { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(BooleanPrototype::to_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(BooleanPrototype::to_string); } struct BooleanPrototypeValueOf; @@ -33,17 +32,16 @@ impl Builtin for BooleanPrototypeValueOf { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(BooleanPrototype::value_of); + const BEHAVIOUR: Behaviour = Behaviour::Regular(BooleanPrototype::value_of); } impl BooleanPrototype { - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let b = this_boolean_value(agent, this_value, gc.nogc())?; if b { Ok(BUILTIN_STRING_MEMORY.r#true.into()) @@ -52,12 +50,12 @@ impl BooleanPrototype { } } - fn value_of( + fn value_of<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { this_boolean_value(agent, this_value, gc.nogc()).map(|result| result.into()) } diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs index bef6e0619..e6207e0ae 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs @@ -2,12 +2,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::ecmascript::abstract_operations::operations_on_objects::create_array_from_scoped_list; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{ operations_on_iterator_objects::{get_iterator, iterator_to_list}, - operations_on_objects::{create_array_from_list, define_property_or_throw}, + operations_on_objects::define_property_or_throw, type_conversion::to_string, }, builders::builtin_function_builder::BuiltinFunctionBuilder, @@ -39,13 +40,13 @@ impl BuiltinIntrinsicConstructor for AggregateErrorConstructor { } impl AggregateErrorConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let errors = arguments.get(0); let message = arguments.get(1); let options = arguments.get(2); @@ -93,7 +94,11 @@ impl AggregateErrorConstructor { // [[Writable]]: true, writable: Some(true), // [[Value]]: CreateArrayFromList(errorsList) - value: Some(create_array_from_list(agent, &errors_list, gc.nogc()).into_value()), + value: Some( + create_array_from_scoped_list(agent, errors_list, gc.nogc()) + .into_value() + .unbind(), + ), ..Default::default() }; define_property_or_throw( diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_prototypes.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_prototypes.rs index 3ed01307a..288a3be6c 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_prototypes.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_prototypes.rs @@ -5,7 +5,7 @@ use crate::ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, execution::{Agent, RealmIdentifier}, - types::{String, BUILTIN_STRING_MEMORY}, + types::{IntoValue, String, BUILTIN_STRING_MEMORY}, }; pub(crate) struct AggregateErrorPrototype; diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs index f15e310a1..dad54bb33 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs @@ -25,6 +25,7 @@ use crate::ecmascript::types::PropertyKey; use crate::ecmascript::types::String; use crate::ecmascript::types::Value; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; +use crate::engine::context::Bindable; use crate::engine::context::GcScope; use crate::heap::IntrinsicConstructorIndexes; @@ -43,29 +44,48 @@ impl BuiltinIntrinsicConstructor for ErrorConstructor { impl ErrorConstructor { /// ### [20.5.1.1 Error ( message \[ , options \] )](https://tc39.es/ecma262/#sec-error-message) - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { - let message = arguments.get(0); - let options = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let message = arguments.get(0).bind(gc.nogc()); + let mut options = arguments.get(1).bind(gc.nogc()); + let mut new_target = new_target.bind(gc.nogc()); // 3. If message is not undefined, then - let message = if !message.is_undefined() { + let message = if let Ok(message) = String::try_from(message) { + Some(message.scope(agent, gc.nogc())) + } else if !message.is_undefined() { // a. Let msg be ? ToString(message). - Some( - to_string(agent, message, gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()), - ) + let scoped_options = options.scope(agent, gc.nogc()); + let scoped_new_target = new_target.map(|n| n.scope(agent, gc.nogc())); + let message = to_string(agent, message.unbind(), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); + // SAFETY: Never shared. + unsafe { + new_target = scoped_new_target.map(|n| n.take(agent)).bind(gc.nogc()); + options = scoped_options.take(agent).bind(gc.nogc()); + } + Some(message) } else { None }; // 4. Perform ? InstallErrorCause(O, options). - let cause = get_error_cause(agent, options, gc.reborrow())?; + let cause = if !options.is_object() { + None + } else { + let scoped_new_target = new_target.map(|n| n.scope(agent, gc.nogc())); + let cause = get_error_cause(agent, options.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + // SAFETY: Never shared. + new_target = unsafe { scoped_new_target.map(|n| n.take(agent)).bind(gc.nogc()) }; + cause.map(|c| c.scope(agent, gc.nogc())) + }; // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = new_target.map_or_else( @@ -75,13 +95,16 @@ impl ErrorConstructor { // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »). let o = ordinary_create_from_constructor( agent, - new_target, + new_target.unbind(), ProtoIntrinsics::Error, gc.reborrow(), - )?; + )? + .unbind() + .bind(gc.into_nogc()); let o = Error::try_from(o).unwrap(); // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). let message = message.map(|message| message.get(agent)); + let cause = cause.map(|c| c.get(agent)); let heap_data = &mut agent[o]; heap_data.kind = ExceptionType::Error; heap_data.message = message; @@ -101,11 +124,11 @@ impl ErrorConstructor { } } -pub(super) fn get_error_cause( +pub(super) fn get_error_cause<'gc>( agent: &mut Agent, options: Value, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'gc, '_>, +) -> JsResult>> { let Ok(options) = Object::try_from(options) else { return Ok(None); }; diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_prototype.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_prototype.rs index bc2ce8961..3aa86ab82 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_prototype.rs @@ -6,11 +6,11 @@ use crate::{ ecmascript::{ abstract_operations::{operations_on_objects::get, type_conversion::to_string}, builders::ordinary_object_builder::OrdinaryObjectBuilder, - builtins::{ArgumentsList, Builtin}, + builtins::{ArgumentsList, Behaviour, Builtin}, execution::{agent::ExceptionType, Agent, JsResult, RealmIdentifier}, - types::{Object, PropertyKey, String, Value, BUILTIN_STRING_MEMORY}, + types::{IntoValue, Object, PropertyKey, String, Value, BUILTIN_STRING_MEMORY}, }, - engine::context::GcScope, + engine::context::{Bindable, GcScope}, }; pub(crate) struct ErrorPrototype; @@ -22,18 +22,18 @@ impl Builtin for ErrorPrototypeToString { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ErrorPrototype::to_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ErrorPrototype::to_string); } impl ErrorPrototype { /// ### [20.5.3.4 Error.prototype.toString ( )](https://tc39.es/ecma262/#sec-error.prototype.tostring) - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); // 1. Let O be the this value. // 2. If O is not an Object, throw a TypeError exception. let Ok(o) = Object::try_from(this_value) else { @@ -43,10 +43,11 @@ impl ErrorPrototype { gc.nogc(), )); }; + let scoped_o = o.scope(agent, gc.nogc()); // 3. Let name be ? Get(O, "name"). let name = get( agent, - o, + o.unbind(), PropertyKey::from(BUILTIN_STRING_MEMORY.name), gc.reborrow(), )?; @@ -55,30 +56,28 @@ impl ErrorPrototype { None } else { Some( - to_string(agent, name, gc.reborrow())? + to_string(agent, name.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()), ) }; // 5. Let msg be ? Get(O, "message"). - let key = PropertyKey::from(BUILTIN_STRING_MEMORY.message); - let msg = get(agent, o, key, gc.reborrow())?; + let msg = get( + agent, + scoped_o.get(agent), + BUILTIN_STRING_MEMORY.message.into(), + gc.reborrow(), + )?; // 6. If msg is undefined, set msg to the empty String; otherwise set msg to ? ToString(msg). let msg = if msg.is_undefined() { - None + String::EMPTY_STRING } else { - Some( - to_string(agent, msg, gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()), - ) + to_string(agent, msg.unbind(), gc.reborrow())? }; // No more GC can be triggered. - let gc = gc.nogc(); - // 6. If msg is undefined, set msg to the empty String - let msg = msg - .map_or(String::EMPTY_STRING, |msg| msg.get(agent)) - .bind(gc); + let msg = msg.unbind(); + let gc = gc.into_nogc(); + let msg = msg.bind(gc); // 4. If name is undefined, set name to "Error" let name = name .map_or(BUILTIN_STRING_MEMORY.Error, |name| name.get(agent)) diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs index 56d8fadf7..3cf9206e0 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_string, @@ -95,15 +95,17 @@ impl BuiltinIntrinsicConstructor for URIErrorConstructor { pub(crate) struct NativeErrorConstructors; impl NativeErrorConstructors { #[inline(always)] - fn constructor( + fn constructor<'gc>( agent: &mut Agent, error_kind: ExceptionType, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { - let message = arguments.get(0); - let options = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let scoped_message = arguments.get(0).scope(agent, nogc); + let options = arguments.get(1).scope(agent, nogc); + let new_target = new_target.bind(nogc); let intrinsic = match error_kind { ExceptionType::Error => ProtoIntrinsics::Error, @@ -123,7 +125,6 @@ impl NativeErrorConstructors { .unwrap() .into_object() }); - let new_target = new_target.bind(gc.nogc()); let o = ordinary_create_from_constructor( agent, Function::try_from(new_target.unbind()).unwrap(), @@ -133,53 +134,54 @@ impl NativeErrorConstructors { .unbind() .bind(gc.nogc()) .scope(agent, gc.nogc()); + let message = scoped_message.get(agent); let msg = if !message.is_undefined() { - Some( - to_string(agent, message, gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()), - ) + let msg = to_string(agent, message.unbind(), gc.reborrow())?; + // Safety: scoped_message is never shared. + Some(unsafe { scoped_message.replace_self(agent, msg.unbind()) }) } else { None }; - let cause = get_error_cause(agent, options, gc.reborrow())?; - let o = Error::try_from(o.get(agent).bind(gc.nogc())).unwrap(); + let cause = get_error_cause(agent, options.get(agent), gc.reborrow())?.unbind(); + let gc = gc.into_nogc(); + let cause = cause.bind(gc); + let o = Error::try_from(o.get(agent).bind(gc)).unwrap(); // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - let msg = msg.map(|msg| msg.get(agent)); + let msg = msg.map(|msg| msg.get(agent).bind(gc)); let heap_data = &mut agent[o]; heap_data.kind = error_kind; - heap_data.message = msg; - heap_data.cause = cause; + heap_data.message = msg.unbind(); + heap_data.cause = cause.unbind(); Ok(o.into_value()) } - fn eval_error_constructor( + fn eval_error_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::constructor(agent, ExceptionType::EvalError, arguments, new_target, gc) } - fn range_error_constructor( + fn range_error_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::constructor(agent, ExceptionType::RangeError, arguments, new_target, gc) } - fn reference_error_constructor( + fn reference_error_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::constructor( agent, ExceptionType::ReferenceError, @@ -189,33 +191,33 @@ impl NativeErrorConstructors { ) } - fn syntax_error_constructor( + fn syntax_error_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::constructor(agent, ExceptionType::SyntaxError, arguments, new_target, gc) } - fn type_error_constructor( + fn type_error_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::constructor(agent, ExceptionType::TypeError, arguments, new_target, gc) } - fn uri_error_constructor( + fn uri_error_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::constructor(agent, ExceptionType::UriError, arguments, new_target, gc) } diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_prototypes.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_prototypes.rs index 00b1be9a8..5c820f7a0 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_prototypes.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_prototypes.rs @@ -5,7 +5,7 @@ use crate::ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, execution::{Agent, RealmIdentifier}, - types::{String, BUILTIN_STRING_MEMORY}, + types::{IntoValue, String, BUILTIN_STRING_MEMORY}, }; pub(crate) struct NativeErrorPrototypes; diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_constructor.rs index 1d3caba4c..234d642fd 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_constructor.rs @@ -23,7 +23,7 @@ use crate::{ BUILTIN_STRING_MEMORY, }, }, - engine::context::GcScope, + engine::context::{Bindable, GcScope}, heap::IntrinsicConstructorIndexes, }; @@ -41,13 +41,13 @@ impl BuiltinIntrinsicConstructor for FunctionConstructor { } impl FunctionConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 2. If bodyArg is not present, set bodyArg to the empty String. let (parameter_args, body_arg) = if arguments.is_empty() { (&[] as &[Value], String::EMPTY_STRING.into_value()) @@ -77,7 +77,7 @@ impl FunctionConstructor { // a. Perform MakeConstructor(F). make_constructor(agent, f.unbind(), None, None, gc.nogc()); - Ok(f.into_value()) + Ok(f.into_value().unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs index 531101ee1..e01293271 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs @@ -6,7 +6,7 @@ use crate::ecmascript::abstract_operations::operations_on_objects::{ try_get, try_has_own_property, }; use crate::ecmascript::abstract_operations::type_conversion::to_integer_or_infinity_number; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -52,8 +52,7 @@ impl Builtin for FunctionPrototypeApply { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(FunctionPrototype::apply); + const BEHAVIOUR: Behaviour = Behaviour::Regular(FunctionPrototype::apply); } struct FunctionPrototypeBind; @@ -62,8 +61,7 @@ impl Builtin for FunctionPrototypeBind { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(FunctionPrototype::bind); + const BEHAVIOUR: Behaviour = Behaviour::Regular(FunctionPrototype::bind); } struct FunctionPrototypeCall; @@ -72,8 +70,7 @@ impl Builtin for FunctionPrototypeCall { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(FunctionPrototype::call); + const BEHAVIOUR: Behaviour = Behaviour::Regular(FunctionPrototype::call); } struct FunctionPrototypeToString; @@ -82,8 +79,7 @@ impl Builtin for FunctionPrototypeToString { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(FunctionPrototype::to_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(FunctionPrototype::to_string); } struct FunctionPrototypeHasInstance; @@ -95,25 +91,32 @@ impl Builtin for FunctionPrototypeHasInstance { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(FunctionPrototype::has_instance); + const BEHAVIOUR: Behaviour = Behaviour::Regular(FunctionPrototype::has_instance); const WRITABLE: bool = false; const CONFIGURABLE: bool = false; } impl FunctionPrototype { - fn behaviour(_: &mut Agent, _: Value, _: ArgumentsList, _: GcScope) -> JsResult { + fn behaviour( + _: &mut Agent, + _: Value, + _: ArgumentsList, + _: GcScope, + ) -> JsResult> { Ok(Value::Undefined) } /// ### [20.2.3.1 Function.prototype.apply ( thisArg, argArray )](https://tc39.es/ecma262/#sec-function.prototype.apply) - fn apply( + fn apply<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); + let this_arg = args.get(0).bind(gc.nogc()); + let arg_array = args.get(1).bind(gc.nogc()); // 1. Let func be the this value. let Some(func) = is_callable(this_value, gc.nogc()) else { // 2. If IsCallable(func) is false, throw a TypeError exception. @@ -123,22 +126,25 @@ impl FunctionPrototype { gc.nogc(), )); }; - let func = func.bind(gc.nogc()); - let this_arg = args.get(0); - let arg_array = args.get(1); if arg_array.is_undefined() || arg_array.is_null() { // 3. If argArray is either undefined or null, then // a. TODO: Perform PrepareForTailCall(). // b. Return ? Call(func, thisArg). - return call_function(agent, func.unbind(), this_arg, None, gc); + return call_function(agent, func.unbind(), this_arg.unbind(), None, gc); } let func = func.scope(agent, gc.nogc()); + let this_arg = this_arg.scope(agent, gc.nogc()); // 4. Let argList be ? CreateListFromArrayLike(argArray). - let args = create_list_from_array_like(agent, arg_array, gc.reborrow())?; - let args_list = ArgumentsList(&args); + let args_list = create_list_from_array_like(agent, arg_array.unbind(), gc.reborrow())?; // 5. TODO: Perform PrepareForTailCall(). // 6.Return ? Call(func, thisArg, argList). - call_function(agent, func.get(agent), this_arg, Some(args_list), gc) + call_function( + agent, + func.get(agent), + this_arg.get(agent), + Some(ArgumentsList(&args_list.unbind())), + gc, + ) } /// ### [20.2.3.2 Function.prototype.bind ( thisArg, ...args )](https://tc39.es/ecma262/#sec-function.prototype.bind) @@ -153,13 +159,14 @@ impl FunctionPrototype { /// > If `Target` is either an arrow function or a bound function exotic /// > object, then the `thisArg` passed to this method will not be used by /// > subsequent calls to `F`. - fn bind( + fn bind<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let this_arg = args.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); + let this_arg = args.get(0).bind(gc.nogc()); let args = if args.len() > 1 { &args[1..] } else { &[] }; // 1. Let Target be the this value. let target = this_value; @@ -173,9 +180,15 @@ impl FunctionPrototype { }; let scoped_target = target.scope(agent, gc.nogc()); // 3. Let F be ? BoundFunctionCreate(Target, thisArg, args). - let mut f = bound_function_create(agent, target.unbind(), this_arg, args, gc.reborrow())? - .unbind() - .bind(gc.nogc()); + let mut f = bound_function_create( + agent, + target.unbind(), + this_arg.unbind(), + args, + gc.reborrow(), + )? + .unbind() + .bind(gc.nogc()); target = scoped_target.get(agent); let mut scoped_f = None; // 4. Let L be 0. @@ -217,13 +230,14 @@ impl FunctionPrototype { } let result = get( agent, - target, - BUILTIN_STRING_MEMORY.length.into(), + target.unbind(), + BUILTIN_STRING_MEMORY.length.unbind().into(), gc.reborrow(), - )?; + )? + .unbind(); f = scoped_f.as_ref().unwrap().get(agent).bind(gc.nogc()); target = scoped_target.get(agent); - result + result.unbind() }; // b. If targetLen is a Number, then if let Ok(target_len) = Number::try_from(target_len) { @@ -272,10 +286,11 @@ impl FunctionPrototype { } let result = get( agent, - target, - BUILTIN_STRING_MEMORY.name.into(), + target.unbind(), + BUILTIN_STRING_MEMORY.name.unbind().into(), gc.reborrow(), - )?; + )? + .unbind(); f = scoped_f.unwrap().get(agent).bind(gc.nogc()); result }; @@ -291,35 +306,37 @@ impl FunctionPrototype { ); // 11. Return F. - Ok(f.into_value()) + Ok(f.into_value().unbind()) } - fn call( + fn call<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { - let Some(func) = is_callable(this_value, gc.nogc()) else { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let this_arg = args.get(0).bind(nogc); + let Some(func) = is_callable(this_value, nogc) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Not a callable value", - gc.nogc(), + nogc, )); }; - let func = func.bind(gc.nogc()); // TODO: PrepareForTailCall - let this_arg = args.get(0); let args = ArgumentsList(if args.len() > 0 { &args[1..] } else { &args }); - call_function(agent, func.unbind(), this_arg, Some(args), gc) + call_function(agent, func.unbind(), this_arg.unbind(), Some(args), gc) } - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); // Let func be the this value. let Ok(func) = Function::try_from(this_value) else { // 5. Throw a TypeError exception. @@ -341,7 +358,7 @@ impl FunctionPrototype { let source_text = data.source_code.get_source_text(agent) [(span.start as usize)..(span.end as usize)] .to_string(); - Ok(Value::from_string(agent, source_text, gc.nogc())) + Ok(Value::from_string(agent, source_text, gc.nogc()).unbind()) } // 4. If func is an Object and IsCallable(func) is true, return an // implementation-defined String source code representation of func. @@ -367,21 +384,18 @@ impl FunctionPrototype { } }, ); - Ok(Value::from_string(agent, initial_name, gc.nogc())) + Ok(Value::from_string(agent, initial_name, gc.nogc()).unbind()) } Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(_) => Ok(Value::from_static_str( - agent, - "class { [ native code ] }", - gc.nogc(), - )), + Function::BuiltinConstructorFunction(_) => { + Ok(Value::from_static_str(agent, "class { [ native code ] }", gc.nogc()).unbind()) + } Function::BuiltinPromiseResolvingFunction(_) => { // Promise resolving functions have no initial name. - Ok(Value::from_static_str( - agent, - "function () { [ native code ] }", - gc.nogc(), - )) + Ok( + Value::from_static_str(agent, "function () { [ native code ] }", gc.nogc()) + .unbind(), + ) } Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), @@ -392,12 +406,12 @@ impl FunctionPrototype { // is an optional template part. } - fn has_instance( + fn has_instance<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let v = args.get(0); let f = this_value; ordinary_has_instance(agent, f, v, gc).map(|result| result.into()) @@ -460,7 +474,12 @@ impl BuiltinIntrinsic for ThrowTypeError { } impl ThrowTypeError { - fn behaviour(agent: &mut Agent, _: Value, _: ArgumentsList, gc: GcScope) -> JsResult { + fn behaviour<'gc>( + agent: &mut Agent, + _: Value, + _: ArgumentsList, + gc: GcScope<'gc, '_>, + ) -> JsResult> { Err(agent.throw_exception_with_static_message(ExceptionType::TypeError, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them", gc.nogc())) } diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs index 7546f6fab..2bd565888 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs @@ -26,13 +26,12 @@ use crate::{ }, execution::{agent::ExceptionType, Agent, JsResult, ProtoIntrinsics, RealmIdentifier}, types::{ - bind_property_keys, scope_property_keys, unbind_property_keys, InternalMethods, - IntoFunction, IntoObject, IntoValue, Object, OrdinaryObject, PropertyDescriptor, - PropertyKey, String, Value, BUILTIN_STRING_MEMORY, + scope_property_keys, InternalMethods, IntoFunction, IntoObject, IntoValue, Object, + OrdinaryObject, PropertyDescriptor, PropertyKey, String, Value, BUILTIN_STRING_MEMORY, }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, unwrap_try, Scoped, TryResult, }, heap::{IntrinsicConstructorIndexes, ObjectEntry, WellKnownSymbolIndexes}, @@ -264,15 +263,15 @@ impl Builtin for ObjectValues { impl ObjectConstructor { /// ### [20.1.1.1 Object ( \[ value \] )](https://tc39.es/ecma262/#sec-object-value) - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { - let value = arguments.get(0); - let new_target = new_target.map(|f| f.bind(gc.nogc())); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let value = arguments.get(0).bind(gc.nogc()); + let new_target = new_target.bind(gc.nogc()); // 1. If NewTarget is neither undefined nor the active function object, then if new_target.is_some() && new_target @@ -291,7 +290,7 @@ impl ObjectConstructor { ProtoIntrinsics::Object, gc.reborrow(), ) - .map(|value| value.into_value()) + .map(|value| value.into_value().unbind()) } else if value == Value::Undefined || value == Value::Null { // 2. If value is either undefined or null, return OrdinaryObjectCreate(%Object.prototype%). Ok(ordinary_object_create_with_intrinsics( @@ -303,7 +302,7 @@ impl ObjectConstructor { .into_value()) } else { // 3. Return ! ToObject(value). - Ok(to_object(agent, value, gc.into_nogc()) + Ok(to_object(agent, value.unbind(), gc.into_nogc()) .unwrap() .into_value()) } @@ -313,43 +312,56 @@ impl ObjectConstructor { /// /// This function copies the values of all of the enumerable own properties /// from one or more source objects to a target object. - fn assign( + fn assign<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); // 1. Let to be ? ToObject(target). - let to = to_object(agent, target, gc.nogc())?; + let to = to_object(agent, target, nogc)?; // 2. If only one argument was passed, return to. if arguments.len() <= 1 { - return Ok(to.into_value()); + return Ok(to.into_value().unbind()); } - let to = to.scope(agent, gc.nogc()); - let sources = &arguments[1..]; + let sources = arguments[1..] + .iter() + .map(|a| a.scope(agent, nogc)) + .collect::>(); + // Note: We scope to twice: First for the from object and then for + // itself. The scoped_from's heap data will be replaced on every loop + // and thus cannot be used for storing the to object. We must not + // use "clone" to duplicate scoped_from into to as that would reuse the + // heap data slot. + let mut scoped_from = to.scope(agent, nogc); + let to = to.scope(agent, nogc); // 3. For each element nextSource of sources, do - for next_source in sources { + for scoped_next_source in sources { + let next_source = scoped_next_source.get(agent).bind(gc.nogc()); // a. If nextSource is neither undefined nor null, then if next_source.is_undefined() || next_source.is_null() { continue; } // i. Let from be ! ToObject(nextSource). - let from = to_object(agent, *next_source, gc.nogc())?.scope(agent, gc.nogc()); + let from = to_object(agent, next_source, gc.nogc()).unwrap(); + // SAFETY: scoped_from does not share its heap slot with anyone as + // it is created separately (not a clone itself) and never cloned. + unsafe { scoped_from.replace(agent, from.unbind()) }; // ii. Let keys be ? from.[[OwnPropertyKeys]](). - let keys = unbind_property_keys( - from.get(agent) - .internal_own_property_keys(agent, gc.reborrow())?, - ); - let keys = scope_property_keys(agent, keys, gc.nogc()); + let keys = from + .unbind() + .internal_own_property_keys(agent, gc.reborrow())?; + let keys = scope_property_keys(agent, keys.unbind(), gc.nogc()); // iii. For each element nextKey of keys, do for next_key in keys { // 1. Let desc be ? from.[[GetOwnProperty]](nextKey). - let desc = { - let next_key = next_key.get(agent); - from.get(agent) - .internal_get_own_property(agent, next_key, gc.reborrow())? - }; + let desc = scoped_from.get(agent).internal_get_own_property( + agent, + next_key.get(agent), + gc.reborrow(), + )?; // 2. If desc is not undefined and desc.[[Enumerable]] is true, then let Some(desc) = desc else { continue; @@ -358,17 +370,18 @@ impl ObjectConstructor { continue; } // a. Let propValue be ? Get(from, nextKey). - let prop_value = { - let next_key = next_key.get(agent); - get(agent, from.get(agent), next_key, gc.reborrow())? - }; + let prop_value = get( + agent, + scoped_from.get(agent), + next_key.get(agent), + gc.reborrow(), + )?; // b. Perform ? Set(to, nextKey, propValue, true). - let next_key = next_key.get(agent); set( agent, to.get(agent), - next_key, - prop_value, + next_key.get(agent), + prop_value.unbind(), true, gc.reborrow(), )?; @@ -378,53 +391,58 @@ impl ObjectConstructor { Ok(to.get(agent).into_value()) } - fn create( + fn create<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let o = arguments.get(0).bind(nogc); + let properties = arguments.get(1).bind(nogc); let obj: OrdinaryObject = if o == Value::Null { - agent.heap.create_null_object(&[]) + agent.heap.create_null_object(&[]).bind(nogc) } else if let Ok(o) = Object::try_from(o) { - agent.heap.create_object_with_prototype(o, &[]) + agent.heap.create_object_with_prototype(o, &[]).bind(nogc) } else { let error_message = format!( "{} is not an object or null", - o.string_repr(agent, gc.reborrow()).as_str(agent) + o.unbind().string_repr(agent, gc.reborrow()).as_str(agent) ); return Err(agent.throw_exception(ExceptionType::TypeError, error_message, gc.nogc())); }; - let properties = arguments.get(1); if properties != Value::Undefined { - object_define_properties(agent, obj, properties, gc.reborrow())?; + let scoped_obj = obj.scope(agent, gc.nogc()); + object_define_properties(agent, obj.unbind(), properties.unbind(), gc.reborrow())?; + Ok(scoped_obj.get(agent).into_value()) + } else { + Ok(obj.into_value().unbind()) } - Ok(obj.into_value()) } /// ### [20.1.2.3 Object.defineProperties ( O, Properties )](https://tc39.es/ecma262/#sec-object.defineproperties) /// /// This function adds own properties and/or updates the attributes of /// existing own properties of an object. - fn define_properties( + fn define_properties<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); - let properties = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let o = arguments.get(0).bind(nogc); + let properties = arguments.get(1).bind(nogc); // 1. If O is not an Object, throw a TypeError exception. let Ok(o) = Object::try_from(o) else { let error_message = format!( "{} is not an object", - o.string_repr(agent, gc.reborrow()).as_str(agent) + o.unbind().string_repr(agent, gc.reborrow()).as_str(agent) ); return Err(agent.throw_exception(ExceptionType::TypeError, error_message, gc.nogc())); }; // 2. Return ? ObjectDefineProperties(O, Properties). - let result = object_define_properties(agent, o, properties, gc.reborrow())?; + let result = object_define_properties(agent, o.unbind(), properties.unbind(), gc)?; Ok(result.into_value()) } @@ -432,42 +450,67 @@ impl ObjectConstructor { /// /// This function adds an own property and/or updates the attributes of an /// existing own property of an object. - fn define_property( + fn define_property<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); - let p = arguments.get(1); - let attributes = arguments.get(2); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let o = arguments.get(0).bind(nogc); + let p = arguments.get(1).bind(nogc); + let mut attributes = arguments.get(2).bind(nogc); // 1. If O is not an Object, throw a TypeError exception. let Ok(o) = Object::try_from(o) else { let error_message = format!( "{} is not an object", - o.string_repr(agent, gc.reborrow()).as_str(agent) + o.unbind().string_repr(agent, gc.reborrow()).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message, gc.nogc())); + return Err(agent.throw_exception( + ExceptionType::TypeError, + error_message, + gc.into_nogc(), + )); }; + let o = o.scope(agent, gc.nogc()); // 2. Let key be ? ToPropertyKey(P). - let key = to_property_key(agent, p, gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()); + let mut key = if let TryResult::Continue(key) = to_property_key_simple(agent, p, nogc) { + key + } else { + let scoped_attributes = attributes.scope(agent, nogc); + let key = to_property_key(agent, p.unbind(), gc.reborrow())?.unbind(); + let gc = gc.nogc(); + attributes = scoped_attributes.get(agent).bind(gc); + key.bind(gc) + }; // 3. Let desc be ? ToPropertyDescriptor(Attributes). - let desc = PropertyDescriptor::to_property_descriptor(agent, attributes, gc.reborrow())?; + let desc = if let TryResult::Continue(desc) = + PropertyDescriptor::try_to_property_descriptor(agent, attributes, gc.nogc()) + { + desc? + } else { + let scoped_key = key.scope(agent, gc.nogc()); + let desc = PropertyDescriptor::to_property_descriptor( + agent, + attributes.unbind(), + gc.reborrow(), + )?; + key = scoped_key.get(agent).bind(gc.nogc()); + desc + }; // 4. Perform ? DefinePropertyOrThrow(O, key, desc). - define_property_or_throw(agent, o, key.get(agent), desc, gc.reborrow())?; + define_property_or_throw(agent, o.get(agent), key.unbind(), desc, gc.reborrow())?; // 5. Return O. - Ok(o.into_value()) + Ok(o.get(agent).into_value()) } - fn entries( + fn entries<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); // 1. Let obj be ? ToObject(O). let obj = to_object(agent, o, gc.nogc())?; // 2. Let entryList be ? EnumerableOwnProperties(obj, KEY+VALUE). @@ -475,23 +518,24 @@ impl ObjectConstructor { enumerable_properties_kind::EnumerateKeysAndValues, >(agent, obj.unbind(), gc.reborrow())?; // 3. Return CreateArrayFromList(entryList). - Ok(create_array_from_list(agent, &entry_list, gc.nogc()).into_value()) + Ok(create_array_from_list(agent, &entry_list.unbind(), gc.into_nogc()).into_value()) } /// ### [20.1.2.6 Object.freeze ( O )](https://tc39.es/ecma262/#sec-object.freeze) - fn freeze( + fn freeze<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. If O is not an Object, return O. - let o = arguments.get(0); + let o = arguments.get(0).bind(gc.nogc()); let Ok(o) = Object::try_from(o) else { - return Ok(o); + return Ok(o.unbind()); }; // 2. Let status be ? SetIntegrityLevel(O, FROZEN). - let status = set_integrity_level::(agent, o, gc.reborrow())?; + let scoped_o = o.scope(agent, gc.nogc()); + let status = set_integrity_level::(agent, o.unbind(), gc.reborrow())?; if !status { // 3. If status is false, throw a TypeError exception. Err(agent.throw_exception_with_static_message( @@ -501,20 +545,20 @@ impl ObjectConstructor { )) } else { // 4. Return O. - Ok(o.into_value()) + Ok(scoped_o.get(agent).into_value()) } } /// ### [20.1.2.7 Object.fromEntries ( iterable )](https://tc39.es/ecma262/#sec-object.fromentries) - fn from_entries( + fn from_entries<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let iterable = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let mut iterable = arguments.get(0).bind(gc.nogc()); // Fast path: Simple, dense array of N simple, dense arrays. - if let Value::Array(entries_array) = iterable { + if matches!(iterable, Value::Array(_)) { let array_prototype = agent.current_realm().intrinsics().array_prototype(); let intrinsic_array_iterator = agent .current_realm() @@ -522,12 +566,20 @@ impl ObjectConstructor { .array_prototype_values() .into_function() .unbind(); + let scoped_iterable = iterable.scope(agent, gc.nogc()); let array_iterator = get_method( agent, array_prototype.into_value(), WellKnownSymbolIndexes::Iterator.into(), gc.reborrow(), - )?; + )? + .unbind() + .bind(gc.nogc()); + // SAFETY: Not shared. + iterable = unsafe { scoped_iterable.take(agent).bind(gc.nogc()) }; + let Value::Array(entries_array) = iterable else { + unreachable!() + }; // SAFETY: If the iterator of the array is the intrinsic array // values iterator and the array is simple and dense, then we know // the behaviour of the iterator (access elements one by one) and @@ -622,19 +674,19 @@ impl ObjectConstructor { // called: // 5. Let adder be CreateBuiltinFunction(closure, 2, "", « »). // 6. Return ? AddEntriesFromIterable(obj, iterable, adder). - add_entries_from_iterable_from_entries(agent, obj.unbind(), iterable, gc) + add_entries_from_iterable_from_entries(agent, obj.unbind(), iterable.unbind(), gc) .map(|obj| obj.into_value()) } /// ### [20.1.2.8 Object.getOwnPropertyDescriptor ( O, P )](https://tc39.es/ecma262/#sec-object.getownpropertydescriptor) - fn get_own_property_descriptor( + fn get_own_property_descriptor<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); - let p = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); + let p = arguments.get(1).bind(gc.nogc()); // 1. Let obj be ? ToObject(O). let mut obj = to_object(agent, o, gc.nogc())?; // 2. Let key be ? ToPropertyKey(P). @@ -642,7 +694,7 @@ impl ObjectConstructor { key } else { let scoped_obj = obj.scope(agent, gc.nogc()); - let key = to_property_key(agent, p, gc.reborrow())? + let key = to_property_key(agent, p.unbind(), gc.reborrow())? .unbind() .bind(gc.nogc()); obj = scoped_obj.get(agent).bind(gc.nogc()); @@ -655,18 +707,18 @@ impl ObjectConstructor { // 4. Return FromPropertyDescriptor(desc). Ok( PropertyDescriptor::from_property_descriptor(desc, agent, gc.nogc()) - .map_or(Value::Undefined, |obj| obj.into_value()), + .map_or(Value::Undefined, |obj| obj.into_value().unbind()), ) } /// ### [20.1.2.9 Object.getOwnPropertyDescriptors ( O )](https://tc39.es/ecma262/#sec-object.getownpropertydescriptors) - fn get_own_property_descriptors( + fn get_own_property_descriptors<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); // 1. Let obj be ? ToObject(O). let mut obj = to_object(agent, o, gc.nogc())?; let mut scoped_obj = None; @@ -676,13 +728,11 @@ impl ObjectConstructor { own_keys } else { scoped_obj = Some(obj.scope(agent, gc.nogc())); - let own_keys = bind_property_keys( - unbind_property_keys( - obj.unbind() - .internal_own_property_keys(agent, gc.reborrow())?, - ), - gc.nogc(), - ); + let own_keys = obj + .unbind() + .internal_own_property_keys(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()); obj = scoped_obj.as_ref().unwrap().get(agent).bind(gc.nogc()); own_keys }; @@ -717,48 +767,57 @@ impl ObjectConstructor { .bind(gc.nogc()); if i < own_keys.len() { let _ = own_keys.drain(..i); - let own_keys = unbind_property_keys(own_keys); let obj = scoped_obj.unwrap_or_else(|| obj.scope(agent, gc.nogc())); - get_own_property_descriptors_slow(agent, obj, own_keys, descriptors.unbind(), gc) + get_own_property_descriptors_slow( + agent, + obj, + own_keys.unbind(), + descriptors.unbind(), + gc, + ) } else { // 5. Return descriptors. - Ok(descriptors.into_value()) + Ok(descriptors.into_value().unbind()) } } /// ### [20.1.2.10 Object.getOwnPropertyNames ( O )](https://tc39.es/ecma262/#sec-object.getownpropertynames) - fn get_own_property_names( + fn get_own_property_names<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); // 1. Return CreateArrayFromList(? GetOwnPropertyKeys(O, STRING)). - let keys = get_own_string_property_keys(agent, o, gc.reborrow())?; - Ok(create_array_from_list(agent, &keys, gc.nogc()).into_value()) + let keys = get_own_string_property_keys(agent, o.unbind(), gc.reborrow())?; + Ok(create_array_from_list(agent, &keys.unbind(), gc.nogc()) + .into_value() + .unbind()) } /// ### [20.1.2.11 Object.getOwnPropertySymbols ( O )](https://tc39.es/ecma262/#sec-object.getownpropertysymbols) - fn get_own_property_symbols( + fn get_own_property_symbols<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); // 1. Return CreateArrayFromList(? GetOwnPropertyKeys(O, SYMBOL)). - let keys = get_own_symbol_property_keys(agent, o, gc.reborrow())?; - Ok(create_array_from_list(agent, &keys, gc.nogc()).into_value()) + let keys = get_own_symbol_property_keys(agent, o.unbind(), gc.reborrow())?; + Ok(create_array_from_list(agent, &keys.unbind(), gc.nogc()) + .into_value() + .unbind()) } /// ### [20.1.2.12 Object.getPrototypeOf ( O )](https://tc39.es/ecma262/#sec-object.getprototypeof) - fn get_prototype_of( + fn get_prototype_of<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let obj = to_object(agent, arguments.get(0), gc.nogc())?; // Note: We do not use try_get_prototype_of here as we don't need to // protect any on-stack values from GC. We're perfectly okay with @@ -769,17 +828,17 @@ impl ObjectConstructor { } // ### [20.1.2.13 Object.groupBy ( items, callback )](https://tc39.es/ecma262/#sec-object.groupby) - fn group_by( + fn group_by<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let items = arguments.get(0); - let callback_fn = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let items = arguments.get(0).bind(gc.nogc()); + let callback_fn = arguments.get(1).bind(gc.nogc()); // 1. Let groups be ? GroupBy(items, callback, property). - let groups = group_by_property(agent, items, callback_fn, gc.reborrow())?; + let groups = group_by_property(agent, items.unbind(), callback_fn.unbind(), gc.reborrow())?; // 2. Let obj be OrdinaryObjectCreate(null). // 3. For each Record { [[Key]], [[Elements]] } g of groups, do @@ -800,19 +859,19 @@ impl ObjectConstructor { Ok(object.into_value()) } - fn has_own( + fn has_own<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let p = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let p = arguments.get(1).bind(gc.nogc()); let mut obj = to_object(agent, arguments.get(0), gc.nogc())?; let key = if let TryResult::Continue(key) = to_property_key_simple(agent, p, gc.nogc()) { key } else { let scoped_obj = obj.scope(agent, gc.nogc()); - let key = to_property_key(agent, p, gc.reborrow())? + let key = to_property_key(agent, p.unbind(), gc.reborrow())? .unbind() .bind(gc.nogc()); obj = scoped_obj.get(agent).bind(gc.nogc()); @@ -822,65 +881,65 @@ impl ObjectConstructor { .map(|result| result.into()) } - fn is( + fn is<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { Ok(same_value(agent, arguments.get(0), arguments.get(1)).into()) } - fn is_extensible( + fn is_extensible<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); let result = match Object::try_from(o) { - Ok(o) => o.internal_is_extensible(agent, gc.reborrow())?, + Ok(o) => o.unbind().internal_is_extensible(agent, gc.reborrow())?, Err(_) => false, }; Ok(result.into()) } - fn is_frozen( + fn is_frozen<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); let result = match Object::try_from(o) { - Ok(o) => test_integrity_level::(agent, o, gc.reborrow())?, + Ok(o) => test_integrity_level::(agent, o.unbind(), gc.reborrow())?, Err(_) => true, }; Ok(result.into()) } - fn is_sealed( + fn is_sealed<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); let result = match Object::try_from(o) { - Ok(o) => test_integrity_level::(agent, o, gc.reborrow())?, + Ok(o) => test_integrity_level::(agent, o.unbind(), gc.reborrow())?, Err(_) => true, }; Ok(result.into()) } /// ### [20.1.2.19 Object.keys ( O )](https://tc39.es/ecma262/#sec-object.keys) - fn keys( + fn keys<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); // 1. Let obj be ? ToObject(O). let obj = to_object(agent, o, gc.nogc())?; // 2. Let keyList be ? EnumerableOwnProperties(obj, KEY). @@ -890,23 +949,28 @@ impl ObjectConstructor { gc.reborrow(), )?; // 3. Return CreateArrayFromList(keyList). - Ok(create_array_from_list(agent, &key_list, gc.nogc()).into_value()) + Ok(create_array_from_list(agent, &key_list.unbind(), gc.nogc()) + .into_value() + .unbind()) } /// ### [20.1.2.20 Object.preventExtensions ( O )](https://tc39.es/ecma262/#sec-object.preventextensions) - fn prevent_extensions( + fn prevent_extensions<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. If O is not an Object, return O. - let o = arguments.get(0); + let o = arguments.get(0).bind(gc.nogc()); let Ok(o) = Object::try_from(o) else { - return Ok(o); + return Ok(o.unbind()); }; + let scoped_o = o.scope(agent, gc.nogc()); // 2. Let status be ? O.[[PreventExtensions]](). - let status = o.internal_prevent_extensions(agent, gc.reborrow())?; + let status = o + .unbind() + .internal_prevent_extensions(agent, gc.reborrow())?; // 3. If status is false, throw a TypeError exception. if !status { Err(agent.throw_exception_with_static_message( @@ -916,24 +980,25 @@ impl ObjectConstructor { )) } else { // 4. Return O. - Ok(o.into_value()) + Ok(scoped_o.get(agent).into_value()) } } /// ### [20.1.2.22 Object.seal ( O )](https://tc39.es/ecma262/#sec-object.seal) - fn seal( + fn seal<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. If O is not an Object, return O. - let o = arguments.get(0); + let o = arguments.get(0).bind(gc.nogc()); let Ok(o) = Object::try_from(o) else { - return Ok(o); + return Ok(o.unbind()); }; // 2. Let status be ? SetIntegrityLevel(O, SEALED). - let status = set_integrity_level::(agent, o, gc.reborrow())?; + let scoped_o = o.scope(agent, gc.nogc()); + let status = set_integrity_level::(agent, o.unbind(), gc.reborrow())?; if !status { // 3. If status is false, throw a TypeError exception. Err(agent.throw_exception_with_static_message( @@ -943,19 +1008,19 @@ impl ObjectConstructor { )) } else { // 4. Return O. - Ok(o.into_value()) + Ok(scoped_o.get(agent).into_value()) } } /// ### [20.1.2.23 Object.setPrototypeOf ( O, proto )](https://tc39.es/ecma262/#sec-object.setprototypeof) - fn set_prototype_of( + fn set_prototype_of<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); - let proto = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); + let proto = arguments.get(1).bind(gc.nogc()); // 1. Set O to ? RequireObjectCoercible(O). let o = require_object_coercible(agent, o, gc.nogc())?; // 2. If proto is not an Object and proto is not null, throw a TypeError exception. @@ -966,16 +1031,26 @@ impl ObjectConstructor { } else { let error_message = format!( "{} is not an object or null", - proto.string_repr(agent, gc.reborrow()).as_str(agent) + proto + .unbind() + .string_repr(agent, gc.reborrow()) + .as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message, gc.nogc())); + return Err(agent.throw_exception( + ExceptionType::TypeError, + error_message, + gc.into_nogc(), + )); }; // 3. If O is not an Object, return O. let Ok(o) = Object::try_from(o) else { - return Ok(o); + return Ok(o.unbind()); }; // 4. Let status be ? O.[[SetPrototypeOf]](proto). - let status = o.internal_set_prototype_of(agent, proto, gc.reborrow())?; + let scoped_o = o.scope(agent, gc.nogc()); + let status = o + .unbind() + .internal_set_prototype_of(agent, proto.unbind(), gc.reborrow())?; // 5. If status is false, throw a TypeError exception. if !status { return Err(agent.throw_exception_with_static_message( @@ -985,16 +1060,16 @@ impl ObjectConstructor { )); } // 6. Return O. - Ok(o.into_value()) + Ok(scoped_o.get(agent).into_value()) } - fn values( + fn values<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let o = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let o = arguments.get(0).bind(gc.nogc()); // 1. Let obj be ? ToObject(O). let obj = to_object(agent, o, gc.nogc())?; // 2. Let valueList be ? EnumerableOwnProperties(obj, VALUE). @@ -1004,7 +1079,11 @@ impl ObjectConstructor { gc.reborrow(), )?; // 3. Return CreateArrayFromList(valueList). - Ok(create_array_from_list(agent, &value_list, gc.nogc()).into_value()) + Ok( + create_array_from_list(agent, &value_list.unbind(), gc.nogc()) + .into_value() + .unbind(), + ) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -1055,14 +1134,11 @@ fn object_define_properties<'a, T: InternalMethods<'a>>( // 1. Let props be ? ToObject(Properties). let props = to_object(agent, properties, gc.nogc())?.scope(agent, gc.nogc()); // 2. Let keys be ? props.[[OwnPropertyKeys]](). - let keys = bind_property_keys( - unbind_property_keys( - props - .get(agent) - .internal_own_property_keys(agent, gc.reborrow())?, - ), - gc.nogc(), - ); + let keys = props + .get(agent) + .internal_own_property_keys(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()); let keys = scope_property_keys(agent, keys, gc.nogc()); // 3. Let descriptors be a new empty List. let mut descriptors = Vec::with_capacity(keys.len()); @@ -1084,8 +1160,9 @@ fn object_define_properties<'a, T: InternalMethods<'a>>( // i. Let descObj be ? Get(props, nextKey). let desc_obj = get(agent, props.get(agent), next_key.get(agent), gc.reborrow())?; // ii. Let desc be ? ToPropertyDescriptor(descObj). - let desc = PropertyDescriptor::to_property_descriptor(agent, desc_obj, gc.reborrow())? - .scope(agent, gc.nogc()); + let desc = + PropertyDescriptor::to_property_descriptor(agent, desc_obj.unbind(), gc.reborrow())? + .scope(agent, gc.nogc()); // iii. Append the Record { [[Key]]: nextKey, [[Descriptor]]: desc } to descriptors. descriptors.push((next_key, desc)); } @@ -1183,11 +1260,19 @@ pub fn add_entries_from_iterable_from_entries<'a>( iterable: Value, mut gc: GcScope<'a, '_>, ) -> JsResult> { - let target = target.bind(gc.nogc()).scope(agent, gc.nogc()); + // Note: scoped_next is a slot for next value scoping that originally holds + // the target but will later be reused for each repeat of the loop. We + // cannot reuse the scoped target below for this, as the value held in the + // scoped_next will change on each loop. + let mut scoped_next = target.into_object().scope(agent, gc.nogc()); + let target = target.scope(agent, gc.nogc()); + let iterable = iterable.scope(agent, gc.nogc()); // 1. Let iteratorRecord be ? GetIterator(iterable, SYNC). - let mut iterator_record = get_iterator(agent, iterable, false, gc.reborrow())?; + let mut iterator_record = get_iterator(agent, iterable.get(agent), false, gc.reborrow())?; // 2. Repeat, + let mut scoped_k = Value::Undefined.scope_static(); + let mut scoped_v = Value::Undefined.scope_static(); loop { // a. Let next be ? IteratorStepValue(iteratorRecord). let next = iterator_step_value(agent, &mut iterator_record, gc.reborrow())?; @@ -1200,38 +1285,43 @@ pub fn add_entries_from_iterable_from_entries<'a>( // i. Let error be ThrowCompletion(a newly created TypeError object). let error_message = format!( "Invalid iterator next return value: {} is not an object", - next.string_repr(agent, gc.reborrow()).as_str(agent) + next.unbind() + .string_repr(agent, gc.reborrow()) + .as_str(agent) ); let error = agent.throw_exception(ExceptionType::TypeError, error_message, gc.nogc()); // ii. Return ? IteratorClose(iteratorRecord, error). iterator_close(agent, &iterator_record, Err(error), gc.reborrow())?; return Ok(target.get(agent).bind(gc.into_nogc())); }; + // SAFETY: scoped_next is its own Scoped value, not a clone from target + // or anything else. Hence we can change its held value freely. + unsafe { scoped_next.replace(agent, next.unbind()) }; // d. Let k be Completion(Get(next, "0")). - let k = get(agent, next, 0.into(), gc.reborrow()); + let k = get(agent, next.unbind(), 0.into(), gc.reborrow()); // e. IfAbruptCloseIterator(k, iteratorRecord). - let k = if_abrupt_close_iterator(agent, k, &iterator_record, gc.reborrow())?; + let k = if_abrupt_close_iterator!(agent, k, iterator_record, gc); + // SAFETY: scoped_k is never shared. + unsafe { scoped_k.replace(agent, k.unbind()) }; // f. Let v be Completion(Get(next, "1")). - let v = get(agent, next, 1.into(), gc.reborrow()); + let v = get(agent, scoped_next.get(agent), 1.into(), gc.reborrow()); // g. IfAbruptCloseIterator(v, iteratorRecord). - let v = if_abrupt_close_iterator(agent, v, &iterator_record, gc.reborrow())?; + let v = if_abrupt_close_iterator!(agent, v, iterator_record, gc); + // SAFETY: scoped_v is never shared. + unsafe { scoped_v.replace(agent, v.unbind()) }; // h. Let status be Completion(Call(adder, target, « k, v »)). { // a. Let propertyKey be ? ToPropertyKey(key). - let property_key = to_property_key(agent, k, gc.reborrow()).map(|pk| pk.unbind()); + let property_key = to_property_key(agent, scoped_k.get(agent), gc.reborrow()); // i. IfAbruptCloseIterator(status, iteratorRecord). - let property_key = - if_abrupt_close_iterator(agent, property_key, &iterator_record, gc.reborrow())?; + let property_key = if_abrupt_close_iterator!(agent, property_key, iterator_record, gc); // b. Perform ! CreateDataPropertyOrThrow(obj, propertyKey, value). - target - .get(agent) - .internal_define_own_property( - agent, - property_key.unbind(), - PropertyDescriptor::new_data_descriptor(v), - gc.reborrow(), - ) - .unwrap(); + unwrap_try(target.get(agent).try_define_own_property( + agent, + property_key.unbind(), + PropertyDescriptor::new_data_descriptor(scoped_v.get(agent)), + gc.nogc(), + )); // c. Return undefined. } } @@ -1242,20 +1332,20 @@ pub fn add_entries_from_iterable_from_entries<'a>( /// The abstract operation GetOwnPropertyKeys takes arguments O (an ECMAScript /// language value) and type (STRING or SYMBOL) and returns either a normal /// completion containing a List of property keys or a throw completion. -fn get_own_string_property_keys( +fn get_own_string_property_keys<'gc>( agent: &mut Agent, o: Value, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'gc, '_>, +) -> JsResult>> { // 1. Let obj be ? ToObject(O). let obj = to_object(agent, o, gc.nogc())?; // 2. Let keys be ? obj.[[OwnPropertyKeys]](). - let keys = unbind_property_keys( - obj.unbind() - .internal_own_property_keys(agent, gc.reborrow())?, - ); + let keys = obj + .unbind() + .internal_own_property_keys(agent, gc.reborrow())? + .unbind(); let gc = gc.into_nogc(); - let keys = bind_property_keys(keys, gc); + let keys = keys.bind(gc); // 3. Let nameList be a new empty List. let mut name_list = Vec::with_capacity(keys.len()); // 4. For each element nextKey of keys, do @@ -1276,11 +1366,11 @@ fn get_own_string_property_keys( Ok(name_list) } -fn get_own_symbol_property_keys( +fn get_own_symbol_property_keys<'gc>( agent: &mut Agent, o: Value, - mut gc: GcScope, -) -> JsResult> { + mut gc: GcScope<'gc, '_>, +) -> JsResult>> { // 1. Let obj be ? ToObject(O). let obj = to_object(agent, o, gc.nogc())?; // 2. Let keys be ? obj.[[OwnPropertyKeys]](). @@ -1293,20 +1383,20 @@ fn get_own_symbol_property_keys( for next_key in keys { // a. If nextKey is a Symbol and type is SYMBOL then if let PropertyKey::Symbol(next_key) = next_key { - name_list.push(next_key.into_value()) + name_list.push(next_key.into_value().unbind()) } } // 5. Return nameList. Ok(name_list) } -fn get_own_property_descriptors_slow( +fn get_own_property_descriptors_slow<'gc>( agent: &mut Agent, obj: Scoped<'_, Object<'static>>, own_keys: Vec, descriptors: OrdinaryObject, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { let descriptors = descriptors.scope(agent, gc.nogc()); let own_keys = scope_property_keys(agent, own_keys, gc.nogc()); for key in own_keys { diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs index b69f1b7f2..cb3f3bd7c 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs @@ -3,7 +3,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::operations_on_objects::is_prototype_of_loop; -use crate::engine::context::GcScope; +use crate::ecmascript::types::IntoValue; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -81,12 +82,12 @@ impl Builtin for ObjectPrototypeValueOf { } impl ObjectPrototype { - fn has_own_property( + fn has_own_property<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let p = to_property_key(agent, arguments.get(0), gc.reborrow())? .unbind() .bind(gc.nogc()); @@ -94,12 +95,12 @@ impl ObjectPrototype { has_own_property(agent, o.unbind(), p.unbind(), gc.reborrow()).map(|result| result.into()) } - fn is_prototype_of( + fn is_prototype_of<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let v = arguments.get(0); let Ok(v) = Object::try_from(v) else { return Ok(false.into()); @@ -109,12 +110,12 @@ impl ObjectPrototype { Ok(result.into()) } - fn property_is_enumerable( + fn property_is_enumerable<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let p = to_property_key(agent, arguments.get(0), gc.reborrow())? .unbind() .bind(gc.nogc()); @@ -129,24 +130,24 @@ impl ObjectPrototype { } } - fn to_locale_string( + fn to_locale_string<'gc>( agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let o = this_value; let p = PropertyKey::from(BUILTIN_STRING_MEMORY.toString); - invoke(agent, o, p, None, gc.reborrow()) + invoke(agent, o, p, None, gc) } - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - match this_value { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + match this_value.bind(gc.nogc()) { // 1. If the this value is undefined, return "[object Undefined]". Value::Undefined => Ok(BUILTIN_STRING_MEMORY._object_Undefined_.into_value()), // 2. If the this value is null, return "[object Null]". @@ -210,11 +211,11 @@ impl ObjectPrototype { )?; if let Ok(tag) = String::try_from(tag) { let str = format!("[object {}]", tag.as_str(agent)); - Ok(Value::from_string(agent, str, gc.nogc())) + Ok(Value::from_string(agent, str, gc.into_nogc())) } else { let str = format!("[object {}]", BUILTIN_STRING_MEMORY.Object.as_str(agent)); - Ok(Value::from_string(agent, str, gc.nogc())) + Ok(Value::from_string(agent, str, gc.into_nogc())) } } }, @@ -231,23 +232,23 @@ impl ObjectPrototype { )?; if let Ok(tag) = String::try_from(tag) { let str = format!("[object {}]", tag.as_str(agent)); - Ok(Value::from_string(agent, str, gc.nogc())) + Ok(Value::from_string(agent, str, gc.into_nogc())) } else { // 14. Else, let builtinTag be "Object". let str = format!("[object {}]", BUILTIN_STRING_MEMORY.Object.as_str(agent)); - Ok(Value::from_string(agent, str, gc.nogc())) + Ok(Value::from_string(agent, str, gc.into_nogc())) } } } } - fn value_of( + fn value_of<'gc>( agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { - to_object(agent, this_value, gc.nogc()).map(|result| result.into_value()) + gc: GcScope<'gc, '_>, + ) -> JsResult> { + to_object(agent, this_value, gc.into_nogc()).map(|result| result.into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_constructor.rs index b79f5ab62..dc39185e7 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_constructor.rs @@ -20,7 +20,7 @@ use crate::ecmascript::types::String; use crate::ecmascript::types::SymbolHeapData; use crate::ecmascript::types::Value; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::heap::CreateHeapData; use crate::heap::IntrinsicConstructorIndexes; use crate::heap::WellKnownSymbolIndexes; @@ -59,13 +59,13 @@ impl Builtin for SymbolKeyFor { } impl SymbolConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { if new_target.is_some() { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -88,22 +88,22 @@ impl SymbolConstructor { .into_value()) } - fn r#for( + fn r#for<'gc>( _agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(arguments.get(0)) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(arguments.get(0).unbind()) } - fn key_for( + fn key_for<'gc>( _agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(arguments.get(0)) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(arguments.get(0).unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_prototype.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_prototype.rs index c5f3c0b63..d913a9af0 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/symbol_objects/symbol_prototype.rs @@ -2,7 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::{GcScope, NoGcScope}; +use crate::ecmascript::builtins::Behaviour; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -26,8 +27,7 @@ impl Builtin for SymbolPrototypeGetDescription { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(SymbolPrototype::get_description); + const BEHAVIOUR: Behaviour = Behaviour::Regular(SymbolPrototype::get_description); } impl BuiltinGetter for SymbolPrototypeGetDescription {} @@ -37,8 +37,7 @@ impl Builtin for SymbolPrototypeToString { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(SymbolPrototype::to_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(SymbolPrototype::to_string); } struct SymbolPrototypeValueOf; @@ -47,8 +46,7 @@ impl Builtin for SymbolPrototypeValueOf { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(SymbolPrototype::value_of); + const BEHAVIOUR: Behaviour = Behaviour::Regular(SymbolPrototype::value_of); } struct SymbolPrototypeToPrimitive; @@ -60,8 +58,7 @@ impl Builtin for SymbolPrototypeToPrimitive { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(SymbolPrototype::value_of); + const BEHAVIOUR: Behaviour = Behaviour::Regular(SymbolPrototype::value_of); const WRITABLE: bool = false; } @@ -71,12 +68,12 @@ impl SymbolPrototype { /// /// Symbol.prototype.description is an accessor property whose set accessor /// function is undefined. - fn get_description( + fn get_description<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let s be the this value. // 2. Let sym be ? ThisSymbolValue(s). let sym = this_symbol_value(agent, this_value, gc.nogc())?; @@ -86,23 +83,25 @@ impl SymbolPrototype { .map_or_else(|| Ok(Value::Undefined), |desc| Ok(desc.into_value())) } - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let symb = this_symbol_value(agent, this_value, gc.nogc())?; - Ok(symbol_descriptive_string(agent, symb, gc.nogc()).into_value()) + Ok(symbol_descriptive_string(agent, symb, gc.nogc()) + .into_value() + .unbind()) } - fn value_of( + fn value_of<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { - this_symbol_value(agent, this_value, gc.nogc()).map(|res| res.into_value()) + gc: GcScope<'gc, '_>, + ) -> JsResult> { + this_symbol_value(agent, this_value, gc.nogc()).map(|res| res.into_value().unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -182,7 +181,7 @@ fn this_symbol_value<'a>( gc: NoGcScope<'a, '_>, ) -> JsResult> { match value { - Value::Symbol(symbol) => Ok(symbol), + Value::Symbol(symbol) => Ok(symbol.unbind()), Value::PrimitiveObject(object) if object.is_symbol_object(agent) => { let s: Symbol = agent[object].data.try_into().unwrap(); Ok(s) diff --git a/nova_vm/src/ecmascript/builtins/global_object.rs b/nova_vm/src/ecmascript/builtins/global_object.rs index 6a45d9834..2edede0b1 100644 --- a/nova_vm/src/ecmascript/builtins/global_object.rs +++ b/nova_vm/src/ecmascript/builtins/global_object.rs @@ -11,7 +11,7 @@ use crate::ecmascript::abstract_operations::type_conversion::{ is_trimmable_whitespace, to_int32, to_int32_number, to_number_primitive, to_string, }; use crate::ecmascript::types::Primitive; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_number, @@ -145,19 +145,19 @@ impl BuiltinIntrinsic for GlobalObjectUnescape { /// language value), strictCaller (a Boolean), and direct (a Boolean) and /// returns either a normal completion containing an ECMAScript language value /// or a throw completion. -pub fn perform_eval( +pub fn perform_eval<'gc>( agent: &mut Agent, x: Value, direct: bool, strict_caller: bool, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { // 1. Assert: If direct is false, then strictCaller is also false. assert!(direct || !strict_caller); // 2. If x is not a String, return x. let Ok(x) = String::try_from(x) else { - return Ok(x); + return Ok(x.unbind()); }; // 3. Let evalRealm be the current Realm Record. @@ -356,7 +356,7 @@ pub fn perform_eval( // 32. Resume the context that is now on the top of the execution context stack as the running execution context. // 33. Return ? result. - result + result.map(|v| v.unbind()) } /// ### [19.2.1.3 EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )](https://tc39.es/ecma262/#sec-evaldeclarationinstantiation) @@ -629,8 +629,9 @@ pub fn eval_declaration_instantiation( }); // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. - let fo = - instantiate_function_object(agent, f, lex_env, private_env, gc.nogc()).into_value(); + let fo = instantiate_function_object(agent, f, lex_env, private_env, gc.nogc()) + .into_value() + .unbind(); // c. If varEnv is a Global Environment Record, then if let EnvironmentIndex::Global(var_env) = var_env { @@ -639,8 +640,8 @@ pub fn eval_declaration_instantiation( // i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true). var_env.create_global_function_binding( agent, - function_name, - fo, + function_name.unbind(), + fo.unbind(), true, gc.reborrow(), )?; @@ -650,7 +651,7 @@ pub fn eval_declaration_instantiation( let function_name = String::from_str(agent, function_name.unwrap().as_str(), gc.nogc()) .scope(agent, gc.nogc()); let binding_exists = var_env - .has_binding(agent, function_name.get(agent), gc.reborrow()) + .has_binding(agent, function_name.get(agent).unbind(), gc.reborrow()) .unwrap(); // ii. If bindingExists is false, then @@ -658,17 +659,28 @@ pub fn eval_declaration_instantiation( // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14. // 2. Perform ! varEnv.CreateMutableBinding(fn, true). var_env - .create_mutable_binding(agent, function_name.get(agent), true, gc.reborrow()) + .create_mutable_binding( + agent, + function_name.get(agent).unbind(), + true, + gc.reborrow(), + ) .unwrap(); // 3. Perform ! varEnv.InitializeBinding(fn, fo). var_env - .initialize_binding(agent, function_name.get(agent), fo, gc.reborrow()) + .initialize_binding(agent, function_name.get(agent).unbind(), fo, gc.reborrow()) .unwrap(); } else { // iii. Else, // 1. Perform ! varEnv.SetMutableBinding(fn, fo, false). var_env - .set_mutable_binding(agent, function_name.get(agent), fo, false, gc.reborrow()) + .set_mutable_binding( + agent, + function_name.get(agent).unbind(), + fo, + false, + gc.reborrow(), + ) .unwrap(); } } @@ -709,27 +721,27 @@ impl GlobalObject { /// ### [19.2.1 eval ( x )](https://tc39.es/ecma262/#sec-eval-x) /// /// This function is the %eval% intrinsic object. - fn eval( + fn eval<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let x = arguments.get(0); // 1. Return ? PerformEval(x, false, false). - perform_eval(agent, x, false, false, gc.reborrow()) + perform_eval(agent, x, false, false, gc.reborrow()).map(|v| v.unbind()) } /// ### [19.2.2 isFinite ( number )](https://tc39.es/ecma262/#sec-isfinite-number) /// /// This function is the %isFinite% intrinsic object. - fn is_finite( + fn is_finite<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let number = arguments.get(0); // 1. Let num be ? ToNumber(number). let num = to_number(agent, number, gc.reborrow())?; @@ -745,12 +757,12 @@ impl GlobalObject { /// > NOTE: A reliable way for ECMAScript code to test if a value X is NaN /// > is an expression of the form X !== X. The result will be true if and /// > only if X is NaN. - fn is_nan( + fn is_nan<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let number = arguments.get(0); // 1. Let num be ? ToNumber(number). let num = to_number(agent, number, gc.reborrow())?; @@ -763,12 +775,12 @@ impl GlobalObject { /// /// This function produces a Number value dictated by interpretation of the /// contents of the string argument as a decimal literal. - fn parse_float( + fn parse_float<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { if arguments.len() == 0 { return Ok(Value::nan()); } @@ -817,7 +829,7 @@ impl GlobalObject { } } - Ok(Value::from_f64(agent, f, gc.nogc())) + Ok(Value::from_f64(agent, f, gc.nogc()).unbind()) } else { Ok(Value::nan()) } @@ -832,12 +844,12 @@ impl GlobalObject { /// representation begins with "0x" or "0X", in which case it is assumed to /// be 16. If radix is 16, the number representation may optionally begin /// with "0x" or "0X". - fn parse_int( + fn parse_int<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let string = arguments.get(0); let radix = arguments.get(1); @@ -854,7 +866,7 @@ impl GlobalObject { if let Value::Integer(radix) = radix { let radix = radix.into_i64(); if radix == 10 && matches!(string, Value::Integer(_)) { - return Ok(string); + return Ok(string.unbind()); } } @@ -1002,59 +1014,59 @@ impl GlobalObject { // a. If sign = -1, return -0𝔽. // b. Return +0𝔽. // 16. Return 𝔽(sign × mathInt). - Ok(Value::from_f64(agent, sign as f64 * math_int, gc.nogc())) + Ok(Value::from_f64(agent, sign as f64 * math_int, gc.nogc()).unbind()) } } } } } - fn decode_uri( + fn decode_uri<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn decode_uri_component( + fn decode_uri_component<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn encode_uri( + fn encode_uri<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn encode_uri_component( + fn encode_uri_component<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn escape( + fn escape<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn unescape( + fn unescape<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_constructor.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_constructor.rs index d1204a35f..eb201e175 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_constructor.rs @@ -20,6 +20,7 @@ use crate::ecmascript::abstract_operations::testing_and_comparison::is_callable; use crate::ecmascript::abstract_operations::testing_and_comparison::is_constructor; use crate::ecmascript::abstract_operations::testing_and_comparison::same_value_zero; use crate::ecmascript::abstract_operations::type_conversion::to_object; +use crate::ecmascript::abstract_operations::type_conversion::to_uint32_number; use crate::ecmascript::builders::builtin_function_builder::BuiltinFunctionBuilder; use crate::ecmascript::builtins::array_create; @@ -37,16 +38,21 @@ use crate::ecmascript::execution::ProtoIntrinsics; use crate::ecmascript::execution::RealmIdentifier; use crate::ecmascript::types::Function; +use crate::ecmascript::types::InternalMethods; +use crate::ecmascript::types::IntoFunction; use crate::ecmascript::types::IntoObject; use crate::ecmascript::types::IntoValue; use crate::ecmascript::types::Number; use crate::ecmascript::types::Object; +use crate::ecmascript::types::PropertyDescriptor; use crate::ecmascript::types::PropertyKey; use crate::ecmascript::types::String; use crate::ecmascript::types::Value; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; +use crate::engine::context::Bindable; use crate::engine::context::GcScope; use crate::engine::unwrap_try; +use crate::engine::Scoped; use crate::heap::IntrinsicConstructorIndexes; use crate::heap::WellKnownSymbolIndexes; use crate::SmallInteger; @@ -93,144 +99,162 @@ impl BuiltinGetter for ArrayGetSpecies {} /// ### [23.1.1 The Array Constructor](https://tc39.es/ecma262/#sec-array-constructor) impl ArrayConstructor { /// ### [23.1.1.1 Array ( ...values )](https://tc39.es/ecma262/#sec-array) - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + enum BoundArgs<'a> { + Length(Scoped<'a, Value<'static>>), + Items(Vec>>), + } + let new_target = new_target.bind(gc.nogc()); // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = new_target.map_or_else( || agent.running_execution_context().function.unwrap(), |new_target| Function::try_from(new_target).unwrap(), ); + let arguments = if arguments.len() == 0 { + None + } else if arguments.len() == 1 { + Some(BoundArgs::Length(arguments.get(0).scope(agent, gc.nogc()))) + } else { + Some(BoundArgs::Items( + arguments + .iter() + .map(|v| v.scope(agent, gc.nogc())) + .collect(), + )) + }; + // 2. Let proto be ? GetPrototypeFromConstructor(newTarget, "%Array.prototype%"). let proto = get_prototype_from_constructor( agent, - new_target, + new_target.unbind(), ProtoIntrinsics::Array, gc.reborrow(), )? - .map(|p| p.unbind()) - .map(|p| p.bind(gc.nogc())); + .unbind(); + let gc = gc.into_nogc(); + let proto = proto.bind(gc); // 3. Let numberOfArgs be the number of elements in values. - let number_of_args = arguments.len(); // 4. If numberOfArgs = 0, then - if number_of_args == 0 { + let Some(arguments) = arguments else { // a. Return ! ArrayCreate(0, proto). - return Ok(array_create(agent, 0, 0, proto, gc.nogc()) - .unwrap() - .into_value()); - } + return Ok(array_create(agent, 0, 0, proto, gc).unwrap().into_value()); + }; // 5. Else if numberOfArgs = 1, then - if number_of_args == 1 { - // a. Let len be values[0]. - let len = arguments.get(0); - - // c. If len is not a Number, then - let array = if !len.is_number() { - // b. Let array be ! ArrayCreate(0, proto). - let array = array_create(agent, 1, 1, proto, gc.nogc()).unwrap(); - // i. Perform ! CreateDataPropertyOrThrow(array, "0", len). - unwrap_try(try_create_data_property_or_throw( - agent, - array, - PropertyKey::from(SmallInteger::zero()), - len, - gc.nogc(), - )) - .unwrap(); - // ii. Let intLen be 1𝔽. - // e. Perform ! Set(array, "length", intLen, true). - debug_assert_eq!(agent[array].elements.len(), 1); - array - } else { - // d. Else, - // i. Let intLen be ! ToUint32(len). - let proto = proto.map(|p| p.scope(agent, gc.nogc())); - let int_len = len.to_uint32(agent, gc.reborrow()).unwrap(); - // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception. - if !same_value_zero(agent, int_len, len) { - return Err(agent.throw_exception_with_static_message( - ExceptionType::RangeError, - "Invalid array length", - gc.nogc(), - )); - } - let array = array_create( - agent, - int_len as usize, - int_len as usize, - proto.map(|p| p.get(agent)), - gc.nogc(), - ) - .unwrap(); - // e. Perform ! Set(array, "length", intLen, true). - debug_assert_eq!(agent[array].elements.len(), int_len); - array - }; - - // f. Return array. - return Ok(array.into_value()); - } - - // 6. Else, - // a. Assert: numberOfArgs ≥ 2. - debug_assert!(number_of_args >= 2); - - // b. Let array be ? ArrayCreate(numberOfArgs, proto). - let array = array_create(agent, number_of_args, number_of_args, proto, gc.nogc())?; - // NOTE: `array_create` guarantees that it is less than `u32::MAX` - let number_of_args = number_of_args as u32; - - // c. Let k be 0. - let mut k: u32 = 0; - - // d. Repeat, while k < numberOfArgs, - while k < number_of_args { - // NOTE: We slightly deviate from the exact spec wording here, see [@aapoalas comment on #180](https://github.com/trynova/nova/pull/180#discussion_r1600382492) - // i. Let Pk be ! ToString(𝔽(k)). - let pk = PropertyKey::from(SmallInteger::from(k)); + match arguments { + BoundArgs::Length(len) => { + // a. Let len be values[0]. + let len = len.get(agent).bind(gc); + + // c. If len is not a Number, then + let array = if let Ok(len) = Number::try_from(len) { + // d. Else, + // i. Let intLen be ! ToUint32(len). + let proto = proto.map(|p| p.scope(agent, gc)); + let int_len = to_uint32_number(agent, len); + // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception. + if !same_value_zero(agent, int_len, len) { + return Err(agent.throw_exception_with_static_message( + ExceptionType::RangeError, + "Invalid array length", + gc, + )); + } + let array = array_create( + agent, + int_len as usize, + int_len as usize, + proto.map(|p| p.get(agent)), + gc, + ) + .unwrap(); + // e. Perform ! Set(array, "length", intLen, true). + debug_assert_eq!(agent[array].elements.len(), int_len); + array + } else { + // b. Let array be ! ArrayCreate(0, proto). + let array = array_create(agent, 1, 1, proto, gc).unwrap(); + // i. Perform ! CreateDataPropertyOrThrow(array, "0", len). + unwrap_try(try_create_data_property_or_throw( + agent, + array, + PropertyKey::from(SmallInteger::zero()), + len, + gc, + )) + .unwrap(); + // ii. Let intLen be 1𝔽. + // e. Perform ! Set(array, "length", intLen, true). + debug_assert_eq!(agent[array].elements.len(), 1); + array + }; - // ii. Let itemK be values[k]. - let item_k = arguments.get(k as usize); + // f. Return array. + Ok(array.into_value()) + } + BoundArgs::Items(args) => { + // 6. Else, + // a. Assert: numberOfArgs ≥ 2. + let number_of_args = args.len(); + debug_assert!(number_of_args >= 2); + + // b. Let array be ? ArrayCreate(numberOfArgs, proto). + let array = array_create(agent, number_of_args, number_of_args, proto, gc)?; + + // c. Let k be 0. + // d. Repeat, while k < numberOfArgs, + for (k, item_k) in args.into_iter().enumerate() { + // NOTE: `array_create` guarantees that it is less than `u32::MAX` + let k = k as u32; + // NOTE: We slightly deviate from the exact spec wording + // here, see [@aapoalas comment on #180](https://github.com/trynova/nova/pull/180#discussion_r1600382492) + // i. Let Pk be ! ToString(𝔽(k)). + let pk = PropertyKey::from(SmallInteger::from(k)); + + // ii. Let itemK be values[k]. + + // iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK). + unwrap_try(try_create_data_property_or_throw( + agent, + array, + pk, + item_k.get(agent), + gc, + )) + .unwrap(); + + // iv. Set k to k + 1. + } - // iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK). - unwrap_try(try_create_data_property_or_throw( - agent, - array, - pk, - item_k, - gc.nogc(), - )) - .unwrap(); + // e. Assert: The mathematical value of array's "length" property is numberOfArgs. + debug_assert_eq!(array.len(agent) as usize, number_of_args); - // iv. Set k to k + 1. - k += 1; + // f. Return array. + Ok(array.into_value()) + } } - - // e. Assert: The mathematical value of array's "length" property is numberOfArgs. - debug_assert_eq!(array.len(agent), number_of_args); - - // f. Return array. - Ok(array.into_value()) } /// ### [23.1.2.1 Array.from ( items \[ , mapfn \[ , thisArg \] \] )](https://tc39.es/ecma262/#sec-array.from) - fn from( + fn from<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let items = arguments.get(0); - let mapfn = arguments.get(1); - let this_arg = arguments.get(2); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); + let items = arguments.get(0).bind(gc.nogc()); + let mapfn = arguments.get(1).bind(gc.nogc()); + let this_arg = arguments.get(2).bind(gc.nogc()); // 1. Let C be the this value. // 2. If mapfn is undefined, then @@ -251,11 +275,14 @@ impl ArrayConstructor { // b. Let mapping be true. Some(mapfn.scope(agent, gc.nogc())) }; + let scoped_this_value = this_value.scope(agent, gc.nogc()); + let scoped_items = items.scope(agent, gc.nogc()); + let scoped_this_arg = this_arg.scope(agent, gc.nogc()); // 4. Let usingIterator be ? GetMethod(items, @@iterator). let using_iterator = get_method( agent, - items, + items.unbind(), WellKnownSymbolIndexes::Iterator.into(), gc.reborrow(), )?; @@ -264,10 +291,10 @@ impl ArrayConstructor { if let Some(using_iterator) = using_iterator { let mut using_iterator = using_iterator.unbind().bind(gc.nogc()); // a. If IsConstructor(C) is true, then - let a = if let Some(c) = is_constructor(agent, this_value) { + let a = if let Some(c) = is_constructor(agent, scoped_this_value.get(agent)) { let scoped_using_iterator = using_iterator.scope(agent, gc.nogc()); // i. Let A be ? Construct(C). - let a = construct(agent, c, None, None, gc.reborrow())? + let a = construct(agent, c.unbind(), None, None, gc.reborrow())? .unbind() .bind(gc.nogc()); using_iterator = scoped_using_iterator.get(agent).bind(gc.nogc()); @@ -283,8 +310,12 @@ impl ArrayConstructor { let a = a.scope(agent, gc.nogc()); // c. Let iteratorRecord be ? GetIteratorFromMethod(items, usingIterator). - let mut iterator_record = - get_iterator_from_method(agent, items, using_iterator.unbind(), gc.reborrow())?; + let mut iterator_record = get_iterator_from_method( + agent, + scoped_items.get(agent), + using_iterator.unbind(), + gc.reborrow(), + )?; // d. Let k be 0. let mut k = 0; @@ -335,20 +366,13 @@ impl ArrayConstructor { let mapped_value = call_function( agent, mapping.get(agent), - this_arg, - Some(ArgumentsList(&[next, fk])), + scoped_this_arg.get(agent), + Some(ArgumentsList(&[next.unbind(), fk])), gc.reborrow(), ); // 2. IfAbruptCloseIterator(mappedValue, iteratorRecord). - let _ = if_abrupt_close_iterator( - agent, - mapped_value, - &iterator_record, - gc.reborrow(), - ); - - mapped_value.unwrap() + if_abrupt_close_iterator!(agent, mapped_value, iterator_record, gc) } else { // vi. Else, // 1. Let mappedValue be next. @@ -360,17 +384,12 @@ impl ArrayConstructor { agent, a.get(agent), pk, - mapped_value, + mapped_value.unbind(), gc.reborrow(), ); // viii. IfAbruptCloseIterator(defineStatus, iteratorRecord). - let _ = if_abrupt_close_iterator( - agent, - define_status.map(|_| Value::Undefined), - &iterator_record, - gc.reborrow(), - ); + if_abrupt_close_iterator!(agent, define_status, iterator_record, gc); // ix. Set k to k + 1. k += 1; @@ -379,7 +398,7 @@ impl ArrayConstructor { // 6. NOTE: items is not an Iterable so assume it is an array-like object. // 7. Let arrayLike be ! ToObject(items). - let array_like = to_object(agent, items, gc.nogc()) + let array_like = to_object(agent, scoped_items.get(agent), gc.nogc()) .unwrap() .scope(agent, gc.nogc()); @@ -388,7 +407,7 @@ impl ArrayConstructor { let len_value = Value::try_from(len).unwrap(); // 9. If IsConstructor(C) is true, then - let a = if let Some(c) = is_constructor(agent, this_value) { + let a = if let Some(c) = is_constructor(agent, scoped_this_value.get(agent)) { // a. Let A be ? Construct(C, « 𝔽(len) »). construct( agent, @@ -428,8 +447,8 @@ impl ArrayConstructor { call_function( agent, mapping.get(agent), - this_arg, - Some(ArgumentsList(&[k_value, fk])), + scoped_this_arg.get(agent), + Some(ArgumentsList(&[k_value.unbind(), fk])), gc.reborrow(), )? } else { @@ -439,7 +458,13 @@ impl ArrayConstructor { }; // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). - create_data_property_or_throw(agent, a.get(agent), pk, mapped_value, gc.reborrow())?; + create_data_property_or_throw( + agent, + a.get(agent), + pk, + mapped_value.unbind(), + gc.reborrow(), + )?; // f. Set k to k + 1. k += 1; @@ -460,89 +485,81 @@ impl ArrayConstructor { } /// ### [23.1.2.2 Array.isArray ( arg )](https://tc39.es/ecma262/#sec-array.isarray) - fn is_array( + fn is_array<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { - is_array(agent, arguments.get(0), gc.nogc()).map(Value::Boolean) + gc: GcScope<'gc, '_>, + ) -> JsResult> { + is_array(agent, arguments.get(0), gc.into_nogc()).map(Value::Boolean) } /// ### [23.1.2.3 Array.of ( ...items )](https://tc39.es/ecma262/#sec-array.of) - fn of( + fn of<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - // 1. Let len be the number of elements in items. - let len = arguments.len(); - - // 2. Let lenNumber be 𝔽(len). - let len_number = Value::try_from(len as i64).unwrap(); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); // 3. Let C be the this value. // 4. If IsConstructor(C) is true, then - let a = if let Some(c) = is_constructor(agent, this_value) { + if let Some(c) = is_constructor(agent, this_value) { // a. Let A be ? Construct(C, « lenNumber »). - construct( - agent, - c, - Some(ArgumentsList(&[len_number])), - None, - gc.reborrow(), - )? - .unbind() - .bind(gc.nogc()) - } else { - // 5. Else, - // a. Let A be ? ArrayCreate(len). - array_create(agent, len, len, None, gc.nogc())?.into_object() - }; + if c != agent.current_realm().intrinsics().array().into_function() { + let arguments = arguments + .iter() + .map(|v| v.scope(agent, gc.nogc())) + .collect(); + return array_of_generic(agent, c.unbind(), arguments, gc); + } + // We're constructring an array with the default constructor. + } - // 6. Let k be 0. - let mut k = 0; + // 1. Let len be the number of elements in items. + // 2. Let lenNumber be 𝔽(len). + let len = arguments.len(); - let a = a.scope(agent, gc.nogc()); + // 5. Else, + // a. Let A be ? ArrayCreate(len). + let gc = gc.into_nogc(); + let a = array_create(agent, len, len, None, gc)?; + // 6. Let k be 0. // 7. Repeat, while k < len, - while k < len { + for (k, &k_value) in arguments.iter().enumerate() { // a. Let kValue be items[k]. - let k_value = arguments.get(k); // NOTE: `array_create` guarantees that `len` and by extension `k` is less than `u32::MAX` // b. Let Pk be ! ToString(𝔽(k)). let pk = PropertyKey::from(SmallInteger::from(k as u32)); // c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue). - create_data_property_or_throw(agent, a.get(agent), pk, k_value, gc.reborrow())?; + assert!(unwrap_try(a.try_define_own_property( + agent, + pk, + PropertyDescriptor::new_data_descriptor(k_value), + gc + ))); // d. Set k to k + 1. - k += 1; } // 8. Perform ? Set(A, "length", lenNumber, true). - set( - agent, - a.get(agent), - PropertyKey::from(BUILTIN_STRING_MEMORY.length), - len_number, - true, - gc.reborrow(), - )?; + // Note: Array's own length setting cannot be observed. // 9. Return A. - Ok(a.get(agent).into_value()) + Ok(a.into_value()) } - fn get_species( + fn get_species<'gc>( _: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(this_value) + gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.bind(gc.into_nogc())) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -561,3 +578,49 @@ impl ArrayConstructor { .build(); } } + +fn array_of_generic<'gc>( + agent: &mut Agent, + c: Function, + args: Vec>>, + mut gc: GcScope<'gc, '_>, +) -> JsResult> { + let c = c.bind(gc.nogc()); + // a. Let A be ? Construct(C, « lenNumber »). + let len_number = Number::try_from(args.len()).unwrap(); + let a = construct( + agent, + c.unbind(), + Some(ArgumentsList(&[len_number.into_value()])), + None, + gc.reborrow(), + )? + .unbind(); + + let a = a.unbind().scope(agent, gc.nogc()); + + // 6. Let k be 0. + // 7. Repeat, while k < len, + for (k, k_value) in args.into_iter().enumerate() { + // a. Let kValue be items[k]. + + let pk = PropertyKey::try_from(k).unwrap(); + + // c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue). + create_data_property_or_throw(agent, a.get(agent), pk, k_value.get(agent), gc.reborrow())?; + + // d. Set k to k + 1. + } + + // 8. Perform ? Set(A, "length", lenNumber, true). + set( + agent, + a.get(agent), + PropertyKey::from(BUILTIN_STRING_MEMORY.length), + len_number.into_value(), + true, + gc.reborrow(), + )?; + + Ok(a.get(agent).bind(gc.into_nogc()).into_value()) +} diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator.rs index 8f6f66c0e..cb82b9807 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator.rs @@ -11,7 +11,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::ArrayIteratorIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, @@ -74,8 +78,8 @@ impl ArrayIterator<'_> { } } -impl IntoValue for ArrayIterator<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for ArrayIterator<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -92,16 +96,16 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: ArrayIterator) -> Self { - Self::ArrayIterator(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: ArrayIterator<'a>) -> Self { + Self::ArrayIterator(value) } } -impl TryFrom for ArrayIterator<'_> { +impl<'a> TryFrom> for ArrayIterator<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::ArrayIterator(data) => Ok(data), _ => Err(()), diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator_prototype.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator_prototype.rs index 22643e51a..f3e9d3f17 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_iterator_objects/array_iterator_prototype.rs @@ -23,14 +23,13 @@ use crate::{ builtins::{ array::ARRAY_INDEX_RANGE, indexed_collections::array_objects::array_iterator_objects::array_iterator::CollectionIteratorKind, - ArgumentsList, Builtin, + ArgumentsList, Behaviour, Builtin, }, execution::{agent::ExceptionType, Agent, JsResult, RealmIdentifier}, - types::{IntoValue, Object, PropertyKey, String, Value, BUILTIN_STRING_MEMORY}, + types::{IntoValue, Object, String, Value, BUILTIN_STRING_MEMORY}, }, - engine::context::GcScope, + engine::context::{Bindable, GcScope}, heap::WellKnownSymbolIndexes, - SmallInteger, }; pub(crate) struct ArrayIteratorPrototype; @@ -41,33 +40,35 @@ impl Builtin for ArrayIteratorPrototypeNext { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ArrayIteratorPrototype::next); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayIteratorPrototype::next); } impl ArrayIteratorPrototype { - fn next( + fn next<'gc>( agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); // 27.5.3.2 GeneratorValidate ( generator, generatorBrand ) // 3. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception. let Value::ArrayIterator(iterator) = this_value else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "ArrayIterator expected", - gc.nogc(), + nogc, )); }; - let mut iterator = iterator.bind(gc.nogc()); + let mut iterator = iterator.bind(nogc); // 23.1.5.1 CreateArrayIterator ( array, kind ), step 1. b // NOTE: We set `array` to None when the generator in the spec text has returned. let Some(array) = agent[iterator].array else { return Ok( - create_iter_result_object(agent, Value::Undefined, true, gc.nogc()).into_value(), + create_iter_result_object(agent, Value::Undefined, true, gc.into_nogc()) + .into_value(), ); }; @@ -80,93 +81,85 @@ impl ArrayIteratorPrototype { agent, array, crate::ecmascript::builtins::array_buffer::Ordering::SeqCst, - gc.nogc(), + nogc, ); // 2. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception. if match array { TypedArray::Int8Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Uint8Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Uint8ClampedArray(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Int16Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Uint16Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Int32Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Uint32Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::BigInt64Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::BigUint64Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } #[cfg(feature = "proposal-float16array")] TypedArray::Float16Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Float32Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } TypedArray::Float64Array(_) => { - is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()) + is_typed_array_out_of_bounds::(agent, &ta_record, nogc) } } { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "TypedArray out of bounds", - gc.nogc(), + nogc, )); } // 3. Let len be TypedArrayLength(taRecord). (match array { - TypedArray::Int8Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } - TypedArray::Uint8Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + TypedArray::Int8Array(_) => typed_array_length::(agent, &ta_record, nogc), + TypedArray::Uint8Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Uint8ClampedArray(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } - TypedArray::Int16Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } + TypedArray::Int16Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Uint16Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } - TypedArray::Int32Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } + TypedArray::Int32Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Uint32Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } TypedArray::BigInt64Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } TypedArray::BigUint64Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } #[cfg(feature = "proposal-float16array")] TypedArray::Float16Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } TypedArray::Float32Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } TypedArray::Float64Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } }) as i64 }}; @@ -192,7 +185,7 @@ impl ArrayIteratorPrototype { // 1. Let len be ? LengthOfArrayLike(array). Object::Array(array) => array.len(agent).into(), _ => { - let scoped_iterator = iterator.scope(agent, gc.nogc()); + let scoped_iterator = iterator.scope(agent, nogc); let res = length_of_array_like(agent, array, gc.reborrow())?; iterator = scoped_iterator.get(agent).bind(gc.nogc()); res @@ -203,12 +196,13 @@ impl ArrayIteratorPrototype { if agent[iterator].next_index >= len { agent[iterator].array = None; return Ok( - create_iter_result_object(agent, Value::Undefined, true, gc.nogc()).into_value(), + create_iter_result_object(agent, Value::Undefined, true, gc.into_nogc()) + .into_value(), ); } // iv. Let indexNumber be 𝔽(index). - let index = SmallInteger::try_from(agent[iterator].next_index).unwrap(); + let index = agent[iterator].next_index; // viii. Set index to index + 1. agent[iterator].next_index += 1; @@ -216,7 +210,7 @@ impl ArrayIteratorPrototype { // v. If kind is key, then CollectionIteratorKind::Key => { // 1. Let result be indexNumber. - Value::Integer(index) + Value::Integer(index.try_into().unwrap()) } // 3. If kind is value, then CollectionIteratorKind::Value => { @@ -225,15 +219,15 @@ impl ArrayIteratorPrototype { // a. Let result be elementValue. let fast_path_result = match array { Object::Array(array) => { - assert!(ARRAY_INDEX_RANGE.contains(&index.into_i64())); - let idx = usize::try_from(index.into_i64()).unwrap(); + assert!(ARRAY_INDEX_RANGE.contains(&index)); + let idx = usize::try_from(index).unwrap(); array.as_slice(agent)[idx] } _ => None, }; match fast_path_result { Some(result) => result, - None => get(agent, array, PropertyKey::from(index), gc.reborrow())?, + None => get(agent, array, index.try_into().unwrap(), gc.reborrow())?, } } // 4. Else, @@ -242,25 +236,29 @@ impl ArrayIteratorPrototype { // 2. Let elementValue be ? Get(array, elementKey). let fast_path_result = match array { Object::Array(array) if agent[array].object_index.is_none() => { - assert!(ARRAY_INDEX_RANGE.contains(&index.into_i64())); - let idx = usize::try_from(index.into_i64()).unwrap(); + assert!(ARRAY_INDEX_RANGE.contains(&index)); + let idx = usize::try_from(index).unwrap(); array.as_slice(agent)[idx] } _ => None, }; let value = match fast_path_result { Some(result) => result, - None => get(agent, array, PropertyKey::from(index), gc.reborrow())?, + None => get(agent, array, index.try_into().unwrap(), gc.reborrow())?, }; // a. Assert: kind is key+value. // b. Let result be CreateArrayFromList(« indexNumber, elementValue »). - create_array_from_list(agent, &[Value::Integer(index), value], gc.nogc()) - .into_value() + create_array_from_list( + agent, + &[index.try_into().unwrap(), value.unbind()], + gc.nogc(), + ) + .into_value() } }; // vii. Perform ? GeneratorYield(CreateIteratorResultObject(result, false)). - Ok(create_iter_result_object(agent, result, false, gc.nogc()).into_value()) + Ok(create_iter_result_object(agent, result.unbind(), false, gc.into_nogc()).into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs index 60009e5a6..256daeca2 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs @@ -4,15 +4,13 @@ use core::cmp::Ordering; -use small_string::SmallString; - use crate::ecmascript::abstract_operations::operations_on_objects::{ try_create_data_property_or_throw, try_length_of_array_like, }; use crate::ecmascript::abstract_operations::type_conversion::{ try_to_integer_or_infinity, try_to_string, }; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::engine::{unwrap_try, Scoped, TryResult}; use crate::{ ecmascript::{ @@ -288,13 +286,15 @@ impl Builtin for ArrayPrototypeWith { impl ArrayPrototype { /// ### [23.1.3.1 Array.prototype.at ( index )](https://tc39.es/ecma262/#sec-array.prototype.at) - fn at( + fn at<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let index = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let mut index = arguments.get(0).bind(nogc); // 1. Let O be ? ToObject(this value). let mut o = to_object(agent, this_value, gc.nogc())?; @@ -304,20 +304,23 @@ impl ArrayPrototype { len? } else { scoped_o = Some(o.scope(agent, gc.nogc())); + let scoped_index = index.scope(agent, gc.nogc()); let result = length_of_array_like(agent, o.unbind(), gc.reborrow())?; o = scoped_o.as_ref().unwrap().get(agent).bind(gc.nogc()); + index = scoped_index.get(agent).bind(gc.nogc()); result }; // 3. Let relativeIndex be ? ToIntegerOrInfinity(index). - let relative_index = - if let TryResult::Continue(len) = try_to_integer_or_infinity(agent, index, gc.nogc()) { - len?.into_i64() - } else { - scoped_o = Some(scoped_o.unwrap_or_else(|| o.scope(agent, gc.nogc()))); - let result = to_integer_or_infinity(agent, index, gc.reborrow())?.into_i64(); - o = scoped_o.unwrap().get(agent).bind(gc.nogc()); - result - }; + let relative_index = if let TryResult::Continue(len) = + try_to_integer_or_infinity(agent, index, gc.nogc()) + { + len?.into_i64() + } else { + scoped_o = Some(scoped_o.unwrap_or_else(|| o.scope(agent, gc.nogc()))); + let result = to_integer_or_infinity(agent, index.unbind(), gc.reborrow())?.into_i64(); + o = scoped_o.unwrap().get(agent).bind(gc.nogc()); + result + }; // 4. If relativeIndex ≥ 0, then let k = if relative_index >= 0 { // a. Let k be relativeIndex. @@ -336,7 +339,7 @@ impl ArrayPrototype { agent, o.unbind(), PropertyKey::Integer(k.try_into().unwrap()), - gc.reborrow(), + gc, ) } } @@ -354,29 +357,58 @@ impl ArrayPrototype { /// > Note 2: This method is intentionally generic; it does not require /// > that its this value be an Array. Therefore it can be transferred to /// > other kinds of objects for use as a method. - fn concat( + fn concat<'gc>( agent: &mut Agent, this_value: Value, items: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let mut items = items + .0 + .iter() + .map(|i| i.scope(agent, gc.nogc())) + .collect::>(); + // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?; let scoped_o = o.scope(agent, gc.nogc()); // 2. Let A be ? ArraySpeciesCreate(O, 0). let a = array_species_create(agent, o.unbind(), 0, gc.reborrow())? .unbind() - .bind(gc.nogc()) - .scope(agent, gc.nogc()); + .bind(gc.nogc()); + + // Optimisation: Reserve space for all Arrays being concatenated. + if let Object::Array(a) = a { + let mut total_len = 0u32; + if let Object::Array(this_value) = scoped_o.get(agent) { + total_len = total_len.saturating_add(this_value.len(agent)); + } + items.iter().for_each(|item| { + if let Value::Array(item) = item.get(agent) { + total_len = item.len(agent); + } + }); + let Heap { + arrays, elements, .. + } = &mut agent.heap; + arrays[a].elements.reserve(elements, total_len); + } + + let a = a.scope(agent, gc.nogc()); // 3. Let n be 0. let mut n = 0; // 4. Prepend O to items. - let mut items = items - .0 - .iter() - .map(|i| i.scope(agent, gc.nogc())) - .collect::>(); - items.insert(0, scoped_o.get(agent).into_value().scope(agent, gc.nogc())); + // SAFETY: We're replacing the stored Object value with itself as a + // Value; their heap root data is the same in either case so on the + // heap this is a no-op. + let o_as_value = unsafe { + scoped_o + .clone() + .replace_self(agent, scoped_o.get(agent).into_value()) + }; + items.insert(0, o_as_value); // 5. For each element E of items, do for e in items { // a. Let spreadable be ? IsConcatSpreadable(E). @@ -487,16 +519,18 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn copy_within( + fn copy_within<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let start = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let target = arguments.get(0).bind(nogc); + let start = arguments.get(1).bind(nogc); let end = if arguments.len() >= 3 { - Some(arguments.get(2)) + Some(arguments.get(2).bind(nogc)) } else { None }; @@ -542,16 +576,25 @@ impl ArrayPrototype { let data = array.as_mut_slice(agent); data.copy_within((from as usize)..((from + count) as usize), to as usize); - return Ok(array.into_value()); + return Ok(array.into_value().unbind()); } } // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + + let end = end.map(|e| e.scope(agent, nogc)); + // Note: Start is second to last to be scoped. + let start = start.scope(agent, nogc); + // Note: Target is last to be scoped. + let target = target.scope(agent, nogc); + // 2. Let len be ? LengthOfArrayLike(O). let len: i64 = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. Let relativeTarget be ? ToIntegerOrInfinity(target). - let relative_target = to_integer_or_infinity(agent, target, gc.reborrow())?; + // SAFETY: target has not been shared. + let relative_target = + to_integer_or_infinity(agent, unsafe { target.take(agent) }, gc.reborrow())?; let to = if relative_target.is_neg_infinity() { // 4. If relativeTarget = -∞, let to be 0. @@ -565,7 +608,9 @@ impl ArrayPrototype { }; // 7. Let relativeStart be ? ToIntegerOrInfinity(start). - let relative_start = to_integer_or_infinity(agent, start, gc.reborrow())?; + // SAFETY: start has not been shared. + let relative_start = + to_integer_or_infinity(agent, unsafe { start.take(agent) }, gc.reborrow())?; let from = if relative_start.is_neg_infinity() { // 8. If relativeStart = -∞, let from be 0. @@ -579,10 +624,12 @@ impl ArrayPrototype { }; // 11. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + // SAFETY: end has not been shared. + let end = end.map(|e| unsafe { e.take(agent) }.bind(gc.nogc())); let final_end = if end.is_none() || end.unwrap().is_undefined() { len } else { - let relative_end = to_integer_or_infinity(agent, end.unwrap(), gc.reborrow())?; + let relative_end = to_integer_or_infinity(agent, end.unwrap().unbind(), gc.reborrow())?; // 12. If relativeEnd = -∞, let final be 0. if relative_end.is_neg_infinity() { 0 @@ -623,7 +670,14 @@ impl ArrayPrototype { // i. Let fromValue be ? Get(O, fromKey). let from_value = get(agent, o.get(agent), from_key, gc.reborrow())?; // ii. Perform ? Set(O, toKey, fromValue, true). - set(agent, o.get(agent), to_key, from_value, true, gc.reborrow())?; + set( + agent, + o.get(agent), + to_key, + from_value.unbind(), + true, + gc.reborrow(), + )?; } else { // e. Else, // i. Assert: fromPresent is false. @@ -641,12 +695,12 @@ impl ArrayPrototype { Ok(o.get(agent).into_value()) } - fn entries( + fn entries<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? ToObject(this value). let Ok(o) = Object::try_from(this_value) else { return Err(agent.throw_exception_with_static_message( @@ -695,27 +749,31 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its this /// > value be an Array. Therefore it can be transferred to other kinds of /// > objects for use as a method. - fn every( + fn every<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).scope(agent, nogc); + let this_arg = arguments.get(1).scope(agent, nogc); + // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(stack_callback_fn) = is_callable(callback_fn.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback is not a function", gc.nogc(), )); }; - let callback_fn = callback_fn.scope(agent, gc.nogc()); + // SAFETY: callback_fn never escapes this call. + let callback_fn = unsafe { callback_fn.replace_self(agent, stack_callback_fn.unbind()) }; // 4. Let k be 0. let mut k = 0; // 5. Repeat, while k < len, @@ -733,8 +791,8 @@ impl ArrayPrototype { let test_result = call_function( agent, callback_fn.get(agent), - this_arg, - Some(ArgumentsList(&[k_value, f_k])), + this_arg.get(agent), + Some(ArgumentsList(&[k_value.unbind(), f_k])), gc.reborrow(), )?; let test_result = to_boolean(agent, test_result); @@ -770,15 +828,17 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn fill( + fn fill<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let value = arguments.get(0); - let start = arguments.get(1); - let end = arguments.get(2); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let value = arguments.get(0).bind(nogc); + let start = arguments.get(1).bind(nogc); + let end = arguments.get(2).bind(nogc); if let ( Value::Array(array), Value::Undefined | Value::Integer(_), @@ -815,16 +875,19 @@ impl ArrayPrototype { }; let data = array.as_mut_slice(agent); - data[k..final_end].fill(Some(value)); - return Ok(value.into_value()); + data[k..final_end].fill(Some(value.unbind())); + return Ok(value.into_value().unbind()); } }; + let value = value.scope(agent, nogc); + let start = start.scope(agent, nogc); + let end = end.scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. Let relativeStart be ? ToIntegerOrInfinity(start). - let relative_start = to_integer_or_infinity(agent, start, gc.reborrow())?; + let relative_start = to_integer_or_infinity(agent, start.get(agent), gc.reborrow())?; // 4. If relativeStart = -∞, let k be 0. let mut k = if relative_start.is_neg_infinity() { @@ -838,10 +901,10 @@ impl ArrayPrototype { }; // 7. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). - let final_end = if end.is_undefined() { + let final_end = if end.get(agent).is_undefined() { len } else { - let relative_end = to_integer_or_infinity(agent, end, gc.reborrow())?; + let relative_end = to_integer_or_infinity(agent, end.get(agent), gc.reborrow())?; // 8. If relativeEnd = -∞, let final be 0. if relative_end.is_neg_infinity() { 0 @@ -859,7 +922,14 @@ impl ArrayPrototype { // a. Let Pk be ! ToString(𝔽(k)). let pk = PropertyKey::Integer(k.try_into().unwrap()); // b. Perform ? Set(O, Pk, value, true). - set(agent, o.get(agent), pk, value, true, gc.reborrow())?; + set( + agent, + o.get(agent).unbind(), + pk, + value.get(agent).unbind(), + true, + gc.reborrow(), + )?; // c. Set k to k + 1. k += 1; } @@ -901,28 +971,31 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > **this** value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn filter( + fn filter<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).bind(nogc).scope(agent, nogc); + let this_arg = arguments.get(1).bind(nogc).scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(stack_callback_fn) = is_callable(callback_fn.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function is not callable", gc.nogc(), )); }; - let callback_fn = callback_fn.scope(agent, gc.nogc()); + // SAFETY: callback_fn never escapes this call. + let callback_fn = unsafe { callback_fn.replace_self(agent, stack_callback_fn.unbind()) }; // 4. Let A be ? ArraySpeciesCreate(O, 0). let a = array_species_create(agent, o.get(agent), 0, gc.reborrow())? .unbind() @@ -931,6 +1004,7 @@ impl ArrayPrototype { let mut k = 0; // 6. Let to be 0. let mut to: u32 = 0; + let mut scoped_k_value: Scoped<'_, Value<'static>> = Value::Undefined.scope_static(); // 7. Repeat, while k < len, while k < len { // a. Let Pk be ! ToString(𝔽(k)). @@ -941,13 +1015,15 @@ impl ArrayPrototype { if k_present { // i. Let kValue be ? Get(O, Pk). let k_value = get(agent, o.get(agent), pk, gc.reborrow())?; + // SAFETY: scoped_k_value never escapes this call + unsafe { scoped_k_value.replace(agent, k_value.unbind()) }; // ii. Let selected be ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). let result = call_function( agent, callback_fn.get(agent), - this_arg, + this_arg.get(agent), Some(ArgumentsList(&[ - k_value, + k_value.unbind(), k.try_into().unwrap(), o.get(agent).into_value(), ])), @@ -961,7 +1037,7 @@ impl ArrayPrototype { agent, a.get(agent), to.into(), - k_value, + scoped_k_value.get(agent), gc.reborrow(), )?; // 2. Set to to to + 1. @@ -992,14 +1068,16 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn find( + fn find<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let predicate = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let predicate = arguments.get(0).bind(nogc).scope(agent, nogc); + let this_arg = arguments.get(1).bind(nogc).scope(agent, nogc); // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); @@ -1008,7 +1086,7 @@ impl ArrayPrototype { // 3. Let findRec be ? FindViaPredicate(O, len, ascending, predicate, thisArg). let find_rec = find_via_predicate(agent, o, len, true, predicate, this_arg, gc.reborrow())?; // 4. Return findRec.[[Value]]. - Ok(find_rec.1) + Ok(find_rec.1.unbind()) } /// ### [23.1.3.10 Array.prototype.findIndex ( predicate \[ , thisArg \] )](https://tc39.es/ecma262/#sec-array.prototype.findindex) @@ -1028,14 +1106,16 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn find_index( + fn find_index<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let predicate = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let predicate = arguments.get(0).bind(nogc).scope(agent, nogc); + let this_arg = arguments.get(1).bind(nogc).scope(agent, nogc); // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); @@ -1048,14 +1128,16 @@ impl ArrayPrototype { } /// ### [23.1.3.11 Array.prototype.findLast ( predicate \[ , thisArg \] )](https://tc39.es/ecma262/#sec-array.prototype.findlast) - fn find_last( + fn find_last<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let predicate = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let predicate = arguments.get(0).bind(nogc).scope(agent, nogc); + let this_arg = arguments.get(1).bind(nogc).scope(agent, nogc); // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); @@ -1065,21 +1147,23 @@ impl ArrayPrototype { let find_rec = find_via_predicate(agent, o, len, false, predicate, this_arg, gc.reborrow())?; // 4. Return findRec.[[Value]]. - Ok(find_rec.1) + Ok(find_rec.1.unbind()) } /// ### [23.1.3.12 Array.prototype.findLastIndex ( predicate \[ , thisArg \] )](https://tc39.es/ecma262/#sec-array.prototype.findlastindex) - fn find_last_index( + fn find_last_index<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let predicate = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let predicate = arguments.get(0).bind(nogc).scope(agent, nogc); + let this_arg = arguments.get(1).bind(nogc).scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. Let findRec be ? FindViaPredicate(O, len, descending, predicate, thisArg). @@ -1090,23 +1174,25 @@ impl ArrayPrototype { } /// ### [23.1.3.13 Array.prototype.flat ( \[ depth \] )]() - fn flat( + fn flat<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let depth = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let depth = arguments.get(0).scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let sourceLen be ? LengthOfArrayLike(O). let source_len = length_of_array_like(agent, o.get(agent), gc.reborrow())? as usize; // 3. Let depthNum be 1. let mut depth_num = 1; // 4. If depth is not undefined, then - if !depth.is_undefined() { + if !depth.get(agent).is_undefined() { // a. Set depthNum to ? ToIntegerOrInfinity(depth). - depth_num = to_integer_or_infinity(agent, depth, gc.reborrow())?.into_i64(); + depth_num = to_integer_or_infinity(agent, depth.get(agent), gc.reborrow())?.into_i64(); } // b. If depthNum < 0, set depthNum to 0. if depth_num < 0 { @@ -1133,28 +1219,33 @@ impl ArrayPrototype { } /// ### [23.1.3.14 Array.prototype.flatMap ( mapperFunction \[ , thisArg \] )](https://tc39.es/ecma262/#sec-array.prototype.flatmap) - fn flat_map( + fn flat_map<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let mapper_function = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let mapper_function = arguments.get(0).scope(agent, nogc); + let this_arg = arguments.get(1).scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let sourceLen be ? LengthOfArrayLike(O). let source_len = length_of_array_like(agent, o.get(agent), gc.reborrow())? as usize; // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception. - let Some(mapper_function) = is_callable(mapper_function, gc.nogc()) else { + let Some(stack_mapper_function) = is_callable(mapper_function.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Mapper function is not callable", gc.nogc(), )); }; - let mapper_function = mapper_function.scope(agent, gc.nogc()); + // SAFETY: callback_fn is not shared. + let mapper_function = + unsafe { mapper_function.replace_self(agent, stack_mapper_function.unbind()) }; + // 4. Let A be ? ArraySpeciesCreate(O, 0). let a = array_species_create(agent, o.get(agent), 0, gc.reborrow())? .unbind() @@ -1209,30 +1300,33 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that /// > its this value be an Array. Therefore it can be transferred to /// > other kinds of objects for use as a method. - fn for_each( + fn for_each<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).scope(agent, nogc); + let this_arg = arguments.get(1).scope(agent, nogc); + // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; - let callback_fn = arguments.get(0); - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(stack_callback_fn) = is_callable(callback_fn.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function is not a function", gc.nogc(), )); }; - let callback_fn = callback_fn.scope(agent, gc.nogc()); + // SAFETY: callback_fn is not shared. + let callback_fn = unsafe { callback_fn.replace_self(agent, stack_callback_fn.unbind()) }; - let this_arg = arguments.get(0); // 4. Let k be 0. let mut k = 0; // 5. Repeat, while k < len, @@ -1249,9 +1343,9 @@ impl ArrayPrototype { call_function( agent, callback_fn.get(agent), - this_arg, + this_arg.get(agent), Some(ArgumentsList(&[ - k_value, + k_value.unbind(), k.try_into().unwrap(), o.get(agent).into_value(), ])), @@ -1294,14 +1388,16 @@ impl ArrayPrototype { /// > of IsStrictlyEqual, allowing it to detect NaN array elements. /// > Second, it does not skip missing array elements, instead treating /// > them as undefined. - fn includes( + fn includes<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_element = arguments.get(0); - let from_index = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_element = arguments.get(0).bind(nogc); + let from_index = arguments.get(1).bind(nogc); if let (Value::Array(array), Value::Undefined | Value::Integer(_)) = (this_value, from_index) { @@ -1343,6 +1439,9 @@ impl ArrayPrototype { return Ok(false.into()); } }; + let from_index_is_undefined = from_index.is_undefined(); + let from_index = from_index.scope(agent, nogc); + let search_element = search_element.scope(agent, nogc); // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); // 2. Let len be ? LengthOfArrayLike(O). @@ -1352,9 +1451,9 @@ impl ArrayPrototype { return Ok(false.into()); } // 4. Let n be ? ToIntegerOrInfinity(fromIndex). - let n = to_integer_or_infinity(agent, from_index, gc.reborrow())?; + let n = to_integer_or_infinity(agent, from_index.get(agent), gc.reborrow())?; // 5. Assert: If fromIndex is undefined, then n is 0. - if from_index.is_undefined() { + if from_index_is_undefined { assert_eq!(n.into_i64(), 0); } // 6. If n = +∞, return false. @@ -1388,7 +1487,7 @@ impl ArrayPrototype { let pk = PropertyKey::Integer(k.try_into().unwrap()); let element_k = get(agent, o.get(agent), pk, gc.reborrow())?; // b. If SameValueZero(searchElement, elementK) is true, return true. - if same_value_zero(agent, search_element, element_k) { + if same_value_zero(agent, search_element.get(agent), element_k) { return Ok(true.into()); } // c. Set k to k + 1. @@ -1419,14 +1518,16 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that /// > its this value be an Array. Therefore it can be transferred to /// > other kinds of objects for use as a method. - fn index_of( + fn index_of<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_element = arguments.get(0); - let from_index = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_element = arguments.get(0).bind(nogc); + let from_index = arguments.get(1).bind(nogc); if let (Value::Array(array), Value::Undefined | Value::Integer(_)) = (this_value, from_index) { @@ -1468,6 +1569,9 @@ impl ArrayPrototype { return Ok((-1).into()); } }; + let from_index_is_undefined = from_index.is_undefined(); + let from_index = from_index.scope(agent, nogc); + let search_element = search_element.scope(agent, nogc); // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); // 2. Let len be ? LengthOfArrayLike(O). @@ -1477,9 +1581,9 @@ impl ArrayPrototype { return Ok((-1).into()); } // 4. Let n be ? ToIntegerOrInfinity(fromIndex). - let n = to_integer_or_infinity(agent, from_index, gc.reborrow())?; + let n = to_integer_or_infinity(agent, from_index.get(agent), gc.reborrow())?; // 5. Assert: If fromIndex is undefined, then n is 0. - if from_index.is_undefined() { + if from_index_is_undefined { assert_eq!(n.into_i64(), 0); } // 6. If n = +∞, return -1𝔽. @@ -1518,7 +1622,7 @@ impl ArrayPrototype { // i. Let elementK be ? Get(O, Pk). let element_k = get(agent, o.get(agent), pk, gc.reborrow())?; // ii. If IsStrictlyEqual(searchElement, elementK) is true, return 𝔽(k). - if is_strictly_equal(agent, search_element, element_k) { + if is_strictly_equal(agent, search_element.get(agent), element_k) { return Ok(k.try_into().unwrap()); } } @@ -1534,16 +1638,18 @@ impl ArrayPrototype { /// This method converts the elements of the array to Strings, and then /// concatenates these Strings, separated by occurrences of the separator. /// If no separator is provided, a single comma is used as the separator. - fn join( + fn join<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let separator = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let separator = arguments.get(0).scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; if len == 0 { @@ -1551,16 +1657,15 @@ impl ArrayPrototype { } let len = len as usize; // 3. If separator is undefined, let sep be ",". - let separator = if separator.is_undefined() { - SmallString::from_str_unchecked(",").into() + let separator = if separator.get(agent).is_undefined() { + String::from_small_string(",").scope_static() } else { // 4. Else, let sep be ? ToString(separator). - to_string(agent, separator, gc.reborrow())? - .unbind() - .bind(gc.nogc()) + let sep = to_string(agent, separator.get(agent), gc.reborrow())?.unbind(); + // SAFETY: separator is not shared. + // Note: Separator is likely a small string so this is a very cheap. + unsafe { separator.replace_self(agent, sep) } }; - // Note: Separator is likely a small string so this is a very cheap. - let separator = separator.scope(agent, gc.nogc()); // 5. Let R be the empty String. let mut r = std::string::String::with_capacity(len * 10); // 6. Let k be 0. @@ -1571,7 +1676,7 @@ impl ArrayPrototype { // c. If element is neither undefined nor null, then if !element.is_undefined() && !element.is_null() { // i. Let S be ? ToString(element). - let s = to_string(agent, element, gc.reborrow())?; + let s = to_string(agent, element.unbind(), gc.reborrow())?; // ii. Set R to the string-concatenation of R and S. r.push_str(s.as_str(agent)); } @@ -1589,22 +1694,22 @@ impl ArrayPrototype { // c. If element is neither undefined nor null, then if !element.is_undefined() && !element.is_null() { // i. Let S be ? ToString(element). - let s = to_string(agent, element, gc.reborrow())?; + let s = to_string(agent, element.unbind(), gc.reborrow())?; // ii. Set R to the string-concatenation of R and S. r.push_str(s.as_str(agent)); } // d. Set k to k + 1. } // 8. Return R. - Ok(Value::from_string(agent, r, gc.nogc()).into_value()) + Ok(Value::from_string(agent, r, gc.into_nogc()).into_value()) } - fn keys( + fn keys<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? ToObject(this value). let Ok(o) = Object::try_from(this_value) else { return Err(agent.throw_exception_with_static_message( @@ -1638,15 +1743,17 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn last_index_of( + fn last_index_of<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_element = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_element = arguments.get(0).bind(nogc); let from_index = if arguments.len() > 1 { - Some(arguments.get(1)) + Some(arguments.get(1).bind(nogc)) } else { None }; @@ -1694,8 +1801,10 @@ impl ArrayPrototype { return Ok((-1).into()); } }; + let from_index = from_index.map(|i| i.scope(agent, nogc)); + let search_element = search_element.scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. If len = 0, return -1𝔽. @@ -1704,7 +1813,7 @@ impl ArrayPrototype { } // 4. If fromIndex is present, let n be ? ToIntegerOrInfinity(fromIndex); else let n be len - 1. let mut k = if let Some(from_index) = from_index { - let n = to_integer_or_infinity(agent, from_index, gc.reborrow())?; + let n = to_integer_or_infinity(agent, from_index.get(agent), gc.reborrow())?; // 5. If n = -∞, return -1𝔽. if n.is_neg_infinity() { return Ok((-1).into()); @@ -1734,7 +1843,7 @@ impl ArrayPrototype { // i. Let elementK be ? Get(O, Pk). let element_k = get(agent, o.get(agent), pk, gc.reborrow())?; // ii. If IsStrictlyEqual(searchElement, elementK) is true, return 𝔽(k). - if is_strictly_equal(agent, search_element, element_k) { + if is_strictly_equal(agent, search_element.get(agent), element_k) { return Ok(k.try_into().unwrap()); } } @@ -1778,28 +1887,31 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn map( + fn map<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).scope(agent, nogc); + let this_arg = arguments.get(1).scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(stack_callback_fn) = is_callable(callback_fn.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function is not a function", gc.nogc(), )); }; - let callback_fn = callback_fn.scope(agent, gc.nogc()); + // SAFETY: callback_fn is not shared. + let callback_fn = unsafe { callback_fn.replace_self(agent, stack_callback_fn.unbind()) }; // 4. Let A be ? ArraySpeciesCreate(O, len). let a = array_species_create(agent, o.get(agent), len as usize, gc.reborrow())? .unbind() @@ -1820,9 +1932,9 @@ impl ArrayPrototype { let mapped_value = call_function( agent, callback_fn.get(agent), - this_arg, + this_arg.get(agent), Some(ArgumentsList(&[ - k_value, + k_value.unbind(), k.try_into().unwrap(), o.get(agent).into_value(), ])), @@ -1833,7 +1945,7 @@ impl ArrayPrototype { agent, a.get(agent), pk, - mapped_value, + mapped_value.unbind(), gc.reborrow(), )?; } @@ -1855,12 +1967,13 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that /// > its this value be an Array. Therefore it can be transferred to /// > other kinds of objects for use as a method. - fn pop( + fn pop<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); if let Value::Array(array) = this_value { // Fast path: Trivial (no descriptors) array means mutating // elements is direct. @@ -1923,7 +2036,9 @@ impl ArrayPrototype { // c. Let index be ! ToString(newLen). let index = PropertyKey::Integer(new_len.try_into().unwrap()); // d. Let element be ? Get(O, index). - let element = get(agent, o.get(agent), index, gc.reborrow())?; + let element = get(agent, o.get(agent), index, gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); // e. Perform ? DeletePropertyOrThrow(O, index). delete_property_or_throw(agent, o.get(agent), index, gc.reborrow())?; // f. Perform ? Set(O, "length", newLen, true). @@ -1936,7 +2051,7 @@ impl ArrayPrototype { gc.reborrow(), )?; // g. Return element. - Ok(element) + Ok(element.get(agent)) } } @@ -1953,12 +2068,12 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that /// > its this value be an Array. Therefore it can be transferred to /// > other kinds of objects for use as a method. - fn push( + fn push<'gc>( agent: &mut Agent, this_value: Value, items: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); // 2. Let len be ? LengthOfArrayLike(O). @@ -2047,33 +2162,36 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that /// > its this value be an Array. Therefore it can be transferred to /// > other kinds of objects for use as a method. - fn reduce( + fn reduce<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).scope(agent, nogc); let initial_value = if arguments.len() >= 2 { - Some(arguments.get(1)) + Some(arguments.get(1).scope(agent, nogc)) } else { None }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(stack_callback_fn) = is_callable(callback_fn.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function is not a function", gc.nogc(), )); }; - let callback_fn = callback_fn.scope(agent, gc.nogc()); + // SAFETY: callback_fn is not shared. + let callback_fn = unsafe { callback_fn.replace_self(agent, stack_callback_fn.unbind()) }; // 4. If len = 0 and initialValue is not present, throw a TypeError exception. if len == 0 && initial_value.is_none() { @@ -2089,10 +2207,11 @@ impl ArrayPrototype { // 6. Let accumulator be undefined. // 7. If initialValue is present, // a. Set accumulator to initialValue. - let mut accumulator = initial_value.unwrap_or(Value::Undefined); + let initial_value_is_none = initial_value.is_none(); + let mut accumulator = initial_value.unwrap_or(Value::Undefined.scope_static()); // 8. Else, - if initial_value.is_none() { + if initial_value_is_none { // a. Let kPresent be false. let mut k_present = false; @@ -2107,7 +2226,9 @@ impl ArrayPrototype { // iii. If kPresent is true, then if k_present { // 1. Set accumulator to ? Get(O, Pk). - accumulator = get(agent, o.get(agent), pk, gc.reborrow())?; + let result = get(agent, o.get(agent), pk, gc.reborrow())?; + // SAFETY: accumulator is not shared. + unsafe { accumulator.replace(agent, result.unbind()) }; } // iv. Set k to k + 1. @@ -2139,18 +2260,20 @@ impl ArrayPrototype { let k_value = get(agent, o.get(agent), pk, gc.reborrow())?; // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »). - accumulator = call_function( + let result = call_function( agent, callback_fn.get(agent), Value::Undefined, Some(ArgumentsList(&[ - accumulator, - k_value, + accumulator.get(agent), + k_value.unbind(), Number::from(k_int).into_value(), o.get(agent).into_value(), ])), gc.reborrow(), )?; + // SAFETY: accumulator is not shared. + unsafe { accumulator.replace(agent, result.unbind()) }; } // d. Set k to k + 1. @@ -2158,7 +2281,7 @@ impl ArrayPrototype { } // 10. Return accumulator. - Ok(accumulator) + Ok(accumulator.get(agent)) } /// ### [23.1.3.25 Array.prototype.reduceRight ( callbackfn \[ , initialValue \] )](https://tc39.es/ecma262/#sec-array.prototype.reduceright) @@ -2197,34 +2320,37 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn reduce_right( + fn reduce_right<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).bind(nogc).scope(agent, nogc); let initial_value = if arguments.len() >= 2 { - Some(arguments.get(1)) + Some(arguments.get(1).bind(nogc).scope(agent, nogc)) } else { None }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(stack_callback_fn) = is_callable(callback_fn.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function is not a function", gc.nogc(), )); }; - let callback_fn = callback_fn.scope(agent, gc.nogc()); + // SAFETY: callback_fn is not shared outside this call. + let callback_fn = unsafe { callback_fn.replace_self(agent, stack_callback_fn.unbind()) }; // 4. If len = 0 and initialValue is not present, throw a TypeError exception. if len == 0 && initial_value.is_none() { @@ -2240,10 +2366,11 @@ impl ArrayPrototype { // 6. Let accumulator be undefined. // 7. If initialValue is present, then // a. Set accumulator to initialValue. - let mut accumulator = initial_value.unwrap_or(Value::Undefined); + let no_initial_value = initial_value.is_none(); + let mut accumulator = initial_value.unwrap_or(Value::Undefined.scope_static()); // 8. Else, - if initial_value.is_none() { + if no_initial_value { // a. Let kPresent be false. let mut k_present = false; @@ -2258,7 +2385,9 @@ impl ArrayPrototype { // iii. If kPresent is true, then if k_present { // 1. Set accumulator to ? Get(O, Pk). - accumulator = get(agent, o.get(agent), pk, gc.reborrow())?; + let result = get(agent, o.get(agent), pk, gc.reborrow())?; + // SAFETY: Accumulator is not shared outside this call. + unsafe { accumulator.replace(agent, result.unbind()) }; } // iv. Set k to k - 1. @@ -2289,18 +2418,20 @@ impl ArrayPrototype { let k_value = get(agent, o.get(agent), pk, gc.reborrow())?; // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »). - accumulator = call_function( + let result = call_function( agent, callback_fn.get(agent), Value::Undefined, Some(ArgumentsList(&[ - accumulator, - k_value, + accumulator.get(agent), + k_value.unbind(), Number::try_from(k).unwrap().into(), o.get(agent).into_value(), ])), gc.reborrow(), )?; + // SAFETY: Accumulator is not shared outside this call. + unsafe { accumulator.replace(agent, result.unbind()) }; } // d. Set k to k - 1. @@ -2308,21 +2439,22 @@ impl ArrayPrototype { } // 10. Return accumulator. - Ok(accumulator) + Ok(accumulator.get(agent)) } - fn reverse( + fn reverse<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); if let Value::Array(array) = this_value { // Fast path: Array is dense and contains no descriptors. No JS // functions can thus be called by shift. if array.is_trivial(agent) && array.is_dense(agent) { array.as_mut_slice(agent).reverse(); - return Ok(array.into_value()); + return Ok(array.into_value().unbind()); } } @@ -2336,79 +2468,95 @@ impl ArrayPrototype { let mut lower: i64 = 0; // 5. Repeat, while lower ≠ middle, while lower != middle { - // a. Let upper be len - lower - 1. + // a. Let upper be len - lower - 1. let upper = len - lower - 1; - // b. Let upperP be ! ToString(𝔽(upper)). + // b. Let upperP be ! ToString(𝔽(upper)). let upper_p = PropertyKey::Integer(upper.try_into().unwrap()); - // c. Let lowerP be ! ToString(𝔽(lower)). + // c. Let lowerP be ! ToString(𝔽(lower)). let lower_p = PropertyKey::Integer(lower.try_into().unwrap()); - // d. Let lowerExists be ? HasProperty(O, lowerP). - // e. If lowerExists is true, then - // i. Let lowerValue be ? Get(O, lowerP). + // d. Let lowerExists be ? HasProperty(O, lowerP). let lower_exists = has_property(agent, o.get(agent), lower_p, gc.reborrow())?; - // f. Let upperExists be ? HasProperty(O, upperP). - // g. If upperExists is true, then - // i. Let upperValue be ? Get(O, upperP). + // e. If lowerExists is true, then + let lower_value = if lower_exists { + // i. Let lowerValue be ? Get(O, lowerP). + Some( + get(agent, o.get(agent), lower_p, gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()), + ) + } else { + None + }; + // f. Let upperExists be ? HasProperty(O, upperP). let upper_exists = has_property(agent, o.get(agent), upper_p, gc.reborrow())?; + // g. If upperExists is true, then + let upper_value = if upper_exists { + // i. Let upperValue be ? Get(O, upperP). + Some( + get(agent, o.get(agent), upper_p, gc.reborrow())? + .unbind() + .bind(gc.nogc()), + ) + } else { + None + }; - // h. If lowerExists is true and upperExists is true, then - if lower_exists && upper_exists { - // i. Perform ? Set(O, lowerP, upperValue, true). - // ii. Perform ? Set(O, upperP, lowerValue, true). - let lower_value = get(agent, o.get(agent), lower_p, gc.reborrow())?; - let upper_value = get(agent, o.get(agent), upper_p, gc.reborrow())?; - set( - agent, - o.get(agent), - lower_p, - upper_value, - true, - gc.reborrow(), - )?; - set( - agent, - o.get(agent), - upper_p, - lower_value, - true, - gc.reborrow(), - )?; - } - // i. Else if lowerExists is false and upperExists is true, then - else if !lower_exists && upper_exists { - // i. Perform ? Set(O, lowerP, upperValue, true). - // ii. Perform ? DeletePropertyOrThrow(O, upperP). - let upper_value = get(agent, o.get(agent), upper_p, gc.reborrow())?; - set( - agent, - o.get(agent), - lower_p, - upper_value, - true, - gc.reborrow(), - )?; - delete_property_or_throw(agent, o.get(agent), upper_p, gc.reborrow())?; - } - // j. Else if lowerExists is true and upperExists is false, then - else if lower_exists && !upper_exists { - // i. Perform ? DeletePropertyOrThrow(O, lowerP). - // ii. Perform ? Set(O, upperP, lowerValue, true). - let lower_value = get(agent, o.get(agent), lower_p, gc.reborrow())?; - delete_property_or_throw(agent, o.get(agent), lower_p, gc.reborrow())?; - set( - agent, - o.get(agent), - upper_p, - lower_value, - true, - gc.reborrow(), - )?; - } - // k. Else, - else { - // i. Assert: lowerExists and upperExists are both false. - // ii. NOTE: No action is required. - assert!(!(lower_exists && upper_exists)); + match (lower_value, upper_value) { + // h. If lowerExists is true and upperExists is true, then + (Some(lower_value), Some(upper_value)) => { + // i. Perform ? Set(O, lowerP, upperValue, true). + set( + agent, + o.get(agent), + lower_p, + upper_value.unbind(), + true, + gc.reborrow(), + )?; + // ii. Perform ? Set(O, upperP, lowerValue, true). + set( + agent, + o.get(agent), + upper_p, + lower_value.get(agent), + true, + gc.reborrow(), + )?; + } + // i. Else if lowerExists is false and upperExists is true, then + (None, Some(upper_value)) => { + // i. Perform ? Set(O, lowerP, upperValue, true). + set( + agent, + o.get(agent), + lower_p, + upper_value.unbind(), + true, + gc.reborrow(), + )?; + // ii. Perform ? DeletePropertyOrThrow(O, upperP). + delete_property_or_throw(agent, o.get(agent), upper_p, gc.reborrow())?; + } + // j. Else if lowerExists is true and upperExists is false, then + (Some(lower_value), None) => { + // i. Perform ? DeletePropertyOrThrow(O, lowerP). + delete_property_or_throw(agent, o.get(agent), lower_p, gc.reborrow())?; + // ii. Perform ? Set(O, upperP, lowerValue, true). + set( + agent, + o.get(agent), + upper_p, + lower_value.get(agent), + true, + gc.reborrow(), + )?; + } + // k. Else, + (None, None) => { + // i. Assert: lowerExists and upperExists are both false. + // ii. NOTE: No action is required. + assert!(!(lower_exists && upper_exists)); + } } // l. Set lower to lower + 1. lower += 1; @@ -2426,12 +2574,13 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn shift( + fn shift<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); if let Value::Array(array) = this_value { if array.is_empty(agent) { if agent[array].elements.len_writable { @@ -2440,7 +2589,7 @@ impl ArrayPrototype { // This will throw set( agent, - array.into_object(), + array.into_object().unbind(), BUILTIN_STRING_MEMORY.length.into(), 0.into(), true, @@ -2453,18 +2602,18 @@ impl ArrayPrototype { // Fast path: Array is dense and contains no descriptors. No JS // functions can thus be called by shift. let slice = array.as_mut_slice(agent); - let first = slice[0].unwrap(); + let first = slice[0].unwrap().bind(gc.nogc()); slice.copy_within(1.., 0); *slice.last_mut().unwrap() = None; let array_data = &mut agent[array]; if array_data.elements.len_writable { array_data.elements.len -= 1; - return Ok(first); + return Ok(first.unbind()); } else { // This will throw set( agent, - array.into_object(), + array.into_object().unbind(), BUILTIN_STRING_MEMORY.length.into(), (array.len(agent) - 1).into(), true, @@ -2493,7 +2642,9 @@ impl ArrayPrototype { return Ok(Value::Undefined); } // 4. Let first be ? Get(O, "0"). - let first = get(agent, o.get(agent), 0.into(), gc.reborrow())?; + let first = get(agent, o.get(agent), 0.into(), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); // 5. Let k be 1. let mut k = 1; // 6. Repeat, while k < len, @@ -2509,7 +2660,14 @@ impl ArrayPrototype { // i. Let fromValue be ? Get(O, from). let from_value = get(agent, o.get(agent), from, gc.reborrow())?; // ii. Perform ? Set(O, to, fromValue, true). - set(agent, o.get(agent), to, from_value, true, gc.reborrow())?; + set( + agent, + o.get(agent), + to, + from_value.unbind(), + true, + gc.reborrow(), + )?; } else { // e. Else, // i. Assert: fromPresent is false. @@ -2536,7 +2694,7 @@ impl ArrayPrototype { gc.reborrow(), )?; // 9. Return first. - Ok(first) + Ok(first.get(agent)) } /// ### [23.1.3.28 Array.prototype.slice ( start, end )](https://tc39.es/ecma262/#sec-array.prototype.slice) @@ -2556,21 +2714,22 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn slice( + fn slice<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let start = arguments.get(0); - let end = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let start = arguments.get(0).bind(nogc); + let end = arguments.get(1).bind(nogc); if let ( Value::Array(array), Value::Undefined | Value::Integer(_), Value::Undefined | Value::Integer(_), ) = (this_value, start, end) { - let array = array.bind(gc.nogc()); let len = array.len(agent) as usize; if array.is_trivial(agent) && array.is_dense(agent) { let start = if let Value::Integer(relative_start) = start { @@ -2593,7 +2752,7 @@ impl ArrayPrototype { } else { len }; - let array = array.scope(agent, gc.nogc()); + let array = array.scope(agent, nogc); let count = end.saturating_sub(start); let a = array_species_create( agent, @@ -2676,12 +2835,14 @@ impl ArrayPrototype { return Ok(a.get(agent).into_value()); } } + let start = start.scope(agent, nogc); + let end = end.scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())? as usize; // 3. Let relativeStart be ? ToIntegerOrInfinity(start). - let relative_start = to_integer_or_infinity(agent, start, gc.reborrow())?; + let relative_start = to_integer_or_infinity(agent, start.get(agent), gc.reborrow())?; // 4. If relativeStart = -∞, let k be 0. let mut k = if relative_start.is_neg_infinity() { 0 @@ -2694,10 +2855,11 @@ impl ArrayPrototype { }; // 7. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + let end = end.get(agent).bind(gc.nogc()); let final_end = if end.is_undefined() { len } else { - let relative_end = to_integer_or_infinity(agent, end, gc.reborrow())?; + let relative_end = to_integer_or_infinity(agent, end.unbind(), gc.reborrow())?; // 8. If relativeEnd = -∞, let final be 0. if relative_end.is_neg_infinity() { 0 @@ -2732,7 +2894,7 @@ impl ArrayPrototype { agent, a.get(agent), n.into(), - k_value, + k_value.unbind(), gc.reborrow(), )?; } @@ -2792,28 +2954,31 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn some( + fn some<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).bind(nogc).scope(agent, nogc); + let this_arg = arguments.get(1).bind(nogc).scope(agent, nogc); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(stack_callback_fn) = is_callable(callback_fn.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function is not callable", gc.nogc(), )); }; - let callback_fn = callback_fn.scope(agent, gc.nogc()); + // SAFETY: callback_fn is never shared before this call. + let callback_fn = unsafe { callback_fn.replace_self(agent, stack_callback_fn.unbind()) }; // 4. Let k be 0. let mut k = 0; // 5. Repeat, while k < len, @@ -2830,9 +2995,9 @@ impl ArrayPrototype { let test_result = call_function( agent, callback_fn.get(agent), - this_arg, + this_arg.get(agent), Some(ArgumentsList(&[ - k_value, + k_value.unbind(), k.try_into().unwrap(), o.get(agent).into_value(), ])), @@ -2874,12 +3039,12 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore, it can be transferred to other /// > kinds of objects for use as a method. - fn sort( + fn sort<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let comparator = args.get(0); // 1. If comparator is not undefined and IsCallable(comparator) is false, throw a TypeError exception. let comparator = if comparator.is_undefined() { @@ -2904,7 +3069,7 @@ impl ArrayPrototype { // a. Return ? CompareArrayElements(x, y, comparator). // 5. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, // skip-holes). - let sorted_list: Vec = sort_indexed_properties::( + let sorted_list: Vec> = sort_indexed_properties::( agent, obj.get(agent), len, @@ -2922,7 +3087,7 @@ impl ArrayPrototype { agent, obj.get(agent), j.try_into().unwrap(), - sorted_list[j], + sorted_list[j].get(agent), true, gc.reborrow(), )?; @@ -2944,25 +3109,29 @@ impl ArrayPrototype { Ok(obj.get(agent).into_value()) } - fn splice( + fn splice<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let start = arguments.get(0); - let delete_count = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let start = arguments.get(0).bind(nogc).scope(agent, nogc); + let delete_count = arguments.get(1).bind(nogc).scope(agent, nogc); let items = if arguments.len() > 2 { - &arguments[2..] + arguments[2..] + .iter() + .map(|v| v.scope(agent, nogc)) + .collect() } else { - &[] + vec![] }; // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. Let relativeStart be ? ToIntegerOrInfinity(start). - let relative_start = to_integer_or_infinity(agent, start, gc.reborrow())?; + let relative_start = to_integer_or_infinity(agent, start.get(agent), gc.reborrow())?; let actual_start = if relative_start.is_neg_infinity() { // 4. If relativeStart = -∞, let actualStart be 0. 0 @@ -2986,7 +3155,7 @@ impl ArrayPrototype { } else { // 10. Else, // a. Let dc be ? ToIntegerOrInfinity(deleteCount). - let dc = to_integer_or_infinity(agent, delete_count, gc.reborrow())?; + let dc = to_integer_or_infinity(agent, delete_count.get(agent), gc.reborrow())?; // b. Let actualDeleteCount be the result of clamping dc between 0 and len - actualStart. (dc.into_i64().max(0) as usize).min(len as usize - actual_start) }; @@ -3017,7 +3186,7 @@ impl ArrayPrototype { agent, a.get(agent), k.try_into().unwrap(), - from_value, + from_value.unbind(), gc.reborrow(), )?; } @@ -3049,7 +3218,14 @@ impl ArrayPrototype { // 1. Let fromValue be ? Get(O, from). let from_value = get(agent, o.get(agent), from, gc.reborrow())?; // 2. Perform ? Set(O, to, fromValue, true). - set(agent, o.get(agent), to, from_value, true, gc.reborrow())?; + set( + agent, + o.get(agent), + to, + from_value.unbind(), + true, + gc.reborrow(), + )?; } else { // iv. Else, // 1. Perform ? DeletePropertyOrThrow(O, to). @@ -3088,7 +3264,14 @@ impl ArrayPrototype { // 1. Let fromValue be ? Get(O, from). let from_value = get(agent, o.get(agent), from, gc.reborrow())?; // 2. Perform ? Set(O, to, fromValue, true). - set(agent, o.get(agent), to, from_value, true, gc.reborrow())?; + set( + agent, + o.get(agent), + to, + from_value.unbind(), + true, + gc.reborrow(), + )?; } else { // iv. Else, // 1. Perform ? DeletePropertyOrThrow(O, to). @@ -3109,7 +3292,7 @@ impl ArrayPrototype { agent, o.get(agent), k.try_into().unwrap(), - *e, + e.get(agent), true, gc.reborrow(), )?; @@ -3131,26 +3314,27 @@ impl ArrayPrototype { Ok(a.get(agent).into_value()) } - fn to_locale_string( + fn to_locale_string<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn to_reversed( + fn to_reversed<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); if let Value::Array(array) = this_value { - let array = array.bind(gc.nogc()); // Fast path: Array is dense and contains no descriptors. No JS // functions can thus be called by to_reversed. if array.is_trivial(agent) && array.is_dense(agent) { + let array = array.unbind().bind(gc.into_nogc()); let cloned_array = array.to_cloned(agent); cloned_array.as_mut_slice(agent).reverse(); return Ok(cloned_array.into_value()); @@ -3180,7 +3364,7 @@ impl ArrayPrototype { agent, a.get(agent), pk, - from_value, + from_value.unbind(), gc.nogc(), )) .unwrap(); @@ -3192,12 +3376,12 @@ impl ArrayPrototype { } /// ### [23.1.3.34 Array.prototype.toSorted ( comparator )](https://tc39.es/ecma262/#sec-array.prototype.tosorted) - fn to_sorted( + fn to_sorted<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let comparator = args.get(0); // 1. If comparator is not undefined and IsCallable(comparator) is false, throw a TypeError exception. let comparator = if comparator.is_undefined() { @@ -3223,46 +3407,47 @@ impl ArrayPrototype { // called: // a. Return ? CompareArrayElements(x, y, comparator). // 6. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes). - let sorted_list: Vec = sort_indexed_properties::( + let sorted_list: Vec> = sort_indexed_properties::( agent, o.get(agent), len, comparator, gc.reborrow(), )?; + let gc = gc.into_nogc(); // 7. Let j be 0. // 8. Repeat, while j < len, // a. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(j)), sortedList[j]). // b. Set j to j + 1. // Fast path: Copy sorted items directly into array. - let a = a.get(agent); + let a = a.get(agent).bind(gc); + let sorted_list = sorted_list + .into_iter() + .map(|v| Some(v.get(agent))) + .collect::>>(); let slice = a.as_mut_slice(agent); - slice.copy_from_slice( - &sorted_list - .into_iter() - .map(Some) - .collect::>>()[..], - ); + slice.copy_from_slice(&sorted_list); // 9. Return A. Ok(a.into()) } - fn to_spliced( + fn to_spliced<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } /// ### [23.1.3.36 Array.prototype.toString ( )](https://tc39.es/ecma262/#sec-array.prototype.tostring) - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); // 1. Let array be ? ToObject(this value). let array = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); // 2. Let func be ? Get(array, "join"). @@ -3271,7 +3456,9 @@ impl ArrayPrototype { array.get(agent), BUILTIN_STRING_MEMORY.join.into(), gc.reborrow(), - )?; + )? + .unbind() + .bind(gc.nogc()); // 3. If IsCallable(func) is false, set func to the intrinsic function %Object.prototype.toString%. let func = is_callable(func, gc.nogc()).unwrap_or_else(|| { agent @@ -3301,12 +3488,13 @@ impl ArrayPrototype { /// > This method is intentionally generic; it does not require that its /// > this value be an Array. Therefore it can be transferred to other /// > kinds of objects for use as a method. - fn unshift( + fn unshift<'gc>( agent: &mut Agent, this_value: Value, items: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.unbind(); // Fast path: Array is dense and contains no descriptors. No JS // functions can thus be called by unshift. if let Value::Array(array) = this_value { @@ -3336,6 +3524,10 @@ impl ArrayPrototype { return Ok(final_len.unwrap().into()); } } + let items = items + .iter() + .map(|v| v.scope(agent, gc.nogc())) + .collect::>(); // 1. Let O be ? ToObject(this value). let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); // 2. Let len be ? LengthOfArrayLike(O). @@ -3367,7 +3559,14 @@ impl ArrayPrototype { // 1. Let fromValue be ? Get(O, from). let from_value = get(agent, o.get(agent), from, gc.reborrow())?; // 2. Perform ? Set(O, to, fromValue, true). - set(agent, o.get(agent), to, from_value, true, gc.reborrow())?; + set( + agent, + o.get(agent), + to, + from_value.unbind(), + true, + gc.reborrow(), + )?; } else { // v. Else, // 1. Assert: fromPresent is false. @@ -3386,7 +3585,7 @@ impl ArrayPrototype { agent, o.get(agent), j.try_into().unwrap(), - *e, + e.get(agent), true, gc.reborrow(), )?; @@ -3406,12 +3605,12 @@ impl ArrayPrototype { Ok(len) } - fn values( + fn values<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? ToObject(this value). let Ok(o) = Object::try_from(this_value) else { return Err(agent.throw_exception_with_static_message( @@ -3425,14 +3624,16 @@ impl ArrayPrototype { } /// ### [23.1.3.39 Array.prototype.with ( index, value )](https://tc39.es/ecma262/#sec-array.prototype.with) - fn with( + fn with<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let index = arguments.get(0); - let value = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let index = arguments.get(0).bind(nogc); + let value = arguments.get(1).bind(nogc); // Fast path: Array is dense and contains no descriptors. No JS // functions can thus be called by with. if let (Value::Array(array), Value::Integer(index)) = (this_value, index) { @@ -3448,21 +3649,24 @@ impl ArrayPrototype { return Err(agent.throw_exception_with_static_message( ExceptionType::RangeError, "invalid or out-of-range index", - gc.nogc(), + nogc, )); } // Fast path: Set new value in cloned array. let cloned_array = array.to_cloned(agent); - cloned_array.as_mut_slice(agent)[actual_index as usize] = Some(value); - return Ok(cloned_array.into()); + cloned_array.as_mut_slice(agent)[actual_index as usize] = Some(value.unbind()); + return Ok(cloned_array.into_value().unbind().bind(gc.into_nogc())); } } // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value, gc.nogc())?.scope(agent, gc.nogc()); + let o = to_object(agent, this_value, nogc)?.scope(agent, nogc); + let index = index.scope(agent, nogc); + let value = value.scope(agent, nogc); // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, o.get(agent), gc.reborrow())?; // 3. Let relativeIndex be ? ToIntegerOrInfinity(index). - let relative_index = to_integer_or_infinity(agent, index, gc.reborrow())?.into_i64(); + let relative_index = + to_integer_or_infinity(agent, index.get(agent), gc.reborrow())?.into_i64(); // 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex. let actual_index = if relative_index >= 0 { relative_index @@ -3489,17 +3693,19 @@ impl ArrayPrototype { let pk = PropertyKey::try_from(k).unwrap(); // b. If k = actualIndex, let fromValue be value. let from_value = if k == actual_index { - value + value.get(agent).bind(gc.nogc()) // c. Else, let fromValue be ? Get(O, Pk). } else { get(agent, o.get(agent), pk, gc.reborrow())? + .unbind() + .bind(gc.nogc()) }; // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue). unwrap_try(try_create_data_property_or_throw( agent, a.get(agent), pk, - from_value, + from_value.unbind(), gc.nogc(), )) .unwrap(); @@ -3507,7 +3713,7 @@ impl ArrayPrototype { k += 1; } // 10. Return A. - Ok(a.get(agent).into()) + Ok(a.get(agent).bind(gc.into_nogc()).into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -3626,14 +3832,16 @@ impl ArrayPrototype { /// language value) and returns either a normal completion containing a Boolean /// or a throw completion. fn is_concat_spreadable(agent: &mut Agent, o: Value, mut gc: GcScope) -> JsResult { + let o = o.bind(gc.nogc()); // 1. If O is not an Object, return false. let Ok(o) = Object::try_from(o) else { return Ok(false); }; + let scoped_o = o.scope(agent, gc.nogc()); // 2. Let spreadable be ? Get(O, @@isConcatSpreadable). let spreadable = get( agent, - o, + o.unbind(), WellKnownSymbolIndexes::IsConcatSpreadable.into(), gc.reborrow(), )?; @@ -3647,7 +3855,7 @@ fn is_concat_spreadable(agent: &mut Agent, o: Value, mut gc: GcScope) -> JsResul } } else { // 4. Return ? IsArray(O). - let o_is_array = is_array(agent, o.into_value(), gc.nogc())?; + let o_is_array = is_array(agent, scoped_o.get(agent).into_value(), gc.nogc())?; if o_is_array { Ok(true) } else { @@ -3690,43 +3898,49 @@ fn is_concat_spreadable(agent: &mut Agent, o: Value, mut gc: GcScope) -> JsResul /// the time that this operation visits them. Elements that are deleted after /// traversal begins and before being visited are still visited and are either /// looked up from the prototype or are undefined. -fn find_via_predicate( +fn find_via_predicate<'gc, 'scope>( agent: &mut Agent, o: Scoped<'_, Object<'static>>, len: i64, ascending: bool, - predicate: Value, - this_arg: Value, - mut gc: GcScope, -) -> JsResult<(i64, Value)> { + predicate: Scoped<'scope, Value<'static>>, + this_arg: Scoped<'scope, Value<'static>>, + mut gc: GcScope<'gc, 'scope>, +) -> JsResult<(i64, Value<'gc>)> { // 1. If IsCallable(predicate) is false, throw a TypeError exception. - let Some(predicate) = is_callable(predicate, gc.nogc()) else { + let Some(stack_predicate) = is_callable(predicate.get(agent), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Predicate is not a function", gc.nogc(), )); }; - let predicate = predicate.scope(agent, gc.nogc()); + // SAFETY: We're only ever called in a way that gives ownership of + // predicate to us. TODO: Make this fn unsafe. + let predicate = unsafe { predicate.replace_self(agent, stack_predicate.unbind()) }; // 4. For each integer k of indices, do - let check = |agent: &mut Agent, - o: Scoped<'_, Object<'static>>, - predicate: Scoped<'_, Function<'static>>, - this_arg: Value, - k: i64, - mut gc: GcScope| - -> JsResult> { + fn check<'gc>( + agent: &mut Agent, + o: Scoped<'_, Object<'static>>, + predicate: Scoped<'_, Function<'static>>, + this_arg: Scoped<'_, Value<'static>>, + k: i64, + mut gc: GcScope<'gc, '_>, + ) -> JsResult)>> { // a. Let Pk be ! ToString(𝔽(k)). let pk = PropertyKey::Integer(k.try_into().unwrap()); - // b. NOTE: If O is a TypedArray, the following invocation of Get will return a normal completion. + // b. NOTE: If O is a TypedArray, the following invocation of Get will + // return a normal completion. // c. Let kValue be ? Get(O, Pk). - let k_value = get(agent, o.get(agent), pk, gc.reborrow())?; + let k_value = get(agent, o.get(agent), pk, gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); // d. Let testResult be ? Call(predicate, thisArg, « kValue, 𝔽(k), O »). let test_result = call_function( agent, predicate.get(agent), - this_arg, + this_arg.get(agent), Some(ArgumentsList(&[ Number::try_from(k).unwrap().into_value(), o.get(agent).into_value(), @@ -3735,40 +3949,40 @@ fn find_via_predicate( )?; // e. If ToBoolean(testResult) is true, return the Record { [[Index]]: 𝔽(k), [[Value]]: kValue }. if to_boolean(agent, test_result) { - Ok(Some((k, k_value))) + Ok(Some((k, k_value.get(agent)))) } else { Ok(None) } - }; + } // 2. If direction is ascending, then if ascending { // a. Let indices be a List of the integers in the interval from 0 (inclusive) to len (exclusive), in ascending order. for k in 0..len { - if let Some(result) = check( + if let Some((index, value)) = check( agent, o.clone(), predicate.clone(), - this_arg, + this_arg.clone(), k, gc.reborrow(), )? { - return Ok(result); + return Ok((index, value.unbind().bind(gc.into_nogc()))); } } } else { // 3. Else, // a. Let indices be a List of the integers in the interval from 0 (inclusive) to len (exclusive), in descending order. for k in (0..len).rev() { - if let Some(result) = check( + if let Some((index, value)) = check( agent, o.clone(), predicate.clone(), - this_arg, + this_arg.clone(), k, gc.reborrow(), )? { - return Ok(result); + return Ok((index, value.unbind().bind(gc.into_nogc()))); } } }; @@ -3792,7 +4006,7 @@ fn flatten_into_array( start: usize, depth: Option, mapper_function: Option>>, - this_arg: Option, + this_arg: Option>>, mut gc: GcScope, ) -> JsResult { // 1. Assert: If mapperFunction is present, then IsCallable(mapperFunction) is true, thisArg is present, and depth is 1. @@ -3815,21 +4029,25 @@ fn flatten_into_array( continue; } // i. Let element be ? Get(source, P). - let element = get(agent, source.get(agent), p, gc.reborrow())?; + let element = get(agent, source.get(agent), p, gc.reborrow())? + .unbind() + .bind(gc.nogc()); // ii. If mapperFunction is present, then let element = if let Some(mapper_function) = &mapper_function { // 1. Set element to ? Call(mapperFunction, thisArg, « element, sourceIndex, source »). call_function( agent, mapper_function.get(agent), - this_arg.unwrap(), + this_arg.as_ref().unwrap().get(agent), Some(ArgumentsList(&[ - element, + element.unbind(), source_index_number.into_value(), source.get(agent).into_value(), ])), gc.reborrow(), )? + .unbind() + .bind(gc.nogc()) } else { element }; @@ -3878,7 +4096,7 @@ fn flatten_into_array( agent, target.get(agent), target_index.try_into().unwrap(), - element, + element.unbind(), gc.reborrow(), )?; // 3. Set targetIndex to targetIndex + 1. @@ -3943,13 +4161,14 @@ fn flatten_into_array( /// > The above conditions are necessary and sufficient to ensure that /// > comparator divides the set S into equivalence classes and that these /// > equivalence classes are totally ordered. -fn sort_indexed_properties( +fn sort_indexed_properties<'scope, const SKIP_HOLES: bool, const TYPED_ARRAY: bool>( agent: &mut Agent, obj: Object, len: usize, - comparator: Option>>, - mut gc: GcScope, -) -> JsResult> { + comparator: Option>>, + mut gc: GcScope<'_, 'scope>, +) -> JsResult>>> { + let obj = obj.scope(agent, gc.nogc()); // 1. Let items be a new empty List. let mut items = Vec::with_capacity(len); // 2. Let k be 0. @@ -3961,7 +4180,7 @@ fn sort_indexed_properties( // b. If holes is skip-holes, then let k_read = if SKIP_HOLES { // i. Let kRead be ? HasProperty(obj, Pk). - has_property(agent, obj, pk, gc.reborrow())? + has_property(agent, obj.get(agent), pk, gc.reborrow())? } else { // c. Else, // i. Assert: holes is read-through-holes. @@ -3971,9 +4190,9 @@ fn sort_indexed_properties( // d. If kRead is true, then if k_read { // i. Let kValue be ? Get(obj, Pk). - let k_value = get(agent, obj, pk, gc.reborrow())?; + let k_value = get(agent, obj.get(agent), pk, gc.reborrow())?; // ii. Append kValue to items. - items.push(k_value); + items.push(k_value.unbind().scope(agent, gc.nogc())); } // e. Set k to k + 1. k += 1; @@ -3991,7 +4210,7 @@ fn sort_indexed_properties( // This is dangerous but we don't have much of a choice. return Ordering::Equal; } - let result = compare_array_elements(agent, *a, *b, comparator.clone(), gc.reborrow()); + let result = compare_array_elements(agent, a, b, comparator.clone(), gc.reborrow()); let Ok(result) = result else { error = Some(result.unwrap_err()); return Ordering::Equal; @@ -4013,11 +4232,13 @@ fn sort_indexed_properties( /// completion containing a Number or an abrupt completion. fn compare_array_elements( agent: &mut Agent, - x: Value, - y: Value, + scoped_x: &Scoped<'_, Value<'static>>, + scoped_y: &Scoped<'_, Value<'static>>, comparator: Option>>, mut gc: GcScope, ) -> JsResult { + let x = scoped_x.get(agent).bind(gc.nogc()); + let y = scoped_y.get(agent).bind(gc.nogc()); // 1. If x and y are both undefined, return +0𝔽. if x.is_undefined() && y.is_undefined() { Ok(Ordering::Equal) @@ -4035,10 +4256,10 @@ fn compare_array_elements( agent, comparator.get(agent), Value::Undefined, - Some(ArgumentsList(&[x, y])), + Some(ArgumentsList(&[x.unbind(), y.unbind()])), gc.reborrow(), )?; - let v = to_number(agent, v, gc.reborrow())?; + let v = to_number(agent, v.unbind(), gc.reborrow())?; // b. If v is NaN, return +0𝔽. // c. Return v. if v.is_nan(agent) { @@ -4062,16 +4283,19 @@ fn compare_array_elements( let (x, y) = if let TryResult::Continue(x) = try_to_string(agent, x, gc.nogc()) { (x?, y) } else { - let y = y.scope(agent, gc.nogc()); - let x = to_string(agent, x, gc.reborrow())?.unbind().bind(gc.nogc()); - (x, y.get(agent)) + let x = to_string(agent, x.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + (x, scoped_y.get(agent).bind(gc.nogc())) }; // 6. Let yString be ? ToString(y). let (x, y) = if let TryResult::Continue(y) = try_to_string(agent, y, gc.nogc()) { (x, y?) } else { let x = x.scope(agent, gc.nogc()); - let y = to_string(agent, y, gc.reborrow())?.unbind().bind(gc.nogc()); + let y = to_string(agent, y.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); (x.get(agent).bind(gc.nogc()), y) }; // 7. Let xSmaller be ! IsLessThan(xString, yString, true). diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/abstract_operations.rs index 4c61ab1c0..a648c079a 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/abstract_operations.rs @@ -29,8 +29,8 @@ use crate::{ }, }, engine::{ - context::{GcScope, NoGcScope}, - unwrap_try, TryResult, + context::{Bindable, GcScope, NoGcScope}, + unwrap_try, Scoped, TryResult, }, heap::indexes::TypedArrayIndex, SmallInteger, @@ -500,7 +500,7 @@ pub(crate) fn typed_array_set_element( v.into_numeric() } else { let scoped_o = o.scope(agent, gc.nogc()); - let v = to_big_int(agent, value, gc.reborrow())? + let v = to_big_int(agent, value.unbind(), gc.reborrow())? .into_numeric() .unbind() .bind(gc.nogc()); @@ -513,7 +513,7 @@ pub(crate) fn typed_array_set_element( v.into_numeric() } else { let scoped_o = o.scope(agent, gc.nogc()); - let v = to_number(agent, value, gc.reborrow())? + let v = to_number(agent, value.unbind(), gc.reborrow())? .into_numeric() .unbind() .bind(gc.nogc()); @@ -1011,25 +1011,24 @@ pub(crate) fn initialize_typed_array_from_array_buffer( /// either a normal completion containing unused or a throw completion. pub(crate) fn initialize_typed_array_from_list( agent: &mut Agent, - o: TypedArray, - values: Vec, + scoped_o: Scoped<'_, TypedArray<'static>>, + values: Vec>>, mut gc: GcScope, ) -> JsResult<()> { - let mut o = o.bind(gc.nogc()); + let mut o = scoped_o.get(agent).bind(gc.nogc()); // 1. Let len be the number of elements in values. // 2. Perform ? AllocateTypedArrayBuffer(O, len). allocate_typed_array_buffer::(agent, o, values.len(), gc.nogc())?; - let scoped_o = o.scope(agent, gc.nogc()); - // 3. Let k be 0. // 4. Repeat, while k < len, // b. Let kValue be the first element of values. // c. Remove the first element from values. // e. Set k to k + 1. - for (k, &k_value) in values.iter().enumerate() { + for (k, k_value) in values.iter().enumerate() { // a. Let Pk be ! ToString(𝔽(k)). let pk = PropertyKey::from(SmallInteger::try_from(k as i64).unwrap()); + let k_value = k_value.get(agent).bind(gc.nogc()); // d. Perform ? Set(O, Pk, kValue, true). if k_value.is_numeric() { unwrap_try(try_set( @@ -1045,7 +1044,7 @@ pub(crate) fn initialize_typed_array_from_list( agent, o.unbind().into_object(), pk, - k_value, + k_value.unbind(), true, gc.reborrow(), )?; @@ -1066,11 +1065,10 @@ pub(crate) fn initialize_typed_array_from_list( /// throw completion. pub(crate) fn initialize_typed_array_from_array_like( agent: &mut Agent, - o: TypedArray, + o: Scoped<'_, TypedArray<'static>>, array_like: Object, mut gc: GcScope, ) -> JsResult<()> { - let o = o.bind(gc.nogc()).scope(agent, gc.nogc()); // 1. Let len be ? LengthOfArrayLike(arrayLike). let len = length_of_array_like(agent, array_like, gc.reborrow())? as usize; @@ -1090,7 +1088,7 @@ pub(crate) fn initialize_typed_array_from_array_like( agent, o.get(agent).into_object(), pk, - k_value, + k_value.unbind(), true, gc.reborrow(), )?; diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_constructors.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_constructors.rs index 498ed4558..0f0037f3c 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_constructors.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_constructors.rs @@ -6,7 +6,7 @@ use crate::ecmascript::abstract_operations::operations_on_iterator_objects::{ get_iterator_from_method, iterator_to_list, }; use crate::ecmascript::abstract_operations::operations_on_objects::get_method; -use crate::ecmascript::abstract_operations::type_conversion::to_index; +use crate::ecmascript::abstract_operations::type_conversion::{to_index, try_to_index}; use crate::ecmascript::builtins::indexed_collections::typed_array_objects::abstract_operations::{ allocate_typed_array, initialize_typed_array_from_array_buffer, initialize_typed_array_from_array_like, initialize_typed_array_from_list, @@ -16,7 +16,8 @@ use crate::ecmascript::builtins::typed_array::TypedArray; use crate::ecmascript::builtins::ArrayBuffer; use crate::ecmascript::execution::agent::ExceptionType; use crate::ecmascript::types::{Function, IntoValue, PropertyKey, U8Clamped, Viewable}; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; +use crate::engine::TryResult; use crate::heap::WellKnownSymbolIndexes; use crate::{ ecmascript::{ @@ -182,124 +183,124 @@ impl BuiltinIntrinsicConstructor for Float64ArrayConstructor { } impl TypedArrayConstructors { - fn int8_array_constructor( + fn int8_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn uint8_array_constructor( + fn uint8_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn uint8_clamped_array_constructor( + fn uint8_clamped_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn int16_array_constructor( + fn int16_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn uint16_array_constructor( + fn uint16_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn int32_array_constructor( + fn int32_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn uint32_array_constructor( + fn uint32_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn big_int64_array_constructor( + fn big_int64_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn big_uint64_array_constructor( + fn big_uint64_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } #[cfg(feature = "proposal-float16array")] - fn float16_array_constructor( + fn float16_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn float32_array_constructor( + fn float32_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } - fn float64_array_constructor( + fn float64_array_constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { typed_array_constructor::(agent, arguments, new_target, gc) } @@ -702,12 +703,28 @@ impl TypedArrayPrototypes { /// ### [23.2.5.1 TypedArray ( ...args )](https://tc39.es/ecma262/#sec-typedarray) #[inline(always)] -fn typed_array_constructor( +fn typed_array_constructor<'gc, T: Viewable>( agent: &mut Agent, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { + let new_target = new_target.bind(gc.nogc()); + // 4. Let numberOfArgs be the number of elements in args. + let number_of_args = arguments.len(); + // a. Let firstArgument be args[0]. + let first_argument = arguments.get(0).bind(gc.nogc()); + let first_argument_is_object = first_argument.is_object(); + let second_argument = if first_argument_is_object && number_of_args > 1 { + Some(arguments.get(1).scope(agent, gc.nogc())) + } else { + None + }; + let third_argument = if first_argument_is_object && number_of_args > 2 { + Some(arguments.get(2).scope(agent, gc.nogc())) + } else { + None + }; // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( @@ -716,31 +733,30 @@ fn typed_array_constructor( gc.nogc(), )); }; - let new_target = Function::try_from(new_target).unwrap(); + let mut new_target = Function::try_from(new_target).unwrap(); // 2. Let constructorName be the String value of the Constructor Name value specified in Table 69 for this TypedArray constructor. // 3. Let proto be "%TypedArray.prototype%". let proto = T::PROTO; - // 4. Let numberOfArgs be the number of elements in args. // 5. If numberOfArgs = 0, then if arguments.is_empty() { // a. Return ? AllocateTypedArray(constructorName, NewTarget, proto, 0). - return allocate_typed_array::(agent, new_target, proto, Some(0), gc.reborrow()) - .map(|typed_array| typed_array.into_value()); + return allocate_typed_array::(agent, new_target.unbind(), proto, Some(0), gc) + .map(TypedArray::into_value); } // 6. Else, - // a. Let firstArgument be args[0]. - let first_argument = arguments.get(0); // b. If firstArgument is an Object, then - if first_argument.is_object() { + if first_argument_is_object { + let scoped_first_argument = first_argument.scope(agent, gc.nogc()); // i. Let O be ? AllocateTypedArray(constructorName, NewTarget, proto). - let o = allocate_typed_array::(agent, new_target, proto, None, gc.reborrow())? + let o = allocate_typed_array::(agent, new_target.unbind(), proto, None, gc.reborrow())? .unbind() .bind(gc.nogc()); let scoped_o = o.scope(agent, gc.nogc()); + let first_argument = scoped_first_argument.get(agent).bind(gc.nogc()); // ii. If firstArgument has a [[TypedArrayName]] internal slot, then if let Ok(first_argument) = TypedArray::try_from(first_argument) { @@ -823,20 +839,15 @@ fn typed_array_constructor( } } else if let Ok(first_argument) = ArrayBuffer::try_from(first_argument) { let first_argument = first_argument.bind(gc.nogc()); - // iii. Else if firstArgument has an [[ArrayBufferData]] internal slot, then - // 1. If numberOfArgs > 1, let byteOffset be args[1]; else let byteOffset be undefined. - let byte_offset = if arguments.len() > 1 { - Some(arguments.get(1)) - } else { - None - }; + // iii. Else if firstArgument has an [[ArrayBufferData]] internal + // slot, then + // 1. If numberOfArgs > 1, let byteOffset be args[1]; else let + // byteOffset be undefined. + let byte_offset = second_argument.map(|v| v.get(agent).bind(gc.nogc())); - // 2. If numberOfArgs > 2, let length be args[2]; else let length be undefined. - let length = if arguments.len() > 2 { - Some(arguments.get(2)) - } else { - None - }; + // 2. If numberOfArgs > 2, let length be args[2]; else let length + // be undefined. + let length = third_argument.map(|v| v.get(agent).bind(gc.nogc())); // 3. Perform ? InitializeTypedArrayFromArrayBuffer(O, firstArgument, byteOffset, length). @@ -844,19 +855,20 @@ fn typed_array_constructor( agent, o.unbind(), first_argument.unbind(), - byte_offset, - length, - gc.reborrow(), + byte_offset.unbind(), + length.unbind(), + gc, )?; } else { - let scoped_first_argument = first_argument.scope(agent, gc.nogc()); // iv. Else, - // 1. Assert: firstArgument is an Object and firstArgument does not have either a [[TypedArrayName]] or an [[ArrayBufferData]] internal slot. + // 1. Assert: firstArgument is an Object and firstArgument does not + // have either a [[TypedArrayName]] or an [[ArrayBufferData]] + // internal slot. // 2. Let usingIterator be ? GetMethod(firstArgument, %Symbol.iterator%). let using_iterator = get_method( agent, - first_argument, + first_argument.unbind(), PropertyKey::Symbol(WellKnownSymbolIndexes::Iterator.into()), gc.reborrow(), )?; @@ -874,18 +886,19 @@ fn typed_array_constructor( // b. Perform ? InitializeTypedArrayFromList(O, values). initialize_typed_array_from_list::( agent, - scoped_o.get(agent), + scoped_o.clone(), values, gc.reborrow(), )?; } else { // 4. Else, - // a. NOTE: firstArgument is not an iterable object, so assume it is already an array-like object. + // a. NOTE: firstArgument is not an iterable object, so assume + // it is already an array-like object. let first_argument = Object::try_from(scoped_first_argument.get(agent)).unwrap(); // b. Perform ? InitializeTypedArrayFromArrayLike(O, firstArgument). initialize_typed_array_from_array_like::( agent, - scoped_o.get(agent), + scoped_o.clone(), first_argument, gc.reborrow(), )?; @@ -898,12 +911,27 @@ fn typed_array_constructor( // c. Else, // i. Assert: firstArgument is not an Object. - assert!(!first_argument.is_object()); + assert!(!first_argument_is_object); // ii. Let elementLength be ? ToIndex(firstArgument). - let element_length = to_index(agent, first_argument, gc.reborrow())?; + let element_length = if let TryResult::Continue(element_length) = + try_to_index(agent, first_argument, gc.nogc()) + { + element_length? + } else { + let scoped_new_target = new_target.scope(agent, gc.nogc()); + let element_length = to_index(agent, first_argument.unbind(), gc.reborrow())?; + new_target = scoped_new_target.get(agent).bind(gc.nogc()); + element_length + }; // iii. Return ? AllocateTypedArray(constructorName, NewTarget, proto, elementLength). - allocate_typed_array::(agent, new_target, proto, Some(element_length as usize), gc) - .map(|typed_array| typed_array.into_value()) + allocate_typed_array::( + agent, + new_target.unbind(), + proto, + Some(element_length as usize), + gc, + ) + .map(|typed_array| typed_array.into_value()) } diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs index dc4ad05d4..c7b99d16d 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs @@ -32,7 +32,7 @@ use crate::{ }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, unwrap_try, TryResult, }, heap::{IntrinsicConstructorIndexes, IntrinsicFunctionIndexes, WellKnownSymbolIndexes}, @@ -78,13 +78,13 @@ impl Builtin for TypedArrayGetSpecies { } impl BuiltinGetter for TypedArrayGetSpecies {} impl TypedArrayIntrinsicObject { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Err(agent.throw_exception_with_static_message( crate::ecmascript::execution::agent::ExceptionType::TypeError, "Abstract class TypedArray not directly constructable", @@ -92,40 +92,40 @@ impl TypedArrayIntrinsicObject { )) } - fn from( + fn from<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn is_array( + fn is_array<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { is_array(agent, arguments.get(0), gc.nogc()).map(Value::Boolean) } - fn of( + fn of<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn get_species( + fn get_species<'gc>( _: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(this_value) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -375,13 +375,14 @@ impl BuiltinGetter for TypedArrayPrototypeGetToStringTag {} impl TypedArrayPrototype { /// ### [23.2.3.1 %TypedArray%.prototype.at ( index )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.at) - fn at( + fn at<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let index = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let this_value = this_value.bind(gc.nogc()); + let index = arguments.get(0).bind(gc.nogc()); // 1. Let O be the this value. let o = this_value; // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). @@ -415,7 +416,7 @@ impl TypedArrayPrototype { index.into_i64() } else { let scoped_o = o.scope(agent, gc.nogc()); - let result = to_integer_or_infinity(agent, index, gc.reborrow())?.into_i64(); + let result = to_integer_or_infinity(agent, index.unbind(), gc.reborrow())?.into_i64(); o = scoped_o.get(agent).bind(gc.nogc()); result }; @@ -435,22 +436,23 @@ impl TypedArrayPrototype { // 8. Return ! Get(O, ! ToString(𝔽(k))). Ok(unwrap_try(try_get( agent, - o, + o.unbind(), PropertyKey::Integer(k.try_into().unwrap()), gc.nogc(), - ))) + )) + .unbind()) } /// ### [23.2.3.2 get %TypedArray%.prototype.buffer](https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer) /// /// %TypedArray%.prototype.buffer is an accessor property whose set accessor /// function is undefined. - fn get_buffer( + fn get_buffer<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). @@ -466,12 +468,12 @@ impl TypedArrayPrototype { /// /// %TypedArray%.prototype.byteLength is an accessor property whose set /// accessor function is undefined. - fn get_byte_length( + fn get_byte_length<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). @@ -511,12 +513,12 @@ impl TypedArrayPrototype { /// /// %TypedArray%.prototype.byteOffset is an accessor property whose set /// accessor function is undefined. - fn get_byte_offset( + fn get_byte_offset<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). @@ -566,25 +568,26 @@ impl TypedArrayPrototype { Ok(Value::try_from(o.byte_offset(agent) as i64).unwrap()) } - fn copy_within( + fn copy_within<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } /// ### [23.2.3.7 %TypedArray%.prototype.entries ( )](https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries) - fn entries( + fn entries<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O, seq-cst). - let o = validate_typed_array(agent, this_value, Ordering::SeqCst, gc.nogc())?.object; + let o = validate_typed_array(agent, this_value, Ordering::SeqCst, gc)?.object; // 3. Return CreateArrayIterator(O, key+value). Ok( ArrayIterator::from_object(agent, o.into_object(), CollectionIteratorKind::KeyAndValue) @@ -593,52 +596,49 @@ impl TypedArrayPrototype { } // ### [23.2.3.8 %%TypedArray%.prototype.every ( callback [ , thisArg ] )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-%typedarray%.prototype.every) - fn every( + fn every<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback = arguments.get(0).bind(gc.nogc()); - let this_arg = arguments.get(1).bind(gc.nogc()); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback = arguments.get(0).bind(nogc); + let this_arg = arguments.get(1).bind(nogc); // 1. Let O be the this value. let o = this_value; // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). - let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, gc.nogc())?; + let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, nogc)?; let mut o = ta_record.object; // 3. Let len be TypedArrayLength(taRecord). let len = match o { TypedArray::Int8Array(_) | TypedArray::Uint8Array(_) - | TypedArray::Uint8ClampedArray(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Uint8ClampedArray(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Int16Array(_) | TypedArray::Uint16Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } #[cfg(feature = "proposal-float16array")] - TypedArray::Float16Array(_) => typed_array_length::(agent, &ta_record, gc.nogc()), + TypedArray::Float16Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Int32Array(_) | TypedArray::Uint32Array(_) - | TypedArray::Float32Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float32Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::BigInt64Array(_) | TypedArray::BigUint64Array(_) - | TypedArray::Float64Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float64Array(_) => typed_array_length::(agent, &ta_record, nogc), }; // 4. If IsCallable(callback) is false, throw a TypeError exception. - let Some(callback) = is_callable(callback, gc.nogc()) else { + let Some(callback) = is_callable(callback, nogc) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback is not callable", - gc.nogc(), + nogc, )); }; - let callback = callback.scope(agent, gc.nogc()); - let scoped_o = o.scope(agent, gc.nogc()); + let callback = callback.scope(agent, nogc); + let this_arg = this_arg.scope(agent, nogc); + let scoped_o = o.scope(agent, nogc); // 5. Let k be 0. let mut k = 0; // 6. Repeat, while k < len, @@ -651,11 +651,11 @@ impl TypedArrayPrototype { let call = call_function( agent, callback.get(agent), - this_arg, + this_arg.get(agent), Some(ArgumentsList(&[ - k_value, + k_value.unbind(), Number::try_from(k).unwrap().into_value(), - o.into_value(), + o.into_value().unbind(), ])), gc.reborrow(), )?; @@ -672,108 +672,105 @@ impl TypedArrayPrototype { Ok(true.into()) } - fn fill( + fn fill<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn filter( + fn filter<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn find( + fn find<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn find_index( + fn find_index<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn find_last( + fn find_last<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn find_last_index( + fn find_last_index<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } // ### [ 23.2.3.15 %TypedArray%.prototype.forEach ( callback [ , thisArg ] )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-%typedarray%.prototype.foreach) // The interpretation and use of the arguments of this method are the same as for Array.prototype.forEach as defined in 23.1.3.15. - fn for_each( + fn for_each<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback = arguments.get(0).bind(gc.nogc()); - let this_arg = arguments.get(1).bind(gc.nogc()); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback = arguments.get(0).bind(nogc); + let this_arg = arguments.get(1).bind(nogc); // 1. Let O be the this value. let o = this_value; // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). - let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, gc.nogc())?; + let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, nogc)?; // 3. Let len be TypedArrayLength(taRecord). let mut o = ta_record.object; - let scoped_o = o.scope(agent, gc.nogc()); + let scoped_o = o.scope(agent, nogc); let len = match o { TypedArray::Int8Array(_) | TypedArray::Uint8Array(_) - | TypedArray::Uint8ClampedArray(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Uint8ClampedArray(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Int16Array(_) | TypedArray::Uint16Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } #[cfg(feature = "proposal-float16array")] - TypedArray::Float16Array(_) => typed_array_length::(agent, &ta_record, gc.nogc()), + TypedArray::Float16Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Int32Array(_) | TypedArray::Uint32Array(_) - | TypedArray::Float32Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float32Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::BigInt64Array(_) | TypedArray::BigUint64Array(_) - | TypedArray::Float64Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float64Array(_) => typed_array_length::(agent, &ta_record, nogc), }; // 4. If IsCallable(callback) is false, throw a TypeError exception. - let Some(callback) = is_callable(callback, gc.nogc()) else { + let Some(callback) = is_callable(callback, nogc) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback is not callable", - gc.nogc(), + nogc, )); }; - let callback = callback.scope(agent, gc.nogc()); + let callback = callback.scope(agent, nogc); + let this_arg = this_arg.scope(agent, nogc); // 5. Let k be 0. let mut k = 0; // 6. Repeat, while k < len, @@ -788,8 +785,12 @@ impl TypedArrayPrototype { call_function( agent, callback.get(agent), - this_arg, - Some(ArgumentsList(&[k_value, fk, o.into_value()])), + this_arg.get(agent), + Some(ArgumentsList(&[ + k_value.unbind(), + fk.unbind(), + o.into_value().unbind(), + ])), gc.reborrow(), )?; // d. Set k to k + 1. @@ -802,59 +803,63 @@ impl TypedArrayPrototype { // ### [23.2.3.16 %TypedArray%.prototype.includes ( searchElement [ , fromIndex ] )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-%typedarray%.prototype.includes) // The interpretation and use of the arguments of this method are the same as for Array.prototype.includes as defined in 23.1.3.16. - fn includes( + fn includes<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_element = arguments.get(0).bind(gc.nogc()); - let from_index = arguments.get(1).bind(gc.nogc()); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let mut search_element = arguments.get(0).bind(nogc); + let from_index = arguments.get(1).bind(nogc); // 1. Let O be the this value. let o = this_value; // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). - let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, gc.nogc())?; + let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, nogc)?; // 3. Let len be TypedArrayLength(taRecord). let mut o = ta_record.object; let len = match o { TypedArray::Int8Array(_) | TypedArray::Uint8Array(_) - | TypedArray::Uint8ClampedArray(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Uint8ClampedArray(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Int16Array(_) | TypedArray::Uint16Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } #[cfg(feature = "proposal-float16array")] TypedArray::Float16Array(_) => typed_array_length::(agent, &ta_record, gc.nogc()), TypedArray::Int32Array(_) | TypedArray::Uint32Array(_) - | TypedArray::Float32Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float32Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::BigInt64Array(_) | TypedArray::BigUint64Array(_) - | TypedArray::Float64Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float64Array(_) => typed_array_length::(agent, &ta_record, nogc), } as i64; // 4. If len = 0, return false. if len == 0 { return Ok(false.into()); }; // 5. Let n be ? ToIntegerOrInfinity(fromIndex). - let n = if let TryResult::Continue(n) = - try_to_integer_or_infinity(agent, from_index, gc.nogc()) + let from_index_is_undefined = from_index.is_undefined(); + let n = if let TryResult::Continue(n) = try_to_integer_or_infinity(agent, from_index, nogc) { n? } else { - let scoped_o = o.scope(agent, gc.nogc()); - let result = to_integer_or_infinity(agent, from_index, gc.reborrow()); - o = scoped_o.get(agent).bind(gc.nogc()); - result? + let scoped_o = o.scope(agent, nogc); + let scoped_search_element = search_element.scope(agent, nogc); + let result = to_integer_or_infinity(agent, from_index.unbind(), gc.reborrow())?; + let gc = gc.nogc(); + o = scoped_o.get(agent).bind(gc); + search_element = scoped_search_element.get(agent).bind(gc); + result }; + let o = o.unbind(); + let search_element = search_element.unbind(); + let gc = gc.into_nogc(); + let o = o.bind(gc); + let search_element = search_element.bind(gc); // 6. Assert: If fromIndex is undefined, then n is 0. - if from_index.is_undefined() { + if from_index_is_undefined { assert_eq!(n.into_i64(), 0); } // 7. If n = +∞, return false. @@ -888,7 +893,7 @@ impl TypedArrayPrototype { agent, o, PropertyKey::Integer(k.try_into().unwrap()), - gc.nogc(), + gc, )); // b. If SameValueZero(searchElement, elementK) is true, return true. if same_value_zero(agent, search_element, element_k) { @@ -901,12 +906,12 @@ impl TypedArrayPrototype { Ok(false.into()) } - fn index_of( + fn index_of<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } @@ -917,67 +922,69 @@ impl TypedArrayPrototype { /// /// This method is not generic. The this value must be an object with a /// `[[TypedArrayName]]` internal slot. - fn join( + fn join<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let separator = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let separator = arguments.get(0).bind(nogc); // 1. Let O be the this value. let o = this_value; // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). - let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, gc.nogc())?; + let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, nogc)?; let mut o = ta_record.object; // 3. Let len be TypedArrayLength(taRecord). let (len, element_size) = match o { TypedArray::Int8Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Uint8Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Uint8ClampedArray(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Int16Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Uint16Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Int32Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Uint32Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::BigInt64Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::BigUint64Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), #[cfg(feature = "proposal-float16array")] TypedArray::Float16Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Float32Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), TypedArray::Float64Array(_) => ( - typed_array_length::(agent, &ta_record, gc.nogc()), + typed_array_length::(agent, &ta_record, nogc), core::mem::size_of::(), ), }; @@ -988,13 +995,17 @@ impl TypedArrayPrototype { (sep, false) } else { // 5. Else, let sep be ? ToString(separator). - let scoped_o = o.scope(agent, gc.nogc()); - let result = to_string(agent, separator, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - o = scoped_o.get(agent).bind(gc.nogc()); - (result, true) + let scoped_o = o.scope(agent, nogc); + let result = to_string(agent, separator.unbind(), gc.reborrow())?.unbind(); + let gc = gc.nogc(); + o = scoped_o.get(agent).bind(gc); + (result.bind(gc), true) }; + let o = o.unbind(); + let sep_string = sep_string.unbind(); + let gc = gc.into_nogc(); + let o = o.bind(gc); + let sep_string = sep_string.bind(gc); if len == 0 { return Ok(String::EMPTY_STRING.into_value()); } @@ -1016,75 +1027,61 @@ impl TypedArrayPrototype { // 7. Let k be 0. // 8. Repeat, while k < len, let offset = o.byte_offset(agent); - let viewed_array_buffer = o.get_viewed_array_buffer(agent, gc.nogc()); + let viewed_array_buffer = o.get_viewed_array_buffer(agent, gc); // Note: Above ToString might have detached the ArrayBuffer or shrunk its length. let (is_invalid_typed_array, after_len) = if recheck_buffer { let is_detached = is_detached_buffer(agent, viewed_array_buffer); - let ta_record = make_typed_array_with_buffer_witness_record( - agent, - o, - Ordering::Unordered, - gc.nogc(), - ); + let ta_record = + make_typed_array_with_buffer_witness_record(agent, o, Ordering::Unordered, gc); match o { TypedArray::Int8Array(_) => ( - is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Uint8Array(_) => ( - is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Uint8ClampedArray(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Int16Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Uint16Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Int32Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Uint32Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::BigInt64Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::BigUint64Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), #[cfg(feature = "proposal-float16array")] TypedArray::Float16Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Float32Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), TypedArray::Float64Array(_) => ( - is_detached - || is_typed_array_out_of_bounds::(agent, &ta_record, gc.nogc()), - typed_array_length::(agent, &ta_record, gc.nogc()), + is_detached || is_typed_array_out_of_bounds::(agent, &ta_record, gc), + typed_array_length::(agent, &ta_record, gc), ), } } else { @@ -1114,7 +1111,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Uint8Array(_) => get_value_from_buffer::( agent, @@ -1123,7 +1120,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Uint8ClampedArray(_) => get_value_from_buffer::( agent, @@ -1132,7 +1129,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Int16Array(_) => get_value_from_buffer::( agent, @@ -1141,7 +1138,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Uint16Array(_) => get_value_from_buffer::( agent, @@ -1150,7 +1147,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Int32Array(_) => get_value_from_buffer::( agent, @@ -1159,7 +1156,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Uint32Array(_) => get_value_from_buffer::( agent, @@ -1168,7 +1165,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::BigInt64Array(_) => get_value_from_buffer::( agent, @@ -1177,7 +1174,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::BigUint64Array(_) => get_value_from_buffer::( agent, @@ -1186,7 +1183,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), #[cfg(feature = "proposal-float16array")] TypedArray::Float16Array(_) => get_value_from_buffer::( @@ -1196,7 +1193,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Float32Array(_) => get_value_from_buffer::( agent, @@ -1205,7 +1202,7 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), TypedArray::Float64Array(_) => get_value_from_buffer::( agent, @@ -1214,26 +1211,26 @@ impl TypedArrayPrototype { true, Ordering::Unordered, None, - gc.nogc(), + gc, ), }; // i. Let S be ! ToString(element). - let s = unwrap_try(try_to_string(agent, element, gc.nogc())).unwrap(); + let s = unwrap_try(try_to_string(agent, element, gc)).unwrap(); // ii. Set R to the string-concatenation of R and S. r.push_str(s.as_str(agent)); // d. Set k to k + 1. } // 9. Return R. - Ok(String::from_string(agent, r, gc.nogc()).into_value()) + Ok(String::from_string(agent, r, gc).into_value().unbind()) } /// ### [23.2.3.19 %TypedArray%.prototype.keys ( )](https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys) - fn keys( + fn keys<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O, seq-cst). let o = validate_typed_array(agent, this_value, Ordering::SeqCst, gc.nogc())?.object; @@ -1244,22 +1241,22 @@ impl TypedArrayPrototype { ) } - fn last_index_of( + fn last_index_of<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } /// ### [23.2.3.21 get %TypedArray%.prototype.length](https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length) - fn get_length( + fn get_length<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). @@ -1323,107 +1320,104 @@ impl TypedArrayPrototype { Ok(Value::try_from(length).unwrap()) } - fn map( + fn map<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn reduce( + fn reduce<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn reduce_right( + fn reduce_right<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn reverse( + fn reverse<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn set( + fn set<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn slice( + fn slice<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } /// ### [23.2.3.28 get %TypedArray%.prototype.some](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-%typedarray%.prototype.some) - fn some( + fn some<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback = arguments.get(0).bind(gc.nogc()); - let this_arg = arguments.get(1).bind(gc.nogc()); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback = arguments.get(0).bind(nogc); + let this_arg = arguments.get(1).bind(nogc); // 1. Let O be the this value. let o = this_value; // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). - let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, gc.nogc())?; + let ta_record = validate_typed_array(agent, o, Ordering::SeqCst, nogc)?; let mut o = ta_record.object; // 3. Let len be TypedArrayLength(taRecord). let len = match o { TypedArray::Int8Array(_) | TypedArray::Uint8Array(_) - | TypedArray::Uint8ClampedArray(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Uint8ClampedArray(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Int16Array(_) | TypedArray::Uint16Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) + typed_array_length::(agent, &ta_record, nogc) } #[cfg(feature = "proposal-float16array")] - TypedArray::Float16Array(_) => typed_array_length::(agent, &ta_record, gc.nogc()), + TypedArray::Float16Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::Int32Array(_) | TypedArray::Uint32Array(_) - | TypedArray::Float32Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float32Array(_) => typed_array_length::(agent, &ta_record, nogc), TypedArray::BigInt64Array(_) | TypedArray::BigUint64Array(_) - | TypedArray::Float64Array(_) => { - typed_array_length::(agent, &ta_record, gc.nogc()) - } + | TypedArray::Float64Array(_) => typed_array_length::(agent, &ta_record, nogc), }; // 4. If IsCallable(callback) is false, throw a TypeError exception. - let Some(callback) = is_callable(callback, gc.nogc()) else { + let Some(callback) = is_callable(callback, nogc) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback is not callable", - gc.nogc(), + nogc, )); }; - let callback = callback.scope(agent, gc.nogc()); - let scoped_o = o.scope(agent, gc.nogc()); + let callback = callback.scope(agent, nogc); + let this_arg = this_arg.scope(agent, nogc); + let scoped_o = o.scope(agent, nogc); // 5. Let k be 0. let mut k = 0; // 6. Repeat, while k < len, @@ -1436,11 +1430,11 @@ impl TypedArrayPrototype { let call = call_function( agent, callback.get(agent), - this_arg, + this_arg.get(agent), Some(ArgumentsList(&[ - k_value, - Number::try_from(k).unwrap().into_value(), - o.into_value(), + k_value.unbind(), + Number::try_from(k).unwrap().into_value().unbind(), + o.into_value().unbind(), ])), gc.reborrow(), )?; @@ -1457,70 +1451,71 @@ impl TypedArrayPrototype { Ok(false.into()) } - fn sort( + fn sort<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn subarray( + fn subarray<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn to_locale_string( + fn to_locale_string<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn to_reversed( + fn to_reversed<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn to_sorted( + fn to_sorted<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn to_spliced( + fn to_spliced<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } /// ### [23.2.3.35 %TypedArray%.prototype.values ( )](https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-%symbol.tostringtag%) - fn values( + fn values<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O, seq-cst). - let o = validate_typed_array(agent, this_value, Ordering::SeqCst, gc.nogc())?.object; + let o = validate_typed_array(agent, this_value, Ordering::SeqCst, gc)?.object; // 3. Return CreateArrayIterator(O, value). Ok( ArrayIterator::from_object(agent, o.into_object(), CollectionIteratorKind::Value) @@ -1528,22 +1523,24 @@ impl TypedArrayPrototype { ) } - fn with( + fn with<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } /// ### [23.2.3.38 get %TypedArray%.prototype \[ %Symbol.toStringTag% \]](https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-%symbol.tostringtag%) - fn get_to_string_tag( + fn get_to_string_tag<'gc>( _agent: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); // 1. Let O be the this value. if let Ok(o) = TypedArray::try_from(this_value) { // 4. Let name be O.[[TypedArrayName]]. @@ -1647,7 +1644,7 @@ pub(crate) fn require_internal_slot_typed_array<'a>( gc: NoGcScope<'a, '_>, ) -> JsResult> { // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). - TypedArray::try_from(o).map_err(|_| { + TypedArray::try_from(o.unbind()).map_err(|_| { agent.throw_exception_with_static_message( crate::ecmascript::execution::agent::ExceptionType::TypeError, "Expected this to be TypedArray", diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_constructor.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_constructor.rs index aa8ee9430..5c77d22b2 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_constructor.rs @@ -6,15 +6,14 @@ use core::hash::Hasher; use ahash::AHasher; +use crate::ecmascript::abstract_operations::operations_on_iterator_objects::if_abrupt_close_iterator; use crate::ecmascript::abstract_operations::operations_on_objects::try_get; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ abstract_operations::{ - operations_on_iterator_objects::{ - get_iterator, if_abrupt_close_iterator, iterator_close, iterator_step_value, - }, + operations_on_iterator_objects::{get_iterator, iterator_close, iterator_step_value}, operations_on_objects::{call_function, get, get_method}, testing_and_comparison::is_callable, }, @@ -65,13 +64,18 @@ impl Builtin for MapGetSpecies { impl BuiltinGetter for MapGetSpecies {} impl MapConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let iterable = arguments.get(0).bind(nogc); + let no_iterable = iterable.is_undefined() || iterable.is_null(); + let new_target = new_target.bind(nogc); + // If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( @@ -82,9 +86,20 @@ impl MapConstructor { }; let new_target = Function::try_from(new_target).unwrap(); // 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%Map.prototype%", « [[MapData]] »). + // 4. If iterable is either undefined or null, return map. + if no_iterable { + return Ok(ordinary_create_from_constructor( + agent, + new_target.unbind(), + ProtoIntrinsics::Map, + gc, + )? + .into_value()); + } + let iterable = iterable.scope(agent, nogc); let mut map = Map::try_from(ordinary_create_from_constructor( agent, - new_target, + new_target.unbind(), ProtoIntrinsics::Map, gc.reborrow(), )?) @@ -92,74 +107,70 @@ impl MapConstructor { .unbind() .bind(gc.nogc()); // 3. Set map.[[MapData]] to a new empty List. - let iterable = arguments.get(0); - // 4. If iterable is either undefined or null, return map. - if iterable.is_undefined() || iterable.is_null() { - Ok(map.into_value()) - } else { - // Note - // If the parameter iterable is present, it is expected to be an - // object that implements an @@iterator method that returns an - // iterator object that produces a two element array-like object - // whose first element is a value that will be used as a Map key - // and whose second element is the value to associate with that - // key. + // Note + // If the parameter iterable is present, it is expected to be an + // object that implements an @@iterator method that returns an + // iterator object that produces a two element array-like object + // whose first element is a value that will be used as a Map key + // and whose second element is the value to associate with that + // key. - // 5. Let adder be ? Get(map, "set"). - let adder = if let TryResult::Continue(adder) = try_get( + // 5. Let adder be ? Get(map, "set"). + let adder = if let TryResult::Continue(adder) = try_get( + agent, + map.into_object().unbind(), + BUILTIN_STRING_MEMORY.set.to_property_key(), + gc.nogc(), + ) { + adder + } else { + let scoped_map = map.scope(agent, gc.nogc()); + let adder = get( agent, map.into_object().unbind(), BUILTIN_STRING_MEMORY.set.to_property_key(), - gc.nogc(), - ) { - adder - } else { - let scoped_map = map.scope(agent, gc.nogc()); - let adder = get( - agent, - map.into_object().unbind(), - BUILTIN_STRING_MEMORY.set.to_property_key(), - gc.reborrow(), - )?; - map = scoped_map.get(agent).bind(gc.nogc()); - adder - }; - // 6. If IsCallable(adder) is false, throw a TypeError exception. - let Some(adder) = is_callable(adder, gc.nogc()) else { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "Map.prototype.set is not callable", - gc.nogc(), - )); - }; - // 7. Return ? AddEntriesFromIterable(map, iterable, adder). - add_entries_from_iterable_map_constructor( - agent, - map.unbind(), - iterable, - adder.unbind(), gc.reborrow(), - ) - .map(|result| result.into_value()) - } + )? + .unbind(); + let gc = gc.nogc(); + map = scoped_map.get(agent).bind(gc); + adder.bind(gc) + }; + // 6. If IsCallable(adder) is false, throw a TypeError exception. + let Some(adder) = is_callable(adder, gc.nogc()) else { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "Map.prototype.set is not callable", + gc.nogc(), + )); + }; + // 7. Return ? AddEntriesFromIterable(map, iterable, adder). + add_entries_from_iterable_map_constructor( + agent, + map.unbind(), + iterable.get(agent), + adder.unbind(), + gc, + ) + .map(|result| result.into_value()) } - fn group_by( + fn group_by<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_species( + fn get_species<'gc>( _: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(this_value) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -187,17 +198,20 @@ pub fn add_entries_from_iterable_map_constructor<'a>( adder: Function, mut gc: GcScope<'a, '_>, ) -> JsResult> { - let mut target = target.bind(gc.nogc()); - let mut adder = adder.bind(gc.nogc()); + let nogc = gc.nogc(); + let mut target = target.bind(nogc); + let mut iterable = iterable.bind(nogc); + let mut adder = adder.bind(nogc); if let Function::BuiltinFunction(bf) = adder { if agent[bf].behaviour == MapPrototypeSet::BEHAVIOUR { // Normal Map.prototype.set - if let Value::Array(iterable) = iterable { - let scoped_adder = bf.scope(agent, gc.nogc()); - let scoped_target = target.scope(agent, gc.nogc()); + if let Value::Array(arr_iterable) = iterable { + let scoped_target = target.scope(agent, nogc); + let scoped_iterable = arr_iterable.scope(agent, nogc); + let scoped_adder = bf.scope(agent, nogc); let using_iterator = get_method( agent, - iterable.into_value(), + arr_iterable.into_value().unbind(), WellKnownSymbolIndexes::Iterator.into(), gc.reborrow(), )? @@ -213,6 +227,7 @@ pub fn add_entries_from_iterable_map_constructor<'a>( .into_function(), ) { + let arr_iterable = scoped_iterable.get(agent).bind(gc.nogc()); let Heap { elements, arrays, @@ -226,12 +241,12 @@ pub fn add_entries_from_iterable_map_constructor<'a>( let primitive_heap = PrimitiveHeap::new(bigints, numbers, strings); // Iterable uses the normal Array iterator of this realm. - if iterable.len(&array_heap) == 0 { + if arr_iterable.len(&array_heap) == 0 { // Array iterator does not iterate empty arrays. return Ok(scoped_target.get(agent).bind(gc.into_nogc())); } - if iterable.is_trivial(&array_heap) - && iterable.as_slice(&array_heap).iter().all(|entry| { + if arr_iterable.is_trivial(&array_heap) + && arr_iterable.as_slice(&array_heap).iter().all(|entry| { if let Some(Value::Array(entry)) = *entry { entry.len(&array_heap) == 2 && entry.is_trivial(&array_heap) @@ -242,7 +257,12 @@ pub fn add_entries_from_iterable_map_constructor<'a>( }) { // Trivial, dense array of trivial, dense arrays of two elements. - let length = iterable.len(&array_heap); + let target = target.unbind(); + let arr_iterable = arr_iterable.unbind(); + let gc = gc.into_nogc(); + let target = target.bind(gc); + let arr_iterable = arr_iterable.bind(gc); + let length = arr_iterable.len(&array_heap); let MapData { keys, values, @@ -262,14 +282,17 @@ pub fn add_entries_from_iterable_map_constructor<'a>( value.hash(&primitive_heap, &mut hasher); hasher.finish() }; - for entry in iterable.as_slice(&array_heap).iter() { + for entry in arr_iterable.as_slice(&array_heap).iter() { let Some(Value::Array(entry)) = *entry else { unreachable!() }; let slice = entry.as_slice(&array_heap); - let key = canonicalize_keyed_collection_key(numbers, slice[0].unwrap()); + let key = canonicalize_keyed_collection_key( + numbers, + slice[0].unwrap().bind(gc), + ); let key_hash = hasher(key); - let value = slice[1].unwrap(); + let value = slice[1].unwrap().bind(gc); let next_index = keys.len() as u32; let entry = map_data.entry( key_hash, @@ -281,24 +304,32 @@ pub fn add_entries_from_iterable_map_constructor<'a>( // We have duplicates in the array. Latter // ones overwrite earlier ones. let index = *occupied.get(); - values[index as usize] = Some(value); + values[index as usize] = Some(value.unbind()); } hashbrown::hash_table::Entry::Vacant(vacant) => { vacant.insert(next_index); - keys.push(Some(key)); - values.push(Some(value)); + keys.push(Some(key.unbind())); + values.push(Some(value.unbind())); } } } - return Ok(scoped_target.get(agent).bind(gc.into_nogc())); + return Ok(scoped_target.get(agent).bind(gc)); } } - adder = scoped_adder.get(agent).bind(gc.nogc()).into_function(); + let gc = gc.nogc(); + iterable = scoped_iterable.get(agent).bind(gc).into_value(); + adder = scoped_adder.get(agent).bind(gc).into_function(); } } } - add_entries_from_iterable(agent, target.unbind(), iterable, adder.unbind(), gc) + add_entries_from_iterable( + agent, + target.unbind(), + iterable.unbind(), + adder.unbind(), + gc, + ) } /// ### [24.1.1.2 AddEntriesFromIterable ( target, iterable, adder )](https://tc39.es/ecma262/#sec-add-entries-from-iterable) @@ -321,10 +352,12 @@ pub(crate) fn add_entries_from_iterable<'a>( adder: Function, mut gc: GcScope<'a, '_>, ) -> JsResult> { - let target = target.bind(gc.nogc()).scope(agent, gc.nogc()); - let adder = adder.bind(gc.nogc()).scope(agent, gc.nogc()); + let nogc = gc.nogc(); + let target = target.bind(nogc).scope(agent, nogc); + let iterable = iterable.bind(nogc); + let adder = adder.bind(nogc).scope(agent, nogc); // 1. Let iteratorRecord be ? GetIterator(iterable, SYNC). - let mut iterator_record = get_iterator(agent, iterable, false, gc.reborrow())?; + let mut iterator_record = get_iterator(agent, iterable.unbind(), false, gc.reborrow())?; // 2. Repeat, loop { // a. Let next be ? IteratorStepValue(iteratorRecord). @@ -344,22 +377,24 @@ pub(crate) fn add_entries_from_iterable<'a>( // ii. Return ? IteratorClose(iteratorRecord, error). return iterator_close(agent, &iterator_record, Err(error), gc.reborrow()); }; + let next = next.unbind().bind(gc.nogc()); + let scoped_next = next.scope(agent, gc.nogc()); // d. Let k be Completion(Get(next, "0")). - let k = get(agent, next, 0.into(), gc.reborrow()); + let k = get(agent, next.unbind(), 0.into(), gc.reborrow()); // e. IfAbruptCloseIterator(k, iteratorRecord). - let k = if_abrupt_close_iterator(agent, k, &iterator_record, gc.reborrow())?; + let k = if_abrupt_close_iterator!(agent, k, iterator_record, gc).scope(agent, gc.nogc()); // f. Let v be Completion(Get(next, "1")). - let v = get(agent, next, 1.into(), gc.reborrow()); + let v = get(agent, scoped_next.get(agent), 1.into(), gc.reborrow()); // g. IfAbruptCloseIterator(v, iteratorRecord). - let v = if_abrupt_close_iterator(agent, v, &iterator_record, gc.reborrow())?; + let v = if_abrupt_close_iterator!(agent, v, iterator_record, gc); // h. Let status be Completion(Call(adder, target, « k, v »)). let status = call_function( agent, adder.get(agent), target.get(agent).into_value(), - Some(ArgumentsList(&[k, v])), + Some(ArgumentsList(&[k.get(agent), v.unbind()])), gc.reborrow(), ); - let _ = if_abrupt_close_iterator(agent, status, &iterator_record, gc.reborrow())?; + let _ = if_abrupt_close_iterator!(agent, status, iterator_record, gc); } } diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator.rs index fd602a8b0..207ba1d1e 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator.rs @@ -15,7 +15,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::MapIteratorIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, @@ -74,8 +78,8 @@ impl MapIterator<'_> { } } -impl IntoValue for MapIterator<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for MapIterator<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -92,16 +96,16 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: MapIterator) -> Self { - Self::MapIterator(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: MapIterator<'a>) -> Self { + Self::MapIterator(value) } } -impl TryFrom for MapIterator<'_> { +impl<'a> TryFrom> for MapIterator<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::MapIterator(data) => Ok(data), _ => Err(()), diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator_prototype.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator_prototype.rs index 98e59c42f..aaf6a741b 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_iterator_objects/map_iterator_prototype.rs @@ -2,7 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::ecmascript::builtins::Behaviour; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -28,18 +29,18 @@ impl Builtin for MapIteratorPrototypeNext { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MapIteratorPrototype::next); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MapIteratorPrototype::next); } impl MapIteratorPrototype { - fn next( + fn next<'gc>( agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); // 27.5.3.2 GeneratorValidate ( generator, generatorBrand ) // 3. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception. let Value::MapIterator(iterator) = this_value else { @@ -49,7 +50,6 @@ impl MapIteratorPrototype { gc, )); }; - let iterator = iterator.bind(gc); // 24.1.5.1 CreateMapIterator ( map, kind ), step 2 // NOTE: We set `map` to None when the generator in the spec text has returned. @@ -60,7 +60,7 @@ impl MapIteratorPrototype { // a. Let entries be map.[[MapData]]. // c. Let numEntries be the number of elements in entries. // d. Repeat, while index < numEntries, - while agent[iterator].next_index < agent[map].keys().len() { + while agent[iterator].next_index < agent[map].keys(gc).len() { // i. Let e be entries[index]. // ii. Set index to index + 1. let index = agent[iterator].next_index; @@ -71,7 +71,7 @@ impl MapIteratorPrototype { // iii. If e.[[Key]] is not EMPTY, then // 1. If kind is KEY, then // a. Let result be e.[[Key]]. - let Some(key) = agent[map].keys()[index] else { + let Some(key) = agent[map].keys(gc)[index] else { continue; }; key @@ -80,7 +80,7 @@ impl MapIteratorPrototype { // iii. If e.[[Key]] is not EMPTY, then // 2. If kind is VALUE, then // a. Let result be e.[[Value]]. - let Some(value) = agent[map].values()[index] else { + let Some(value) = agent[map].values(gc)[index] else { continue; }; value @@ -90,10 +90,10 @@ impl MapIteratorPrototype { // 3. Else, // a. Assert: kind is KEY+VALUE. // b. Let result be CreateArrayFromList(« e.[[Key]], e.[[Value]] »). - let Some(key) = agent[map].keys()[index] else { + let Some(key) = agent[map].keys(gc)[index] else { continue; }; - let value = agent[map].values()[index].unwrap(); + let value = agent[map].values(gc)[index].unwrap(); create_array_from_list(agent, &[key, value], gc).into_value() } }; @@ -102,7 +102,7 @@ impl MapIteratorPrototype { return Ok(create_iter_result_object(agent, result, false, gc).into_value()); } - debug_assert_eq!(agent[iterator].next_index, agent[map].keys().len()); + debug_assert_eq!(agent[iterator].next_index, agent[map].keys(gc).len()); // e. Return undefined. agent[iterator].map = None; diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_prototype.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_prototype.rs index 0fb1cd280..c0ab7c06b 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/map_objects/map_prototype.rs @@ -6,7 +6,7 @@ use core::{hash::Hasher, ops::Index}; use ahash::AHasher; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -101,15 +101,15 @@ impl MapPrototype { /// > The existing \[\[MapData]] List is preserved because there may be /// > existing Map Iterator objects that are suspended midway through /// > iterating over that List. - fn clear( + fn clear<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; // 3. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do // a. Set p.[[Key]] to EMPTY. @@ -125,15 +125,16 @@ impl MapPrototype { /// > The value EMPTY is used as a specification device to indicate that an /// > entry has been deleted. Actual implementations may take other actions /// > such as physically removing the entry from internal data structures. - fn delete( + fn delete<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let gc = gc.into_nogc(); + let key = arguments.get(0).bind(gc); let m = require_map_data_internal_slot(agent, this_value, gc)?; let Heap { @@ -146,7 +147,7 @@ impl MapPrototype { let primitive_heap = PrimitiveHeap::new(bigints, numbers, strings); // 3. Set key to CanonicalizeKeyedCollectionKey(key). - let key = canonicalize_keyed_collection_key(numbers, arguments.get(0)); + let key = canonicalize_keyed_collection_key(numbers, key); let key_hash = { let mut hasher = AHasher::default(); key.hash(&primitive_heap, &mut hasher); @@ -181,18 +182,18 @@ impl MapPrototype { } } - fn entries( + fn entries<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); // 1. Let M be the this value. // 2. Return ? CreateMapIterator(M, KEY+VALUE). // 24.1.5.1 CreateMapIterator ( map, kind ) // 1. Perform ? RequireInternalSlot(map, [[MapData]]). - let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; Ok(MapIterator::from_map(agent, m, CollectionIteratorKind::KeyAndValue).into_value()) } @@ -222,32 +223,35 @@ impl MapPrototype { /// > after the call to `forEach` begins and before being visited are not /// > visited unless the key is added again before the `forEach` call /// > completes. - fn for_each( + fn for_each<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).bind(nogc); + let this_arg = arguments.get(1).bind(nogc); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let mut m = require_map_data_internal_slot(agent, this_value, gc.nogc())?; + let mut m = require_map_data_internal_slot(agent, this_value, nogc)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(callback_fn) = is_callable(callback_fn, nogc) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function parameter is not callable", - gc.nogc(), + nogc, )); }; // 4. Let entries be M.[[MapData]]. // 5. Let numEntries be the number of elements in entries. - let mut num_entries = agent[m].values().len(); + let mut num_entries = agent[m].values(gc.nogc()).len(); - let callback_fn = callback_fn.scope(agent, gc.nogc()); - let scoped_m = m.scope(agent, gc.nogc()); + let this_arg = this_arg.scope(agent, nogc); + let callback_fn = callback_fn.scope(agent, nogc); + let scoped_m = m.scope(agent, nogc); // 6. Let index be 0. let mut index = 0; @@ -258,23 +262,27 @@ impl MapPrototype { let entry_index = index; // b. Set index to index + 1. index += 1; - let k = data.keys()[entry_index]; + let k = data.keys(gc.nogc())[entry_index]; // c. If e.[[Key]] is not EMPTY, then if let Some(k) = k { - let v = data.values()[entry_index].unwrap(); + let v = data.values(gc.nogc())[entry_index].unwrap(); // i. Perform ? Call(callbackfn, thisArg, « e.[[Value]], e.[[Key]], M »). call_function( agent, callback_fn.get(agent), - this_arg, - Some(ArgumentsList(&[v, k, m.into_value()])), + this_arg.get(agent), + Some(ArgumentsList(&[ + v.unbind(), + k.unbind(), + m.into_value().unbind(), + ])), gc.reborrow(), )?; // ii. NOTE: The number of elements in entries may have // increased during execution of callbackfn. // iii. Set numEntries to the number of elements in entries. m = scoped_m.get(agent).bind(gc.nogc()); - num_entries = agent[m].values().len(); + num_entries = agent[m].values(gc.nogc()).len(); } } // 8. Return undefined. @@ -282,15 +290,16 @@ impl MapPrototype { } /// ### [24.1.3.6 Map.prototype.get ( key )](https://tc39.es/ecma262/#sec-map.prototype.get) - fn get( + fn get<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let key = arguments.get(0).bind(gc); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; let Heap { @@ -303,7 +312,7 @@ impl MapPrototype { let primitive_heap = PrimitiveHeap::new(bigints, numbers, strings); // 3. Set key to CanonicalizeKeyedCollectionKey(key). - let key = canonicalize_keyed_collection_key(agent, arguments.get(0)); + let key = canonicalize_keyed_collection_key(agent, key); let key_hash = { let mut hasher = AHasher::default(); key.hash(agent, &mut hasher); @@ -333,15 +342,16 @@ impl MapPrototype { } /// ### [24.1.3.7 Map.prototype.has ( key )](https://tc39.es/ecma262/#sec-map.prototype.has) - fn has( + fn has<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let key = arguments.get(0).bind(gc); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; let Heap { @@ -354,7 +364,7 @@ impl MapPrototype { let primitive_heap = PrimitiveHeap::new(bigints, numbers, strings); // 3. Set key to CanonicalizeKeyedCollectionKey(key). - let key = canonicalize_keyed_collection_key(numbers, arguments.get(0)); + let key = canonicalize_keyed_collection_key(numbers, key); let key_hash = { let mut hasher = AHasher::default(); key.hash(&primitive_heap, &mut hasher); @@ -376,33 +386,34 @@ impl MapPrototype { Ok(found.into()) } - fn keys( + fn keys<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); // 1. Let M be the this value. // 2. Return ? CreateMapIterator(M, KEY). // 24.1.5.1 CreateMapIterator ( map, kind ) // 1. Perform ? RequireInternalSlot(map, [[MapData]]). - let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; Ok(MapIterator::from_map(agent, m, CollectionIteratorKind::Key).into_value()) } /// ### [24.1.3.9 Map.prototype.set ( key, value )](https://tc39.es/ecma262/#sec-map.prototype.set) - fn set( + fn set<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { - let value = arguments.get(1); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let key = arguments.get(0).bind(gc); + let value = arguments.get(1).bind(gc); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; let Heap { @@ -429,7 +440,7 @@ impl MapPrototype { }; // 3. Set key to CanonicalizeKeyedCollectionKey(key). - let key = canonicalize_keyed_collection_key(numbers, arguments.get(0)); + let key = canonicalize_keyed_collection_key(numbers, key); let key_hash = hasher(key); // 4. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do // a. If p.[[Key]] is not EMPTY and SameValue(p.[[Key]], key) is true, then @@ -446,7 +457,7 @@ impl MapPrototype { hashbrown::hash_table::Entry::Occupied(occupied) => { let index = *occupied.get(); // i. Set p.[[Value]] to value. - values[index as usize] = Some(value); + values[index as usize] = Some(value.unbind()); // ii. Return M. } hashbrown::hash_table::Entry::Vacant(vacant) => { @@ -454,38 +465,38 @@ impl MapPrototype { // 6. Append p to M.[[MapData]]. let index = u32::try_from(values.len()).unwrap(); vacant.insert(index); - keys.push(Some(key)); - values.push(Some(value)); + keys.push(Some(key.unbind())); + values.push(Some(value.unbind())); } } // 7. Return M. Ok(m.into_value()) } - fn get_size( + fn get_size<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; let count = agent[m].size(); Ok(count.into()) } - fn values( + fn values<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); // 1. Let M be the this value. // 2. Return ? CreateMapIterator(M, VALUE). // 24.1.5.1 CreateMapIterator ( map, kind ) // 1. Perform ? RequireInternalSlot(map, [[MapData]]). - let gc = gc.into_nogc(); let m = require_map_data_internal_slot(agent, this_value, gc)?; Ok(MapIterator::from_map(agent, m, CollectionIteratorKind::Value).into_value()) } @@ -538,7 +549,7 @@ fn require_map_data_internal_slot<'a>( gc: NoGcScope<'a, '_>, ) -> JsResult> { match value { - Value::Map(map) => Ok(map), + Value::Map(map) => Ok(map.bind(gc)), _ => Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Object is not a Map", @@ -551,10 +562,10 @@ fn require_map_data_internal_slot<'a>( /// ### [24.5.1 CanonicalizeKeyedCollectionKey ( key )](https://tc39.es/ecma262/#sec-canonicalizekeyedcollectionkey) /// The abstract operation CanonicalizeKeyedCollectionKey takes argument key /// (an ECMAScript language value) and returns an ECMAScript language value. -pub(crate) fn canonicalize_keyed_collection_key( - agent: &impl Index, Output = f64>, - key: Value, -) -> Value { +pub(crate) fn canonicalize_keyed_collection_key<'gc>( + agent: &impl Index, Output = f64>, + key: Value<'gc>, +) -> Value<'gc> { // 1. If key is -0𝔽, return +0𝔽. if let Value::SmallF64(key) = key { // Note: Only f32 should hold -0. diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_constructor.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_constructor.rs index ac78033a8..3eaab3183 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_constructor.rs @@ -6,7 +6,7 @@ use core::hash::Hasher; use ahash::AHasher; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -55,14 +55,16 @@ impl BuiltinGetter for SetGetSpecies {} impl SetConstructor { /// ### [24.2.2.1 Set ( \[ iterable \] )](https://tc39.es/ecma262/#sec-set-iterable) - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { - let iterable = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let iterable = arguments.get(0).bind(nogc); + let new_target = new_target.bind(nogc); // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( @@ -73,9 +75,20 @@ impl SetConstructor { }; // 2. Let set be ? OrdinaryCreateFromConstructor(NewTarget, "%Set.prototype%", « [[SetData]] »). let new_target = Function::try_from(new_target).unwrap(); + // 4. If iterable is either undefined or null, return set. + if iterable.is_undefined() || iterable.is_null() { + return ordinary_create_from_constructor( + agent, + new_target.unbind(), + ProtoIntrinsics::Set, + gc, + ) + .map(|o| o.into_value()); + } + let scoped_iterable = iterable.scope(agent, nogc); let set = Set::try_from(ordinary_create_from_constructor( agent, - new_target, + new_target.unbind(), ProtoIntrinsics::Set, gc.reborrow(), )?) @@ -84,10 +97,7 @@ impl SetConstructor { .bind(gc.nogc()); let scoped_set = set.scope(agent, gc.nogc()); // 3. Set set.[[SetData]] to a new empty List. - // 4. If iterable is either undefined or null, return set. - if iterable.is_undefined() || iterable.is_null() { - return Ok(set.into_value()); - } + // 5. Let adder be ? Get(set, "add"). let adder = get( agent, @@ -96,7 +106,7 @@ impl SetConstructor { gc.reborrow(), )?; // 6. If IsCallable(adder) is false, throw a TypeError exception. - let Some(adder) = is_callable(adder, gc.nogc()) else { + let Some(adder) = is_callable(adder.unbind(), gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Invalid adder function", @@ -104,9 +114,8 @@ impl SetConstructor { )); }; let adder = adder.scope(agent, gc.nogc()); - if let Value::Array(iterable) = iterable { + if let Value::Array(iterable) = scoped_iterable.get(agent) { let iterable = iterable.bind(gc.nogc()); - let scoped_iterable = iterable.scope(agent, gc.nogc()); if iterable.is_trivial(agent) && iterable.is_dense(agent) && get_method( @@ -125,7 +134,9 @@ impl SetConstructor { // Accessorless, holeless array with standard Array values // iterator. We can fast-path this. let set = scoped_set.get(agent).bind(gc.nogc()); - let iterable = scoped_iterable.get(agent).bind(gc.nogc()); + let Value::Array(iterable) = scoped_iterable.get(agent).bind(gc.nogc()) else { + unreachable!() + }; let Heap { elements, arrays, @@ -169,19 +180,20 @@ impl SetConstructor { // We have duplicates in the array. Latter // ones overwrite earlier ones. let index = *occupied.get(); - values[index as usize] = Some(value); + values[index as usize] = Some(value.unbind()); } hashbrown::hash_table::Entry::Vacant(vacant) => { vacant.insert(next_index); - values.push(Some(value)); + values.push(Some(value.unbind())); } } }); - return Ok(set.into_value()); + return Ok(set.into_value().unbind()); } } // 7. Let iteratorRecord be ? GetIterator(iterable, SYNC). - let mut iterator_record = get_iterator(agent, iterable, false, gc.reborrow())?; + let mut iterator_record = + get_iterator(agent, scoped_iterable.get(agent), false, gc.reborrow())?; // 8. Repeat, loop { // a. Let next be ? IteratorStepValue(iteratorRecord). @@ -195,21 +207,21 @@ impl SetConstructor { agent, adder.get(agent), scoped_set.get(agent).into_value(), - Some(ArgumentsList(&[next])), + Some(ArgumentsList(&[next.unbind()])), gc.reborrow(), ); // d. IfAbruptCloseIterator(status, iteratorRecord). - let _ = if_abrupt_close_iterator(agent, status, &iterator_record, gc.reborrow())?; + let _ = if_abrupt_close_iterator!(agent, status, iterator_record, gc); } } - fn get_species( + fn get_species<'gc>( _: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(this_value) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator.rs index 7d164a60c..3dff9cc5c 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator.rs @@ -15,7 +15,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::SetIteratorIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, @@ -74,8 +78,8 @@ impl SetIterator<'_> { } } -impl IntoValue for SetIterator<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for SetIterator<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -92,16 +96,16 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: SetIterator) -> Self { - Self::SetIterator(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: SetIterator<'a>) -> Self { + Self::SetIterator(value) } } -impl TryFrom for SetIterator<'_> { +impl<'a> TryFrom> for SetIterator<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::SetIterator(data) => Ok(data), _ => Err(()), diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator_prototype.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator_prototype.rs index 000f00479..34a86e53d 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_iterator_objects/set_iterator_prototype.rs @@ -2,7 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::ecmascript::builtins::Behaviour; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -28,17 +29,16 @@ impl Builtin for SetIteratorPrototypeNext { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(SetIteratorPrototype::next); + const BEHAVIOUR: Behaviour = Behaviour::Regular(SetIteratorPrototype::next); } impl SetIteratorPrototype { - fn next( + fn next<'gc>( agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); // 27.5.3.2 GeneratorValidate ( generator, generatorBrand ) // 3. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception. @@ -60,14 +60,14 @@ impl SetIteratorPrototype { // b. Let entries be set.[[SetData]]. // c. Let numEntries be the number of elements in entries. // d. Repeat, while index < numEntries, - while agent[iterator].next_index < agent[set].values().len() { + while agent[iterator].next_index < agent[set].values(gc).len() { // i. Let e be entries[index]. // ii. Set index to index + 1. let index = agent[iterator].next_index; agent[iterator].next_index += 1; // iii. if e is not EMPTY, then - let Some(e) = agent[set].values()[index] else { + let Some(e) = agent[set].values(gc)[index] else { continue; }; @@ -77,7 +77,7 @@ impl SetIteratorPrototype { // 1. If kind is KEY+VALUE, then // a. Let result be CreateArrayFromList(« e, e »). // b. Perform ? GeneratorYield(CreateIteratorResultObject(result, false)). - create_array_from_list(agent, &[e, e], gc).into_value() + create_array_from_list(agent, &[e.unbind(), e.unbind()], gc).into_value() } CollectionIteratorKind::Value => { // 2. Else, @@ -87,10 +87,10 @@ impl SetIteratorPrototype { } }; - return Ok(create_iter_result_object(agent, result, false, gc).into_value()); + return Ok(create_iter_result_object(agent, result.unbind(), false, gc).into_value()); } - debug_assert_eq!(agent[iterator].next_index, agent[set].values().len()); + debug_assert_eq!(agent[iterator].next_index, agent[set].values(gc).len()); // e. Return undefined. agent[iterator].set = None; diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_prototype.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_prototype.rs index 632aa30bc..26637bb29 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/set_objects/set_prototype.rs @@ -6,7 +6,7 @@ use core::hash::Hasher; use ahash::AHasher; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -88,15 +88,17 @@ impl BuiltinIntrinsic for SetPrototypeValues { impl SetPrototype { /// #### [24.2.4.1 Set.prototype.add ( value )](https://tc39.es/ecma262/#sec-set.prototype.add) - fn add( + fn add<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); + let value = arguments.get(0).bind(gc); // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let gc = gc.into_nogc(); let s = require_set_data_internal_slot(agent, this_value, gc)?; let Heap { @@ -109,7 +111,7 @@ impl SetPrototype { let primitive_heap = PrimitiveHeap::new(bigints, numbers, strings); // 3. Set value to CanonicalizeKeyedCollectionKey(value). - let value = canonicalize_keyed_collection_key(numbers, arguments.get(0)); + let value = canonicalize_keyed_collection_key(numbers, value); let SetData { values, set_data, .. @@ -137,7 +139,7 @@ impl SetPrototype { // 5. Append value to S.[[SetData]]. let index = u32::try_from(values.len()).unwrap(); entry.insert(index); - values.push(Some(value)); + values.push(Some(value.unbind())); } // i. Return S. // 6. Return S. @@ -150,15 +152,16 @@ impl SetPrototype { /// > The existing \[\[SetData]] List is preserved because there may be /// > existing Set Iterator objects that are suspended midway through /// > iterating over that List. - fn clear( + fn clear<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let gc = gc.into_nogc(); let s = require_set_data_internal_slot(agent, this_value, gc)?; // 3. For each element e of S.[[SetData]], do // a. Replace the element of S.[[SetData]] whose value is e with an @@ -175,15 +178,17 @@ impl SetPrototype { /// > The value EMPTY is used as a specification device to indicate that an /// > entry has been deleted. Actual implementations may take other actions /// > such as physically removing the entry from internal data structures. - fn delete( + fn delete<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); + let value = arguments.get(0).bind(gc); // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let gc = gc.into_nogc(); let s = require_set_data_internal_slot(agent, this_value, gc)?; let Heap { @@ -196,7 +201,7 @@ impl SetPrototype { let primitive_heap = PrimitiveHeap::new(bigints, numbers, strings); // 3. Set value to CanonicalizeKeyedCollectionKey(value). - let value = canonicalize_keyed_collection_key(numbers, arguments.get(0)); + let value = canonicalize_keyed_collection_key(numbers, value); let mut hasher = AHasher::default(); let value_hash = { value.hash(&primitive_heap, &mut hasher); @@ -226,18 +231,19 @@ impl SetPrototype { } } - fn entries( + fn entries<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); // 1. Let S be the this value. // 2. Return ? CreateSetIterator(S, KEY+VALUE). // 24.2.6.1 CreateSetIterator ( set, kind ) // 1. Perform ? RequireInternalSlot(set, [[SetData]]). - let gc = gc.into_nogc(); let s = require_set_data_internal_slot(agent, this_value, gc)?; Ok(SetIterator::from_set(agent, s, CollectionIteratorKind::KeyAndValue).into_value()) } @@ -275,40 +281,43 @@ impl SetPrototype { /// > are not visited unless the value is added again before the /// > **forEach** call completes. New values added after the call to /// > **forEach** begins are visited. - fn for_each( + fn for_each<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let callback_fn = arguments.get(0); - let this_arg = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let callback_fn = arguments.get(0).bind(nogc); + let this_arg = arguments.get(1).bind(nogc); // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let mut s = require_set_data_internal_slot(agent, this_value, gc.nogc())?; + let mut s = require_set_data_internal_slot(agent, this_value, nogc)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - let Some(callback_fn) = is_callable(callback_fn, gc.nogc()) else { + let Some(callback_fn) = is_callable(callback_fn, nogc) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Callback function is not a function", - gc.nogc(), + nogc, )); }; // 4. Let entries be S.[[SetData]]. // 5. Let numEntries be the number of elements in entries. // Note: We must use the values vector length, not the size. The size // does not contain empty slots. - let mut num_entries = agent[s].values().len() as u32; + let mut num_entries = agent[s].values(gc.nogc()).len() as u32; - let callback_fn = callback_fn.scope(agent, gc.nogc()); - let scoped_s = s.scope(agent, gc.nogc()); + let callback_fn = callback_fn.scope(agent, nogc); + let scoped_s = s.scope(agent, nogc); + let scoped_this_arg = this_arg.scope(agent, nogc); // 6. Let index be 0. let mut index = 0; // 7. Repeat, while index < numEntries, while index < num_entries { // a. Let e be entries[index]. - let e = agent[s].values()[index as usize]; + let e = agent[s].values(gc.nogc())[index as usize]; // b. Set index to index + 1. index += 1; // c. If e is not EMPTY, then @@ -317,14 +326,18 @@ impl SetPrototype { call_function( agent, callback_fn.get(agent), - this_arg, - Some(ArgumentsList(&[e, e, s.into_value()])), + scoped_this_arg.get(agent), + Some(ArgumentsList(&[ + e.unbind(), + e.unbind(), + s.into_value().unbind(), + ])), gc.reborrow(), )?; // ii. NOTE: The number of elements in entries may have increased during execution of callbackfn. // iii. Set numEntries to the number of elements in entries. s = scoped_s.get(agent).bind(gc.nogc()); - num_entries = agent[s].values().len() as u32; + num_entries = agent[s].values(gc.nogc()).len() as u32; } } // 8. Return undefined. @@ -332,15 +345,17 @@ impl SetPrototype { } /// ### [24.2.4.8 Set.prototype.has ( value )](https://tc39.es/ecma262/#sec-set.prototype.has) - fn has( + fn has<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); + let value = arguments.get(0).bind(gc); // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let gc = gc.into_nogc(); let s = require_set_data_internal_slot(agent, this_value, gc)?; let Heap { @@ -357,7 +372,7 @@ impl SetPrototype { let set_data = set_data.borrow(); // 3. Set value to CanonicalizeKeyedCollectionKey(value). - let value = canonicalize_keyed_collection_key(&primitive_heap, arguments.get(0)); + let value = canonicalize_keyed_collection_key(&primitive_heap, value); let mut hasher = AHasher::default(); let value_hash = { value.hash(&primitive_heap, &mut hasher); @@ -380,15 +395,16 @@ impl SetPrototype { /// /// Set.prototype.size is an accessor property whose set accessor function /// is undefined. - fn get_size( + fn get_size<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let gc = gc.into_nogc(); let s = require_set_data_internal_slot(agent, this_value, gc)?; // 3. Let size be SetDataSize(S.[[SetData]]). let size = agent[s].size(); @@ -396,18 +412,19 @@ impl SetPrototype { Ok(Number::from(size).into_value()) } - fn values( + fn values<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let this_value = this_value.bind(gc); // 1. Let S be the this value. // 2. Return ? CreateSetIterator(S, VALUE). // 24.2.6.1 CreateSetIterator ( set, kind ) // 1. Perform ? RequireInternalSlot(set, [[SetData]]). - let gc = gc.into_nogc(); let s = require_set_data_internal_slot(agent, this_value, gc)?; Ok(SetIterator::from_set(agent, s, CollectionIteratorKind::Value).into_value()) } @@ -466,7 +483,7 @@ fn require_set_data_internal_slot<'a>( gc: NoGcScope<'a, '_>, ) -> JsResult> { match value { - Value::Set(map) => Ok(map), + Value::Set(map) => Ok(map.bind(gc)), _ => Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Object is not a Set", diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_constructor.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_constructor.rs index e5e8b44b5..f27456050 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_constructor.rs @@ -26,13 +26,13 @@ impl BuiltinIntrinsicConstructor for WeakMapConstructor { } impl WeakMapConstructor { - fn constructor( + fn constructor<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_prototype.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_prototype.rs index 4699205ef..373c94b32 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_map_objects/weak_map_prototype.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::types::IntoValue; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -41,39 +42,39 @@ impl Builtin for WeakMapPrototypeSet { } impl WeakMapPrototype { - fn delete( + fn delete<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get( + fn get<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn has( + fn has<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn set( + fn set<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_constructor.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_constructor.rs index 6bd45b3c8..8d67fb527 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_constructor.rs @@ -26,13 +26,13 @@ impl BuiltinIntrinsicConstructor for WeakSetConstructor { } impl WeakSetConstructor { - fn constructor( + fn constructor<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_prototype.rs b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_prototype.rs index 6ec345070..b402f4e61 100644 --- a/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/keyed_collections/weak_set_objects/weak_set_prototype.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::types::IntoValue; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -35,30 +36,30 @@ impl Builtin for WeakSetPrototypeHas { } impl WeakSetPrototype { - fn add( + fn add<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn delete( + fn delete<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn has( + fn has<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_constructor.rs b/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_constructor.rs index 6bb19b6ea..7b33b2d90 100644 --- a/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_constructor.rs @@ -26,13 +26,13 @@ impl BuiltinIntrinsicConstructor for FinalizationRegistryConstructor { } impl FinalizationRegistryConstructor { - fn constructor( + fn constructor<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_prototype.rs b/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_prototype.rs index 518bd399f..2ad9a8e31 100644 --- a/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/managing_memory/finalization_registry_objects/finalization_registry_prototype.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::types::IntoValue; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -29,21 +30,21 @@ impl Builtin for FinalizationRegistryPrototypeUnregister { } impl FinalizationRegistryPrototype { - fn register( + fn register<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn unregister( + fn unregister<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_constructor.rs b/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_constructor.rs index cf86aa805..22cbbb3ef 100644 --- a/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_constructor.rs @@ -26,13 +26,13 @@ impl BuiltinIntrinsicConstructor for WeakRefConstructor { } impl WeakRefConstructor { - fn constructor( + fn constructor<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_prototype.rs b/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_prototype.rs index 1cb80851a..e67674efb 100644 --- a/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/managing_memory/weak_ref_objects/weak_ref_prototype.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::types::IntoValue; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -23,12 +24,12 @@ impl Builtin for WeakRefPrototypeDeref { } impl WeakRefPrototype { - fn deref( + fn deref<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/map.rs b/nova_vm/src/ecmascript/builtins/map.rs index 180ce9591..dc537576e 100644 --- a/nova_vm/src/ecmascript/builtins/map.rs +++ b/nova_vm/src/ecmascript/builtins/map.rs @@ -12,7 +12,7 @@ use crate::{ }, }, engine::{ - context::NoGcScope, + context::{Bindable, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, Scoped, }, @@ -68,8 +68,8 @@ impl Map<'_> { } } -impl IntoValue for Map<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Map<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -80,15 +80,15 @@ impl<'a> IntoObject<'a> for Map<'a> { } } -impl From> for Value { - fn from(val: Map) -> Self { - Value::Map(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Map<'a>) -> Self { + Value::Map(value) } } impl<'a> From> for Object<'a> { - fn from(val: Map) -> Self { - Object::Map(val.unbind()) + fn from(value: Map<'a>) -> Self { + Object::Map(value) } } diff --git a/nova_vm/src/ecmascript/builtins/map/data.rs b/nova_vm/src/ecmascript/builtins/map/data.rs index f7c74fbc5..658000559 100644 --- a/nova_vm/src/ecmascript/builtins/map/data.rs +++ b/nova_vm/src/ecmascript/builtins/map/data.rs @@ -7,6 +7,7 @@ use crate::{ bigint::HeapBigInt, HeapNumber, HeapPrimitive, HeapString, OrdinaryObject, Value, BIGINT_DISCRIMINANT, NUMBER_DISCRIMINANT, STRING_DISCRIMINANT, }, + engine::context::{Bindable, NoGcScope}, heap::{CompactionLists, HeapMarkAndSweep, PrimitiveHeapIndexable, WorkQueues}, }; use ahash::AHasher; @@ -30,8 +31,8 @@ pub struct MapHeapData { pub(crate) struct MapData { // TODO: Use a ParallelVec to remove one unnecessary allocation. // pub(crate) key_values: ParallelVec, Option> - pub(crate) keys: Vec>, - pub(crate) values: Vec>, + pub(crate) keys: Vec>>, + pub(crate) values: Vec>>, /// Low-level hash table pointing to keys-values indexes. pub(crate) map_data: RefCell>, /// Flag that lets the Map know if it needs to rehash its primitive keys. @@ -58,11 +59,11 @@ impl MapHeapData { self.map_data.map_data.borrow().len() as u32 } - pub fn keys(&self) -> &[Option] { + pub fn keys<'a>(&self, _gc: NoGcScope<'a, '_>) -> &[Option>] { &self.map_data.keys } - pub fn values(&self) -> &[Option] { + pub fn values<'a>(&self, _gc: NoGcScope<'a, '_>) -> &[Option>] { &self.map_data.values } @@ -115,7 +116,7 @@ fn rehash_map_data( ) { let hasher = |value: Value| { let mut hasher = AHasher::default(); - value.hash(arena, &mut hasher); + value.unbind().hash(arena, &mut hasher); hasher.finish() }; let hashes = { diff --git a/nova_vm/src/ecmascript/builtins/module.rs b/nova_vm/src/ecmascript/builtins/module.rs index 47bf16a63..3929f12ff 100644 --- a/nova_vm/src/ecmascript/builtins/module.rs +++ b/nova_vm/src/ecmascript/builtins/module.rs @@ -4,7 +4,7 @@ use core::ops::{Index, IndexMut}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::rootable::HeapRootData; use crate::engine::{unwrap_try, Scoped, TryResult}; use crate::{ @@ -45,8 +45,8 @@ impl<'a> From> for Module<'a> { } } -impl IntoValue for Module<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Module<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -57,15 +57,15 @@ impl<'a> IntoObject<'a> for Module<'a> { } } -impl From> for Value { - fn from(val: Module) -> Self { - Value::Module(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Module<'a>) -> Self { + Value::Module(value) } } impl<'a> From> for Object<'a> { - fn from(val: Module) -> Self { - Object::Module(val.unbind()) + fn from(value: Module<'a>) -> Self { + Object::Module(value) } } @@ -230,7 +230,9 @@ impl<'a> InternalMethods<'a> for Module<'a> { TryResult::Continue(None) } else { // 4. Let value be ? O.[[Get]](P, O). - let value = self.try_get(agent, property_key, self.into_value(), gc)?; + let value = self + .try_get(agent, property_key, self.into_value(), gc)? + .unbind(); // 5. Return PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }. TryResult::Continue(Some(PropertyDescriptor { value: Some(value), @@ -273,8 +275,9 @@ impl<'a> InternalMethods<'a> for Module<'a> { Ok(None) } else { // 4. Let value be ? O.[[Get]](P, O). - let value = - self.internal_get(agent, property_key.unbind(), self.into_value(), gc)?; + let value = self + .internal_get(agent, property_key.unbind(), self.into_value(), gc)? + .unbind(); // 5. Return PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }. Ok(Some(PropertyDescriptor { value: Some(value), @@ -436,13 +439,13 @@ impl<'a> InternalMethods<'a> for Module<'a> { } /// ### [10.4.6.8 \[\[Get\]\] ( P, Receiver )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver) - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { // NOTE: ResolveExport is side-effect free. Each time this operation // is called with a specific exportName, resolveSet pair as arguments // it must return the same result. An implementation might choose to @@ -510,13 +513,13 @@ impl<'a> InternalMethods<'a> for Module<'a> { } /// ### [10.4.6.8 \[\[Get\]\] ( P, Receiver )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver) - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let property_key = property_key.bind(gc.nogc()); // NOTE: ResolveExport is side-effect free. Each time this operation @@ -537,7 +540,8 @@ impl<'a> InternalMethods<'a> for Module<'a> { receiver, gc.reborrow(), ) - .unwrap(), + .unwrap() + .unbind(), None => Value::Undefined, }) } diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_constructor.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_constructor.rs index 61df0d03a..b8e226da7 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_constructor.rs @@ -29,7 +29,7 @@ use crate::ecmascript::types::Object; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; use crate::ecmascript::types::{String, Value}; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::heap::CreateHeapData; use crate::heap::IntrinsicConstructorIndexes; use crate::SmallInteger; @@ -60,13 +60,13 @@ impl Builtin for BigIntAsUintN { } impl BigIntConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { if new_target.is_some() { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -87,16 +87,16 @@ impl BigIntConstructor { Ok(BigInt::from_i64(agent, prim.into_i64(agent)).into_value()) } else { - to_big_int(agent, value, gc.reborrow()).map(|result| result.into_value()) + to_big_int(agent, value, gc.reborrow()).map(|result| result.into_value().unbind()) } } - fn as_int_n( + fn as_int_n<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let bits = to_index(agent, arguments.get(0), gc.reborrow())?; let Ok(bits) = u32::try_from(bits) else { return Err(agent.throw_exception_with_static_message( @@ -162,19 +162,19 @@ impl BigIntConstructor { BigInt::SmallBigInt(_) => { // Probably safe: The divisor is bigger than i64 but // value is i54. - Ok(bigint.into_value()) + Ok(bigint.into_value().unbind()) } } } } } - fn as_uint_n( + fn as_uint_n<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let bits = to_index(agent, arguments.get(0), gc.reborrow())?; let Ok(bits) = u32::try_from(bits) else { return Err(agent.throw_exception_with_static_message( @@ -210,13 +210,14 @@ impl BigIntConstructor { fn number_to_big_int<'a>( agent: &mut Agent, value: Number<'a>, - mut gc: GcScope<'a, '_>, + gc: GcScope<'a, '_>, ) -> JsResult> { - if !is_integral_number(agent, value, gc.reborrow()) { + let gc = gc.into_nogc(); + if !is_integral_number(agent, value) { Err(agent.throw_exception_with_static_message( ExceptionType::RangeError, "Not an integer", - gc.nogc(), + gc, )) } else { match value { diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_prototype.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_prototype.rs index e99860ef4..c12640ff2 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/bigint_objects/bigint_prototype.rs @@ -2,7 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::{GcScope, NoGcScope}; +use crate::ecmascript::builtins::Behaviour; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -21,8 +22,7 @@ impl Builtin for BigIntPrototypeToLocaleString { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(BigIntPrototype::to_locale_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(BigIntPrototype::to_locale_string); } struct BigIntPrototypeToString; @@ -31,8 +31,7 @@ impl Builtin for BigIntPrototypeToString { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(BigIntPrototype::to_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(BigIntPrototype::to_string); } struct BigIntPrototypeValueOf; @@ -41,26 +40,25 @@ impl Builtin for BigIntPrototypeValueOf { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(BigIntPrototype::value_of); + const BEHAVIOUR: Behaviour = Behaviour::Regular(BigIntPrototype::value_of); } impl BigIntPrototype { - fn to_locale_string( + fn to_locale_string<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::to_string(agent, this_value, arguments, gc) } - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _x = this_big_int_value(agent, this_value, gc.nogc())?; let radix = arguments.get(0); if radix.is_undefined() || radix == Value::from(10u8) { @@ -71,13 +69,13 @@ impl BigIntPrototype { } } - fn value_of( + fn value_of<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { - this_big_int_value(agent, this_value, gc.nogc()).map(|result| result.into_value()) + gc: GcScope<'gc, '_>, + ) -> JsResult> { + this_big_int_value(agent, this_value, gc.nogc()).map(|result| result.into_value().unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -117,7 +115,7 @@ fn this_big_int_value<'a>( ) -> JsResult> { match value { // 1. If value is a BigInt, return value. - Value::BigInt(value) => Ok(value.into()), + Value::BigInt(value) => Ok(value.unbind().into()), Value::SmallBigInt(value) => Ok(value.into()), // 2. If value is an Object and value has a [[BigIntData]] internal slot, then Value::PrimitiveObject(value) if value.is_bigint_object(agent) => { diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_constructor.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_constructor.rs index cccc7fb40..4a0310763 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_constructor.rs @@ -23,6 +23,7 @@ use crate::ecmascript::types::Number; use crate::ecmascript::types::Object; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; use crate::ecmascript::types::{String, Value}; +use crate::engine::context::Bindable; use crate::engine::context::GcScope; use crate::heap::IntrinsicConstructorIndexes; use crate::SmallInteger; @@ -57,13 +58,13 @@ impl Builtin for DateUTC { const NAME: String<'static> = BUILTIN_STRING_MEMORY.utc; } impl DateConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. If NewTarget is undefined, then let Some(new_target) = new_target else { // a. Let now be the time value (UTC) identifying the current time. @@ -121,16 +122,16 @@ impl DateConstructor { // 7. Set O.[[DateValue]] to dv. agent[Date::try_from(o).unwrap()].date = Some(dv); // 8. Return O. - Ok(o.into_value()) + Ok(o.unbind().into_value()) } /// ### [21.1.2.2 Number.isFinite ( number )](https://tc39.es/ecma262/#sec-number.isfinite) - fn now( + fn now<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { let time_value = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() @@ -144,22 +145,22 @@ impl DateConstructor { } /// ### [21.1.2.3 Number.isInteger ( number )](https://tc39.es/ecma262/#sec-number.isinteger) - fn parse( + fn parse<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } /// ### [21.4.3.4 Date.UTC ( year \[ , month \[ , date \[ , hours \[ , minutes \[ , seconds \[ , ms \] \] \] \] \] \] )](https://tc39.es/ecma262/#sec-date.utc) - fn utc( + fn utc<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let _ns = arguments.get(0); // 1. Let y be ? ToNumber(year). let _y = to_number(agent, arguments.get(0), gc.reborrow())?; diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_prototype.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_prototype.rs index ca0613697..143c19fbe 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/date_objects/date_prototype.rs @@ -4,7 +4,7 @@ use std::time::SystemTime; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::{ordinary_to_primitive, PreferredType}, @@ -289,8 +289,7 @@ impl Builtin for DatePrototypeToPrimitive { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(DatePrototype::to_primitive); + const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_primitive); const WRITABLE: bool = false; } @@ -298,428 +297,428 @@ impl Builtin for DatePrototypeToPrimitive { const MAX_SYSTEM_TIME_VALUE: u128 = SmallInteger::MAX_NUMBER as u128; impl DatePrototype { - fn get_date( + fn get_date<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_day( + fn get_day<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_full_year( + fn get_full_year<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_hours( + fn get_hours<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_milliseconds( + fn get_milliseconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_minutes( + fn get_minutes<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_month( + fn get_month<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_seconds( + fn get_seconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_time( + fn get_time<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_timezone_offset( + fn get_timezone_offset<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_date( + fn get_utc_date<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_day( + fn get_utc_day<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_full_year( + fn get_utc_full_year<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_hours( + fn get_utc_hours<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_milliseconds( + fn get_utc_milliseconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_minutes( + fn get_utc_minutes<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_month( + fn get_utc_month<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn get_utc_seconds( + fn get_utc_seconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_date( + fn set_date<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_full_year( + fn set_full_year<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_hours( + fn set_hours<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_milliseconds( + fn set_milliseconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_minutes( + fn set_minutes<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_month( + fn set_month<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_seconds( + fn set_seconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_time( + fn set_time<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_utc_date( + fn set_utc_date<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_utc_full_year( + fn set_utc_full_year<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_utc_hours( + fn set_utc_hours<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_utc_milliseconds( + fn set_utc_milliseconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_utc_minutes( + fn set_utc_minutes<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_utc_month( + fn set_utc_month<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn set_utc_seconds( + fn set_utc_seconds<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn to_date_string( + fn to_date_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn to_iso_string( + fn to_iso_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn to_json( + fn to_json<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn to_locale_date_string( + fn to_locale_date_string<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn to_locale_string( + fn to_locale_string<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn to_locale_time_string( + fn to_locale_time_string<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn to_time_string( + fn to_time_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn to_utc_string( + fn to_utc_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let _date_object = check_date_object(agent, this_value, gc.nogc())?; todo!() } - fn value_of( + fn value_of<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let date_object = check_date_object(agent, this_value, gc.nogc())?; let data = &agent[date_object].date; match data { @@ -769,12 +768,12 @@ impl DatePrototype { /// object in that they treat "default" as being equivalent to "string". /// All other built-in ECMAScript objects treat "default" as being /// equivalent to "number". - fn to_primitive( + fn to_primitive<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let hint = arguments.get(0); // 1. Let O be the this value. // 2. If O is not an Object, throw a TypeError exception. @@ -805,7 +804,10 @@ impl DatePrototype { return Err(agent.throw_exception(ExceptionType::TypeError, error_message, gc.nogc())); }; // 6. Return ? OrdinaryToPrimitive(O, tryFirst). - ordinary_to_primitive(agent, o, try_first, gc.reborrow()).map(|result| result.into_value()) + Ok(ordinary_to_primitive(agent, o, try_first, gc.reborrow())? + .into_value() + .unbind() + .bind(gc.into_nogc())) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -873,7 +875,7 @@ fn check_date_object<'a>( gc: NoGcScope<'a, '_>, ) -> JsResult> { match this_value { - Value::Date(date) => Ok(date), + Value::Date(date) => Ok(date.bind(gc)), _ => Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "this is not a Date object.", diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/math_object.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/math_object.rs index bde6c05b8..f49e5656b 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/math_object.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/math_object.rs @@ -8,11 +8,11 @@ use crate::{ ecmascript::{ abstract_operations::type_conversion::{to_number, to_number_primitive, to_uint32}, builders::ordinary_object_builder::OrdinaryObjectBuilder, - builtins::{ArgumentsList, Builtin}, + builtins::{ArgumentsList, Behaviour, Builtin}, execution::{Agent, JsResult, RealmIdentifier}, types::{IntoValue, Number, Primitive, String, Value, BUILTIN_STRING_MEMORY}, }, - engine::context::{GcScope, NoGcScope}, + engine::context::{Bindable, GcScope, NoGcScope}, heap::WellKnownSymbolIndexes, }; @@ -33,8 +33,7 @@ impl Builtin for MathObjectAbs { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::abs); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::abs); } struct MathObjectAcos; @@ -43,8 +42,7 @@ impl Builtin for MathObjectAcos { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::acos); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::acos); } struct MathObjectAcosh; impl Builtin for MathObjectAcosh { @@ -52,8 +50,7 @@ impl Builtin for MathObjectAcosh { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::acosh); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::acosh); } struct MathObjectAsin; impl Builtin for MathObjectAsin { @@ -61,8 +58,7 @@ impl Builtin for MathObjectAsin { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::asin); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::asin); } struct MathObjectAsinh; impl Builtin for MathObjectAsinh { @@ -70,8 +66,7 @@ impl Builtin for MathObjectAsinh { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::asinh); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::asinh); } struct MathObjectAtan; impl Builtin for MathObjectAtan { @@ -79,8 +74,7 @@ impl Builtin for MathObjectAtan { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::atan); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::atan); } struct MathObjectAtanh; impl Builtin for MathObjectAtanh { @@ -88,8 +82,7 @@ impl Builtin for MathObjectAtanh { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::atanh); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::atanh); } struct MathObjectAtan2; impl Builtin for MathObjectAtan2 { @@ -97,8 +90,7 @@ impl Builtin for MathObjectAtan2 { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::atan2); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::atan2); } struct MathObjectCbrt; impl Builtin for MathObjectCbrt { @@ -106,8 +98,7 @@ impl Builtin for MathObjectCbrt { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::cbrt); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::cbrt); } struct MathObjectCeil; impl Builtin for MathObjectCeil { @@ -115,8 +106,7 @@ impl Builtin for MathObjectCeil { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::ceil); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::ceil); } struct MathObjectClz32; impl Builtin for MathObjectClz32 { @@ -124,8 +114,7 @@ impl Builtin for MathObjectClz32 { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::clz32); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::clz32); } struct MathObjectCos; impl Builtin for MathObjectCos { @@ -133,8 +122,7 @@ impl Builtin for MathObjectCos { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::cos); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::cos); } struct MathObjectCosh; impl Builtin for MathObjectCosh { @@ -142,8 +130,7 @@ impl Builtin for MathObjectCosh { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::cosh); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::cosh); } struct MathObjectExp; impl Builtin for MathObjectExp { @@ -151,8 +138,7 @@ impl Builtin for MathObjectExp { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::exp); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::exp); } struct MathObjectExpm1; impl Builtin for MathObjectExpm1 { @@ -160,8 +146,7 @@ impl Builtin for MathObjectExpm1 { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::expm1); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::expm1); } struct MathObjectFloor; impl Builtin for MathObjectFloor { @@ -169,8 +154,7 @@ impl Builtin for MathObjectFloor { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::floor); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::floor); } struct MathObjectFround; impl Builtin for MathObjectFround { @@ -178,8 +162,7 @@ impl Builtin for MathObjectFround { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::fround); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::fround); } struct MathObjectHypot; impl Builtin for MathObjectHypot { @@ -187,8 +170,7 @@ impl Builtin for MathObjectHypot { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::hypot); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::hypot); } struct MathObjectImul; impl Builtin for MathObjectImul { @@ -196,8 +178,7 @@ impl Builtin for MathObjectImul { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::imul); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::imul); } struct MathObjectLog; impl Builtin for MathObjectLog { @@ -205,8 +186,7 @@ impl Builtin for MathObjectLog { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::log); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::log); } struct MathObjectLog1p; impl Builtin for MathObjectLog1p { @@ -214,8 +194,7 @@ impl Builtin for MathObjectLog1p { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::log1p); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::log1p); } struct MathObjectLog10; impl Builtin for MathObjectLog10 { @@ -223,8 +202,7 @@ impl Builtin for MathObjectLog10 { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::log10); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::log10); } struct MathObjectLog2; impl Builtin for MathObjectLog2 { @@ -232,8 +210,7 @@ impl Builtin for MathObjectLog2 { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::log2); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::log2); } struct MathObjectMax; impl Builtin for MathObjectMax { @@ -241,8 +218,7 @@ impl Builtin for MathObjectMax { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::max); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::max); } struct MathObjectMin; impl Builtin for MathObjectMin { @@ -250,8 +226,7 @@ impl Builtin for MathObjectMin { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::min); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::min); } struct MathObjectPow; impl Builtin for MathObjectPow { @@ -259,8 +234,7 @@ impl Builtin for MathObjectPow { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::pow); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::pow); } struct MathObjectRandom; impl Builtin for MathObjectRandom { @@ -268,8 +242,7 @@ impl Builtin for MathObjectRandom { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::random); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::random); } struct MathObjectRound; impl Builtin for MathObjectRound { @@ -277,8 +250,7 @@ impl Builtin for MathObjectRound { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::round); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::round); } struct MathObjectSign; impl Builtin for MathObjectSign { @@ -286,8 +258,7 @@ impl Builtin for MathObjectSign { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::sign); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::sign); } struct MathObjectSin; impl Builtin for MathObjectSin { @@ -295,8 +266,7 @@ impl Builtin for MathObjectSin { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::sin); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::sin); } struct MathObjectSinh; impl Builtin for MathObjectSinh { @@ -304,8 +274,7 @@ impl Builtin for MathObjectSinh { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::sinh); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::sinh); } struct MathObjectSqrt; impl Builtin for MathObjectSqrt { @@ -313,8 +282,7 @@ impl Builtin for MathObjectSqrt { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::sqrt); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::sqrt); } struct MathObjectTan; impl Builtin for MathObjectTan { @@ -322,8 +290,7 @@ impl Builtin for MathObjectTan { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::tan); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::tan); } struct MathObjectTanh; impl Builtin for MathObjectTanh { @@ -331,8 +298,7 @@ impl Builtin for MathObjectTanh { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::tanh); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::tanh); } struct MathObjectTrunc; impl Builtin for MathObjectTrunc { @@ -340,8 +306,7 @@ impl Builtin for MathObjectTrunc { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::trunc); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::trunc); } #[cfg(feature = "proposal-float16array")] @@ -352,8 +317,7 @@ impl Builtin for MathObjectF16round { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::f16round); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::f16round); } #[cfg(feature = "proposal-math-sum")] @@ -364,27 +328,26 @@ impl Builtin for MathObjectSumPrecise { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(MathObject::sum_precise); + const BEHAVIOUR: Behaviour = Behaviour::Regular(MathObject::sum_precise); } impl MathObject { - fn abs( + fn abs<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let n = to_number(agent, arguments.get(0), gc.reborrow())?; - Ok(n.abs(agent).into_value()) + Ok(n.abs(agent).into_value().unbind()) } - fn acos( + fn acos<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?.into_f64(agent); // 2. If n is NaN, n > 1𝔽, or n < -1𝔽, return NaN. @@ -393,18 +356,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.acos(), gc.into_nogc())) } - fn acosh( + fn acosh<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; // 2. If n is either NaN or +∞𝔽, return n. if n.is_nan(agent) || n.is_pos_infinity(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 3. If n is 1𝔽, return +0𝔽. @@ -419,18 +382,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.acosh(), gc.into_nogc())) } - fn asin( + fn asin<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n. if n.is_nan(agent) || n.is_pos_zero(agent) || n.is_neg_zero(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } let n = n.into_f64(agent); @@ -444,18 +407,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.asin(), gc.into_nogc())) } - fn asinh( + fn asinh<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n. if !n.is_finite(agent) || n.is_pos_zero(agent) || n.is_neg_zero(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 3. Return an implementation-approximated Number value representing the result of the inverse hyperbolic sine of ℝ(n). @@ -463,18 +426,18 @@ impl MathObject { Ok(Value::from_f64(agent, result, gc.into_nogc())) } - fn atan( + fn atan<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n. if n.is_nan(agent) || n.is_pos_zero(agent) || n.is_neg_zero(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 3. If n is +∞𝔽, return an implementation-approximated Number value representing π / 2. @@ -492,18 +455,18 @@ impl MathObject { Ok(Value::from_f64(agent, result, gc.into_nogc())) } - fn atanh( + fn atanh<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n. if n.is_nan(agent) || n.is_pos_zero(agent) || n.is_neg_zero(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 4. If n is 1𝔽, return +∞𝔽. @@ -527,12 +490,12 @@ impl MathObject { Ok(Value::from_f64(agent, n.atanh(), gc.into_nogc())) } - fn atan2( + fn atan2<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let ny be ? ToNumber(y). let ny = to_number(agent, arguments.get(0), gc.reborrow())?.into_f64(agent); // 2. Let nx be ? ToNumber(x). @@ -670,12 +633,12 @@ impl MathObject { Ok(Value::from_f64(agent, r, gc.into_nogc())) } - fn cbrt( + fn cbrt<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?.unbind(); let gc = gc.into_nogc(); @@ -690,16 +653,16 @@ impl MathObject { Ok(Value::from_f64(agent, n.into_f64(agent).cbrt(), gc)) } - fn ceil( + fn ceil<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let n = arguments.get(0); // 4. If n is an integral Number, return n. if n.is_integer() { - return Ok(n); + return Ok(n.unbind()); } // 1. Let n be ? ToNumber(x). @@ -707,12 +670,12 @@ impl MathObject { // 4. If n is an integral Number, return n. if n.is_integer(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n. if !n.is_finite(agent) || n.is_pos_zero(agent) || n.is_neg_zero(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } let n = n.into_f64(agent); @@ -726,12 +689,12 @@ impl MathObject { Ok(Value::from_f64(agent, n.ceil(), gc.into_nogc())) } - fn clz32( + fn clz32<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToUint32(x). let n = to_uint32(agent, arguments.get(0), gc.reborrow())?; @@ -742,12 +705,12 @@ impl MathObject { Ok(Value::from(p)) } - fn cos( + fn cos<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?.into_f64(agent); @@ -765,12 +728,12 @@ impl MathObject { Ok(Value::from_f64(agent, n.cos(), gc.into_nogc())) } - fn cosh( + fn cosh<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?.into_f64(agent); @@ -793,18 +756,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.cosh(), gc.into_nogc())) } - fn exp( + fn exp<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { //1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; //2. If n is either NaN or +∞𝔽, return n. if n.is_nan(agent) || n.is_pos_infinity(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } let n = n.into_f64(agent); @@ -823,12 +786,12 @@ impl MathObject { Ok(Value::from_f64(agent, n.exp(), gc.into_nogc())) } - fn expm1( + fn expm1<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; @@ -838,7 +801,7 @@ impl MathObject { || n.is_neg_zero(agent) || n.is_pos_infinity(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } let n = n.into_f64(agent); @@ -852,17 +815,17 @@ impl MathObject { Ok(Value::from_f64(agent, n.exp_m1(), gc.into_nogc())) } - fn floor( + fn floor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let n = arguments.get(0); // 4. If n is an integral Number, return n. if n.is_integer() { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 1. Let n be ? ToNumber(x). @@ -870,12 +833,12 @@ impl MathObject { // 4. If n is an integral Number, return n. if n.is_integer(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n. if !n.is_finite(agent) || n.is_pos_zero(agent) || n.is_neg_zero(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } let n = n.into_f64(agent); @@ -889,12 +852,12 @@ impl MathObject { Ok(Value::from_f64(agent, n.floor(), gc.into_nogc())) } - fn fround( + fn fround<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; @@ -909,7 +872,7 @@ impl MathObject { || n.is_pos_infinity(agent) || n.is_neg_infinity(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 4. Let n32 be the result of converting n to IEEE 754-2019 binary32 format using roundTiesToEven mode. @@ -922,12 +885,12 @@ impl MathObject { Ok(Value::from_f64(agent, n64, gc.into_nogc())) } - fn hypot( + fn hypot<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let coerced be a new empty List. // 2. For each element arg of args, do @@ -980,12 +943,12 @@ impl MathObject { )) } - fn imul( + fn imul<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let a be ℝ(? ToUint32(x)). let a = to_uint32(agent, arguments.get(0), gc.reborrow())?; @@ -999,19 +962,19 @@ impl MathObject { Ok(Value::from(product as i32)) } - fn log( + fn log<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is either NaN or +∞𝔽, return n. if n.is_nan() || n == f64::INFINITY { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n is 1𝔽, return +0𝔽. @@ -1033,18 +996,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.ln(), gc.into_nogc())) } - fn log1p( + fn log1p<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is one of NaN, +0𝔽, -0𝔽, or +∞𝔽, return n. if n.is_nan() || n == 0.0 || n == f64::INFINITY { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n is -1𝔽, return -∞𝔽. if n == -1.0 { @@ -1058,18 +1021,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.ln_1p(), gc.into_nogc())) } - fn log10( + fn log10<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is either NaN or +∞𝔽, return n. if n.is_nan() || n == f64::INFINITY { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n is 1𝔽, return +0𝔽. if n == 1.0 { @@ -1088,18 +1051,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.log10(), gc.into_nogc())) } - fn log2( + fn log2<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is either NaN or +∞𝔽, return n. if n.is_nan() || n == f64::INFINITY { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n is 1𝔽, return +0𝔽. if n == 1.0 { @@ -1117,12 +1080,12 @@ impl MathObject { Ok(Value::from_f64(agent, n.log2(), gc.into_nogc())) } - fn max( + fn max<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { if arguments.is_empty() { return Ok(Value::neg_inf()); } @@ -1212,16 +1175,16 @@ impl MathObject { highest_f64 = (highest_i64 as f64).max(highest_f64); } let result = Number::from_f64(agent, highest_f64, gc.nogc()); - Ok(result.into_value()) + Ok(result.into_value().unbind()) } } - fn min( + fn min<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { if arguments.is_empty() { return Ok(Value::pos_inf()); } @@ -1313,16 +1276,18 @@ impl MathObject { lowest_f64 = lowest_f64.min(lowest_i64 as f64); } } - Ok(Number::from_f64(agent, lowest_f64, gc.nogc()).into_value()) + Ok(Number::from_f64(agent, lowest_f64, gc.nogc()) + .into_value() + .unbind()) } } - fn pow( + fn pow<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let base = arguments.get(0); let exponent = arguments.get(1); let (base, exponent) = if let (Ok(base), Ok(exponent)) = @@ -1352,34 +1317,34 @@ impl MathObject { if let Ok(result) = Number::try_from(result) { return Ok(result.into_value()); } else { - return Ok(Value::from_f64(agent, result as f64, gc)); + return Ok(Value::from_f64(agent, result as f64, gc).unbind()); } } else if let Some(result) = (base as i128).checked_pow(exponent) { - return Ok(Value::from_f64(agent, result as f64, gc)); + return Ok(Value::from_f64(agent, result as f64, gc).unbind()); } else { - return Ok(Value::from_f64( - agent, - (base as f64).powf(exponent as f64), - gc, - )); + return Ok( + Value::from_f64(agent, (base as f64).powf(exponent as f64), gc).unbind(), + ); } } else if let Ok(exponent) = i32::try_from(exponent) { let result = (base as f64).powi(exponent); - return Ok(Value::from_f64(agent, result, gc)); + return Ok(Value::from_f64(agent, result, gc).unbind()); } else { let result = (base as f64).powf(exponent as f64); - return Ok(Value::from_f64(agent, result, gc)); + return Ok(Value::from_f64(agent, result, gc).unbind()); } } - Ok(Number::exponentiate(agent, base, exponent).into_value()) + Ok(Number::exponentiate(agent, base, exponent) + .into_value() + .unbind()) } - fn random( + fn random<'gc>( agent: &mut Agent, _this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Ok(Value::from_f64( agent, rand::random::(), @@ -1387,22 +1352,22 @@ impl MathObject { )) } - fn round( + fn round<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let n = arguments.get(0); if n.is_integer() { - return Ok(n); + return Ok(n.unbind()); } // 1. Let n be ? ToNumber(x). let n = to_number(agent, n, gc.reborrow())?; // 2. If n is not finite or n is an integral Number, return n. if !n.is_finite(agent) || n.is_integer(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } let n = n.into_f64(agent); @@ -1421,17 +1386,17 @@ impl MathObject { Ok(Value::from_f64(agent, n.round(), gc.into_nogc())) } - fn sign( + fn sign<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n. if n.is_nan(agent) || n.is_pos_zero(agent) || n.is_neg_zero(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 3. If n < -0𝔽, return -1𝔽. if n.is_sign_negative(agent) { @@ -1441,18 +1406,18 @@ impl MathObject { Ok(Value::from(1)) } - fn sin( + fn sin<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n. if n.is_nan() || n == 0.0 { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n is either +∞𝔽 or -∞𝔽, return NaN. if n.is_infinite() { @@ -1462,35 +1427,35 @@ impl MathObject { Ok(Value::from_f64(agent, n.sin(), gc.into_nogc())) } - fn sinh( + fn sinh<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n. if !n.is_finite() || n == 0.0 { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. Return an implementation-approximated Number value representing the hyperbolic sine of ℝ(n). Ok(Value::from_f64(agent, n.sinh(), gc.into_nogc())) } - fn sqrt( + fn sqrt<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is one of NaN, +0𝔽, -0𝔽, or +∞𝔽, return n. if n.is_nan() || n == 0.0 || n == f64::INFINITY { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n < -0𝔽, return NaN. if n < -0.0 { @@ -1500,18 +1465,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.sqrt(), gc.into_nogc())) } - fn tan( + fn tan<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n. if n.is_nan() || n == 0.0 { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n is either +∞𝔽 or -∞𝔽, return NaN. if n.is_infinite() { @@ -1521,18 +1486,18 @@ impl MathObject { Ok(Value::from_f64(agent, n.tan(), gc.into_nogc())) } - fn tanh( + fn tanh<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n. if n.is_nan() || n == 0.0 { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n is +∞𝔽, return 1𝔽. if n == f64::INFINITY { @@ -1546,19 +1511,19 @@ impl MathObject { Ok(Value::from_f64(agent, n.tanh(), gc.into_nogc())) } - fn trunc( + fn trunc<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n_number = to_number(agent, arguments.get(0), gc.reborrow())?; let n = n_number.into_f64(agent); // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n. if !n.is_finite() || n == 0.0 { - return Ok(n_number.into_value()); + return Ok(n_number.into_value().unbind()); } // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽. @@ -1596,12 +1561,12 @@ impl MathObject { /// > adjusted value to binary16 under roundTiesToEven then produces the /// > correct value. #[cfg(feature = "proposal-float16array")] - fn f16round( + fn f16round<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let n be ? ToNumber(x). let n = to_number(agent, arguments.get(0), gc.reborrow())?; @@ -1616,7 +1581,7 @@ impl MathObject { || n.is_pos_infinity(agent) || n.is_neg_infinity(agent) { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); } // 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format using roundTiesToEven mode. @@ -1644,12 +1609,12 @@ impl MathObject { /// > recent algorithm is given in "Fast exact summation using small and large superaccumulators", /// > code for which is available at https://gitlab.com/radfordneal/xsum. #[cfg(feature = "proposal-math-sum")] - fn sum_precise( + fn sum_precise<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let items = arguments.get(0); // 1. Perform ? RequireObjectCoercible(items). @@ -1737,10 +1702,11 @@ impl MathObject { // 10. If state is minus-infinity, return -∞𝔽. // 11. If state is minus-zero, return -0𝔽. if state.is_nan() || state.is_infinite() || state == -0.0 { - return Ok(Value::from_f64(agent, state, gc.into_nogc())); + Ok(Value::from_f64(agent, state, gc.into_nogc())) + } else { + // 12. Return 𝔽(sum). + Ok(Value::from_f64(agent, sum, gc.into_nogc())) } - // 12. Return 𝔽(sum). - Ok(Value::from_f64(agent, sum, gc.into_nogc())) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier, gc: NoGcScope) { @@ -1763,7 +1729,7 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.E.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::E, gc).into_value() + Number::from_f64(agent, consts::E, gc).into_value().unbind() }) .with_enumerable(false) .with_configurable(false) @@ -1773,7 +1739,9 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.LN10.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::LN_10, gc).into_value() + Number::from_f64(agent, consts::LN_10, gc) + .into_value() + .unbind() }) .with_enumerable(false) .with_configurable(false) @@ -1783,7 +1751,9 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.LN2.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::LN_2, gc).into_value() + Number::from_f64(agent, consts::LN_2, gc) + .into_value() + .unbind() }) .with_enumerable(false) .with_configurable(false) @@ -1793,7 +1763,9 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.LOG10E.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::LOG10_E, gc).into_value() + Number::from_f64(agent, consts::LOG10_E, gc) + .into_value() + .unbind() }) .with_enumerable(false) .with_configurable(false) @@ -1803,7 +1775,9 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.LOG2E.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::LOG2_E, gc).into_value() + Number::from_f64(agent, consts::LOG2_E, gc) + .into_value() + .unbind() }) .with_enumerable(false) .with_configurable(false) @@ -1813,7 +1787,9 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.PI.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::PI, gc).into_value() + Number::from_f64(agent, consts::PI, gc) + .into_value() + .unbind() }) .with_enumerable(false) .with_configurable(false) @@ -1823,7 +1799,9 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.SQRT1_2.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::FRAC_1_SQRT_2, gc).into_value() + Number::from_f64(agent, consts::FRAC_1_SQRT_2, gc) + .into_value() + .unbind() }) .with_enumerable(false) .with_configurable(false) @@ -1833,7 +1811,9 @@ impl MathObject { builder .with_key(BUILTIN_STRING_MEMORY.SQRT2.into()) .with_value_creator_readonly(|agent| { - Number::from_f64(agent, consts::SQRT_2, gc).into_value() + Number::from_f64(agent, consts::SQRT_2, gc) + .into_value() + .unbind() }) .with_enumerable(false) .with_configurable(false) diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs index 172d2ffd3..63869a3e3 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs @@ -3,6 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::testing_and_comparison::is_integral_number; +use crate::ecmascript::abstract_operations::type_conversion::to_numeric_primitive; use crate::ecmascript::builders::builtin_function_builder::BuiltinFunctionBuilder; use crate::ecmascript::builtins::ordinary::ordinary_create_from_constructor; use crate::ecmascript::builtins::primitive_objects::PrimitiveObject; @@ -24,10 +25,11 @@ use crate::ecmascript::types::Number; use crate::ecmascript::types::Numeric; use crate::ecmascript::types::Object; +use crate::ecmascript::types::Primitive; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; use crate::ecmascript::types::{String, Value}; -use crate::engine::context::GcScope; use crate::engine::context::NoGcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::heap::CreateHeapData; use crate::heap::IntrinsicConstructorIndexes; use crate::SmallInteger; @@ -70,22 +72,32 @@ impl Builtin for NumberIsSafeInteger { } impl NumberConstructor { - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { - let value = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let value = arguments.get(0).bind(nogc); + let mut new_target = new_target.map(|n| n.bind(nogc)); // 1. If value is present, then let n = if !value.is_undefined() { // a. Let prim be ? ToNumeric(value). - let prim = value - .to_numeric(agent, gc.reborrow())? - .unbind() - .bind(gc.nogc()); + let prim = if let Ok(prim) = Primitive::try_from(value) { + to_numeric_primitive(agent, prim, nogc)? + } else { + let scoped_new_target = new_target.map(|n| n.scope(agent, nogc)); + let prim = value + .unbind() + .to_numeric(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()); + new_target = scoped_new_target.map(|n| n.get(agent)); + prim + }; // b. If prim is a BigInt, let n be 𝔽(ℝ(prim)). match prim { @@ -113,7 +125,7 @@ impl NumberConstructor { // 3. If NewTarget is undefined, return n. let Some(new_target) = new_target else { - return Ok(n.into_value()); + return Ok(n.into_value().unbind()); }; let n = n.scope(agent, gc.nogc()); @@ -123,7 +135,7 @@ impl NumberConstructor { // 4. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Number.prototype%", « [[NumberData]] »). let o = PrimitiveObject::try_from(ordinary_create_from_constructor( agent, - new_target, + new_target.unbind(), ProtoIntrinsics::Number, gc.reborrow(), )?) @@ -136,17 +148,18 @@ impl NumberConstructor { Number::SmallF64(d) => PrimitiveObjectData::Float(d), }; // 6. Return O. - Ok(o.into_value()) + Ok(o.unbind().into_value()) } /// ### [21.1.2.2 Number.isFinite ( number )](https://tc39.es/ecma262/#sec-number.isfinite) - fn is_finite( + fn is_finite<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - let maybe_number = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let maybe_number = arguments.get(0).bind(gc); // 1. If number is not a Number, return false. let Ok(number) = Number::try_from(maybe_number) else { @@ -159,26 +172,28 @@ impl NumberConstructor { } /// ### [21.1.2.3 Number.isInteger ( number )](https://tc39.es/ecma262/#sec-number.isinteger) - fn is_integer( + fn is_integer<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let maybe_number = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let maybe_number = arguments.get(0).bind(gc); // 1. Return IsIntegralNumber(number). - Ok(is_integral_number(agent, maybe_number, gc.reborrow()).into()) + Ok(is_integral_number(agent, maybe_number).into()) } /// ### [21.1.2.4 Number.isNaN ( number )](https://tc39.es/ecma262/#sec-number.isnan) - fn is_nan( + fn is_nan<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - let maybe_number = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let maybe_number = arguments.get(0).bind(gc); // 1. If number is not a Number, return false. let Ok(number) = Number::try_from(maybe_number) else { @@ -191,13 +206,14 @@ impl NumberConstructor { } /// ### [21.1.2.5 Number.isSafeInteger ( number )](https://tc39.es/ecma262/#sec-number.issafeinteger) - fn is_safe_integer( + fn is_safe_integer<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - let maybe_number = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let gc = gc.into_nogc(); + let maybe_number = arguments.get(0).bind(gc); // 1. If IsIntegralNumber(number) is true, then // a. If abs(ℝ(number)) ≤ 2**53 - 1, return true. @@ -221,7 +237,7 @@ impl NumberConstructor { let value = Value::from_f64(builder.agent, f64::EPSILON, gc); builder .with_key(BUILTIN_STRING_MEMORY.EPSILON.into()) - .with_value_readonly(value) + .with_value_readonly(value.unbind()) .with_enumerable(false) .with_configurable(false) .build() diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_prototype.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_prototype.rs index 3103f25be..e30cb7cef 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_prototype.rs @@ -2,7 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::{GcScope, NoGcScope}; +use crate::ecmascript::builtins::Behaviour; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_integer_or_infinity, @@ -25,8 +26,7 @@ impl Builtin for NumberPrototypeToExponential { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(NumberPrototype::to_exponential); + const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberPrototype::to_exponential); } struct NumberPrototypeToFixed; @@ -35,8 +35,7 @@ impl Builtin for NumberPrototypeToFixed { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(NumberPrototype::to_fixed); + const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberPrototype::to_fixed); } struct NumberPrototypeToLocaleString; @@ -45,8 +44,7 @@ impl Builtin for NumberPrototypeToLocaleString { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(NumberPrototype::to_locale_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberPrototype::to_locale_string); } struct NumberPrototypeToPrecision; @@ -55,8 +53,7 @@ impl Builtin for NumberPrototypeToPrecision { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(NumberPrototype::to_precision); + const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberPrototype::to_precision); } struct NumberPrototypeToString; @@ -65,8 +62,7 @@ impl Builtin for NumberPrototypeToString { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(NumberPrototype::to_string); + const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberPrototype::to_string); } struct NumberPrototypeValueOf; @@ -75,29 +71,29 @@ impl Builtin for NumberPrototypeValueOf { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(NumberPrototype::value_of); + const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberPrototype::value_of); } impl NumberPrototype { - fn to_exponential( + fn to_exponential<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let fraction_digits = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let fraction_digits = arguments.get(0).bind(nogc); // Let x be ? ThisNumberValue(this value). - let x = this_number_value(agent, this_value, gc.nogc())?; - let x = x.scope(agent, gc.nogc()); + let fraction_digits_is_undefined = fraction_digits.is_undefined(); + let x = this_number_value(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let f be ? ToIntegerOrInfinity(fractionDigits). - let f = to_integer_or_infinity(agent, fraction_digits, gc.reborrow())?; + let f = to_integer_or_infinity(agent, fraction_digits.unbind(), gc.reborrow())?; // No GC can happen after this point. let gc = gc.into_nogc(); let x = x.get(agent).bind(gc); // 3. Assert: If fractionDigits is undefined, then f is 0. - debug_assert!(!fraction_digits.is_undefined() || f.into_i64() == 0); + debug_assert!(!fraction_digits_is_undefined || f.into_i64() == 0); // 4. If x is not finite, return Number::toString(x, 10). if !x.is_finite(agent) { return Ok(Number::to_string_radix_10(agent, x, gc).into_value()); @@ -126,23 +122,24 @@ impl NumberPrototype { } } - fn to_fixed( + fn to_fixed<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let fraction_digits = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let fraction_digits = arguments.get(0).bind(nogc); // Let x be ? ThisNumberValue(this value). - let x = this_number_value(agent, this_value, gc.nogc())?; - let x = x.scope(agent, gc.nogc()); + let x = this_number_value(agent, this_value, nogc)?.scope(agent, nogc); // 2. Let f be ? ToIntegerOrInfinity(fractionDigits). - let f = to_integer_or_infinity(agent, fraction_digits, gc.reborrow())?; + let fraction_digits_is_undefined = fraction_digits.is_undefined(); + let f = to_integer_or_infinity(agent, fraction_digits.unbind(), gc.reborrow())?; // No GC is possible after this point. let gc = gc.into_nogc(); let x = x.get(agent).bind(gc); // 3. Assert: If fractionDigits is undefined, then f is 0. - debug_assert!(!fraction_digits.is_undefined() || f.into_i64() == 0); + debug_assert!(!fraction_digits_is_undefined || f.into_i64() == 0); // 4. If f is not finite, throw a RangeError exception. if !f.is_finite() { return Err(agent.throw_exception_with_static_message( @@ -171,12 +168,12 @@ impl NumberPrototype { Ok(Value::from_str(agent, string, gc)) } - fn to_locale_string( + fn to_locale_string<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { Self::to_string(agent, this_value, arguments, gc) } @@ -184,31 +181,32 @@ impl NumberPrototype { /// Copied from Boa JS engine. Source https://github.com/boa-dev/boa/blob/6f1d7d11ce49040eafe54e5ff2da379be4d998c2/core/engine/src/builtins/number/mod.rs#L412 /// /// Copyright (c) 2019 Jason Williams - fn to_precision( + fn to_precision<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let precision = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let precision = arguments.get(0).bind(nogc); // 1. Let x be ? ThisNumberValue(this value). - let x = this_number_value(agent, this_value, gc.nogc())? - .unbind() - .bind(gc.nogc()); + let x = this_number_value(agent, this_value, nogc)?; // 2. If precision is undefined, return ! ToString(x). if precision.is_undefined() { // Skip: We know ToString calls Number::toString(argument, 10). // Note: That is not `Number.prototype.toString`, but the abstract // operation Number::toString. - return Ok(Number::to_string_radix_10(agent, x, gc.nogc()).into_value()); + return Ok(Number::to_string_radix_10(agent, x, nogc) + .unbind() + .into_value()); } - let x = x.scope(agent, gc.nogc()); + let x = x.scope(agent, nogc); // 3. Let p be ? ToIntegerOrInfinity(precision). - let p = to_integer_or_infinity(agent, precision, gc.reborrow())?; + let p = to_integer_or_infinity(agent, precision.unbind(), gc.reborrow())?; // No GC can occur after this point. let gc = gc.into_nogc(); @@ -430,28 +428,31 @@ impl NumberPrototype { (flt.len() as i32) - 1 } - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { - let x = this_number_value(agent, this_value, gc.nogc())?; - let radix = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.into_nogc(); + let x = this_number_value(agent, this_value, nogc)?; + let radix = arguments.get(0).bind(nogc); if radix.is_undefined() || radix == Value::from(10u8) { - Ok(Number::to_string_radix_10(agent, x, gc.nogc()).into_value()) + Ok(Number::to_string_radix_10(agent, x, nogc) + .unbind() + .into_value()) } else { todo!(); } } - fn value_of( + fn value_of<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { - this_number_value(agent, this_value, gc.nogc()).map(|result| result.into_value()) + gc: GcScope<'gc, '_>, + ) -> JsResult> { + this_number_value(agent, this_value, gc.nogc()).map(|result| result.unbind().into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -520,7 +521,7 @@ fn this_number_value<'gc>( ) -> JsResult> { // 1. If value is a Number, return value. if let Ok(value) = Number::try_from(value) { - return Ok(value); + return Ok(value.unbind()); } // 2. If value is an Object and value has a [[NumberData]] internal slot, then if let Ok(value) = PrimitiveObject::try_from(value) { diff --git a/nova_vm/src/ecmascript/builtins/ordinary.rs b/nova_vm/src/ecmascript/builtins/ordinary.rs index da98feed2..f5bc6d481 100644 --- a/nova_vm/src/ecmascript/builtins/ordinary.rs +++ b/nova_vm/src/ecmascript/builtins/ordinary.rs @@ -8,7 +8,7 @@ use std::vec; use crate::{ ecmascript::abstract_operations::operations_on_objects::try_create_data_property, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, unwrap_try, Scoped, TryResult, }, }; @@ -647,13 +647,13 @@ pub(crate) fn ordinary_has_property_entry<'a>( } /// ### [10.1.8.1 OrdinaryGet ( O, P, Receiver )](https://tc39.es/ecma262/#sec-ordinaryget) -pub(crate) fn ordinary_try_get( +pub(crate) fn ordinary_try_get<'gc>( agent: &mut Agent, object: OrdinaryObject, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, -) -> TryResult { + gc: NoGcScope<'gc, '_>, +) -> TryResult> { // 1. Let desc be ? O.[[GetOwnProperty]](P). let Some(descriptor) = object.try_get_own_property(agent, property_key, gc)? else { // 2. If desc is undefined, then @@ -696,13 +696,13 @@ pub(crate) fn ordinary_try_get( } /// ### [10.1.8.1 OrdinaryGet ( O, P, Receiver )](https://tc39.es/ecma262/#sec-ordinaryget) -pub(crate) fn ordinary_get( +pub(crate) fn ordinary_get<'gc>( agent: &mut Agent, object: OrdinaryObject, property_key: PropertyKey, receiver: Value, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { let object = object.bind(gc.nogc()); let property_key = property_key.bind(gc.nogc()); // Note: We scope here because it's likely we've already tried. @@ -749,7 +749,7 @@ pub(crate) fn ordinary_get( // c. Return ? parent.[[Get]](P, Receiver). return parent .unbind() - .internal_get(agent, property_key.unbind(), receiver, gc.reborrow()); + .internal_get(agent, property_key.unbind(), receiver, gc); }; // 3. If IsDataDescriptor(desc) is true, return desc.[[Value]]. @@ -896,7 +896,7 @@ fn ordinary_try_set_with_own_descriptor( // iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }. let value_descriptor = PropertyDescriptor { - value: Some(value), + value: Some(value.unbind()), ..Default::default() }; @@ -1010,7 +1010,7 @@ fn ordinary_set_with_own_descriptor( // iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }. let value_descriptor = PropertyDescriptor { - value: Some(value), + value: Some(value.unbind()), ..Default::default() }; @@ -1051,7 +1051,13 @@ fn ordinary_set_with_own_descriptor( }; // 6. Perform ? Call(setter, Receiver, « V »). - call_function(agent, setter, receiver, Some(ArgumentsList(&[value])), gc)?; + call_function( + agent, + setter, + receiver, + Some(ArgumentsList(&[value.unbind()])), + gc, + )?; // 7. Return true. Ok(true) diff --git a/nova_vm/src/ecmascript/builtins/primitive_objects.rs b/nova_vm/src/ecmascript/builtins/primitive_objects.rs index 32f761cc6..4ed88f3cd 100644 --- a/nova_vm/src/ecmascript/builtins/primitive_objects.rs +++ b/nova_vm/src/ecmascript/builtins/primitive_objects.rs @@ -4,7 +4,7 @@ use core::ops::{Index, IndexMut}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::rootable::{HeapRootData, HeapRootRef, Rootable}; use crate::engine::{unwrap_try, Scoped, TryResult}; use crate::{ @@ -52,9 +52,9 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: PrimitiveObject) -> Self { - Self::PrimitiveObject(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: PrimitiveObject<'a>) -> Self { + Self::PrimitiveObject(value) } } @@ -64,8 +64,8 @@ impl<'a> IntoObject<'a> for PrimitiveObject<'a> { } } -impl IntoValue for PrimitiveObject<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for PrimitiveObject<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -81,10 +81,10 @@ impl<'a> TryFrom> for PrimitiveObject<'a> { } } -impl TryFrom for PrimitiveObject<'_> { +impl<'a> TryFrom> for PrimitiveObject<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::PrimitiveObject(obj) => Ok(obj), _ => Err(()), @@ -358,13 +358,13 @@ impl<'a> InternalMethods<'a> for PrimitiveObject<'a> { } } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { if let Ok(string) = String::try_from(agent[self].data) { if let Some(string_desc) = string.get_property_descriptor(agent, property_key) { return TryResult::Continue(string_desc.value.unwrap()); @@ -389,13 +389,13 @@ impl<'a> InternalMethods<'a> for PrimitiveObject<'a> { } } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let property_key = property_key.bind(gc.nogc()); if let Ok(string) = String::try_from(agent[self].data) { if let Some(string_desc) = string.get_property_descriptor(agent, property_key) { diff --git a/nova_vm/src/ecmascript/builtins/promise.rs b/nova_vm/src/ecmascript/builtins/promise.rs index 670990019..fba0d56c7 100644 --- a/nova_vm/src/ecmascript/builtins/promise.rs +++ b/nova_vm/src/ecmascript/builtins/promise.rs @@ -4,7 +4,7 @@ use core::ops::{Index, IndexMut}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::rootable::{HeapRootData, HeapRootRef, Rootable}; use crate::engine::Scoped; use crate::{ @@ -74,7 +74,7 @@ impl<'a> Promise<'a> { // a. Let xConstructor be ? Get(x, "constructor"). // b. If SameValue(xConstructor, C) is true, return x. // NOTE: Ignoring subclasses. - promise + promise.unbind() } else { // 2. Let promiseCapability be ? NewPromiseCapability(C). let promise_capability = PromiseCapability::new(agent); @@ -86,8 +86,8 @@ impl<'a> Promise<'a> { } } -impl IntoValue for Promise<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Promise<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -98,15 +98,15 @@ impl<'a> IntoObject<'a> for Promise<'a> { } } -impl From> for Value { - fn from(val: Promise) -> Self { - Value::Promise(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Promise<'a>) -> Self { + Value::Promise(value) } } impl<'a> From> for Object<'a> { - fn from(val: Promise) -> Self { - Object::Promise(val.unbind()) + fn from(value: Promise<'a>) -> Self { + Object::Promise(value) } } diff --git a/nova_vm/src/ecmascript/builtins/promise/data.rs b/nova_vm/src/ecmascript/builtins/promise/data.rs index 7ae7e8442..0277c4af0 100644 --- a/nova_vm/src/ecmascript/builtins/promise/data.rs +++ b/nova_vm/src/ecmascript/builtins/promise/data.rs @@ -29,10 +29,10 @@ pub(crate) enum PromiseState { is_resolved: bool, }, Fulfilled { - promise_result: Value, + promise_result: Value<'static>, }, Rejected { - promise_result: Value, + promise_result: Value<'static>, is_handled: bool, }, } diff --git a/nova_vm/src/ecmascript/builtins/proxy.rs b/nova_vm/src/ecmascript/builtins/proxy.rs index ff1fee5f7..2b2ac24be 100644 --- a/nova_vm/src/ecmascript/builtins/proxy.rs +++ b/nova_vm/src/ecmascript/builtins/proxy.rs @@ -21,13 +21,13 @@ use crate::{ builtins::ArgumentsList, execution::{agent::ExceptionType, Agent, JsResult}, types::{ - scope_property_keys, unbind_property_keys, Function, InternalMethods, InternalSlots, - IntoObject, IntoValue, Object, OrdinaryObject, PropertyDescriptor, PropertyKey, String, - Value, BUILTIN_STRING_MEMORY, + scope_property_keys, Function, InternalMethods, InternalSlots, IntoObject, IntoValue, + Object, OrdinaryObject, PropertyDescriptor, PropertyKey, String, Value, + BUILTIN_STRING_MEMORY, }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::HeapRootData, Scoped, TryResult, }, @@ -84,8 +84,8 @@ impl Proxy<'_> { } } -impl IntoValue for Proxy<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Proxy<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -96,15 +96,15 @@ impl<'a> IntoObject<'a> for Proxy<'a> { } } -impl From> for Value { - fn from(val: Proxy) -> Self { - Value::Proxy(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Proxy<'a>) -> Self { + Value::Proxy(value) } } impl<'a> From> for Object<'a> { - fn from(val: Proxy) -> Self { - Object::Proxy(val.unbind()) + fn from(value: Proxy<'a>) -> Self { + Object::Proxy(value) } } @@ -179,8 +179,8 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { BUILTIN_STRING_MEMORY.getPrototypeOf.into(), gc.reborrow(), )? - .map(Function::unbind); - let trap = trap.map(|t| t.bind(gc.nogc())); + .map(Function::unbind) + .map(|t| t.bind(gc.nogc())); handler = scoped_handler.get(agent).bind(gc.nogc()); trap }; @@ -195,10 +195,11 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let handler_proto = call_function( agent, trap.unbind(), - handler.into(), + handler.into_value().unbind(), Some(ArgumentsList(&[target.get(agent).into()])), gc.reborrow(), - )?; + )? + .unbind(); // 8. If handlerProto is not an Object and handlerProto is not null, throw a TypeError exception. let handler_proto = if handler_proto.is_null() { @@ -213,7 +214,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { )); }; - // 9. Let extensibleTarget be ? IsExtensible(target). + // 9. Let extensibleTarget be ? IsExtensible(target). let extensible_target = is_extensible(agent, target.get(agent), gc.reborrow())?; // 10. If extensibleTarget is true, return handlerProto. @@ -259,6 +260,8 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { prototype: Option, mut gc: GcScope, ) -> JsResult { + let nogc = gc.nogc(); + let mut prototype = prototype.map(|p| p.bind(nogc)); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 3. Let handler be O.[[ProxyHandler]]. @@ -266,18 +269,20 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { mut target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; - let scoped_target = target.scope(agent, gc.nogc()); + } = validate_non_revoked_proxy(agent, self, nogc)?; + let scoped_target = target.scope(agent, nogc); + let mut scoped_prototype = None; // 5. Let trap be ? GetMethod(handler, "setPrototypeOf"). let trap = if let TryResult::Continue(trap) = try_get_object_method( agent, handler, BUILTIN_STRING_MEMORY.setPrototypeOf.into(), - gc.nogc(), + nogc, ) { trap? } else { - let scoped_handler = handler.scope(agent, gc.nogc()); + scoped_prototype = prototype.map(|p| p.scope(agent, nogc)); + let scoped_handler = handler.scope(agent, nogc); let trap = get_object_method( agent, handler.unbind(), @@ -289,21 +294,32 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let trap = trap.map(|t| t.bind(gc)); target = scoped_target.get(agent).bind(gc); handler = scoped_handler.get(agent).bind(gc); + prototype = scoped_prototype.as_ref().map(|p| p.get(agent).bind(gc)); trap }; // 6. If trap is undefined, then let Some(trap) = trap else { // a. Return ? target.[[SetPrototypeOf]](V). - return target - .unbind() - .internal_set_prototype_of(agent, prototype, gc); + return target.unbind().internal_set_prototype_of( + agent, + prototype.map(|p| p.unbind()), + gc, + ); + }; + let scoped_prototype = if scoped_prototype.is_none() && prototype.is_some() { + prototype.map(|p| p.scope(agent, gc.nogc())) + } else { + scoped_prototype }; // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, V »)). let argument = call_function( agent, trap.unbind(), - handler.into_value(), - Some(ArgumentsList(&[target.into(), prototype.into()])), + handler.into_value().unbind(), + Some(ArgumentsList(&[ + target.into_value().unbind(), + prototype.map_or(Value::Null, |p| p.into_value().unbind()), + ])), gc.reborrow(), )?; let boolean_trap_result = to_boolean(agent, argument); @@ -322,7 +338,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { .get(agent) .internal_get_prototype_of(agent, gc.reborrow())?; // 12. If SameValue(V, targetProto) is false, throw a TypeError exception. - if prototype != target_proto { + if scoped_prototype.map(|p| p.get(agent)) != target_proto { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "'setPrototypeOf' on proxy: trap returned truish for setting a new prototype on the non-extensible proxy target", @@ -380,7 +396,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let argument = call_function( agent, trap.unbind(), - handler.into_value(), + handler.into_value().unbind(), Some(ArgumentsList(&[target.get(agent).into()])), gc.reborrow(), )?; @@ -449,7 +465,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let argument = call_function( agent, trap.unbind(), - handler.into_value(), + handler.into_value().unbind(), Some(ArgumentsList(&[target.get(agent).into()])), gc.reborrow(), )?; @@ -495,6 +511,8 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { property_key: PropertyKey, mut gc: GcScope, ) -> JsResult> { + let nogc = gc.nogc(); + let mut property_key = property_key.bind(nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 3. Let handler be O.[[ProxyHandler]]. @@ -502,18 +520,20 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { mut target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; - let scoped_target = target.scope(agent, gc.nogc()); + } = validate_non_revoked_proxy(agent, self, nogc)?; + let scoped_target = target.scope(agent, nogc); + let mut scoped_property_key = None; // 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). let trap = if let TryResult::Continue(trap) = try_get_object_method( agent, handler, BUILTIN_STRING_MEMORY.getOwnPropertyDescriptor.into(), - gc.nogc(), + nogc, ) { trap? } else { - let scoped_handler = handler.scope(agent, gc.nogc()); + let scoped_handler = handler.scope(agent, nogc); + scoped_property_key = Some(property_key.scope(agent, nogc)); let trap = get_object_method( agent, handler.unbind(), @@ -525,6 +545,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let trap = trap.map(|f| f.bind(gc)); handler = scoped_handler.get(agent).bind(gc); target = scoped_target.get(agent).bind(gc); + property_key = scoped_property_key.as_ref().unwrap().get(agent).bind(gc); trap }; // 6. If trap is undefined, then @@ -535,30 +556,33 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { .internal_get_own_property(agent, property_key.unbind(), gc); }; // 7. Let trapResultObj be ? Call(trap, handler, « target, P »). - + let scoped_property_key = + scoped_property_key.unwrap_or_else(|| property_key.scope(agent, gc.nogc())); let p = property_key.convert_to_value(agent, gc.nogc()); let trap_result_obj = call_function( agent, trap.unbind(), - handler.into_value(), - Some(ArgumentsList(&[target.into(), p])), + handler.into_value().unbind(), + Some(ArgumentsList(&[target.unbind().into_value(), p.unbind()])), gc.reborrow(), )?; // 8. If trapResultObj is not an Object and trapResultObj is not undefined, throw a TypeError exception. - if !trap_result_obj.is_object() && !trap_result_obj.is_undefined() { + let trap_result_obj_is_undefined = trap_result_obj.is_undefined(); + if !trap_result_obj.is_object() && !trap_result_obj_is_undefined { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "proxy [[GetOwnProperty]] must return an object or undefined", gc.nogc(), )); }; + let trap_result_obj = trap_result_obj.unbind().scope(agent, gc.nogc()); // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). let target_desc = scoped_target .get(agent) .unbind() - .internal_get_own_property(agent, property_key.unbind(), gc.reborrow())?; + .internal_get_own_property(agent, scoped_property_key.get(agent), gc.reborrow())?; // 10. If trapResultObj is undefined, then - if trap_result_obj.is_undefined() { + if trap_result_obj_is_undefined { if let Some(target_desc) = target_desc { // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. if target_desc.configurable == Some(false) { @@ -566,7 +590,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't report a non-configurable own property '{}' as non-existent.", - property_key.as_display(agent) + scoped_property_key.get(agent).as_display(agent) ), gc.nogc(), )); @@ -576,14 +600,15 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { return Ok(None); } // c. Let extensibleTarget be ? IsExtensible(target). - let extensible_target = is_extensible(agent, scoped_target.get(agent), gc.reborrow())?; + let extensible_target = + is_extensible(agent, scoped_target.get(agent).unbind(), gc.reborrow())?; // d. If extensibleTarget is false, throw a TypeError exception. if !extensible_target { return Err(agent.throw_exception( ExceptionType::TypeError, format!( "proxy can't report a extensibleTarget own property '{}' as non-existent.", - property_key.as_display(agent) + scoped_property_key.get(agent).as_display(agent) ), gc.nogc(), )); @@ -592,10 +617,14 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { return Ok(None); }; // 11. Let extensibleTarget be ? IsExtensible(target). - let extensible_target = is_extensible(agent, scoped_target.get(agent), gc.reborrow())?; + let extensible_target = + is_extensible(agent, scoped_target.get(agent).unbind(), gc.reborrow())?; // 12. Let resultDesc be ? ToPropertyDescriptor(trapResultObj). - let mut result_desc = - PropertyDescriptor::to_property_descriptor(agent, trap_result_obj, gc.reborrow())?; + let mut result_desc = PropertyDescriptor::to_property_descriptor( + agent, + trap_result_obj.get(agent), + gc.reborrow(), + )?; // 13. Perform CompletePropertyDescriptor(resultDesc). result_desc.complete_property_descriptor()?; // 14. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc). @@ -626,7 +655,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't report a non-existent property '{}' as non-configurable", - property_key.as_display(agent) + scoped_property_key.get(agent).as_display(agent) ), gc.nogc(), )); @@ -642,7 +671,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't report existing writable property '{}' as non-writable", - property_key.as_display(agent) + scoped_property_key.get(agent).as_display(agent) ), gc.nogc(), )); @@ -670,50 +699,33 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { property_descriptor: PropertyDescriptor, mut gc: GcScope, ) -> JsResult { + let nogc = gc.nogc(); + let property_key = property_key.bind(nogc).scope(agent, nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 3. Let handler be O.[[ProxyHandler]]. // 4. Assert: handler is an Object. - let NonRevokedProxy { - mut target, - mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; - let mut scoped_target = None; + let NonRevokedProxy { target, handler } = validate_non_revoked_proxy(agent, self, nogc)?; + let scoped_target = target.scope(agent, nogc); + let scoped_handler = handler.scope(agent, nogc); // 5. Let trap be ? GetMethod(handler, "defineProperty"). - let trap = if let TryResult::Continue(trap) = try_get_object_method( + let trap = get_object_method( agent, - handler, + handler.unbind(), BUILTIN_STRING_MEMORY.defineProperty.into(), - gc.nogc(), - ) { - trap? - } else { - let scoped_handler = handler.scope(agent, gc.nogc()); - scoped_target = Some(target.scope(agent, gc.nogc())); - let trap = get_object_method( - agent, - handler.unbind(), - BUILTIN_STRING_MEMORY.defineProperty.into(), - gc.reborrow(), - )? - .map(Function::unbind); - let gc = gc.nogc(); - let trap = trap.map(|f| f.bind(gc)); - handler = scoped_handler.get(agent).bind(gc); - target = scoped_target.as_ref().unwrap().get(agent).bind(gc); - trap - }; + gc.reborrow(), + )?; // 6. If trap is undefined, then let Some(trap) = trap else { // a. Return ? target.[[DefineOwnProperty]](P, Desc). - return target.unbind().internal_define_own_property( + return scoped_target.get(agent).internal_define_own_property( agent, - property_key.unbind(), + property_key.get(agent), property_descriptor, gc, ); }; - let scoped_target = scoped_target.unwrap_or_else(|| target.scope(agent, gc.nogc())); + let trap = trap.unbind().bind(gc.nogc()); // 7. Let descObj be FromPropertyDescriptor(Desc). let desc_obj = PropertyDescriptor::from_property_descriptor( property_descriptor.clone().into(), @@ -721,12 +733,16 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { gc.nogc(), ); // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P, descObj »)). - let p = property_key.convert_to_value(agent, gc.nogc()); + let p = property_key.get(agent).convert_to_value(agent, gc.nogc()); let argument = call_function( agent, trap.unbind(), - handler.into_value(), - Some(ArgumentsList(&[target.into_value(), p, desc_obj.into()])), + scoped_handler.get(agent).into_value(), + Some(ArgumentsList(&[ + scoped_target.get(agent).into_value(), + p.unbind(), + desc_obj.map_or(Value::Null, |d| d.into_value().unbind()), + ])), gc.reborrow(), )?; let boolean_trap_result = to_boolean(agent, argument); @@ -737,7 +753,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). let target_desc = scoped_target.get(agent).internal_get_own_property( agent, - property_key.unbind(), + property_key.get(agent), gc.reborrow(), )?; // 11. Let extensibleTarget be ? IsExtensible(target). @@ -753,7 +769,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't define a new property '{}' on a non-extensible object", - property_key.as_display(agent) + property_key.get(agent).as_display(agent) ), gc, )); @@ -764,7 +780,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't define a non-existent '{}' property as non-configurable", - property_key.as_display(agent) + property_key.get(agent).as_display(agent) ), gc, )); @@ -783,7 +799,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't define an incompatible property descriptor ('{}', proxy can't report an existing non-configurable property as configurable)", - property_key.as_display(agent) + property_key.get(agent).as_display(agent) ), gc, )); @@ -796,7 +812,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't define an incompatible property descriptor ('{}', proxy can't define an existing configurable property as non-configurable)", - property_key.as_display(agent) + property_key.get(agent).as_display(agent) ), gc, )); @@ -816,7 +832,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't define an incompatible property descriptor ('{}', proxy can't define an existing non-configurable writable property as non-writable)", - property_key.as_display(agent) + property_key.get(agent).as_display(agent) ), gc, )); @@ -839,7 +855,8 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { property_key: PropertyKey, mut gc: GcScope, ) -> JsResult { - let mut property_key = property_key.bind(gc.nogc()); + let nogc = gc.nogc(); + let mut property_key = property_key.bind(nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. @@ -848,17 +865,17 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { mut target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; + } = validate_non_revoked_proxy(agent, self, nogc)?; // 5. Let trap be ? GetMethod(handler, "has"). - let scoped_target = target.scope(agent, gc.nogc()); - let scoped_property_key = property_key.scope(agent, gc.nogc()); + let scoped_target = target.scope(agent, nogc); + let scoped_property_key = property_key.scope(agent, nogc); let trap = if let TryResult::Continue(trap) = - try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.has.into(), gc.nogc()) + try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.has.into(), nogc) { trap? } else { - let scoped_handler = handler.scope(agent, gc.nogc()); + let scoped_handler = handler.scope(agent, nogc); let trap = get_object_method( agent, handler.unbind(), @@ -886,8 +903,8 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let argument = call_function( agent, trap.unbind(), - handler.into_value(), - Some(ArgumentsList(&[target.into_value(), p])), + handler.into_value().unbind(), + Some(ArgumentsList(&[target.into_value().unbind(), p.unbind()])), gc.reborrow(), )?; let boolean_trap_result = to_boolean(agent, argument); @@ -895,13 +912,10 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { // 8. If booleanTrapResult is false, then if !boolean_trap_result { // a. Let targetDesc be ? target.[[GetOwnProperty]](P). - let target = scoped_target.get(agent).bind(gc.nogc()); - let property_key = scoped_property_key.get(agent).bind(gc.nogc()); - let target_desc = target.unbind().internal_get_own_property( - agent, - property_key.unbind(), - gc.reborrow(), - )?; + let target_desc = scoped_target + .get(agent) + .unbind() + .internal_get_own_property(agent, scoped_property_key.get(agent), gc.reborrow())?; // b. If targetDesc is not undefined, then if let Some(target_desc) = target_desc { // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. @@ -932,7 +946,13 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { Ok(boolean_trap_result) } - fn try_get(self, _: &mut Agent, _: PropertyKey, _: Value, _: NoGcScope) -> TryResult { + fn try_get<'gc>( + self, + _: &mut Agent, + _: PropertyKey, + _: Value, + _: NoGcScope<'gc, '_>, + ) -> TryResult> { TryResult::Break(()) } @@ -952,14 +972,16 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { /// > The value reported for a property must be undefined if the /// > corresponding target object property is a non-configurable own /// > accessor property that has undefined as its \[\[Get]] attribute. - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, - mut receiver: Value, - mut gc: GcScope, - ) -> JsResult { - let mut property_key = property_key.bind(gc.nogc()); + receiver: Value, + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let mut property_key = property_key.bind(nogc); + let mut receiver = receiver.bind(nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 3. Let handler be O.[[ProxyHandler]]. @@ -967,17 +989,17 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { mut target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; + } = validate_non_revoked_proxy(agent, self, nogc)?; // 5. Let trap be ? GetMethod(handler, "get"). - let scoped_target = target.scope(agent, gc.nogc()); - let scoped_property_key = property_key.scope(agent, gc.nogc()); + let scoped_target = target.scope(agent, nogc); + let scoped_property_key = property_key.scope(agent, nogc); let trap = if let TryResult::Continue(trap) = - try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.get.into(), gc.nogc()) + try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.get.into(), nogc) { trap? } else { - let scoped_handler = handler.scope(agent, gc.nogc()); - let scoped_receiver = receiver.scope(agent, gc.nogc()); + let scoped_handler = handler.scope(agent, nogc); + let scoped_receiver = receiver.scope(agent, nogc); let trap = get_object_method( agent, handler.unbind(), @@ -996,19 +1018,28 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { // 6. If trap is undefined, then let Some(trap) = trap else { // a. Return ? target.[[Get]](P, Receiver). - return target - .unbind() - .internal_get(agent, property_key.unbind(), receiver, gc); + return target.unbind().internal_get( + agent, + property_key.unbind(), + receiver.unbind(), + gc, + ); }; // 7. Let trapResult be ? Call(trap, handler, « target, P, Receiver »). let p = property_key.convert_to_value(agent, gc.nogc()); let trap_result = call_function( agent, trap.unbind(), - handler.into_value(), - Some(ArgumentsList(&[target.into_value(), p, receiver])), + handler.into_value().unbind(), + Some(ArgumentsList(&[ + target.into_value().unbind(), + p.unbind(), + receiver.unbind(), + ])), gc.reborrow(), - )?; + )? + .unbind() + .scope(agent, gc.nogc()); // 8. Let targetDesc be ? target.[[GetOwnProperty]](P). let target = scoped_target.get(agent).bind(gc.nogc()); let property_key = scoped_property_key.get(agent).bind(gc.nogc()); @@ -1017,6 +1048,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { property_key.unbind(), gc.reborrow(), )?; + let trap_result = trap_result.get(agent).bind(gc.nogc()); // 9. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then if let Some(target_desc) = target_desc { if target_desc.configurable == Some(false) { @@ -1047,7 +1079,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { } } // 10. Return trapResult. - Ok(trap_result) + Ok(trap_result.unbind()) } fn try_set( @@ -1072,10 +1104,13 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { agent: &mut Agent, property_key: PropertyKey, value: Value, - mut receiver: Value, + receiver: Value, mut gc: GcScope, ) -> JsResult { - let mut property_key = property_key.bind(gc.nogc()); + let nogc = gc.nogc(); + let mut property_key = property_key.bind(nogc); + let mut value = value.bind(nogc); + let mut receiver = receiver.bind(nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 3. Let handler be O.[[ProxyHandler]]. @@ -1083,17 +1118,18 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { mut target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; + } = validate_non_revoked_proxy(agent, self, nogc)?; // 5. Let trap be ? GetMethod(handler, "set"). - let scoped_target = target.scope(agent, gc.nogc()); - let scoped_property_key = property_key.scope(agent, gc.nogc()); + let scoped_target = target.scope(agent, nogc); + let scoped_property_key = property_key.scope(agent, nogc); let trap = if let TryResult::Continue(trap) = - try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.set.into(), gc.nogc()) + try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.set.into(), nogc) { trap? } else { - let scoped_handler = handler.scope(agent, gc.nogc()); - let scoped_receiver = receiver.scope(agent, gc.nogc()); + let scoped_value = value.scope(agent, nogc); + let scoped_handler = handler.scope(agent, nogc); + let scoped_receiver = receiver.scope(agent, nogc); let trap = get_object_method( agent, handler.unbind(), @@ -1103,6 +1139,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { .map(Function::unbind); let gc = gc.nogc(); let trap = trap.map(|f| f.bind(gc)); + value = scoped_value.get(agent).bind(gc); handler = scoped_handler.get(agent).bind(gc); receiver = scoped_receiver.get(agent).bind(gc); target = scoped_target.get(agent).bind(gc); @@ -1112,17 +1149,27 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { // 6. If trap is undefined, then let Some(trap) = trap else { // a. Return ? target.[[Set]](P, V, Receiver). - return target - .unbind() - .internal_set(agent, property_key.unbind(), value, receiver, gc); + return target.unbind().internal_set( + agent, + property_key.unbind(), + value.unbind(), + receiver.unbind(), + gc, + ); }; // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)). + let scoped_value = value.scope(agent, gc.nogc()); let p = property_key.convert_to_value(agent, gc.nogc()); let argument = call_function( agent, trap.unbind(), - handler.into_value(), - Some(ArgumentsList(&[target.into_value(), p, value, receiver])), + handler.into_value().unbind(), + Some(ArgumentsList(&[ + target.into_value().unbind(), + p.unbind(), + value.unbind(), + receiver.unbind(), + ])), gc.reborrow(), )?; let boolean_trap_result = to_boolean(agent, argument); @@ -1153,7 +1200,11 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { // throw a TypeError exception. if target_desc.is_data_descriptor() && target_desc.writable == Some(false) - && !same_value(agent, value, target_desc.value.unwrap_or(Value::Undefined)) + && !same_value( + agent, + scoped_value.get(agent), + target_desc.value.unwrap_or(Value::Undefined), + ) || target_desc.is_accessor_descriptor() && target_desc.set.is_none() { return Err(agent.throw_exception_with_static_message( @@ -1175,9 +1226,11 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { fn internal_delete( self, agent: &mut Agent, - mut property_key: PropertyKey, + property_key: PropertyKey, mut gc: GcScope, ) -> JsResult { + let nogc = gc.nogc(); + let mut property_key = property_key.bind(nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 3. Let handler be O.[[ProxyHandler]]. @@ -1185,20 +1238,21 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { mut target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; + } = validate_non_revoked_proxy(agent, self, nogc)?; let mut scoped_target = None; + let mut scoped_property_key = None; // 5. Let trap be ? GetMethod(handler, "deleteProperty"). let trap = if let TryResult::Continue(trap) = try_get_object_method( agent, handler, BUILTIN_STRING_MEMORY.deleteProperty.into(), - gc.nogc(), + nogc, ) { trap? } else { - let scoped_handler = handler.scope(agent, gc.nogc()); - scoped_target = Some(target.scope(agent, gc.nogc())); - let scoped_property_key = property_key.scope(agent, gc.nogc()); + let scoped_handler = handler.scope(agent, nogc); + scoped_target = Some(target.scope(agent, nogc)); + scoped_property_key = Some(property_key.scope(agent, nogc)); let trap = get_object_method( agent, handler.unbind(), @@ -1210,7 +1264,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let trap = trap.map(|f| f.bind(gc)); handler = scoped_handler.get(agent).bind(gc); target = scoped_target.as_ref().unwrap().get(agent).bind(gc); - property_key = scoped_property_key.get(agent); + property_key = scoped_property_key.as_ref().unwrap().get(agent).bind(gc); trap }; // 6. If trap is undefined, then @@ -1222,12 +1276,14 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { }; let scoped_target = scoped_target.unwrap_or_else(|| target.scope(agent, gc.nogc())); // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P »)). + let scoped_property_key = + scoped_property_key.unwrap_or_else(|| property_key.scope(agent, gc.nogc())); let p = property_key.convert_to_value(agent, gc.nogc()); let argument = call_function( agent, trap.unbind(), - handler.into_value(), - Some(ArgumentsList(&[target.into_value(), p])), + handler.into_value().unbind(), + Some(ArgumentsList(&[target.into_value().unbind(), p.unbind()])), gc.reborrow(), )?; let boolean_trap_result = to_boolean(agent, argument); @@ -1238,7 +1294,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). let target_desc = scoped_target.get(agent).internal_get_own_property( agent, - property_key.unbind(), + scoped_property_key.get(agent), gc.reborrow(), )?; // 10. If targetDesc is undefined, return true. @@ -1251,7 +1307,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "property '{}' is non-configurable and can't be deleted", - property_key.as_display(agent) + scoped_property_key.get(agent).as_display(agent) ), gc.into_nogc(), )); @@ -1264,7 +1320,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { ExceptionType::TypeError, format!( "proxy can't delete property '{}' on a non-extensible object", - property_key.as_display(agent) + scoped_property_key.get(agent).as_display(agent) ), gc.into_nogc(), )); @@ -1361,7 +1417,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let target_keys = scoped_target .get(agent) .internal_own_property_keys(agent, gc.reborrow())?; - let target_keys = scope_property_keys(agent, unbind_property_keys(target_keys), gc.nogc()); + let target_keys = scope_property_keys(agent, target_keys.unbind(), gc.nogc()); // 13. Assert: targetKeys contains no duplicate entries. let mut unique_target_keys = Vec::with_capacity(target_keys.len()); for value in target_keys.iter() { @@ -1471,15 +1527,16 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { /// and argumentsList (a List of ECMAScript language values) /// and returns either a normal completion containing an ECMAScript /// language value or a throw completion. - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, _: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let this_argument = arguments.get(1); - let arguments_list = arguments.get(2); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let mut this_argument = arguments.get(1).bind(nogc); + let mut arguments_list = arguments.get(2).bind(nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 3. Let handler be O.[[ProxyHandler]]. @@ -1487,18 +1544,17 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { mut target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; + } = validate_non_revoked_proxy(agent, self, nogc)?; // 5. Let trap be ? GetMethod(handler, "apply"). - let trap = if let TryResult::Continue(trap) = try_get_object_method( - agent, - handler, - BUILTIN_STRING_MEMORY.apply.into(), - gc.nogc(), - ) { + let trap = if let TryResult::Continue(trap) = + try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.apply.into(), nogc) + { trap? } else { - let scoped_handler = handler.scope(agent, gc.nogc()); - let scoped_target = target.scope(agent, gc.nogc()); + let scoped_handler = handler.scope(agent, nogc); + let scoped_target = target.scope(agent, nogc); + let scoped_this_argument = this_argument.scope(agent, nogc); + let scoped_arguments_list = arguments_list.scope(agent, nogc); let trap = get_object_method( agent, handler.unbind(), @@ -1510,6 +1566,8 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let trap = trap.map(|f| f.bind(gc)); handler = scoped_handler.get(agent).bind(gc); target = scoped_target.get(agent).bind(gc); + this_argument = scoped_this_argument.get(agent).bind(gc); + arguments_list = scoped_arguments_list.get(agent).bind(gc); trap }; // 6. If trap is undefined, then @@ -1517,9 +1575,9 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { // a. Return ? Call(target, thisArgument, argumentsList). return call( agent, - target.into_value(), - this_argument, - Some(ArgumentsList(&[arguments_list])), + target.into_value().unbind(), + this_argument.unbind(), + Some(ArgumentsList(&[arguments_list.unbind()])), gc, ); }; @@ -1529,11 +1587,11 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { call_function( agent, trap.unbind(), - handler.into_value(), + handler.into_value().unbind(), Some(ArgumentsList(&[ - target.into_value(), - this_argument, - arg_array.into_value(), + target.into_value().unbind(), + this_argument.unbind(), + arg_array.into_value().unbind(), ])), gc, ) @@ -1546,6 +1604,8 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { new_target: Function, mut gc: GcScope<'gc, '_>, ) -> JsResult> { + let nogc = gc.nogc(); + let mut new_target = new_target.bind(nogc); // 1. Perform ? ValidateNonRevokedProxy(O). // 2. Let target be O.[[ProxyTarget]]. // 4. Let handler be O.[[ProxyHandler]]. @@ -1553,20 +1613,18 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { let NonRevokedProxy { target, mut handler, - } = validate_non_revoked_proxy(agent, self, gc.nogc())?; + } = validate_non_revoked_proxy(agent, self, nogc)?; // 3. Assert: IsConstructor(target) is true. let mut target = is_constructor(agent, target).unwrap(); // 6. Let trap be ? GetMethod(handler, "construct"). - let trap = if let TryResult::Continue(trap) = try_get_object_method( - agent, - handler, - BUILTIN_STRING_MEMORY.construct.into(), - gc.nogc(), - ) { + let trap = if let TryResult::Continue(trap) = + try_get_object_method(agent, handler, BUILTIN_STRING_MEMORY.construct.into(), nogc) + { trap? } else { - let scoped_target = target.scope(agent, gc.nogc()); - let scoped_handler = handler.scope(agent, gc.nogc()); + let scoped_new_target = new_target.scope(agent, nogc); + let scoped_target = target.scope(agent, nogc); + let scoped_handler = handler.scope(agent, nogc); let trap = get_object_method( agent, handler.unbind(), @@ -1576,6 +1634,7 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { .map(Function::unbind); let gc = gc.nogc(); let trap = trap.map(|f| f.bind(gc)); + new_target = scoped_new_target.get(agent).bind(gc); target = scoped_target.get(agent).bind(gc); handler = scoped_handler.get(agent).bind(gc); trap @@ -1587,26 +1646,27 @@ impl<'a> InternalMethods<'a> for Proxy<'a> { agent, target.unbind(), Some(arguments_list), - Some(new_target), + Some(new_target.unbind()), gc, ); }; // 8. Let argArray be CreateArrayFromList(argumentsList). - let arg_array = create_array_from_list(agent, arguments_list.0, gc.nogc()); + let arg_array = create_array_from_list(agent, &arguments_list, gc.nogc()); // 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »). let new_obj = call_function( agent, trap.unbind(), - handler.into_value(), + handler.into_value().unbind(), Some(ArgumentsList(&[ - target.into_value(), - arg_array.into_value(), - new_target.into_value(), + target.into_value().unbind(), + arg_array.into_value().unbind(), + new_target.into_value().unbind(), ])), gc.reborrow(), )?; // 11. Return newObj. new_obj + .unbind() .try_into() // 10. If newObj is not an Object, throw a TypeError exception. .or(Err(agent.throw_exception_with_static_message( @@ -1630,7 +1690,7 @@ pub(crate) fn proxy_create<'a>( gc: NoGcScope<'a, '_>, ) -> JsResult> { // 1. If target is not an Object, throw a TypeError exception. - let Ok(target) = Object::try_from(target) else { + let Ok(target) = Object::try_from(target.unbind()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Proxy target must be an object", @@ -1638,7 +1698,7 @@ pub(crate) fn proxy_create<'a>( )); }; // 2. If handler is not an Object, throw a TypeError exception. - let Ok(handler) = Object::try_from(handler) else { + let Ok(handler) = Object::try_from(handler.unbind()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Proxy handler must be an object", diff --git a/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs b/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs index 9773285f9..5e07965fb 100644 --- a/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs @@ -39,13 +39,13 @@ impl Builtin for ProxyRevocable { impl ProxyConstructor { /// ### [28.2.1.1 Proxy ( target, handler )](https://tc39.es/ecma262/#sec-proxy-target-handler) - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let gc = gc.into_nogc(); let target = arguments.get(0); let handler = arguments.get(1); @@ -61,12 +61,12 @@ impl ProxyConstructor { proxy_create(agent, target, handler, gc).map(|proxy| proxy.into_value()) } - fn revocable( + fn revocable<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs b/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs index d8b9d839f..9807395a6 100644 --- a/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs +++ b/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs @@ -5,8 +5,8 @@ use crate::ecmascript::abstract_operations::type_conversion::{ to_property_key_complex, to_property_key_simple, }; -use crate::ecmascript::types::{bind_property_keys, unbind_property_keys}; -use crate::engine::context::GcScope; +use crate::ecmascript::builtins::Behaviour; +use crate::engine::context::{Bindable, GcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -35,8 +35,7 @@ impl Builtin for ReflectObjectApply { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::apply); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::apply); } struct ReflectObjectConstruct; @@ -45,8 +44,7 @@ impl Builtin for ReflectObjectConstruct { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::construct); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::construct); } struct ReflectObjectDefineProperty; impl Builtin for ReflectObjectDefineProperty { @@ -54,8 +52,7 @@ impl Builtin for ReflectObjectDefineProperty { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::define_property); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::define_property); } struct ReflectObjectDeleteProperty; impl Builtin for ReflectObjectDeleteProperty { @@ -63,8 +60,7 @@ impl Builtin for ReflectObjectDeleteProperty { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::delete_property); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::delete_property); } struct ReflectObjectGet; impl Builtin for ReflectObjectGet { @@ -72,8 +68,7 @@ impl Builtin for ReflectObjectGet { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::get); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::get); } struct ReflectObjectGetOwnPropertyDescriptor; impl Builtin for ReflectObjectGetOwnPropertyDescriptor { @@ -81,8 +76,7 @@ impl Builtin for ReflectObjectGetOwnPropertyDescriptor { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::get_own_property_descriptor); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::get_own_property_descriptor); } struct ReflectObjectGetPrototypeOf; impl Builtin for ReflectObjectGetPrototypeOf { @@ -90,8 +84,7 @@ impl Builtin for ReflectObjectGetPrototypeOf { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::get_prototype_of); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::get_prototype_of); } struct ReflectObjectHas; @@ -100,8 +93,7 @@ impl Builtin for ReflectObjectHas { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::has); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::has); } struct ReflectObjectIsExtensible; impl Builtin for ReflectObjectIsExtensible { @@ -109,8 +101,7 @@ impl Builtin for ReflectObjectIsExtensible { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::is_extensible); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::is_extensible); } struct ReflectObjectOwnKeys; impl Builtin for ReflectObjectOwnKeys { @@ -118,8 +109,7 @@ impl Builtin for ReflectObjectOwnKeys { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::own_keys); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::own_keys); } struct ReflectObjectPreventExtensions; impl Builtin for ReflectObjectPreventExtensions { @@ -127,8 +117,7 @@ impl Builtin for ReflectObjectPreventExtensions { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::prevent_extensions); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::prevent_extensions); } struct ReflectObjectSet; impl Builtin for ReflectObjectSet { @@ -136,8 +125,7 @@ impl Builtin for ReflectObjectSet { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::set); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::set); } struct ReflectObjectSetPrototypeOf; impl Builtin for ReflectObjectSetPrototypeOf { @@ -145,72 +133,76 @@ impl Builtin for ReflectObjectSetPrototypeOf { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(ReflectObject::set_prototype_of); + const BEHAVIOUR: Behaviour = Behaviour::Regular(ReflectObject::set_prototype_of); } impl ReflectObject { /// [28.1.1 Reflect.apply ( target, thisArgument, argumentsList )](https://tc39.es/ecma262/#sec-reflect.apply) - fn apply( + fn apply<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let this_argument = arguments.get(1); - let arguments_list = arguments.get(2); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let this_argument = arguments.get(1).bind(nogc); + let arguments_list = arguments.get(2).bind(nogc); // 1. If IsCallable(target) is false, throw a TypeError exception. - let Some(target) = is_callable(target, gc.nogc()) else { + let Some(target) = is_callable(target, nogc) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not callable", - gc.nogc(), + nogc, )); }; - let target = target.scope(agent, gc.nogc()); + let target = target.scope(agent, nogc); + let this_argument = this_argument.scope(agent, nogc); // 2. Let args be ? CreateListFromArrayLike(argumentsList). - let args = create_list_from_array_like(agent, arguments_list, gc.reborrow())?; + let args = create_list_from_array_like(agent, arguments_list.unbind(), gc.reborrow())?; // TODO: 3. Perform PrepareForTailCall(). // 4. Return ? Call(target, thisArgument, args) call_function( agent, target.get(agent), - this_argument, - Some(ArgumentsList(&args)), + this_argument.get(agent), + Some(ArgumentsList( + &args.into_iter().map(|v| v.unbind()).collect::>(), + )), gc, ) } /// [28.1.2 Reflect.construct ( target, argumentsList \[ , newTarget \] )](https://tc39.es/ecma262/#sec-reflect.construct) - fn construct( + fn construct<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let arguments_list = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let arguments_list = arguments.get(1).bind(nogc); // 1. If IsConstructor(target) is false, throw a TypeError exception. let Some(target) = is_constructor(agent, target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not a constructor", - gc.nogc(), + nogc, )); }; // 2. If newTarget is not present, set newTarget to target. // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception. let new_target = if arguments.len() > 2 { - let new_target = arguments.get(2); + let new_target = arguments.get(2).bind(nogc); let Some(new_target) = is_constructor(agent, new_target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not a constructor", - gc.nogc(), + nogc, )); }; new_target @@ -218,57 +210,61 @@ impl ReflectObject { target }; + let target = target.scope(agent, nogc); + let new_target = new_target.scope(agent, nogc); // 4. Let args be ? CreateListFromArrayLike(argumentsList). - let args = create_list_from_array_like(agent, arguments_list, gc.reborrow())?; + let args = create_list_from_array_like(agent, arguments_list.unbind(), gc.reborrow())?; // 5. Return ? Construct(target, args, newTarget) - let ret = construct( + construct( agent, - target, - Some(ArgumentsList(&args)), - Some(new_target), - gc.reborrow(), - )?; - Ok(ret.into_value()) + target.get(agent), + Some(ArgumentsList( + &args.into_iter().map(|v| v.unbind()).collect::>(), + )), + Some(new_target.get(agent)), + gc, + ) + .map(|o| o.into_value()) } /// [28.1.3 Reflect.defineProperty ( target, propertyKey, attributes )](https://tc39.es/ecma262/#sec-reflect.defineproperty) - fn define_property( + fn define_property<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let property_key = arguments.get(1); - let mut attributes = arguments.get(2); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let property_key = arguments.get(1).bind(nogc); + let mut attributes = arguments.get(2).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; - let mut target = target.bind(gc.nogc()); + let mut target = target.bind(nogc); let mut scoped_target = None; // 2. Let key be ? ToPropertyKey(propertyKey). - let mut key = if let TryResult::Continue(key) = - to_property_key_simple(agent, property_key, gc.nogc()) - { - key - } else { - scoped_target = Some(target.scope(agent, gc.nogc())); - let scoped_attributes = attributes.scope(agent, gc.nogc()); - let key = to_property_key_complex(agent, property_key, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - target = scoped_target.as_ref().unwrap().get(agent); - attributes = scoped_attributes.get(agent); - key - }; + let mut key = + if let TryResult::Continue(key) = to_property_key_simple(agent, property_key, nogc) { + key + } else { + scoped_target = Some(target.scope(agent, nogc)); + let scoped_attributes = attributes.scope(agent, nogc); + let key = to_property_key_complex(agent, property_key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + target = scoped_target.as_ref().unwrap().get(agent); + attributes = scoped_attributes.get(agent); + key + }; // 3. Let desc be ? ToPropertyDescriptor(attributes). let desc = if let TryResult::Continue(desc) = @@ -280,8 +276,11 @@ impl ReflectObject { scoped_target = Some(target.scope(agent, gc.nogc())); } let scoped_key = key.scope(agent, gc.nogc()); - let desc = - PropertyDescriptor::to_property_descriptor(agent, attributes, gc.reborrow())?; + let desc = PropertyDescriptor::to_property_descriptor( + agent, + attributes.unbind(), + gc.reborrow(), + )?; key = scoped_key.get(agent).bind(gc.nogc()); target = scoped_target.unwrap().get(agent).bind(gc.nogc()); desc @@ -298,54 +297,57 @@ impl ReflectObject { } /// [28.1.4 Reflect.deleteProperty ( target, propertyKey )](https://tc39.es/ecma262/#sec-reflect.deleteproperty) - fn delete_property( + fn delete_property<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let property_key = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let property_key = arguments.get(1).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(mut target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; // 2. Let key be ? ToPropertyKey(propertyKey). - let key = if let TryResult::Continue(key) = - to_property_key_simple(agent, property_key, gc.nogc()) - { - key - } else { - let scoped_target = target.scope(agent, gc.nogc()); - let key = to_property_key_complex(agent, property_key, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - target = scoped_target.get(agent); - key - }; + let key = + if let TryResult::Continue(key) = to_property_key_simple(agent, property_key, nogc) { + key + } else { + let scoped_target = target.scope(agent, nogc); + let key = to_property_key_complex(agent, property_key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + target = scoped_target.get(agent); + key + }; // 3. Return ? target.[[Delete]](key). - let ret = target.internal_delete(agent, key.unbind(), gc.reborrow())?; + let ret = target + .unbind() + .internal_delete(agent, key.unbind(), gc.reborrow())?; Ok(ret.into()) } /// [28.1.5 Reflect.get ( target, propertyKey \[ , receiver \] )](https://tc39.es/ecma262/#sec-reflect.get) - fn get( + fn get<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let property_key = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let property_key = arguments.get(1).bind(nogc); let mut receiver = if arguments.len() > 2 { - Some(arguments.get(2)) + Some(arguments.get(2).bind(nogc)) } else { None }; @@ -355,87 +357,87 @@ impl ReflectObject { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; - let mut target = target.bind(gc.nogc()); + let mut target = target.bind(nogc); // 2. Let key be ? ToPropertyKey(propertyKey). - let key = if let TryResult::Continue(key) = - to_property_key_simple(agent, property_key, gc.nogc()) - { - key - } else { - let scoped_target = target.scope(agent, gc.nogc()); - let scoped_receiver = receiver.map(|receiver| receiver.scope(agent, gc.nogc())); - let key = to_property_key_complex(agent, property_key, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - target = scoped_target.get(agent).bind(gc.nogc()); - receiver = scoped_receiver.map(|scoped_receiver| scoped_receiver.get(agent)); - key - }; + let key = + if let TryResult::Continue(key) = to_property_key_simple(agent, property_key, nogc) { + key + } else { + let scoped_target = target.scope(agent, nogc); + let scoped_receiver = receiver.map(|receiver| receiver.scope(agent, nogc)); + let key = to_property_key_complex(agent, property_key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + target = scoped_target.get(agent).bind(gc.nogc()); + receiver = scoped_receiver.map(|scoped_receiver| scoped_receiver.get(agent)); + key + }; // 3. If receiver is not present, then // a. Set receiver to target. let receiver = receiver.unwrap_or(target.into_value()); // 4. Return ? target.[[Get]](key, receiver). target .unbind() - .internal_get(agent, key.unbind(), receiver, gc.reborrow()) + .internal_get(agent, key.unbind(), receiver.unbind(), gc) } /// [28.1.6 Reflect.getOwnPropertyDescriptor ( target, propertyKey )](https://tc39.es/ecma262/#sec-reflect.getownpropertydescriptor) - fn get_own_property_descriptor( + fn get_own_property_descriptor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let property_key = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let property_key = arguments.get(1).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; - let mut target = target.bind(gc.nogc()); + let mut target = target.bind(nogc); // 2. Let key be ? ToPropertyKey(propertyKey). - let key = if let TryResult::Continue(key) = - to_property_key_simple(agent, property_key, gc.nogc()) - { - key - } else { - let scoped_target = target.scope(agent, gc.nogc()); - let key = to_property_key_complex(agent, property_key, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - target = scoped_target.get(agent).bind(gc.nogc()); - key - }; + let key = + if let TryResult::Continue(key) = to_property_key_simple(agent, property_key, nogc) { + key + } else { + let scoped_target = target.scope(agent, nogc); + let key = to_property_key_complex(agent, property_key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + target = scoped_target.get(agent).bind(gc.nogc()); + key + }; // 3. Let desc be ? target.[[GetOwnProperty]](key). let desc = target .unbind() .internal_get_own_property(agent, key.unbind(), gc.reborrow())?; // 4. Return FromPropertyDescriptor(desc). match PropertyDescriptor::from_property_descriptor(desc, agent, gc.nogc()) { - Some(ret) => Ok(ret.into_value()), + Some(ret) => Ok(ret.into_value().unbind()), None => Ok(Value::Undefined), } } /// [28.1.7 Reflect.getPrototypeOf ( target )](https://tc39.es/ecma262/#sec-reflect.getprototypeof) - fn get_prototype_of( + fn get_prototype_of<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { @@ -447,45 +449,45 @@ impl ReflectObject { }; // 2. Return ? target.[[GetPrototypeOf]](). - match target.internal_get_prototype_of(agent, gc)? { + match target.unbind().internal_get_prototype_of(agent, gc)? { Some(ret) => Ok(ret.into_value()), None => Ok(Value::Null), } } /// [28.1.8 Reflect.has ( target, propertyKey )](https://tc39.es/ecma262/#sec-reflect.has) - fn has( + fn has<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let property_key = arguments.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let property_key = arguments.get(1).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; - let mut target = target.bind(gc.nogc()); + let mut target = target.bind(nogc); // 2. Let key be ? ToPropertyKey(propertyKey). - let key = if let TryResult::Continue(key) = - to_property_key_simple(agent, property_key, gc.nogc()) - { - key - } else { - let scoped_target = target.scope(agent, gc.nogc()); - let key = to_property_key_complex(agent, property_key, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - target = scoped_target.get(agent).bind(gc.nogc()); - key - }; + let key = + if let TryResult::Continue(key) = to_property_key_simple(agent, property_key, nogc) { + key + } else { + let scoped_target = target.scope(agent, nogc); + let key = to_property_key_complex(agent, property_key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + target = scoped_target.get(agent).bind(gc.nogc()); + key + }; // 3. Return ? target.[[HasProperty]](key). let ret = target .unbind() @@ -494,95 +496,100 @@ impl ReflectObject { } /// [28.1.9 Reflect.isExtensible ( target )](https://tc39.es/ecma262/#sec-reflect.isextensible) - fn is_extensible( + fn is_extensible<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; // 2. Return ? target.[[IsExtensible]](). - let ret = target.internal_is_extensible(agent, gc.reborrow())?; + let ret = target.unbind().internal_is_extensible(agent, gc)?; Ok(ret.into()) } /// [28.1.10 Reflect.ownKeys ( target )](https://tc39.es/ecma262/#sec-reflect.ownkeys) - fn own_keys( + fn own_keys<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; // 2. Let keys be ? target.[[OwnPropertyKeys]](). - // TODO: `PropertyKey::into_value` might not do the right thing for - // integer keys. - let keys: Vec = bind_property_keys( - unbind_property_keys(target.internal_own_property_keys(agent, gc.reborrow())?), - gc.nogc(), - ) - .into_iter() - .map(|key| key.convert_to_value(agent, gc.nogc())) - .collect(); + let keys: Vec = target + .unbind() + .internal_own_property_keys(agent, gc.reborrow())? + .unbind() + .bind(gc.nogc()) + .into_iter() + .map(|key| key.convert_to_value(agent, gc.nogc())) + .collect(); // 3. Return CreateArrayFromList(keys). - Ok(create_array_from_list(agent, &keys, gc.nogc()).into_value()) + Ok(create_array_from_list(agent, &keys, gc.nogc()) + .into_value() + .unbind()) } /// [28.1.11 Reflect.preventExtensions ( target )](https://tc39.es/ecma262/#sec-reflect.preventextensions) - fn prevent_extensions( + fn prevent_extensions<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; // 2. Return ? target.[[PreventExtensions]](). - let ret = target.internal_prevent_extensions(agent, gc.reborrow())?; + let ret = target.unbind().internal_prevent_extensions(agent, gc)?; Ok(ret.into()) } /// [28.1.12 Reflect.set ( target, propertyKey, V \[ , receiver \] )](https://tc39.es/ecma262/#sec-reflect.set) - fn set( + fn set<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let property_key = arguments.get(1); - let mut v = arguments.get(2); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let property_key = arguments.get(1).bind(nogc); + let mut v = arguments.get(2).bind(nogc); let mut receiver = if arguments.len() > 3 { - Some(arguments.get(3)) + Some(arguments.get(3).bind(nogc)) } else { None }; @@ -592,53 +599,56 @@ impl ReflectObject { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; // 2. Let key be ? ToPropertyKey(propertyKey). - let key = if let TryResult::Continue(key) = - to_property_key_simple(agent, property_key, gc.nogc()) - { - key.bind(gc.nogc()) - } else { - let scoped_target = target.scope(agent, gc.nogc()); - let scoped_v = v.scope(agent, gc.nogc()); - let scoped_receiver = receiver.map(|receiver| receiver.scope(agent, gc.nogc())); - let key = to_property_key_complex(agent, property_key, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - target = scoped_target.get(agent); - v = scoped_v.get(agent); - receiver = scoped_receiver.map(|scoped_receiver| scoped_receiver.get(agent)); - key - }; + let key = + if let TryResult::Continue(key) = to_property_key_simple(agent, property_key, nogc) { + key.bind(nogc) + } else { + let scoped_target = target.scope(agent, nogc); + let scoped_v = v.scope(agent, nogc); + let scoped_receiver = receiver.map(|receiver| receiver.scope(agent, nogc)); + let key = to_property_key_complex(agent, property_key.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + target = scoped_target.get(agent); + v = scoped_v.get(agent); + receiver = scoped_receiver.map(|scoped_receiver| scoped_receiver.get(agent)); + key + }; // 3. If receiver is not present, then // a. Set receiver to target. let receiver = receiver.unwrap_or(target.into_value()); // 4. Return ? target.[[Set]](key, V, receiver). - let ret = target.internal_set(agent, key.unbind(), v, receiver, gc.reborrow())?; + let ret = + target + .unbind() + .internal_set(agent, key.unbind(), v.unbind(), receiver.unbind(), gc)?; Ok(ret.into()) } /// [28.1.13 Reflect.setPrototypeOf ( target, proto )](https://tc39.es/ecma262/#sec-reflect.setprototypeof) - fn set_prototype_of( + fn set_prototype_of<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let target = arguments.get(0); - let proto = arguments.get(1); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let target = arguments.get(0).bind(nogc); + let proto = arguments.get(1).bind(nogc); // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(target) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Value is not an object", - gc.nogc(), + nogc, )); }; @@ -651,12 +661,15 @@ impl ReflectObject { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Prototype must be an object or null", - gc.nogc(), + nogc, )); }; // 3. Return ? target.[[SetPrototypeOf]](proto). - let ret = target.internal_set_prototype_of(agent, proto, gc.reborrow())?; + let ret = + target + .unbind() + .internal_set_prototype_of(agent, proto.map(|p| p.unbind()), gc)?; Ok(ret.into()) } diff --git a/nova_vm/src/ecmascript/builtins/regexp.rs b/nova_vm/src/ecmascript/builtins/regexp.rs index 3c4e3a322..1fbea52b1 100644 --- a/nova_vm/src/ecmascript/builtins/regexp.rs +++ b/nova_vm/src/ecmascript/builtins/regexp.rs @@ -16,7 +16,7 @@ use crate::{ }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::HeapRootData, unwrap_try, Scoped, TryResult, }, @@ -74,9 +74,9 @@ impl RegExp<'_> { } } -impl From> for Value { - fn from(value: RegExp) -> Self { - Self::RegExp(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: RegExp<'a>) -> Self { + Self::RegExp(value) } } @@ -97,10 +97,10 @@ impl<'a> TryFrom> for RegExp<'a> { } } -impl TryFrom for RegExp<'_> { +impl<'a> TryFrom> for RegExp<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::RegExp(regexp) => Ok(regexp), _ => Err(()), @@ -108,8 +108,8 @@ impl TryFrom for RegExp<'_> { } } -impl IntoValue for RegExp<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for RegExp<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -228,13 +228,13 @@ impl<'a> InternalMethods<'a> for RegExp<'a> { } } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { if property_key == BUILTIN_STRING_MEMORY.lastIndex.into() { // Regardless of the backing object, we might have a valid value // for lastIndex. @@ -257,13 +257,13 @@ impl<'a> InternalMethods<'a> for RegExp<'a> { } } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let property_key = property_key.bind(gc.nogc()); if property_key == BUILTIN_STRING_MEMORY.lastIndex.into() { // Regardless of the backing object, we might have a valid value diff --git a/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs index b94dc5702..156df7106 100644 --- a/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs @@ -6,7 +6,7 @@ use oxc_ast::ast::RegExpFlags; use crate::ecmascript::abstract_operations::operations_on_objects::{set, try_set}; use crate::ecmascript::types::IntoObject; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ builtins::ordinary::ordinary_create_from_constructor, diff --git a/nova_vm/src/ecmascript/builtins/set.rs b/nova_vm/src/ecmascript/builtins/set.rs index 983c8ce32..a65739eff 100644 --- a/nova_vm/src/ecmascript/builtins/set.rs +++ b/nova_vm/src/ecmascript/builtins/set.rs @@ -11,7 +11,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::{BaseIndex, SetIndex}, CompactionLists, CreateHeapData, HeapMarkAndSweep, WorkQueues, @@ -65,8 +69,8 @@ impl Set<'_> { } } -impl IntoValue for Set<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Set<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -77,22 +81,22 @@ impl<'a> IntoObject<'a> for Set<'a> { } } -impl From> for Value { - fn from(val: Set) -> Self { - Value::Set(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: Set<'a>) -> Self { + Value::Set(value) } } impl<'a> From> for Object<'a> { - fn from(val: Set) -> Self { - Object::Set(val.unbind()) + fn from(value: Set<'a>) -> Self { + Object::Set(value) } } -impl TryFrom for Set<'_> { +impl<'a> TryFrom> for Set<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { if let Value::Set(set) = value { Ok(set) } else { diff --git a/nova_vm/src/ecmascript/builtins/set/data.rs b/nova_vm/src/ecmascript/builtins/set/data.rs index c6f4143e9..38bf3d8f2 100644 --- a/nova_vm/src/ecmascript/builtins/set/data.rs +++ b/nova_vm/src/ecmascript/builtins/set/data.rs @@ -7,6 +7,7 @@ use crate::{ bigint::HeapBigInt, HeapNumber, HeapString, OrdinaryObject, Value, BIGINT_DISCRIMINANT, NUMBER_DISCRIMINANT, STRING_DISCRIMINANT, }, + engine::context::{Bindable, NoGcScope}, heap::{CompactionLists, HeapMarkAndSweep, PrimitiveHeapIndexable, WorkQueues}, }; use ahash::AHasher; @@ -40,7 +41,7 @@ impl SetHeapData { self.set_data.set_data.borrow().len() as u32 } - pub fn values(&self) -> &[Option] { + pub fn values<'a>(&self, _gc: NoGcScope<'a, '_>) -> &[Option>] { &self.set_data.values } @@ -65,7 +66,7 @@ impl SetHeapData { #[derive(Debug, Default)] pub(crate) struct SetData { - pub(crate) values: Vec>, + pub(crate) values: Vec>>, /// Low-level hash table pointing to value indexes. pub(crate) set_data: RefCell>, /// Flag that lets the Set know if it needs to rehash its primitive keys. @@ -114,7 +115,7 @@ fn rehash_set_data( ) { let hasher = |value: Value| { let mut hasher = AHasher::default(); - value.hash(arena, &mut hasher); + value.unbind().hash(arena, &mut hasher); hasher.finish() }; let hashes = { diff --git a/nova_vm/src/ecmascript/builtins/shared_array_buffer.rs b/nova_vm/src/ecmascript/builtins/shared_array_buffer.rs index d3c91c8af..54582dc51 100644 --- a/nova_vm/src/ecmascript/builtins/shared_array_buffer.rs +++ b/nova_vm/src/ecmascript/builtins/shared_array_buffer.rs @@ -11,7 +11,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::SharedArrayBufferIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, @@ -64,8 +68,8 @@ impl SharedArrayBuffer<'_> { } } -impl IntoValue for SharedArrayBuffer<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for SharedArrayBuffer<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -76,15 +80,15 @@ impl<'a> IntoObject<'a> for SharedArrayBuffer<'a> { } } -impl From> for Value { - fn from(val: SharedArrayBuffer) -> Self { - Value::SharedArrayBuffer(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: SharedArrayBuffer<'a>) -> Self { + Value::SharedArrayBuffer(value) } } impl<'a> From> for Object<'a> { - fn from(val: SharedArrayBuffer) -> Self { - Object::SharedArrayBuffer(val.unbind()) + fn from(value: SharedArrayBuffer<'a>) -> Self { + Object::SharedArrayBuffer(value) } } diff --git a/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_constructor.rs b/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_constructor.rs index ff09f9840..ae367f5fb 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_constructor.rs @@ -2,7 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +use crate::ecmascript::abstract_operations::type_conversion::validate_index; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{operations_on_objects::get, type_conversion::to_index}, @@ -56,47 +57,75 @@ impl BuiltinGetter for ArrayBufferGetSpecies {} impl ArrayBufferConstructor { // ### [25.1.4.1 ArrayBuffer ( length \[ , options \] )](https://tc39.es/ecma262/#sec-arraybuffer-constructor) - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Constructor ArrayBuffer requires 'new'", - gc.nogc(), + nogc, )); }; - // 2. Let byteLength be ? ToIndex(length). - let byte_length = to_index(agent, arguments.get(0), gc.reborrow())? as u64; - // 3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options). - let requested_max_byte_length = if arguments.len() > 1 { - get_array_buffer_max_byte_length_option(agent, arguments.get(1), gc.reborrow())? + let new_target = new_target.bind(nogc); + let length = arguments.get(0).bind(nogc); + let options = if arguments.len() > 1 { + Some(arguments.get(1).bind(nogc)) } else { None }; + let (byte_length, new_target, requested_max_byte_length) = + if let (Value::Integer(integer), true) = (length, options.is_none()) { + ( + validate_index(agent, integer.into_i64(), nogc)?, + new_target, + None, + ) + } else { + let options = options.map(|o| o.scope(agent, nogc)); + let new_target = new_target.scope(agent, nogc); + // 2. Let byteLength be ? ToIndex(length). + let byte_length = to_index(agent, length.unbind(), gc.reborrow())? as u64; + // 3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options). + let requested_max_byte_length = if let Some(options) = options { + get_array_buffer_max_byte_length_option( + agent, + options.get(agent), + gc.reborrow(), + )? + } else { + None + }; + ( + byte_length, + new_target.get(agent).bind(gc.nogc()), + requested_max_byte_length, + ) + }; // 4. Return ? AllocateArrayBuffer(NewTarget, byteLength, requestedMaxByteLength). allocate_array_buffer( agent, - Function::try_from(new_target).unwrap(), + Function::try_from(new_target).unwrap().unbind(), byte_length, requested_max_byte_length, - gc.nogc(), + gc.into_nogc(), ) .map(|ab| ab.into_value()) } /// ### [25.1.5.1 ArrayBuffer.isView ( arg )](https://tc39.es/ecma262/#sec-arraybuffer.isview) - fn is_view( + fn is_view<'gc>( _agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. If arg is not an Object, return false. // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. // 3. Return false. @@ -129,15 +158,15 @@ impl ArrayBufferConstructor { /// > subclass constructor may over-ride that default behaviour for the /// > `ArrayBuffer.prototype.slice ( start, end )` method by redefining its /// > `%Symbol.species%` property. - fn species( + fn species<'gc>( _agent: &mut Agent, this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Return the this value. // The value of the "name" property of this function is "get [Symbol.species]". - Ok(this_value) + Ok(this_value.bind(gc.into_nogc())) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -180,6 +209,6 @@ fn get_array_buffer_max_byte_length_option( Ok(None) } else { // 4. Return ? ToIndex(maxByteLength). - Ok(Some(to_index(agent, max_byte_length, gc)? as u64)) + Ok(Some(to_index(agent, max_byte_length.unbind(), gc)? as u64)) } } diff --git a/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_prototype.rs b/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_prototype.rs index f98d8c06e..a82a8daac 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/array_buffer_objects/array_buffer_prototype.rs @@ -3,7 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::type_conversion::try_to_index; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -92,12 +92,12 @@ impl ArrayBufferPrototype { /// /// ArrayBuffer.prototype.byteLength is an accessor property whose set /// accessor function is undefined. - fn get_byte_length( + fn get_byte_length<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. @@ -114,12 +114,12 @@ impl ArrayBufferPrototype { /// ### [25.1.6.3 get ArrayBuffer.prototype.detached](https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.detached) /// /// ArrayBuffer.prototype.detached is an accessor property whose set accessor function is undefined. - fn get_detached( + fn get_detached<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. @@ -131,12 +131,12 @@ impl ArrayBufferPrototype { /// ### [25.1.6.4 get ArrayBuffer.prototype.maxByteLength](https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.maxbytelength) /// /// ArrayBuffer.prototype.maxByteLength is an accessor property whose set accessor function is undefined. - fn get_max_byte_length( + fn get_max_byte_length<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. @@ -153,12 +153,12 @@ impl ArrayBufferPrototype { /// ### [25.1.6.5 get ArrayBuffer.prototype.resizable](https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.resizable) /// /// ArrayBuffer.prototype.resizable is an accessor property whose set accessor function is undefined. - fn get_resizable( + fn get_resizable<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.´ @@ -170,12 +170,12 @@ impl ArrayBufferPrototype { /// ### [25.1.6.6 ArrayBuffer.prototype.resize ( newLength )](https://tc39.es/ecma262/#sec-arraybuffer.prototype.resize) /// /// This method performs the following steps when called: - fn resize( + fn resize<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let new_length = arguments.get(0); // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]). @@ -236,12 +236,12 @@ impl ArrayBufferPrototype { /// ### [25.1.6.7 ArrayBuffer.prototype.slice ( start, end )](https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice) /// /// This method performs the following steps when called: - fn slice( + fn slice<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let start = arguments.get(0); let end = arguments.get(1); // 1. Let O be the this value. @@ -362,12 +362,12 @@ impl ArrayBufferPrototype { /// ### [25.1.6.8 ArrayBuffer.prototype.transfer ( [ newLength ] )](https://tc39.es/ecma262/#sec-arraybuffer.prototype.transfer) /// /// This method performs the following steps when called: - fn transfer( + fn transfer<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Return ? ArrayBufferCopyAndDetach(O, newLength, preserve-resizability). todo!() @@ -376,12 +376,12 @@ impl ArrayBufferPrototype { /// ### [25.1.6.9 ArrayBuffer.prototype.transferToFixedLength ( [ newLength ] )](https://tc39.es/ecma262/#sec-arraybuffer.prototype.transfertofixedlength) /// /// This method performs the following steps when called: - fn transfer_to_fixed_length( + fn transfer_to_fixed_length<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Return ? ArrayBufferCopyAndDetach(O, newLength, fixed-length). todo!() @@ -426,7 +426,7 @@ pub(crate) fn require_internal_slot_array_buffer<'a>( match o { // 1. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 2. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. - Value::ArrayBuffer(array_buffer) => Ok(array_buffer), + Value::ArrayBuffer(array_buffer) => Ok(array_buffer.unbind()), _ => Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Expected this to be ArrayBuffer", diff --git a/nova_vm/src/ecmascript/builtins/structured_data/atomics_object.rs b/nova_vm/src/ecmascript/builtins/structured_data/atomics_object.rs index e50e86675..be9531948 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/atomics_object.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/atomics_object.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::builtins::Behaviour; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -21,8 +22,7 @@ impl Builtin for AtomicsObjectAdd { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::add); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::add); } struct AtomicsObjectAnd; @@ -31,8 +31,7 @@ impl Builtin for AtomicsObjectAnd { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::and); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::and); } struct AtomicsObjectCompareExchange; impl Builtin for AtomicsObjectCompareExchange { @@ -40,8 +39,7 @@ impl Builtin for AtomicsObjectCompareExchange { const LENGTH: u8 = 4; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::compare_exchange); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::compare_exchange); } struct AtomicsObjectExchange; impl Builtin for AtomicsObjectExchange { @@ -49,8 +47,7 @@ impl Builtin for AtomicsObjectExchange { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::exchange); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::exchange); } struct AtomicsObjectIsLockFree; impl Builtin for AtomicsObjectIsLockFree { @@ -58,8 +55,7 @@ impl Builtin for AtomicsObjectIsLockFree { const LENGTH: u8 = 1; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::is_lock_free); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::is_lock_free); } struct AtomicsObjectLoad; impl Builtin for AtomicsObjectLoad { @@ -67,8 +63,7 @@ impl Builtin for AtomicsObjectLoad { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::load); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::load); } struct AtomicsObjectOr; impl Builtin for AtomicsObjectOr { @@ -76,8 +71,7 @@ impl Builtin for AtomicsObjectOr { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::or); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::or); } struct AtomicsObjectStore; impl Builtin for AtomicsObjectStore { @@ -85,8 +79,7 @@ impl Builtin for AtomicsObjectStore { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::store); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::store); } struct AtomicsObjectSub; impl Builtin for AtomicsObjectSub { @@ -94,8 +87,7 @@ impl Builtin for AtomicsObjectSub { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::sub); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::sub); } struct AtomicsObjectWait; impl Builtin for AtomicsObjectWait { @@ -103,8 +95,7 @@ impl Builtin for AtomicsObjectWait { const LENGTH: u8 = 4; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::wait); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::wait); } struct AtomicsObjectWaitAsync; impl Builtin for AtomicsObjectWaitAsync { @@ -112,8 +103,7 @@ impl Builtin for AtomicsObjectWaitAsync { const LENGTH: u8 = 4; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::wait_async); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::wait_async); } struct AtomicsObjectNotify; impl Builtin for AtomicsObjectNotify { @@ -121,8 +111,7 @@ impl Builtin for AtomicsObjectNotify { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::notify); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::notify); } struct AtomicsObjectXor; impl Builtin for AtomicsObjectXor { @@ -130,125 +119,124 @@ impl Builtin for AtomicsObjectXor { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(AtomicsObject::xor); + const BEHAVIOUR: Behaviour = Behaviour::Regular(AtomicsObject::xor); } impl AtomicsObject { - fn add( + fn add<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn and( + fn and<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn compare_exchange( + fn compare_exchange<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn exchange( + fn exchange<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn is_lock_free( + fn is_lock_free<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn load( + fn load<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn or( + fn or<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn store( + fn store<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn sub( + fn sub<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn wait( + fn wait<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn wait_async( + fn wait_async<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn notify( + fn notify<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn xor( + fn xor<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } diff --git a/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_constructor.rs b/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_constructor.rs index 2816a1961..c050b5bb4 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_constructor.rs @@ -6,7 +6,7 @@ use crate::ecmascript::abstract_operations::type_conversion::try_to_index; use crate::ecmascript::builtins::array_buffer::{ ViewedArrayBufferByteLength, ViewedArrayBufferByteOffset, }; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -43,13 +43,13 @@ impl BuiltinIntrinsicConstructor for DataViewConstructor { /// ### [25.3.2 The DataView Constructor](https://tc39.es/ecma262/#sec-dataview-constructor) impl DataViewConstructor { /// ### [25.3.2.1 DataView ( buffer \[ , byteOffset \[ , byteLength \] \] )](https://tc39.es/ecma262/#sec-dataview-buffer-byteoffset-bytelength) - fn constructor( + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( diff --git a/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_prototype.rs b/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_prototype.rs index 304dc34f5..654e4c6a7 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/data_view_objects/data_view_prototype.rs @@ -196,12 +196,12 @@ impl DataViewPrototype { /// /// DataView.prototype.buffer is an accessor property whose set accessor /// function is undefined. - fn get_buffer( + fn get_buffer<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). let gc = gc.into_nogc(); @@ -216,12 +216,12 @@ impl DataViewPrototype { /// /// DataView.prototype.byteLength is an accessor property whose set accessor /// function is undefined. - fn get_byte_length( + fn get_byte_length<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). let gc = gc.into_nogc(); @@ -247,12 +247,12 @@ impl DataViewPrototype { /// /// DataView.prototype.byteOffset is an accessor property whose set accessor /// function is undefined. - fn get_byte_offset( + fn get_byte_offset<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). let gc = gc.into_nogc(); @@ -274,12 +274,12 @@ impl DataViewPrototype { } /// ### [25.3.4.5 DataView.prototype.getBigInt64 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getbigint64) - fn get_big_int64( + fn get_big_int64<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let little_endian = to_boolean(agent, arguments.get(1)); // 1. Let v be the this value. @@ -289,12 +289,12 @@ impl DataViewPrototype { } /// ### [25.3.4.6 DataView.prototype.getBigUint64 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getbiguint64) - fn get_big_uint64( + fn get_big_uint64<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let little_endian = to_boolean(agent, arguments.get(1)); // 1. Let v be the this value. @@ -305,12 +305,12 @@ impl DataViewPrototype { /// ### [7.1 DataView.prototype.getFloat16 ( byteOffset [ , littleEndian ] )](https://tc39.es/proposal-float16array/#sec-dataview.prototype.getfloat16) #[cfg(feature = "proposal-float16array")] - fn get_float16( + fn get_float16<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 2. If littleEndian is not present, set littleEndian to false. let little_endian = to_boolean(agent, arguments.get(1)); @@ -321,12 +321,12 @@ impl DataViewPrototype { } /// ### [25.3.4.7 DataView.prototype.getFloat32 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getfloat32) - fn get_float32( + fn get_float32<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 2. If littleEndian is not present, set littleEndian to false. let little_endian = to_boolean(agent, arguments.get(1)); @@ -337,12 +337,12 @@ impl DataViewPrototype { } /// ### [25.3.4.8 DataView.prototype.getFloat64 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getfloat64) - fn get_float64( + fn get_float64<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 2. If littleEndian is not present, set littleEndian to false. let little_endian = to_boolean(agent, arguments.get(1)); @@ -353,12 +353,12 @@ impl DataViewPrototype { } /// ### [25.3.4.9 DataView.prototype.getInt8 ( byteOffset )](https://tc39.es/ecma262/#sec-dataview.prototype.getint8) - fn get_int8( + fn get_int8<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 1. Let v be the this value. // 2. Return ? GetViewValue(v, byteOffset, true, int8). @@ -366,12 +366,12 @@ impl DataViewPrototype { } /// ### [25.3.4.10 DataView.prototype.getInt16 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getint16) - fn get_int16( + fn get_int16<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 2. If littleEndian is not present, set littleEndian to false. let little_endian = to_boolean(agent, arguments.get(1)); @@ -382,12 +382,12 @@ impl DataViewPrototype { } /// ### [25.3.4.11 DataView.prototype.getInt32 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getint32) - fn get_int32( + fn get_int32<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 2. If littleEndian is not present, set littleEndian to false. let little_endian = to_boolean(agent, arguments.get(1)); @@ -398,12 +398,12 @@ impl DataViewPrototype { } /// ### [25.3.4.12 DataView.prototype.getUint8 ( byteOffset )](https://tc39.es/ecma262/#sec-dataview.prototype.getuint8) - fn get_uint8( + fn get_uint8<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 1. Let v be the this value. // 2. Return ? GetViewValue(v, byteOffset, true, uint8). @@ -411,12 +411,12 @@ impl DataViewPrototype { } /// ### [25.3.4.13 DataView.prototype.getUint16 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getuint16) - fn get_uint16( + fn get_uint16<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 2. If littleEndian is not present, set littleEndian to false. let little_endian = to_boolean(agent, arguments.get(1)); @@ -427,12 +427,12 @@ impl DataViewPrototype { } /// ### [25.3.4.14 DataView.prototype.getUint32 ( byteOffset \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.getuint32) - fn get_uint32( + fn get_uint32<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); // 2. If littleEndian is not present, set littleEndian to false. let little_endian = to_boolean(agent, arguments.get(1)); @@ -443,12 +443,12 @@ impl DataViewPrototype { } /// ### [25.3.4.15 DataView.prototype.setBigInt64 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setbigint64) - fn set_big_int64( + fn set_big_int64<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); let little_endian = to_boolean(agent, arguments.get(2)); @@ -458,12 +458,12 @@ impl DataViewPrototype { } /// ### [25.3.4.16 DataView.prototype.setBigUint64 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setbiguint64) - fn set_big_uint64( + fn set_big_uint64<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); let little_endian = to_boolean(agent, arguments.get(2)); @@ -474,12 +474,12 @@ impl DataViewPrototype { /// ### [7.2 DataView.prototype.setFloat16 ( byteOffset, value [ , littleEndian ] )](https://tc39.es/proposal-float16array/#sec-dataview.prototype.setfloat16) #[cfg(feature = "proposal-float16array")] - fn set_float16( + fn set_float16<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 2. If littleEndian is not present, set littleEndian to false. @@ -490,12 +490,12 @@ impl DataViewPrototype { } /// ### [25.3.4.17 DataView.prototype.setFloat32 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setfloat32) - fn set_float32( + fn set_float32<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 2. If littleEndian is not present, set littleEndian to false. @@ -506,12 +506,12 @@ impl DataViewPrototype { } /// ### [25.3.4.18 DataView.prototype.setFloat64 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setfloat64) - fn set_float64( + fn set_float64<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 2. If littleEndian is not present, set littleEndian to false. @@ -522,12 +522,12 @@ impl DataViewPrototype { } /// ### [25.3.4.19 DataView.prototype.setInt8 ( byteOffset, value )](https://tc39.es/ecma262/#sec-dataview.prototype.setint8) - fn set_int8( + fn set_int8<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 1. Let v be the this value. @@ -536,12 +536,12 @@ impl DataViewPrototype { } /// ### [25.3.4.20 DataView.prototype.setInt16 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setint16) - fn set_int16( + fn set_int16<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 2. If littleEndian is not present, set littleEndian to false. @@ -552,12 +552,12 @@ impl DataViewPrototype { } /// ### [25.3.4.21 DataView.prototype.setInt32 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setint32) - fn set_int32( + fn set_int32<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 2. If littleEndian is not present, set littleEndian to false. @@ -568,12 +568,12 @@ impl DataViewPrototype { } /// ### [25.3.4.22 DataView.prototype.setUint8 ( byteOffset, value )](https://tc39.es/ecma262/#sec-dataview.prototype.setuint8) - fn set_uint8( + fn set_uint8<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 1. Let v be the this value. @@ -582,12 +582,12 @@ impl DataViewPrototype { } /// ### [25.3.4.23 DataView.prototype.setUint16 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setuint16) - fn set_uint16( + fn set_uint16<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 2. If littleEndian is not present, set littleEndian to false. @@ -598,12 +598,12 @@ impl DataViewPrototype { } /// ### [25.3.4.24 DataView.prototype.setUint32 ( byteOffset, value \[ , littleEndian \] )](https://tc39.es/ecma262/#sec-dataview.prototype.setuint32) - fn set_uint32( + fn set_uint32<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let byte_offset = arguments.get(0); let value = arguments.get(1); // 2. If littleEndian is not present, set littleEndian to false. @@ -677,7 +677,7 @@ pub(crate) fn require_internal_slot_data_view<'a>( ) -> JsResult> { match o { // 1. Perform ? RequireInternalSlot(O, [[DataView]]). - Value::DataView(array_buffer) => Ok(array_buffer), + Value::DataView(array_buffer) => Ok(array_buffer.unbind().bind(gc)), _ => Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Expected this to be DataView", diff --git a/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs b/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs index f34563a0e..e0ac2fbba 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs @@ -8,8 +8,9 @@ use crate::ecmascript::abstract_operations::operations_on_objects::{ length_of_array_like, try_create_data_property, try_create_data_property_or_throw, }; use crate::ecmascript::abstract_operations::testing_and_comparison::is_array; +use crate::ecmascript::builtins::Behaviour; use crate::ecmascript::types::{IntoObject, IntoValue}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::{unwrap_try, Scoped}; use crate::{ ecmascript::{ @@ -42,8 +43,7 @@ impl Builtin for JSONObjectParse { const LENGTH: u8 = 2; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(JSONObject::parse); + const BEHAVIOUR: Behaviour = Behaviour::Regular(JSONObject::parse); } struct JSONObjectStringify; @@ -52,8 +52,7 @@ impl Builtin for JSONObjectStringify { const LENGTH: u8 = 3; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(JSONObject::stringify); + const BEHAVIOUR: Behaviour = Behaviour::Regular(JSONObject::stringify); } impl JSONObject { @@ -88,17 +87,17 @@ impl JSONObject { /// > likewise does not apply during JSON.parse, means that not all texts /// > accepted by JSON.parse are valid as a PrimaryExpression, despite /// > matching the grammar. - fn parse( + fn parse<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let text = arguments.get(0); let reviver = arguments.get(1); // 1. Let jsonString be ? ToString(text). - let json_string = to_string(agent, text, gc.reborrow())?; + let json_string = to_string(agent, text, gc.reborrow())?.unbind(); // 2. Parse StringToCodePoints(jsonString) as a JSON text as specified in ECMA-404. Throw a SyntaxError exception if it is not a valid JSON text as defined in that specification. let json_value = match sonic_rs::from_str::(json_string.as_str(agent)) { @@ -146,7 +145,10 @@ impl JSONObject { }; // b. Let rootName be the empty String. - let root_name = String::EMPTY_STRING.to_property_key().scope_static(); + let root_name = String::EMPTY_STRING + .to_property_key() + .unbind() + .scope_static(); // c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered). unwrap_try(try_create_data_property_or_throw( @@ -159,22 +161,22 @@ impl JSONObject { .unwrap(); // d. Return ? InternalizeJSONProperty(root, rootName, reviver). - let root = root.into_object().scope(agent, gc.nogc()); - let reviver = reviver.scope(agent, gc.nogc()); - return internalize_json_property(agent, root, root_name, reviver, gc.reborrow()); + let root = root.unbind().into_object().scope(agent, gc.nogc()); + let reviver = reviver.unbind().scope(agent, gc.nogc()); + return internalize_json_property(agent, root, root_name, reviver, gc); } // 12. Else, // a. Return unfiltered. - Ok(unfiltered) + Ok(unfiltered.unbind()) } - fn stringify( + fn stringify<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } @@ -214,15 +216,15 @@ impl JSONObject { /// > Note 2 /// > In the case where there are duplicate name Strings within an object, /// > lexically preceding values for the same key shall be overwritten. -fn internalize_json_property<'a>( +fn internalize_json_property<'gc, 'a>( agent: &mut Agent, holder: Scoped<'a, Object<'static>>, name: Scoped<'a, PropertyKey<'static>>, reviver: Scoped<'a, Function<'static>>, - mut gc: GcScope<'_, 'a>, -) -> JsResult { + mut gc: GcScope<'gc, 'a>, +) -> JsResult> { // 1. Let val be ? Get(holder, name). - let val = get(agent, holder.get(agent), name.get(agent), gc.reborrow())?; + let val = get(agent, holder.get(agent), name.get(agent), gc.reborrow())?.unbind(); // 2. If val is an Object, then if let Ok(val) = Object::try_from(val) { // a. Let isArray be ? IsArray(val). @@ -246,7 +248,8 @@ fn internalize_json_property<'a>( prop.clone(), reviver.clone(), gc.reborrow(), - )?; + )? + .unbind(); // 3. If newElement is undefined, then if new_element.is_undefined() { @@ -284,7 +287,8 @@ fn internalize_json_property<'a>( p.clone(), reviver.clone(), gc.reborrow(), - )?; + )? + .unbind(); // 2. If newElement is undefined, then if new_element.is_undefined() { @@ -310,17 +314,21 @@ fn internalize_json_property<'a>( // 3. Return ? Call(reviver, holder, « name, val »). // Note: Because this call gets holder as `this`, it can do dirty things to // it, such as `holder[other_key] = new Proxy()`. - let name = name.get(agent).convert_to_value(agent, gc.nogc()); + let name = name.get(agent).convert_to_value(agent, gc.nogc()).unbind(); call_function( agent, reviver.get(agent), holder.get(agent).into_value(), Some(ArgumentsList(&[name, val])), - gc.reborrow(), + gc, ) } -pub(crate) fn value_from_json(agent: &mut Agent, json: &sonic_rs::Value, gc: NoGcScope) -> Value { +pub(crate) fn value_from_json<'gc>( + agent: &mut Agent, + json: &sonic_rs::Value, + gc: NoGcScope<'gc, '_>, +) -> Value<'gc> { match json.get_type() { sonic_rs::JsonType::Null => Value::Null, sonic_rs::JsonType::Boolean => Value::Boolean(json.is_true()), diff --git a/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_constructor.rs b/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_constructor.rs index 81f37b573..4d7e5a15b 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_constructor.rs @@ -39,22 +39,22 @@ impl Builtin for SharedArrayBufferGetSpecies { impl BuiltinGetter for SharedArrayBufferGetSpecies {} impl SharedArrayBufferConstructor { - fn constructor( + fn constructor<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn species( + fn species<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_prototype.rs b/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_prototype.rs index 61a23ab64..d26e159dd 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/shared_array_buffer_objects/shared_array_buffer_prototype.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::types::IntoValue; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -57,48 +58,48 @@ impl Builtin for SharedArrayBufferPrototypeSlice { } impl SharedArrayBufferPrototype { - fn get_byte_length( + fn get_byte_length<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn grow( + fn grow<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_growable( + fn get_growable<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_max_byte_length( + fn get_max_byte_length<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn slice( + fn slice<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_constructor.rs b/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_constructor.rs index a8913482d..00765f185 100644 --- a/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_constructor.rs @@ -18,7 +18,7 @@ use crate::ecmascript::types::PropertyKey; use crate::ecmascript::types::String; use crate::ecmascript::types::Value; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::heap::IntrinsicConstructorIndexes; use crate::heap::WellKnownSymbolIndexes; @@ -44,23 +44,23 @@ impl Builtin for RegExpGetSpecies { impl BuiltinGetter for RegExpGetSpecies {} impl RegExpConstructor { - fn constructor( + fn constructor<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } - fn get_species( + fn get_species<'gc>( _: &mut Agent, this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - Ok(this_value) + _gc: GcScope<'gc, '_>, + ) -> JsResult> { + Ok(this_value.unbind()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { diff --git a/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_prototype.rs b/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_prototype.rs index 650ace995..944885296 100644 --- a/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_prototype.rs @@ -3,7 +3,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::type_conversion::to_boolean; -use crate::engine::context::GcScope; +use crate::ecmascript::types::IntoValue; +use crate::engine::context::{Bindable, GcScope}; use crate::{ ecmascript::{ abstract_operations::{operations_on_objects::get, type_conversion::to_string}, @@ -162,40 +163,41 @@ impl Builtin for RegExpPrototypeGetUnicodeSets { impl BuiltinGetter for RegExpPrototypeGetUnicodeSets {} impl RegExpPrototype { - fn exec( + fn exec<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_dot_all( + fn get_dot_all<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } /// ### [22.2.6.4 get RegExp.prototype.flags](https://tc39.es/ecma262/#sec-get-regexp.prototype.flags) - fn get_flags( + fn get_flags<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); // 1. Let R be the this value. - let r = this_value; + let r = this_value.bind(nogc); // 2. If R is not an Object, throw a TypeError exception. let Ok(r) = Object::try_from(r) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "value is not object", - gc.nogc(), + nogc, )); }; @@ -204,10 +206,10 @@ impl RegExpPrototype { let mut i: usize = 0; // 4. Let hasIndices be ToBoolean(? Get(R, "hasIndices")). - let r = r.scope(agent, gc.nogc()); + let scoped_r = r.scope(agent, nogc); let has_indices_args = get( agent, - r.get(agent), + r.unbind(), BUILTIN_STRING_MEMORY.hasIndices.into(), gc.reborrow(), )?; @@ -222,7 +224,7 @@ impl RegExpPrototype { // 6. Let global be ToBoolean(? Get(R, "global")). let global_args = get( agent, - r.get(agent), + scoped_r.get(agent), BUILTIN_STRING_MEMORY.global.into(), gc.reborrow(), )?; @@ -237,7 +239,7 @@ impl RegExpPrototype { // 8. Let ignoreCase be ToBoolean(? Get(R, "ignoreCase")). let ignore_case_args = get( agent, - r.get(agent), + scoped_r.get(agent), BUILTIN_STRING_MEMORY.ignoreCase.into(), gc.reborrow(), )?; @@ -252,7 +254,7 @@ impl RegExpPrototype { // 10. Let multiline be ToBoolean(? Get(R, "multiline")). let mutliline_args = get( agent, - r.get(agent), + scoped_r.get(agent), BUILTIN_STRING_MEMORY.multiline.into(), gc.reborrow(), )?; @@ -266,7 +268,7 @@ impl RegExpPrototype { // 12. Let dotAll be ToBoolean(? Get(R, "dotAll")). let dot_all_args = get( agent, - r.get(agent), + scoped_r.get(agent), BUILTIN_STRING_MEMORY.dotAll.into(), gc.reborrow(), )?; @@ -281,7 +283,7 @@ impl RegExpPrototype { // 14. Let unicode be ToBoolean(? Get(R, "unicode")). let unicode_args = get( agent, - r.get(agent), + scoped_r.get(agent), BUILTIN_STRING_MEMORY.unicode.into(), gc.reborrow(), )?; @@ -296,7 +298,7 @@ impl RegExpPrototype { // 16. Let unicodeSets be ToBoolean(? Get(R, "unicodeSets")). let unicode_sets_args = get( agent, - r.get(agent), + scoped_r.get(agent), BUILTIN_STRING_MEMORY.unicodeSets.into(), gc.reborrow(), )?; @@ -311,7 +313,7 @@ impl RegExpPrototype { // 18. Let sticky be ToBoolean(? Get(R, "sticky")). let sticky_args = get( agent, - r.get(agent), + scoped_r.get(agent), BUILTIN_STRING_MEMORY.sticky.into(), gc.reborrow(), )?; @@ -325,114 +327,114 @@ impl RegExpPrototype { // 20. Return the String value whose code units are the elements of the List codeUnits. If codeUnits has no elements, the empty String is returned. let res = unsafe { core::str::from_utf8_unchecked(&code_units[0..i]) }; - Ok(Value::from_string(agent, res.to_string(), gc.nogc())) + Ok(Value::from_string(agent, res.to_string(), gc.nogc()).unbind()) } - fn get_global( + fn get_global<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_has_indices( + fn get_has_indices<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_ignore_case( + fn get_ignore_case<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn r#match( + fn r#match<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn match_all( + fn match_all<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_multiline( + fn get_multiline<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn replace( + fn replace<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn search( + fn search<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_source( + fn get_source<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn split( + fn split<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_sticky( + fn get_sticky<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn test( + fn test<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } @@ -443,18 +445,23 @@ impl RegExpPrototype { /// > The returned String has the form of a RegularExpressionLiteral that /// > evaluates to another RegExp object with the same behaviour as this /// > object. - fn to_string( + fn to_string<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); // 1. Let R be the this value. // 2. If R is not an Object, throw a TypeError exception. let Ok(r) = Object::try_from(this_value) else { let error_message = format!( "{} is not an object", - this_value.string_repr(agent, gc.reborrow()).as_str(agent) + this_value + .unbind() + .string_repr(agent, gc.reborrow()) + .as_str(agent) ); return Err(agent.throw_exception(ExceptionType::TypeError, error_message, gc.nogc())); }; @@ -472,16 +479,29 @@ impl RegExpPrototype { data.original_flags.iter_names().for_each(|(flag, _)| { regexp_string.push_str(flag); }); - return Ok(String::from_string(agent, regexp_string, gc.nogc()).into_value()); + return Ok(String::from_string(agent, regexp_string, nogc) + .into_value() + .unbind()); } + let scoped_r = r.scope(agent, nogc); // 3. Let pattern be ? ToString(? Get(R, "source")). - let pattern = get(agent, r, BUILTIN_STRING_MEMORY.source.into(), gc.reborrow())?; - let pattern = to_string(agent, pattern, gc.reborrow())? + let pattern = get( + agent, + r.unbind(), + BUILTIN_STRING_MEMORY.source.into(), + gc.reborrow(), + )?; + let pattern = to_string(agent, pattern.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 4. Let flags be ? ToString(? Get(R, "flags")). - let flags = get(agent, r, BUILTIN_STRING_MEMORY.flags.into(), gc.reborrow())?; - let flags = to_string(agent, flags, gc.reborrow())? + let flags = get( + agent, + scoped_r.get(agent), + BUILTIN_STRING_MEMORY.flags.into(), + gc.reborrow(), + )?; + let flags = to_string(agent, flags.unbind(), gc.reborrow())? .unbind() .bind(gc.nogc()); // 5. Let result be the string-concatenation of "/", pattern, "/", and flags. @@ -490,26 +510,26 @@ impl RegExpPrototype { pattern.get(agent).bind(gc.nogc()).as_str(agent), flags.as_str(agent) ); - let result = String::from_string(agent, result, gc.nogc()); + let result = String::from_string(agent, result, gc.into_nogc()); // 6. Return result. Ok(result.into_value()) } - fn get_unicode( + fn get_unicode<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn get_unicode_sets( + fn get_unicode_sets<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } diff --git a/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_string_iterator_prototype.rs b/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_string_iterator_prototype.rs index 020d0ea93..250daaef2 100644 --- a/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_string_iterator_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/text_processing/regexp_objects/regexp_string_iterator_prototype.rs @@ -2,6 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::builtins::Behaviour; +use crate::ecmascript::types::IntoValue; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -21,17 +23,16 @@ impl Builtin for RegExpStringIteratorPrototypeNext { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(RegExpStringIteratorPrototype::next); + const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpStringIteratorPrototype::next); } impl RegExpStringIteratorPrototype { - fn next( + fn next<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } diff --git a/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_constructor.rs b/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_constructor.rs index 6649bb410..9d9c60902 100644 --- a/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_constructor.rs @@ -5,6 +5,7 @@ use crate::ecmascript::abstract_operations::testing_and_comparison::is_integral_number; use crate::ecmascript::abstract_operations::type_conversion::to_number; use crate::ecmascript::abstract_operations::type_conversion::to_string; +use crate::ecmascript::abstract_operations::type_conversion::to_uint16_number; use crate::ecmascript::builders::builtin_function_builder::BuiltinFunctionBuilder; use crate::ecmascript::builtins::ordinary::get_prototype_from_constructor; use crate::ecmascript::builtins::ordinary::ordinary_object_create_with_intrinsics; @@ -22,11 +23,12 @@ use crate::ecmascript::execution::RealmIdentifier; use crate::ecmascript::types::Function; use crate::ecmascript::types::IntoObject; use crate::ecmascript::types::IntoValue; +use crate::ecmascript::types::Number; use crate::ecmascript::types::Object; use crate::ecmascript::types::String; use crate::ecmascript::types::Value; use crate::ecmascript::types::BUILTIN_STRING_MEMORY; -use crate::engine::context::GcScope; +use crate::engine::context::{Bindable, GcScope}; use crate::heap::IntrinsicConstructorIndexes; use crate::SmallString; @@ -60,40 +62,52 @@ impl Builtin for StringRaw { const NAME: String<'static> = BUILTIN_STRING_MEMORY.raw; } impl StringConstructor { - fn constructor( + /// ### [22.1.1.1 String ( value )](https://tc39.es/ecma262/#sec-string-constructor-string-value) + fn constructor<'gc>( agent: &mut Agent, _this_value: Value, arguments: ArgumentsList, new_target: Option, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let value = arguments.get(0).bind(nogc); + let new_target = new_target.map(|n| n.bind(nogc)); + // 1. If value is not present, then - let s = if arguments.is_empty() { + let (s, new_target) = if arguments.is_empty() { // a. Let s be the empty String. - String::EMPTY_STRING + (String::EMPTY_STRING, new_target) } else { // 2. Else, - let value = arguments.get(0); // a. If NewTarget is undefined and value is a Symbol, return SymbolDescriptiveString(value). if new_target.is_none() { if let Value::Symbol(value) = value { - return Ok(value.descriptive_string(agent, gc.nogc()).into_value()); + return Ok(value + .unbind() + .descriptive_string(agent, gc.into_nogc()) + .into_value()); } } // b. Let s be ? ToString(value). - to_string(agent, value, gc.reborrow())? - .unbind() - .bind(gc.nogc()) + if let Ok(s) = String::try_from(value) { + (s, new_target) + } else { + let new_target = new_target.map(|n| n.scope(agent, gc.nogc())); + let s = to_string(agent, value.unbind(), gc.reborrow())?.unbind(); + let nogc = gc.nogc(); + (s.bind(nogc), new_target.map(|n| n.get(agent).bind(nogc))) + } }; // 3. If NewTarget is undefined, return s. let Some(new_target) = new_target else { - return Ok(s.into_value()); + return Ok(s.into_value().unbind()); }; // 4. Return StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, "%String.prototype%")). let value = s.scope(agent, gc.nogc()); let prototype = get_prototype_from_constructor( agent, - Function::try_from(new_target).unwrap(), + Function::try_from(new_target.unbind()).unwrap(), ProtoIntrinsics::String, gc.reborrow(), )? @@ -122,19 +136,19 @@ impl StringConstructor { // 7. Let length be the length of value. // 8. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). // 9. Return S. - Ok(s.into_value()) + Ok(s.into_value().unbind()) } /// ### [22.1.2.1 String.fromCharCode ( ...`codeUnits` )](https://262.ecma-international.org/15.0/index.html#sec-string.fromcharcode) /// /// This function may be called with any number of arguments which form /// the rest parameter `codeUnits`. - fn from_char_code( + fn from_char_code<'gc>( agent: &mut Agent, _this_value: Value, code_units: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let result be the empty String. // 2. For each element next of codeUnits, do // a. Let nextCU be the code unit whose numeric value is ℝ(? ToUint16(next)). @@ -153,27 +167,40 @@ impl StringConstructor { } } - let mut buf = Vec::with_capacity(code_units.len()); + let buf = if code_units.iter().all(|cu| cu.is_number()) { + code_units + .iter() + .map(|&cu| to_uint16_number(agent, Number::try_from(cu).unwrap())) + .collect::>() + } else { + let scoped_code_units = code_units + .iter() + .map(|cu| cu.scope(agent, gc.nogc())) + .collect::>(); + scoped_code_units + .iter() + .map(|cu| { + let next = cu.get(agent); + next.to_uint16(agent, gc.reborrow()) + }) + .collect::>>()? + }; - for next in code_units.iter() { - let code_unit = next.to_uint16(agent, gc.reborrow())?; - buf.push(code_unit); - } let result = std::string::String::from_utf16_lossy(&buf); - Ok(String::from_string(agent, result, gc.nogc()).into()) + Ok(String::from_string(agent, result, gc.into_nogc()).into()) } /// ### [22.1.2.2 String.fromCodePoint ( ...`codePoints` )](https://tc39.es/ecma262/multipage/text-processing.html#sec-string.fromcodepoint) /// /// This function may be called with any number of arguments which form /// the rest parameter `codePoints`. - fn from_code_point( + fn from_code_point<'gc>( agent: &mut Agent, _this_value: Value, code_points: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 3. Assert: If codePoints is empty, then result is the empty String. if code_points.is_empty() { return Ok(String::EMPTY_STRING.into_value()); @@ -201,41 +228,65 @@ impl StringConstructor { } }; // 1. Let result be the empty String. - let mut result = std::string::String::new(); - // 2. For each element next of codePoints, do - for next in code_points.iter() { - // a. Let nextCP be ? ToNumber(next). - let next_cp = to_number(agent, *next, gc.reborrow())?.unbind(); - // b. If IsIntegralNumber(nextCP) is false, throw a RangeError exception. - if !is_integral_number(agent, next_cp, gc.reborrow()) { - return Err(agent.throw_exception( - ExceptionType::RangeError, - format!("{:?} is not a valid code point", next_cp.to_real(agent)), - gc.nogc(), - )); + let mut result = std::string::String::with_capacity(code_points.len()); + if code_points.iter().all(|cp| cp.is_integer()) { + // 2. For each element next of codePoints, do + for next in code_points.iter() { + let Value::Integer(next_cp) = next else { + unreachable!() + }; + let next_cp = next_cp.into_i64(); + // c. If ℝ(nextCP) < 0 or ℝ(nextCP) > 0x10FFFF, throw a RangeError exception. + if !(0..=0x10FFFF).contains(&next_cp) { + return Err(agent.throw_exception( + ExceptionType::RangeError, + format!("{:?} is not a valid code point", next_cp), + gc.nogc(), + )); + } + // d. Set result to the string-concatenation of result and UTF16EncodeCodePoint(ℝ(nextCP)). + result.push(char::from_u32(next_cp as u32).unwrap()); } - // c. If ℝ(nextCP) < 0 or ℝ(nextCP) > 0x10FFFF, throw a RangeError exception. - let next_cp = next_cp.into_i64(agent); - if !(0..=0x10FFFF).contains(&next_cp) { - return Err(agent.throw_exception( - ExceptionType::RangeError, - format!("{:?} is not a valid code point", next_cp), - gc.nogc(), - )); + } else { + let code_points = code_points + .iter() + .map(|cp| cp.scope(agent, gc.nogc())) + .collect::>(); + // 2. For each element next of codePoints, do + for next in code_points.into_iter() { + // a. Let nextCP be ? ToNumber(next). + let next_cp = to_number(agent, next.get(agent), gc.reborrow())?; + // b. If IsIntegralNumber(nextCP) is false, throw a RangeError exception. + if !is_integral_number(agent, next_cp) { + return Err(agent.throw_exception( + ExceptionType::RangeError, + format!("{:?} is not a valid code point", next_cp.to_real(agent)), + gc.nogc(), + )); + } + // c. If ℝ(nextCP) < 0 or ℝ(nextCP) > 0x10FFFF, throw a RangeError exception. + let next_cp = next_cp.into_i64(agent); + if !(0..=0x10FFFF).contains(&next_cp) { + return Err(agent.throw_exception( + ExceptionType::RangeError, + format!("{:?} is not a valid code point", next_cp), + gc.nogc(), + )); + } + // d. Set result to the string-concatenation of result and UTF16EncodeCodePoint(ℝ(nextCP)). + result.push(char::from_u32(next_cp as u32).unwrap()); } - // d. Set result to the string-concatenation of result and UTF16EncodeCodePoint(ℝ(nextCP)). - result.push(char::from_u32(next_cp as u32).unwrap()); } // 4. Return result. - Ok(String::from_string(agent, result, gc.nogc()).into()) + Ok(String::from_string(agent, result, gc.into_nogc()).into()) } - fn raw( + fn raw<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } diff --git a/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_iterator_objects.rs b/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_iterator_objects.rs index 504dbaaef..26c8273e9 100644 --- a/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_iterator_objects.rs +++ b/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_iterator_objects.rs @@ -2,6 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::builtins::Behaviour; +use crate::ecmascript::types::IntoValue; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -21,17 +23,16 @@ impl Builtin for StringIteratorPrototypeNext { const LENGTH: u8 = 0; - const BEHAVIOUR: crate::ecmascript::builtins::Behaviour = - crate::ecmascript::builtins::Behaviour::Regular(StringIteratorPrototype::next); + const BEHAVIOUR: Behaviour = Behaviour::Regular(StringIteratorPrototype::next); } impl StringIteratorPrototype { - fn next( + fn next<'gc>( _agent: &mut Agent, _this_value: Value, _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!(); } diff --git a/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_prototype.rs b/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_prototype.rs index 982971613..d7e15f67b 100644 --- a/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/text_processing/string_objects/string_prototype.rs @@ -11,12 +11,12 @@ use unicode_normalization::{ use crate::ecmascript::abstract_operations::testing_and_comparison::is_reg_exp; use crate::ecmascript::abstract_operations::type_conversion::{ - to_integer_or_infinity_number, to_string_primitive, try_to_integer_or_infinity, try_to_string, + to_integer_or_infinity_number, to_string_primitive, try_to_integer_or_infinity, try_to_length, + try_to_string, }; use crate::ecmascript::types::Primitive; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; -use crate::SmallInteger; use crate::{ ecmascript::{ abstract_operations::{ @@ -386,27 +386,33 @@ impl Builtin for StringPrototypeSup { } impl StringPrototype { - fn at( + /// ### [22.1.3.1 String.prototype.at ( index )](https://tc39.es/ecma262/#sec-string.prototype.at) + fn at<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let pos = args.get(0); - let (s, relative_index) = - if let (Ok(s), Value::Integer(relative_index)) = (String::try_from(this_value), pos) { - (s.bind(gc.nogc()), relative_index.into_i64()) - } else { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()); - // 4. Let relativeIndex be ? ToIntegerOrInfinity(pos). - let relative_index = to_integer_or_infinity(agent, pos, gc.reborrow())?.into_i64(); - (s.get(agent).bind(gc.nogc()), relative_index) - }; + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let index = args.get(0).bind(nogc); + let (s, relative_index) = if let (Ok(s), Value::Integer(relative_index)) = + (String::try_from(this_value), index) + { + (s, relative_index.into_i64()) + } else { + let index = index.scope(agent, nogc); + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, this_value, nogc)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); + // 4. Let relativeIndex be ? ToIntegerOrInfinity(pos). + let relative_index = + to_integer_or_infinity(agent, index.get(agent), gc.reborrow())?.into_i64(); + (s.get(agent).bind(gc.nogc()), relative_index) + }; // 3. Let len be the length of S. let len = i64::try_from(s.utf16_len(agent)).unwrap(); // 5. If relativeIndex ≥ 0, then @@ -428,26 +434,30 @@ impl StringPrototype { } } - fn char_at( + /// ### [22.1.3.2 String.prototype.charAt ( pos )](https://tc39.es/ecma262/#sec-string.prototype.charat) + fn char_at<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let pos = args.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let pos = args.get(0).bind(nogc); let (s, position) = if let (Ok(s), Value::Integer(position)) = (String::try_from(this_value), pos) { - (s.bind(gc.nogc()), position.into_i64()) + (s, position.into_i64()) } else { + let pos = pos.scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? + let s = to_string(agent, o.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 3. Let position be ? ToIntegerOrInfinity(pos). - let position = to_integer_or_infinity(agent, args.get(0), gc.reborrow())?.into_i64(); + let position = to_integer_or_infinity(agent, pos.get(agent), gc.reborrow())?.into_i64(); (s.get(agent).bind(gc.nogc()), position) }; // 4. Let size be the length of S. @@ -462,26 +472,30 @@ impl StringPrototype { } } - fn char_code_at( + /// ### [22.1.3.3 String.prototype.charCodeAt ( pos )](https://tc39.es/ecma262/#sec-string.prototype.charcodeat) + fn char_code_at<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let pos = args.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let pos = args.get(0).bind(nogc); let (s, position) = if let (Ok(s), Value::Integer(position)) = (String::try_from(this_value), pos) { - (s.bind(gc.nogc()), position.into_i64()) + (s, position.into_i64()) } else { + let pos = pos.scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? + let s = to_string(agent, o.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 3. Let position be ? ToIntegerOrInfinity(pos). - let position = to_integer_or_infinity(agent, args.get(0), gc.reborrow())?.into_i64(); + let position = to_integer_or_infinity(agent, pos.get(agent), gc.reborrow())?.into_i64(); (s.get(agent).bind(gc.nogc()), position) }; // 4. Let size be the length of S. @@ -497,26 +511,30 @@ impl StringPrototype { } } - fn code_point_at( + /// ### [22.1.3.4 String.prototype.codePointAt ( pos )](https://tc39.es/ecma262/#sec-string.prototype.codepointat) + fn code_point_at<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let pos = args.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let pos = args.get(0).bind(nogc); let (s, position) = if let (Ok(s), Value::Integer(position)) = (String::try_from(this_value), pos) { - (s.bind(gc.nogc()), position.into_i64()) + (s, position.into_i64()) } else { + let pos = pos.scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? + let s = to_string(agent, o.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 3. Let position be ? ToIntegerOrInfinity(pos). - let position = to_integer_or_infinity(agent, args.get(0), gc.reborrow())?.into_i64(); + let position = to_integer_or_infinity(agent, pos.get(agent), gc.reborrow())?.into_i64(); (s.get(agent).bind(gc.nogc()), position) }; // 4. Let size be the length of S. @@ -536,12 +554,13 @@ impl StringPrototype { } } - fn concat( + /// ### [22.1.3.5 String.prototype.concat ( ...args )](https://tc39.es/ecma262/#sec-string.prototype.concat) + fn concat<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let nogc = gc.nogc(); let (s, args) = if let Ok(s) = String::try_from(this_value) { (s, &args[..]) @@ -551,19 +570,38 @@ impl StringPrototype { if let TryResult::Continue(s) = try_to_string(agent, o, nogc) { (s?, &args[..]) } else { + let scoped_args = args + .iter() + .map(|arg| arg.scope(agent, nogc)) + .collect::>(); // 2. Let S be ? ToString(O). - // TODO: args should be rooted here. - ( - to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()), - &args[..], - ) + let s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); + let mut scoped_string_args = scoped_args + .into_iter() + .map(|arg| { + let stack_arg = arg.get(agent); + let stack_arg = to_string(agent, stack_arg, gc.reborrow())?; + // SAFETY: args are never shared + Ok(unsafe { arg.replace_self(agent, stack_arg.unbind()) }) + }) + .collect::>>()?; + scoped_string_args.insert(0, s); + let nogc = gc.into_nogc(); + let string_args = scoped_string_args + .into_iter() + .map(|arg| arg.get(agent).bind(nogc)) + .collect::>(); + return Ok(String::concat(agent, &string_args, nogc).into_value()); } }; // 3. Let R be S. - let strings = if args.iter().all(|arg| arg.is_primitive()) { + let (strings, nogc) = if args.iter().all(|arg| arg.is_primitive()) { let mut strings = Vec::with_capacity(args.len() + 1); - strings.push(s); - let nogc = gc.nogc(); + let s = s.unbind(); + let nogc = gc.into_nogc(); + strings.push(s.bind(nogc)); for next in args.iter() { strings.push(to_string_primitive( agent, @@ -571,7 +609,7 @@ impl StringPrototype { nogc, )?); } - strings + (strings, nogc) } else { let mut string_roots = Vec::with_capacity(args.len() + 1); string_roots.push(s.scope(agent, gc.nogc())); @@ -582,27 +620,30 @@ impl StringPrototype { .scope(agent, gc.nogc()), ); } - let nogc = gc.nogc(); - string_roots + let nogc = gc.into_nogc(); + let strings = string_roots .into_iter() .map(|string_root| string_root.get(agent).bind(nogc)) - .collect::>() + .collect::>(); + (strings, nogc) }; // 4. For each element next of args, do // a. Let nextString be ? ToString(next). // b. Set R to the string-concatenation of R and nextString. // 5. Return R. - Ok(String::concat(agent, &strings, gc.nogc()).into_value()) + Ok(String::concat(agent, &strings, nogc).into_value()) } - fn ends_with( + fn ends_with<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_string = args.get(0); - let end_position = args.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_string = args.get(0).bind(nogc); + let end_position = args.get(1).bind(nogc); let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( String::try_from(this_value), @@ -617,16 +658,18 @@ impl StringPrototype { ) { (s, search_str, position.into_i64().max(0) as usize) } else { + let search_string = search_string.scope(agent, nogc); + let end_position = end_position.scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? + let s = to_string(agent, o.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 3. Let isRegExp be ? IsRegExp(searchString). // 4. If isRegExp is true, throw a TypeError exception. - if is_reg_exp(agent, search_string, gc.reborrow())? { + if is_reg_exp(agent, search_string.get(agent), gc.reborrow())? { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "searchString is RegExp", @@ -635,12 +678,13 @@ impl StringPrototype { } // 5. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, search_string, gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()); + let search_str = to_string(agent, search_string.get(agent), gc.reborrow())?.unbind(); + // SAFETY: search_string is not shared. + let search_str = unsafe { search_string.replace_self(agent, search_str) }; // 6. Let len be the length of S. // 7. If endPosition is undefined, let pos be len; + let end_position = end_position.get(agent); let pos = if end_position.is_undefined() { usize::MAX } else { @@ -650,9 +694,10 @@ impl StringPrototype { .max(0) as usize }; + let nogc = gc.nogc(); ( - s.get(agent).bind(gc.nogc()), - search_str.get(agent).bind(gc.nogc()), + s.get(agent).bind(nogc), + search_str.get(agent).bind(nogc), pos, ) }; @@ -684,14 +729,16 @@ impl StringPrototype { )) } - fn includes( + fn includes<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_string = args.get(0); - let position = args.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_string = args.get(0).bind(nogc); + let position = args.get(1).bind(nogc); let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( String::try_from(this_value), @@ -706,16 +753,18 @@ impl StringPrototype { ) { (s, search_str, position.into_i64().max(0) as usize) } else { + let position = position.scope(agent, nogc); + let search_string = search_string.scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? + let s = to_string(agent, o.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 3. Let isRegExp be ? IsRegExp(searchString). // 4. If isRegExp is true, throw a TypeError exception. - if is_reg_exp(agent, search_string, gc.reborrow())? { + if is_reg_exp(agent, search_string.get(agent), gc.reborrow())? { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "searchString is RegExp", @@ -724,20 +773,27 @@ impl StringPrototype { } // 5. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, search_string, gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()); + let search_str = to_string(agent, search_string.get(agent), gc.reborrow())?.unbind(); + + // SAFETY: search_string is not shared. + let search_str = unsafe { search_string.replace_self(agent, search_str) }; // 6. Let pos be ? ToIntegerOrInfinity(position). + let position = position.get(agent); + let position_is_undefined = position.is_undefined(); let pos = to_integer_or_infinity(agent, position, gc.reborrow())? .into_i64() .max(0) as usize; // 7. Assert: If position is undefined, then pos is 0. - assert!(!position.is_undefined() || pos == 0); + assert!(!position_is_undefined || pos == 0); - (s.get(agent), search_str.get(agent), pos) + let nogc = gc.nogc(); + ( + s.get(agent).bind(nogc), + search_str.get(agent).bind(nogc), + pos, + ) }; - let s = s.bind(gc.nogc()); let search_str = search_str.bind(gc.nogc()); // 8. Let len be the length of S. @@ -758,14 +814,17 @@ impl StringPrototype { Ok(Value::from(haystack_str.contains(search_str.as_str(agent)))) } - fn index_of( + /// ### [22.1.3.9 String.prototype.indexOf ( searchString \[ , position \] )](https://tc39.es/ecma262/#sec-string.prototype.indexof) + fn index_of<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_string = args.get(0); - let position = args.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_string = args.get(0).bind(nogc); + let position = args.get(1).bind(nogc); let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( String::try_from(this_value), @@ -780,23 +839,32 @@ impl StringPrototype { ) { (s, search_str, position.into_i64().max(0) as usize) } else { + let search_string = search_string.scope(agent, nogc); + let position = position.scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? + let s = to_string(agent, o.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 3. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, search_string, gc.reborrow())? + let search_str = to_string(agent, search_string.get(agent), gc.reborrow())? .unbind() - .scope(agent, gc.nogc()); + .bind(gc.nogc()); + // SAFETY: search_str is not shared. + let search_str = unsafe { search_string.replace_self(agent, search_str.unbind()) }; // 4. Let pos be ? ToIntegerOrInfinity(position). // 5. Assert: If position is undefined, then pos is 0. - let pos = to_integer_or_infinity(agent, position, gc.reborrow())? + let pos = to_integer_or_infinity(agent, position.get(agent), gc.reborrow())? .into_i64() .max(0) as usize; - (s.get(agent), search_str.get(agent), pos) + let nogc = gc.nogc(); + ( + s.get(agent).bind(nogc), + search_str.get(agent).bind(nogc), + pos, + ) }; // 6. Let len be the length of S. @@ -824,16 +892,17 @@ impl StringPrototype { } } - fn is_well_formed( + /// ### [22.1.3.10 String.prototype.isWellFormed ( )](https://tc39.es/ecma262/#sec-string.prototype.iswellformed) + fn is_well_formed<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())?; + let s = to_string(agent, o.unbind(), gc.reborrow())?; // 3. Return IsStringWellFormedUnicode(S). // TODO: For now, all strings are well-formed Unicode. In the future, `.as_str()` will @@ -852,14 +921,16 @@ impl StringPrototype { /// > otherwise, **`-1𝔽`** is returned. If position is **undefined**, the /// > length of the String value is assumed, so as to search all of the /// > String. - fn last_index_of( + fn last_index_of<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let search_string = args.get(0); - let position = args.get(1); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_string = args.get(0).bind(nogc); + let position = args.get(1).bind(nogc); let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( String::try_from(this_value), @@ -874,17 +945,20 @@ impl StringPrototype { ) { (s, search_str, position.into_i64().max(0) as usize) } else { + let search_string = search_string.scope(agent, nogc); + let position = position.scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())? + let s = to_string(agent, o.unbind(), gc.reborrow())? .unbind() .scope(agent, gc.nogc()); // 3. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, search_string, gc.reborrow())? + let mut search_str = to_string(agent, search_string.get(agent), gc.reborrow())? .unbind() - .scope(agent, gc.nogc()); + .bind(gc.nogc()); + let position = position.get(agent).bind(gc.nogc()); let pos = if position.is_undefined() { // 5. Assert: If position is undefined, then numPos is NaN. // 6. If numPos is NaN, let pos be +∞; @@ -893,9 +967,11 @@ impl StringPrototype { position.into_i64().max(0) as usize } else { // 4. Let numPos be ? ToNumber(position). - let num_pos = to_number(agent, position, gc.reborrow())? + let scoped_search_str = search_str.scope(agent, gc.nogc()); + let num_pos = to_number(agent, position.unbind(), gc.reborrow())? .unbind() .bind(gc.nogc()); + search_str = scoped_search_str.get(agent).bind(gc.nogc()); if num_pos.is_nan(agent) { // 6. If numPos is NaN, let pos be +∞; usize::MAX @@ -907,12 +983,9 @@ impl StringPrototype { } }; - (s.get(agent), search_str.get(agent), pos) + (s.get(agent).bind(gc.nogc()), search_str, pos) }; - let s = s.bind(gc.nogc()); - let search_str = search_str.bind(gc.nogc()); - // 7. Let len be the length of S. // 8. Let searchLen be the length of searchStr. // 9. Let start be the result of clamping pos between 0 and len - searchLen. @@ -945,50 +1018,56 @@ impl StringPrototype { } } - fn locale_compare( + fn locale_compare<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn r#match( + fn r#match<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn match_all( + fn match_all<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } /// ### [22.1.3.15 String.prototype.normalize ( \[ form \] )](https://tc39.es/ecma262/#sec-string.prototype.normalize) - fn normalize( + fn normalize<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let form = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let mut form = arguments.get(0).bind(nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let mut s = if let TryResult::Continue(s) = try_to_string(agent, o, gc.nogc()) { + let mut s = if let TryResult::Continue(s) = try_to_string(agent, o, nogc) { s? } else { - to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()) + let scoped_form = form.scope(agent, nogc); + let s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + form = scoped_form.get(agent).bind(gc.nogc()); + s }; // 3. If form is undefined, let f be "NFC". @@ -996,11 +1075,16 @@ impl StringPrototype { NormalizeForm::Nfc } else { // 4. Else, let f be ? ToString(form). - let s_root = s.scope(agent, gc.nogc()); - let f = to_string(agent, form, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - s = s_root.get(agent).bind(gc.nogc()); + let f = if let TryResult::Continue(f) = try_to_string(agent, form, gc.nogc()) { + f? + } else { + let scoped_s = s.scope(agent, gc.nogc()); + let f = to_string(agent, form.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + s = scoped_s.get(agent).bind(gc.nogc()); + f + }; let form_result = NormalizeForm::from_str(f.as_str(agent)); match form_result { Ok(form) => form, @@ -1018,68 +1102,89 @@ impl StringPrototype { // 6. Let ns be the String value that is the result of normalizing S into the normalization form named by f as specified in the latest Unicode Standard, Normalization Forms. match unicode_normalize(s.as_str(agent), f) { // 7. Return ns. - None => Ok(s.into_value()), - Some(ns) => Ok(Value::from_string(agent, ns, gc.nogc()).into_value()), + None => Ok(s.into_value().unbind()), + Some(ns) => Ok(Value::from_string(agent, ns, gc.into_nogc()).into_value()), } } /// ### [22.1.3.16 String.prototype.padEnd ( maxLength \[ , fillString \] )](https://tc39.es/ecma262/#sec-string.prototype.padend) - fn pad_end( + fn pad_end<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let max_length = arguments.get(0); - let fill_string = arguments.get(1); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let max_length = arguments.get(0).bind(nogc); + let fill_string = arguments.get(1).bind(nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Return ? StringPaddingBuiltinsImpl(O, maxLength, fillString, end). - string_padding_builtins_impl(agent, o, max_length, fill_string, false, gc.reborrow()) + string_padding_builtins_impl( + agent, + o.unbind(), + max_length.unbind(), + fill_string.unbind(), + false, + gc, + ) } /// ### [22.1.3.17 String.prototype.padStart ( maxLength \[ , fillString \] )](https://tc39.es/ecma262/#sec-string.prototype.padstart) - fn pad_start( + fn pad_start<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let max_length = arguments.get(0); - let fill_string = arguments.get(1); + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let max_length = arguments.get(0).bind(nogc); + let fill_string = arguments.get(1).bind(nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Return ? StringPaddingBuiltinsImpl(O, maxLength, fillString, start). - string_padding_builtins_impl(agent, o, max_length, fill_string, true, gc.reborrow()) + string_padding_builtins_impl( + agent, + o.unbind(), + max_length.unbind(), + fill_string.unbind(), + true, + gc, + ) } /// ### [22.1.3.18 String.prototype.repeat ( count )](https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.repeat) - fn repeat( + fn repeat<'gc>( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let count = arguments.get(0); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let count = arguments.get(0).scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let mut s = to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()); + let mut s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); // 3. Let n be ? ToIntegerOrInfinity(count). + let count = count.get(agent); let n = if let TryResult::Continue(n) = try_to_integer_or_infinity(agent, count, gc.nogc()) { n? } else { - let s_root = s.scope(agent, gc.nogc()); + let scoped_s = s.scope(agent, gc.nogc()); let result = to_integer_or_infinity(agent, count, gc.reborrow())?; - s = s_root.get(agent).bind(gc.nogc()); + s = scoped_s.get(agent).bind(gc.nogc()); result }; @@ -1108,61 +1213,63 @@ impl StringPrototype { } if n == 1 { - return Ok(s.into_value()); + return Ok(s.into_value().unbind()); } // 6. Return the String value that is made from n copies of S appended together. Ok(Value::from_string( agent, s.as_str(agent).repeat(n as usize), - gc.nogc(), + gc.into_nogc(), )) } /// ### [22.1.3.19 String.prototype.replace ( searchValue, replaceValue )](https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.replace) - fn replace( + fn replace<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_value = args.get(0).bind(nogc); + let replace_value = args.get(1).scope(agent, nogc); - let search_value = args.get(0); - let replace_value = args.get(1); + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, this_value, nogc)?.scope(agent, nogc); + let scoped_search_value = search_value.scope(agent, nogc); // 2. If searchValue is neither undefined nor null, then if !search_value.is_null() && !search_value.is_undefined() { // a. Let replacer be ? GetMethod(searchValue, %Symbol.replace%). let symbol = WellKnownSymbolIndexes::Replace.into(); - let replacer = get_method(agent, search_value, symbol, gc.reborrow())?; + let replacer = get_method(agent, search_value.unbind(), symbol, gc.reborrow())?; // b. If replacer is not undefined, Return ? Call(replacer, searchValue, « O, replaceValue »). if let Some(replacer) = replacer { return call_function( agent, replacer.unbind(), - search_value, - Some(ArgumentsList(&[o, replace_value])), - gc.reborrow(), + scoped_search_value.get(agent), + Some(ArgumentsList(&[o.get(agent), replace_value.get(agent)])), + gc, ); } } // 3. Let s be ? ToString(O). - let mut s = to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()); - let s_root = s.scope(agent, gc.nogc()); + let s = to_string(agent, o.get(agent), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); // 4. Let searchString be ? ToString(searchValue). - let search_string = to_string(agent, search_value, gc.reborrow())? + let search_string = to_string(agent, scoped_search_value.get(agent), gc.reborrow())? .unbind() .bind(gc.nogc()); - s = s_root.get(agent).bind(gc.nogc()); - // 5. Let functionalReplace be IsCallable(replaceValue). - if let Some(functional_replace) = is_callable(replace_value, gc.nogc()) { + if let Some(functional_replace) = is_callable(replace_value.get(agent), gc.nogc()) { // 7. Let searchLength be the length of searchString. let search_length = search_string.len(agent); @@ -1172,14 +1279,14 @@ impl StringPrototype { position } else { // 9. If position is not-found, return s. - return Ok(s.into_value()); + return Ok(s.get(agent).into_value()); }; // Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)). let args = &[ search_string.unbind().into_value(), Number::from(position as u32).into_value(), - s.into_value().unbind(), + s.get(agent).into_value().unbind(), ]; let result = call_function( agent, @@ -1189,12 +1296,10 @@ impl StringPrototype { gc.reborrow(), )?; - let result = to_string(agent, result, gc.reborrow())? + let result = to_string(agent, result.unbind(), gc.reborrow())? .unbind() .bind(gc.nogc()); - s = s_root.get(agent).bind(gc.nogc()); - // 10. Let preceding be the substring of s from 0 to position. // 11. Let following be the substring of s from position + searchLength. // 12. If functionalReplace is true, @@ -1203,38 +1308,42 @@ impl StringPrototype { // 14. Return the string-concatenation of preceding, replacement, and following. let concatenated_result = format!("{}{}{}", preceding, result.as_str(agent), following); - return Ok(String::from_string(agent, concatenated_result, gc.nogc()).into_value()); + return Ok( + String::from_string(agent, concatenated_result, gc.into_nogc()).into_value(), + ); } let search_string_root = search_string.scope(agent, gc.nogc()); // 6. If functionalReplace is false, Set replaceValue to ? ToString(replaceValue). - let replace_string = to_string(agent, replace_value, gc.reborrow())? + let replace_string = to_string(agent, replace_value.get(agent), gc.reborrow())? .unbind() .bind(gc.nogc()); - s = s_root.get(agent).bind(gc.nogc()); - - let search_string = search_string_root.get(agent).bind(gc.nogc()); // Everything are strings: `"foo".replace("o", "a")` => use rust's replace - let result = - s.as_str(agent) - .replacen(search_string.as_str(agent), replace_string.as_str(agent), 1); - Ok(String::from_string(agent, result, gc.nogc()).into_value()) + let result = s.as_str(agent).replacen( + search_string_root.as_str(agent), + replace_string.as_str(agent), + 1, + ); + Ok(String::from_string(agent, result, gc.into_nogc()).into_value()) } /// ### [22.1.3.20 String.prototype.replaceAll ( searchValue, replaceValue )](https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.replaceall) - fn replace_all( + fn replace_all<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_value = args.get(0).bind(nogc); + let replace_value = args.get(1).scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?.scope(agent, nogc); - let search_value = args.get(0); - let replace_value = args.get(1); + let scoped_search_value = search_value.scope(agent, nogc); // 2. If searchValue is neither undefined nor null, then if !search_value.is_null() && !search_value.is_undefined() { @@ -1251,34 +1360,33 @@ impl StringPrototype { // c. Let replacer be ? GetMethod(searchValue, %Symbol.replace%). let symbol = WellKnownSymbolIndexes::Replace.into(); - let replacer = get_method(agent, search_value, symbol, gc.reborrow())?; + let replacer = get_method(agent, search_value.unbind(), symbol, gc.reborrow())?; // d. If replacer is not undefined, Return ? Call(replacer, searchValue, « O, replaceValue »). if let Some(replacer) = replacer { return call_function( agent, replacer.unbind(), - search_value, - Some(ArgumentsList(&[o, replace_value])), - gc.reborrow(), + scoped_search_value.get(agent), + Some(ArgumentsList(&[o.get(agent), replace_value.get(agent)])), + gc, ); } } // 3. Let s be ? ToString(O). - let mut s = to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()); - let s_root = s.scope(agent, gc.nogc()); + let s = to_string(agent, o.get(agent), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); // 4. Let searchString be ? ToString(searchValue). - let mut search_string = to_string(agent, search_value, gc.reborrow())? + let mut search_string = to_string(agent, scoped_search_value.get(agent), gc.reborrow())? .unbind() .bind(gc.nogc()); let search_string_root = search_string.scope(agent, gc.nogc()); - s = s_root.get(agent).bind(gc.nogc()); - // 5. Let functionalReplace be IsCallable(replaceValue). - if let Some(functional_replace) = is_callable(replace_value, gc.nogc()) { + if let Some(functional_replace) = is_callable(replace_value.get(agent), gc.nogc()) { // 7. Let searchLength be the length of searchString. let search_length = search_string.len(agent); @@ -1301,7 +1409,7 @@ impl StringPrototype { // If none has found, return s. if match_positions.is_empty() { - return Ok(s.into_value()); + return Ok(s.get(agent).into_value()); } // 12. Let endOfLastMatch be 0. @@ -1313,26 +1421,24 @@ impl StringPrototype { // 14. For each element p of matchPositions, do let functional_replace = functional_replace.scope(agent, gc.nogc()); for p in match_positions { - search_string = search_string_root.get(agent); - s = s_root.get(agent); // b. let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(p), string »)). let replacement = call_function( agent, functional_replace.get(agent), Value::Undefined, Some(ArgumentsList(&[ - search_string.into_value(), + search_string_root.get(agent).into_value(), Number::from(position as u32).into_value(), - s.into_value(), + s.get(agent).into_value(), ])), gc.reborrow(), )?; + let replacement = to_string(agent, replacement.unbind(), gc.reborrow())?; // a. Let preserved be the substring of string from endOfLastMatch to p. let preserved = &subject[end_of_last_match..p]; // d. Set result to the string-concatenation of result, preserved, and replacement. - let replacement_str = replacement.to_string(agent, gc.reborrow())?; - let replacement_str = replacement_str.as_str(agent); + let replacement_str = replacement.as_str(agent); result.reserve(preserved.len() + replacement_str.len()); result.push_str(preserved); result.push_str(replacement_str); @@ -1346,59 +1452,63 @@ impl StringPrototype { } // 16. Return result. - return Ok(String::from_string(agent, result, gc.nogc()).into_value()); + return Ok(String::from_string(agent, result, gc.into_nogc()).into_value()); } // 6. If functionalReplace is false, Set replaceValue to ? ToString(replaceValue). - let replace_string = to_string(agent, replace_value, gc.reborrow())? + let replace_string = to_string(agent, replace_value.get(agent), gc.reborrow())? .unbind() .bind(gc.nogc()); // Everything are strings: `"foo".replaceAll("o", "a")` => use rust's replace search_string = search_string_root.get(agent).bind(gc.nogc()); - s = s_root.get(agent).bind(gc.nogc()); + let s = s.get(agent).bind(gc.nogc()); let result = s .as_str(agent) .replace(search_string.as_str(agent), replace_string.as_str(agent)); - Ok(String::from_string(agent, result, gc.nogc()).into_value()) + Ok(String::from_string(agent, result, gc.into_nogc()).into_value()) } - fn search( + fn search<'gc>( _agent: &mut Agent, _this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { todo!() } - fn slice( + fn slice<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let start = args.get(0).scope(agent, nogc); + let end = args.get(1).scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - let mut s = to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()); - let s_root = s.scope(agent, gc.nogc()); + let s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); // 3. Let len be the length of S. // 4. Let intStart be ? ToIntegerOrInfinity(start). - let int_start = to_integer_or_infinity(agent, args.get(0), gc.reborrow())?; - s = s_root.get(agent).bind(gc.nogc()); + let int_start = to_integer_or_infinity(agent, start.get(agent), gc.reborrow())?; // 5. If intStart = -∞, let from be 0. // NOTE: We use `None` when `from` would be `len` in the spec. let from = if int_start.is_neg_infinity() { Some(0) } else if int_start.is_negative() { // 6. Else if intStart < 0, let from be max(len + intStart, 0). - let len = i64::try_from(s.utf16_len(agent)).unwrap(); + let len = i64::try_from(s.get(agent).utf16_len(agent)).unwrap(); let int_start = int_start.into_i64(); Some((len + int_start).max(0) as usize) } else { // 7. Else, let from be min(intStart, len). - let len = s.utf16_len(agent); + let len = s.get(agent).utf16_len(agent); let int_start = int_start.into_i64() as usize; if int_start >= len { None @@ -1409,22 +1519,22 @@ impl StringPrototype { // 8. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end). // NOTE: We use `None` when `to` would be `len` in the spec. - let to = if args.get(1).is_undefined() { + let end = end.get(agent).bind(gc.nogc()); + let to = if end.is_undefined() { None } else { - let int_end = to_integer_or_infinity(agent, args.get(1), gc.reborrow())?; - s = s_root.get(agent).bind(gc.nogc()); + let int_end = to_integer_or_infinity(agent, end.unbind(), gc.reborrow())?; // 9. If intEnd = -∞, let to be 0. if int_end.is_neg_infinity() { Some(0) } else if int_end.is_negative() { // 10. Else if intEnd < 0, let to be max(len + intEnd, 0). - let len = i64::try_from(s.utf16_len(agent)).unwrap(); + let len = i64::try_from(s.get(agent).utf16_len(agent)).unwrap(); let int_end = int_end.into_i64(); Some((len + int_end).max(0) as usize) } else { // 11. Else, let to be min(intEnd, len). - let len = s.utf16_len(agent); + let len = s.get(agent).utf16_len(agent); let int_end = int_end.into_i64() as usize; if int_end >= len { None @@ -1434,6 +1544,9 @@ impl StringPrototype { } }; + let gc = gc.into_nogc(); + let s = s.get(agent).bind(gc); + // 12. If from ≥ to, return the empty String. // 13. Return the substring of S from from to to. let substring = match (from, to) { @@ -1453,77 +1566,82 @@ impl StringPrototype { // SAFETY: The memory for `substring` (and for the WTF-8 representation // of `s`) won't be moved or deallocated before this function returns. let substring: &'static str = unsafe { core::mem::transmute(substring) }; - Ok(String::from_str(agent, substring, gc.nogc()).into_value()) + Ok(String::from_str(agent, substring, gc).into_value()) } /// ### [22.1.3.23 String.prototype.split ( separator, limit )](https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.split) - fn split( + fn split<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let separator = args.get(0).scope(agent, nogc); + let limit = args.get(1).scope(agent, nogc); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?.scope(agent, nogc); // 2. If separator is neither undefined nor null, then - let separator = args.get(0); - if matches!(separator, Value::Undefined | Value::Null) { + let stack_separator = separator.get(agent).bind(gc.nogc()); + if matches!(stack_separator, Value::Undefined | Value::Null) { let symbol = WellKnownSymbolIndexes::Split.into(); // If splitter is not undefined, then return ? Call(splitter, separator, « O, limit »). - if let Ok(Some(splitter)) = get_method(agent, separator, symbol, gc.reborrow()) { + if let Ok(Some(splitter)) = + get_method(agent, stack_separator.unbind(), symbol, gc.reborrow()) + { return call_function( agent, splitter.unbind(), - separator, - Some(ArgumentsList(&[o, args.get(1)])), - gc.reborrow(), + separator.get(agent), + Some(ArgumentsList(&[o.get(agent), limit.get(agent)])), + gc, ); } } // 3. Let S be ? ToString(O). - let mut s = to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()); - let s_root = s.scope(agent, gc.nogc()); + let s = to_string(agent, o.get(agent), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + let scoped_s = s.scope(agent, gc.nogc()); - let limit = args.get(1); - let lim = match limit { + let lim = limit.get(agent).bind(gc.nogc()); + let lim = match limit.get(agent) { // 4. If limit is undefined, lim is 2**32 - 1. Value::Undefined => u32::MAX, // else let lim be ℝ(? ToUint32(limit)). // Note: Fast path for integer parameter. Value::Integer(value) => value.into_i64() as u32, - _ => to_uint32(agent, limit, gc.reborrow())?, + _ => to_uint32(agent, lim.unbind(), gc.reborrow())?, }; // 5. Let R be ? ToString(separator). - let r = to_string(agent, separator, gc.reborrow())? - .unbind() - .bind(gc.nogc()); + let r = to_string(agent, separator.get(agent), gc.reborrow())?.unbind(); + + let gc = gc.into_nogc(); + + let s = scoped_s.get(agent).bind(gc); + let r = r.bind(gc); // 6. If lim is zero, return an empty array if lim == 0 { - return Ok(create_array_from_list(agent, &[], gc.nogc()).into_value()); + return Ok(create_array_from_list(agent, &[], gc).into_value()); } // 7. If separator is undefined, return an array with the whole string - if separator.is_undefined() { - return Ok(create_array_from_list( - agent, - &[s_root.get(agent).bind(gc.nogc()).into_value()], - gc.nogc(), - ) - .into_value()); + if separator.get(agent).is_undefined() { + return Ok(create_array_from_list(agent, &[s.into_value()], gc).into_value()); } // 8. Let separatorLength be the length of R. let separator_length = r.len(agent); - // 9. If separatorLength = 0, the split by characters + // 9. If separatorLength = 0, then split into characters if separator_length == 0 { - s = s_root.get(agent).bind(gc.nogc()); let subject = s.as_str(agent); let head = subject.split(""); @@ -1539,15 +1657,15 @@ impl StringPrototype { results.pop(); } - let results = Array::from_slice(agent, results.as_slice(), gc.nogc()); + let results = Array::from_slice(agent, results.as_slice(), gc); return Ok(results.into_value()); } // 10. If S is the empty String, return CreateArrayFromList(« S »). - let s = s_root.get(agent).bind(gc.nogc()); + let s = scoped_s.get(agent).bind(gc); if s.is_empty_string() { - let list: [Value; 1] = [s.into_value()]; - return Ok(create_array_from_list(agent, &list, gc.nogc()).into_value()); + let list: [Value; 1] = [s.into_value().unbind()]; + return Ok(create_array_from_list(agent, &list, gc).into_value()); } // 11-17. Normal split @@ -1560,109 +1678,162 @@ impl StringPrototype { if lim as usize == i { break; } - results.push(Value::from_str(agent, part, gc.nogc())); + results.push(Value::from_str(agent, part, gc)); } - let results = Array::from_slice(agent, results.as_slice(), gc.nogc()); + let results = Array::from_slice(agent, results.as_slice(), gc); Ok(results.into_value()) } - fn starts_with( + /// ### [22.1.3.24 String.prototype.startsWith ( searchString \[ , position \] )] + fn starts_with<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; - // 2. Let S be ? ToString(O). - let mut s = to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let search_string = args.get(0).bind(nogc); + let position = args.get(1).bind(nogc); - let s_root = s.scope(agent, gc.nogc()); + let position_option = if position.is_undefined() { + Some(0) + } else if let Value::Integer(position) = position { + Some(position.into_i64().max(0) as usize) + } else { + None + }; - // 3. Let isRegExp be ? IsRegExp(searchString). - // 4. If isRegExp is true, throw a TypeError exception. - if is_reg_exp(agent, args.get(0), gc.reborrow())? { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "searchString is RegExp", - gc.nogc(), - )); - } + let (s, search_str, start) = if let (Ok(s), Ok(search_string), Some(position)) = ( + String::try_from(this_value), + String::try_from(search_string), + position_option, + ) { + (s, search_string, position) + } else { + let search_string = search_string.scope(agent, nogc); + let position = position.scope(agent, nogc); - // 5. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, args.get(0), gc.reborrow())? - .unbind() - .scope(agent, gc.nogc()); + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, this_value, nogc)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); - // 6. Let len be the length of S. - // 7. If position is undefined, let pos be 0; else let pos be ? ToIntegerOrInfinity(endPosition). - // 8. Let start be the result of clamping pos between 0 and len. - let position = args.get(1); - let haystack_str = if position.is_undefined() { - s = s_root.get(agent).bind(gc.nogc()); - s.as_str(agent) - } else { - let pos = to_integer_or_infinity(agent, position, gc.reborrow())?; - s = s_root.get(agent).bind(gc.nogc()); - if pos.is_negative() { - s.as_str(agent) - } else { - let len = s.utf16_len(agent); - let pos = pos.into_i64() as usize; - if pos >= len { - "" - } else { - let u8_idx = s.utf8_index(agent, pos).unwrap(); - &s.as_str(agent)[u8_idx..] - } + // 3. Let isRegExp be ? IsRegExp(searchString). + // 4. If isRegExp is true, throw a TypeError exception. + if is_reg_exp(agent, search_string.get(agent), gc.reborrow())? { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "searchString is RegExp", + gc.nogc(), + )); } + + // 5. Let searchStr be ? ToString(searchString). + let search_str = to_string(agent, search_string.get(agent), gc.reborrow())? + .unbind() + .scope(agent, gc.nogc()); + + // 6. Let len be the length of S. + // 7. If position is undefined, let pos be 0; else let pos be + // ? ToIntegerOrInfinity(endPosition). + // 8. Let start be the result of clamping pos between 0 and len. + let position = position.get(agent); + let start = if position.is_undefined() { + 0 + } else { + to_integer_or_infinity(agent, position, gc.reborrow())? + .into_i64() + .max(0) as usize + }; + + ( + s.get(agent).bind(gc.nogc()), + search_str.get(agent).bind(gc.nogc()), + start, + ) }; // 9. Let searchLength be the length of searchStr. // 10. If searchLength = 0, return true. - // 11. Let start be end - searchLength. - // 12. If start < 0, return false. + if search_str.len(agent) == 0 { + return Ok(true.into()); + } + // 11. Let end be start + searchLength. + // 12. If end > len, return false. // 13. Let substring be the substring of S from start to end. // 14. If substring is searchStr, return true. // 15. Return false. - Ok(Value::from( - haystack_str.starts_with(search_str.get(agent).as_str(agent)), - )) + let haystack_str = if start == 0 { + s.as_str(agent) + } else { + let len = s.utf16_len(agent); + if start >= len { + "" + } else { + let start = s.utf8_index(agent, start).unwrap(); + &s.as_str(agent)[start..] + } + }; + Ok(haystack_str.starts_with(search_str.as_str(agent)).into()) } - fn substring( + fn substring<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let nogc = gc.nogc(); - let start = args.get(0).bind(nogc); - let end = args.get(1).bind(nogc); - let s = if let Ok(s) = String::try_from(this_value) { + let this_value = this_value.bind(nogc); + let start = args.get(0).scope(agent, nogc); + let end = args.get(1).scope(agent, nogc); + let mut s = if let Ok(s) = String::try_from(this_value) { s.bind(nogc) } else { // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()) + to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()) }; - let s = s.scope(agent, gc.nogc()); + let mut scoped_s = None; // 3. Let len be the length of S. // 4. Let intStart be ? ToIntegerOrInfinity(start). - let int_start = to_integer_or_infinity(agent, start, gc.reborrow())?; + // SAFETY: Never shared. + let start = unsafe { start.take(agent) }.bind(gc.nogc()); + let int_start = if let TryResult::Continue(int_start) = + try_to_integer_or_infinity(agent, start, gc.nogc()) + { + int_start? + } else { + let local_scoped_s = s.scope(agent, gc.nogc()); + let int_start = to_integer_or_infinity(agent, start.unbind(), gc.reborrow())?; + s = local_scoped_s.get(agent).bind(gc.nogc()); + scoped_s = Some(local_scoped_s); + int_start + }; // 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end). + let end = end.get(agent).bind(gc.nogc()); let int_end = if end.is_undefined() { None + } else if let TryResult::Continue(int_end) = + try_to_integer_or_infinity(agent, end, gc.nogc()) + { + Some(int_end?) } else { - Some(to_integer_or_infinity(agent, end, gc.reborrow())?) + let local_scoped_s = scoped_s.unwrap_or_else(|| s.scope(agent, gc.nogc())); + let int_end = to_integer_or_infinity(agent, end.unbind(), gc.reborrow())?; + s = local_scoped_s.get(agent).bind(gc.nogc()); + Some(int_end) }; - let s = s.get(agent).bind(gc.nogc()); - // Fast path: can we return `s` without computing the UTF-16 length? // We can if int_start <= 0 and we know int_end must be >= len // (i.e. it's either None or is greater than the UTF-8 length). @@ -1671,7 +1842,7 @@ impl StringPrototype { .map(|int_end| int_end.into_i64() >= s.len(agent) as i64) .unwrap_or(true) { - return Ok(s.into_value()); + return Ok(s.into_value().unbind()); } let len = s.utf16_len(agent); @@ -1704,97 +1875,104 @@ impl StringPrototype { // SAFETY: The memory for `substring` (and for the WTF-8 representation // of `s`) won't be moved or deallocated before this function returns. let substring: &'static str = unsafe { core::mem::transmute(substring) }; - Ok(String::from_str(agent, substring, gc.nogc()).into_value()) + Ok(String::from_str(agent, substring, gc.into_nogc()).into_value()) } - fn to_locale_lower_case( + /// ### [22.1.3.26 String.prototype.toLocaleLowerCase ( \[ reserved1 \[ , reserved2 \] \] )](https://tc39.es/ecma262/#sec-string.prototype.tolocalelowercase) + fn to_locale_lower_case<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? RequireObjectCoercible(this value). let o: Value = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())?; + let s = to_string(agent, o.unbind(), gc.reborrow())?; // 3. Let sText be [StringToCodePoints](https://tc39.es/ecma262/#sec-stringtocodepoints)(S). // 4. Let lowerText be toLowercase(sText), according to the Unicode Default Case Conversion algorithm. // 5. Let L be [CodePointsToString](https://tc39.es/ecma262/#sec-codepointstostring)(lowerText). // 6. Return L. let lower_case_string: std::string::String = s.as_str(agent).to_lowercase(); - Ok(String::from_string(agent, lower_case_string, gc.nogc()).into_value()) + Ok(String::from_string(agent, lower_case_string, gc.into_nogc()).into_value()) } - fn to_locale_upper_case( + /// ### [22.1.3.27 String.prototype.toLocaleUpperCase ( \[ reserved1 \[ , reserved2 \] \] )](https://tc39.es/ecma262/#sec-string.prototype.tolocaleuppercase) + fn to_locale_upper_case<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())?; + let s = to_string(agent, o.unbind(), gc.reborrow())?; // 3. Let sText be [StringToCodePoints](https://tc39.es/ecma262/#sec-stringtocodepoints)(S). // 4. Let upperText be toUppercase(sText), according to the Unicode Default Case Conversion algorithm. // 5. Let L be [CodePointsToString](https://tc39.es/ecma262/#sec-codepointstostring)(upperText). // 6. Return L. let upper_case_string = s.as_str(agent).to_uppercase(); - Ok(String::from_string(agent, upper_case_string, gc.nogc()).into_value()) + Ok(String::from_string(agent, upper_case_string, gc.into_nogc()).into_value()) } + /// ### [22.1.3.28 String.prototype.toLowerCase ( )](https://tc39.es/ecma262/#sec-string.prototype.tolowercase) + /// /// > NOTE: The implementation might not reflect the spec. - fn to_lower_case( + fn to_lower_case<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())?; + let s = to_string(agent, o.unbind(), gc.reborrow())?; // 3. Let sText be [StringToCodePoints](https://tc39.es/ecma262/#sec-stringtocodepoints)(S). // 4. Let lowerText be toLowercase(sText), according to the Unicode Default Case Conversion algorithm. // 5. Let L be [CodePointsToString](https://tc39.es/ecma262/#sec-codepointstostring)(lowerText). // 6. Return L. let lower_case_string = s.as_str(agent).to_lowercase(); - Ok(String::from_string(agent, lower_case_string, gc.nogc()).into_value()) + Ok(String::from_string(agent, lower_case_string, gc.into_nogc()).into_value()) } + /// ### [22.1.3.30 String.prototype.toUpperCase ( )](https://tc39.es/ecma262/#sec-string.prototype.touppercase) + /// /// > NOTE: The implementation might not reflect the spec. - fn to_upper_case( + fn to_upper_case<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())?; + let s = to_string(agent, o.unbind(), gc.reborrow())?; // 3. Let sText be [StringToCodePoints](https://tc39.es/ecma262/#sec-stringtocodepoints)(S). // 4. Let upperText be toUppercase(sText), according to the Unicode Default Case Conversion algorithm. // 5. Let L be [CodePointsToString](https://tc39.es/ecma262/#sec-codepointstostring)(upperText). // 6. Return L. let upper_case_string = s.as_str(agent).to_uppercase(); - Ok(String::from_string(agent, upper_case_string, gc.nogc()).into_value()) + Ok(String::from_string(agent, upper_case_string, gc.into_nogc()).into_value()) } - fn to_well_formed( + /// ### [22.1.3.31 String.prototype.toWellFormed ( )](https://tc39.es/ecma262/#sec-string.prototype.towellformed) + fn to_well_formed<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let O be ? RequireObjectCoercible(this value). let o = require_object_coercible(agent, this_value, gc.nogc())?; // 2. Let S be ? ToString(O). - let s = to_string(agent, o, gc.reborrow())?; + let s = to_string(agent, o.unbind(), gc.reborrow())?; // 3. Let strLen be the length of S. // 4. Let k be 0. @@ -1811,34 +1989,34 @@ impl StringPrototype { // TODO: For now, all strings are well-formed Unicode. In the future, `.as_str()` will // return None for WTF-8 strings. let _: &str = s.as_str(agent); - Ok(s.into_value()) + Ok(s.into_value().unbind()) } /// ### [22.1.3.32 String.prototype.trim ( )](https://tc39.es/ecma262/#sec-string.prototype.trim) - fn trim( + fn trim<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? TrimString(S, start+end). Self::trim_string(agent, this_value, TrimWhere::StartAndEnd, gc) } /// #### [22.1.3.32.1 String.prototype.trimString ( )](https://tc39.es/ecma262/#sec-trimstring) - fn trim_string( + fn trim_string<'gc>( agent: &mut Agent, value: Value, trim_where: TrimWhere, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let str be ? RequireObjectCoercible(string). let str = require_object_coercible(agent, value, gc.nogc())?; // 2. Let S be ? ToString(str) - let s = to_string(agent, str, gc.reborrow())?.unbind(); - let gc = gc.nogc(); + let s = to_string(agent, str.unbind(), gc.reborrow())?.unbind(); + let gc = gc.into_nogc(); let s = s.bind(gc); let s_str = s.as_str(agent); @@ -1866,24 +2044,24 @@ impl StringPrototype { } /// ### [22.1.3.33 String.prototype.trimEnd ( )](https://tc39.es/ecma262/#sec-string.prototype.trimend) - fn trim_end( + fn trim_end<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? TrimString(S, end). Self::trim_string(agent, this_value, TrimWhere::End, gc) } /// ### [22.1.3.34 String.prototype.trimStart ( )](https://tc39.es/ecma262/#sec-string.prototype.trimstart) - fn trim_start( + fn trim_start<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? TrimString(S, start). Self::trim_string(agent, this_value, TrimWhere::Start, gc) @@ -1894,23 +2072,50 @@ impl StringPrototype { /// /// > NOTE: `String.prototype.toString` and `String.prototype.valueOf` are /// > different functions but have the exact same steps. - fn value_of( + fn value_of<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Return ? ThisStringValue(this value). - this_string_value(agent, this_value, gc.nogc()).map(|string| string.into_value()) + this_string_value(agent, this_value, gc.into_nogc()) + .map(|string| string.into_value().unbind()) } - fn iterator( - _agent: &mut Agent, - _this_value: Value, + /// ### [22.1.3.36 String.prototype \[ %Symbol.iterator% \] ( )](https://tc39.es/ecma262/#sec-string.prototype-%symbol.iterator%) + fn iterator<'gc>( + agent: &mut Agent, + this_value: Value, _: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - todo!() + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, this_value, nogc)?; + // 2. Let s be ? ToString(O). + let _s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + // 3. Let closure be a new Abstract Closure with no parameters that + // captures s and performs the following steps when called: + fn closure<'gc>(agent: &mut Agent, s: String, gc: GcScope) -> JsResult> { + let s = s.bind(gc.nogc()); + // a. Let len be the length of s. + let _len = s.utf16_len(agent); + // b. Let position be 0. + // c. Repeat, while position < len, + // i. Let cp be CodePointAt(s, position). + // ii. Let nextIndex be position + cp.[[CodeUnitCount]]. + // iii. Let resultString be the substring of s from position to nextIndex. + // iv. Set position to nextIndex. + // v. Perform ? GeneratorYield(CreateIteratorResultObject(resultString, false)). + todo!() + } + // d. Return undefined. + // 4. Return CreateIteratorFromClosure(closure, "%StringIteratorPrototype%", %StringIteratorPrototype%). + Ok(Value::Iterator) } /// ### [B.2.2.1 String.prototype.substr ( start, length )](https://tc39.es/ecma262/#sec-string.prototype.substr) @@ -1922,22 +2127,26 @@ impl StringPrototype { /// sourceLength is the length of the String. The result is a String value, /// not a String object. #[cfg(feature = "annex-b-string")] - fn substr( + fn substr<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - mut gc: GcScope, - ) -> JsResult { - let start = args.get(0).bind(gc.nogc()); - let length = args.get(1).bind(gc.nogc()); + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { + let nogc = gc.nogc(); + let this_value = this_value.bind(nogc); + let start = args.get(0).scope(agent, nogc); + let length = args.get(1).scope(agent, nogc); let s = if let Ok(s) = String::try_from(this_value) { - s.bind(gc.nogc()) + s.bind(nogc) } else { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value, gc.nogc())?; + let o = require_object_coercible(agent, this_value, nogc)?; // 2. Let S be ? ToString(O). - to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()) + to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()) }; let scoped_s = s.scope(agent, gc.nogc()); @@ -1945,7 +2154,7 @@ impl StringPrototype { let size = s.utf16_len(agent) as i64; // 4. Let intStart be ? ToIntegerOrInfinity(start). - let int_start = to_integer_or_infinity(agent, start, gc.reborrow())?; + let int_start = to_integer_or_infinity(agent, start.get(agent), gc.reborrow())?; // 5. If intStart = -∞, set intStart to 0. let int_start = if int_start.is_neg_infinity() { @@ -1959,10 +2168,10 @@ impl StringPrototype { }; // 8. If length is undefined, let intLength be size; otherwise let intLength be ? ToIntegerOrInfinity(length). - let int_length = if length.is_undefined() { + let int_length = if length.get(agent).is_undefined() { size } else { - to_integer_or_infinity(agent, length, gc.reborrow())?.into_i64() + to_integer_or_infinity(agent, length.get(agent), gc.reborrow())?.into_i64() }; // 9. Set intLength to the result of clamping intLength between 0 and size. @@ -1972,161 +2181,196 @@ impl StringPrototype { let int_end = (int_start + int_length).min(size); // 11. Return the substring of S from intStart to intEnd. - let s = scoped_s.get(agent).bind(gc.nogc()); + let gc = gc.into_nogc(); + let s = scoped_s.get(agent).bind(gc); let s_str = s.as_str(agent); Ok(String::from_string( agent, s_str[int_start as usize..int_end as usize].to_string(), - gc.nogc(), + gc, ) .into_value()) } /// ### [B.2.2.2 String.prototype.anchor ( name )](https://tc39.es/ecma262/#sec-string.prototype.anchor) #[cfg(feature = "annex-b-string")] - fn anchor( + fn anchor<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let name = args.get(0).bind(gc.nogc()); // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "a", "name", name). - create_html(agent, this_value, "a", Some(("name", name)), gc).map(Value::from) + create_html(agent, this_value, "a", Some(("name", name.unbind())), gc).map(Value::from) } /// ### [B.2.2.3 String.prototype.big ( )](https://tc39.es/ecma262/#sec-string.prototype.big) - fn big(agent: &mut Agent, this_value: Value, _: ArgumentsList, gc: GcScope) -> JsResult { + #[cfg(feature = "annex-b-string")] + fn big<'gc>( + agent: &mut Agent, + this_value: Value, + _: ArgumentsList, + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "big", "", ""). create_html(agent, this_value, "big", None, gc).map(Value::from) } /// ### [B.2.2.4 String.prototype.blink ( )](https://tc39.es/ecma262/#sec-string.prototype.blink) - fn blink( + #[cfg(feature = "annex-b-string")] + fn blink<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "blink", "", ""). create_html(agent, this_value, "blink", None, gc).map(Value::from) } /// ### [B.2.2.5 String.prototype.bold ( )](https://tc39.es/ecma262/#sec-string.prototype.bold) - fn bold( + #[cfg(feature = "annex-b-string")] + fn bold<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "b", "", ""). create_html(agent, this_value, "b", None, gc).map(Value::from) } /// ### [B.2.2.6 String.prototype.fixed ( )](https://tc39.es/ecma262/#sec-string.prototype.fixed) - fn fixed( + #[cfg(feature = "annex-b-string")] + fn fixed<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "tt", "", ""). create_html(agent, this_value, "tt", None, gc).map(Value::from) } /// ### [B.2.2.7 String.prototype.fontcolor ( colour )](https://tc39.es/ecma262/#sec-string.prototype.fontcolor) - fn fontcolor( + #[cfg(feature = "annex-b-string")] + fn fontcolor<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let colour = args.get(0).bind(gc.nogc()); // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "font", "colour", colour). - create_html(agent, this_value, "font", Some(("colour", colour)), gc).map(Value::from) + create_html( + agent, + this_value, + "font", + Some(("colour", colour.unbind())), + gc, + ) + .map(Value::from) } /// ### [B.2.2.8 String.prototype.fontsize ( size )](https://tc39.es/ecma262/#sec-string.prototype.fontsize) - fn fontsize( + #[cfg(feature = "annex-b-string")] + fn fontsize<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let size = args.get(0).bind(gc.nogc()); // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "font", "size", size). - create_html(agent, this_value, "font", Some(("size", size)), gc).map(Value::from) + create_html(agent, this_value, "font", Some(("size", size.unbind())), gc).map(Value::from) } /// ### [B.2.2.9 String.prototype.italics ( )](https://tc39.es/ecma262/#sec-string.prototype.italics) - fn italics( + #[cfg(feature = "annex-b-string")] + fn italics<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "tt", "", ""). create_html(agent, this_value, "i", None, gc).map(Value::from) } /// ### [B.2.2.10 String.prototype.link ( url )](https://tc39.es/ecma262/#sec-string.prototype.link) - fn link( + #[cfg(feature = "annex-b-string")] + fn link<'gc>( agent: &mut Agent, this_value: Value, args: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { let url = args.get(0).bind(gc.nogc()); // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "a", "href", url). - create_html(agent, this_value, "a", Some(("href", url)), gc).map(Value::from) + create_html(agent, this_value, "a", Some(("href", url.unbind())), gc).map(Value::from) } /// ### [B.2.2.11 String.prototype.small ( )](https://tc39.es/ecma262/#sec-string.prototype.small) - fn small( + #[cfg(feature = "annex-b-string")] + fn small<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "small", "", ""). create_html(agent, this_value, "small", None, gc).map(Value::from) } /// ### [B.2.2.12 String.prototype.strike ( )](https://tc39.es/ecma262/#sec-string.prototype.strike) - fn strike( + #[cfg(feature = "annex-b-string")] + fn strike<'gc>( agent: &mut Agent, this_value: Value, _: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "strike", "", ""). create_html(agent, this_value, "strike", None, gc).map(Value::from) } /// ### [B.2.2.13 String.prototype.sub ( )](https://tc39.es/ecma262/#sec-string.prototype.sub) - fn sub(agent: &mut Agent, this_value: Value, _: ArgumentsList, gc: GcScope) -> JsResult { + #[cfg(feature = "annex-b-string")] + fn sub<'gc>( + agent: &mut Agent, + this_value: Value, + _: ArgumentsList, + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "sub", "", ""). create_html(agent, this_value, "sub", None, gc).map(Value::from) } /// ### [B.2.2.14 String.prototype.sup ( )](https://tc39.es/ecma262/#sec-string.prototype.sup) - fn sup(agent: &mut Agent, this_value: Value, _: ArgumentsList, gc: GcScope) -> JsResult { + #[cfg(feature = "annex-b-string")] + fn sup<'gc>( + agent: &mut Agent, + this_value: Value, + _: ArgumentsList, + gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. Let S be the this value. // 2. Return ? CreateHTML(S, "sup", "", ""). create_html(agent, this_value, "sup", None, gc).map(Value::from) @@ -2240,34 +2484,63 @@ impl StringPrototype { /// fillString (an ECMAScript language value), and placement (start or end) /// and returns either a normal completion containing a String or a throw /// completion. -fn string_padding_builtins_impl( +fn string_padding_builtins_impl<'gc>( agent: &mut Agent, o: Value, max_length: Value, fill_string: Value, placement_start: bool, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { + let nogc = gc.nogc(); + let o = o.bind(nogc); + let mut max_length = max_length.bind(nogc); + let mut fill_string = fill_string.bind(nogc); + let mut scoped_fill_string = None; // 1. Let S be ? ToString(O). - let mut s = to_string(agent, o, gc.reborrow())?.unbind().bind(gc.nogc()); - let mut s_root = None; - - // 2. Let intMaxLength be ℝ(? ToLength(maxLength)). - let int_max_length = if let Value::Integer(int_max_length) = max_length { - int_max_length.into_i64().clamp(0, SmallInteger::MAX_NUMBER) + let mut s = if let TryResult::Continue(s) = try_to_string(agent, o, nogc) { + s? } else { - s_root = Some(s.scope(agent, gc.nogc())); - let result = to_length(agent, max_length, gc.reborrow())?; - s = s_root.as_ref().unwrap().get(agent).bind(gc.nogc()); - result + scoped_fill_string = Some(fill_string.scope(agent, nogc)); + let scoped_max_length = max_length.scope(agent, nogc); + let s = to_string(agent, o.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + // SAFETY: never leaked outside this call path. + max_length = unsafe { scoped_max_length.take(agent).bind(gc.nogc()) }; + fill_string = scoped_fill_string + .as_ref() + .unwrap() + .get(agent) + .bind(gc.nogc()); + s }; + let mut scoped_s = None; + + // 2. Let intMaxLength be ℝ(? ToLength(maxLength)). + let int_max_length = + if let TryResult::Continue(int_max_length) = try_to_length(agent, max_length, gc.nogc()) { + int_max_length? + } else { + scoped_s = Some(s.scope(agent, gc.nogc())); + scoped_fill_string = + scoped_fill_string.or_else(|| Some(fill_string.scope(agent, gc.nogc()))); + let int_max_length = to_length(agent, max_length.unbind(), gc.reborrow())?; + s = scoped_s.as_ref().unwrap().get(agent).bind(gc.nogc()); + fill_string = scoped_fill_string + .as_ref() + .unwrap() + .get(agent) + .bind(gc.nogc()); + int_max_length + }; // 3. Let stringLength be the length of S. let string_length = s.utf16_len(agent) as i64; // 4. If intMaxLength ≤ stringLength, return S. if int_max_length <= string_length { - return Ok(s.into()); + return Ok(s.unbind().into()); } // 5. If fillString is undefined, set fillString to the String value consisting solely of the code unit 0x0020 (SPACE). @@ -2276,26 +2549,25 @@ fn string_padding_builtins_impl( } else if let Ok(fill_string) = String::try_from(fill_string) { fill_string } else { - if s_root.is_none() { - s_root = Some(s.scope(agent, gc.nogc())); + if scoped_s.is_none() { + scoped_s = Some(s.scope(agent, gc.nogc())); } // 6. Else, set fillString to ? ToString(fillString). - let result = to_string(agent, fill_string, gc.reborrow())? + let result = to_string(agent, fill_string.unbind(), gc.reborrow())? .unbind() .bind(gc.nogc()); - s = s_root.unwrap().get(agent).bind(gc.nogc()); + s = scoped_s.unwrap().get(agent).bind(gc.nogc()); result }; + let s = s.unbind(); + let fill_string = fill_string.unbind(); + let gc = gc.into_nogc(); + let s = s.bind(gc); + let fill_string = fill_string.bind(gc); + // 7. Return StringPad(S, intMaxLength, fillString, placement). - string_pad( - agent, - s, - int_max_length, - fill_string, - placement_start, - gc.nogc(), - ) + string_pad(agent, s, int_max_length, fill_string, placement_start, gc) } /// ### [22.1.3.17.2 StringPad ( S, maxLength, fillString, placement )](https://tc39.es/ecma262/#sec-stringpad) @@ -2303,14 +2575,14 @@ fn string_padding_builtins_impl( /// The abstract operation StringPad takes arguments S (a String), /// maxLength (a non-negative integer), fillString (a String), and /// placement (start or end) and returns a String. -fn string_pad( +fn string_pad<'gc>( agent: &mut Agent, - s: String, + s: String<'gc>, max_len: i64, - fill_string: String, + fill_string: String<'gc>, placement_start: bool, - gc: NoGcScope, -) -> JsResult { + gc: NoGcScope<'gc, '_>, +) -> JsResult> { // 1. Let stringLength be the length of S. let string_len = s.utf16_len(agent) as i64; @@ -2384,7 +2656,7 @@ fn string_pad( /// or a throw completion. fn this_string_value<'gc>( agent: &mut Agent, - value: Value, + value: Value<'gc>, gc: NoGcScope<'gc, '_>, ) -> JsResult> { match value { @@ -2427,14 +2699,26 @@ fn create_html<'gc>( attribute_and_value: Option<(&str, Value)>, mut gc: GcScope<'gc, '_>, ) -> JsResult> { + let nogc = gc.nogc(); // 1. Let str be ? RequireObjectCoercible(string). - let str = require_object_coercible(agent, string, gc.nogc())?; + let string = string.bind(nogc); + let mut attribute_and_value = + attribute_and_value.map(|(attribute, value)| (attribute, value.bind(nogc))); + let str = require_object_coercible(agent, string, nogc)?; // 2. Let S be ? ToString(str) - let s = to_string(agent, str, gc.reborrow())? - .unbind() - .bind(gc.nogc()); - let scoped_s = s.scope(agent, gc.nogc()); + let mut s = if let TryResult::Continue(s) = try_to_string(agent, str.unbind(), nogc) { + s? + } else { + let attribute_and_scoped_value = + attribute_and_value.map(|(attribute, value)| (attribute, value.scope(agent, nogc))); + let s = to_string(agent, str.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + attribute_and_value = attribute_and_scoped_value + .map(|(attribute, scoped_value)| (attribute, scoped_value.get(agent).bind(gc.nogc()))); + s + }; // 3. Let p1 be the string-concatenation of "<" and tag. // 4. If attribute is not the empty String, then @@ -2452,13 +2736,19 @@ fn create_html<'gc>( // 8. Return p4. if let Some((attribute, value)) = attribute_and_value { // a. Let V be ? ToString(value). - let v = to_string(agent, value, gc.reborrow())? - .unbind() - .bind(gc.nogc()); + let v = if let TryResult::Continue(v) = try_to_string(agent, value, gc.nogc()) { + v? + } else { + let scoped_s = s.scope(agent, gc.nogc()); + let v = to_string(agent, value.unbind(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); + s = scoped_s.get(agent).bind(gc.nogc()); + v + }; // b. Let escapedV be the String value that is the same as V except that each occurrence of the code unit 0x0022 (QUOTATION MARK) in V has been replaced with the six code unit sequence """. let escaped_v = v.as_str(agent).replace('"', """); - let s = scoped_s.get(agent).bind(gc.nogc()); let s_str = s.as_str(agent); Ok(String::from_string( agent, @@ -2466,7 +2756,6 @@ fn create_html<'gc>( gc.into_nogc(), )) } else { - let s = scoped_s.get(agent).bind(gc.nogc()); let s_str = s.as_str(agent); Ok(String::from_string( agent, diff --git a/nova_vm/src/ecmascript/builtins/typed_array.rs b/nova_vm/src/ecmascript/builtins/typed_array.rs index d89201d0d..567e832ca 100644 --- a/nova_vm/src/ecmascript/builtins/typed_array.rs +++ b/nova_vm/src/ecmascript/builtins/typed_array.rs @@ -19,7 +19,7 @@ use crate::{ }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::HeapRootData, unwrap_try, Scoped, TryResult, }, @@ -207,8 +207,8 @@ impl<'a> From> for TypedArrayIndex<'a> { } } -impl IntoValue for TypedArray<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for TypedArray<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -219,9 +219,9 @@ impl<'a> IntoObject<'a> for TypedArray<'a> { } } -impl From> for Value { - fn from(val: TypedArray) -> Self { - match val.unbind() { +impl<'a> From> for Value<'a> { + fn from(value: TypedArray<'a>) -> Self { + match value { TypedArray::Int8Array(idx) => Value::Int8Array(idx), TypedArray::Uint8Array(idx) => Value::Uint8Array(idx), TypedArray::Uint8ClampedArray(idx) => Value::Uint8ClampedArray(idx), @@ -240,8 +240,8 @@ impl From> for Value { } impl<'a> From> for Object<'a> { - fn from(val: TypedArray) -> Self { - match val.unbind() { + fn from(value: TypedArray<'a>) -> Self { + match value { TypedArray::Int8Array(idx) => Object::Int8Array(idx), TypedArray::Uint8Array(idx) => Object::Uint8Array(idx), TypedArray::Uint8ClampedArray(idx) => Object::Uint8ClampedArray(idx), @@ -259,10 +259,10 @@ impl<'a> From> for Object<'a> { } } -impl TryFrom for TypedArray<'_> { +impl<'a> TryFrom> for TypedArray<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Int8Array(base_index) => Ok(TypedArray::Int8Array(base_index)), Value::Uint8Array(base_index) => Ok(TypedArray::Uint8Array(base_index)), @@ -397,7 +397,7 @@ impl<'a> InternalMethods<'a> for TypedArray<'a> { // [[Configurable]]: true // }. TryResult::Continue(Some(PropertyDescriptor { - value: Some(value.into_value()), + value: Some(value.into_value().unbind()), writable: Some(true), enumerable: Some(true), configurable: Some(true), @@ -577,13 +577,13 @@ impl<'a> InternalMethods<'a> for TypedArray<'a> { } /// ### [10.4.5.5 Infallible \[\[Get\]\] ( P, Receiver )](https://tc39.es/ecma262/#sec-typedarray-get) - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { // 1. 1. If P is a String, then // a. Let numericIndex be CanonicalNumericIndexString(P). // b. If numericIndex is not undefined, then @@ -613,13 +613,13 @@ impl<'a> InternalMethods<'a> for TypedArray<'a> { } /// ### [10.4.5.5 \[\[Get\]\] ( P, Receiver )](https://tc39.es/ecma262/#sec-typedarray-get) - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { // 1. 1. If P is a String, then // a. Let numericIndex be CanonicalNumericIndexString(P). // b. If numericIndex is not undefined, then diff --git a/nova_vm/src/ecmascript/builtins/typed_array/data.rs b/nova_vm/src/ecmascript/builtins/typed_array/data.rs index 241257f45..ab713786a 100644 --- a/nova_vm/src/ecmascript/builtins/typed_array/data.rs +++ b/nova_vm/src/ecmascript/builtins/typed_array/data.rs @@ -10,6 +10,7 @@ use crate::{ }, types::OrdinaryObject, }, + engine::context::Bindable, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, }; @@ -73,7 +74,7 @@ pub struct TypedArrayHeapData { impl TypedArrayHeapData { pub fn new(object_index: Option>) -> Self { Self { - object_index: object_index.map(|o| o.unbind()), + object_index: object_index.unbind(), viewed_array_buffer: ArrayBuffer::_def(), byte_length: Default::default(), byte_offset: Default::default(), diff --git a/nova_vm/src/ecmascript/builtins/weak_map.rs b/nova_vm/src/ecmascript/builtins/weak_map.rs index a819413dc..921c1e3c5 100644 --- a/nova_vm/src/ecmascript/builtins/weak_map.rs +++ b/nova_vm/src/ecmascript/builtins/weak_map.rs @@ -11,7 +11,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::{BaseIndex, WeakMapIndex}, CreateHeapData, HeapMarkAndSweep, @@ -65,8 +69,8 @@ impl WeakMap<'_> { } } -impl IntoValue for WeakMap<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for WeakMap<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -77,15 +81,15 @@ impl<'a> IntoObject<'a> for WeakMap<'a> { } } -impl From> for Value { - fn from(val: WeakMap) -> Self { - Value::WeakMap(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: WeakMap<'a>) -> Self { + Value::WeakMap(value) } } impl<'a> From> for Object<'a> { - fn from(val: WeakMap) -> Self { - Object::WeakMap(val.unbind()) + fn from(value: WeakMap<'a>) -> Self { + Object::WeakMap(value) } } diff --git a/nova_vm/src/ecmascript/builtins/weak_map/data.rs b/nova_vm/src/ecmascript/builtins/weak_map/data.rs index dc56f9d12..28bcc4ed7 100644 --- a/nova_vm/src/ecmascript/builtins/weak_map/data.rs +++ b/nova_vm/src/ecmascript/builtins/weak_map/data.rs @@ -18,8 +18,8 @@ pub struct WeakMapHeapData { // ValueHash is created using a Value.hash(agent) function and connects to // an index; the index points to a key and value in parallel vector / Vec2. // Note that empty slots are deleted values in the ParallelVec. - pub(crate) keys: Vec, - pub(crate) values: Vec, + pub(crate) keys: Vec>, + pub(crate) values: Vec>, // TODO: When an non-terminal (start or end) iterator exists for the Map, // the items in the map cannot be compacted. // pub(crate) observed: bool; diff --git a/nova_vm/src/ecmascript/builtins/weak_ref.rs b/nova_vm/src/ecmascript/builtins/weak_ref.rs index e8f39de74..b8aa28440 100644 --- a/nova_vm/src/ecmascript/builtins/weak_ref.rs +++ b/nova_vm/src/ecmascript/builtins/weak_ref.rs @@ -11,7 +11,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::{BaseIndex, WeakRefIndex}, CreateHeapData, Heap, HeapMarkAndSweep, @@ -64,8 +68,8 @@ impl WeakRef<'_> { } } -impl IntoValue for WeakRef<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for WeakRef<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -76,15 +80,15 @@ impl<'a> IntoObject<'a> for WeakRef<'a> { } } -impl From> for Value { - fn from(val: WeakRef) -> Self { - Value::WeakRef(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: WeakRef<'a>) -> Self { + Value::WeakRef(value) } } impl<'a> From> for Object<'a> { - fn from(val: WeakRef) -> Self { - Object::WeakRef(val.unbind()) + fn from(value: WeakRef<'a>) -> Self { + Object::WeakRef(value) } } diff --git a/nova_vm/src/ecmascript/builtins/weak_ref/data.rs b/nova_vm/src/ecmascript/builtins/weak_ref/data.rs index dfe750365..807ec7a9f 100644 --- a/nova_vm/src/ecmascript/builtins/weak_ref/data.rs +++ b/nova_vm/src/ecmascript/builtins/weak_ref/data.rs @@ -10,7 +10,7 @@ use crate::{ #[derive(Debug, Clone)] pub struct WeakRefHeapData { pub(crate) object_index: Option>, - pub(crate) value: Value, + pub(crate) value: Value<'static>, pub(crate) is_strong: bool, } diff --git a/nova_vm/src/ecmascript/builtins/weak_set.rs b/nova_vm/src/ecmascript/builtins/weak_set.rs index 484fa8512..17c945343 100644 --- a/nova_vm/src/ecmascript/builtins/weak_set.rs +++ b/nova_vm/src/ecmascript/builtins/weak_set.rs @@ -11,7 +11,11 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{context::NoGcScope, rootable::HeapRootData, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + rootable::HeapRootData, + Scoped, + }, heap::{ indexes::{BaseIndex, WeakSetIndex}, CompactionLists, CreateHeapData, HeapMarkAndSweep, WorkQueues, @@ -65,8 +69,8 @@ impl WeakSet<'_> { } } -impl IntoValue for WeakSet<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for WeakSet<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -77,15 +81,15 @@ impl<'a> IntoObject<'a> for WeakSet<'a> { } } -impl From> for Value { - fn from(val: WeakSet) -> Self { - Value::WeakSet(val.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: WeakSet<'a>) -> Self { + Value::WeakSet(value) } } impl<'a> From> for Object<'a> { - fn from(val: WeakSet) -> Self { - Object::WeakSet(val.unbind()) + fn from(value: WeakSet<'a>) -> Self { + Object::WeakSet(value) } } diff --git a/nova_vm/src/ecmascript/execution/agent.rs b/nova_vm/src/ecmascript/execution/agent.rs index 703bd7dbc..4853e2b44 100644 --- a/nova_vm/src/ecmascript/execution/agent.rs +++ b/nova_vm/src/ecmascript/execution/agent.rs @@ -18,7 +18,7 @@ use crate::{ builtins::{control_abstraction_objects::promise_objects::promise_abstract_operations::promise_jobs::{PromiseReactionJob, PromiseResolveThenableJob}, error::ErrorHeapData, promise::Promise}, scripts_and_modules::ScriptOrModule, types::{Function, IntoValue, Object, Reference, String, Symbol, Value}, - }, engine::{context::{GcScope, NoGcScope}, rootable::HeapRootData, TryResult, Vm}, heap::{heap_gc::heap_gc, CreateHeapData, HeapMarkAndSweep, PrimitiveHeapIndexable}, Heap + }, engine::{context::{Bindable, GcScope, NoGcScope}, rootable::HeapRootData, TryResult, Vm}, heap::{heap_gc::heap_gc, CreateHeapData, HeapMarkAndSweep, PrimitiveHeapIndexable}, Heap }; use core::{any::Any, cell::RefCell, ptr::NonNull}; @@ -32,14 +32,14 @@ pub type JsResult = core::result::Result; #[derive(Debug, Default, Clone, Copy)] #[repr(transparent)] -pub struct JsError(Value); +pub struct JsError(Value<'static>); impl JsError { - pub(crate) fn new(value: Value) -> Self { + pub(crate) fn new(value: Value<'static>) -> Self { Self(value) } - pub fn value(self) -> Value { + pub fn value(self) -> Value<'static> { self.0 } @@ -48,6 +48,21 @@ impl JsError { } } +// SAFETY: Property implemented as a recursive bind. +unsafe impl Bindable for JsError { + type Of<'a> = JsError; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + self + } + + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + self + } +} + impl HeapMarkAndSweep for JsError { fn mark_values(&self, queues: &mut crate::heap::WorkQueues) { self.0.mark_values(queues); diff --git a/nova_vm/src/ecmascript/execution/environments.rs b/nova_vm/src/ecmascript/execution/environments.rs index 394876b2c..76282121d 100644 --- a/nova_vm/src/ecmascript/execution/environments.rs +++ b/nova_vm/src/ecmascript/execution/environments.rs @@ -42,7 +42,7 @@ pub(crate) use global_environment::GlobalEnvironment; pub(crate) use object_environment::ObjectEnvironment; pub(crate) use private_environment::PrivateEnvironment; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::{ ecmascript::types::{Base, Object, Reference, String, Value}, @@ -487,13 +487,13 @@ impl EnvironmentIndex { /// does not exist throw a ReferenceError exception. If the binding exists /// but is uninitialized a ReferenceError is thrown, regardless of the /// value of S. - pub(crate) fn try_get_binding_value( + pub(crate) fn try_get_binding_value<'gc>( self, agent: &mut Agent, name: String, is_strict: bool, - gc: NoGcScope, - ) -> TryResult> { + gc: NoGcScope<'gc, '_>, + ) -> TryResult>> { match self { EnvironmentIndex::Declarative(idx) => { TryResult::Continue(idx.get_binding_value(agent, name, is_strict, gc)) @@ -515,19 +515,19 @@ impl EnvironmentIndex { /// does not exist throw a ReferenceError exception. If the binding exists /// but is uninitialized a ReferenceError is thrown, regardless of the /// value of S. - pub(crate) fn get_binding_value( + pub(crate) fn get_binding_value<'gc>( self, agent: &mut Agent, name: String, is_strict: bool, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { match self { EnvironmentIndex::Declarative(idx) => { - idx.get_binding_value(agent, name, is_strict, gc.nogc()) + idx.get_binding_value(agent, name, is_strict, gc.into_nogc()) } EnvironmentIndex::Function(idx) => { - idx.get_binding_value(agent, name, is_strict, gc.nogc()) + idx.get_binding_value(agent, name, is_strict, gc.into_nogc()) } EnvironmentIndex::Global(idx) => idx.get_binding_value(agent, name, is_strict, gc), EnvironmentIndex::Object(idx) => idx.get_binding_value(agent, name, is_strict, gc), diff --git a/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs b/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs index 6a5521f6c..9ea32b13f 100644 --- a/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs @@ -10,7 +10,7 @@ use crate::{ execution::{agent::ExceptionType, Agent, JsResult}, types::{Object, String, Value}, }, - engine::context::NoGcScope, + engine::context::{Bindable, NoGcScope}, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, }; @@ -33,7 +33,7 @@ pub(crate) struct DeclarativeEnvironment { #[derive(Debug, Clone, Copy)] pub(crate) struct Binding { - pub(crate) value: Option, + pub(crate) value: Option>, // TODO: Pack these into bitfields. pub(super) strict: bool, pub(super) mutable: bool, @@ -111,7 +111,7 @@ impl DeclarativeEnvironment { // 2. Set the bound value for N in envRec to V. // 3. Record that the binding for N in envRec has been initialized. // Note: Initialization status of N is determined by the Some/None. - binding.value = Some(value); + binding.value = Some(value.unbind()); // 4. Return UNUSED. } @@ -299,7 +299,7 @@ impl DeclarativeEnvironmentIndex { // 4. Else if the binding for N in envRec is a mutable binding, then if binding.mutable { // a. Change its bound value to V. - binding.value = Some(value); + binding.value = Some(value.unbind()); } // 5. Else, else { @@ -328,24 +328,24 @@ impl DeclarativeEnvironmentIndex { /// throw completion. It returns the value of its bound identifier whose /// name is N. If the binding exists but is uninitialized a ReferenceError /// is thrown, regardless of the value of S. - pub(crate) fn get_binding_value( + pub(crate) fn get_binding_value<'gc>( self, agent: &mut Agent, name: String, is_strict: bool, - gc: NoGcScope, - ) -> JsResult { + gc: NoGcScope<'gc, '_>, + ) -> JsResult> { let env_rec = &agent[self]; // Delegate to heap data record method. - env_rec.get_binding_value(name, is_strict).map_or_else( - || { + match env_rec.get_binding_value(name, is_strict) { + Some(res) => Ok(res.bind(gc)), + None => { // 2. If the binding for N in envRec is an uninitialized binding, throw // a ReferenceError exception. let error_message = format!("Identifier '{}' does not exist.", name.as_str(agent)); Err(agent.throw_exception(ExceptionType::ReferenceError, error_message, gc)) - }, - Ok, - ) + } + } } /// ### [9.1.1.1.7 DeleteBinding ( N )](https://tc39.es/ecma262/#sec-declarative-environment-records-deletebinding-n) diff --git a/nova_vm/src/ecmascript/execution/environments/function_environment.rs b/nova_vm/src/ecmascript/execution/environments/function_environment.rs index 39525608b..c195452e0 100644 --- a/nova_vm/src/ecmascript/execution/environments/function_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/function_environment.rs @@ -6,7 +6,7 @@ use super::{ DeclarativeEnvironment, DeclarativeEnvironmentIndex, EnvironmentIndex, FunctionEnvironmentIndex, }; use crate::ecmascript::types::OrdinaryObject; -use crate::engine::context::NoGcScope; +use crate::engine::context::{Bindable, NoGcScope}; use crate::engine::unwrap_try; use crate::{ ecmascript::{ @@ -40,7 +40,7 @@ pub(crate) struct FunctionEnvironment { /// ### \[\[ThisValue\]\] /// /// This is the this value used for this invocation of the function. - pub(crate) this_value: Option, + pub(crate) this_value: Option>, /// ### \[\[ThisBindingStatus\]\] /// @@ -170,7 +170,7 @@ pub(crate) fn new_class_static_element_environment( DeclarativeEnvironmentIndex::last(&agent.heap.environments.declarative); let env = FunctionEnvironment { - this_value: Some(class_constructor.into_value()), + this_value: Some(class_constructor.into_value().unbind()), function_object: class_constructor.unbind(), @@ -203,7 +203,7 @@ pub(crate) fn new_class_field_initializer_environment( .heap .environments .push_function_environment(FunctionEnvironment { - this_value: Some(class_instance.into_value()), + this_value: Some(class_instance.into_value().unbind()), this_binding_status: ThisBindingStatus::Initialized, function_object: class_constructor.unbind(), new_target: None, @@ -220,7 +220,11 @@ impl FunctionEnvironmentIndex { /// The GetThisBinding concrete method of a Function Environment Record /// envRec takes no arguments and returns either a normal completion /// containing an ECMAScript language value or a throw completion. - pub(crate) fn get_this_binding(self, agent: &mut Agent, gc: NoGcScope) -> JsResult { + pub(crate) fn get_this_binding<'gc>( + self, + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + ) -> JsResult> { // 1. Assert: envRec.[[ThisBindingStatus]] is not lexical. // 2. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception. // 3. Return envRec.[[ThisValue]]. @@ -320,7 +324,7 @@ impl FunctionEnvironmentIndex { // 4. Else if the binding for N in envRec is a mutable binding, then if binding.mutable { // a. Change its bound value to V. - binding.value = Some(value); + binding.value = Some(value.unbind()); } // 5. Else, else { @@ -342,13 +346,13 @@ impl FunctionEnvironmentIndex { } /// ### [9.1.1.1.6 GetBindingValue ( N, S )](https://tc39.es/ecma262/#sec-declarative-environment-records-getbindingvalue-n-s) - pub(crate) fn get_binding_value( + pub(crate) fn get_binding_value<'gc>( self, agent: &mut Agent, name: String, is_strict: bool, - gc: NoGcScope, - ) -> JsResult { + gc: NoGcScope<'gc, '_>, + ) -> JsResult> { agent[self] .declarative_environment .get_binding_value(agent, name, is_strict, gc) @@ -373,12 +377,12 @@ impl FunctionEnvironmentIndex { /// envRec takes argument V (an ECMAScript language value) and returns /// either a normal completion containing an ECMAScript language value or a /// throw completion. - pub(crate) fn bind_this_value( + pub(crate) fn bind_this_value<'gc>( self, agent: &mut Agent, value: Value, - gc: NoGcScope, - ) -> JsResult { + gc: NoGcScope<'gc, '_>, + ) -> JsResult> { let env_rec = &mut agent[self]; // 1. Assert: envRec.[[ThisBindingStatus]] is not LEXICAL. debug_assert!(env_rec.this_binding_status != ThisBindingStatus::Lexical); @@ -394,13 +398,13 @@ impl FunctionEnvironmentIndex { } // 3. Set envRec.[[ThisValue]] to V. - env_rec.this_value = Some(value); + env_rec.this_value = Some(value.unbind()); // 4. Set envRec.[[ThisBindingStatus]] to INITIALIZED. env_rec.this_binding_status = ThisBindingStatus::Initialized; // 5. Return V. - Ok(value) + Ok(value.bind(gc)) } /// ### [9.1.1.3.2 HasThisBinding ( )](https://tc39.es/ecma262/#sec-function-environment-records-hasthisbinding) diff --git a/nova_vm/src/ecmascript/execution/environments/global_environment.rs b/nova_vm/src/ecmascript/execution/environments/global_environment.rs index c91d25aca..8790bb684 100644 --- a/nova_vm/src/ecmascript/execution/environments/global_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/global_environment.rs @@ -24,7 +24,7 @@ use crate::{ types::{InternalMethods, Object, PropertyDescriptor, PropertyKey, String, Value}, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, TryResult, }, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, @@ -184,6 +184,7 @@ impl GlobalEnvironmentIndex { name: String, gc: GcScope, ) -> JsResult { + let name = name.bind(gc.nogc()); let env_rec = &agent[self]; // 1. Let DclRec be envRec.[[DeclarativeRecord]]. // 2. If ! DclRec.HasBinding(N) is true, return true. @@ -194,7 +195,7 @@ impl GlobalEnvironmentIndex { // 3. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 4. Return ? ObjRec.HasBinding(N). - obj_rec.has_binding(agent, name, gc) + obj_rec.has_binding(agent, name.unbind(), gc) } /// ### [9.1.1.4.2 CreateMutableBinding ( N, D )](https://tc39.es/ecma262/#sec-global-environment-records-createmutablebinding-n-d) @@ -306,6 +307,9 @@ impl GlobalEnvironmentIndex { value: Value, gc: GcScope, ) -> JsResult<()> { + let nogc = gc.nogc(); + let name = name.bind(nogc); + let value = value.bind(nogc); let env_rec = &agent[self]; // 1. Let DclRec be envRec.[[DeclarativeRecord]]. let dcl_rec = env_rec.declarative_record; @@ -319,7 +323,7 @@ impl GlobalEnvironmentIndex { // 4. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 5. Return ? ObjRec.InitializeBinding(N, V). - obj_rec.initialize_binding(agent, name, value, gc) + obj_rec.initialize_binding(agent, name.unbind(), value.unbind(), gc) } } @@ -374,6 +378,9 @@ impl GlobalEnvironmentIndex { is_strict: bool, gc: GcScope, ) -> JsResult<()> { + let nogc = gc.nogc(); + let name = name.bind(nogc); + let value = value.bind(nogc); let env_rec = &agent[self]; // 1. Let DclRec be envRec.[[DeclarativeRecord]]. let dcl_rec = env_rec.declarative_record; @@ -385,7 +392,7 @@ impl GlobalEnvironmentIndex { // 3. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 4. Return ? ObjRec.SetMutableBinding(N, V, S). - obj_rec.set_mutable_binding(agent, name, value, is_strict, gc) + obj_rec.set_mutable_binding(agent, name.unbind(), value.unbind(), is_strict, gc) } } @@ -399,13 +406,13 @@ impl GlobalEnvironmentIndex { /// ReferenceError exception. A property named N normally already exists /// but if it does not or is not currently writable, error handling is /// determined by S. - pub(crate) fn try_get_binding_value( + pub(crate) fn try_get_binding_value<'gc>( self, agent: &mut Agent, n: String, s: bool, - gc: NoGcScope, - ) -> TryResult> { + gc: NoGcScope<'gc, '_>, + ) -> TryResult>> { let env_rec = &agent[self]; // 1. Let DclRec be envRec.[[DeclarativeRecord]]. let dcl_rec = env_rec.declarative_record; @@ -431,25 +438,26 @@ impl GlobalEnvironmentIndex { /// ReferenceError exception. A property named N normally already exists /// but if it does not or is not currently writable, error handling is /// determined by S. - pub(crate) fn get_binding_value( + pub(crate) fn get_binding_value<'gc>( self, agent: &mut Agent, n: String, s: bool, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { + let n = n.bind(gc.nogc()); let env_rec = &agent[self]; // 1. Let DclRec be envRec.[[DeclarativeRecord]]. let dcl_rec = env_rec.declarative_record; // 2. If ! DclRec.HasBinding(N) is true, then if dcl_rec.has_binding(agent, n) { // a. Return ? DclRec.GetBindingValue(N, S). - dcl_rec.get_binding_value(agent, n, s, gc.nogc()) + dcl_rec.get_binding_value(agent, n.unbind(), s, gc.into_nogc()) } else { // 3. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 4. Return ? ObjRec.GetBindingValue(N, S). - obj_rec.get_binding_value(agent, n, s, gc) + obj_rec.get_binding_value(agent, n.unbind(), s, gc) } } @@ -512,6 +520,7 @@ impl GlobalEnvironmentIndex { name: String, mut gc: GcScope, ) -> JsResult { + let name = name.bind(gc.nogc()); let env_rec = &agent[self]; // 1. Let DclRec be envRec.[[DeclarativeRecord]]. let dcl_rec = env_rec.declarative_record; @@ -523,20 +532,23 @@ impl GlobalEnvironmentIndex { // 3. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 4. Let globalObject be ObjRec.[[BindingObject]]. - let global_object = agent[obj_rec].binding_object; + let global_object = agent[obj_rec].binding_object.bind(gc.nogc()); // 5. Let existingProp be ? HasOwnProperty(globalObject, N). let n = PropertyKey::from(name); - let existing_prop = has_own_property(agent, global_object, n, gc.reborrow())?; + let scoped_name = name.scope(agent, gc.nogc()); + let existing_prop = + has_own_property(agent, global_object.unbind(), n.unbind(), gc.reborrow())?; // 6. If existingProp is true, then if existing_prop { // a. Let status be ? ObjRec.DeleteBinding(N). - let status = obj_rec.delete_binding(agent, name, gc)?; + let status = obj_rec.delete_binding(agent, scoped_name.get(agent), gc)?; // b. If status is true and envRec.[[VarNames]] contains N, then if status { + let name = scoped_name.get(agent); let env_rec = &mut agent[self]; if env_rec.var_names.contains(&name) { // i. Remove N from envRec.[[VarNames]]. - env_rec.var_names.remove(&name.unbind()); + env_rec.var_names.remove(&name); } } // c. Return status. @@ -663,14 +675,18 @@ impl GlobalEnvironmentIndex { name: String, gc: GcScope, ) -> JsResult { + let name = name.bind(gc.nogc()); let env_rec = &agent[self]; // 1. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 2. Let globalObject be ObjRec.[[BindingObject]]. - let global_object = agent[obj_rec].binding_object; + let global_object = agent[obj_rec].binding_object.bind(gc.nogc()); // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). let n = PropertyKey::from(name); - let existing_prop = global_object.internal_get_own_property(agent, n, gc)?; + let existing_prop = + global_object + .unbind() + .internal_get_own_property(agent, n.unbind(), gc)?; let Some(existing_prop) = existing_prop else { // 4. If existingProp is undefined, return false. return Ok(false); @@ -725,20 +741,23 @@ impl GlobalEnvironmentIndex { name: String, mut gc: GcScope, ) -> JsResult { + let name = name.bind(gc.nogc()); let env_rec = &agent[self]; // 1. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 2. Let globalObject be ObjRec.[[BindingObject]]. - let global_object = agent[obj_rec].binding_object; + let global_object = agent[obj_rec].binding_object.bind(gc.nogc()); + let scoped_global_object = global_object.scope(agent, gc.nogc()); // 3. Let hasProperty be ? HasOwnProperty(globalObject, N). let n = PropertyKey::from(name); - let has_property = has_own_property(agent, global_object, n, gc.reborrow())?; + let has_property = + has_own_property(agent, global_object.unbind(), n.unbind(), gc.reborrow())?; // 4. If hasProperty is true, return true. if has_property { Ok(true) } else { // 5. Return ? IsExtensible(globalObject). - is_extensible(agent, global_object, gc) + is_extensible(agent, scoped_global_object.get(agent), gc) } } @@ -794,17 +813,22 @@ impl GlobalEnvironmentIndex { name: String, mut gc: GcScope, ) -> JsResult { + let name = name.bind(gc.nogc()); let env_rec = &agent[self]; // 1. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 2. Let globalObject be ObjRec.[[BindingObject]]. - let global_object = agent[obj_rec].binding_object; + let global_object = agent[obj_rec].binding_object.bind(gc.nogc()); + let scoped_global_object = global_object.scope(agent, gc.nogc()); let n = PropertyKey::from(name); // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). - let existing_prop = global_object.internal_get_own_property(agent, n, gc.reborrow())?; + let existing_prop = + global_object + .unbind() + .internal_get_own_property(agent, n.unbind(), gc.reborrow())?; // 4. If existingProp is undefined, return ? IsExtensible(globalObject). let Some(existing_prop) = existing_prop else { - return is_extensible(agent, global_object, gc); + return is_extensible(agent, scoped_global_object.get(agent), gc); }; // 5. If existingProp.[[Configurable]] is true, return true. if existing_prop.configurable == Some(true) @@ -883,28 +907,35 @@ impl GlobalEnvironmentIndex { is_deletable: bool, mut gc: GcScope, ) -> JsResult<()> { + let nogc = gc.nogc(); + let name = name.bind(nogc); let env_rec = &agent[self]; // 1. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 2. Let globalObject be ObjRec.[[BindingObject]]. - let global_object = agent[obj_rec].binding_object; + let global_object = agent[obj_rec].binding_object.bind(nogc); + let scoped_global_object = global_object.scope(agent, nogc); let n = PropertyKey::from(name); + let name = name.scope(agent, nogc); // 3. Let hasProperty be ? HasOwnProperty(globalObject, N). - let has_property = has_own_property(agent, global_object, n, gc.reborrow())?; + let has_property = + has_own_property(agent, global_object.unbind(), n.unbind(), gc.reborrow())?; // 4. Let extensible be ? IsExtensible(globalObject). - let extensible = is_extensible(agent, global_object, gc.reborrow()).unwrap(); + let extensible = + is_extensible(agent, scoped_global_object.get(agent), gc.reborrow()).unwrap(); // 5. If hasProperty is false and extensible is true, then if !has_property && extensible { // a. Perform ? ObjRec.CreateMutableBinding(N, D). - obj_rec.create_mutable_binding(agent, name, is_deletable, gc.reborrow())?; + obj_rec.create_mutable_binding(agent, name.get(agent), is_deletable, gc.reborrow())?; // b. Perform ? ObjRec.InitializeBinding(N, undefined). - obj_rec.initialize_binding(agent, name, Value::Undefined, gc)?; + obj_rec.initialize_binding(agent, name.get(agent), Value::Undefined, gc)?; } // 6. If envRec.[[VarNames]] does not contain N, then // a. Append N to envRec.[[VarNames]]. - let env_rec = &mut agent[self]; - env_rec.var_names.insert(name.unbind()); + // SAFETY: name is not shared. + let name = unsafe { name.take(agent) }; + agent[self].var_names.insert(name); // 7. Return UNUSED. Ok(()) @@ -939,7 +970,7 @@ impl GlobalEnvironmentIndex { let desc = if existing_prop.is_none() || existing_prop.unwrap().configurable == Some(true) { // a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }. PropertyDescriptor { - value: Some(value), + value: Some(value.unbind()), writable: Some(true), get: None, set: None, @@ -950,7 +981,7 @@ impl GlobalEnvironmentIndex { // 5. Else, // a. Let desc be the PropertyDescriptor { [[Value]]: V }. PropertyDescriptor { - value: Some(value), + value: Some(value.unbind()), writable: None, get: None, set: None, @@ -998,19 +1029,27 @@ impl GlobalEnvironmentIndex { d: bool, mut gc: GcScope, ) -> JsResult<()> { + let nogc = gc.nogc(); + let name = name.bind(nogc); + let value = value.scope(agent, nogc); let env_rec = &agent[self]; // 1. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; // 2. Let globalObject be ObjRec.[[BindingObject]]. - let global_object = agent[obj_rec].binding_object; + let global_object = agent[obj_rec].binding_object.bind(nogc); + let scoped_global_object = global_object.scope(agent, nogc); let n = PropertyKey::from(name); + let scoped_n = n.scope(agent, nogc); // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). - let existing_prop = global_object.internal_get_own_property(agent, n, gc.reborrow())?; + let existing_prop = + global_object + .unbind() + .internal_get_own_property(agent, n.unbind(), gc.reborrow())?; // 4. If existingProp is undefined or existingProp.[[Configurable]] is true, then let desc = if existing_prop.is_none() || existing_prop.unwrap().configurable == Some(true) { // a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }. PropertyDescriptor { - value: Some(value), + value: Some(value.get(agent)), writable: Some(true), get: None, set: None, @@ -1021,7 +1060,7 @@ impl GlobalEnvironmentIndex { // 5. Else, // a. Let desc be the PropertyDescriptor { [[Value]]: V }. PropertyDescriptor { - value: Some(value), + value: Some(value.get(agent)), writable: None, get: None, set: None, @@ -1030,13 +1069,27 @@ impl GlobalEnvironmentIndex { } }; // 6. Perform ? DefinePropertyOrThrow(globalObject, N, desc). - define_property_or_throw(agent, global_object, n, desc, gc.reborrow())?; + define_property_or_throw( + agent, + scoped_global_object.get(agent), + scoped_n.get(agent), + desc, + gc.reborrow(), + )?; // 7. Perform ? Set(globalObject, N, V, false). - set(agent, global_object, n, value, false, gc)?; + set( + agent, + scoped_global_object.get(agent), + scoped_n.get(agent), + value.get(agent), + false, + gc, + )?; // 8. If envRec.[[VarNames]] does not contain N, then // a. Append N to envRec.[[VarNames]]. - let env_rec = &mut agent[self]; - env_rec.var_names.insert(name.unbind()); + // SAFETY: Name of a global function cannot be a numeric string. + let n = unsafe { String::try_from(scoped_n.get(agent).into_value_unchecked()).unwrap() }; + agent[self].var_names.insert(n); // 9. Return UNUSED. Ok(()) // NOTE diff --git a/nova_vm/src/ecmascript/execution/environments/object_environment.rs b/nova_vm/src/ecmascript/execution/environments/object_environment.rs index f709f79c8..11d90d025 100644 --- a/nova_vm/src/ecmascript/execution/environments/object_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/object_environment.rs @@ -6,7 +6,8 @@ use super::{ObjectEnvironmentIndex, OuterEnv}; use crate::ecmascript::abstract_operations::operations_on_objects::{ try_define_property_or_throw, try_get, try_has_property, try_set, }; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::ecmascript::types::IntoValue; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -185,7 +186,7 @@ impl ObjectEnvironmentIndex { // 6. If unscopables is an Object, then if let Ok(unscopables) = Object::try_from(unscopables) { // a. Let blocked be ToBoolean(? Get(unscopables, N)). - let blocked = get(agent, unscopables, name, gc.reborrow())?; + let blocked = get(agent, unscopables.unbind(), name, gc.reborrow())?; let blocked = to_boolean(agent, blocked); // b. If blocked is true, return false. Ok(!blocked) @@ -431,13 +432,13 @@ impl ObjectEnvironmentIndex { /// throw completion. It returns the value of its associated binding /// object's property whose name is N. The property should already exist /// but if it does not the result depends upon S. - pub(crate) fn try_get_binding_value( + pub(crate) fn try_get_binding_value<'gc>( self, agent: &mut Agent, n: String, s: bool, - gc: NoGcScope, - ) -> TryResult> { + gc: NoGcScope<'gc, '_>, + ) -> TryResult>> { let env_rec = &agent[self]; // 1. Let bindingObject be envRec.[[BindingObject]]. let binding_object = env_rec.binding_object; @@ -474,13 +475,13 @@ impl ObjectEnvironmentIndex { /// throw completion. It returns the value of its associated binding /// object's property whose name is N. The property should already exist /// but if it does not the result depends upon S. - pub(crate) fn get_binding_value( + pub(crate) fn get_binding_value<'gc>( self, agent: &mut Agent, n: String, s: bool, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let env_rec = &agent[self]; // 1. Let bindingObject be envRec.[[BindingObject]]. let binding_object = env_rec.binding_object; diff --git a/nova_vm/src/ecmascript/execution/environments/private_environment.rs b/nova_vm/src/ecmascript/execution/environments/private_environment.rs index 22509b678..172e30790 100644 --- a/nova_vm/src/ecmascript/execution/environments/private_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/private_environment.rs @@ -13,7 +13,7 @@ use super::PrivateEnvironmentIndex; #[derive(Debug)] pub enum PrivateName { - Field(Option), + Field(Option>), Method(Option>), /// Accessor(get, set) Accessor(Option>, Option>), diff --git a/nova_vm/src/ecmascript/execution/execution_context.rs b/nova_vm/src/ecmascript/execution/execution_context.rs index 3c6afd276..0b681088f 100644 --- a/nova_vm/src/ecmascript/execution/execution_context.rs +++ b/nova_vm/src/ecmascript/execution/execution_context.rs @@ -8,7 +8,7 @@ use crate::{ scripts_and_modules::{source_code::SourceCode, ScriptOrModule}, types::*, }, - engine::context::NoGcScope, + engine::context::{Bindable, NoGcScope}, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, }; diff --git a/nova_vm/src/ecmascript/execution/realm.rs b/nova_vm/src/ecmascript/execution/realm.rs index 21c899ebb..78c1c2943 100644 --- a/nova_vm/src/ecmascript/execution/realm.rs +++ b/nova_vm/src/ecmascript/execution/realm.rs @@ -7,7 +7,7 @@ mod intrinsics; use super::{ environments::GlobalEnvironmentIndex, Agent, ExecutionContext, GlobalEnvironment, JsResult, }; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_objects::define_property_or_throw, @@ -328,7 +328,7 @@ pub(crate) fn set_default_global_bindings<'a>( mut gc: GcScope<'a, '_>, ) -> JsResult> { // 1. Let global be realmRec.[[GlobalObject]]. - let global = agent[realm_id].global_object; + let global = agent[realm_id].global_object.scope(agent, gc.nogc()); // 2. For each property of the Global Object specified in clause 19, do // a. Let name be the String value of the property name. @@ -348,34 +348,35 @@ pub(crate) fn set_default_global_bindings<'a>( global_env .unwrap() .get_this_binding(agent, gc.nogc()) - .into_value(), + .into_value() + .unbind(), ), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Infinity); let value = Number::from_f64(agent, f64::INFINITY, gc.nogc()); let desc = PropertyDescriptor { - value: Some(value.into_value()), + value: Some(value.into_value().unbind()), writable: Some(false), enumerable: Some(false), configurable: Some(false), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; let name = PropertyKey::from(BUILTIN_STRING_MEMORY.NaN); let value = Number::from_f64(agent, f64::NAN, gc.nogc()); let desc = PropertyDescriptor { - value: Some(value.into_value()), + value: Some(value.into_value().unbind()), writable: Some(false), enumerable: Some(false), configurable: Some(false), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; let name = PropertyKey::from(BUILTIN_STRING_MEMORY.undefined); let desc = PropertyDescriptor { @@ -385,7 +386,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(false), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.2 Function Properties of the Global Object @@ -400,7 +401,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.2 isFinite ( number ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.isFinite); @@ -412,7 +413,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.3 isNaN ( number ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.isNaN); @@ -424,7 +425,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.4 parseFloat ( string ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.parseFloat); @@ -436,7 +437,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.5 parseInt ( string, radix ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.parseInt); @@ -448,7 +449,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.6.1 decodeURI ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.decodeURI); @@ -460,7 +461,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.6.2 decodeURIComponent ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.decodeURIComponent); @@ -475,7 +476,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.6.3 encodeURI ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.encodeURI); @@ -487,7 +488,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.2.6.4 encodeURIComponent ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.encodeURIComponent); @@ -502,7 +503,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3 Constructor Properties of the Global Object @@ -517,7 +518,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.2 Array ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Array); @@ -529,7 +530,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.3 ArrayBuffer ( . . . ) #[cfg(feature = "array-buffer")] @@ -543,7 +544,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.4 BigInt ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.BigInt); @@ -555,7 +556,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.5 BigInt64Array ( . . . ) #[cfg(feature = "array-buffer")] @@ -569,7 +570,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.6 BigUint64Array ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.BigUint64Array); @@ -581,7 +582,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.7 Boolean ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Boolean); @@ -593,7 +594,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.8 DataView ( . . . ) #[cfg(feature = "array-buffer")] @@ -607,7 +608,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } #[cfg(feature = "date")] { @@ -621,7 +622,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.10 Error ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Error); @@ -633,7 +634,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.11 EvalError ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.EvalError); @@ -645,7 +646,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.12 FinalizationRegistry ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.FinalizationRegistry); @@ -660,7 +661,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.13 Float32Array ( . . . ) #[cfg(feature = "array-buffer")] @@ -674,7 +675,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.14 Float64Array ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Float64Array); @@ -686,7 +687,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.15 Function ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Function); @@ -698,7 +699,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.16 Int8Array ( . . . ) #[cfg(feature = "array-buffer")] { @@ -711,7 +712,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.17 Int16Array ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Int16Array); @@ -723,7 +724,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.18 Int32Array ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Int32Array); @@ -735,7 +736,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.19 Map ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Map); @@ -747,7 +748,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.20 Number ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Number); @@ -759,7 +760,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.21 Object ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Object); @@ -771,7 +772,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.22 Promise ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Promise); @@ -783,7 +784,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.23 Proxy ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Proxy); @@ -795,7 +796,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.24 RangeError ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.RangeError); @@ -807,7 +808,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.25 ReferenceError ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.ReferenceError); @@ -819,7 +820,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.26 RegExp ( . . . ) #[cfg(feature = "regexp")] @@ -833,7 +834,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.27 Set ( . . . ) @@ -848,7 +849,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.28 SharedArrayBuffer ( . . . ) #[cfg(feature = "shared-array-buffer")] @@ -862,7 +863,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.29 String ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.String); @@ -874,7 +875,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.30 Symbol ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Symbol); @@ -886,7 +887,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.31 SyntaxError ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.SyntaxError); @@ -898,7 +899,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.32 TypeError ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.TypeError); @@ -910,7 +911,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.33 Uint8Array ( . . . ) #[cfg(feature = "array-buffer")] @@ -924,7 +925,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.34 Uint8ClampedArray ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Uint8ClampedArray); @@ -936,7 +937,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.35 Uint16Array ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Uint16Array); @@ -948,7 +949,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.36 Uint32Array ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Uint32Array); @@ -960,7 +961,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.3.37 URIError ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.URIError); @@ -972,7 +973,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.38 WeakMap ( . . . ) #[cfg(feature = "weak-refs")] @@ -986,7 +987,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.39 WeakRef ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.WeakRef); let value = agent.get_realm(realm_id).intrinsics().weak_ref(); @@ -997,7 +998,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; // 19.3.40 WeakSet ( . . . ) let name = PropertyKey::from(BUILTIN_STRING_MEMORY.WeakSet); @@ -1009,7 +1010,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } } @@ -1027,7 +1028,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.4.2 JSON #[cfg(feature = "json")] @@ -1041,7 +1042,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.4.3 Math @@ -1056,7 +1057,7 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 19.4.4 Reflect let name = PropertyKey::from(BUILTIN_STRING_MEMORY.Reflect); @@ -1068,11 +1069,11 @@ pub(crate) fn set_default_global_bindings<'a>( configurable: Some(true), ..Default::default() }; - define_property_or_throw(agent, global, name, desc, gc.reborrow())?; + define_property_or_throw(agent, global.get(agent), name, desc, gc.reborrow())?; } // 3. Return global. - Ok(global) + Ok(global.get(agent).bind(gc.into_nogc())) } /// ### [9.6 InitializeHostDefinedRealm ( )](https://tc39.es/ecma262/#sec-initializehostdefinedrealm) @@ -1156,7 +1157,7 @@ pub(crate) fn initialize_default_realm(agent: &mut Agent, gc: GcScope) { mod test { #[allow(unused_imports)] use crate::{ - engine::context::GcScope, + engine::context::{Bindable, GcScope}, heap::{ IntrinsicConstructorIndexes, IntrinsicFunctionIndexes, IntrinsicObjectIndexes, LAST_INTRINSIC_CONSTRUCTOR_INDEX, LAST_INTRINSIC_FUNCTION_INDEX, diff --git a/nova_vm/src/ecmascript/scripts_and_modules/script.rs b/nova_vm/src/ecmascript/scripts_and_modules/script.rs index c4aa6a87f..09dbeab62 100644 --- a/nova_vm/src/ecmascript/scripts_and_modules/script.rs +++ b/nova_vm/src/ecmascript/scripts_and_modules/script.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ execution::{ @@ -248,7 +248,11 @@ pub fn parse_script( /// The abstract operation ScriptEvaluation takes argument scriptRecord (a /// Script Record) and returns either a normal completion containing an /// ECMAScript language value or an abrupt completion. -pub fn script_evaluation(agent: &mut Agent, script: Script, mut gc: GcScope) -> JsResult { +pub fn script_evaluation<'gc>( + agent: &mut Agent, + script: Script, + mut gc: GcScope<'gc, '_>, +) -> JsResult> { let realm_id = script.realm; let is_strict_mode = script.ecmascript_code.source_type.is_strict(); let source_code = script.source_code; @@ -549,13 +553,12 @@ pub(crate) fn global_declaration_instantiation( private_env, gc.nogc(), ); - let function_name = - String::from_str(agent, function_name.unwrap().as_str(), gc.nogc()).unbind(); + let function_name = String::from_str(agent, function_name.unwrap().as_str(), gc.nogc()); // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). env.create_global_function_binding( agent, - function_name, - fo.into_value(), + function_name.unbind(), + fo.into_value().unbind(), false, gc.reborrow(), )?; @@ -572,7 +575,8 @@ pub(crate) fn global_declaration_instantiation( #[cfg(test)] mod test { - use crate::engine::context::GcScope; + use crate::ecmascript::builtins::Array; + use crate::engine::context::{Bindable, GcScope}; use crate::engine::unwrap_try; use crate::{ ecmascript::{ @@ -713,7 +717,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "undefined", gc.nogc()) ); @@ -721,7 +725,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "object", gc.nogc()) ); @@ -729,7 +733,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "string", gc.nogc()) ); @@ -737,7 +741,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "symbol", gc.nogc()) ); @@ -745,7 +749,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "boolean", gc.nogc()) ); @@ -753,7 +757,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "number", gc.nogc()) ); @@ -761,7 +765,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "bigint", gc.nogc()) ); @@ -769,7 +773,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "object", gc.nogc()) ); @@ -777,7 +781,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "function", gc.nogc()) ); } @@ -898,18 +902,17 @@ mod test { let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert!(result.is_undefined()); let foo_key = String::from_static_str(&mut agent, "foo", gc.nogc()).unbind(); - let foo = agent - .get_realm(realm) - .global_env - .unwrap() - .get_binding_value(&mut agent, foo_key, true, gc.reborrow()) - .unwrap(); + let foo = unwrap_try( + agent + .get_realm(realm) + .global_env + .unwrap() + .try_get_binding_value(&mut agent, foo_key, true, gc.nogc()), + ) + .unwrap(); assert!(foo.is_object()); let result = Object::try_from(foo).unwrap(); - assert!(result - .internal_own_property_keys(&mut agent, gc) - .unwrap() - .is_empty()); + assert!(unwrap_try(result.try_own_property_keys(&mut agent, gc.nogc())).is_empty()); } #[test] @@ -925,34 +928,36 @@ mod test { let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert!(result.is_undefined()); let foo_key = String::from_static_str(&mut agent, "foo", gc.nogc()).unbind(); - let foo = agent - .get_realm(realm) - .global_env - .unwrap() - .get_binding_value(&mut agent, foo_key, true, gc.reborrow()) - .unwrap(); + let foo = unwrap_try( + agent + .get_realm(realm) + .global_env + .unwrap() + .try_get_binding_value(&mut agent, foo_key, true, gc.nogc()), + ) + .unwrap(); assert!(foo.is_object()); - let result = Object::try_from(foo).unwrap(); + let result = Array::try_from(foo).unwrap(); let key = PropertyKey::Integer(0.into()); - assert!(result - .internal_has_property(&mut agent, key, gc.reborrow()) - .unwrap()); + assert!(unwrap_try(result.try_has_property( + &mut agent, + key, + gc.nogc() + ))); assert_eq!( - result - .internal_get_own_property(&mut agent, key, gc.reborrow()) - .unwrap() + unwrap_try(result.try_get_own_property(&mut agent, key, gc.nogc())) .unwrap() .value, Some(Value::from_static_str(&mut agent, "a", gc.nogc())) ); let key = PropertyKey::Integer(1.into()); - assert!(result - .internal_has_property(&mut agent, key, gc.reborrow()) - .unwrap()); + assert!(unwrap_try(result.unbind().try_has_property( + &mut agent, + key, + gc.nogc() + ))); assert_eq!( - result - .internal_get_own_property(&mut agent, key, gc) - .unwrap() + unwrap_try(result.try_get_own_property(&mut agent, key, gc.nogc())) .unwrap() .value, Some(Value::from(3)) @@ -1280,12 +1285,13 @@ mod test { String::from_static_str(&mut agent, "var foo = {}; foo.a = 42; foo", gc.nogc()); let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); - let object = Object::try_from(result).unwrap(); + let object = Object::try_from(result).unwrap().unbind().bind(gc.nogc()); - let pk = PropertyKey::from_static_str(&mut agent, "a", gc.nogc()).unbind(); + let pk = PropertyKey::from_static_str(&mut agent, "a", gc.nogc()); assert_eq!( object - .internal_get(&mut agent, pk, object.into_value(), gc) + .unbind() + .internal_get(&mut agent, pk.unbind(), object.into_value().unbind(), gc) .unwrap(), Value::Integer(SmallInteger::from(42)) ); @@ -1343,7 +1349,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "thrown", gc.nogc()) ); } @@ -1438,7 +1444,10 @@ mod test { let source_text = String::from_static_str(&mut agent, "'foo' ?? 12", gc.nogc()); let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "foo", gc.nogc())); + assert_eq!( + result.unbind(), + Value::from_static_str(&mut agent, "foo", gc.nogc()) + ); let source_text = String::from_static_str(&mut agent, "undefined ?? null", gc.nogc()); let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); @@ -1458,7 +1467,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "foobar", gc.nogc()) ); @@ -1467,7 +1476,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "foo a heap string", gc.nogc()) ); @@ -1479,7 +1488,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "Concatenating two heap strings", gc.nogc()) ); } @@ -1506,7 +1515,10 @@ mod test { let source_text = String::from_static_str(&mut agent, "foo.name", gc.nogc()); let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "foo", gc.nogc())); + assert_eq!( + result.unbind(), + Value::from_static_str(&mut agent, "foo", gc.nogc()) + ); let source_text = String::from_static_str(&mut agent, "foo.length", gc.nogc()); let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); @@ -1531,7 +1543,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - result, + result.unbind(), Value::from_static_str(&mut agent, "TypeError", gc.nogc()) ); @@ -1553,7 +1565,10 @@ mod test { String::from_static_str(&mut agent, "function foo() {}; foo.prototype", gc.nogc()); let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let result = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); - let foo_prototype = Object::try_from(result).unwrap(); + let foo_prototype = Object::try_from(result) + .unwrap() + .unbind() + .scope(&mut agent, gc.nogc()); let source_text = String::from_static_str(&mut agent, "new foo()", gc.nogc()); let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); @@ -1566,8 +1581,13 @@ mod test { }; let instance = Object::try_from(result).unwrap(); assert_eq!( - unwrap_try(instance.try_get_prototype_of(&mut agent, gc.nogc())), - Some(foo_prototype) + unwrap_try( + instance + .unbind() + .try_get_prototype_of(&mut agent, gc.nogc()) + ) + .unwrap(), + foo_prototype.get(&agent) ); } @@ -1618,7 +1638,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let value = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - value, + value.unbind(), Value::from_static_str(&mut agent, "Symbol()", gc.nogc()) ); @@ -1626,7 +1646,7 @@ mod test { let script = parse_script(&mut agent, source_text, realm, false, None, gc.nogc()).unwrap(); let value = script_evaluation(&mut agent, script, gc.reborrow()).unwrap(); assert_eq!( - value, + value.unbind(), Value::from_static_str(&mut agent, "Symbol(foo)", gc.nogc()) ); } diff --git a/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs b/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs index 8b7a9d10f..920729bf7 100644 --- a/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs +++ b/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs @@ -21,7 +21,7 @@ use crate::{ execution::Agent, types::{HeapString, String}, }, - engine::context::NoGcScope, + engine::context::{Bindable, NoGcScope}, heap::{ indexes::BaseIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, diff --git a/nova_vm/src/ecmascript/syntax_directed_operations/class_definitions.rs b/nova_vm/src/ecmascript/syntax_directed_operations/class_definitions.rs index 1a4aeab0a..57b42d31f 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/class_definitions.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/class_definitions.rs @@ -14,7 +14,10 @@ use crate::{ execution::{agent::ExceptionType, Agent, JsResult, ProtoIntrinsics}, types::{Function, InternalMethods, Object}, }, - engine::{context::GcScope, unwrap_try}, + engine::{ + context::{Bindable, GcScope}, + unwrap_try, + }, }; pub(crate) fn base_class_default_constructor<'a>( diff --git a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs index 236cda17c..ca971da5f 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs @@ -7,7 +7,7 @@ use std::collections::VecDeque; use crate::ecmascript::abstract_operations::operations_on_objects::try_define_property_or_throw; use crate::ecmascript::builtins::async_generator_objects::AsyncGeneratorState; use crate::ecmascript::builtins::generator_objects::SuspendedGeneratorState; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::unwrap_try; use crate::{ ecmascript::{ @@ -175,7 +175,7 @@ pub(crate) fn instantiate_ordinary_function_object<'a>( BUILTIN_STRING_MEMORY.prototype.to_property_key(), PropertyDescriptor { // [[Value]]: prototype, - value: Some(prototype.into_value()), + value: Some(prototype.into_value().unbind()), // [[Writable]]: true, writable: Some(true), // [[Enumerable]]: false, @@ -285,12 +285,12 @@ impl CompileFunctionBodyData<'static> { /// functionObject (an ECMAScript function object) and argumentsList (a List of /// ECMAScript language values) and returns either a normal completion /// containing an ECMAScript language value or an abrupt completion. -pub(crate) fn evaluate_function_body( +pub(crate) fn evaluate_function_body<'gc>( agent: &mut Agent, function_object: ECMAScriptFunction, arguments_list: ArgumentsList, - gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { let function_object = function_object.bind(gc.nogc()); // 1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList). //function_declaration_instantiation(agent, function_object, arguments_list)?; @@ -343,7 +343,7 @@ pub(crate) fn evaluate_async_function_body<'a>( // i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). // f. Else if result is a return completion, then // i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). - promise_capability.resolve(agent, result, gc.reborrow()); + promise_capability.resolve(agent, result.unbind(), gc.reborrow()); } ExecutionResult::Throw(err) => { // [27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )](https://tc39.es/ecma262/#sec-asyncblockstart) @@ -365,7 +365,7 @@ pub(crate) fn evaluate_async_function_body<'a>( return_promise_capability: promise_capability, })); // 2. Let promise be ? PromiseResolve(%Promise%, value). - let promise = Promise::resolve(agent, awaited_value, gc.reborrow()); + let promise = Promise::resolve(agent, awaited_value.unbind(), gc.reborrow()); // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected). inner_promise_then(agent, promise.unbind(), handler, handler, None); } @@ -382,12 +382,12 @@ pub(crate) fn evaluate_async_function_body<'a>( /// functionObject (an ECMAScript function object) and argumentsList (a List of /// ECMAScript language values) and returns a throw completion or a return /// completion. -pub(crate) fn evaluate_generator_body( +pub(crate) fn evaluate_generator_body<'gc>( agent: &mut Agent, function_object: ECMAScriptFunction, arguments_list: ArgumentsList, - mut gc: GcScope, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { let function_object = function_object.bind(gc.nogc()); // 1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList). //function_declaration_instantiation(agent, function_object, arguments_list)?; @@ -434,12 +434,12 @@ pub(crate) fn evaluate_generator_body( /// functionObject (an ECMAScript function object) and argumentsList (a List of /// ECMAScript language values) and returns a throw completion or a return /// completion. -pub(crate) fn evaluate_async_generator_body( +pub(crate) fn evaluate_async_generator_body<'gc>( agent: &mut Agent, function_object: ECMAScriptFunction, arguments_list: ArgumentsList, - mut gc: GcScope<'_, '_>, -) -> JsResult { + mut gc: GcScope<'gc, '_>, +) -> JsResult> { let function_object = function_object.bind(gc.nogc()); // 1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList). @@ -457,16 +457,19 @@ pub(crate) fn evaluate_async_generator_body( let Object::AsyncGenerator(generator) = generator else { unreachable!() }; + let generator = generator.unbind(); + let gc = gc.into_nogc(); + let generator = generator.bind(gc); // 3. Set generator.[[GeneratorBrand]] to empty. // 4. Set generator.[[AsyncGeneratorState]] to suspended-start. // 5. Perform AsyncGeneratorStart(generator, FunctionBody). - let function_object = scoped_function_object.get(agent).bind(gc.nogc()); + let function_object = scoped_function_object.get(agent).bind(gc); let executable = if let Some(exe) = agent[function_object].compiled_bytecode { exe } else { let data = CompileFunctionBodyData::new(agent, function_object); - let exe = Executable::compile_function_body(agent, data, gc.nogc()); + let exe = Executable::compile_function_body(agent, data, gc); agent[function_object].compiled_bytecode = Some(exe); exe }; diff --git a/nova_vm/src/ecmascript/types/language.rs b/nova_vm/src/ecmascript/types/language.rs index 8040441f5..f271e660c 100644 --- a/nova_vm/src/ecmascript/types/language.rs +++ b/nova_vm/src/ecmascript/types/language.rs @@ -31,8 +31,8 @@ pub use into_value::IntoValue; pub use number::{HeapNumber, Number, NumberHeapData}; pub use numeric::Numeric; pub use object::{ - bind_property_keys, scope_property_keys, unbind_property_keys, InternalMethods, InternalSlots, - IntoObject, Object, ObjectHeapData, OrdinaryObject, PropertyKey, + scope_property_keys, InternalMethods, InternalSlots, IntoObject, Object, ObjectHeapData, + OrdinaryObject, PropertyKey, }; pub(crate) use primitive::HeapPrimitive; pub use primitive::Primitive; diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 2051948c0..e831d78a1 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -25,8 +25,8 @@ use core::ops::{Index, IndexMut, Neg}; pub use data::BigIntHeapData; use num_bigint::Sign; -impl IntoValue for BigInt<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for BigInt<'a> { + fn into_value(self) -> Value<'a> { match self { BigInt::BigInt(data) => Value::BigInt(data.unbind()), BigInt::SmallBigInt(data) => Value::SmallBigInt(data), @@ -120,8 +120,8 @@ impl<'a> HeapBigInt<'a> { } } -impl IntoValue for HeapBigInt<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for HeapBigInt<'a> { + fn into_value(self) -> Value<'a> { Value::BigInt(self.unbind()) } } @@ -132,10 +132,10 @@ impl<'a> IntoPrimitive<'a> for HeapBigInt<'a> { } } -impl TryFrom for HeapBigInt<'_> { +impl<'a> TryFrom> for HeapBigInt<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { if let Value::BigInt(x) = value { Ok(x) } else { @@ -204,13 +204,13 @@ impl From for BigInt<'static> { } } -impl From> for Value { - fn from(value: HeapBigInt<'_>) -> Self { - Self::BigInt(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: HeapBigInt<'a>) -> Self { + Self::BigInt(value) } } -impl From for Value { +impl From for Value<'static> { fn from(value: SmallBigInt) -> Self { Self::SmallBigInt(value) } @@ -671,9 +671,9 @@ impl From for BigInt<'static> { } } -impl TryFrom for BigInt<'_> { +impl<'a> TryFrom> for BigInt<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::BigInt(x) => Ok(BigInt::BigInt(x)), Value::SmallBigInt(x) => Ok(BigInt::SmallBigInt(x)), @@ -704,8 +704,8 @@ impl<'a> TryFrom> for BigInt<'a> { } } -impl From> for Value { - fn from(value: BigInt<'_>) -> Value { +impl<'a> From> for Value<'a> { + fn from(value: BigInt<'a>) -> Self { match value { BigInt::BigInt(x) => Value::BigInt(x.unbind()), BigInt::SmallBigInt(x) => Value::SmallBigInt(x), diff --git a/nova_vm/src/ecmascript/types/language/function.rs b/nova_vm/src/ecmascript/types/language/function.rs index d5867460c..6657d6eb1 100644 --- a/nova_vm/src/ecmascript/types/language/function.rs +++ b/nova_vm/src/ecmascript/types/language/function.rs @@ -14,7 +14,7 @@ use super::{ ECMASCRIPT_FUNCTION_DISCRIMINANT, }, InternalMethods, IntoObject, IntoValue, Object, OrdinaryObject, InternalSlots, PropertyKey, Value }; -use crate::engine::{context::{GcScope, NoGcScope}, Scoped, TryResult}; +use crate::engine::{context::{ Bindable, GcScope, NoGcScope}, Scoped, TryResult}; use crate::{ ecmascript::{ builtins::{ @@ -69,8 +69,8 @@ impl core::fmt::Debug for Function<'_> { } } -impl IntoValue for Function<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Function<'a> { + fn into_value(self) -> Value<'a> { self.into() } } @@ -117,9 +117,9 @@ impl<'a> TryFrom> for Function<'a> { } } -impl TryFrom for Function<'_> { +impl<'a> TryFrom> for Function<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::BoundFunction(d) => Ok(Function::BoundFunction(d)), Value::BuiltinFunction(d) => Ok(Function::BuiltinFunction(d)), @@ -157,8 +157,8 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: Function) -> Self { +impl<'a> From> for Value<'a> { + fn from(value: Function<'a>) -> Self { match value { Function::BoundFunction(d) => Value::BoundFunction(d.unbind()), Function::BuiltinFunction(d) => Value::BuiltinFunction(d.unbind()), @@ -176,27 +176,7 @@ impl From> for Value { } } -impl<'a> Function<'a> { - /// Unbind this Function from its current lifetime. This is necessary to - /// use the Function as a parameter in a call that can perform garbage - /// collection. - pub fn unbind(self) -> Function<'static> { - unsafe { core::mem::transmute::, Function<'static>>(self) } - } - - // Bind this Function to the garbage collection lifetime. This enables - // Rust's borrow checker to verify that your Functions cannot not be - // invalidated by garbage collection being performed. - // - // This function is best called with the form - // ```rs - // let function = function.bind(&gc); - // ``` - // to make sure that the unbound Function cannot be used after binding. - pub const fn bind<'gc>(self, _: NoGcScope<'gc, '_>) -> Function<'gc> { - unsafe { core::mem::transmute::, Function<'gc>>(self) } - } - +impl Function<'_> { pub fn scope<'b>( self, agent: &mut Agent, @@ -219,6 +199,19 @@ impl<'a> Function<'a> { } } +// SAFETY: Property implemented as a lifetime transmute. +unsafe impl Bindable for Function<'_> { + type Of<'a> = Function<'a>; + + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + impl<'a> InternalSlots<'a> for Function<'a> { const DEFAULT_PROTOTYPE: ProtoIntrinsics = ProtoIntrinsics::Function; @@ -428,13 +421,13 @@ impl<'a> InternalMethods<'a> for Function<'a> { } } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { match self { Function::BoundFunction(x) => x.try_get(agent, property_key, receiver, gc), Function::BuiltinFunction(x) => x.try_get(agent, property_key, receiver, gc), @@ -449,13 +442,13 @@ impl<'a> InternalMethods<'a> for Function<'a> { } } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { match self { Function::BoundFunction(x) => x.internal_get(agent, property_key, receiver, gc), Function::BuiltinFunction(x) => x.internal_get(agent, property_key, receiver, gc), @@ -559,13 +552,13 @@ impl<'a> InternalMethods<'a> for Function<'a> { } } - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, this_argument: Value, arguments_list: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { match self { Function::BoundFunction(x) => x.internal_call(agent, this_argument, arguments_list, gc), Function::BuiltinFunction(x) => { @@ -645,13 +638,13 @@ impl HeapMarkAndSweep for Function<'static> { } impl Function<'_> { - pub fn call( + pub fn call<'gc>( self, agent: &mut Agent, - this_argument: Value, - args: &[Value], - gc: GcScope, - ) -> JsResult { + this_argument: Value<'static>, + args: &[Value<'static>], + gc: GcScope<'gc, '_>, + ) -> JsResult> { self.internal_call(agent, this_argument, ArgumentsList(args), gc) } } diff --git a/nova_vm/src/ecmascript/types/language/function/data.rs b/nova_vm/src/ecmascript/types/language/function/data.rs index 2c215d4ae..939c59f6f 100644 --- a/nova_vm/src/ecmascript/types/language/function/data.rs +++ b/nova_vm/src/ecmascript/types/language/function/data.rs @@ -29,7 +29,7 @@ pub struct BoundFunctionHeapData { /// /// The value that is always passed as the **this** value when calling the /// wrapped function. - pub(crate) bound_this: Value, + pub(crate) bound_this: Value<'static>, /// ### \[\[BoundArguments\]\] /// /// A list of values whose elements are used as the first arguments to any diff --git a/nova_vm/src/ecmascript/types/language/function/into_function.rs b/nova_vm/src/ecmascript/types/language/function/into_function.rs index 2c596ad30..d3596d6b8 100644 --- a/nova_vm/src/ecmascript/types/language/function/into_function.rs +++ b/nova_vm/src/ecmascript/types/language/function/into_function.rs @@ -10,12 +10,12 @@ use crate::{ }, execution::{Agent, JsResult}, types::{ - language::IntoObject, InternalMethods, InternalSlots, ObjectHeapData, OrdinaryObject, - PropertyDescriptor, PropertyKey, String, Value, BUILTIN_STRING_MEMORY, + language::IntoObject, InternalMethods, InternalSlots, IntoValue, ObjectHeapData, + OrdinaryObject, PropertyDescriptor, PropertyKey, String, Value, BUILTIN_STRING_MEMORY, }, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, unwrap_try, TryResult, }, heap::{CreateHeapData, ObjectEntry, ObjectEntryPropertyDescriptor}, @@ -165,13 +165,13 @@ pub(crate) fn function_internal_has_property<'a>( } } -pub(crate) fn function_try_get<'a>( +pub(crate) fn function_try_get<'gc, 'a>( func: impl FunctionInternalProperties<'a>, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, -) -> TryResult { + gc: NoGcScope<'gc, '_>, +) -> TryResult> { if let Some(backing_object) = func.get_backing_object(agent) { backing_object.try_get(agent, property_key, receiver, gc) } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { @@ -186,20 +186,20 @@ pub(crate) fn function_try_get<'a>( } } -pub(crate) fn function_internal_get<'a>( +pub(crate) fn function_internal_get<'gc, 'a>( func: impl FunctionInternalProperties<'a>, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { let property_key = property_key.bind(gc.nogc()); if let Some(backing_object) = func.get_backing_object(agent) { backing_object.internal_get(agent, property_key.unbind(), receiver, gc) } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { Ok(func.get_length(agent).into()) } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.name) { - Ok(func.get_name(agent).into_value()) + Ok(func.get_name(agent).into_value().bind(gc.into_nogc())) } else { // Note: Getting a function's prototype never calls JavaScript. let parent = unwrap_try(func.try_get_prototype_of(agent, gc.nogc())); diff --git a/nova_vm/src/ecmascript/types/language/into_value.rs b/nova_vm/src/ecmascript/types/language/into_value.rs index 6c45f39a2..8f504d53c 100644 --- a/nova_vm/src/ecmascript/types/language/into_value.rs +++ b/nova_vm/src/ecmascript/types/language/into_value.rs @@ -4,9 +4,9 @@ use super::Value; -pub trait IntoValue +pub trait IntoValue<'a> where - Self: Sized + Copy, + Self: 'a + Sized + Copy, { - fn into_value(self) -> Value; + fn into_value(self) -> Value<'a>; } diff --git a/nova_vm/src/ecmascript/types/language/number.rs b/nova_vm/src/ecmascript/types/language/number.rs index dfb4184af..bec6a1aa5 100644 --- a/nova_vm/src/ecmascript/types/language/number.rs +++ b/nova_vm/src/ecmascript/types/language/number.rs @@ -12,7 +12,10 @@ use super::{ }; use crate::{ ecmascript::abstract_operations::type_conversion::{to_int32_number, to_uint32_number}, - engine::{context::NoGcScope, Scoped}, + engine::{ + context::{Bindable, NoGcScope}, + Scoped, + }, }; use crate::{ ecmascript::execution::Agent, @@ -85,8 +88,8 @@ pub enum NumberRootRepr { HeapRef(HeapRootRef) = 0x80, } -impl IntoValue for HeapNumber<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for HeapNumber<'a> { + fn into_value(self) -> Value<'a> { Value::Number(self.unbind()) } } @@ -97,8 +100,8 @@ impl<'a> IntoPrimitive<'a> for HeapNumber<'a> { } } -impl IntoValue for Number<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Number<'a> { + fn into_value(self) -> Value<'a> { match self { Number::Number(idx) => Value::Number(idx.unbind()), Number::Integer(data) => Value::Integer(data), @@ -113,10 +116,10 @@ impl<'a> IntoNumeric<'a> for HeapNumber<'a> { } } -impl TryFrom for HeapNumber<'_> { +impl<'a> TryFrom> for HeapNumber<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { if let Value::Number(x) = value { Ok(x) } else { @@ -260,9 +263,9 @@ impl TryFrom for Number<'static> { } } -impl TryFrom for Number<'_> { +impl<'a> TryFrom> for Number<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Number(data) => Ok(Number::Number(data)), Value::Integer(data) => Ok(Number::Integer(data)), diff --git a/nova_vm/src/ecmascript/types/language/numeric.rs b/nova_vm/src/ecmascript/types/language/numeric.rs index 9320a668a..0cd92a32f 100644 --- a/nova_vm/src/ecmascript/types/language/numeric.rs +++ b/nova_vm/src/ecmascript/types/language/numeric.rs @@ -110,8 +110,8 @@ impl<'a> Numeric<'a> { } } -impl IntoValue for Numeric<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Numeric<'a> { + fn into_value(self) -> Value<'a> { match self { Numeric::Number(data) => Value::Number(data.unbind()), Numeric::Integer(data) => Value::Integer(data), @@ -134,8 +134,8 @@ impl<'a> IntoPrimitive<'a> for Numeric<'a> { } } -impl From> for Value { - fn from(value: Numeric<'_>) -> Self { +impl<'a> From> for Value<'a> { + fn from(value: Numeric<'a>) -> Self { value.into_value() } } @@ -146,10 +146,10 @@ impl<'a> From> for Primitive<'a> { } } -impl TryFrom for Numeric<'_> { +impl<'a> TryFrom> for Numeric<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Number(data) => Ok(Numeric::Number(data)), Value::Integer(data) => Ok(Numeric::Integer(data)), diff --git a/nova_vm/src/ecmascript/types/language/object.rs b/nova_vm/src/ecmascript/types/language/object.rs index 0fce0490c..e7202f3c3 100644 --- a/nova_vm/src/ecmascript/types/language/object.rs +++ b/nova_vm/src/ecmascript/types/language/object.rs @@ -90,7 +90,11 @@ use crate::{ execution::{Agent, JsResult, ProtoIntrinsics}, types::PropertyDescriptor, }, - engine::{context::GcScope, rootable::HeapRootData, TryResult}, + engine::{ + context::{Bindable, GcScope}, + rootable::HeapRootData, + TryResult, + }, heap::{ indexes::ObjectIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, @@ -100,9 +104,7 @@ pub use data::ObjectHeapData; pub use internal_methods::InternalMethods; pub use internal_slots::InternalSlots; pub use into_object::IntoObject; -pub use property_key::{ - bind_property_keys, scope_property_keys, unbind_property_keys, PropertyKey, -}; +pub use property_key::{scope_property_keys, PropertyKey}; pub use property_storage::PropertyStorage; /// ### [6.1.7 The Object Type](https://tc39.es/ecma262/#sec-object-type) @@ -173,7 +175,7 @@ pub enum Object<'a> { #[cfg(feature = "array-buffer")] Float64Array(TypedArrayIndex<'a>) = FLOAT_64_ARRAY_DISCRIMINANT, AsyncFromSyncIterator = ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, - AsyncGenerator(AsyncGenerator<'static>) = ASYNC_GENERATOR_DISCRIMINANT, + AsyncGenerator(AsyncGenerator<'a>) = ASYNC_GENERATOR_DISCRIMINANT, Iterator = ITERATOR_DISCRIMINANT, ArrayIterator(ArrayIterator<'a>) = ARRAY_ITERATOR_DISCRIMINANT, #[cfg(feature = "set")] @@ -187,8 +189,8 @@ pub enum Object<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct OrdinaryObject<'a>(pub(crate) ObjectIndex<'a>); -impl IntoValue for Object<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Object<'a> { + fn into_value(self) -> Value<'a> { match self { Object::Object(data) => Value::Object(data.unbind()), Object::BoundFunction(data) => Value::BoundFunction(data.unbind()), @@ -267,6 +269,36 @@ impl IntoValue for Object<'_> { } } +// SAFETY: Property implemented as a lifetime transmute. +unsafe impl Bindable for Object<'_> { + type Of<'a> = Object<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + +// SAFETY: Property implemented as a lifetime transmute. +unsafe impl Bindable for OrdinaryObject<'_> { + type Of<'a> = OrdinaryObject<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + impl<'a> IntoObject<'a> for Object<'a> { #[inline(always)] fn into_object(self) -> Object<'a> { @@ -280,15 +312,15 @@ impl<'a> IntoObject<'a> for OrdinaryObject<'a> { } } -impl IntoValue for OrdinaryObject<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for OrdinaryObject<'a> { + fn into_value(self) -> Value<'a> { self.into() } } impl<'a> From> for Object<'a> { - fn from(value: OrdinaryObject<'_>) -> Self { - Self::Object(value.unbind()) + fn from(value: OrdinaryObject<'a>) -> Self { + Self::Object(value) } } @@ -298,16 +330,16 @@ impl<'a> From> for OrdinaryObject<'a> { } } -impl From> for Value { - fn from(value: OrdinaryObject<'_>) -> Self { - Self::Object(value.unbind()) +impl<'a> From> for Value<'a> { + fn from(value: OrdinaryObject<'a>) -> Self { + Self::Object(value) } } -impl TryFrom for OrdinaryObject<'_> { +impl<'a> TryFrom> for OrdinaryObject<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Object(data) => Ok(data), _ => Err(()), @@ -359,26 +391,6 @@ impl<'a> InternalSlots<'a> for OrdinaryObject<'a> { } impl<'a> OrdinaryObject<'a> { - /// Unbind this OrdinaryObject from its current lifetime. This is necessary to use - /// the OrdinaryObject as a parameter in a call that can perform garbage - /// collection. - pub fn unbind(self) -> OrdinaryObject<'static> { - unsafe { core::mem::transmute::, OrdinaryObject<'static>>(self) } - } - - // Bind this OrdinaryObject to the garbage collection lifetime. This enables Rust's - // borrow checker to verify that your OrdinaryObjects cannot not be invalidated by - // garbage collection being performed. - // - // This function is best called with the form - // ```rs - // let number = number.bind(&gc); - // ``` - // to make sure that the unbound OrdinaryObject cannot be used after binding. - pub const fn bind<'gc>(self, _: NoGcScope<'gc, '_>) -> OrdinaryObject<'gc> { - unsafe { core::mem::transmute::, OrdinaryObject<'gc>>(self) } - } - pub fn scope<'scope>( self, agent: &mut Agent, @@ -421,8 +433,8 @@ impl<'a> From> for Object<'a> { } } -impl From> for Value { - fn from(value: Object) -> Self { +impl<'a> From> for Value<'a> { + fn from(value: Object<'a>) -> Self { match value { Object::Object(data) => Value::Object(data.unbind()), Object::BoundFunction(data) => Value::BoundFunction(data.unbind()), @@ -501,9 +513,9 @@ impl From> for Value { } } -impl TryFrom for Object<'_> { +impl<'a> TryFrom> for Object<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Undefined | Value::Null @@ -592,26 +604,6 @@ impl TryFrom for Object<'_> { } impl<'a> Object<'a> { - /// Unbind this Object from its current lifetime. This is necessary to use - /// the Object as a parameter in a call that can perform garbage - /// collection. - pub fn unbind(self) -> Object<'static> { - unsafe { core::mem::transmute::>(self) } - } - - // Bind this Object to the garbage collection lifetime. This enables Rust's - // borrow checker to verify that your Objects cannot not be invalidated by - // garbage collection being performed. - // - // This function is best called with the form - // ```rs - // let object = object.bind(&gc); - // ``` - // to make sure that the unbound Object cannot be used after binding. - pub const fn bind<'gc>(self, _: NoGcScope<'gc, '_>) -> Object<'gc> { - unsafe { core::mem::transmute::>(self) } - } - pub fn scope<'scope>( self, agent: &mut Agent, @@ -620,10 +612,6 @@ impl<'a> Object<'a> { Scoped::new(agent, self.unbind(), gc) } - pub fn into_value(self) -> Value { - self.into() - } - pub fn property_storage(self) -> PropertyStorage<'a> { PropertyStorage::new(self) } @@ -2688,13 +2676,13 @@ impl<'a> InternalMethods<'a> for Object<'a> { } } - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { match self { Object::Object(data) => data.try_get(agent, property_key, receiver, gc), Object::Array(data) => data.try_get(agent, property_key, receiver, gc), @@ -2796,13 +2784,13 @@ impl<'a> InternalMethods<'a> for Object<'a> { } } - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { match self { Object::Object(data) => data.internal_get(agent, property_key, receiver, gc), Object::Array(data) => data.internal_get(agent, property_key, receiver, gc), @@ -3600,13 +3588,13 @@ impl<'a> InternalMethods<'a> for Object<'a> { } } - fn internal_call( + fn internal_call<'gc>( self, agent: &mut Agent, this_value: Value, arguments_list: ArgumentsList, - gc: GcScope, - ) -> JsResult { + gc: GcScope<'gc, '_>, + ) -> JsResult> { match self { Object::BoundFunction(data) => { data.internal_call(agent, this_value, arguments_list, gc) diff --git a/nova_vm/src/ecmascript/types/language/object/data.rs b/nova_vm/src/ecmascript/types/language/object/data.rs index 00f8cb9f6..0ad94ef6f 100644 --- a/nova_vm/src/ecmascript/types/language/object/data.rs +++ b/nova_vm/src/ecmascript/types/language/object/data.rs @@ -5,6 +5,7 @@ use super::Object; use crate::{ ecmascript::{execution::Agent, types::Value}, + engine::context::Bindable, heap::{element_array::ElementsVector, CompactionLists, HeapMarkAndSweep, WorkQueues}, }; @@ -27,7 +28,7 @@ impl ObjectHeapData { None } else { // TODO: Throw error. - Some(Object::try_from(prototype).unwrap()) + Some(Object::try_from(prototype.unbind()).unwrap()) }; Self { extensible, diff --git a/nova_vm/src/ecmascript/types/language/object/internal_methods.rs b/nova_vm/src/ecmascript/types/language/object/internal_methods.rs index 3205eb1a8..b34ab70af 100644 --- a/nova_vm/src/ecmascript/types/language/object/internal_methods.rs +++ b/nova_vm/src/ecmascript/types/language/object/internal_methods.rs @@ -19,7 +19,7 @@ use crate::{ types::{Function, PropertyDescriptor, Value}, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, unwrap_try, TryResult, }, }; @@ -298,13 +298,13 @@ where /// method cannot be completed without calling into JavaScript, then `None` /// is returned. It is preferable to call this method first and only call /// the main method if this returns None. - fn try_get( + fn try_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - gc: NoGcScope, - ) -> TryResult { + gc: NoGcScope<'gc, '_>, + ) -> TryResult> { // 1. Return ? OrdinaryGet(O, P, Receiver). match self.get_backing_object(agent) { Some(backing_object) => { @@ -324,13 +324,13 @@ where } /// ## \[\[Get\]\] - fn internal_get( + fn internal_get<'gc>( self, agent: &mut Agent, property_key: PropertyKey, receiver: Value, - mut gc: GcScope, - ) -> JsResult { + mut gc: GcScope<'gc, '_>, + ) -> JsResult> { let property_key = property_key.bind(gc.nogc()); // 1. Return ? OrdinaryGet(O, P, Receiver). match self.get_backing_object(agent) { @@ -450,13 +450,13 @@ where } /// ## \[\[Call\]\] - fn internal_call( + fn internal_call<'gc>( self, _agent: &mut Agent, _this_value: Value, _arguments_list: ArgumentsList, - _gc: GcScope, - ) -> JsResult { + _gc: GcScope<'gc, '_>, + ) -> JsResult> { unreachable!() } diff --git a/nova_vm/src/ecmascript/types/language/object/into_object.rs b/nova_vm/src/ecmascript/types/language/object/into_object.rs index 859ecbe55..005913c7f 100644 --- a/nova_vm/src/ecmascript/types/language/object/into_object.rs +++ b/nova_vm/src/ecmascript/types/language/object/into_object.rs @@ -8,7 +8,7 @@ use super::Object; pub trait IntoObject<'a> where - Self: 'a + Sized + Copy + IntoValue, + Self: 'a + Sized + Copy + IntoValue<'a>, { fn into_object(self) -> Object<'a>; } diff --git a/nova_vm/src/ecmascript/types/language/object/property_key.rs b/nova_vm/src/ecmascript/types/language/object/property_key.rs index 7d8329990..7c3ab4b1b 100644 --- a/nova_vm/src/ecmascript/types/language/object/property_key.rs +++ b/nova_vm/src/ecmascript/types/language/object/property_key.rs @@ -18,7 +18,7 @@ use crate::{ }, }, engine::{ - context::NoGcScope, + context::{Bindable, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, Scoped, }, @@ -37,26 +37,6 @@ pub enum PropertyKey<'a> { } impl<'a> PropertyKey<'a> { - /// Unbind this PropertyKey from its current lifetime. This is necessary to - /// use the PropertyKey as a parameter in a call that can perform garbage - /// collection. - pub fn unbind(self) -> PropertyKey<'static> { - unsafe { core::mem::transmute::>(self) } - } - - // Bind this PropertyKey to the garbage collection lifetime. This enables - // Rust's borrow checker to verify that your PropertyKeys cannot not be - // invalidated by garbage collection being performed. - // - // This function is best called with the form - // ```rs - // let primitive = primitive.bind(&gc); - // ``` - // to make sure that the unbound PropertyKey cannot be used after binding. - pub const fn bind(self, _: NoGcScope<'a, '_>) -> Self { - unsafe { core::mem::transmute::(self) } - } - pub fn scope<'scope>( self, agent: &Agent, @@ -100,10 +80,10 @@ impl<'a> PropertyKey<'a> { /// /// This converts any integer keys into strings. This matches what the /// ECMAScript specification expects. - pub fn convert_to_value(self, agent: &mut Agent, gc: NoGcScope) -> Value { + pub fn convert_to_value<'gc>(self, agent: &mut Agent, gc: NoGcScope<'gc, '_>) -> Value<'gc> { match self { PropertyKey::Integer(small_integer) => { - Value::from_string(agent, format!("{}", small_integer.into_i64()), gc) + Value::from_string(agent, small_integer.into_i64().to_string(), gc) } PropertyKey::SmallString(small_string) => Value::SmallString(small_string), PropertyKey::String(heap_string) => Value::String(heap_string.unbind()), @@ -122,12 +102,12 @@ impl<'a> PropertyKey<'a> { /// If the resulting PropertyKey is mixed with normal JavaScript values or /// passed to user code, the resulting JavaScript will not necessarily /// correctly match the ECMAScript specification or user's expectations. - pub(crate) unsafe fn into_value_unchecked(self) -> Value { + pub(crate) unsafe fn into_value_unchecked(self) -> Value<'a> { match self { PropertyKey::Integer(small_integer) => Value::Integer(small_integer), PropertyKey::SmallString(small_string) => Value::SmallString(small_string), - PropertyKey::String(heap_string) => Value::String(heap_string.unbind()), - PropertyKey::Symbol(symbol) => Value::Symbol(symbol.unbind()), + PropertyKey::String(heap_string) => Value::String(heap_string), + PropertyKey::Symbol(symbol) => Value::Symbol(symbol), } } @@ -147,12 +127,12 @@ impl<'a> PropertyKey<'a> { /// /// If the passed-in Value is not a string, integer, or symbol, the method /// will panic. - pub(crate) unsafe fn from_value_unchecked(value: Value) -> Self { + pub(crate) unsafe fn from_value_unchecked(value: Value<'a>) -> Self { match value { Value::Integer(small_integer) => PropertyKey::Integer(small_integer), Value::SmallString(small_string) => PropertyKey::SmallString(small_string), - Value::String(heap_string) => PropertyKey::String(heap_string.unbind()), - Value::Symbol(symbol) => PropertyKey::Symbol(symbol.unbind()), + Value::String(heap_string) => PropertyKey::String(heap_string), + Value::Symbol(symbol) => PropertyKey::Symbol(symbol), _ => unreachable!(), } } @@ -213,14 +193,19 @@ impl<'a> PropertyKey<'a> { } } -#[inline(always)] -pub fn unbind_property_keys<'a>(vec: Vec>) -> Vec> { - unsafe { core::mem::transmute::>, Vec>>(vec) } -} +// SAFETY: Properly implemented as a lifetime transmute. +unsafe impl Bindable for PropertyKey<'_> { + type Of<'a> = PropertyKey<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } -#[inline(always)] -pub fn bind_property_keys<'a>(vec: Vec, _: NoGcScope<'a, '_>) -> Vec> { - unsafe { core::mem::transmute::, Vec>>(vec) } + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } } #[inline] @@ -314,12 +299,12 @@ impl<'a> From> for PropertyKey<'a> { } } -impl From> for Value { +impl<'a> From> for Value<'a> { /// Note: You should not be using this conversion without thinking. Integer /// keys don't actually become proper strings here, so converting a /// PropertyKey into a Value using this and then comparing that with an /// actual Value is unsound. - fn from(value: PropertyKey) -> Self { + fn from(value: PropertyKey<'a>) -> Self { // SAFETY: Don't be silly! unsafe { value.into_value_unchecked() } } diff --git a/nova_vm/src/ecmascript/types/language/object/property_storage.rs b/nova_vm/src/ecmascript/types/language/object/property_storage.rs index 085570988..5f86319ea 100644 --- a/nova_vm/src/ecmascript/types/language/object/property_storage.rs +++ b/nova_vm/src/ecmascript/types/language/object/property_storage.rs @@ -7,8 +7,9 @@ use core::cell::Ref; use crate::{ ecmascript::{ execution::{Agent, Realm}, - types::{PropertyDescriptor, Value, BUILTIN_STRING_MEMORY}, + types::{IntoValue, PropertyDescriptor, Value, BUILTIN_STRING_MEMORY}, }, + engine::context::Bindable, heap::element_array::ElementDescriptor, Heap, }; @@ -27,8 +28,8 @@ impl<'a> PropertyStorage<'a> { self.0 } - fn into_value(self) -> Value { - self.into_object().into_value() + fn into_value(self) -> Value<'a> { + self.0.into_value() } pub fn has(self, agent: &Agent, key: PropertyKey) -> bool { @@ -106,7 +107,7 @@ impl<'a> PropertyStorage<'a> { .map(|res| res.0); if let Some(index) = result { let key_entry = agent.heap.elements.get_mut(keys).get_mut(index).unwrap(); - *key_entry = Some(property_key); + *key_entry = Some(property_key.unbind()); let value_entry = agent.heap.elements.get_mut(values).get_mut(index).unwrap(); *value_entry = value; agent diff --git a/nova_vm/src/ecmascript/types/language/primitive.rs b/nova_vm/src/ecmascript/types/language/primitive.rs index 9028a9df6..4e39eff0d 100644 --- a/nova_vm/src/ecmascript/types/language/primitive.rs +++ b/nova_vm/src/ecmascript/types/language/primitive.rs @@ -7,7 +7,7 @@ use small_string::SmallString; use crate::{ ecmascript::execution::Agent, engine::{ - context::NoGcScope, + context::{Bindable, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, small_f64::SmallF64, Scoped, @@ -95,26 +95,26 @@ pub enum PrimitiveRootRepr { /// stored on the stack. #[derive(Debug, Clone, Copy, PartialEq)] #[repr(u8)] -pub(crate) enum HeapPrimitive { +pub(crate) enum HeapPrimitive<'a> { /// ### [6.1.4 The String Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type) /// /// UTF-8 string on the heap. Accessing the data must be done through the /// Agent. ECMAScript specification compliant UTF-16 indexing is /// implemented through an index mapping. - String(HeapString<'static>) = STRING_DISCRIMINANT, + String(HeapString<'a>) = STRING_DISCRIMINANT, /// ### [6.1.6.1 The Number Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type) /// /// f64 on the heap. Accessing the data must be done through the Agent. - Number(HeapNumber<'static>) = NUMBER_DISCRIMINANT, + Number(HeapNumber<'a>) = NUMBER_DISCRIMINANT, /// ### [6.1.6.2 The BigInt Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-bigint-type) /// /// Unlimited size integer data on the heap. Accessing the data must be /// done through the Agent. - BigInt(HeapBigInt<'static>) = BIGINT_DISCRIMINANT, + BigInt(HeapBigInt<'a>) = BIGINT_DISCRIMINANT, } -impl IntoValue for HeapPrimitive { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for HeapPrimitive<'a> { + fn into_value(self) -> Value<'a> { match self { HeapPrimitive::String(data) => Value::String(data), HeapPrimitive::Number(data) => Value::Number(data), @@ -123,10 +123,10 @@ impl IntoValue for HeapPrimitive { } } -impl TryFrom for HeapPrimitive { +impl<'a> TryFrom> for HeapPrimitive<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::String(data) => Ok(Self::String(data)), Value::Number(data) => Ok(Self::Number(data)), @@ -136,8 +136,8 @@ impl TryFrom for HeapPrimitive { } } -impl IntoValue for Primitive<'_> { - fn into_value(self) -> super::Value { +impl<'a> IntoValue<'a> for Primitive<'a> { + fn into_value(self) -> Value<'a> { match self { Primitive::Undefined => Value::Undefined, Primitive::Null => Value::Null, @@ -211,8 +211,8 @@ impl<'a> Primitive<'a> { } } -impl From> for Value { - fn from(value: Primitive) -> Self { +impl<'a> From> for Value<'a> { + fn from(value: Primitive<'a>) -> Self { value.into_value() } } @@ -224,10 +224,10 @@ impl<'a> IntoPrimitive<'a> for Primitive<'a> { } } -impl TryFrom for Primitive<'_> { +impl<'a> TryFrom> for Primitive<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Undefined => Ok(Primitive::Undefined), Value::Null => Ok(Primitive::Null), diff --git a/nova_vm/src/ecmascript/types/language/string.rs b/nova_vm/src/ecmascript/types/language/string.rs index 34e1fd1fd..ed6f7dbde 100644 --- a/nova_vm/src/ecmascript/types/language/string.rs +++ b/nova_vm/src/ecmascript/types/language/string.rs @@ -17,7 +17,7 @@ use super::{ use crate::{ ecmascript::{execution::Agent, types::PropertyDescriptor}, engine::{ - context::NoGcScope, + context::{Bindable, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, Scoped, }, @@ -36,26 +36,6 @@ use wtf8::Wtf8Buf; pub struct HeapString<'a>(pub(crate) StringIndex<'a>); impl HeapString<'_> { - /// Unbind this HeapString from its current lifetime. This is necessary to - /// use the HeapString as a parameter in a call that can perform garbage - /// collection. - pub const fn unbind(self) -> HeapString<'static> { - unsafe { core::mem::transmute::, HeapString<'static>>(self) } - } - - // Bind this HeapString to the garbage collection lifetime. This enables - // Rust's borrow checker to verify that your HeapStrings cannot not be - // invalidated by garbage collection being performed. - // - // This function is best called with the form - // ```rs - // let heap_string = heap_string.bind(&gc); - // ``` - // to make sure that the unbound HeapString cannot be used after binding. - pub const fn bind<'gc>(self, _: NoGcScope<'gc, '_>) -> HeapString<'gc> { - unsafe { core::mem::transmute::, HeapString<'gc>>(self) } - } - pub fn len(self, agent: &Agent) -> usize { agent[self].len() } @@ -73,6 +53,19 @@ impl HeapString<'_> { } } +// SAFETY: Property implemented as a lifetime transmute. +unsafe impl Bindable for HeapString<'_> { + type Of<'a> = HeapString<'a>; + + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + impl Index> for PrimitiveHeap<'_> { type Output = StringHeapData; @@ -142,16 +135,16 @@ pub enum StringRootRepr { HeapRef(HeapRootRef) = 0x80, } -impl IntoValue for HeapString<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for HeapString<'a> { + fn into_value(self) -> Value<'a> { Value::String(self.unbind()) } } -impl TryFrom for HeapString<'_> { +impl<'a> TryFrom> for HeapString<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { if let Value::String(x) = value { Ok(x) } else { @@ -160,10 +153,10 @@ impl TryFrom for HeapString<'_> { } } -impl IntoValue for String<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for String<'a> { + fn into_value(self) -> Value<'a> { match self { - String::String(idx) => Value::String(idx.unbind()), + String::String(idx) => Value::String(idx), String::SmallString(data) => Value::SmallString(data), } } @@ -172,7 +165,7 @@ impl IntoValue for String<'_> { impl<'a> IntoPrimitive<'a> for String<'a> { fn into_primitive(self) -> Primitive<'a> { match self { - String::String(idx) => Primitive::String(idx.unbind()), + String::String(idx) => Primitive::String(idx), String::SmallString(data) => Primitive::SmallString(data), } } @@ -197,9 +190,9 @@ impl TryFrom<&str> for String<'static> { } } -impl TryFrom for String<'_> { +impl<'a> TryFrom> for String<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::String(x) => Ok(String::String(x)), Value::SmallString(x) => Ok(String::SmallString(x)), @@ -219,16 +212,16 @@ impl<'a> TryFrom> for String<'a> { } } -impl From> for Value { - fn from(value: String) -> Self { +impl<'a> From> for Value<'a> { + fn from(value: String<'a>) -> Self { match value { - String::String(x) => Value::String(x.unbind()), + String::String(x) => Value::String(x), String::SmallString(x) => Value::SmallString(x), } } } -impl From for Value { +impl From for Value<'static> { fn from(value: SmallString) -> Self { Value::SmallString(value) } @@ -240,8 +233,8 @@ impl From for String<'static> { } } -impl IntoValue for SmallString { - fn into_value(self) -> Value { +impl IntoValue<'static> for SmallString { + fn into_value(self) -> Value<'static> { self.into() } } @@ -261,26 +254,6 @@ impl IntoPrimitive<'static> for SmallString { impl<'a> String<'a> { pub const EMPTY_STRING: String<'static> = String::from_small_string(""); - /// Unbind this String from its current lifetime. This is necessary to use - /// the String as a parameter in a call that can perform garbage - /// collection. - pub fn unbind(self) -> String<'static> { - unsafe { core::mem::transmute::, String<'static>>(self) } - } - - // Bind this String to the garbage collection lifetime. This enables Rust's - // borrow checker to verify that your Strings cannot not be invalidated by - // garbage collection being performed. - // - // This function is best called with the form - // ```rs - // let string = string.bind(&gc); - // ``` - // to make sure that the unbound String cannot be used after binding. - pub fn bind<'gc>(self, _gc: NoGcScope<'gc, '_>) -> String<'gc> { - unsafe { core::mem::transmute::, String<'gc>>(self) } - } - pub fn scope<'scope>( self, agent: &mut Agent, @@ -289,13 +262,28 @@ impl<'a> String<'a> { Scoped::new(agent, self.unbind(), gc) } + /// Scope a stack-only String. Stack-only Strings do not need to store any + /// data on the heap, hence scoping them is effectively a no-op. These + /// Strings are also not concerned with the garbage collector. + /// + /// ## Panics + /// + /// If the String is not stack-only, this method will panic. + pub const fn scope_static(self) -> Scoped<'static, String<'static>> { + let key_root_repr = match self { + String::SmallString(small_string) => StringRootRepr::SmallString(small_string), + _ => panic!("String required rooting"), + }; + Scoped::from_root_repr(key_root_repr) + } + pub fn is_empty_string(self) -> bool { self == Self::EMPTY_STRING } pub const fn to_property_key(self) -> PropertyKey<'a> { match self { - String::String(data) => PropertyKey::String(data.unbind()), + String::String(data) => PropertyKey::String(data), String::SmallString(data) => PropertyKey::SmallString(data), } } @@ -405,10 +393,6 @@ impl<'a> String<'a> { } } - pub fn into_value(self) -> Value { - self.into() - } - /// Byte length of the string. pub fn len(self, agent: &impl Index, Output = StringHeapData>) -> usize { match self { @@ -567,6 +551,19 @@ impl<'gc> String<'gc> { } } +// SAFETY: Property implemented as a lifetime transmute. +unsafe impl Bindable for String<'_> { + type Of<'a> = String<'a>; + + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + impl Scoped<'_, String<'static>> { pub fn as_str<'string, 'agent: 'string>(&'string self, agent: &'agent Agent) -> &'string str { match &self.inner { diff --git a/nova_vm/src/ecmascript/types/language/symbol.rs b/nova_vm/src/ecmascript/types/language/symbol.rs index 315154f0c..a2700a3cf 100644 --- a/nova_vm/src/ecmascript/types/language/symbol.rs +++ b/nova_vm/src/ecmascript/types/language/symbol.rs @@ -94,8 +94,8 @@ impl<'a> Symbol<'a> { } } -impl IntoValue for Symbol<'_> { - fn into_value(self) -> Value { +impl<'a> IntoValue<'a> for Symbol<'a> { + fn into_value(self) -> Value<'a> { Value::Symbol(self.unbind()) } } @@ -106,8 +106,8 @@ impl<'a> IntoPrimitive<'a> for Symbol<'a> { } } -impl From> for Value { - fn from(value: Symbol) -> Self { +impl<'a> From> for Value<'a> { + fn from(value: Symbol<'a>) -> Self { value.into_value() } } @@ -118,10 +118,10 @@ impl<'a> From> for Primitive<'a> { } } -impl TryFrom for Symbol<'_> { +impl<'a> TryFrom> for Symbol<'a> { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Symbol(idx) => Ok(idx), _ => Err(()), diff --git a/nova_vm/src/ecmascript/types/language/value.rs b/nova_vm/src/ecmascript/types/language/value.rs index 71ad2ab93..26ec15687 100644 --- a/nova_vm/src/ecmascript/types/language/value.rs +++ b/nova_vm/src/ecmascript/types/language/value.rs @@ -57,7 +57,7 @@ use crate::{ types::BUILTIN_STRING_MEMORY, }, engine::{ - context::{GcScope, NoGcScope}, + context::{Bindable, GcScope, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, small_f64::SmallF64, Scoped, TryResult, @@ -75,7 +75,7 @@ use core::{ /// ### [6.1 ECMAScript Language Types](https://tc39.es/ecma262/#sec-ecmascript-language-types) #[derive(Debug, Clone, Copy, PartialEq, Default)] #[repr(u8)] -pub enum Value { +pub enum Value<'a> { /// ### [6.1.1 The Undefined Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-undefined-type) #[default] Undefined = 1, @@ -91,7 +91,7 @@ pub enum Value { /// UTF-8 string on the heap. Accessing the data must be done through the /// Agent. ECMAScript specification compliant UTF-16 indexing is /// implemented through an index mapping. - String(HeapString<'static>), + String(HeapString<'a>), /// ### [6.1.4 The String Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type) /// /// 7-byte UTF-8 string on the stack. End of the string is determined by @@ -100,12 +100,12 @@ pub enum Value { SmallString(SmallString), /// ### [6.1.5 The Symbol Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-symbol-type) - Symbol(Symbol<'static>), + Symbol(Symbol<'a>), /// ### [6.1.6.1 The Number Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type) /// /// f64 on the heap. Accessing the data must be done through the Agent. - Number(HeapNumber<'static>), + Number(HeapNumber<'a>), /// ### [6.1.6.1 The Number Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type) /// /// 53-bit signed integer on the stack. @@ -120,31 +120,31 @@ pub enum Value { /// /// Unlimited size integer data on the heap. Accessing the data must be /// done through the Agent. - BigInt(HeapBigInt<'static>), + BigInt(HeapBigInt<'a>), /// ### [6.1.6.2 The BigInt Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-bigint-type) /// /// 56-bit signed integer on the stack. SmallBigInt(SmallBigInt), /// ### [6.1.7 The Object Type](https://tc39.es/ecma262/#sec-object-type) - Object(OrdinaryObject<'static>), + Object(OrdinaryObject<'a>), // Functions - BoundFunction(BoundFunction<'static>), - BuiltinFunction(BuiltinFunction<'static>), - ECMAScriptFunction(ECMAScriptFunction<'static>), + BoundFunction(BoundFunction<'a>), + BuiltinFunction(BuiltinFunction<'a>), + ECMAScriptFunction(ECMAScriptFunction<'a>), // TODO: Figure out if all the special function types are wanted or if we'd // prefer to just keep them as internal variants of the three above ones. BuiltinGeneratorFunction, /// Default class constructor created in step 14 of /// [ClassDefinitionEvaluation](https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation). - BuiltinConstructorFunction(BuiltinConstructorFunction<'static>), - BuiltinPromiseResolvingFunction(BuiltinPromiseResolvingFunction<'static>), + BuiltinConstructorFunction(BuiltinConstructorFunction<'a>), + BuiltinPromiseResolvingFunction(BuiltinPromiseResolvingFunction<'a>), BuiltinPromiseCollectorFunction, BuiltinProxyRevokerFunction, // Boolean, Number, String, Symbol, BigInt objects - PrimitiveObject(PrimitiveObject<'static>), + PrimitiveObject(PrimitiveObject<'a>), // Well-known object types // Roughly corresponding to 6.1.7.4 Well-Known Intrinsic Objects @@ -155,75 +155,75 @@ pub enum Value { /// /// An unmapped arguments object is an ordinary object with an additional /// internal slot \[\[ParameterMap]] whose value is always **undefined**. - Arguments(OrdinaryObject<'static>), + Arguments(OrdinaryObject<'a>), // TODO: MappedArguments(MappedArgumentsObject), - Array(Array<'static>), + Array(Array<'a>), #[cfg(feature = "array-buffer")] - ArrayBuffer(ArrayBuffer<'static>), + ArrayBuffer(ArrayBuffer<'a>), #[cfg(feature = "array-buffer")] - DataView(DataView<'static>), + DataView(DataView<'a>), #[cfg(feature = "date")] - Date(Date<'static>), - Error(Error<'static>), - FinalizationRegistry(FinalizationRegistry<'static>), - Map(Map<'static>), - Promise(Promise<'static>), - Proxy(Proxy<'static>), + Date(Date<'a>), + Error(Error<'a>), + FinalizationRegistry(FinalizationRegistry<'a>), + Map(Map<'a>), + Promise(Promise<'a>), + Proxy(Proxy<'a>), #[cfg(feature = "regexp")] - RegExp(RegExp<'static>), + RegExp(RegExp<'a>), #[cfg(feature = "set")] - Set(Set<'static>), + Set(Set<'a>), #[cfg(feature = "shared-array-buffer")] - SharedArrayBuffer(SharedArrayBuffer<'static>), + SharedArrayBuffer(SharedArrayBuffer<'a>), #[cfg(feature = "weak-refs")] - WeakMap(WeakMap<'static>), + WeakMap(WeakMap<'a>), #[cfg(feature = "weak-refs")] - WeakRef(WeakRef<'static>), + WeakRef(WeakRef<'a>), #[cfg(feature = "weak-refs")] - WeakSet(WeakSet<'static>), + WeakSet(WeakSet<'a>), // TypedArrays #[cfg(feature = "array-buffer")] - Int8Array(TypedArrayIndex<'static>), + Int8Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Uint8Array(TypedArrayIndex<'static>), + Uint8Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Uint8ClampedArray(TypedArrayIndex<'static>), + Uint8ClampedArray(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Int16Array(TypedArrayIndex<'static>), + Int16Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Uint16Array(TypedArrayIndex<'static>), + Uint16Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Int32Array(TypedArrayIndex<'static>), + Int32Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Uint32Array(TypedArrayIndex<'static>), + Uint32Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - BigInt64Array(TypedArrayIndex<'static>), + BigInt64Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - BigUint64Array(TypedArrayIndex<'static>), + BigUint64Array(TypedArrayIndex<'a>), #[cfg(feature = "proposal-float16array")] - Float16Array(TypedArrayIndex<'static>), + Float16Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Float32Array(TypedArrayIndex<'static>), + Float32Array(TypedArrayIndex<'a>), #[cfg(feature = "array-buffer")] - Float64Array(TypedArrayIndex<'static>), + Float64Array(TypedArrayIndex<'a>), // Iterator objects // TODO: Figure out if these are needed at all. AsyncFromSyncIterator, - AsyncGenerator(AsyncGenerator<'static>), + AsyncGenerator(AsyncGenerator<'a>), Iterator, - ArrayIterator(ArrayIterator<'static>), + ArrayIterator(ArrayIterator<'a>), #[cfg(feature = "set")] - SetIterator(SetIterator<'static>), - MapIterator(MapIterator<'static>), - Generator(Generator<'static>), + SetIterator(SetIterator<'a>), + MapIterator(MapIterator<'a>), + Generator(Generator<'a>), // ECMAScript Module - Module(Module<'static>), + Module(Module<'a>), // Embedder objects - EmbedderObject(EmbedderObject<'static>) = 0x7f, + EmbedderObject(EmbedderObject<'a>) = 0x7f, } /// We want to guarantee that all handles to JS values are register sized. This @@ -365,47 +365,57 @@ pub(crate) const MODULE_DISCRIMINANT: u8 = value_discriminant(Value::Module(Modu pub(crate) const EMBEDDER_OBJECT_DISCRIMINANT: u8 = value_discriminant(Value::EmbedderObject(EmbedderObject::_def())); -impl Value { - /// Unbind this Value from its current lifetime. This is necessary to use - /// the Value as a parameter in a call that can perform garbage collection. - pub fn unbind(self) -> Self { - self - } - - // Bind this Value to the garbage collection lifetime. This enables Rust's - // borrow checker to verify that your Values cannot not be invalidated by - // garbage collection being performed. - // - // This function is best called with the form - // ```rs - // let value = value.bind(&gc); - // ``` - // to make sure that the unbound Value cannot be used after binding. - pub fn bind(self, _gc: NoGcScope) -> Self { - self - } - +impl<'a> Value<'a> { pub fn scope<'scope>( self, agent: &mut Agent, gc: NoGcScope<'_, 'scope>, - ) -> Scoped<'scope, Value> { + ) -> Scoped<'scope, Value<'static>> { Scoped::new(agent, self.unbind(), gc) } - pub fn from_str(agent: &mut Agent, str: &str, gc: NoGcScope) -> Value { + /// Scope a stack-only Value. Stack-only Values are primitives that do not + /// need to store any data on the heap, hence scoping them is effectively a + /// no-op. These Values are also not concerned with the garbage collector. + /// + /// ## Panics + /// + /// If the Value is not stack-only, this method will panic. + pub const fn scope_static(self) -> Scoped<'static, Value<'static>> { + let key_root_repr = match self { + Value::Undefined => ValueRootRepr::Undefined, + Value::Null => ValueRootRepr::Null, + Value::Boolean(bool) => ValueRootRepr::Boolean(bool), + Value::SmallString(small_string) => ValueRootRepr::SmallString(small_string), + Value::Integer(small_integer) => ValueRootRepr::Integer(small_integer), + Value::SmallF64(small_string) => ValueRootRepr::SmallF64(small_string), + Value::SmallBigInt(small_string) => ValueRootRepr::SmallBigInt(small_string), + _ => panic!("Value required rooting"), + }; + Scoped::from_root_repr(key_root_repr) + } + + pub fn from_str(agent: &mut Agent, str: &str, gc: NoGcScope<'a, '_>) -> Value<'a> { String::from_str(agent, str, gc).into_value() } - pub fn from_string(agent: &mut Agent, string: std::string::String, gc: NoGcScope) -> Value { + pub fn from_string( + agent: &mut Agent, + string: std::string::String, + gc: NoGcScope<'a, '_>, + ) -> Value<'a> { String::from_string(agent, string, gc).into_value() } - pub fn from_static_str(agent: &mut Agent, str: &'static str, gc: NoGcScope) -> Value { + pub fn from_static_str( + agent: &mut Agent, + str: &'static str, + gc: NoGcScope<'a, '_>, + ) -> Value<'a> { String::from_static_str(agent, str, gc).into_value() } - pub fn from_f64(agent: &mut Agent, value: f64, gc: NoGcScope) -> Value { + pub fn from_f64(agent: &mut Agent, value: f64, gc: NoGcScope<'a, '_>) -> Value<'a> { Number::from_f64(agent, value, gc).into_value() } @@ -586,7 +596,8 @@ impl Value { if let Value::Symbol(symbol_idx) = self { // ToString of a symbol always throws. We use the descriptive // string instead (the result of `String(symbol)`). - return symbol_idx.descriptive_string(agent, gc.into_nogc()); + let gc = gc.into_nogc(); + return symbol_idx.unbind().descriptive_string(agent, gc); }; match self.to_string(agent, gc) { Ok(result) => result, @@ -603,7 +614,7 @@ impl Value { if let Value::Symbol(symbol_idx) = self { // ToString of a symbol always throws. We use the descriptive // string instead (the result of `String(symbol)`). - return symbol_idx.descriptive_string(agent, gc); + return symbol_idx.unbind().descriptive_string(agent, gc); }; match self.try_to_string(agent, gc) { TryResult::Continue(result) => result.unwrap(), @@ -628,9 +639,9 @@ impl Value { pub(crate) fn hash(self, arena: &A, hasher: &mut H) where H: Hasher, - A: Index, Output = StringHeapData> - + Index, Output = f64> - + Index, Output = BigIntHeapData>, + A: Index, Output = StringHeapData> + + Index, Output = f64> + + Index, Output = BigIntHeapData>, { let discriminant = core::mem::discriminant(&self); match self { @@ -1099,22 +1110,37 @@ impl Value { } } -impl From for Value { +impl From for Value<'_> { fn from(value: bool) -> Self { Value::Boolean(value) } } -impl From> for Value +// SAFETY: Property implemented as a lifetime transmute. +unsafe impl Bindable for Value<'_> { + type Of<'a> = Value<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + +impl<'a, T> From> for Value<'a> where - T: Into, + T: Into>, { fn from(value: Option) -> Self { value.map_or(Value::Undefined, |v| v.into()) } } -impl TryFrom<&str> for Value { +impl TryFrom<&str> for Value<'static> { type Error = (); fn try_from(value: &str) -> Result { if let Ok(data) = value.try_into() { @@ -1125,36 +1151,36 @@ impl TryFrom<&str> for Value { } } -impl TryFrom for Value { +impl TryFrom for Value<'static> { type Error = (); fn try_from(value: f64) -> Result { Number::try_from(value).map(|v| v.into()) } } -impl From> for Value { - fn from(value: Number<'_>) -> Self { +impl<'a> From> for Value<'a> { + fn from(value: Number<'a>) -> Self { value.into_value() } } -impl From for Value { +impl From for Value<'static> { fn from(value: f32) -> Self { Value::SmallF64(SmallF64::from(value)) } } -impl TryFrom for Value { +impl TryFrom for Value<'static> { type Error = (); fn try_from(value: i64) -> Result { Ok(Value::Integer(SmallInteger::try_from(value)?)) } } -impl TryFrom for bool { +impl<'a> TryFrom> for bool { type Error = (); - fn try_from(value: Value) -> Result { + fn try_from(value: Value<'a>) -> Result { match value { Value::Boolean(bool) => Ok(bool), _ => Err(()), @@ -1164,7 +1190,7 @@ impl TryFrom for bool { macro_rules! impl_value_from_n { ($size: ty) => { - impl From<$size> for Value { + impl From<$size> for Value<'_> { fn from(value: $size) -> Self { Value::Integer(SmallInteger::from(value)) } @@ -1179,14 +1205,14 @@ impl_value_from_n!(i16); impl_value_from_n!(u32); impl_value_from_n!(i32); -impl IntoValue for Value { +impl<'a> IntoValue<'a> for Value<'a> { #[inline(always)] - fn into_value(self) -> Value { + fn into_value(self) -> Value<'a> { self } } -impl Rootable for Value { +impl Rootable for Value<'_> { type RootRepr = ValueRootRepr; fn to_root_repr(value: Self) -> Result { @@ -1194,100 +1220,120 @@ impl Rootable for Value { Self::Undefined => Ok(Self::RootRepr::Undefined), Self::Null => Ok(Self::RootRepr::Null), Self::Boolean(bool) => Ok(Self::RootRepr::Boolean(bool)), - Self::String(heap_string) => Err(HeapRootData::String(heap_string)), + Self::String(heap_string) => Err(HeapRootData::String(heap_string.unbind())), Self::SmallString(small_string) => Ok(Self::RootRepr::SmallString(small_string)), - Self::Symbol(symbol) => Err(HeapRootData::Symbol(symbol)), - Self::Number(heap_number) => Err(HeapRootData::Number(heap_number)), + Self::Symbol(symbol) => Err(HeapRootData::Symbol(symbol.unbind())), + Self::Number(heap_number) => Err(HeapRootData::Number(heap_number.unbind())), Self::Integer(small_integer) => Ok(Self::RootRepr::Integer(small_integer)), Self::SmallF64(small_f64) => Ok(Self::RootRepr::SmallF64(small_f64)), - Self::BigInt(heap_big_int) => Err(HeapRootData::BigInt(heap_big_int)), + Self::BigInt(heap_big_int) => Err(HeapRootData::BigInt(heap_big_int.unbind())), Self::SmallBigInt(small_big_int) => Ok(Self::RootRepr::SmallBigInt(small_big_int)), - Self::Object(ordinary_object) => Err(HeapRootData::Object(ordinary_object)), - Self::BoundFunction(bound_function) => Err(HeapRootData::BoundFunction(bound_function)), - Self::BuiltinFunction(builtin_function) => { - Err(HeapRootData::BuiltinFunction(builtin_function)) + Self::Object(ordinary_object) => Err(HeapRootData::Object(ordinary_object.unbind())), + Self::BoundFunction(bound_function) => { + Err(HeapRootData::BoundFunction(bound_function.unbind())) } - Self::ECMAScriptFunction(ecmascript_function) => { - Err(HeapRootData::ECMAScriptFunction(ecmascript_function)) + Self::BuiltinFunction(builtin_function) => { + Err(HeapRootData::BuiltinFunction(builtin_function.unbind())) } + Self::ECMAScriptFunction(ecmascript_function) => Err(HeapRootData::ECMAScriptFunction( + ecmascript_function.unbind(), + )), Self::BuiltinGeneratorFunction => Err(HeapRootData::BuiltinGeneratorFunction), Self::BuiltinConstructorFunction(builtin_constructor_function) => Err( - HeapRootData::BuiltinConstructorFunction(builtin_constructor_function), - ), - Self::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => Err( - HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function), + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function.unbind()), ), + Self::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => { + Err(HeapRootData::BuiltinPromiseResolvingFunction( + builtin_promise_resolving_function.unbind(), + )) + } Self::BuiltinPromiseCollectorFunction => { Err(HeapRootData::BuiltinPromiseCollectorFunction) } Self::BuiltinProxyRevokerFunction => Err(HeapRootData::BuiltinProxyRevokerFunction), Self::PrimitiveObject(primitive_object) => { - Err(HeapRootData::PrimitiveObject(primitive_object)) + Err(HeapRootData::PrimitiveObject(primitive_object.unbind())) + } + Self::Arguments(ordinary_object) => { + Err(HeapRootData::Arguments(ordinary_object.unbind())) } - Self::Arguments(ordinary_object) => Err(HeapRootData::Arguments(ordinary_object)), - Self::Array(array) => Err(HeapRootData::Array(array)), + Self::Array(array) => Err(HeapRootData::Array(array.unbind())), #[cfg(feature = "array-buffer")] - Self::ArrayBuffer(array_buffer) => Err(HeapRootData::ArrayBuffer(array_buffer)), + Self::ArrayBuffer(array_buffer) => { + Err(HeapRootData::ArrayBuffer(array_buffer.unbind())) + } #[cfg(feature = "array-buffer")] - Self::DataView(data_view) => Err(HeapRootData::DataView(data_view)), + Self::DataView(data_view) => Err(HeapRootData::DataView(data_view.unbind())), #[cfg(feature = "date")] - Self::Date(date) => Err(HeapRootData::Date(date)), - Self::Error(error) => Err(HeapRootData::Error(error)), - Self::FinalizationRegistry(finalization_registry) => { - Err(HeapRootData::FinalizationRegistry(finalization_registry)) - } - Self::Map(map) => Err(HeapRootData::Map(map)), - Self::Promise(promise) => Err(HeapRootData::Promise(promise)), - Self::Proxy(proxy) => Err(HeapRootData::Proxy(proxy)), + Self::Date(date) => Err(HeapRootData::Date(date.unbind())), + Self::Error(error) => Err(HeapRootData::Error(error.unbind())), + Self::FinalizationRegistry(finalization_registry) => Err( + HeapRootData::FinalizationRegistry(finalization_registry.unbind()), + ), + Self::Map(map) => Err(HeapRootData::Map(map.unbind())), + Self::Promise(promise) => Err(HeapRootData::Promise(promise.unbind())), + Self::Proxy(proxy) => Err(HeapRootData::Proxy(proxy.unbind())), #[cfg(feature = "regexp")] - Self::RegExp(reg_exp) => Err(HeapRootData::RegExp(reg_exp)), + Self::RegExp(reg_exp) => Err(HeapRootData::RegExp(reg_exp.unbind())), #[cfg(feature = "set")] - Self::Set(set) => Err(HeapRootData::Set(set)), + Self::Set(set) => Err(HeapRootData::Set(set.unbind())), #[cfg(feature = "shared-array-buffer")] - Self::SharedArrayBuffer(shared_array_buffer) => { - Err(HeapRootData::SharedArrayBuffer(shared_array_buffer)) - } + Self::SharedArrayBuffer(shared_array_buffer) => Err(HeapRootData::SharedArrayBuffer( + shared_array_buffer.unbind(), + )), #[cfg(feature = "weak-refs")] - Self::WeakMap(weak_map) => Err(HeapRootData::WeakMap(weak_map)), + Self::WeakMap(weak_map) => Err(HeapRootData::WeakMap(weak_map.unbind())), #[cfg(feature = "weak-refs")] - Self::WeakRef(weak_ref) => Err(HeapRootData::WeakRef(weak_ref)), + Self::WeakRef(weak_ref) => Err(HeapRootData::WeakRef(weak_ref.unbind())), #[cfg(feature = "weak-refs")] - Self::WeakSet(weak_set) => Err(HeapRootData::WeakSet(weak_set)), + Self::WeakSet(weak_set) => Err(HeapRootData::WeakSet(weak_set.unbind())), #[cfg(feature = "array-buffer")] - Self::Int8Array(base_index) => Err(HeapRootData::Int8Array(base_index)), + Self::Int8Array(base_index) => Err(HeapRootData::Int8Array(base_index.unbind())), #[cfg(feature = "array-buffer")] - Self::Uint8Array(base_index) => Err(HeapRootData::Uint8Array(base_index)), + Self::Uint8Array(base_index) => Err(HeapRootData::Uint8Array(base_index.unbind())), #[cfg(feature = "array-buffer")] - Self::Uint8ClampedArray(base_index) => Err(HeapRootData::Uint8ClampedArray(base_index)), + Self::Uint8ClampedArray(base_index) => { + Err(HeapRootData::Uint8ClampedArray(base_index.unbind())) + } #[cfg(feature = "array-buffer")] - Self::Int16Array(base_index) => Err(HeapRootData::Int16Array(base_index)), + Self::Int16Array(base_index) => Err(HeapRootData::Int16Array(base_index.unbind())), #[cfg(feature = "array-buffer")] - Self::Uint16Array(base_index) => Err(HeapRootData::Uint16Array(base_index)), + Self::Uint16Array(base_index) => Err(HeapRootData::Uint16Array(base_index.unbind())), + #[cfg(feature = "proposal-float16array")] + Self::Float16Array(base_index) => Err(HeapRootData::Float16Array(base_index.unbind())), #[cfg(feature = "array-buffer")] - Self::Int32Array(base_index) => Err(HeapRootData::Int32Array(base_index)), + Self::Int32Array(base_index) => Err(HeapRootData::Int32Array(base_index.unbind())), #[cfg(feature = "array-buffer")] - Self::Uint32Array(base_index) => Err(HeapRootData::Uint32Array(base_index)), + Self::Uint32Array(base_index) => Err(HeapRootData::Uint32Array(base_index.unbind())), #[cfg(feature = "array-buffer")] - Self::BigInt64Array(base_index) => Err(HeapRootData::BigInt64Array(base_index)), + Self::Float32Array(base_index) => Err(HeapRootData::Float32Array(base_index.unbind())), #[cfg(feature = "array-buffer")] - Self::BigUint64Array(base_index) => Err(HeapRootData::BigUint64Array(base_index)), - #[cfg(feature = "proposal-float16array")] - Self::Float16Array(base_index) => Err(HeapRootData::Float16Array(base_index)), + Self::BigInt64Array(base_index) => { + Err(HeapRootData::BigInt64Array(base_index.unbind())) + } #[cfg(feature = "array-buffer")] - Self::Float32Array(base_index) => Err(HeapRootData::Float32Array(base_index)), + Self::BigUint64Array(base_index) => { + Err(HeapRootData::BigUint64Array(base_index.unbind())) + } #[cfg(feature = "array-buffer")] - Self::Float64Array(base_index) => Err(HeapRootData::Float64Array(base_index)), + Self::Float64Array(base_index) => Err(HeapRootData::Float64Array(base_index.unbind())), Self::AsyncFromSyncIterator => Err(HeapRootData::AsyncFromSyncIterator), - Self::AsyncGenerator(gen) => Err(HeapRootData::AsyncGenerator(gen)), + Self::AsyncGenerator(gen) => Err(HeapRootData::AsyncGenerator(gen.unbind())), Self::Iterator => Err(HeapRootData::Iterator), - Self::ArrayIterator(array_iterator) => Err(HeapRootData::ArrayIterator(array_iterator)), + Self::ArrayIterator(array_iterator) => { + Err(HeapRootData::ArrayIterator(array_iterator.unbind())) + } #[cfg(feature = "set")] - Self::SetIterator(set_iterator) => Err(HeapRootData::SetIterator(set_iterator)), - Self::MapIterator(map_iterator) => Err(HeapRootData::MapIterator(map_iterator)), - Self::Generator(generator) => Err(HeapRootData::Generator(generator)), - Self::Module(module) => Err(HeapRootData::Module(module)), + Self::SetIterator(set_iterator) => { + Err(HeapRootData::SetIterator(set_iterator.unbind())) + } + Self::MapIterator(map_iterator) => { + Err(HeapRootData::MapIterator(map_iterator.unbind())) + } + Self::Generator(generator) => Err(HeapRootData::Generator(generator.unbind())), + Self::Module(module) => Err(HeapRootData::Module(module.unbind())), Self::EmbedderObject(embedder_object) => { - Err(HeapRootData::EmbedderObject(embedder_object)) + Err(HeapRootData::EmbedderObject(embedder_object.unbind())) } } } @@ -1431,7 +1477,7 @@ pub enum ValueRootRepr { HeapRef(HeapRootRef) = 0x80, } -impl HeapMarkAndSweep for Value { +impl HeapMarkAndSweep for Value<'static> { fn mark_values(&self, queues: &mut WorkQueues) { match self { Value::Undefined diff --git a/nova_vm/src/ecmascript/types/spec/property_descriptor.rs b/nova_vm/src/ecmascript/types/spec/property_descriptor.rs index 40e5f430c..5c3e3f063 100644 --- a/nova_vm/src/ecmascript/types/spec/property_descriptor.rs +++ b/nova_vm/src/ecmascript/types/spec/property_descriptor.rs @@ -3,7 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::operations_on_objects::{try_get, try_has_property}; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::{Scoped, TryResult}; use crate::{ ecmascript::{ @@ -24,7 +24,7 @@ use crate::{ #[derive(Debug, Clone, Default)] pub struct PropertyDescriptor { /// \[\[Value]] - pub value: Option, + pub value: Option>, /// \[\[Writable]] pub writable: Option, @@ -45,7 +45,7 @@ pub struct PropertyDescriptor { #[derive(Debug)] pub struct ScopedPropertyDescriptor<'a> { /// \[\[Value]] - pub value: Option>, + pub value: Option>>, /// \[\[Writable]] pub writable: Option, @@ -94,7 +94,7 @@ impl PropertyDescriptor { pub fn new_data_descriptor(value: Value) -> Self { Self { - value: Some(value), + value: Some(value.unbind()), writable: Some(true), get: None, set: None, @@ -303,7 +303,7 @@ impl PropertyDescriptor { gc.reborrow(), )?; // b. Set desc.[[Value]] to value. - desc.value = Some(value); + desc.value = Some(value.unbind()); } // 9. Let hasWritable be ? HasProperty(Obj, "writable"). let has_writable = has_property( @@ -330,7 +330,9 @@ impl PropertyDescriptor { // 12. If hasGet is true, then if has_get { // a. Let getter be ? Get(Obj, "get"). - let getter = get(agent, obj, BUILTIN_STRING_MEMORY.get.into(), gc.reborrow())?; + let getter = get(agent, obj, BUILTIN_STRING_MEMORY.get.into(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); // b. If IsCallable(getter) is false and getter is not undefined, // throw a TypeError exception. if !getter.is_undefined() { @@ -350,7 +352,9 @@ impl PropertyDescriptor { // 14. If hasSet is true, then if has_set { // a. Let setter be ? Get(Obj, "set"). - let setter = get(agent, obj, BUILTIN_STRING_MEMORY.set.into(), gc.reborrow())?; + let setter = get(agent, obj, BUILTIN_STRING_MEMORY.set.into(), gc.reborrow())? + .unbind() + .bind(gc.nogc()); // b. If IsCallable(setter) is false and setter is not undefined, // throw a TypeError exception. if !setter.is_undefined() { @@ -426,7 +430,7 @@ impl PropertyDescriptor { // a. Let value be ? Get(Obj, "value"). let value = try_get(agent, obj, BUILTIN_STRING_MEMORY.value.into(), gc)?; // b. Set desc.[[Value]] to value. - desc.value = Some(value); + desc.value = Some(value.unbind()); } // 9. Let hasWritable be ? HasProperty(Obj, "writable"). let has_writable = try_has_property(agent, obj, BUILTIN_STRING_MEMORY.writable.into(), gc)?; diff --git a/nova_vm/src/ecmascript/types/spec/reference.rs b/nova_vm/src/ecmascript/types/spec/reference.rs index 4c15f5b28..6a9e89514 100644 --- a/nova_vm/src/ecmascript/types/spec/reference.rs +++ b/nova_vm/src/ecmascript/types/spec/reference.rs @@ -3,7 +3,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::operations_on_objects::try_set; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::ecmascript::types::IntoValue; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::engine::TryResult; use crate::{ ecmascript::{ @@ -24,13 +25,13 @@ use agent::{Agent, JsResult}; /// operators as delete, typeof, the assignment operators, the super keyword /// and other language features. For example, the left-hand operand of an /// assignment is expected to produce a Reference Record. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Reference<'a> { /// ### \[\[Base]] /// /// The value or Environment Record which holds the binding. A \[\[Base]] /// of UNRESOLVABLE indicates that the binding could not be resolved. - pub(crate) base: Base, + pub(crate) base: Base<'a>, /// ### \[\[ReferencedName]] /// @@ -51,7 +52,7 @@ pub struct Reference<'a> { /// Record and its \[\[Base]] value will never be an Environment Record. In /// that case, the \[\[ThisValue]] field holds the this value at the time /// the Reference Record was created. - pub(crate) this_value: Option, + pub(crate) this_value: Option>, } impl<'a> Reference<'a> { @@ -123,11 +124,11 @@ pub(crate) fn is_private_reference(_: &Reference) -> bool { /// The abstract operation GetValue takes argument V (a Reference Record or an /// ECMAScript language value) and returns either a normal completion /// containing an ECMAScript language value or an abrupt completion. -pub(crate) fn get_value( +pub(crate) fn get_value<'gc>( agent: &mut Agent, reference: &Reference, - mut gc: GcScope, -) -> JsResult { + gc: GcScope<'gc, '_>, +) -> JsResult> { let referenced_name = reference.referenced_name.bind(gc.nogc()); match reference.base { Base::Value(value) => { @@ -146,7 +147,7 @@ pub(crate) fn get_value( agent, referenced_name.unbind(), get_this_value(reference), - gc.reborrow(), + gc, )?) } else { // Primitive value. annoying stuff. @@ -177,7 +178,7 @@ pub(crate) fn get_value( .current_realm() .intrinsics() .boolean_prototype() - .internal_get(agent, referenced_name.unbind(), value, gc.reborrow()), + .internal_get(agent, referenced_name.unbind(), value, gc), Value::String(_) | Value::SmallString(_) => { let string = String::try_from(value).unwrap(); if let Some(prop_desc) = @@ -189,24 +190,24 @@ pub(crate) fn get_value( .current_realm() .intrinsics() .string_prototype() - .internal_get(agent, referenced_name.unbind(), value, gc.reborrow()) + .internal_get(agent, referenced_name.unbind(), value, gc) } } Value::Symbol(_) => agent .current_realm() .intrinsics() .symbol_prototype() - .internal_get(agent, referenced_name.unbind(), value, gc.reborrow()), + .internal_get(agent, referenced_name.unbind(), value, gc), Value::Number(_) | Value::Integer(_) | Value::SmallF64(_) => agent .current_realm() .intrinsics() .number_prototype() - .internal_get(agent, referenced_name.unbind(), value, gc.reborrow()), + .internal_get(agent, referenced_name.unbind(), value, gc), Value::BigInt(_) | Value::SmallBigInt(_) => agent .current_realm() .intrinsics() .big_int_prototype() - .internal_get(agent, referenced_name.unbind(), value, gc.reborrow()), + .internal_get(agent, referenced_name.unbind(), value, gc), _ => unreachable!(), } } @@ -221,7 +222,7 @@ pub(crate) fn get_value( PropertyKey::SmallString(data) => String::SmallString(*data), _ => unreachable!(), }; - Ok(env.get_binding_value(agent, referenced_name, reference.strict, gc.reborrow())?) + Ok(env.get_binding_value(agent, referenced_name, reference.strict, gc)?) } Base::Unresolvable => { // 2. If IsUnresolvableReference(V) is true, throw a ReferenceError exception. @@ -234,6 +235,127 @@ pub(crate) fn get_value( } } +/// ### [6.2.5.5 GetValue ( V )](https://tc39.es/ecma262/#sec-getvalue) +/// The abstract operation GetValue takes argument V (a Reference Record or an +/// ECMAScript language value) and returns either a normal completion +/// containing an ECMAScript language value or an abrupt completion. +pub(crate) fn try_get_value<'gc>( + agent: &mut Agent, + reference: &Reference, + gc: NoGcScope<'gc, '_>, +) -> TryResult>> { + let referenced_name = reference.referenced_name.bind(gc); + match reference.base { + Base::Value(value) => { + // 3. If IsPropertyReference(V) is true, then + // a. Let baseObj be ? ToObject(V.[[Base]]). + + // NOTE + // The object that may be created in step 3.a is not + // accessible outside of the above abstract operation + // and the ordinary object [[Get]] internal method. An + // implementation might choose to avoid the actual + // creation of the object. + if let Ok(object) = Object::try_from(value) { + // c. Return ? baseObj.[[Get]](V.[[ReferencedName]], GetThisValue(V)). + TryResult::Continue(Ok(object.try_get( + agent, + referenced_name.unbind(), + get_this_value(reference), + gc, + )?)) + } else { + // Primitive value. annoying stuff. + match value { + Value::Undefined => { + let error_message = format!( + "Cannot read property '{}' of undefined.", + referenced_name.as_display(agent) + ); + TryResult::Continue(Err(agent.throw_exception( + ExceptionType::TypeError, + error_message, + gc, + ))) + } + Value::Null => { + let error_message = format!( + "Cannot read property '{}' of null.", + referenced_name.as_display(agent) + ); + TryResult::Continue(Err(agent.throw_exception( + ExceptionType::TypeError, + error_message, + gc, + ))) + } + Value::Boolean(_) => TryResult::Continue(Ok(agent + .current_realm() + .intrinsics() + .boolean_prototype() + .try_get(agent, referenced_name.unbind(), value, gc)?)), + Value::String(_) | Value::SmallString(_) => { + let string = String::try_from(value).unwrap(); + if let Some(prop_desc) = + string.get_property_descriptor(agent, referenced_name) + { + TryResult::Continue(Ok(prop_desc.value.unwrap())) + } else { + TryResult::Continue(Ok(agent + .current_realm() + .intrinsics() + .string_prototype() + .try_get(agent, referenced_name.unbind(), value, gc)?)) + } + } + Value::Symbol(_) => TryResult::Continue(Ok(agent + .current_realm() + .intrinsics() + .symbol_prototype() + .try_get(agent, referenced_name.unbind(), value, gc)?)), + Value::Number(_) | Value::Integer(_) | Value::SmallF64(_) => { + TryResult::Continue(Ok(agent + .current_realm() + .intrinsics() + .number_prototype() + .try_get(agent, referenced_name.unbind(), value, gc)?)) + } + Value::BigInt(_) | Value::SmallBigInt(_) => TryResult::Continue(Ok(agent + .current_realm() + .intrinsics() + .big_int_prototype() + .try_get(agent, referenced_name.unbind(), value, gc)?)), + _ => unreachable!(), + } + } + } + Base::Environment(env) => { + // 4. Else, + // a. Let base be V.[[Base]]. + // b. Assert: base is an Environment Record. + // c. Return ? base.GetBindingValue(V.[[ReferencedName]], V.[[Strict]]) (see 9.1). + let referenced_name = match &reference.referenced_name { + PropertyKey::String(data) => String::String(*data), + PropertyKey::SmallString(data) => String::SmallString(*data), + _ => unreachable!(), + }; + env.try_get_binding_value(agent, referenced_name, reference.strict, gc) + } + Base::Unresolvable => { + // 2. If IsUnresolvableReference(V) is true, throw a ReferenceError exception. + let error_message = format!( + "Cannot access undeclared variable '{}'.", + referenced_name.as_display(agent) + ); + TryResult::Continue(Err(agent.throw_exception( + ExceptionType::ReferenceError, + error_message, + gc, + ))) + } + } +} + /// ### [6.2.5.6 PutValue ( V, W )](https://tc39.es/ecma262/#sec-putvalue) /// /// The abstract operation PutValue takes arguments V (a Reference Record or an @@ -471,7 +593,7 @@ pub(crate) fn try_initialize_referenced_binding<'a>( /// ### {6.2.5.7 GetThisValue ( V )}(https://tc39.es/ecma262/#sec-getthisvalue) /// The abstract operation GetThisValue takes argument V (a Reference Record) /// and returns an ECMAScript language value. -pub(crate) fn get_this_value(reference: &Reference) -> Value { +pub(crate) fn get_this_value<'a>(reference: &Reference<'a>) -> Value<'a> { // 1. Assert: IsPropertyReference(V) is true. debug_assert!(is_property_reference(reference)); // 2. If IsSuperReference(V) is true, return V.[[ThisValue]]; otherwise return V.[[Base]]. @@ -483,9 +605,9 @@ pub(crate) fn get_this_value(reference: &Reference) -> Value { }) } -#[derive(Debug, PartialEq)] -pub(crate) enum Base { - Value(Value), +#[derive(Debug, PartialEq, Clone)] +pub(crate) enum Base<'a> { + Value(Value<'a>), Environment(EnvironmentIndex), Unresolvable, } @@ -516,7 +638,7 @@ impl HeapMarkAndSweep for Reference<'static> { } } -impl HeapMarkAndSweep for Base { +impl HeapMarkAndSweep for Base<'static> { fn mark_values(&self, queues: &mut WorkQueues) { match self { Base::Value(value) => value.mark_values(queues), diff --git a/nova_vm/src/engine.rs b/nova_vm/src/engine.rs index 61adcb12f..98446c3d4 100644 --- a/nova_vm/src/engine.rs +++ b/nova_vm/src/engine.rs @@ -4,7 +4,6 @@ mod bytecode; pub mod context; -pub mod register_value; pub mod rootable; pub mod small_f64; pub mod small_integer; diff --git a/nova_vm/src/engine/bytecode/bytecode_compiler.rs b/nova_vm/src/engine/bytecode/bytecode_compiler.rs index bdedeae40..13f81a784 100644 --- a/nova_vm/src/engine/bytecode/bytecode_compiler.rs +++ b/nova_vm/src/engine/bytecode/bytecode_compiler.rs @@ -22,7 +22,7 @@ use crate::{ }, types::{BigInt, IntoValue, Number, PropertyKey, String, Value, BUILTIN_STRING_MEMORY}, }, - engine::context::NoGcScope, + engine::context::{Bindable, NoGcScope}, heap::CreateHeapData, }; use num_traits::Num; @@ -53,7 +53,7 @@ pub(crate) struct CompileContext<'agent, 'gc, 'scope> { /// Instructions being built instructions: Vec, /// Constants being built - constants: Vec, + constants: Vec>, /// Function expressions being built function_expressions: Vec, /// Arrow function expressions being built @@ -231,7 +231,7 @@ impl<'a, 'gc, 'scope> CompileContext<'a, 'gc, 'scope> { pub(super) fn finish(self) -> Executable { self.agent.heap.create(ExecutableHeapData { instructions: self.instructions.into_boxed_slice(), - constants: self.constants.into_boxed_slice(), + constants: self.constants.iter().map(|v| v.unbind()).collect(), function_expressions: self.function_expressions.into_boxed_slice(), arrow_function_expressions: self.arrow_function_expressions.into_boxed_slice(), class_initializer_bytecodes: self.class_initializer_bytecodes.into_boxed_slice(), @@ -303,7 +303,7 @@ impl<'a, 'gc, 'scope> CompileContext<'a, 'gc, 'scope> { } } - fn add_constant(&mut self, constant: Value) -> usize { + fn add_constant(&mut self, constant: Value<'gc>) -> usize { let duplicate = self .constants .iter() @@ -318,7 +318,7 @@ impl<'a, 'gc, 'scope> CompileContext<'a, 'gc, 'scope> { }) } - fn add_identifier(&mut self, identifier: String) -> usize { + fn add_identifier(&mut self, identifier: String<'gc>) -> usize { let duplicate = self .constants .iter() @@ -342,7 +342,7 @@ impl<'a, 'gc, 'scope> CompileContext<'a, 'gc, 'scope> { fn add_instruction_with_constant( &mut self, instruction: Instruction, - constant: impl Into, + constant: impl Into>, ) { debug_assert_eq!(instruction.argument_count(), 1); debug_assert!(instruction.has_constant_index()); @@ -367,7 +367,7 @@ impl<'a, 'gc, 'scope> CompileContext<'a, 'gc, 'scope> { &mut self, instruction: Instruction, identifier: String<'gc>, - constant: impl Into, + constant: impl Into>, ) { debug_assert_eq!(instruction.argument_count(), 2); debug_assert!(instruction.has_identifier_index() && instruction.has_constant_index()); @@ -629,6 +629,7 @@ impl CompileEvaluation for ast::UnaryExpression<'_> { if is_reference(&self.argument) { ctx.add_instruction(Instruction::GetValue); } + ctx.add_instruction(Instruction::ToNumeric); ctx.add_instruction(Instruction::BitwiseNot); } // 13.5.3 The typeof Operator @@ -1753,10 +1754,10 @@ impl CompileEvaluation for ast::UpdateExpression<'_> { | ast::SimpleAssignmentTarget::TSTypeAssertion(_) => unreachable!(), } ctx.add_instruction(Instruction::GetValueKeepReference); + ctx.add_instruction(Instruction::ToNumeric); if !self.prefix { // The return value of postfix increment/decrement is the value // after ToNumeric. - ctx.add_instruction(Instruction::ToNumeric); ctx.add_instruction(Instruction::LoadCopy); } match self.operator { diff --git a/nova_vm/src/engine/bytecode/executable.rs b/nova_vm/src/engine/bytecode/executable.rs index b42d7af0b..100da4ad5 100644 --- a/nova_vm/src/engine/bytecode/executable.rs +++ b/nova_vm/src/engine/bytecode/executable.rs @@ -17,7 +17,7 @@ use crate::{ syntax_directed_operations::function_definitions::CompileFunctionBodyData, types::{String, Value}, }, - engine::context::NoGcScope, + engine::context::{Bindable, NoGcScope}, heap::{CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues}, }; use oxc_ast::ast::{self, Program, Statement}; @@ -101,13 +101,13 @@ const EXECUTABLE_OPTION_SIZE_IS_U32: () = #[derive(Debug, Clone)] pub(crate) struct ExecutableHeapData { pub instructions: Box<[u8]>, - pub(crate) constants: Box<[Value]>, + pub(crate) constants: Box<[Value<'static>]>, pub(crate) function_expressions: Box<[FunctionExpression]>, pub(crate) arrow_function_expressions: Box<[ArrowFunctionExpression]>, pub(crate) class_initializer_bytecodes: Box<[(Option, bool)]>, } -impl Executable { +impl<'gc> Executable { pub(crate) fn compile_script( agent: &mut Agent, script: ScriptIdentifier, @@ -208,12 +208,16 @@ impl Executable { } #[inline] - pub(super) fn get_constants(self, agent: &Agent) -> &[Value] { + pub(super) fn get_constants<'a>( + self, + agent: &'a Agent, + _: NoGcScope<'gc, '_>, + ) -> &'a [Value<'gc>] { &agent[self].constants[..] } #[inline] - pub(super) fn fetch_identifier<'gc>( + pub(super) fn fetch_identifier( self, agent: &Agent, index: usize, @@ -231,12 +235,17 @@ impl Executable { } #[inline] - pub(super) fn fetch_constant(self, agent: &Agent, index: usize) -> Value { + pub(super) fn fetch_constant( + self, + agent: &Agent, + index: usize, + gc: NoGcScope<'gc, '_>, + ) -> Value<'gc> { // SAFETY: As long as we're alive the constants Box lives. It is // accessed mutably only during GC, during which this function is never // called. As we do not hand out a reference here, the mutable // reference during GC and fetching references here never overlap. - agent[self].constants[index] + agent[self].constants[index].bind(gc) } pub(super) fn fetch_function_expression( diff --git a/nova_vm/src/engine/bytecode/instructions.rs b/nova_vm/src/engine/bytecode/instructions.rs index 94a58c11e..a0250c229 100644 --- a/nova_vm/src/engine/bytecode/instructions.rs +++ b/nova_vm/src/engine/bytecode/instructions.rs @@ -591,7 +591,7 @@ fn debug_print_constant( index: usize, gc: NoGcScope, ) -> std::string::String { - let constant = exe.fetch_constant(agent, index); + let constant = exe.fetch_constant(agent, index, gc); if let Ok(string_constant) = String::try_from(constant) { format!("\"{}\"", string_constant.as_str(agent)) } else { diff --git a/nova_vm/src/engine/bytecode/iterator.rs b/nova_vm/src/engine/bytecode/iterator.rs index 38818f445..110b7759d 100644 --- a/nova_vm/src/engine/bytecode/iterator.rs +++ b/nova_vm/src/engine/bytecode/iterator.rs @@ -7,7 +7,8 @@ use std::collections::VecDeque; use crate::ecmascript::abstract_operations::operations_on_iterator_objects::get_iterator_from_method; use crate::ecmascript::abstract_operations::operations_on_objects::{call, get, get_method}; use crate::ecmascript::abstract_operations::type_conversion::to_boolean; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::ecmascript::types::IntoValue; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_iterator_objects::IteratorRecord, @@ -25,7 +26,7 @@ pub(super) enum VmIterator { ObjectProperties(ObjectPropertiesIterator), ArrayValues(ArrayValuesIterator), GenericIterator(IteratorRecord), - SliceIterator(SendableRef<[Value]>), + SliceIterator(SendableRef<[Value<'static>]>), } impl VmIterator { @@ -55,21 +56,24 @@ impl VmIterator { /// function implements much the same intent. It does the IteratorNext /// step, followed by a completion check, and finally extracts the value /// if the iterator did not complete yet. - pub(super) fn step_value( + pub(super) fn step_value<'gc>( &mut self, agent: &mut Agent, - mut gc: GcScope, - ) -> JsResult> { + mut gc: GcScope<'gc, '_>, + ) -> JsResult>> { match self { VmIterator::ObjectProperties(iter) => { let result = iter.next(agent, gc.reborrow())?; if let Some(result) = result { + let result = result.unbind(); + let gc = gc.into_nogc(); + let result = result.bind(gc); Ok(Some(match result { PropertyKey::Integer(int) => { - Value::from_string(agent, int.into_i64().to_string(), gc.nogc()) + Value::from_string(agent, int.into_i64().to_string(), gc) } PropertyKey::SmallString(data) => Value::SmallString(data), - PropertyKey::String(data) => Value::String(data.unbind()), + PropertyKey::String(data) => Value::String(data), _ => unreachable!(), })) } else { @@ -92,10 +96,12 @@ impl VmIterator { gc.nogc(), )); }; + let result = result.unbind().bind(gc.nogc()); + let scoped_result = result.scope(agent, gc.nogc()); // 1. Return ToBoolean(? Get(iterResult, "done")). let done = get( agent, - result, + result.unbind(), BUILTIN_STRING_MEMORY.done.into(), gc.reborrow(), )?; @@ -106,9 +112,9 @@ impl VmIterator { // 1. Return ? Get(iterResult, "value"). let value = get( agent, - result, + scoped_result.get(agent), BUILTIN_STRING_MEMORY.value.into(), - gc.reborrow(), + gc, )?; Ok(Some(value)) } @@ -266,7 +272,11 @@ impl ArrayValuesIterator { } } - pub(super) fn next(&mut self, agent: &mut Agent, gc: GcScope) -> JsResult> { + pub(super) fn next<'gc>( + &mut self, + agent: &mut Agent, + gc: GcScope<'gc, '_>, + ) -> JsResult>> { // b. Repeat, let array = self.array; // iv. Let indexNumber be 𝔽(index). @@ -282,7 +292,7 @@ impl ArrayValuesIterator { if let Some(element_value) = array.as_slice(agent)[index as usize] { // Fast path: If the element at this index has a Value, then it is // not an accessor nor a hole. Yield the result as-is. - return Ok(Some(element_value)); + return Ok(Some(element_value.bind(gc.into_nogc()))); } // 1. Let elementKey be ! ToString(indexNumber). // 2. Let elementValue be ? Get(array, elementKey). diff --git a/nova_vm/src/engine/bytecode/vm.rs b/nova_vm/src/engine/bytecode/vm.rs index 56c078900..ad477b351 100644 --- a/nova_vm/src/engine/bytecode/vm.rs +++ b/nova_vm/src/engine/bytecode/vm.rs @@ -4,8 +4,7 @@ mod binding_methods; -use core::ptr::NonNull; -use std::sync::OnceLock; +use std::{ptr::NonNull, sync::OnceLock}; use ahash::AHashSet; use binding_methods::{execute_simple_array_binding, execute_simple_object_binding}; @@ -24,7 +23,7 @@ use crate::{ copy_data_properties_into_object, create_data_property_or_throw, define_property_or_throw, get_method, has_property, ordinary_has_instance, set, try_copy_data_properties_into_object, try_create_data_property, - try_create_data_property_or_throw, try_define_property_or_throw, + try_create_data_property_or_throw, try_define_property_or_throw, try_has_property, }, testing_and_comparison::{ is_callable, is_constructor, is_less_than, is_loosely_equal, is_strictly_equal, @@ -50,9 +49,10 @@ use crate::{ }, types::{ get_this_value, get_value, initialize_referenced_binding, is_private_reference, - is_super_reference, put_value, Base, BigInt, Function, InternalMethods, IntoFunction, - IntoObject, IntoValue, Number, Numeric, Object, OrdinaryObject, Primitive, - PropertyDescriptor, PropertyKey, Reference, String, Value, BUILTIN_STRING_MEMORY, + is_super_reference, put_value, try_initialize_referenced_binding, Base, BigInt, + Function, InternalMethods, IntoFunction, IntoObject, IntoValue, Number, Numeric, + Object, OrdinaryObject, Primitive, PropertyDescriptor, PropertyKey, Reference, String, + Value, BUILTIN_STRING_MEMORY, }, }, engine::{ @@ -63,7 +63,7 @@ use crate::{ Executable, FunctionExpression, IndexType, Instruction, InstructionIter, NamedEvaluationParameter, }, - context::GcScope, + context::{Bindable, GcScope, NoGcScope}, unwrap_try, TryResult, }, heap::{CompactionLists, HeapMarkAndSweep, WellKnownSymbolIndexes, WorkQueues}, @@ -76,20 +76,20 @@ unsafe impl Send for EmptyParametersList {} unsafe impl Sync for EmptyParametersList {} #[derive(Debug)] -pub(crate) enum ExecutionResult { - Return(Value), +pub(crate) enum ExecutionResult<'a> { + Return(Value<'a>), Throw(JsError), Await { vm: SuspendedVm, - awaited_value: Value, + awaited_value: Value<'a>, }, Yield { vm: SuspendedVm, - yielded_value: Value, + yielded_value: Value<'a>, }, } -impl ExecutionResult { - pub(crate) fn into_js_result(self) -> JsResult { +impl<'a> ExecutionResult<'a> { + pub(crate) fn into_js_result(self) -> JsResult> { match self { ExecutionResult::Return(value) => Ok(value), ExecutionResult::Throw(err) => Err(err), @@ -98,6 +98,43 @@ impl ExecutionResult { } } +// SAFETY: Property implemented as a recursive bind. +unsafe impl Bindable for ExecutionResult<'_> { + type Of<'a> = ExecutionResult<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + match self { + Self::Return(value) => ExecutionResult::Return(value.unbind()), + Self::Throw(js_error) => ExecutionResult::Throw(js_error), + Self::Await { vm, awaited_value } => ExecutionResult::Await { + vm, + awaited_value: awaited_value.unbind(), + }, + Self::Yield { vm, yielded_value } => ExecutionResult::Yield { + vm, + yielded_value: yielded_value.unbind(), + }, + } + } + + #[inline(always)] + fn bind<'a>(self, gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + match self { + Self::Return(value) => ExecutionResult::Return(value.bind(gc)), + Self::Throw(js_error) => ExecutionResult::Throw(js_error), + Self::Await { vm, awaited_value } => ExecutionResult::Await { + vm, + awaited_value: awaited_value.bind(gc), + }, + Self::Yield { vm, yielded_value } => ExecutionResult::Yield { + vm, + yielded_value: yielded_value.bind(gc), + }, + } + } +} + /// Indicates how the execution of an instruction should affect the remainder of /// execution that contains it. #[must_use] @@ -125,11 +162,11 @@ struct ExceptionJumpTarget { pub(crate) struct Vm { /// Instruction pointer. ip: usize, - stack: Vec, + stack: Vec>, reference_stack: Vec>, iterator_stack: Vec, exception_jump_target_stack: Vec, - result: Option, + result: Option>, reference: Option>, } @@ -140,7 +177,7 @@ pub(crate) struct SuspendedVm { /// expression. This is reasonably rare that we can expect the stack to /// usually be empty. In this case this Box is an empty dangling pointer /// and no heap data clone is required. - stack: Box<[Value]>, + stack: Box<[Value<'static>]>, /// Note: Reference stack is non-empty only if the code awaits inside a /// call expression. This means that usually no heap data clone is /// required. @@ -155,28 +192,28 @@ pub(crate) struct SuspendedVm { } impl SuspendedVm { - pub(crate) fn resume( + pub(crate) fn resume<'gc>( self, agent: &mut Agent, executable: Executable, value: Value, - gc: GcScope, - ) -> ExecutionResult { + gc: GcScope<'gc, '_>, + ) -> ExecutionResult<'gc> { let vm = Vm::from_suspended(self); vm.resume(agent, executable, value, gc) } - pub(crate) fn resume_throw( + pub(crate) fn resume_throw<'gc>( self, agent: &mut Agent, executable: Executable, err: Value, - gc: GcScope, - ) -> ExecutionResult { + gc: GcScope<'gc, '_>, + ) -> ExecutionResult<'gc> { // Optimisation: Avoid unsuspending the Vm if we're just going to throw // out of it immediately. if self.exception_jump_target_stack.is_empty() { - let err = JsError::new(err); + let err = JsError::new(err.unbind()); return ExecutionResult::Throw(err); } let vm = Vm::from_suspended(self); @@ -224,12 +261,12 @@ impl<'a> Vm { } /// Executes an executable using the virtual machine. - pub(crate) fn execute( + pub(crate) fn execute<'gc>( agent: &mut Agent, executable: Executable, - arguments: Option<&[Value]>, - gc: GcScope, - ) -> ExecutionResult { + arguments: Option<&[Value<'static>]>, + gc: GcScope<'gc, '_>, + ) -> ExecutionResult<'gc> { let mut vm = Vm::new(); if let Some(arguments) = arguments { @@ -243,7 +280,10 @@ impl<'a> Vm { if agent.options.print_internals { eprintln!(); eprintln!("=== Executing Executable ==="); - eprintln!("Constants: {:?}", executable.get_constants(agent)); + eprintln!( + "Constants: {:?}", + executable.get_constants(agent, gc.nogc()) + ); eprintln!(); eprintln!("Instructions:"); @@ -257,37 +297,38 @@ impl<'a> Vm { vm.inner_execute(agent, executable, gc) } - pub fn resume( + pub fn resume<'gc>( mut self, agent: &mut Agent, executable: Executable, value: Value, - gc: GcScope, - ) -> ExecutionResult { - self.result = Some(value); + gc: GcScope<'gc, '_>, + ) -> ExecutionResult<'gc> { + self.result = Some(value.unbind()); self.inner_execute(agent, executable, gc) } - pub fn resume_throw( + pub fn resume_throw<'gc>( mut self, agent: &mut Agent, executable: Executable, err: Value, - gc: GcScope, - ) -> ExecutionResult { - let err = JsError::new(err); + gc: GcScope<'gc, '_>, + ) -> ExecutionResult<'gc> { + let err = err.bind(gc.nogc()); + let err = JsError::new(err.unbind()); if !self.handle_error(agent, err) { return ExecutionResult::Throw(err); } self.inner_execute(agent, executable, gc) } - fn inner_execute( + fn inner_execute<'gc>( mut self, agent: &mut Agent, executable: Executable, - mut gc: GcScope<'a, '_>, - ) -> ExecutionResult { + mut gc: GcScope<'gc, '_>, + ) -> ExecutionResult<'gc> { #[cfg(feature = "interleaved-gc")] let do_gc = !agent.options.disable_gc; #[cfg(feature = "interleaved-gc")] @@ -306,19 +347,15 @@ impl<'a> Vm { .enumerate() .map(|(i, _)| Some(RealmIdentifier::from_index(i))) .collect::>(); - let vm = unsafe { NonNull::new_unchecked(&mut self) }; - agent - .vm_stack - // SAFETY: Pointer to self is never null. - .push(vm); - heap_gc(agent, &mut root_realms, gc.reborrow()); - let return_vm = agent.vm_stack.pop().unwrap(); - assert_eq!(vm, return_vm, "VM Stack was misused"); + with_vm_gc( + agent, + &mut self, + |agent, gc| heap_gc(agent, &mut root_realms, gc), + gc.reborrow(), + ); } } - let temp = &mut self; - let temp_self = unsafe { core::mem::transmute::<&mut Vm, &mut Vm>(temp) }; - match Self::execute_instruction(agent, temp_self, executable, &instr, gc.reborrow()) { + match Self::execute_instruction(agent, &mut self, executable, &instr, gc.reborrow()) { Ok(ContinuationKind::Normal) => {} Ok(ContinuationKind::Return) => { let result = self.result.unwrap_or(Value::Undefined); @@ -378,44 +415,63 @@ impl<'a> Vm { } match instr.kind { Instruction::ArrayCreate => { - vm.stack.push( + let result = array_create(agent, 0, instr.args[0].unwrap() as usize, None, gc.nogc())? - .into_value(), - ); + .into_value(); + vm.stack.push(result.unbind()); } Instruction::ArrayPush => { - let value = vm.result.take().unwrap(); - let array = *vm.stack.last().unwrap(); + let value = vm.result.take().unwrap().bind(gc.nogc()); + let array = vm.stack.last().unwrap().bind(gc.nogc()); let Ok(array) = Array::try_from(array) else { unreachable!(); }; let len = array.len(agent); let key = PropertyKey::Integer(len.into()); - create_data_property_or_throw(agent, array, key, value, gc.reborrow())? + let array = array.unbind(); + let value = value.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| create_data_property_or_throw(agent, array, key, value, gc), + gc, + )?; } Instruction::ArrayElision => { - let array = *vm.stack.last().unwrap(); + let array = vm.stack.last().unwrap().bind(gc.nogc()); let Ok(array) = Array::try_from(array) else { unreachable!(); }; - set( + let length = array.len(agent) + 1; + let array = array.into_object().unbind(); + with_vm_gc( agent, - array.into_object(), - BUILTIN_STRING_MEMORY.length.into(), - (array.len(agent) + 1).into(), - true, + vm, + |agent, gc| { + set( + agent, + array, + BUILTIN_STRING_MEMORY.length.into(), + length.into(), + true, + gc, + ) + }, gc, )?; } Instruction::Await => return Ok(ContinuationKind::Await), Instruction::BitwiseNot => { // 2. Let oldValue be ? ToNumeric(? GetValue(expr)). - let old_value = to_numeric(agent, vm.result.take().unwrap(), gc.reborrow())?; + // Note: This step is a separate instruction. + let old_value = Numeric::try_from(vm.result.take().unwrap()) + .unwrap() + .bind(gc.nogc()); // 3. If oldValue is a Number, then if let Ok(old_value) = Number::try_from(old_value) { // a. Return Number::bitwiseNOT(oldValue). - vm.result = Some(Number::bitwise_not(agent, old_value).into_value()); + vm.result = Some(Number::bitwise_not(agent, old_value).into_value().unbind()); } else { // 4. Else, // a. Assert: oldValue is a BigInt. @@ -424,7 +480,7 @@ impl<'a> Vm { }; // b. Return BigInt::bitwiseNOT(oldValue). - vm.result = Some(BigInt::bitwise_not(agent, old_value).into_value()); + vm.result = Some(BigInt::bitwise_not(agent, old_value).into_value().unbind()); } } Instruction::Debug => { @@ -444,18 +500,20 @@ impl<'a> Vm { // 1. Let envRec be GetThisEnvironment(). let env_rec = get_this_environment(agent); // 2. Return ? envRec.GetThisBinding(). - vm.result = Some(match env_rec { + let result = match env_rec { EnvironmentIndex::Declarative(_) => unreachable!(), EnvironmentIndex::Function(idx) => idx.get_this_binding(agent, gc.nogc())?, EnvironmentIndex::Global(idx) => { idx.get_this_binding(agent, gc.nogc()).into_value() } EnvironmentIndex::Object(_) => unreachable!(), - }); + }; + vm.result = Some(result.unbind()); } Instruction::LoadConstant => { - let constant = executable.fetch_constant(agent, instr.args[0].unwrap() as usize); - vm.stack.push(constant); + let constant = + executable.fetch_constant(agent, instr.args[0].unwrap() as usize, gc.nogc()); + vm.stack.push(constant.unbind()); } Instruction::Load => { vm.stack.push(vm.result.take().unwrap()); @@ -481,16 +539,17 @@ impl<'a> Vm { vm.result = Some(*vm.stack.last().expect("Trying to get from empty stack")); } Instruction::StoreConstant => { - let constant = executable.fetch_constant(agent, instr.args[0].unwrap() as usize); - vm.result = Some(constant); + let constant = + executable.fetch_constant(agent, instr.args[0].unwrap() as usize, gc.nogc()); + vm.result = Some(constant.unbind()); } Instruction::UnaryMinus => { - let old_value = vm.result.unwrap(); + let old_value = vm.result.unwrap().bind(gc.nogc()); // 3. If oldValue is a Number, then - if let Ok(old_value) = Number::try_from(old_value) { + let result = if let Ok(old_value) = Number::try_from(old_value) { // a. Return Number::unaryMinus(oldValue). - vm.result = Some(Number::unary_minus(agent, old_value).into()); + Number::unary_minus(agent, old_value).into_value() } // 4. Else, else { @@ -498,35 +557,51 @@ impl<'a> Vm { let old_value = BigInt::try_from(old_value).unwrap(); // b. Return BigInt::unaryMinus(oldValue). - vm.result = Some(BigInt::unary_minus(agent, old_value).into()); - } + BigInt::unary_minus(agent, old_value).into_value() + }; + vm.result = Some(result.unbind()); } Instruction::ToNumber => { - vm.result = to_number(agent, vm.result.unwrap(), gc.reborrow()) - .map(|number| Some(number.into()))?; + let arg0 = vm.result.unwrap(); + let result = with_vm_gc(agent, vm, |agent, gc| to_number(agent, arg0, gc), gc)?; + vm.result = Some(result.into_value().unbind()); } Instruction::ToNumeric => { - vm.result = Some( - to_numeric(agent, vm.result.unwrap(), gc.reborrow()) - .map(|result| result.into_value())?, - ); + let arg0 = vm.result.unwrap(); + let result = with_vm_gc(agent, vm, |agent, gc| to_numeric(agent, arg0, gc), gc)?; + vm.result = Some(result.into_value().unbind()); } Instruction::ToObject => { - vm.result = Some(to_object(agent, vm.result.unwrap(), gc.nogc())?.into_value()); + vm.result = Some( + to_object(agent, vm.result.unwrap(), gc.nogc())? + .into_value() + .unbind(), + ); } Instruction::ApplyStringOrNumericBinaryOperator(op_text) => { let lval = vm.stack.pop().unwrap(); let rval = vm.result.take().unwrap(); - vm.result = Some(apply_string_or_numeric_binary_operator( - agent, lval, op_text, rval, gc, - )?); + let result = with_vm_gc( + agent, + vm, + |agent, gc| { + apply_string_or_numeric_binary_operator(agent, lval, op_text, rval, gc) + }, + gc, + )?; + vm.result = Some(result.unbind()); } Instruction::ObjectDefineProperty => { - let value = vm.result.take().unwrap(); - let key = to_property_key(agent, vm.stack.pop().unwrap(), gc.reborrow())? - .unbind() - .bind(gc.nogc()); - let object = *vm.stack.last().unwrap(); + let key = vm.stack.pop().unwrap(); + let key = with_vm_gc( + agent, + vm, + |agent, gc| to_property_key(agent, key, gc), + gc.reborrow(), + )?; + let key = key.unbind().bind(gc.nogc()); + let value = vm.result.take().unwrap().bind(gc.nogc()); + let object = vm.stack.last().unwrap().bind(gc.nogc()); let object = Object::try_from(object).unwrap(); unwrap_try(try_create_data_property_or_throw( @@ -544,10 +619,18 @@ impl<'a> Vm { let function_expression = expression.get(); let enumerable = instr.args[1].unwrap() != 0; // 1. Let propKey be ? Evaluation of ClassElementName. - let prop_key = to_property_key(agent, vm.stack.pop().unwrap(), gc.reborrow())? - .unbind() + let prop_key = vm.stack.pop().unwrap(); + let prop_key = with_vm_gc( + agent, + vm, + |agent, gc| to_property_key(agent, prop_key, gc), + gc.reborrow(), + )? + .unbind() + .bind(gc.nogc()); + let object = Object::try_from(*vm.stack.last().unwrap()) + .unwrap() .bind(gc.nogc()); - let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); // 2. Let env be the running execution context's LexicalEnvironment. // 3. Let privateEnv be the running execution context's PrivateEnvironment. @@ -614,7 +697,7 @@ impl<'a> Vm { // [[Configurable]]: true // }. let desc = PropertyDescriptor { - value: Some(closure.into_value()), + value: Some(closure.into_value().unbind()), writable: Some(true), enumerable: Some(enumerable), configurable: Some(true), @@ -624,7 +707,14 @@ impl<'a> Vm { // c. NOTE: DefinePropertyOrThrow only returns an abrupt // completion when attempting to define a class static method whose key is "prototype". - define_property_or_throw(agent, object, prop_key.unbind(), desc, gc.reborrow())?; + let object = object.unbind(); + let prop_key = prop_key.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| define_property_or_throw(agent, object, prop_key, desc, gc), + gc, + )?; // c. Return unused. } Instruction::ObjectDefineGetter => { @@ -633,9 +723,15 @@ impl<'a> Vm { let function_expression = expression.get(); let enumerable = instr.args[1].unwrap() != 0; // 1. Let propKey be ? Evaluation of ClassElementName. - let prop_key = to_property_key(agent, vm.stack.pop().unwrap(), gc.reborrow())? - .unbind() - .bind(gc.nogc()); + let prop_key = vm.stack.pop().unwrap(); + let prop_key = with_vm_gc( + agent, + vm, + |agent, gc| to_property_key(agent, prop_key, gc), + gc.reborrow(), + )? + .unbind() + .bind(gc.nogc()); // 2. Let env be the running execution context's LexicalEnvironment. // 3. Let privateEnv be the running execution context's PrivateEnvironment. let ECMAScriptCodeEvaluationState { @@ -686,12 +782,14 @@ impl<'a> Vm { // ). let closure = ordinary_function_create(agent, params, gc.nogc()); // 7. Perform MakeMethod(closure, object). - let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); + let object = Object::try_from(*vm.stack.last().unwrap()) + .unwrap() + .bind(gc.nogc()); make_method(agent, closure, object.into_object()); // 8. Perform SetFunctionName(closure, propKey, "get"). set_function_name( agent, - closure.unbind(), + closure, prop_key, Some(BUILTIN_STRING_MEMORY.get), gc.nogc(), @@ -709,8 +807,14 @@ impl<'a> Vm { configurable: Some(true), }; // b. Perform ? DefinePropertyOrThrow(object, propKey, desc). - - define_property_or_throw(agent, object, prop_key.unbind(), desc, gc.reborrow())?; + let object = object.unbind(); + let prop_key = prop_key.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| define_property_or_throw(agent, object, prop_key, desc, gc), + gc, + )?; // c. Return unused. } Instruction::ObjectDefineSetter => { @@ -719,9 +823,15 @@ impl<'a> Vm { let function_expression = expression.get(); let enumerable = instr.args[1].unwrap() != 0; // 1. Let propKey be ? Evaluation of ClassElementName. - let prop_key = to_property_key(agent, vm.stack.pop().unwrap(), gc.reborrow())? - .unbind() - .bind(gc.nogc()); + let prop_key = vm.stack.pop().unwrap(); + let prop_key = with_vm_gc( + agent, + vm, + |agent, gc| to_property_key(agent, prop_key, gc), + gc.reborrow(), + )? + .unbind() + .bind(gc.nogc()); // 2. Let env be the running execution context's LexicalEnvironment. // 3. Let privateEnv be the running execution context's PrivateEnvironment. let ECMAScriptCodeEvaluationState { @@ -758,12 +868,14 @@ impl<'a> Vm { // ). let closure = ordinary_function_create(agent, params, gc.nogc()); // 6. Perform MakeMethod(closure, object). - let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); + let object = Object::try_from(*vm.stack.last().unwrap()) + .unwrap() + .bind(gc.nogc()); make_method(agent, closure, object.into_object()); // 7. Perform SetFunctionName(closure, propKey, "set"). set_function_name( agent, - closure.unbind(), + closure, prop_key, Some(BUILTIN_STRING_MEMORY.set), gc.nogc(), @@ -781,25 +893,40 @@ impl<'a> Vm { configurable: Some(true), }; // b. Perform ? DefinePropertyOrThrow(object, propKey, desc). - - define_property_or_throw(agent, object, prop_key.unbind(), desc, gc.reborrow())?; + let object = object.unbind(); + let prop_key = prop_key.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| define_property_or_throw(agent, object, prop_key, desc, gc), + gc.reborrow(), + )?; // c. Return unused. } Instruction::ObjectSetPrototype => { - let prop_value = vm.result.take().unwrap(); + let prop_value = vm.result.take().unwrap().bind(gc.nogc()); + // i. Perform ! object.[[SetPrototypeOf]](propValue). + let object = Object::try_from(*vm.stack.last().unwrap()) + .unwrap() + .bind(gc.nogc()); // a. If propValue is an Object or propValue is null, then let prop_value = if prop_value.is_null() { None } else if let Ok(prop_value) = Object::try_from(prop_value) { - Some(prop_value) + Some(prop_value.unbind()) } else { // b. Return unused. return Ok(ContinuationKind::Normal); }; - // i. Perform ! object.[[SetPrototypeOf]](propValue). - let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); - object.internal_set_prototype_of(agent, prop_value, gc.reborrow())?; + + let object = object.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| object.internal_set_prototype_of(agent, prop_value, gc), + gc, + )?; // b. Return unused. } Instruction::PushReference => { @@ -811,19 +938,30 @@ impl<'a> Vm { Instruction::PutValue => { let value = vm.result.take().unwrap(); let reference = vm.reference.take().unwrap(); - put_value(agent, &reference, value, gc.reborrow())?; + with_vm_gc( + agent, + vm, + |agent, gc| put_value(agent, &reference, value, gc), + gc, + )?; } Instruction::GetValue => { // 1. If V is not a Reference Record, return V. let reference = vm.reference.take().unwrap(); - vm.result = Some(get_value(agent, &reference, gc.reborrow())?); + let result = + with_vm_gc(agent, vm, |agent, gc| get_value(agent, &reference, gc), gc)?; + + vm.result = Some(result.unbind()); } Instruction::GetValueKeepReference => { // 1. If V is not a Reference Record, return V. - let reference = vm.reference.as_ref().unwrap(); + let reference = vm.reference.as_ref().unwrap().clone(); + + let result = + with_vm_gc(agent, vm, |agent, gc| get_value(agent, &reference, gc), gc)?; - vm.result = Some(get_value(agent, reference, gc.reborrow())?); + vm.result = Some(result.unbind()); } Instruction::Typeof => { // 2. If val is a Reference Record, then @@ -833,12 +971,12 @@ impl<'a> Vm { Value::Undefined } else { // 3. Set val to ? GetValue(val). - get_value(agent, &reference, gc.reborrow())? + with_vm_gc(agent, vm, |agent, gc| get_value(agent, &reference, gc), gc)? } } else { - vm.result.unwrap() + vm.result.unwrap().bind(gc.nogc()) }; - vm.result = Some(typeof_operator(agent, val).into()) + vm.result = Some(typeof_operator(agent, val).into_value()) } Instruction::ObjectCreate => { let object = ordinary_object_create_with_intrinsics( @@ -847,17 +985,24 @@ impl<'a> Vm { None, gc.nogc(), ); - vm.stack.push(object.into()) + vm.stack.push(object.into_value().unbind()) } Instruction::CopyDataProperties => { let source = vm.result.take().unwrap(); let Value::Object(target) = *vm.stack.last().unwrap() else { unreachable!() }; - copy_data_properties(agent, target, source, gc.reborrow())?; + with_vm_gc( + agent, + vm, + |agent, gc| copy_data_properties(agent, target, source, gc), + gc, + )?; } Instruction::CopyDataPropertiesIntoObject => { - let from = Object::try_from(vm.result.unwrap()).unwrap(); + let from = Object::try_from(vm.result.unwrap()) + .unwrap() + .bind(gc.nogc()); let num_excluded_items = usize::from(instr.args[0].unwrap()); let mut excluded_items = AHashSet::with_capacity(num_excluded_items); @@ -872,17 +1017,18 @@ impl<'a> Vm { if let TryResult::Continue(result) = try_copy_data_properties_into_object(agent, from, &excluded_items, gc.nogc()) { - vm.result = Some(result.into_value()); + vm.result = Some(result.into_value().unbind()); } else { - vm.result = Some( - copy_data_properties_into_object( - agent, - from, - &excluded_items, - gc.reborrow(), - )? - .into_value(), - ); + let from = from.unbind(); + let result = with_vm_gc( + agent, + vm, + |agent, gc| { + copy_data_properties_into_object(agent, from, &excluded_items, gc) + }, + gc, + )?; + vm.result = Some(result.into_value().unbind()); } } Instruction::InstantiateArrowFunctionExpression => { @@ -925,39 +1071,64 @@ impl<'a> Vm { }; let mut function = ordinary_function_create(agent, params, gc.nogc()); let name = if let Some(parameter) = &identifier { - let mut pk_option = None; let pk_result = match parameter { NamedEvaluationParameter::Result => { - pk_option = Some(vm.result.unwrap()); - to_property_key_simple(agent, pk_option.unwrap(), gc.nogc()) + let value = vm.result.unwrap().bind(gc.nogc()); + if let TryResult::Continue(pk) = + to_property_key_simple(agent, value, gc.nogc()) + { + Ok(pk) + } else { + Err(value) + } } NamedEvaluationParameter::Stack => { - pk_option = Some(*vm.stack.last().unwrap()); - to_property_key_simple(agent, pk_option.unwrap(), gc.nogc()) - } - NamedEvaluationParameter::Reference => { - TryResult::Continue(vm.reference.as_ref().unwrap().referenced_name) - } - NamedEvaluationParameter::ReferenceStack => { - TryResult::Continue(vm.reference_stack.last().unwrap().referenced_name) + let value = vm.stack.last().unwrap().bind(gc.nogc()); + if let TryResult::Continue(pk) = + to_property_key_simple(agent, value, gc.nogc()) + { + Ok(pk) + } else { + Err(value) + } } + NamedEvaluationParameter::Reference => Ok(vm + .reference + .as_ref() + .unwrap() + .referenced_name + .bind(gc.nogc())), + NamedEvaluationParameter::ReferenceStack => Ok(vm + .reference_stack + .last() + .unwrap() + .referenced_name + .bind(gc.nogc())), }; - if let TryResult::Continue(pk) = pk_result { - pk.bind(gc.nogc()) - } else { - let scoped_function = function.scope(agent, gc.nogc()); - let pk = pk_option.unwrap(); - let pk = to_property_key_complex(agent, pk, gc.reborrow())? + let pk = match pk_result { + Ok(pk) => pk.bind(gc.nogc()), + Err(pk_value) => { + let scoped_function = function.scope(agent, gc.nogc()); + let pk_value = pk_value.unbind(); + let pk = with_vm_gc( + agent, + vm, + |agent, gc| to_property_key_complex(agent, pk_value, gc), + gc.reborrow(), + )? .unbind() .bind(gc.nogc()); - function = scoped_function.get(agent).bind(gc.nogc()); - pk - } + function = scoped_function.get(agent).bind(gc.nogc()); + pk + } + }; + pk } else { - String::EMPTY_STRING.into() + let pk: PropertyKey = String::EMPTY_STRING.into(); + pk.bind(gc.nogc()) }; set_function_name(agent, function, name, None, gc.nogc()); - vm.result = Some(function.into_value()); + vm.result = Some(function.into_value().unbind()); } Instruction::InstantiateOrdinaryFunctionExpression => { let FunctionExpression { @@ -980,24 +1151,27 @@ impl<'a> Vm { let (name, env, init_binding) = if let Some(parameter) = identifier { debug_assert!(function_expression.id.is_none()); - let name = match parameter { - NamedEvaluationParameter::Result => { - to_property_key(agent, vm.result.unwrap(), gc.reborrow())? - .unbind() - .bind(gc.nogc()) - } - NamedEvaluationParameter::Stack => { - to_property_key(agent, *vm.stack.last().unwrap(), gc.reborrow())? - .unbind() - .bind(gc.nogc()) - } + let pk = match parameter { + NamedEvaluationParameter::Result => Ok(vm.result.unwrap()), + NamedEvaluationParameter::Stack => Ok(*vm.stack.last().unwrap()), NamedEvaluationParameter::Reference => { - vm.reference.as_ref().unwrap().referenced_name + Err(vm.reference.as_ref().unwrap().referenced_name) } NamedEvaluationParameter::ReferenceStack => { - vm.reference_stack.last().unwrap().referenced_name + Err(vm.reference_stack.last().unwrap().referenced_name) } }; + let name = with_vm_gc( + agent, + vm, + |agent, gc| match pk { + Ok(value) => to_property_key(agent, value, gc), + Err(pk) => Ok(pk.bind(gc.into_nogc())), + }, + gc.reborrow(), + )? + .unbind() + .bind(gc.nogc()); (name, lexical_environment, false) } else if let Some(binding_identifier) = &function_expression.id { let name = String::from_str(agent, &binding_identifier.name, gc.nogc()); @@ -1037,13 +1211,19 @@ impl<'a> Vm { let prototype = ordinary_object_create_with_intrinsics( agent, Some(ProtoIntrinsics::Object), - Some( + Some(if function_expression.r#async { + agent + .current_realm() + .intrinsics() + .async_generator_prototype() + .into_object() + } else { agent .current_realm() .intrinsics() .generator_prototype() - .into_object(), - ), + .into_object() + }), gc.nogc(), ); // 8. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). @@ -1052,7 +1232,7 @@ impl<'a> Vm { function, BUILTIN_STRING_MEMORY.prototype.to_property_key(), PropertyDescriptor { - value: Some(prototype.into_value()), + value: Some(prototype.into_value().unbind()), writable: Some(true), get: None, set: None, @@ -1080,7 +1260,7 @@ impl<'a> Vm { .unwrap(); } - vm.result = Some(function.into_value()); + vm.result = Some(function.into_value().unbind()); } Instruction::ClassDefineConstructor => { let FunctionExpression { @@ -1146,7 +1326,7 @@ impl<'a> Vm { agent, BUILTIN_STRING_MEMORY.constructor.into(), PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), writable: Some(true), enumerable: Some(false), configurable: Some(true), @@ -1155,7 +1335,7 @@ impl<'a> Vm { gc.nogc(), )); - vm.result = Some(function.into_value()); + vm.result = Some(function.into_value().unbind()); } Instruction::ClassDefineDefaultConstructor => { let class_initializer_bytecode_index = instr.args[0].unwrap(); @@ -1210,7 +1390,7 @@ impl<'a> Vm { agent, BUILTIN_STRING_MEMORY.constructor.into(), PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), writable: Some(true), enumerable: Some(false), configurable: Some(true), @@ -1219,7 +1399,7 @@ impl<'a> Vm { gc.nogc(), )); - vm.result = Some(function.into_value()); + vm.result = Some(function.into_value().unbind()); } Instruction::Swap => { let a = vm.stack.pop().unwrap(); @@ -1228,18 +1408,31 @@ impl<'a> Vm { vm.stack.push(b); } Instruction::DirectEvalCall => { - let args = vm.get_call_args(instr); - - let func_reference = - resolve_binding(agent, BUILTIN_STRING_MEMORY.eval, None, gc.reborrow())?; - let func = { get_value(agent, &func_reference.unbind(), gc.reborrow())? }; + let func = with_vm_gc( + agent, + vm, + |agent, mut gc| { + let func_ref = resolve_binding( + agent, + BUILTIN_STRING_MEMORY.eval, + None, + gc.reborrow(), + )? + .unbind(); + get_value(agent, &func_ref, gc) + }, + gc.reborrow(), + )? + .unbind() + .bind(gc.nogc()); + let args = vm.get_call_args(instr, gc.nogc()); // a. If SameValue(func, %eval%) is true, then - if func == agent.current_realm().intrinsics().eval().into_value() { + let result = if func == agent.current_realm().intrinsics().eval().into_value() { // i. Let argList be ? ArgumentListEvaluation of arguments. // ii. If argList has no elements, return undefined. if args.is_empty() { - vm.result = Some(Value::Undefined); + Value::Undefined } else { // iii. Let evalArg be the first element of argList. let eval_arg = args[0]; @@ -1252,56 +1445,42 @@ impl<'a> Vm { .unwrap() .is_strict_mode; // v. Return ? PerformEval(evalArg, strictCaller, true). - vm.result = Some(perform_eval( + let eval_arg = eval_arg.unbind(); + with_vm_gc( agent, - eval_arg, - true, - strict_caller, - gc.reborrow(), - )?); + vm, + |agent, gc| perform_eval(agent, eval_arg, true, strict_caller, gc), + gc, + )? } - } else if cfg!(feature = "interleaved-gc") { - let mut vm = NonNull::from(vm); - agent.vm_stack.push(vm); - let result = call( - agent, - func, - Value::Undefined, - Some(ArgumentsList(&args)), - gc, - ); - let return_vm = agent.vm_stack.pop().unwrap(); - assert_eq!(vm, return_vm, "VM Stack was misused"); - // SAFETY: This is fairly bonkers-unsafe. We have an - // exclusive reference to `Vm` so turning that to a NonNull - // and making the `&mut Vm` unreachable here isn't wrong. - // Passing that NonNull into a stack isn't wrong. - // Popping from that stack isn't wrong. - // Turning that back into a `&mut Vm` is probably wrong. - // Even though we can't reach the `vm: &mut Vm` in this - // scope anymore, it's still there. Hence we have two - // exclusive references alive at the same time. That's not - // a good look. I'm sorry. - unsafe { vm.as_mut() }.result = Some(result?); } else { - vm.result = Some(call( + let func = func.unbind(); + let args = args.unbind(); + with_vm_gc( agent, - func, - Value::Undefined, - Some(ArgumentsList(&args)), + vm, + |agent, gc| { + call( + agent, + func, + Value::Undefined, + Some(ArgumentsList(&args)), + gc, + ) + }, gc, - )?); - } + )? + }; + vm.result = Some(result.unbind()); } Instruction::EvaluateCall => { - let args = vm.get_call_args(instr); let reference = vm.reference.take(); // 1. If ref is a Reference Record, then let this_value = if let Some(reference) = reference { // a. If IsPropertyReference(ref) is true, then match reference.base { // i. Let thisValue be GetThisValue(ref). - Base::Value(_) => get_this_value(&reference), + Base::Value(_) => get_this_value(&reference).bind(gc.nogc()), // b. Else, Base::Environment(ref_env) => { // i. Let refEnv be ref.[[Base]]. @@ -1309,6 +1488,7 @@ impl<'a> Vm { ref_env .with_base_object(agent) .map_or(Value::Undefined, |object| object.into_value()) + .bind(gc.nogc()) } // ii. Assert: refEnv is an Environment Record. Base::Unresolvable => unreachable!(), @@ -1318,32 +1498,33 @@ impl<'a> Vm { // a. Let thisValue be undefined. Value::Undefined }; - let func = vm.stack.pop().unwrap(); - if cfg!(feature = "interleaved-gc") { - let mut vm = NonNull::from(vm); - agent.vm_stack.push(vm); - let result = call(agent, func, this_value, Some(ArgumentsList(&args)), gc); - let return_vm = agent.vm_stack.pop().unwrap(); - assert_eq!(vm, return_vm, "VM Stack was misused"); - // SAFETY: This is fairly bonkers-unsafe. I'm sorry. - unsafe { vm.as_mut() }.result = Some(result?); - } else { - vm.result = Some(call( - agent, - func, - this_value, - Some(ArgumentsList(&args)), - gc, - )?); - } + let args = vm.get_call_args(instr, gc.nogc()).unbind(); + let func = vm.stack.pop().unwrap().unbind(); + let this_value = this_value.unbind(); + let result = with_vm_gc( + agent, + vm, + |agent, gc| call(agent, func, this_value, Some(ArgumentsList(&args)), gc), + gc.reborrow(), + )?; + vm.result = Some(result.unbind()); } Instruction::EvaluateNew => { - let args = vm.get_call_args(instr); - let constructor = vm.stack.pop().unwrap(); + let args = vm.get_call_args(instr, gc.nogc()); + let constructor = vm.stack.pop().unwrap().bind(gc.nogc()); let Some(constructor) = is_constructor(agent, constructor) else { + let constructor_string = { + let constructor = constructor.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| constructor.string_repr(agent, gc), + gc.reborrow(), + ) + }; let error_message = format!( "'{}' is not a constructor.", - constructor.string_repr(agent, gc.reborrow()).as_str(agent) + constructor_string.as_str(agent) ); return Err(agent.throw_exception( ExceptionType::TypeError, @@ -1352,33 +1533,15 @@ impl<'a> Vm { )); }; - if cfg!(feature = "interleaved-gc") { - let mut vm = NonNull::from(vm); - agent.vm_stack.push(vm); - let result = construct( - agent, - constructor, - Some(ArgumentsList(&args)), - None, - gc.reborrow(), - ) - .map(|result| result.into_value()); - let return_vm = agent.vm_stack.pop().unwrap(); - assert_eq!(vm, return_vm, "VM Stack was misused"); - // SAFETY: This is fairly bonkers-unsafe. I'm sorry. - unsafe { vm.as_mut() }.result = Some(result?); - } else { - vm.result = Some( - construct( - agent, - constructor, - Some(ArgumentsList(&args)), - None, - gc.reborrow(), - )? - .into_value(), - ); - } + let constructor = constructor.unbind(); + let args = args.unbind(); + let result = with_vm_gc( + agent, + vm, + |agent, gc| construct(agent, constructor, Some(ArgumentsList(&args)), None, gc), + gc, + )?; + vm.result = Some(result.unbind().into_value()); } Instruction::EvaluateSuper => { let EnvironmentIndex::Function(this_env) = get_this_environment(agent) else { @@ -1390,21 +1553,30 @@ impl<'a> Vm { let (new_target, func) = { let data = &agent[this_env]; ( - Function::try_from(data.new_target.unwrap()).unwrap(), - data.function_object - .internal_get_prototype_of(agent, gc.reborrow()) - .unwrap(), + Function::try_from(data.new_target.unwrap()) + .unwrap() + .bind(gc.nogc()), + unwrap_try(data.function_object.try_get_prototype_of(agent, gc.nogc())), ) }; // 4. Let argList be ? ArgumentListEvaluation of Arguments. - let arg_list = vm.get_call_args(instr); + let arg_list = vm.get_call_args(instr, gc.nogc()); // 5. If IsConstructor(func) is false, throw a TypeError exception. let Some(func) = func.and_then(|func| is_constructor(agent, func)) else { - let error_message = format!( - "'{}' is not a constructor.", - func.map_or(Value::Null, |func| func.into_value()) - .string_repr(agent, gc.reborrow()) - .as_str(agent) + let constructor = func.map_or(Value::Null, |f| f.into_value().unbind()); + let error_message = with_vm_gc( + agent, + vm, + |agent, gc| { + format!( + "'{}' is not a constructor.", + constructor + .into_value() + .string_repr(agent, gc) + .as_str(agent) + ) + }, + gc.reborrow(), ); return Err(agent.throw_exception( ExceptionType::TypeError, @@ -1413,15 +1585,26 @@ impl<'a> Vm { )); }; // 6. Let result be ? Construct(func, argList, newTarget). - let result = construct( - agent, - func.unbind(), - Some(ArgumentsList(&arg_list)), - Some(new_target), - gc.reborrow(), - )? - .unbind() - .bind(gc.nogc()); + let result = { + let func = func.unbind(); + let arg_list = arg_list.unbind(); + let new_target = new_target.unbind(); + let result = with_vm_gc( + agent, + vm, + |agent, gc| { + construct( + agent, + func, + Some(ArgumentsList(&arg_list)), + Some(new_target), + gc, + ) + }, + gc.reborrow(), + )?; + result.unbind().bind(gc.nogc()) + }; // 7. Let thisER be GetThisEnvironment(). let EnvironmentIndex::Function(this_er) = get_this_environment(agent) else { unreachable!(); @@ -1435,11 +1618,10 @@ impl<'a> Vm { }; // 11. Perform ? InitializeInstanceElements(result, F). // 12. Return result. - vm.result = Some(result.into_value()); + vm.result = Some(result.into_value().unbind()); } Instruction::EvaluatePropertyAccessWithExpressionKey => { - let property_name_value = vm.result.take().unwrap(); - let base_value = vm.stack.pop().unwrap(); + let property_name_value = vm.result.take().unwrap().bind(gc.nogc()); let strict = agent .running_execution_context() @@ -1447,10 +1629,28 @@ impl<'a> Vm { .unwrap() .is_strict_mode; - let property_key = to_property_key(agent, property_name_value, gc.reborrow())?; + let property_key = + if property_name_value.is_string() || property_name_value.is_integer() { + unwrap_try(to_property_key_simple( + agent, + property_name_value, + gc.nogc(), + )) + } else { + let property_name_value = property_name_value.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| to_property_key(agent, property_name_value, gc), + gc.reborrow(), + )? + .unbind() + .bind(gc.nogc()) + }; + let base_value = vm.stack.pop().unwrap().bind(gc.nogc()); vm.reference = Some(Reference { - base: Base::Value(base_value), + base: Base::Value(base_value.unbind()), referenced_name: property_key.unbind(), strict, this_value: None, @@ -1459,7 +1659,7 @@ impl<'a> Vm { Instruction::EvaluatePropertyAccessWithIdentifierKey => { let property_name_string = executable.fetch_identifier(agent, instr.args[0].unwrap() as usize, gc.nogc()); - let base_value = vm.result.take().unwrap(); + let base_value = vm.result.take().unwrap().bind(gc.nogc()); let strict = agent .running_execution_context() .ecmascript_code @@ -1467,7 +1667,7 @@ impl<'a> Vm { .is_strict_mode; vm.reference = Some(Reference { - base: Base::Value(base_value), + base: Base::Value(base_value.unbind()), referenced_name: property_name_string.unbind().into(), strict, this_value: None, @@ -1495,62 +1695,94 @@ impl<'a> Vm { } } Instruction::Increment => { - let lhs = vm.result.take().unwrap(); - let old_value = to_numeric(agent, lhs, gc.reborrow())?; + let lhs = vm.result.take().unwrap().bind(gc.nogc()); + // Note: This is done by the previous instruction. + let old_value = Numeric::try_from(lhs).unwrap(); let new_value = if let Ok(old_value) = Number::try_from(old_value) { - Number::add(agent, old_value, 1.into()) + Number::add(agent, old_value, 1.into()).into_value() } else { - todo!(); - // let old_value = BigInt::try_from(old_value).unwrap(); - // BigInt::add(agent, old_value, 1.into()); + let old_value = BigInt::try_from(old_value).unwrap(); + BigInt::add(agent, old_value, 1.into()).into_value() }; - vm.result = Some(new_value.into_value()); + vm.result = Some(new_value.unbind()) } Instruction::Decrement => { let lhs = vm.result.take().unwrap(); - let old_value = to_numeric(agent, lhs, gc.reborrow())?; + // Note: This is done by the previous instruction. + let old_value = Numeric::try_from(lhs).unwrap(); let new_value = if let Ok(old_value) = Number::try_from(old_value) { - Number::subtract(agent, old_value, 1.into()) + Number::subtract(agent, old_value, 1.into()).into_value() } else { - todo!(); - // let old_value = BigInt::try_from(old_value).unwrap(); - // BigInt::subtract(agent, old_value, 1.into()); + let old_value = BigInt::try_from(old_value).unwrap(); + BigInt::subtract(agent, old_value, 1.into()).into_value() }; - vm.result = Some(new_value.into_value()); + vm.result = Some(new_value.unbind()); } Instruction::LessThan => { let lval = vm.stack.pop().unwrap(); let rval = vm.result.take().unwrap(); - let result = is_less_than::(agent, lval, rval, gc)? == Some(true); + let result = with_vm_gc( + agent, + vm, + |agent, gc| is_less_than::(agent, lval, rval, gc), + gc, + )?; + let result = result == Some(true); vm.result = Some(result.into()); } Instruction::LessThanEquals => { let lval = vm.stack.pop().unwrap(); let rval = vm.result.take().unwrap(); - let result = is_less_than::(agent, rval, lval, gc)? == Some(false); + let result = with_vm_gc( + agent, + vm, + |agent, gc| is_less_than::(agent, rval, lval, gc), + gc, + )?; + let result = result == Some(false); vm.result = Some(result.into()); } Instruction::GreaterThan => { let lval = vm.stack.pop().unwrap(); let rval = vm.result.take().unwrap(); - let result = is_less_than::(agent, rval, lval, gc)? == Some(true); + let result = with_vm_gc( + agent, + vm, + |agent, gc| is_less_than::(agent, rval, lval, gc), + gc, + )?; + let result = result == Some(true); vm.result = Some(result.into()); } Instruction::GreaterThanEquals => { let lval = vm.stack.pop().unwrap(); let rval = vm.result.take().unwrap(); - let result = is_less_than::(agent, lval, rval, gc)? == Some(false); + let result = with_vm_gc( + agent, + vm, + |agent, gc| is_less_than::(agent, lval, rval, gc), + gc, + )?; + let result = result == Some(false); vm.result = Some(result.into()); } Instruction::HasProperty => { - let lval = vm.stack.pop().unwrap(); - let rval = vm.result.take().unwrap(); + let lval = vm.stack.pop().unwrap().bind(gc.nogc()); + let rval = vm.result.take().unwrap().bind(gc.nogc()); // RelationalExpression : RelationalExpression in ShiftExpression // 5. If rval is not an Object, throw a TypeError exception. - let Ok(rval) = Object::try_from(rval) else { - let error_message = format!( - "The right-hand side of an `in` expression must be an object, got '{}'.", - rval.string_repr(agent, gc.reborrow()).as_str(agent) + let Ok(mut rval) = Object::try_from(rval) else { + let rval = rval.unbind(); + let error_message = with_vm_gc( + agent, + vm, + |agent, gc| { + format!( + "The right-hand side of an `in` expression must be an object, got '{}'.", + rval.string_repr(agent, gc).as_str(agent) + ) + }, + gc.reborrow(), ); return Err(agent.throw_exception( ExceptionType::TypeError, @@ -1559,15 +1791,37 @@ impl<'a> Vm { )); }; // 6. Return ? HasProperty(rval, ? ToPropertyKey(lval)). - let property_key = to_property_key(agent, lval.unbind(), gc.reborrow())?; - vm.result = { - Some(Value::Boolean(has_property( + let property_key = if lval.is_string() || lval.is_integer() { + unwrap_try(to_property_key_simple(agent, lval, gc.nogc())) + } else { + let scoped_rval = rval.scope(agent, gc.nogc()); + let lval = lval.unbind(); + let property_key = with_vm_gc( agent, - rval, - property_key.unbind(), + vm, + |agent, gc| to_property_key(agent, lval, gc), gc.reborrow(), - )?)) + )? + .unbind() + .bind(gc.nogc()); + rval = scoped_rval.get(agent).bind(gc.nogc()); + property_key + }; + let result = if let TryResult::Continue(result) = + try_has_property(agent, rval, property_key, gc.nogc()) + { + result + } else { + let rval = rval.unbind(); + let property_key = property_key.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| has_property(agent, rval, property_key, gc), + gc, + )? }; + vm.result = Some(result.into()); } Instruction::IsStrictlyEqual => { let lval = vm.stack.pop().unwrap(); @@ -1578,7 +1832,12 @@ impl<'a> Vm { Instruction::IsLooselyEqual => { let lval = vm.stack.pop().unwrap(); let rval = vm.result.take().unwrap(); - let result = is_loosely_equal(agent, lval, rval, gc.reborrow())?; + let result = with_vm_gc( + agent, + vm, + |agent, gc| is_loosely_equal(agent, lval, rval, gc), + gc, + )?; vm.result = Some(result.into()); } Instruction::IsNullOrUndefined => { @@ -1621,7 +1880,9 @@ impl<'a> Vm { Instruction::InitializeReferencedBinding => { let v = vm.reference.take().unwrap(); let w = vm.result.take().unwrap(); - initialize_referenced_binding(agent, v, w, gc.reborrow())?; + // Note: https://tc39.es/ecma262/#sec-initializereferencedbinding + // suggests this cannot call user code, hence NoGC. + unwrap_try(try_initialize_referenced_binding(agent, v, w, gc.nogc()))?; } Instruction::InitializeVariableEnvironment => { let num_variables = instr.args[0].unwrap(); @@ -1743,9 +2004,13 @@ impl<'a> Vm { let name = executable.fetch_identifier(agent, instr.args[0].unwrap() as usize, gc.nogc()); - lex_env - .create_mutable_binding(agent, name.unbind(), false, gc.reborrow()) - .unwrap(); + unwrap_try(lex_env.try_create_mutable_binding( + agent, + name.unbind(), + false, + gc.nogc(), + )) + .unwrap(); } Instruction::CreateImmutableBinding => { let lex_env = agent @@ -1789,17 +2054,13 @@ impl<'a> Vm { Instruction::InstanceofOperator => { let lval = vm.stack.pop().unwrap(); let rval = vm.result.take().unwrap(); - if cfg!(feature = "interleaved-gc") { - let mut vm = NonNull::from(vm); - agent.vm_stack.push(vm); - let result = instanceof_operator(agent, lval, rval, gc); - let return_vm = agent.vm_stack.pop().unwrap(); - assert_eq!(vm, return_vm, "VM Stack was misused"); - // SAFETY: This is fairly bonkers-unsafe. I'm sorry. - unsafe { vm.as_mut() }.result = Some(result?.into()); - } else { - vm.result = Some(instanceof_operator(agent, lval, rval, gc)?.into()); - } + let result = with_vm_gc( + agent, + vm, + |agent, gc| instanceof_operator(agent, lval, rval, gc), + gc, + )?; + vm.result = Some(result.into()); } Instruction::BeginSimpleArrayBindingPattern => { let lexical = instr.args[1].unwrap() == 1; @@ -1857,26 +2118,67 @@ impl<'a> Vm { unreachable!("BeginArrayBindingPattern should take care of stepping over these"); } Instruction::StringConcat => { - let argument_count = instr.args[0].unwrap(); - let last_item = vm.stack.len() - argument_count as usize; + let argument_count = instr.args[0].unwrap() as usize; + let first_arg_index = vm.stack.len() - argument_count; let mut length = 0; - for ele in vm.stack[last_item..].iter_mut() { - if !ele.is_string() { - *ele = to_string(agent, *ele, gc.reborrow())?.into_value(); + let all_easy = vm.stack[first_arg_index..] + .iter() + .all(|ele| ele.is_primitive() && !ele.is_symbol()); + let string = if all_easy { + let gc = gc.nogc(); + let args = &mut vm.stack[first_arg_index..]; + for arg in args.iter_mut() { + let string: String<'_> = + to_string_primitive(agent, Primitive::try_from(*arg).unwrap(), gc) + .unwrap(); + length += string.len(agent); + // Note: We write String into each arg. + *arg = string.into_value().unbind(); } - let string = String::try_from(*ele).unwrap(); - length += string.len(agent); - } - let mut result_string = std::string::String::with_capacity(length); - for ele in vm.stack[last_item..].iter() { - let string = String::try_from(*ele).unwrap(); - result_string.push_str(string.as_str(agent)); - } - vm.stack.truncate(last_item); - vm.result = Some(String::from_string(agent, result_string, gc.nogc()).into_value()); + let args = &*args; + // SAFETY: String is a sub-enum of Value and we've written + // a String into each of the args. + let args = unsafe { std::mem::transmute::<&[Value<'_>], &[String<'_>]>(args) }; + concat_string_from_slice(agent, args, length, gc) + } else { + let mut args = vm + .stack + .split_off(first_arg_index) + .iter_mut() + .map(|v| v.scope(agent, gc.nogc())) + .collect::>(); + with_vm_gc( + agent, + vm, + |agent, mut gc| { + for ele in args.iter_mut() { + let maybe_string = ele.get(agent).bind(gc.nogc()); + if maybe_string.is_string() { + continue; + } + let string = + to_string(agent, maybe_string.unbind(), gc.reborrow())?; + length += string.len(agent); + let string = string.into_value(); + // SAFETY: args are never shared + unsafe { ele.replace(agent, string.unbind()) }; + } + Ok(()) + }, + gc.reborrow(), + )?; + let gc = gc.nogc(); + let args = args + .into_iter() + .map(|v| String::try_from(v.get(agent)).unwrap().bind(gc)) + .collect::>(); + concat_string_from_slice(agent, &args, length, gc) + }; + vm.stack.truncate(first_arg_index); + vm.result = Some(string.into_value().unbind()); } Instruction::Delete => { - let refer = vm.reference.take().unwrap(); + let refer = vm.reference.take().unwrap().bind(gc.nogc()); match refer.base { // 3. If IsUnresolvableReference(ref) is true, then Base::Unresolvable => { @@ -1903,13 +2205,23 @@ impl<'a> Vm { // TODO: Is this relevant? // i. Set ref.[[ReferencedName]] to ? ToPropertyKey(ref.[[ReferencedName]]). // e. Let deleteStatus be ? baseObj.[[Delete]](ref.[[ReferencedName]]). - let delete_status = base_obj.unbind().internal_delete( - agent, - refer.referenced_name, - gc.reborrow(), - )?; + let strict = refer.strict; + let delete_status = if let TryResult::Continue(delete_status) = + base_obj.try_delete(agent, refer.referenced_name, gc.nogc()) + { + delete_status + } else { + let base_obj = base_obj.unbind(); + let referenced_name = refer.referenced_name.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| base_obj.internal_delete(agent, referenced_name, gc), + gc.reborrow(), + )? + }; // f. If deleteStatus is false and ref.[[Strict]] is true, throw a TypeError exception. - if !delete_status && refer.strict { + if !delete_status && strict { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Cannot delete property", @@ -1929,7 +2241,20 @@ impl<'a> Vm { _ => unreachable!(), }; // c. Return ? base.DeleteBinding(ref.[[ReferencedName]]). - vm.result = Some(base.delete_binding(agent, referenced_name, gc)?.into()); + let result = if let TryResult::Continue(result) = + base.try_delete_binding(agent, referenced_name, gc.nogc()) + { + result? + } else { + let referenced_name = referenced_name.unbind(); + with_vm_gc( + agent, + vm, + |agent, gc| base.delete_binding(agent, referenced_name, gc), + gc, + )? + }; + vm.result = Some(result.into()); } } @@ -1959,44 +2284,52 @@ impl<'a> Vm { } Instruction::GetIteratorSync => { let expr_value = vm.result.take().unwrap(); - vm.iterator_stack - .push(VmIterator::from_value(agent, expr_value, gc.reborrow())?.unbind()); + let result = with_vm_gc( + agent, + vm, + |agent, gc| VmIterator::from_value(agent, expr_value, gc), + gc, + )?; + vm.iterator_stack.push(result); } Instruction::GetIteratorAsync => { todo!(); } Instruction::IteratorStepValue => { - let result = vm - .iterator_stack - .last_mut() - .unwrap() - // TODO: Handle potential GC. - .step_value(agent, gc.reborrow()); + let mut iterator = vm.iterator_stack.pop().unwrap(); + let result = with_vm_gc(agent, vm, |agent, gc| iterator.step_value(agent, gc), gc); if let Ok(result) = result { - vm.result = result; + vm.result = result.map(Value::unbind); if result.is_none() { - // Iterator finished: Jump to escape iterator loop. - vm.iterator_stack.pop().unwrap(); + // Iterator finished: No need to return the iterator + // onto the stack. Jump to escape the iterator loop. vm.ip = instr.args[0].unwrap() as usize; + } else { + // Return our iterator into the iterator stack. + vm.iterator_stack.push(iterator); } } else { - vm.iterator_stack.pop(); + // No need to return the iterator into the stack. result?; } } Instruction::IteratorStepValueOrUndefined => { - // TODO: Handle potential GC. - let iterator = vm.iterator_stack.last_mut().unwrap(); - let result = iterator.step_value(agent, gc.reborrow()); + let mut iterator = vm.iterator_stack.pop().unwrap(); + let result = with_vm_gc(agent, vm, |agent, gc| iterator.step_value(agent, gc), gc); if let Ok(result) = result { - vm.result = Some(result.unwrap_or(Value::Undefined)); + vm.result = Some(result.unwrap_or(Value::Undefined).unbind()); if result.is_none() { - // We have exhausted the iterator; replace it with an empty VmIterator so - // further instructions aren't observable. - *iterator = VmIterator::SliceIterator(SendableRef::new(&[])); + // We have exhausted the iterator; push in an empty + // VmIterator so further instructions aren't + // observable. + vm.iterator_stack + .push(VmIterator::SliceIterator(SendableRef::new(&[]))); + } else { + // Return our iterator into the iterator stack. + vm.iterator_stack.push(iterator); } } else { - vm.iterator_stack.pop(); + // No need to return the iterator into the stack. result?; } } @@ -2006,27 +2339,36 @@ impl<'a> Vm { let array = array_create(agent, 0, capacity, None, gc.nogc())?.scope(agent, gc.nogc()); - let mut idx: u32 = 0; - while let Some(value) = iterator.step_value(agent, gc.reborrow())? { - let key = PropertyKey::Integer(idx.into()); - unwrap_try(try_create_data_property( - agent, - array.get(agent), - key, - value, - gc.nogc(), - )); - idx += 1; - } + with_vm_gc( + agent, + vm, + |agent, mut gc| { + let mut idx: u32 = 0; + while let Some(value) = iterator.step_value(agent, gc.reborrow())? { + let key = PropertyKey::Integer(idx.into()); + unwrap_try(try_create_data_property( + agent, + array.get(agent), + key, + value.unbind(), + gc.nogc(), + )); + idx += 1; + } + Ok(()) + }, + gc, + )?; vm.result = Some(array.get(agent).into_value()); } Instruction::IteratorClose => { let iterator = vm.iterator_stack.pop().unwrap(); if let VmIterator::GenericIterator(iterator_record) = iterator { - iterator_close( + let result = vm.result.take().unwrap_or(Value::Undefined); + with_vm_gc( agent, - &iterator_record, - Ok(vm.result.take().unwrap_or(Value::Undefined)), + vm, + |agent, gc| iterator_close(agent, &iterator_record, Ok(result), gc), gc, )?; } @@ -2037,7 +2379,9 @@ impl<'a> Vm { unreachable!() }; vm.result = Some( - create_unmapped_arguments_object(agent, slice.get(), gc.nogc()).into_value(), + create_unmapped_arguments_object(agent, slice.get(), gc.nogc()) + .into_value() + .unbind(), ); } other => todo!("{other:?}"), @@ -2046,7 +2390,7 @@ impl<'a> Vm { Ok(ContinuationKind::Normal) } - fn get_call_args(&mut self, instr: &Instr) -> Vec { + fn get_call_args<'gc>(&mut self, instr: &Instr, _gc: NoGcScope<'gc, '_>) -> Vec> { let instr_arg0 = instr.args[0].unwrap(); let arg_count = if instr_arg0 != IndexType::MAX { instr_arg0 as usize @@ -2063,6 +2407,19 @@ impl<'a> Vm { } } +fn concat_string_from_slice<'gc>( + agent: &mut Agent, + slice: &[String], + string_length: usize, + gc: NoGcScope<'gc, '_>, +) -> String<'gc> { + let mut result_string = std::string::String::with_capacity(string_length); + for string in slice.iter() { + result_string.push_str(string.as_str(agent)); + } + String::from_string(agent, result_string, gc) +} + /// ### [13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval )](https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator) /// /// The abstract operation ApplyStringOrNumericBinaryOperator takes @@ -2071,41 +2428,45 @@ impl<'a> Vm { /// returns either a normal completion containing either a String, a BigInt, /// or a Number, or a throw completion. #[inline] -fn apply_string_or_numeric_binary_operator( +fn apply_string_or_numeric_binary_operator<'gc>( agent: &mut Agent, lval: Value, op_text: BinaryOperator, rval: Value, - mut gc: GcScope, -) -> JsResult { - let mut lnum: Numeric; - let rnum: Numeric; + mut gc: GcScope<'gc, '_>, +) -> JsResult> { + let lval = lval.bind(gc.nogc()); + let rval = rval.bind(gc.nogc()); + let lnum: Numeric<'gc>; + let rnum: Numeric<'gc>; // 1. If opText is +, then let gc = if op_text == BinaryOperator::Addition { // a. Let lprim be ? ToPrimitive(lval). // b. Let rprim be ? ToPrimitive(rval). let (lprim, rprim, gc) = match (Primitive::try_from(lval), Primitive::try_from(rval)) { (Ok(lprim), Ok(rprim)) => { + let lprim = lprim.unbind(); + let rprim = rprim.unbind(); let gc = gc.into_nogc(); (lprim.bind(gc), rprim.bind(gc), gc) } (Ok(lprim), Err(_)) => { let lprim = lprim.scope(agent, gc.nogc()); - let rprim = to_primitive(agent, rval, None, gc.reborrow())?.unbind(); + let rprim = to_primitive(agent, rval.unbind(), None, gc.reborrow())?.unbind(); let gc = gc.into_nogc(); let lprim = lprim.get(agent); (lprim.bind(gc), rprim.bind(gc), gc) } (Err(_), Ok(rprim)) => { let rprim = rprim.scope(agent, gc.nogc()); - let lprim = to_primitive(agent, lval, None, gc.reborrow())?.unbind(); + let lprim = to_primitive(agent, lval.unbind(), None, gc.reborrow())?.unbind(); let gc = gc.into_nogc(); let rprim = rprim.get(agent); (lprim.bind(gc), rprim.bind(gc), gc) } (Err(_), Err(_)) => { let rval = rval.scope(agent, gc.nogc()); - let lprim = to_primitive(agent, lval, None, gc.reborrow())? + let lprim = to_primitive(agent, lval.unbind(), None, gc.reborrow())? .unbind() .scope(agent, gc.nogc()); let rprim = to_primitive(agent, rval.get(agent), None, gc.reborrow())?.unbind(); @@ -2149,13 +2510,11 @@ fn apply_string_or_numeric_binary_operator( } else { let rval = rval.scope(agent, gc.nogc()); // 3. Let lnum be ? ToNumeric(lval). - lnum = to_numeric(agent, lval, gc.reborrow())? + let scoped_lnum = to_numeric(agent, lval.unbind(), gc.reborrow())? .unbind() - .bind(gc.nogc()); - let scoped_lnum = lnum.scope(agent, gc.nogc()); - let rval = rval.get(agent).unbind(); + .scope(agent, gc.nogc()); // 4. Let rnum be ? ToNumeric(rval). - rnum = to_numeric(agent, rval, gc.reborrow())?.unbind(); + rnum = to_numeric(agent, rval.get(agent), gc.reborrow())?.unbind(); let gc = gc.into_nogc(); lnum = scoped_lnum.get(agent).bind(gc); gc @@ -2245,7 +2604,7 @@ fn apply_string_or_numeric_binary_operator( /// ### [13.5.3 The typeof operator](https://tc39.es/ecma262/#sec-typeof-operator) #[inline] -fn typeof_operator(_: &mut Agent, val: Value) -> String { +fn typeof_operator(_: &mut Agent, val: Value) -> String<'static> { match val { // 4. If val is undefined, return "undefined". Value::Undefined => BUILTIN_STRING_MEMORY.undefined, @@ -2340,10 +2699,10 @@ fn typeof_operator(_: &mut Agent, val: Value) -> String { /// > that did not use a @@hasInstance method to define the instanceof operator /// > semantics. If an object does not define or inherit @@hasInstance it uses /// > the default instanceof semantics. -pub(crate) fn instanceof_operator( +pub(crate) fn instanceof_operator<'a>( agent: &mut Agent, - value: impl IntoValue, - target: impl IntoValue, + value: impl IntoValue<'a>, + target: impl IntoValue<'a>, mut gc: GcScope, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. @@ -2371,7 +2730,7 @@ pub(crate) fn instanceof_operator( agent, inst_of_handler.unbind(), target.into_value(), - Some(ArgumentsList(&[value.into_value()])), + Some(ArgumentsList(&[value.into_value().unbind()])), gc.reborrow(), )?; Ok(to_boolean(agent, result)) @@ -2392,6 +2751,24 @@ pub(crate) fn instanceof_operator( } } +fn with_vm_gc<'a, 'b, R: 'a>( + agent: &mut Agent, + vm: &mut Vm, + work: impl FnOnce(&mut Agent, GcScope<'a, 'b>) -> R, + gc: GcScope<'a, 'b>, +) -> R { + if cfg!(feature = "interleaved-gc") { + let vm = NonNull::from(vm); + agent.vm_stack.push(vm); + let result = work(agent, gc); + let return_vm = agent.vm_stack.pop().unwrap(); + assert_eq!(vm, return_vm, "VM Stack was misused"); + result + } else { + work(agent, gc) + } +} + impl HeapMarkAndSweep for ExceptionJumpTarget { fn mark_values(&self, queues: &mut WorkQueues) { let Self { diff --git a/nova_vm/src/engine/bytecode/vm/binding_methods.rs b/nova_vm/src/engine/bytecode/vm/binding_methods.rs index 2ce0dd43e..ded527d41 100644 --- a/nova_vm/src/engine/bytecode/vm/binding_methods.rs +++ b/nova_vm/src/engine/bytecode/vm/binding_methods.rs @@ -17,7 +17,7 @@ use crate::{ try_create_data_property_or_throw, EnvironmentIndex, Executable, Instruction, Vm, VmIterator, }, - context::GcScope, + context::{Bindable, GcScope}, unwrap_try, }, }; @@ -46,7 +46,7 @@ pub(super) fn execute_simple_array_binding( if instr.kind == Instruction::BindingPatternSkip { continue; } - result.unwrap_or(Value::Undefined) + result.unwrap_or(Value::Undefined).unbind().bind(gc.nogc()) } Instruction::BindingPatternBindRest | Instruction::BindingPatternGetRestValue => { break_after_bind = true; @@ -65,7 +65,7 @@ pub(super) fn execute_simple_array_binding( agent, rest.get(agent), PropertyKey::from(idx), - result, + result.unbind(), gc.nogc(), )) .unwrap(); @@ -84,15 +84,17 @@ pub(super) fn execute_simple_array_binding( Instruction::BindingPatternBind | Instruction::BindingPatternBindRest => { let binding_id = executable.fetch_identifier(agent, instr.args[0].unwrap() as usize, gc.nogc()); - let lhs = { - resolve_binding(agent, binding_id.unbind(), environment, gc.reborrow())? - .unbind() - .bind(gc.nogc()) - }; + let value = value.scope(agent, gc.nogc()); + let lhs = resolve_binding(agent, binding_id.unbind(), environment, gc.reborrow())?; if environment.is_none() { - put_value(agent, &lhs.unbind(), value, gc.reborrow())?; + put_value(agent, &lhs.unbind(), value.get(agent), gc.reborrow())?; } else { - initialize_referenced_binding(agent, lhs.unbind(), value, gc.reborrow())?; + initialize_referenced_binding( + agent, + lhs.unbind(), + value.get(agent), + gc.reborrow(), + )?; } } Instruction::BindingPatternGetValue | Instruction::BindingPatternGetRestValue => { @@ -100,7 +102,7 @@ pub(super) fn execute_simple_array_binding( agent, vm, executable, - value, + value.unbind(), environment, gc.reborrow(), )?; @@ -146,8 +148,11 @@ pub(super) fn execute_simple_object_binding( let property_key = if instr.kind == Instruction::BindingPatternBind { binding_id.into() } else { - let key_value = - executable.fetch_constant(agent, instr.args[1].unwrap() as usize); + let key_value = executable.fetch_constant( + agent, + instr.args[1].unwrap() as usize, + gc.nogc(), + ); // SAFETY: It should be impossible for binding pattern // names to be integer strings. unsafe { PropertyKey::from_value_unchecked(key_value) } @@ -166,9 +171,9 @@ pub(super) fn execute_simple_object_binding( gc.reborrow(), )?; if environment.is_none() { - put_value(agent, &lhs, v, gc.reborrow())?; + put_value(agent, &lhs, v.unbind(), gc.reborrow())?; } else { - initialize_referenced_binding(agent, lhs, v, gc.reborrow())?; + initialize_referenced_binding(agent, lhs, v.unbind(), gc.reborrow())?; } } Instruction::BindingPatternGetValueNamed => { @@ -176,9 +181,11 @@ pub(super) fn execute_simple_object_binding( // which checks for integer-ness, and then converted to Value // without conversion, or is a floating point number string. let property_key = unsafe { - PropertyKey::from_value_unchecked( - executable.fetch_constant(agent, instr.args[0].unwrap() as usize), - ) + PropertyKey::from_value_unchecked(executable.fetch_constant( + agent, + instr.args[0].unwrap() as usize, + gc.nogc(), + )) }; excluded_names.insert(property_key.unbind()); @@ -192,7 +199,7 @@ pub(super) fn execute_simple_object_binding( agent, vm, executable, - v, + v.unbind(), environment, gc.reborrow(), )?; @@ -201,10 +208,9 @@ pub(super) fn execute_simple_object_binding( // 1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment). let binding_id = executable.fetch_identifier(agent, instr.args[0].unwrap() as usize, gc.nogc()); - let lhs = { - resolve_binding(agent, binding_id.unbind(), environment, gc.reborrow())? - .unbind() - }; + // TODO: Properly handle potential GC. + let lhs = resolve_binding(agent, binding_id.unbind(), environment, gc.reborrow())? + .unbind(); // 2. Let restObj be OrdinaryObjectCreate(%Object.prototype%). // 3. Perform ? CopyDataProperties(restObj, value, excludedNames). let rest_obj = copy_data_properties_into_object( @@ -217,9 +223,9 @@ pub(super) fn execute_simple_object_binding( // 4. If environment is undefined, return ? PutValue(lhs, restObj). // 5. Return ? InitializeReferencedBinding(lhs, restObj). if environment.is_none() { - put_value(agent, &lhs, rest_obj, gc.reborrow())?; + put_value(agent, &lhs, rest_obj.unbind(), gc.reborrow())?; } else { - initialize_referenced_binding(agent, lhs, rest_obj, gc.reborrow())?; + initialize_referenced_binding(agent, lhs, rest_obj.unbind(), gc.reborrow())?; } break; } diff --git a/nova_vm/src/engine/context.rs b/nova_vm/src/engine/context.rs index 8ef1cbb01..b6247f916 100644 --- a/nova_vm/src/engine/context.rs +++ b/nova_vm/src/engine/context.rs @@ -162,3 +162,309 @@ impl<'a, 'b> NoGcScope<'a, 'b> { } } } + +/// Method for binding and unbinding garbage collectable values from the +/// garbage collector lifetime. This is a necessary evil for calling and +/// entering functions that contain garbage collector safepoints. +/// +/// ## Why is this needed? +/// +/// From the borrow checker's point of view, bindable values all alias the +/// "garbage collector reference" contained in `GcScope`. Any function that +/// can trigger garbage collection takes an exclusive garbage collector +/// reference, which then means that passing any bound values would be an +/// aliasing violation. The borrow checker will not allow that and a compile +/// error results. To allow the call to compile, the bindable values must be +/// unbound at the call site. +/// +/// Inside the function, the bindable parameter values initially are unbound +/// from the garbage collector lifetime. This means that the borrow checker +/// will not check their usage for use-after-free. To make the borrow checker +/// check them, the values must be bound using the `bind` function. +/// +/// ## Safety +/// +/// The implementations for both functions must be equivalent to a `memcpy` or, +/// for collections of bindable values, a new collection of bindable values +/// recursively mapped. The end result should be entirely equal to a lifetime +/// transmute on performed on Self. The implementation of the functions +/// themselves are also allowed to be a plain transmute. +/// +/// ```rust,compile_fail +/// let result = unsafe { core::mem::transmute::, Value<'b>(value) }; +/// ``` +pub unsafe trait Bindable: Sized { + /// Bound representation of self. This must always be effectively equal to + /// `Self<'a>`. Note that we cannot write `Self<'a>` directly because + /// `Self` cannot have lifetime parameters attached to it. + /// + /// ## Safety + /// + /// This type is the most important part of this trait. It _must be_ + /// correctly set to be effectively equal to `Self<'a>`. + /// + /// ## Examples + /// + /// This is the only correct way to define the type: + /// + /// ```rust + /// use nova_vm::engine::context::{Bindable, NoGcScope}; + /// struct MyType<'a>(std::marker::PhantomData<&'a ()>); + /// unsafe impl Bindable for MyType<'_> { + /// type Of<'a> = MyType<'a>; + /// + /// #[inline(always)] + /// fn unbind(self) -> Self::Of<'static> { + /// unsafe { core::mem::transmute::>(self) } + /// } + /// + /// #[inline(always)] + /// fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + /// unsafe { core::mem::transmute::>(self) } + /// } + /// } + /// ``` + type Of<'a>; + + /// Unbind this value from the garbage collector lifetime. This is + /// necessary for eg. when using the value as a parameter in a call that + /// can perform garbage collection. + /// + /// This function's implementation must be equivalent to a (recursive) + /// `memcpy`. The intention is that the entire function optimises to + /// nothing in the final binary. + /// + /// ## Safety + /// + /// This function is conceptually should only be used for one of the following actions: + /// + /// 1. Unbind a value to allow passing it as a parameter. + /// 2. Unbind a value to allow returning as a result, though this should be + /// avoided if possible. + /// 3. Temporarily unbind a value to allow turning a `GcScope` into a + /// `NoGcScope`, and immediately rebind it with the `NoGcScope`. + /// + /// ## Examples + /// + /// ```rust,ignore + /// // Unbind a value to allow passing it as a parameter. + /// function_call(agent, value.unbind(), gc.reborrow()); + /// ``` + /// + /// ```rust,ignore + /// // Unbind a value to allow returning as a result. + /// let result = function_call(agent, gc.reborrow()); + /// if cond { + /// // Note: `result` is bound to a local temporary created in + /// // `gc.reborrow()`, which is why this will not work without unbind. + /// return Ok(result.unbind()); + /// } + /// ``` + /// + /// ```rust,ignore + /// // Unbind a value temporarily to immediately rebind it with a + /// // `NoGcScope`. + /// let result = function_call(agent, gc.reborrow()).unbind(); + /// let gc = gc.into_nogc(); + /// let result = result.bind(gc); + /// ``` + /// + /// *Incrrect* usage of this function: unbind a value into a variable + /// without immediate rebinding. + /// ```rust,ignore + /// let result = try_function_call(agent, gc.nogc()).unbind(); + /// function_call(agent, result, gc.reborrow()); + /// // Note: `result` is use-after-free because of above `gc.reborrow()`. + /// return Ok(result); + /// ``` + fn unbind(self) -> Self::Of<'static>; + + /// Bind this value to the garbage collector lifetime. This is necessary to + /// enable the borrow checker to check that bindable values are not + /// use-after-free. + /// + /// + /// + /// This function's implementation must be equivalent to a (recursive) + /// `memcpy`. The intention is that the entire function optimises to + /// nothing in the final binary. + /// + /// ## Safety + /// + /// This function is always safe to use. It is required to call it in the + /// following places: + /// + /// 1. Bind every bindable argument when a function with a garbage + /// collector safepoint is entered. + /// 2. Bind a bindable value when it is copied from the engine heap. + /// + /// ## Examples + /// + /// ```rust + /// use nova_vm::ecmascript::builtins::ArgumentsList; + /// use nova_vm::ecmascript::execution::{Agent, JsResult}; + /// use nova_vm::ecmascript::types::Value; + /// use nova_vm::engine::context::{GcScope, Bindable}; + /// fn function_call<'gc>( + /// agent: &mut Agent, + /// this_value: Value, + /// arguments: ArgumentsList, + /// gc: GcScope<'gc, '_> + /// ) -> Value<'gc> { + /// // Bind every bindable argument when a function with a garbage + /// // collector safepoint is entered. + /// // Note: Because this function takes `GcScope`, it should contain a + /// // safepoint. + /// let nogc = gc.nogc(); + /// let this_value = this_value.bind(nogc); + /// let arg0 = arguments.get(0).bind(nogc); + /// // ... + /// Value::Undefined + /// } + /// ``` + /// + /// ```rust,ignore + /// // Bind a bindable value when it is copied from the engine heap. + /// let first = agent[array].as_slice()[0].bind(gc.nogc()); + /// ``` + /// + /// *Incorrect* usage of this function: skip binding arguments when a + /// function with a garbage collector safepoint is entered. + /// ```rust + /// use nova_vm::ecmascript::builtins::ArgumentsList; + /// use nova_vm::ecmascript::execution::{Agent, JsResult}; + /// use nova_vm::ecmascript::types::Value; + /// use nova_vm::engine::context::{GcScope, Bindable}; + /// fn function_call<'gc>( + /// agent: &mut Agent, + /// this_value: Value, + /// arguments: ArgumentsList, + /// mut gc: GcScope<'gc, '_> + /// ) -> Value<'gc> { + /// // Note: This is still technically fine due to no preceding `GcScope` + /// // usage. + /// let string = this_value.to_string(agent, gc.reborrow()); + /// // Note: `arguments` is use-after-free because of above + /// // `gc.reborrow()`. + /// let value = arguments.get(0).bind(gc.nogc()); + /// // ... + /// Value::Undefined + /// } + /// ``` + fn bind<'a>(self, gc: NoGcScope<'a, '_>) -> Self::Of<'a>; +} + +// SAFETY: Trivially safe. +unsafe impl Bindable for () { + type Of<'a> = (); + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> {} + + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> {} +} + +// SAFETY: The blanket impls are safe if the implementors are. +unsafe impl Bindable for Option { + type Of<'a> = Option>; + + // Note: Option is simple enough to always inline the code. + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + const { + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + self.map(T::unbind) + } + + #[inline(always)] + fn bind<'a>(self, gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + const { + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + self.map(|t| t.bind(gc)) + } +} + +// SAFETY: The blanket impls are safe if the implementors are. +unsafe impl Bindable for Result { + type Of<'a> = Result, E::Of<'a>>; + + fn unbind(self) -> Self::Of<'static> { + const { + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + self.map(T::unbind).map_err(E::unbind) + } + + fn bind<'a>(self, gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + const { + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + self.map(|t| t.bind(gc)).map_err(|e| e.bind(gc)) + } +} + +// SAFETY: The blanket impls are safe if the implementors are. +unsafe impl Bindable for Vec { + type Of<'a> = Vec>; + + fn unbind(self) -> Self::Of<'static> { + const { + // Note: These checks do not guarantee that the Vec transmute is + // truly safe: Vec is free to rearrange its fields if its type + // parameter changes. These checks will only catch flagrant misuse. + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + // SAFETY: We assume that T properly implements Bindable. In that case + // we can safely transmute the lifetime out of the T's in the Vec. + unsafe { core::mem::transmute::, Vec>>(self) } + } + + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + const { + // Note: These checks do not guarantee that the Vec transmute is + // truly safe: Vec is free to rearrange its fields if its type + // parameter changes. These checks will only catch flagrant misuse. + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + // SAFETY: We assume that T properly implements Bindable. In that case + // we can safely transmute the lifetime out of the T's in the Vec. + unsafe { core::mem::transmute::, Vec>>(self) } + } +} + +// SAFETY: The blanket impls are safe if the implementors are. +unsafe impl<'slice, T: Bindable> Bindable for &'slice [T] +where + for<'gc> ::Of<'gc>: 'slice, +{ + type Of<'gc> = &'slice [T::Of<'gc>]; + + fn unbind(self) -> Self::Of<'static> { + const { + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + // SAFETY: We assume that T properly implements Bindable. In that case + // we can safely transmute the lifetime out of the T's in the slice. + unsafe { core::mem::transmute::<&'slice [T], &'slice [T::Of<'static>]>(self) } + } + + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + const { + assert!(core::mem::size_of::() == core::mem::size_of::>()); + assert!(core::mem::align_of::() == core::mem::align_of::>()); + } + // SAFETY: We assume that T properly implements Bindable. In that case + // we can safely transmute the lifetime into the T's in the slice. + unsafe { core::mem::transmute::<&'slice [T], &'slice [T::Of<'a>]>(self) } + } +} diff --git a/nova_vm/src/engine/register_value.rs b/nova_vm/src/engine/register_value.rs deleted file mode 100644 index 656d63871..000000000 --- a/nova_vm/src/engine/register_value.rs +++ /dev/null @@ -1,80 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use crate::ecmascript::{execution::Agent, types::IntoValue}; - -/// # Pass-by-register or stack JavaScript Value. -/// -/// This is a wrapper type for passing Values in registers or on the stack -/// without binding them to the `&mut Agent` lifetime while making it harder to -/// forget about the need to immediately re-bind the parameters Values to it. -/// -/// A Value passed through a register or on the stack is not visible to the -/// garbage collector. If a garbage collection safepoint is reached, all Values -/// contained in registers or on the stack must be rooted before the garbage -/// collection is performed and then reloaded after the garbage collection has -/// finished. Any Register Values that are not thus rooted and reloaded are -/// liable to become corrupted by the garbage collection, making them point to -/// different Values (of the same type) than before and possibly making them -/// point beyond the heap's limits. Using such corrupted Values will lead to -/// unpredictable JavaScript execution, or to an immediate crash from indexing -/// beyond a heap vector's bounds. -#[derive(Debug)] -#[repr(transparent)] -pub struct Register { - value: T, -} - -impl Register { - /// Extracts the Value from a passed-in register or from the stack and - /// binds it to the passed in Agent's lifetime (@TODO). - /// - /// ## Safety - /// - /// The binding must be performed before any call taking `&mut Agent` has - /// been called. It is recommended to bind all parameters at the very first - /// line of a function: - /// - /// ```rs - /// fn example( - /// agent: &mut Agent, mut gc: Gc<'_>, - /// this: Register, - /// args: ArgumentsList - /// ) { - /// let (this, arg0, arg1) = unsafe { ( - /// this.bind(agent), - /// args.get(0).bind(agent), - /// args.get(1).bind(agent) - /// ) }; - /// } - /// ``` - /// This ensures that the Values cannot be accidentally misused. - /// - /// ### Invalid example - /// - /// The following code is **invalid**: Do not do this. - /// - /// ```rs - /// fn example( - /// agent: &mut Agent, mut gc: Gc<'_>, - /// this: Register, - /// args: ArgumentsList - /// ) { - /// let (this, arg0) = unsafe { ( - /// this.bind(agent), - /// args.get(0).bind(agent) - /// ) }; - /// example_2(agent, this, arg0); // Takes &mut Agent - /// let arg1 = unsafe { args.get(1).bind(agent) }; // Invalid! - /// } - /// ``` - /// Binding arg1 only after the call to `example_2` is invalid as calling - /// `example_2` may have triggered GC, in which case `arg1` is invalid. - /// - /// See `Local` for how rooting `arg1` should be done in this case. - #[inline] - unsafe fn bind(self, _agent: &Agent) -> T { - self.value - } -} diff --git a/nova_vm/src/engine/rootable.rs b/nova_vm/src/engine/rootable.rs index eeba2c053..edb48ff90 100644 --- a/nova_vm/src/engine/rootable.rs +++ b/nova_vm/src/engine/rootable.rs @@ -176,7 +176,7 @@ mod private { impl RootableSealed for Symbol<'_> {} #[cfg(feature = "array-buffer")] impl RootableSealed for TypedArray<'_> {} - impl RootableSealed for Value {} + impl RootableSealed for Value<'_> {} #[cfg(feature = "weak-refs")] impl RootableSealed for WeakMap<'_> {} #[cfg(feature = "weak-refs")] @@ -188,6 +188,8 @@ mod private { pub use global::Global; pub use scoped::Scoped; +use super::context::Bindable; + pub trait Rootable: core::fmt::Debug + Copy + RootableSealed { type RootRepr: Sized + Clone + core::fmt::Debug; diff --git a/nova_vm/src/engine/rootable/scoped.rs b/nova_vm/src/engine/rootable/scoped.rs index e4c4b9c48..d4619879f 100644 --- a/nova_vm/src/engine/rootable/scoped.rs +++ b/nova_vm/src/engine/rootable/scoped.rs @@ -63,6 +63,34 @@ impl<'scope, T: 'static + Rootable> Scoped<'scope, T> { } } + /// Returns the scoped value from the heap. If the scoped value was at the + /// top of the scope stack, then this will drop the value from the stack. + /// + /// ## Safety + /// + /// The scoped value should not be shared with any other piece of code that + /// is still going to reuse it. + /// + /// ## Panics + /// + /// If the scoped value has been taken by another caller already, the + /// method panics. + pub unsafe fn take(self, agent: &Agent) -> T { + match T::from_root_repr(&self.inner) { + Ok(value) => value, + Err(heap_root_ref) => { + let Some(&heap_data) = agent.stack_refs.borrow().get(heap_root_ref.to_index()) + else { + handle_bound_check_failure() + }; + let Some(value) = T::from_heap_data(heap_data) else { + handle_invalid_scoped_conversion() + }; + value + } + } + } + pub fn get(&self, agent: &Agent) -> T { match T::from_root_repr(&self.inner) { Ok(value) => value, @@ -72,7 +100,7 @@ impl<'scope, T: 'static + Rootable> Scoped<'scope, T> { handle_bound_check_failure() }; let Some(value) = T::from_heap_data(heap_data) else { - handle_invalid_local_conversion() + handle_invalid_scoped_conversion() }; value } @@ -95,7 +123,18 @@ impl<'scope, T: 'static + Rootable> Scoped<'scope, T> { value } - pub fn replace(&mut self, agent: &Agent, value: T) { + /// Replace an existing scoped value on the heap with a new value of the + /// same type. + /// + /// ## Safety + /// + /// If the scoped value has been cloned and is still being used, replacing + /// its value will be observable to the other users and they will likely + /// find this unexpected. + /// + /// This method should only ever be called on scoped values that have not + /// been shared outside the caller. + pub unsafe fn replace(&mut self, agent: &Agent, value: T) { let heap_data = match T::to_root_repr(value) { Ok(stack_repr) => { // The value doesn't need rooting. @@ -131,6 +170,62 @@ impl<'scope, T: 'static + Rootable> Scoped<'scope, T> { } } + /// Replace an existing scoped value on the heap with a new value of a + /// different type. + /// + /// ## Safety + /// + /// If the scoped value has been cloned and is still being used, replacing + /// its value will be observable to the other users and they will likely + /// find this unexpected and will likely panic from a type mismatch. + /// + /// This method should only ever be called on scoped values that have not + /// been shared outside the caller. + pub unsafe fn replace_self( + self, + agent: &mut Agent, + value: U, + ) -> Scoped<'scope, U> { + let heap_data = match U::to_root_repr(value) { + Ok(stack_repr) => { + // The value doesn't need rooting. + return Scoped { + inner: stack_repr, + _marker: PhantomData, + _scope: PhantomData, + }; + } + Err(heap_data) => heap_data, + }; + match T::from_root_repr(&self.inner) { + Ok(_) => { + // The previous scoped value did not have an heap slot but now + // need one. + let mut stack_refs = agent.stack_refs.borrow_mut(); + let next_index = stack_refs.len(); + stack_refs.push(heap_data); + Scoped { + inner: U::from_heap_ref(HeapRootRef::from_index(next_index)), + _marker: PhantomData, + _scope: PhantomData, + } + } + Err(heap_root_ref) => { + // Existing slot, we can just replace the data. + let mut stack_refs_borrow = agent.stack_refs.borrow_mut(); + let Some(heap_slot) = stack_refs_borrow.get_mut(heap_root_ref.to_index()) else { + handle_bound_check_failure() + }; + *heap_slot = heap_data; + Scoped { + inner: U::from_heap_ref(heap_root_ref), + _marker: PhantomData, + _scope: PhantomData, + } + } + } + } + pub fn from_scoped( agent: &Agent, scoped: Scoped<'scope, U>, @@ -169,18 +264,18 @@ impl<'scope, T: 'static + Rootable> Scoped<'scope, T> { #[cold] #[inline(never)] -fn handle_invalid_local_conversion() -> ! { - panic!("Attempted to convert mismatched Local"); +fn handle_invalid_scoped_conversion() -> ! { + panic!("Attempted to convert mismatched Scoped"); } #[cold] #[inline(never)] fn handle_index_overflow() -> ! { - panic!("Locals stack overflowed"); + panic!("Scoped stack overflowed"); } #[cold] #[inline(never)] fn handle_bound_check_failure() -> ! { - panic!("Attempted to access dropped Local") + panic!("Attempted to access dropped Scoped") } diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index d2821e0ed..77442b13d 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -88,7 +88,7 @@ use crate::{ SymbolHeapData, BUILTIN_STRINGS_LIST, }, }, - engine::{rootable::HeapRootData, ExecutableHeapData}, + engine::{context::Bindable, rootable::HeapRootData, ExecutableHeapData}, }; #[cfg(feature = "array-buffer")] use ahash::AHashMap; diff --git a/nova_vm/src/heap/element_array.rs b/nova_vm/src/heap/element_array.rs index 83fadfc0e..68624f605 100644 --- a/nova_vm/src/heap/element_array.rs +++ b/nova_vm/src/heap/element_array.rs @@ -15,7 +15,7 @@ use crate::{ execution::Agent, types::{Function, PropertyDescriptor, PropertyKey, Value}, }, - engine::context::NoGcScope, + engine::context::{Bindable, NoGcScope}, }; use core::{ mem::MaybeUninit, @@ -221,7 +221,7 @@ impl ElementsVector { &mut elements.e2pow32.values[self.elements_index][self.len as usize] } }; - *next_over_end = value; + *next_over_end = value.map(Value::unbind); if let Some(descriptor) = descriptor { let descriptors_map = match self.cap { ElementArrayKey::Empty => unreachable!(), @@ -511,9 +511,9 @@ impl ElementDescriptor { } } - pub(crate) fn from_object_entry_property_descriptor( - desc: &ObjectEntryPropertyDescriptor, - ) -> (Option, Option) { + pub(crate) fn from_object_entry_property_descriptor<'a>( + desc: &ObjectEntryPropertyDescriptor<'a>, + ) -> (Option, Option>) { match desc { ObjectEntryPropertyDescriptor::Data { value, @@ -558,20 +558,32 @@ impl ElementDescriptor { configurable, } => match (enumerable, configurable) { (true, true) => ( - Some(ElementDescriptor::ReadOnlyEnumerableConfigurableAccessor { get: *get }), + Some(ElementDescriptor::ReadOnlyEnumerableConfigurableAccessor { + get: get.unbind(), + }), None, ), (true, false) => ( - Some(ElementDescriptor::ReadOnlyEnumerableUnconfigurableAccessor { get: *get }), + Some( + ElementDescriptor::ReadOnlyEnumerableUnconfigurableAccessor { + get: get.unbind(), + }, + ), None, ), (false, true) => ( - Some(ElementDescriptor::ReadOnlyUnenumerableConfigurableAccessor { get: *get }), + Some( + ElementDescriptor::ReadOnlyUnenumerableConfigurableAccessor { + get: get.unbind(), + }, + ), None, ), (false, false) => ( Some( - ElementDescriptor::ReadOnlyUnenumerableUnconfigurableAccessor { get: *get }, + ElementDescriptor::ReadOnlyUnenumerableUnconfigurableAccessor { + get: get.unbind(), + }, ), None, ), @@ -582,25 +594,31 @@ impl ElementDescriptor { configurable, } => match (enumerable, configurable) { (true, true) => ( - Some(ElementDescriptor::WriteOnlyEnumerableConfigurableAccessor { set: *set }), + Some(ElementDescriptor::WriteOnlyEnumerableConfigurableAccessor { + set: set.unbind(), + }), None, ), (true, false) => ( Some( - ElementDescriptor::WriteOnlyEnumerableUnconfigurableAccessor { set: *set }, + ElementDescriptor::WriteOnlyEnumerableUnconfigurableAccessor { + set: set.unbind(), + }, ), None, ), (false, true) => ( Some( - ElementDescriptor::WriteOnlyUnenumerableConfigurableAccessor { set: *set }, + ElementDescriptor::WriteOnlyUnenumerableConfigurableAccessor { + set: set.unbind(), + }, ), None, ), (false, false) => ( Some( ElementDescriptor::WriteOnlyUnenumerableUnconfigurableAccessor { - set: *set, + set: set.unbind(), }, ), None, @@ -614,16 +632,16 @@ impl ElementDescriptor { } => match (enumerable, configurable) { (true, true) => ( Some(ElementDescriptor::ReadWriteEnumerableConfigurableAccessor { - get: *get, - set: *set, + get: get.unbind(), + set: set.unbind(), }), None, ), (true, false) => ( Some( ElementDescriptor::ReadWriteEnumerableUnconfigurableAccessor { - get: *get, - set: *set, + get: get.unbind(), + set: set.unbind(), }, ), None, @@ -631,8 +649,8 @@ impl ElementDescriptor { (false, true) => ( Some( ElementDescriptor::ReadWriteUnenumerableConfigurableAccessor { - get: *get, - set: *set, + get: get.unbind(), + set: set.unbind(), }, ), None, @@ -640,8 +658,8 @@ impl ElementDescriptor { (false, false) => ( Some( ElementDescriptor::ReadWriteUnenumerableUnconfigurableAccessor { - get: *get, - set: *set, + get: get.unbind(), + set: set.unbind(), }, ), None, @@ -701,6 +719,7 @@ impl ElementDescriptor { ) -> PropertyDescriptor { let descriptor = descriptor.unwrap_or(ElementDescriptor::WritableEnumerableConfigurableData); + let value = value.map(Value::unbind); match descriptor { ElementDescriptor::WritableEnumerableConfigurableData => PropertyDescriptor { value, @@ -989,7 +1008,7 @@ impl ElementDescriptor { /// Element arrays of up to 16 elements #[derive(Debug, Default)] pub struct ElementArray2Pow4 { - pub values: Vec; usize::pow(2, 4)]>>, + pub values: Vec>; usize::pow(2, 4)]>>, pub descriptors: AHashMap>, } @@ -1005,7 +1024,7 @@ impl ElementArray2Pow4 { /// Element arrays of up to 64 elements #[derive(Debug, Default)] pub struct ElementArray2Pow6 { - pub values: Vec; usize::pow(2, 6)]>>, + pub values: Vec>; usize::pow(2, 6)]>>, pub descriptors: AHashMap>, } @@ -1021,7 +1040,7 @@ impl ElementArray2Pow6 { /// Element arrays of up to 256 elements #[derive(Debug, Default)] pub struct ElementArray2Pow8 { - pub values: Vec; usize::pow(2, 8)]>>, + pub values: Vec>; usize::pow(2, 8)]>>, pub descriptors: AHashMap>, } @@ -1037,7 +1056,7 @@ impl ElementArray2Pow8 { /// Element arrays of up to 1024 elements #[derive(Debug, Default)] pub struct ElementArray2Pow10 { - pub values: Vec; usize::pow(2, 10)]>>, + pub values: Vec>; usize::pow(2, 10)]>>, pub descriptors: AHashMap>, } @@ -1053,7 +1072,7 @@ impl ElementArray2Pow10 { /// Element arrays of up to 4096 elements #[derive(Debug, Default)] pub struct ElementArray2Pow12 { - pub values: Vec; usize::pow(2, 12)]>>, + pub values: Vec>; usize::pow(2, 12)]>>, pub descriptors: AHashMap>, } @@ -1069,7 +1088,7 @@ impl ElementArray2Pow12 { /// Element arrays of up to 65536 elements #[derive(Debug, Default)] pub struct ElementArray2Pow16 { - pub values: Vec; usize::pow(2, 16)]>>, + pub values: Vec>; usize::pow(2, 16)]>>, pub descriptors: AHashMap>, } @@ -1085,7 +1104,7 @@ impl ElementArray2Pow16 { /// Element arrays of up to 16777216 elements #[derive(Debug, Default)] pub struct ElementArray2Pow24 { - pub values: Vec; usize::pow(2, 24)]>>, + pub values: Vec>; usize::pow(2, 24)]>>, pub descriptors: AHashMap>, } @@ -1101,7 +1120,7 @@ impl ElementArray2Pow24 { /// Element arrays of up to 4294967296 elements #[derive(Debug, Default)] pub struct ElementArray2Pow32 { - pub values: Vec; usize::pow(2, 32)]>>, + pub values: Vec>; usize::pow(2, 32)]>>, pub descriptors: AHashMap>, } @@ -1135,7 +1154,7 @@ pub struct ElementArrays { } impl Index for ElementArrays { - type Output = [Option]; + type Output = [Option>]; fn index(&self, index: ElementsVector) -> &Self::Output { self.get(index) @@ -1149,7 +1168,7 @@ impl IndexMut for ElementArrays { } impl Index for Agent { - type Output = [Option]; + type Output = [Option>]; fn index(&self, index: ElementsVector) -> &Self::Output { &self.heap.elements[index] @@ -1163,7 +1182,7 @@ impl IndexMut for Agent { } impl Index for ElementArrays { - type Output = [Option]; + type Output = [Option>]; fn index(&self, index: SealableElementsVector) -> &Self::Output { self.get(index.into()) @@ -1177,7 +1196,7 @@ impl IndexMut for ElementArrays { } impl Index for Agent { - type Output = [Option]; + type Output = [Option>]; fn index(&self, index: SealableElementsVector) -> &Self::Output { &self.heap.elements[index] @@ -1954,7 +1973,7 @@ impl ElementArrays { ) } - pub fn get(&self, vector: ElementsVector) -> &[Option] { + pub fn get(&self, vector: ElementsVector) -> &[Option>] { match vector.cap { ElementArrayKey::Empty => &[], ElementArrayKey::E4 => { @@ -1984,7 +2003,7 @@ impl ElementArrays { } } - pub fn get_mut(&mut self, vector: ElementsVector) -> &mut [Option] { + pub fn get_mut(&mut self, vector: ElementsVector) -> &mut [Option>] { match vector.cap { ElementArrayKey::Empty => &mut [], ElementArrayKey::E4 => &mut self.e2pow4.values[vector.elements_index].as_mut_slice() @@ -2009,7 +2028,10 @@ impl ElementArrays { pub fn get_descriptors_and_slice( &self, vector: ElementsVector, - ) -> (Option<&AHashMap>, &[Option]) { + ) -> ( + Option<&AHashMap>, + &[Option>], + ) { let usize_index = vector.elements_index.into_index(); match vector.cap { ElementArrayKey::Empty => (None, &[]), @@ -2125,7 +2147,7 @@ impl ElementArrays { vector: ElementsVector, ) -> ( Option<&mut AHashMap>, - &mut [Option], + &mut [Option>], ) { let usize_index = vector.elements_index.into_index(); match vector.cap { diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index d1c487138..74bd930db 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -1015,7 +1015,7 @@ pub(crate) fn sweep_heap_vector_values( } pub(crate) fn sweep_heap_u8_elements_vector_values( - vec: &mut Vec; N]>>, + vec: &mut Vec>; N]>>, compactions: &CompactionLists, u8s: &[(bool, u8)], ) { @@ -1033,7 +1033,7 @@ pub(crate) fn sweep_heap_u8_elements_vector_values( } pub(crate) fn sweep_heap_u16_elements_vector_values( - vec: &mut Vec; N]>>, + vec: &mut Vec>; N]>>, compactions: &CompactionLists, u16s: &[(bool, u16)], ) { @@ -1051,7 +1051,7 @@ pub(crate) fn sweep_heap_u16_elements_vector_values( } pub(crate) fn sweep_heap_u32_elements_vector_values( - vec: &mut Vec; N]>>, + vec: &mut Vec>; N]>>, compactions: &CompactionLists, u32s: &[(bool, u32)], ) { diff --git a/nova_vm/src/heap/heap_constants.rs b/nova_vm/src/heap/heap_constants.rs index 09e2fc625..d330f3cc5 100644 --- a/nova_vm/src/heap/heap_constants.rs +++ b/nova_vm/src/heap/heap_constants.rs @@ -384,7 +384,7 @@ impl From for Symbol<'static> { } } -impl From for Value { +impl From for Value<'static> { fn from(value: WellKnownSymbolIndexes) -> Self { Value::Symbol(value.into()) } diff --git a/nova_vm/src/heap/indexes.rs b/nova_vm/src/heap/indexes.rs index 3d37f9740..8bcd13f40 100644 --- a/nova_vm/src/heap/indexes.rs +++ b/nova_vm/src/heap/indexes.rs @@ -201,7 +201,7 @@ pub type DataViewIndex<'a> = BaseIndex<'a, DataViewHeapData>; #[cfg(feature = "date")] pub type DateIndex<'a> = BaseIndex<'a, DateHeapData>; pub type ECMAScriptFunctionIndex<'a> = BaseIndex<'a, ECMAScriptFunctionHeapData>; -pub type ElementIndex = BaseIndex<'static, [Option]>; +pub type ElementIndex = BaseIndex<'static, [Option>]>; pub type EmbedderObjectIndex<'a> = BaseIndex<'a, EmbedderObjectHeapData>; pub type ErrorIndex<'a> = BaseIndex<'a, ErrorHeapData>; pub type FinalizationRegistryIndex<'a> = BaseIndex<'a, FinalizationRegistryHeapData>; @@ -273,8 +273,8 @@ impl ElementIndex { } } -impl Index for Vec; N]>> { - type Output = [Option; N]; +impl Index for Vec>; N]>> { + type Output = [Option>; N]; fn index(&self, index: ElementIndex) -> &Self::Output { self.get(index.into_index()) @@ -284,7 +284,7 @@ impl Index for Vec; N]>> { } } -impl IndexMut for Vec; N]>> { +impl IndexMut for Vec>; N]>> { fn index_mut(&mut self, index: ElementIndex) -> &mut Self::Output { self.get_mut(index.into_index()) .expect("Invalid ElementsVector: No item at index") diff --git a/nova_vm/src/heap/object_entry.rs b/nova_vm/src/heap/object_entry.rs index 36ccf7127..727b1ad51 100644 --- a/nova_vm/src/heap/object_entry.rs +++ b/nova_vm/src/heap/object_entry.rs @@ -7,11 +7,11 @@ use crate::ecmascript::types::{Function, PropertyDescriptor, PropertyKey, Value} #[derive(Debug, Clone, Copy)] pub(crate) struct ObjectEntry<'a> { pub key: PropertyKey<'a>, - pub value: ObjectEntryPropertyDescriptor, + pub value: ObjectEntryPropertyDescriptor<'a>, } impl<'a> ObjectEntry<'a> { - pub(crate) fn new_data_entry(key: PropertyKey<'a>, value: Value) -> Self { + pub(crate) fn new_data_entry(key: PropertyKey<'a>, value: Value<'a>) -> Self { Self { key, value: ObjectEntryPropertyDescriptor::Data { @@ -24,7 +24,7 @@ impl<'a> ObjectEntry<'a> { } } -impl From for ObjectEntryPropertyDescriptor { +impl From for ObjectEntryPropertyDescriptor<'static> { fn from(value: PropertyDescriptor) -> Self { let configurable = value.configurable.unwrap_or(true); let enumerable = value.enumerable.unwrap_or(true); @@ -66,9 +66,9 @@ impl From for ObjectEntryPropertyDescriptor { } #[derive(Debug, Clone, Copy)] -pub(crate) enum ObjectEntryPropertyDescriptor { +pub(crate) enum ObjectEntryPropertyDescriptor<'a> { Data { - value: Value, + value: Value<'a>, writable: bool, enumerable: bool, configurable: bool, @@ -78,18 +78,18 @@ pub(crate) enum ObjectEntryPropertyDescriptor { configurable: bool, }, ReadOnly { - get: Function<'static>, + get: Function<'a>, enumerable: bool, configurable: bool, }, WriteOnly { - set: Function<'static>, + set: Function<'a>, enumerable: bool, configurable: bool, }, ReadWrite { - get: Function<'static>, - set: Function<'static>, + get: Function<'a>, + set: Function<'a>, enumerable: bool, configurable: bool, }, diff --git a/nova_vm/tests/garbage_collection_tests.rs b/nova_vm/tests/garbage_collection_tests.rs index 1ecf84ae1..7060714c6 100644 --- a/nova_vm/tests/garbage_collection_tests.rs +++ b/nova_vm/tests/garbage_collection_tests.rs @@ -9,7 +9,7 @@ use nova_vm::{ scripts_and_modules::script::{parse_script, script_evaluation}, types::{Object, String, Value}, }, - engine::context::GcScope, + engine::context::{Bindable, GcScope}, }; fn initialize_global_object(agent: &mut Agent, global: Object, gc: GcScope) { @@ -20,7 +20,12 @@ fn initialize_global_object(agent: &mut Agent, global: Object, gc: GcScope) { }; // `print` function - fn print(agent: &mut Agent, _this: Value, args: ArgumentsList, gc: GcScope) -> JsResult { + fn print<'gc>( + agent: &mut Agent, + _this: Value, + args: ArgumentsList, + gc: GcScope<'gc, '_>, + ) -> JsResult> { if args.len() == 0 { println!(); } else { @@ -40,7 +45,7 @@ fn initialize_global_object(agent: &mut Agent, global: Object, gc: GcScope) { agent, property_key, PropertyDescriptor { - value: Some(function.into_value()), + value: Some(function.into_value().unbind()), ..Default::default() }, gc, @@ -84,13 +89,13 @@ fn garbage_collection_tests() { let realm = agent.current_realm_id(); let source_text = String::from_string(agent, header_contents, gc.nogc()); let script = parse_script(agent, source_text, realm, false, None, gc.nogc()).unwrap(); - let _ = script_evaluation(agent, script, gc.reborrow()).unwrap_or_else(|err| { + if let Err(err) = script_evaluation(agent, script, gc.reborrow()) { panic!( "Header evaluation failed: '{}' failed: {:?}", d.display(), err.value().string_repr(agent, gc.reborrow()).as_str(agent) ) - }); + } }); agent.gc(); @@ -99,7 +104,7 @@ fn garbage_collection_tests() { let realm = agent.current_realm_id(); let source_text = String::from_string(agent, call_contents.clone(), gc.nogc()); let script = parse_script(agent, source_text, realm, false, None, gc.nogc()).unwrap(); - let _ = script_evaluation(agent, script, gc.reborrow()).unwrap_or_else(|err| { + if let Err(err) = script_evaluation(agent, script, gc.reborrow()) { println!("Error kind: {:?}", err.value()); panic!( "Loop index run {} '{}' failed: {:?}", @@ -107,7 +112,7 @@ fn garbage_collection_tests() { d.display(), err.value().string_repr(agent, gc.reborrow()).as_str(agent) ) - }); + } }); agent.gc(); } diff --git a/nova_vm/tests/object_prototype_tests.rs b/nova_vm/tests/object_prototype_tests.rs index 598c0c4f2..44c8a5806 100644 --- a/nova_vm/tests/object_prototype_tests.rs +++ b/nova_vm/tests/object_prototype_tests.rs @@ -31,12 +31,12 @@ fn object_prototype_tests() { let realm = agent.current_realm_id(); let source_text = String::from_string(agent, contents, gc.nogc()); let script = parse_script(agent, source_text, realm, false, None, gc.nogc()).unwrap(); - let _ = script_evaluation(agent, script, gc.reborrow()).unwrap_or_else(|err| { + if let Err(err) = script_evaluation(agent, script, gc.reborrow()) { panic!( "Test '{}' failed: {:?}", d.display(), err.to_string(agent, gc).as_str(agent) ) - }); + } }); } diff --git a/tests/expectations.json b/tests/expectations.json index aa2c7e71f..68eebc34b 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -1,12 +1,9 @@ { "built-ins/Array/from/from-string.js": "CRASH", - "built-ins/Array/from/iter-map-fn-err.js": "CRASH", - "built-ins/Array/from/iter-set-elem-prop-err.js": "FAIL", - "built-ins/Array/from/mapfn-throws-exception.js": "CRASH", "built-ins/Array/from/proto-from-ctor-realm.js": "FAIL", "built-ins/Array/fromAsync/async-iterable-async-mapped-awaits-once.js": "CRASH", "built-ins/Array/fromAsync/async-iterable-input-does-not-await-input.js": "FAIL", - "built-ins/Array/fromAsync/async-iterable-input-iteration-err.js": "CRASH", + "built-ins/Array/fromAsync/async-iterable-input-iteration-err.js": "FAIL", "built-ins/Array/fromAsync/async-iterable-input.js": "CRASH", "built-ins/Array/fromAsync/asyncitems-array-add-to-empty.js": "FAIL", "built-ins/Array/fromAsync/asyncitems-array-add-to-singleton.js": "FAIL", @@ -41,7 +38,7 @@ "built-ins/Array/fromAsync/builtin.js": "FAIL", "built-ins/Array/fromAsync/length.js": "FAIL", "built-ins/Array/fromAsync/mapfn-async-arraylike.js": "FAIL", - "built-ins/Array/fromAsync/mapfn-async-iterable-async.js": "CRASH", + "built-ins/Array/fromAsync/mapfn-async-iterable-async.js": "FAIL", "built-ins/Array/fromAsync/mapfn-async-iterable-sync.js": "FAIL", "built-ins/Array/fromAsync/mapfn-async-throws-close-async-iterator.js": "FAIL", "built-ins/Array/fromAsync/mapfn-async-throws-close-sync-iterator.js": "FAIL", @@ -49,7 +46,7 @@ "built-ins/Array/fromAsync/mapfn-not-callable.js": "FAIL", "built-ins/Array/fromAsync/mapfn-result-awaited-once-per-iteration.js": "FAIL", "built-ins/Array/fromAsync/mapfn-sync-arraylike.js": "FAIL", - "built-ins/Array/fromAsync/mapfn-sync-iterable-async.js": "CRASH", + "built-ins/Array/fromAsync/mapfn-sync-iterable-async.js": "FAIL", "built-ins/Array/fromAsync/mapfn-sync-iterable-sync.js": "FAIL", "built-ins/Array/fromAsync/mapfn-sync-throws-close-async-iterator.js": "FAIL", "built-ins/Array/fromAsync/mapfn-sync-throws-close-sync-iterator.js": "FAIL", @@ -206,36 +203,10 @@ "built-ins/Array/prototype/flatMap/this-value-ctor-object-species.js": "FAIL", "built-ins/Array/prototype/forEach/15.4.4.18-1-11.js": "CRASH", "built-ins/Array/prototype/forEach/15.4.4.18-1-12.js": "CRASH", - "built-ins/Array/prototype/forEach/15.4.4.18-5-1-s.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-1.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-10.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-11.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-12.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-13.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-14.js": "FAIL", "built-ins/Array/prototype/forEach/15.4.4.18-5-15.js": "CRASH", "built-ins/Array/prototype/forEach/15.4.4.18-5-16.js": "CRASH", - "built-ins/Array/prototype/forEach/15.4.4.18-5-17.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-18.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-19.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-2.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-21.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-22.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-23.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-24.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-3.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-4.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-5.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-6.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-7.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-5-9.js": "FAIL", "built-ins/Array/prototype/forEach/15.4.4.18-7-b-16.js": "FAIL", "built-ins/Array/prototype/forEach/15.4.4.18-7-c-i-20.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-16.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-17.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-18.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-20.js": "FAIL", - "built-ins/Array/prototype/forEach/15.4.4.18-7-c-ii-6.js": "FAIL", "built-ins/Array/prototype/forEach/resizable-buffer.js": "CRASH", "built-ins/Array/prototype/includes/fromIndex-equal-or-greater-length-returns-false.js": "CRASH", "built-ins/Array/prototype/includes/resizable-buffer.js": "CRASH", @@ -286,7 +257,6 @@ "built-ins/Array/prototype/reduceRight/15.4.4.22-9-c-ii-32.js": "CRASH", "built-ins/Array/prototype/reduceRight/resizable-buffer.js": "CRASH", "built-ins/Array/prototype/reverse/get_if_present_with_delete.js": "CRASH", - "built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy.js": "CRASH", "built-ins/Array/prototype/reverse/resizable-buffer.js": "CRASH", "built-ins/Array/prototype/slice/create-revoked-proxy.js": "CRASH", "built-ins/Array/prototype/slice/resizable-buffer.js": "CRASH", @@ -488,9 +458,7 @@ "built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-return-object.js": "CRASH", "built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined.js": "CRASH", "built-ins/AsyncFunction/proto-from-ctor-realm.js": "FAIL", - "built-ins/AsyncGeneratorFunction/instance-await-expr-in-param.js": "CRASH", "built-ins/AsyncGeneratorFunction/instance-prototype.js": "CRASH", - "built-ins/AsyncGeneratorFunction/instance-yield-expr-in-param.js": "CRASH", "built-ins/AsyncGeneratorFunction/invoked-as-constructor-no-arguments.js": "CRASH", "built-ins/AsyncGeneratorFunction/invoked-as-function-multiple-arguments.js": "CRASH", "built-ins/AsyncGeneratorFunction/invoked-as-function-no-arguments.js": "CRASH", @@ -498,8 +466,6 @@ "built-ins/AsyncGeneratorFunction/proto-from-ctor-realm-prototype.js": "FAIL", "built-ins/AsyncGeneratorFunction/proto-from-ctor-realm.js": "FAIL", "built-ins/AsyncGeneratorFunction/prototype/constructor.js": "FAIL", - "built-ins/AsyncGeneratorFunction/prototype/prototype.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/Symbol.toStringTag.js": "CRASH", "built-ins/AsyncGeneratorPrototype/next/request-queue-order.js": "CRASH", "built-ins/AsyncGeneratorPrototype/next/request-queue-promise-resolve-order.js": "CRASH", "built-ins/AsyncGeneratorPrototype/next/this-val-not-async-generator.js": "CRASH", @@ -507,8 +473,6 @@ "built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-state-completed.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-promise.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/return/return-suspendedStart.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-promise.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-try-finally-return.js": "CRASH", @@ -519,9 +483,6 @@ "built-ins/AsyncGeneratorPrototype/return/this-val-not-object.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/this-val-not-async-generator.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/this-val-not-object.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/throw/throw-state-completed.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/throw/throw-suspendedStart-promise.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/throw/throw-suspendedStart.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-promise.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-catch.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-finally-return.js": "CRASH", @@ -5078,8 +5039,6 @@ "built-ins/String/S15.5.5.1_A4_T1.js": "CRASH", "built-ins/String/S9.8_A5_T1.js": "FAIL", "built-ins/String/proto-from-ctor-realm.js": "FAIL", - "built-ins/String/prototype/Symbol.iterator/this-val-non-obj-coercible.js": "CRASH", - "built-ins/String/prototype/Symbol.iterator/this-val-to-str-err.js": "CRASH", "built-ins/String/prototype/at/returns-code-unit.js": "FAIL", "built-ins/String/prototype/codePointAt/return-code-unit-coerced-position.js": "CRASH", "built-ins/String/prototype/codePointAt/return-first-code-unit.js": "FAIL", @@ -9557,7 +9516,6 @@ "built-ins/TypedArray/prototype/copyWithin/this-is-not-typedarray-instance.js": "CRASH", "built-ins/TypedArray/prototype/copyWithin/undefined-end.js": "CRASH", "built-ins/TypedArray/prototype/entries/resizable-buffer.js": "CRASH", - "built-ins/TypedArray/prototype/every/BigInt/callbackfn-set-value-during-interaction.js": "CRASH", "built-ins/TypedArray/prototype/every/resizable-buffer.js": "CRASH", "built-ins/TypedArray/prototype/fill/BigInt/coerced-indexes.js": "CRASH", "built-ins/TypedArray/prototype/fill/BigInt/detached-buffer.js": "CRASH", @@ -9821,7 +9779,6 @@ "built-ins/TypedArray/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js": "CRASH", "built-ins/TypedArray/prototype/findLastIndex/this-is-not-object.js": "CRASH", "built-ins/TypedArray/prototype/findLastIndex/this-is-not-typedarray-instance.js": "CRASH", - "built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-set-value-during-interaction.js": "CRASH", "built-ins/TypedArray/prototype/forEach/resizable-buffer.js": "CRASH", "built-ins/TypedArray/prototype/includes/resizable-buffer.js": "CRASH", "built-ins/TypedArray/prototype/indexOf/BigInt/detached-buffer-during-fromIndex-returns-minus-one-for-undefined.js": "CRASH", @@ -10288,7 +10245,6 @@ "built-ins/TypedArray/prototype/slice/this-is-not-typedarray-instance.js": "CRASH", "built-ins/TypedArray/prototype/slice/tointeger-end.js": "CRASH", "built-ins/TypedArray/prototype/slice/tointeger-start.js": "CRASH", - "built-ins/TypedArray/prototype/some/BigInt/callbackfn-set-value-during-interaction.js": "CRASH", "built-ins/TypedArray/prototype/some/resizable-buffer.js": "CRASH", "built-ins/TypedArray/prototype/sort/BigInt/arraylength-internal.js": "CRASH", "built-ins/TypedArray/prototype/sort/BigInt/comparefn-call-throws.js": "CRASH", @@ -11061,16 +11017,6 @@ "language/arguments-object/S10.6_A3_T3.js": "FAIL", "language/arguments-object/S10.6_A3_T4.js": "FAIL", "language/arguments-object/S10.6_A4.js": "FAIL", - "language/arguments-object/async-gen-meth-args-trailing-comma-multiple.js": "CRASH", - "language/arguments-object/async-gen-meth-args-trailing-comma-null.js": "CRASH", - "language/arguments-object/async-gen-meth-args-trailing-comma-single-args.js": "CRASH", - "language/arguments-object/async-gen-meth-args-trailing-comma-spread-operator.js": "CRASH", - "language/arguments-object/async-gen-meth-args-trailing-comma-undefined.js": "CRASH", - "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-multiple.js": "CRASH", - "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-null.js": "CRASH", - "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-single-args.js": "CRASH", - "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-spread-operator.js": "CRASH", - "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-undefined.js": "CRASH", "language/arguments-object/cls-decl-async-gen-meth-args-trailing-comma-multiple.js": "CRASH", "language/arguments-object/cls-decl-async-gen-meth-args-trailing-comma-null.js": "CRASH", "language/arguments-object/cls-decl-async-gen-meth-args-trailing-comma-single-args.js": "CRASH", @@ -11111,11 +11057,6 @@ "language/arguments-object/cls-decl-private-meth-static-args-trailing-comma-single-args.js": "CRASH", "language/arguments-object/cls-decl-private-meth-static-args-trailing-comma-spread-operator.js": "CRASH", "language/arguments-object/cls-decl-private-meth-static-args-trailing-comma-undefined.js": "CRASH", - "language/arguments-object/cls-expr-async-gen-func-args-trailing-comma-multiple.js": "CRASH", - "language/arguments-object/cls-expr-async-gen-func-args-trailing-comma-null.js": "CRASH", - "language/arguments-object/cls-expr-async-gen-func-args-trailing-comma-single-args.js": "CRASH", - "language/arguments-object/cls-expr-async-gen-func-args-trailing-comma-spread-operator.js": "CRASH", - "language/arguments-object/cls-expr-async-gen-func-args-trailing-comma-undefined.js": "CRASH", "language/arguments-object/cls-expr-async-gen-meth-args-trailing-comma-multiple.js": "CRASH", "language/arguments-object/cls-expr-async-gen-meth-args-trailing-comma-null.js": "CRASH", "language/arguments-object/cls-expr-async-gen-meth-args-trailing-comma-single-args.js": "CRASH", @@ -11711,406 +11652,127 @@ "language/expressions/async-function/try-throw-finally-throw.js": "CRASH", "language/expressions/async-generator/default-proto.js": "CRASH", "language/expressions/async-generator/dflt-params-abrupt.js": "CRASH", - "language/expressions/async-generator/dflt-params-arg-val-not-undefined.js": "CRASH", - "language/expressions/async-generator/dflt-params-arg-val-undefined.js": "CRASH", "language/expressions/async-generator/dflt-params-ref-later.js": "CRASH", - "language/expressions/async-generator/dflt-params-ref-prior.js": "CRASH", "language/expressions/async-generator/dflt-params-ref-self.js": "CRASH", - "language/expressions/async-generator/dflt-params-trailing-comma.js": "CRASH", - "language/expressions/async-generator/dstr/ary-init-iter-close.js": "CRASH", "language/expressions/async-generator/dstr/ary-init-iter-get-err-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/ary-init-iter-get-err.js": "CRASH", - "language/expressions/async-generator/dstr/ary-init-iter-no-close.js": "CRASH", - "language/expressions/async-generator/dstr/ary-name-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-undef.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-iter-done.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elem-obj-val-null.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elision-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-elision-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-elision.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-empty.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-ary-elem.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-ary-elision.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-ary-empty.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-ary-rest.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-id-direct.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-id-elision.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-id-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/expressions/async-generator/dstr/ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-id.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/ary-ptrn-rest-obj-prop-id.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-init-iter-close.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-init-iter-get-err-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-init-iter-get-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-init-iter-no-close.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-name-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-undef.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-done.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-null.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elision-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-elision-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-elision.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-empty.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-ary-elem.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-ary-elision.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-ary-empty.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-ary-rest.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-id-direct.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-id-elision.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-id-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-id.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-ary-ptrn-rest-obj-prop-id.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-init-null.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-init-undefined.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-empty.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-id-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-list-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-ary-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-ary.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-eval-err.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-id.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-obj-init.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-null.js": "CRASH", "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-prop-obj.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-rest-getter.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/expressions/async-generator/dstr/dflt-obj-ptrn-rest-val-obj.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-init-iter-close.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-init-iter-get-err-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-init-iter-get-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-init-iter-no-close.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-name-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-undef.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-iter-done.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elem-obj-val-null.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elision-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-elision-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-elision.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-empty.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-ary-elem.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-ary-elision.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-ary-empty.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-ary-rest.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-id-direct.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-id-elision.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-id-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/expressions/async-generator/dstr/named-ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-ary-ptrn-rest-obj-prop-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-init-iter-close.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-init-iter-get-err-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-init-iter-get-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-init-iter-no-close.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-name-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-undef.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-iter-done.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-obj-val-null.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elision-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elision-step-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-elision.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-empty.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-ary-elem.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-ary-elision.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-ary-empty.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-ary-rest.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-id-direct.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-id-elision.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-id-exhausted.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-obj-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-ary-ptrn-rest-obj-prop-id.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-init-null.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-init-undefined.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-empty.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-id-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-list-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-ary-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-ary.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-eval-err.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-obj-init.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-obj-value-null.js": "CRASH", "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-prop-obj.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-rest-getter.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/expressions/async-generator/dstr/named-dflt-obj-ptrn-rest-val-obj.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-init-null.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-init-undefined.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-empty.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-id-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-list-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-ary-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-ary.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-prop-eval-err.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-prop-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-id.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-obj-init.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-prop-obj-value-null.js": "CRASH", "language/expressions/async-generator/dstr/named-obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-prop-obj.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-rest-getter.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/expressions/async-generator/dstr/named-obj-ptrn-rest-val-obj.js": "CRASH", "language/expressions/async-generator/dstr/obj-init-null.js": "CRASH", "language/expressions/async-generator/dstr/obj-init-undefined.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-empty.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-id-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-list-err.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-ary-init.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-ary.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-prop-eval-err.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-prop-id-init-throws.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-id-init.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-id.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-obj-init.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-prop-obj-value-null.js": "CRASH", "language/expressions/async-generator/dstr/obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-prop-obj.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-rest-getter.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/expressions/async-generator/dstr/obj-ptrn-rest-val-obj.js": "CRASH", "language/expressions/async-generator/eval-body-proto-realm.js": "FAIL", "language/expressions/async-generator/eval-var-scope-syntax-err.js": "CRASH", - "language/expressions/async-generator/expression-await-as-yield-operand.js": "CRASH", - "language/expressions/async-generator/expression-await-promise-as-yield-operand.js": "CRASH", - "language/expressions/async-generator/expression-await-thenable-as-yield-operand.js": "CRASH", - "language/expressions/async-generator/expression-yield-as-operand.js": "CRASH", - "language/expressions/async-generator/expression-yield-as-statement.js": "CRASH", - "language/expressions/async-generator/expression-yield-newline.js": "CRASH", "language/expressions/async-generator/expression-yield-star-before-newline.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b1/async-gen-func-expr-forbidden-ext-direct-access-prop-arguments.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b1/async-gen-func-expr-forbidden-ext-direct-access-prop-caller.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b1/async-gen-named-func-expr-forbidden-ext-direct-access-prop-arguments.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b1/async-gen-named-func-expr-forbidden-ext-direct-access-prop-caller.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b2/async-gen-func-expr-forbidden-ext-indirect-access-own-prop-caller-get.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b2/async-gen-func-expr-forbidden-ext-indirect-access-own-prop-caller-value.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b2/async-gen-func-expr-forbidden-ext-indirect-access-prop-caller.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b2/async-gen-named-func-expr-forbidden-ext-indirect-access-own-prop-caller-get.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b2/async-gen-named-func-expr-forbidden-ext-indirect-access-own-prop-caller-value.js": "CRASH", - "language/expressions/async-generator/forbidden-ext/b2/async-gen-named-func-expr-forbidden-ext-indirect-access-prop-caller.js": "CRASH", "language/expressions/async-generator/generator-created-after-decl-inst.js": "CRASH", "language/expressions/async-generator/named-dflt-params-abrupt.js": "CRASH", - "language/expressions/async-generator/named-dflt-params-arg-val-not-undefined.js": "CRASH", - "language/expressions/async-generator/named-dflt-params-arg-val-undefined.js": "CRASH", "language/expressions/async-generator/named-dflt-params-ref-later.js": "CRASH", - "language/expressions/async-generator/named-dflt-params-ref-prior.js": "CRASH", "language/expressions/async-generator/named-dflt-params-ref-self.js": "CRASH", - "language/expressions/async-generator/named-dflt-params-trailing-comma.js": "CRASH", "language/expressions/async-generator/named-eval-var-scope-syntax-err.js": "CRASH", - "language/expressions/async-generator/named-no-strict-reassign-fn-name-in-body-in-arrow.js": "CRASH", - "language/expressions/async-generator/named-no-strict-reassign-fn-name-in-body-in-eval.js": "CRASH", - "language/expressions/async-generator/named-no-strict-reassign-fn-name-in-body.js": "CRASH", - "language/expressions/async-generator/named-params-trailing-comma-multiple.js": "CRASH", - "language/expressions/async-generator/named-params-trailing-comma-single.js": "CRASH", - "language/expressions/async-generator/named-strict-error-reassign-fn-name-in-body-in-arrow.js": "CRASH", - "language/expressions/async-generator/named-strict-error-reassign-fn-name-in-body-in-eval.js": "CRASH", - "language/expressions/async-generator/named-strict-error-reassign-fn-name-in-body.js": "CRASH", "language/expressions/async-generator/named-unscopables-with-in-nested-fn.js": "CRASH", "language/expressions/async-generator/named-unscopables-with.js": "CRASH", - "language/expressions/async-generator/named-yield-identifier-non-strict.js": "CRASH", "language/expressions/async-generator/named-yield-identifier-spread-non-strict.js": "CRASH", - "language/expressions/async-generator/named-yield-promise-reject-next-catch.js": "CRASH", "language/expressions/async-generator/named-yield-promise-reject-next-for-await-of-async-iterator.js": "CRASH", "language/expressions/async-generator/named-yield-promise-reject-next-for-await-of-sync-iterator.js": "CRASH", "language/expressions/async-generator/named-yield-promise-reject-next-yield-star-async-iterator.js": "CRASH", "language/expressions/async-generator/named-yield-promise-reject-next-yield-star-sync-iterator.js": "CRASH", - "language/expressions/async-generator/named-yield-promise-reject-next.js": "CRASH", "language/expressions/async-generator/named-yield-spread-arr-multiple.js": "CRASH", "language/expressions/async-generator/named-yield-spread-arr-single.js": "CRASH", "language/expressions/async-generator/named-yield-spread-obj.js": "CRASH", @@ -12170,18 +11832,13 @@ "language/expressions/async-generator/named-yield-star-sync-next.js": "CRASH", "language/expressions/async-generator/named-yield-star-sync-return.js": "CRASH", "language/expressions/async-generator/named-yield-star-sync-throw.js": "CRASH", - "language/expressions/async-generator/params-trailing-comma-multiple.js": "CRASH", - "language/expressions/async-generator/params-trailing-comma-single.js": "CRASH", "language/expressions/async-generator/unscopables-with-in-nested-fn.js": "CRASH", "language/expressions/async-generator/unscopables-with.js": "CRASH", - "language/expressions/async-generator/yield-identifier-non-strict.js": "CRASH", "language/expressions/async-generator/yield-identifier-spread-non-strict.js": "CRASH", - "language/expressions/async-generator/yield-promise-reject-next-catch.js": "CRASH", "language/expressions/async-generator/yield-promise-reject-next-for-await-of-async-iterator.js": "CRASH", "language/expressions/async-generator/yield-promise-reject-next-for-await-of-sync-iterator.js": "CRASH", "language/expressions/async-generator/yield-promise-reject-next-yield-star-async-iterator.js": "CRASH", "language/expressions/async-generator/yield-promise-reject-next-yield-star-sync-iterator.js": "CRASH", - "language/expressions/async-generator/yield-promise-reject-next.js": "CRASH", "language/expressions/async-generator/yield-spread-arr-multiple.js": "CRASH", "language/expressions/async-generator/yield-spread-arr-single.js": "CRASH", "language/expressions/async-generator/yield-spread-obj.js": "CRASH", @@ -15555,180 +15212,58 @@ "language/expressions/object/computed-property-name-topropertykey-before-value-evaluation.js": "FAIL", "language/expressions/object/concise-generator.js": "CRASH", "language/expressions/object/cpn-obj-lit-computed-property-name-from-assignment-expression-logical-and.js": "FAIL", - "language/expressions/object/dstr/async-gen-meth-ary-init-iter-close.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-init-iter-get-err-array-prototype.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-init-iter-get-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-init-iter-no-close.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-name-iter-val.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-undef.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-iter-done.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-obj-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-obj-val-null.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elision-exhausted.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elision-step-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-elision.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-empty.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-ary-elem.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-ary-elision.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-ary-empty.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-ary-rest.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-id-direct.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-id-elision.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-id-exhausted.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-obj-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-ary-ptrn-rest-obj-prop-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-init-iter-close.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-init-iter-get-err-array-prototype.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-init-iter-get-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-init-iter-no-close.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-name-iter-val.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-undef.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-iter-done.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-obj-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-obj-val-null.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elision-exhausted.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elision-step-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-elision.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-empty.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-ary-elem.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-ary-elision.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-ary-empty.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-ary-rest.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-id-direct.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-id-elision.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-id-exhausted.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-obj-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-ary-ptrn-rest-obj-prop-id.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-init-null.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-init-undefined.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-empty.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-get-value-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-skipped.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-throws.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-id-trailing-comma.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-list-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-ary-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-ary.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-eval-err.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-id-init-throws.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-id-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-obj-init.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-obj-value-null.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-prop-obj.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-rest-getter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-dflt-obj-ptrn-rest-val-obj.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-init-null.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-init-undefined.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-empty.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-get-value-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-skipped.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-throws.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-id-trailing-comma.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-list-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-ary-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-ary.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-eval-err.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-id-init-throws.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-id-init.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-id.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-obj-init.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-obj-value-null.js": "CRASH", "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-prop-obj.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-rest-getter.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/expressions/object/dstr/async-gen-meth-obj-ptrn-rest-val-obj.js": "CRASH", "language/expressions/object/dstr/gen-meth-ary-init-iter-get-err-array-prototype.js": "CRASH", "language/expressions/object/dstr/gen-meth-ary-init-iter-get-err.js": "CRASH", "language/expressions/object/dstr/gen-meth-ary-ptrn-elem-ary-val-null.js": "CRASH", @@ -15789,23 +15324,14 @@ "language/expressions/object/identifier-shorthand-static-init-await-valid.js": "FAIL", "language/expressions/object/literal-property-name-bigint.js": "CRASH", "language/expressions/object/method-definition/async-gen-meth-dflt-params-abrupt.js": "CRASH", - "language/expressions/object/method-definition/async-gen-meth-dflt-params-arg-val-not-undefined.js": "CRASH", - "language/expressions/object/method-definition/async-gen-meth-dflt-params-arg-val-undefined.js": "CRASH", "language/expressions/object/method-definition/async-gen-meth-dflt-params-ref-later.js": "CRASH", - "language/expressions/object/method-definition/async-gen-meth-dflt-params-ref-prior.js": "CRASH", "language/expressions/object/method-definition/async-gen-meth-dflt-params-ref-self.js": "CRASH", - "language/expressions/object/method-definition/async-gen-meth-dflt-params-trailing-comma.js": "CRASH", "language/expressions/object/method-definition/async-gen-meth-eval-var-scope-syntax-err.js": "CRASH", - "language/expressions/object/method-definition/async-gen-meth-params-trailing-comma-multiple.js": "CRASH", - "language/expressions/object/method-definition/async-gen-meth-params-trailing-comma-single.js": "CRASH", - "language/expressions/object/method-definition/async-gen-yield-identifier-non-strict.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-identifier-spread-non-strict.js": "CRASH", - "language/expressions/object/method-definition/async-gen-yield-promise-reject-next-catch.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-promise-reject-next-for-await-of-async-iterator.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-promise-reject-next-for-await-of-sync-iterator.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-promise-reject-next-yield-star-async-iterator.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-promise-reject-next-yield-star-sync-iterator.js": "CRASH", - "language/expressions/object/method-definition/async-gen-yield-promise-reject-next.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-spread-arr-multiple.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-spread-arr-single.js": "CRASH", "language/expressions/object/method-definition/async-gen-yield-spread-obj.js": "CRASH", @@ -15871,11 +15397,6 @@ "language/expressions/object/method-definition/async-super-call-param.js": "CRASH", "language/expressions/object/method-definition/early-errors-object-async-method-duplicate-parameters.js": "FAIL", "language/expressions/object/method-definition/early-errors-object-method-duplicate-parameters.js": "FAIL", - "language/expressions/object/method-definition/forbidden-ext/b1/async-gen-meth-forbidden-ext-direct-access-prop-arguments.js": "CRASH", - "language/expressions/object/method-definition/forbidden-ext/b1/async-gen-meth-forbidden-ext-direct-access-prop-caller.js": "CRASH", - "language/expressions/object/method-definition/forbidden-ext/b2/async-gen-meth-forbidden-ext-indirect-access-own-prop-caller-get.js": "CRASH", - "language/expressions/object/method-definition/forbidden-ext/b2/async-gen-meth-forbidden-ext-indirect-access-own-prop-caller-value.js": "CRASH", - "language/expressions/object/method-definition/forbidden-ext/b2/async-gen-meth-forbidden-ext-indirect-access-prop-caller.js": "CRASH", "language/expressions/object/method-definition/gen-meth-dflt-params-abrupt.js": "CRASH", "language/expressions/object/method-definition/gen-meth-dflt-params-ref-later.js": "CRASH", "language/expressions/object/method-definition/gen-meth-dflt-params-ref-self.js": "CRASH", @@ -15909,28 +15430,24 @@ "language/expressions/postfix-decrement/S11.3.2_A5_T3.js": "CRASH", "language/expressions/postfix-decrement/S11.3.2_A6_T1.js": "FAIL", "language/expressions/postfix-decrement/S11.3.2_A6_T2.js": "FAIL", - "language/expressions/postfix-decrement/bigint.js": "CRASH", "language/expressions/postfix-decrement/operator-x-postfix-decrement-calls-putvalue-lhs-newvalue-.js": "CRASH", "language/expressions/postfix-increment/S11.3.1_A5_T1.js": "CRASH", "language/expressions/postfix-increment/S11.3.1_A5_T2.js": "CRASH", "language/expressions/postfix-increment/S11.3.1_A5_T3.js": "CRASH", "language/expressions/postfix-increment/S11.3.1_A6_T1.js": "FAIL", "language/expressions/postfix-increment/S11.3.1_A6_T2.js": "FAIL", - "language/expressions/postfix-increment/bigint.js": "CRASH", "language/expressions/postfix-increment/operator-x-postfix-increment-calls-putvalue-lhs-newvalue-.js": "CRASH", "language/expressions/prefix-decrement/S11.4.5_A5_T1.js": "CRASH", "language/expressions/prefix-decrement/S11.4.5_A5_T2.js": "CRASH", "language/expressions/prefix-decrement/S11.4.5_A5_T3.js": "CRASH", "language/expressions/prefix-decrement/S11.4.5_A6_T1.js": "FAIL", "language/expressions/prefix-decrement/S11.4.5_A6_T2.js": "FAIL", - "language/expressions/prefix-decrement/bigint.js": "CRASH", "language/expressions/prefix-decrement/operator-prefix-decrement-x-calls-putvalue-lhs-newvalue-.js": "CRASH", "language/expressions/prefix-increment/S11.4.4_A5_T1.js": "CRASH", "language/expressions/prefix-increment/S11.4.4_A5_T2.js": "CRASH", "language/expressions/prefix-increment/S11.4.4_A5_T3.js": "CRASH", "language/expressions/prefix-increment/S11.4.4_A6_T1.js": "FAIL", "language/expressions/prefix-increment/S11.4.4_A6_T2.js": "FAIL", - "language/expressions/prefix-increment/bigint.js": "CRASH", "language/expressions/prefix-increment/operator-prefix-increment-x-calls-putvalue-lhs-newvalue-.js": "CRASH", "language/expressions/property-accessors/S11.2.1_A4_T9.js": "FAIL", "language/expressions/right-shift/bigint-non-primitive.js": "CRASH", @@ -21443,8 +20960,6 @@ "staging/sm/Array/change-array-by-copy-cross-compartment-create.js": "FAIL", "staging/sm/Array/change-array-by-copy-errors-from-correct-realm.js": "FAIL", "staging/sm/Array/fill.js": "CRASH", - "staging/sm/Array/from-iterator-close.js": "CRASH", - "staging/sm/Array/from_errors.js": "FAIL", "staging/sm/Array/from_primitive.js": "CRASH", "staging/sm/Array/from_string.js": "CRASH", "staging/sm/Array/from_this.js": "CRASH", diff --git a/tests/metrics.json b/tests/metrics.json index bc98eae71..edde49973 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { - "crash": 13216, - "fail": 8877, - "pass": 24643, + "crash": 13224, + "fail": 8384, + "pass": 25128, "skip": 65, "timeout": 0, "unresolved": 0