diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cfeccef7b..542d4c440 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ said, before you being, you'll want to know a few things. evil or anything, he's just an idiot. 2. Nova's code follows the ECMAScript specification. When in doubt, read, copy, implement the specification. -3. Testing is mainly based upon the test262 conformance suit. Updating +3. Testing is mainly based upon the test262 conformance suite. Updating conformance results needs to be done. More information is found below. @@ -456,3 +456,14 @@ Some more long-term prospects and/or wild ideas: Nightly features). But it would mean that returning a (sort of) `Result` would fit in a register. - Consider a register based VM instead of going stack based + +# Running the test262 suite +1. Clone this repository with submodules: + + `git clone --recurse-submodules git@github.com:trynova/nova.git` + +2. Execute the test262 runner: + + `cargo build -p nova_cli && cargo run --bin test262` + + **Important:** The test runner executes the compiled `nova_cli` directly. If you have made changes to CLI or VM, ensure that the `nova_cli` target is rebuilt before executing the test runner. \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 605e2b333..14a82e87c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,26 +4,26 @@ members = ["nova_cli", "nova_vm", "common", "small_string", "tests"] [workspace.dependencies] ahash = "0.8.11" -clap = { version = "4.5.18", features = ["derive"] } +clap = { version = "4.5.21", features = ["derive"] } cliclack = "0.3.5" console = "0.15.8" ctrlc = "3.4.5" fast-float = "0.2.0" -hashbrown = "0.14.5" -miette = { version = "7.2.0", features = ["fancy"] } +hashbrown = "0.15.1" num-bigint = "0.4.6" num-traits = "0.2.19" -oxc_allocator = "0.34.0" -oxc_ast = "0.34.0" -oxc_diagnostics = "0.34.0" -oxc_parser = "0.34.0" -oxc_semantic = "0.34.0" -oxc_span = "0.34.0" -oxc_syntax = "0.34.0" -oxc_ecmascript = "0.34.0" +oxc_allocator = "0.36.0" +oxc_ast = "0.36.0" +oxc_diagnostics = "0.36.0" +oxc-miette = { version = "1.0.2", features = ["fancy"] } +oxc_parser = "0.36.0" +oxc_semantic = "0.36.0" +oxc_span = "0.36.0" +oxc_syntax = "0.36.0" +oxc_ecmascript = "0.36.0" rand = "0.8.5" ryu-js = "1.0.1" -sonic-rs = "0.3.13" +sonic-rs = "0.3.16" unicode-normalization = "0.1.24" wtf8 = "0.1" fast_float = "0.2.0" diff --git a/README.md b/README.md index b1b66e7fc..28eda6e46 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ execution model is currently greatly inspired by [SerenityOS's LibJS](https://github.com/SerenityOS/serenity). See the code for more details. -The core of our team is on our [Discord server](https://discord.gg/RTrgJzXKUM). +The core of our team is on our [Discord server](https://discord.gg/bwY4TRB8J7). ## Talks diff --git a/nova_cli/Cargo.toml b/nova_cli/Cargo.toml index be0258da3..c114c0363 100644 --- a/nova_cli/Cargo.toml +++ b/nova_cli/Cargo.toml @@ -9,9 +9,9 @@ clap = { workspace = true} cliclack = { workspace = true } ctrlc = { workspace = true } console = { workspace = true } -miette = { workspace = true } nova_vm = { path = "../nova_vm" } oxc_ast = { workspace = true } +oxc-miette = { workspace = true } oxc_parser = { workspace = true } oxc_semantic = { workspace = true } oxc_span = { workspace = true } diff --git a/nova_cli/src/helper.rs b/nova_cli/src/helper.rs index 875812558..2a74b59e3 100644 --- a/nova_cli/src/helper.rs +++ b/nova_cli/src/helper.rs @@ -12,7 +12,6 @@ pub fn initialize_global_object(agent: &mut Agent, mut gc: GcScope<'_, '_>, glob fn print( agent: &mut Agent, gc: GcScope<'_, '_>, - _this: Value, args: ArgumentsList, ) -> JsResult { @@ -26,32 +25,36 @@ pub fn initialize_global_object(agent: &mut Agent, mut gc: GcScope<'_, '_>, glob // 'readTextFile' function fn read_text_file( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, _: Value, args: ArgumentsList, ) -> JsResult { if args.len() != 1 { - return Err(agent - .throw_exception_with_static_message(ExceptionType::Error, "Expected 1 argument")); + return Err(agent.throw_exception_with_static_message( + gc.nogc(), + ExceptionType::Error, + "Expected 1 argument", + )); } let Ok(path) = String::try_from(args.get(0)) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::Error, "Expected a string argument", )); }; let file = std::fs::read_to_string(path.as_str(agent)) - .map_err(|e| agent.throw_exception(ExceptionType::Error, e.to_string()))?; - Ok(String::from_string(agent, file).into_value()) + .map_err(|e| agent.throw_exception(gc.nogc(), ExceptionType::Error, e.to_string()))?; + Ok(String::from_string(agent, gc.nogc(), file).into_value()) } let function = create_builtin_function( agent, + gc.nogc(), Behaviour::Regular(print), BuiltinFunctionArgs::new(1, "print", agent.current_realm_id()), ); - let property_key = PropertyKey::from_static_str(agent, "print"); + let property_key = PropertyKey::from_static_str(agent, gc.nogc(), "print"); global .internal_define_own_property( agent, @@ -69,10 +72,11 @@ pub fn initialize_global_object(agent: &mut Agent, mut gc: GcScope<'_, '_>, glob let function = create_builtin_function( agent, + gc.nogc(), Behaviour::Regular(read_text_file), BuiltinFunctionArgs::new(1, "readTextFile", agent.current_realm_id()), ); - let property_key = PropertyKey::from_static_str(agent, "readTextFile"); + let property_key = PropertyKey::from_static_str(agent, gc.nogc(), "readTextFile"); global .internal_define_own_property( agent, diff --git a/nova_cli/src/main.rs b/nova_cli/src/main.rs index b985c7f4f..5ff76d7aa 100644 --- a/nova_cli/src/main.rs +++ b/nova_cli/src/main.rs @@ -146,9 +146,15 @@ fn main() -> Result<(), Box> { |agent, mut gc| -> Result<(), Box> { let realm = agent.current_realm_id(); let file = std::fs::read_to_string(&path)?; - let source_text = JsString::from_string(agent, file); - let script = match parse_script(agent, source_text, realm, !no_strict, None) - { + let source_text = JsString::from_string(agent, gc.nogc(), file); + let script = match parse_script( + agent, + gc.nogc(), + source_text, + realm, + !no_strict, + None, + ) { Ok(script) => script, Err(errors) => { // Borrow the string data from the Agent @@ -228,13 +234,14 @@ fn main() -> Result<(), Box> { placeholder = input.to_string(); agent.run_in_realm(&realm, |agent, mut gc| { let realm = agent.current_realm_id(); - let source_text = JsString::from_string(agent, input); - let script = match parse_script(agent, source_text, realm, true, None) { - Ok(script) => script, - Err(errors) => { - exit_with_parse_errors(errors, "", &placeholder); - } - }; + let source_text = JsString::from_string(agent, gc.nogc(), input); + let script = + match parse_script(agent, gc.nogc(), source_text, realm, true, None) { + Ok(script) => script, + Err(errors) => { + exit_with_parse_errors(errors, "", &placeholder); + } + }; let result = script_evaluation(agent, gc.reborrow(), script); match result { Ok(result) => { diff --git a/nova_vm/build.rs b/nova_vm/build.rs index 9bff77a0b..1193dcd3f 100644 --- a/nova_vm/build.rs +++ b/nova_vm/build.rs @@ -62,7 +62,7 @@ fn gen_builtin_strings() -> io::Result> { output.push_str(" /// ```\n"); output.push_str(" pub r#"); output.push_str(&replace_invalid_key_characters(string)); - output.push_str(": String,\n"); + output.push_str(": String<'static>,\n"); } output.push_str("}\n\npub const BUILTIN_STRING_MEMORY: BuiltinStrings = BuiltinStrings {\n"); let mut i: u32 = 0; 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 d10f6f4b0..592b68bd2 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 @@ -39,7 +39,6 @@ pub(crate) struct IteratorRecord { pub(crate) fn get_iterator_from_method( agent: &mut Agent, mut gc: GcScope<'_, '_>, - obj: Value, method: Function, ) -> JsResult { @@ -47,8 +46,9 @@ pub(crate) fn get_iterator_from_method( let iterator = call(agent, gc.reborrow(), method.into(), obj, None)?; // 2. If iterator is not an Object, throw a TypeError exception. - let Ok(iterator) = to_object(agent, iterator) else { + let Ok(iterator) = to_object(agent, gc.nogc(), iterator) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Iterator is not an object", )); @@ -79,7 +79,6 @@ pub(crate) fn get_iterator_from_method( pub(crate) fn get_iterator( agent: &mut Agent, mut gc: GcScope<'_, '_>, - obj: Value, is_async: bool, ) -> JsResult { @@ -105,6 +104,7 @@ pub(crate) fn get_iterator( else { // ii. If syncMethod is undefined, throw a TypeError exception. return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "No iterator on object", )); @@ -133,6 +133,7 @@ pub(crate) fn get_iterator( // 3. If method is undefined, throw a TypeError exception. let Some(method) = method else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Iterator method cannot be undefined", )); @@ -151,7 +152,6 @@ pub(crate) fn get_iterator( pub(crate) fn iterator_next( agent: &mut Agent, mut gc: GcScope<'_, '_>, - iterator_record: &IteratorRecord, value: Option, ) -> JsResult { @@ -174,6 +174,7 @@ pub(crate) fn iterator_next( result .try_into() .or(Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "The iterator result was not an object", ))) @@ -187,7 +188,6 @@ pub(crate) fn iterator_next( pub(crate) fn iterator_complete( agent: &mut Agent, mut gc: GcScope<'_, '_>, - iter_result: Object, ) -> JsResult { // 1. Return ToBoolean(? Get(iterResult, "done")). @@ -208,7 +208,6 @@ pub(crate) fn iterator_complete( pub(crate) fn iterator_value( agent: &mut Agent, mut gc: GcScope<'_, '_>, - iter_result: Object, ) -> JsResult { // 1. Return ? Get(iterResult, "value"). @@ -235,7 +234,6 @@ pub(crate) fn iterator_value( pub(crate) fn iterator_step( agent: &mut Agent, mut gc: GcScope<'_, '_>, - iterator_record: &IteratorRecord, ) -> JsResult> { // 1. Let result be ? IteratorNext(iteratorRecord). @@ -264,7 +262,6 @@ pub(crate) fn iterator_step( pub(crate) fn iterator_step_value( agent: &mut Agent, mut gc: GcScope<'_, '_>, - iterator_record: &mut IteratorRecord, ) -> JsResult> { // 1. Let result be Completion(IteratorNext(iteratorRecord)). @@ -336,7 +333,6 @@ pub(crate) fn iterator_step_value( pub(crate) fn iterator_close( agent: &mut Agent, mut gc: GcScope<'_, '_>, - iterator_record: &IteratorRecord, completion: JsResult, ) -> JsResult { @@ -376,6 +372,7 @@ pub(crate) fn iterator_close( // 7. If innerResult.[[Value]] is not an Object, throw a TypeError exception. if !inner_result.is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid iterator 'return' method return value", )); @@ -392,7 +389,6 @@ pub(crate) fn iterator_close( pub(crate) fn if_abrupt_close_iterator( agent: &mut Agent, gc: GcScope<'_, '_>, - value: JsResult, iterator_record: &IteratorRecord, ) -> JsResult { @@ -416,7 +412,6 @@ pub(crate) fn if_abrupt_close_iterator( pub(crate) fn async_iterator_close( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _iterator_record: &IteratorRecord, _completion: JsResult, ) -> JsResult { @@ -469,7 +464,6 @@ pub(crate) fn create_iter_result_object(agent: &mut Agent, value: Value, done: b pub(crate) fn create_list_iterator_record( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _list: &[Value], ) -> JsResult { // 1. Let closure be a new Abstract Closure with no parameters that captures list and performs the following steps when called: @@ -489,7 +483,6 @@ pub(crate) fn create_list_iterator_record( pub(crate) fn iterator_to_list( agent: &mut Agent, mut gc: GcScope<'_, '_>, - iterator_record: &IteratorRecord, ) -> JsResult> { // 1. Let values be a new empty List. 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 8e87eb8eb..26a40fe1d 100644 --- a/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs +++ b/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs @@ -11,7 +11,7 @@ use super::{ testing_and_comparison::{is_callable, require_object_coercible, same_value}, type_conversion::{to_length, to_object, to_property_key}, }; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_iterator_objects::iterator_step_value, @@ -71,7 +71,6 @@ pub(crate) fn make_basic_object(_agent: &mut Agent, _internal_slots_list: ()) -> pub(crate) fn get( agent: &mut Agent, gc: GcScope<'_, '_>, - o: impl IntoObject, p: PropertyKey, ) -> JsResult { @@ -90,12 +89,11 @@ pub(crate) fn get( pub(crate) fn get_v( agent: &mut Agent, gc: GcScope<'_, '_>, - v: Value, p: PropertyKey, ) -> JsResult { // 1. Let O be ? ToObject(V). - let o = to_object(agent, v)?; + let o = to_object(agent, gc.nogc(), v)?; // 2. Return ? O.[[Get]](P, V). o.internal_get(agent, gc, p, o.into()) } @@ -109,18 +107,18 @@ pub(crate) fn get_v( /// value for the property. pub(crate) fn set( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, o: Object, p: PropertyKey, v: Value, throw: bool, ) -> JsResult<()> { // 1. Let success be ? O.[[Set]](P, V, O). - let success = o.internal_set(agent, gc, p, v, o.into_value())?; + let success = o.internal_set(agent, gc.reborrow(), p, v, o.into_value())?; // 2. If success is false and Throw is true, throw a TypeError exception. if !success && throw { return Err(agent.throw_exception( + gc.nogc(), ExceptionType::TypeError, format!("Could not set property '{}'.", p.as_display(agent)), )); @@ -144,7 +142,6 @@ pub(crate) fn set( pub(crate) fn create_data_property( agent: &mut Agent, gc: GcScope<'_, '_>, - object: impl InternalMethods, property_key: PropertyKey, value: Value, @@ -171,15 +168,15 @@ pub(crate) fn create_data_property( /// exception if the requested property update cannot be performed. pub(crate) fn create_data_property_or_throw( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, object: impl InternalMethods, property_key: PropertyKey, value: Value, ) -> JsResult<()> { - let success = create_data_property(agent, gc, object, property_key, value)?; + let success = create_data_property(agent, gc.reborrow(), object, property_key, value)?; if !success { Err(agent.throw_exception( + gc.nogc(), ExceptionType::TypeError, format!( "Could not create property '{}'.", @@ -201,17 +198,17 @@ pub(crate) fn create_data_property_or_throw( /// cannot be performed. pub(crate) fn define_property_or_throw( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, object: impl InternalMethods, property_key: PropertyKey, desc: PropertyDescriptor, ) -> JsResult<()> { // 1. Let success be ? O.[[DefineOwnProperty]](P, desc). - let success = object.internal_define_own_property(agent, gc, property_key, desc)?; + let success = object.internal_define_own_property(agent, gc.reborrow(), property_key, desc)?; // 2. If success is false, throw a TypeError exception. if !success { Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Failed to defined property on object", )) @@ -229,16 +226,16 @@ pub(crate) fn define_property_or_throw( /// of an object. It throws an exception if the property is not configurable. pub(crate) fn delete_property_or_throw( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, o: Object, p: PropertyKey, ) -> JsResult<()> { // 1. Let success be ? O.[[Delete]](P). - let success = o.internal_delete(agent, gc, p)?; + let success = o.internal_delete(agent, gc.reborrow(), p)?; // 2. If success is false, throw a TypeError exception. if !success { Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Failed to delete property", )) @@ -258,13 +255,12 @@ pub(crate) fn delete_property_or_throw( pub(crate) fn get_method( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, v: Value, p: PropertyKey, ) -> JsResult> { // 1. Let func be ? GetV(V, P). - let func = get_v(agent, gc, v, p)?; + let func = get_v(agent, gc.reborrow(), v, p)?; // 2. If func is either undefined or null, return undefined. if func.is_undefined() || func.is_null() { return Ok(None); @@ -273,6 +269,7 @@ pub(crate) fn get_method( let func = is_callable(func); if func.is_none() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Not a callable object", )); @@ -291,7 +288,6 @@ pub(crate) fn get_method( pub(crate) fn has_property( agent: &mut Agent, gc: GcScope<'_, '_>, - o: Object, p: PropertyKey, ) -> JsResult { @@ -308,7 +304,6 @@ pub(crate) fn has_property( pub(crate) fn has_own_property( agent: &mut Agent, gc: GcScope<'_, '_>, - o: Object, p: PropertyKey, ) -> JsResult { @@ -333,7 +328,6 @@ pub(crate) fn has_own_property( pub(crate) fn call( agent: &mut Agent, gc: GcScope<'_, '_>, - f: Value, v: Value, arguments_list: Option, @@ -343,11 +337,17 @@ pub(crate) fn call( // 2. If IsCallable(F) is false, throw a TypeError exception. match is_callable(f) { None => Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Not a callable object", )), // 3. Return ? F.[[Call]](V, argumentsList). - Some(f) => f.internal_call(agent, gc, v, arguments_list), + Some(f) => { + let current_stack_size = agent.stack_refs.borrow().len(); + let result = f.internal_call(agent, gc, v, arguments_list); + agent.stack_refs.borrow_mut().truncate(current_stack_size); + result + } } } @@ -385,7 +385,6 @@ pub(crate) mod integrity { pub(crate) fn set_integrity_level( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Object, ) -> JsResult { // 1. Let status be ? O.[[PreventExtensions]](). @@ -455,7 +454,6 @@ pub(crate) fn set_integrity_level( pub(crate) fn test_integrity_level( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Object, ) -> JsResult { // 1. Let extensible be ? IsExtensible(O). @@ -495,10 +493,14 @@ pub(crate) fn test_integrity_level( /// The abstract operation CreateArrayFromList takes argument elements (a List /// of ECMAScript language values) and returns an Array. It is used to create /// an Array whose elements are provided by elements. -pub(crate) fn create_array_from_list(agent: &mut Agent, elements: &[Value]) -> Array { +pub(crate) fn create_array_from_list( + agent: &mut Agent, + gc: NoGcScope, + elements: &[Value], +) -> Array { let len = elements.len(); // 1. Let array be ! ArrayCreate(0). - let array = array_create(agent, len, len, None).unwrap(); + let array = array_create(agent, gc, len, len, None).unwrap(); let array_elements = agent[array].elements; agent[array_elements] .copy_from_slice(unsafe { std::mem::transmute::<&[Value], &[Option]>(elements) }); @@ -519,7 +521,6 @@ pub(crate) fn create_array_from_list(agent: &mut Agent, elements: &[Value]) -> A pub(crate) fn length_of_array_like( agent: &mut Agent, mut gc: GcScope<'_, '_>, - obj: Object, ) -> JsResult { // NOTE: Fast path for Array objects. @@ -550,7 +551,6 @@ pub(crate) fn length_of_array_like( pub(crate) fn create_list_from_array_like( agent: &mut Agent, mut gc: GcScope<'_, '_>, - obj: Value, ) -> JsResult> { match obj { @@ -586,10 +586,11 @@ pub(crate) fn create_list_from_array_like( Ok(list) } // 2. If obj is not an Object, throw a TypeError exception. - _ => { - Err(agent - .throw_exception_with_static_message(ExceptionType::TypeError, "Not an object")) - } + _ => Err(agent.throw_exception_with_static_message( + gc.nogc(), + ExceptionType::TypeError, + "Not an object", + )), } } @@ -597,19 +598,20 @@ pub(crate) fn create_list_from_array_like( pub(crate) fn call_function( agent: &mut Agent, gc: GcScope<'_, '_>, - f: Function, v: Value, arguments_list: Option, ) -> JsResult { let arguments_list = arguments_list.unwrap_or_default(); - f.internal_call(agent, gc, v, arguments_list) + let current_stack_size = agent.stack_refs.borrow().len(); + let result = f.internal_call(agent, gc, v, arguments_list); + agent.stack_refs.borrow_mut().truncate(current_stack_size); + result } pub(crate) fn construct( agent: &mut Agent, gc: GcScope<'_, '_>, - f: Function, arguments_list: Option, new_target: Option, @@ -634,7 +636,6 @@ pub(crate) fn construct( pub(crate) fn invoke( agent: &mut Agent, mut gc: GcScope<'_, '_>, - v: Value, p: PropertyKey, arguments_list: Option, @@ -657,7 +658,6 @@ pub(crate) fn invoke( pub(crate) fn ordinary_has_instance( agent: &mut Agent, mut gc: GcScope<'_, '_>, - c: impl TryInto, o: impl IntoValue, ) -> JsResult { @@ -682,6 +682,7 @@ pub(crate) fn ordinary_has_instance( // 5. If P is not an Object, throw a TypeError exception. let Ok(p) = Object::try_from(p) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Non-object prototype found", )); @@ -743,7 +744,6 @@ pub(crate) mod enumerable_properties_kind { pub(crate) fn enumerable_own_properties( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Object, ) -> JsResult> { // 1. Let ownKeys be ? O.[[OwnPropertyKeys]](). @@ -773,7 +773,7 @@ pub(crate) fn enumerable_own_properties( } PropertyKey::Integer(int) => { let int = int.into_i64(); - String::from_string(agent, format!("{}", int)) + String::from_string(agent, gc.nogc(), format!("{}", int)) } PropertyKey::SmallString(str) => str.into(), PropertyKey::String(str) => str.into(), @@ -797,13 +797,14 @@ pub(crate) fn enumerable_own_properties( } PropertyKey::Integer(int) => { let int = int.into_i64(); - String::from_string(agent, format!("{}", int)) + String::from_string(agent, gc.nogc(), format!("{}", int)) } PropertyKey::SmallString(str) => str.into(), PropertyKey::String(str) => str.into(), }; // ii. Let entry be CreateArrayFromList(« key, value »). - let entry = create_array_from_list(agent, &[key_value.into_value(), value]); + let entry = + create_array_from_list(agent, gc.nogc(), &[key_value.into_value(), value]); // iii. Append entry to results. results.push(entry.into_value()); } @@ -857,7 +858,6 @@ pub(crate) fn get_function_realm( pub(crate) fn copy_data_properties( agent: &mut Agent, mut gc: GcScope<'_, '_>, - target: OrdinaryObject, source: Value, ) -> JsResult<()> { @@ -866,7 +866,7 @@ pub(crate) fn copy_data_properties( return Ok(()); } // 2. Let from be ! ToObject(source). - let from = to_object(agent, source).unwrap(); + let from = to_object(agent, gc.nogc(), source).unwrap(); // 3. Let keys be ? from.[[OwnPropertyKeys]](). let keys = from.internal_own_property_keys(agent, gc.reborrow())?; @@ -913,7 +913,6 @@ pub(crate) fn copy_data_properties( pub(crate) fn copy_data_properties_into_object( agent: &mut Agent, mut gc: GcScope<'_, '_>, - source: impl IntoObject, excluded_items: &AHashSet, ) -> JsResult { @@ -962,7 +961,6 @@ pub(crate) fn copy_data_properties_into_object( pub(crate) fn initialize_instance_elements( agent: &mut Agent, gc: GcScope<'_, '_>, - o: Object, constructor: BuiltinConstructorFunction, ) -> JsResult<()> { @@ -1065,16 +1063,16 @@ pub(crate) struct GroupByRecord> { pub(crate) fn group_by_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - items: Value, callback_fn: Value, ) -> JsResult>> { // 1. Perform ? RequireObjectCoercible(iterable). - require_object_coercible(agent, items)?; + require_object_coercible(agent, gc.nogc(), items)?; // 2. If IsCallable(callback) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback is not callable", )); @@ -1096,6 +1094,7 @@ pub(crate) fn group_by_property( if k >= u32::MAX as usize { // i. Let error be ThrowCompletion(a newly created TypeError object). let error = agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Maximum array size of 2**53-1 exceeded", ); @@ -1158,16 +1157,16 @@ pub(crate) fn group_by_property( pub(crate) fn group_by_collection( agent: &mut Agent, mut gc: GcScope<'_, '_>, - items: Value, callback_fn: Value, ) -> JsResult>> { // 1. Perform ? RequireObjectCoercible(iterable). - require_object_coercible(agent, items)?; + require_object_coercible(agent, gc.nogc(), items)?; // 2. If IsCallable(callback) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback is not callable", )); @@ -1189,6 +1188,7 @@ pub(crate) fn group_by_collection( if k >= u32::MAX as usize { // i. Let error be ThrowCompletion(a newly created TypeError object). let error = agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Maximum array size of 2**53-1 exceeded", ); 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 43f51ff42..fdedd8eb4 100644 --- a/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs +++ b/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs @@ -4,7 +4,7 @@ //! ## [7.2 Testing and Comparison Operations](https://tc39.es/ecma262/#sec-testing-and-comparison-operations) -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_numeric, @@ -25,9 +25,14 @@ use super::type_conversion::{string_to_big_int, to_number, to_primitive, Preferr /// 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(agent: &mut Agent, argument: Value) -> JsResult { +pub(crate) fn require_object_coercible( + agent: &mut Agent, + gc: NoGcScope, + argument: Value, +) -> JsResult { if argument.is_undefined() || argument.is_null() { Err(agent.throw_exception_with_static_message( + gc, ExceptionType::TypeError, "Argument cannot be converted into an object", )) @@ -125,7 +130,6 @@ pub(crate) fn is_same_type, V2: Copy + Into>(x: V1 pub(crate) fn is_integral_number( agent: &mut Agent, gc: GcScope<'_, '_>, - argument: impl Copy + Into, ) -> bool { let argument = argument.into(); @@ -264,7 +268,6 @@ pub(crate) fn same_value_non_number>( pub(crate) fn is_less_than( agent: &mut Agent, mut gc: GcScope<'_, '_>, - x: impl Into + Copy, y: impl Into + Copy, ) -> JsResult> { @@ -386,7 +389,6 @@ pub(crate) fn is_less_than( pub(crate) fn is_loosely_equal( agent: &mut Agent, mut gc: GcScope<'_, '_>, - x: impl Into + Copy, y: impl Into + Copy, ) -> JsResult { @@ -445,15 +447,13 @@ pub(crate) fn is_loosely_equal( // 9. If x is a Boolean, return ! IsLooselyEqual(! ToNumber(x), y). if let Ok(x) = bool::try_from(x) { - // TODO: We know GC cannot be triggered here. - let x = to_number(agent, gc.reborrow(), x).unwrap(); + let x = if x { 1 } else { 0 }; return Ok(is_loosely_equal(agent, gc, x, y).unwrap()); } // 10. If y is a Boolean, return ! IsLooselyEqual(x, ! ToNumber(y)). if let Ok(y) = bool::try_from(y) { - // TODO: We know GC cannot be triggered here. - let y = to_number(agent, gc.reborrow(), y).unwrap(); + let y = if y { 1 } else { 0 }; return Ok(is_loosely_equal(agent, gc, x, y).unwrap()); } diff --git a/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs b/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs index 488dc44c8..0a84868b3 100644 --- a/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs +++ b/nova_vm/src/ecmascript/abstract_operations/type_conversion.rs @@ -16,7 +16,8 @@ use num_bigint::Sign; -use crate::engine::context::GcScope; +use crate::ecmascript::types::IntoPrimitive; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ builtins::{ @@ -63,11 +64,10 @@ pub enum PreferredType { pub(crate) fn to_primitive( agent: &mut Agent, mut gc: GcScope<'_, '_>, - - input: impl Into + Copy, + input: impl IntoValue, preferred_type: Option, ) -> JsResult { - let input: Value = input.into(); + let input = input.into_value(); // 1. If input is an Object, then if let Ok(input) = Object::try_from(input) { // a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). @@ -94,7 +94,7 @@ pub(crate) fn to_primitive( // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result: Value = call_function( agent, - gc, + gc.reborrow(), exotic_to_prim, input.into(), Some(ArgumentsList(&[hint.into()])), @@ -103,6 +103,7 @@ pub(crate) fn to_primitive( Primitive::try_from(result).map_err(|_| { // vi. Throw a TypeError exception. agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid toPrimitive return value", ) @@ -123,6 +124,63 @@ pub(crate) fn to_primitive( } } +pub(crate) fn to_primitive_object( + agent: &mut Agent, + mut gc: GcScope<'_, '_>, + input: impl IntoObject, + preferred_type: Option, +) -> JsResult { + let input = input.into_object(); + // a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). + let exotic_to_prim = get_method( + agent, + gc.reborrow(), + input.into_value(), + PropertyKey::Symbol(WellKnownSymbolIndexes::ToPrimitive.into()), + )?; + // 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, + gc.reborrow(), + exotic_to_prim, + input.into(), + Some(ArgumentsList(&[hint.into()])), + )?; + // 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( + gc.nogc(), + ExceptionType::TypeError, + "Invalid toPrimitive return value", + ) + }) + } else { + // c. If preferredType is not present, let preferredType be NUMBER. + // d. Return ? OrdinaryToPrimitive(input, preferredType). + ordinary_to_primitive( + agent, + gc, + input, + preferred_type.unwrap_or(PreferredType::Number), + ) + } +} + /// ### [7.1.1.1 OrdinaryToPrimitive ( O, hint )](https://tc39.es/ecma262/#sec-ordinarytoprimitive) /// /// The abstract operation OrdinaryToPrimitive takes arguments O (an Object) @@ -131,7 +189,6 @@ pub(crate) fn to_primitive( pub(crate) fn ordinary_to_primitive( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Object, hint: PreferredType, ) -> JsResult { @@ -165,6 +222,7 @@ pub(crate) fn ordinary_to_primitive( } // 4. Throw a TypeError exception. Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Could not convert to primitive", )) @@ -199,8 +257,7 @@ pub(crate) fn to_boolean(agent: &Agent, argument: Value) -> bool { pub(crate) fn to_numeric( agent: &mut Agent, mut gc: GcScope<'_, '_>, - - value: impl Into + Copy, + value: impl IntoValue, ) -> JsResult { // 1. Let primValue be ? ToPrimitive(value, number). let prim_value = to_primitive(agent, gc.reborrow(), value, Some(PreferredType::Number))?; @@ -211,51 +268,75 @@ pub(crate) fn to_numeric( } // 3. Return ? ToNumber(primValue). - to_number(agent, gc, value).map(|n| n.into_numeric()) + to_number_primitive(agent, gc.nogc(), prim_value).map(|n| n.into_numeric()) +} + +pub(crate) fn try_to_number( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + argument: impl IntoValue, +) -> Option> { + let argument = argument.into_value(); + if let Ok(argument) = Primitive::try_from(argument) { + Some(to_number_primitive(agent, gc, argument)) + } else { + None + } } /// ### [7.1.4 ToNumber ( argument )](https://tc39.es/ecma262/#sec-tonumber) pub(crate) fn to_number( agent: &mut Agent, mut gc: GcScope<'_, '_>, - - argument: impl Into + Copy, + argument: impl IntoValue, ) -> JsResult { - let argument: Value = argument.into(); + let argument = argument.into_value(); + if let Ok(argument) = Primitive::try_from(argument) { + to_number_primitive(agent, gc.into_nogc(), argument) + } 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, gc.reborrow(), argument, Some(PreferredType::Number))?; + // 9. Assert: primValue is not an Object. + // 10. Return ? ToNumber(primValue). + to_number(agent, gc, prim_value) + } +} +/// ### [7.1.4 ToNumber ( argument )](https://tc39.es/ecma262/#sec-tonumber) +pub(crate) fn to_number_primitive( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + argument: Primitive, +) -> JsResult { match argument { // 3. If argument is undefined, return NaN. - Value::Undefined => Ok(Number::nan()), + Primitive::Undefined => Ok(Number::nan()), // 4. If argument is either null or false, return +0𝔽. - Value::Null | Value::Boolean(false) => Ok(Number::from(0)), + Primitive::Null | Primitive::Boolean(false) => Ok(Number::from(0)), // 5. If argument is true, return 1𝔽. - Value::Boolean(true) => Ok(Number::from(1)), + Primitive::Boolean(true) => Ok(Number::from(1)), // 6. If argument is a String, return StringToNumber(argument). - Value::String(str) => Ok(string_to_number(agent, str.into())), - Value::SmallString(str) => Ok(string_to_number(agent, str.into())), + Primitive::String(str) => Ok(string_to_number(agent, str.into())), + Primitive::SmallString(str) => Ok(string_to_number(agent, str.into())), // 2. If argument is either a Symbol or a BigInt, throw a TypeError exception. - Value::Symbol(_) => Err(agent.throw_exception_with_static_message( + Primitive::Symbol(_) => Err(agent.throw_exception_with_static_message( + gc, ExceptionType::TypeError, "cannot convert symbol to number", )), // 1. If argument is a Number, return argument. - Value::Number(idx) => Ok(idx.into()), - Value::Integer(idx) => Ok(idx.into()), - Value::SmallF64(idx) => Ok(idx.into()), - Value::BigInt(_) | Value::SmallBigInt(_) => Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "cannot convert bigint to number", - )), - _ => { - // 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(agent, gc.reborrow(), argument, Some(PreferredType::Number))?; - // 9. Assert: primValue is not an Object. - // 10. Return ? ToNumber(primValue). - to_number(agent, gc, prim_value) - } + Primitive::Number(idx) => Ok(idx.into()), + Primitive::Integer(idx) => Ok(idx.into()), + Primitive::SmallF64(idx) => Ok(idx.into()), + Primitive::BigInt(_) | Primitive::SmallBigInt(_) => Err(agent + .throw_exception_with_static_message( + gc, + ExceptionType::TypeError, + "cannot convert bigint to number", + )), } } @@ -336,7 +417,6 @@ fn string_to_number(agent: &mut Agent, str: String) -> Number { pub(crate) fn to_integer_or_infinity( agent: &mut Agent, gc: GcScope<'_, '_>, - argument: Value, ) -> JsResult { // Fast path: A safe integer is already an integer. @@ -365,6 +445,53 @@ pub(crate) fn to_integer_or_infinity( Ok(number.truncate(agent)) } +/// ### [7.1.5 ToIntegerOrInfinity ( argument )](https://tc39.es/ecma262/#sec-tointegerorinfinity) +// TODO: Should we add another [`Value`] newtype for IntegerOrInfinity? +// +// This version of the abstract operation attempts to convert the argument +// Value into an integer or infinity without calling any JavaScript code. If +// that cannot be done, `None` is returned. Note that the method can throw an +// error without calling any JavaScript code. +pub(crate) fn try_to_integer_or_infinity( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + argument: Value, +) -> Option> { + // Fast path: A safe integer is already an integer. + if let Value::Integer(int) = argument { + return Some(Ok(int.into())); + } + // 1. Let number be ? ToNumber(argument). + let Ok(argument) = Primitive::try_from(argument) else { + // Converting to Number would require calling into JavaScript code. + return None; + }; + let number = match to_number_primitive(agent, gc, argument) { + Ok(number) => number, + Err(err) => { + return Some(Err(err)); + } + }; + + // 2. If number is one of NaN, +0𝔽, or -0𝔽, return 0. + if number.is_nan(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Some(Ok(Number::pos_zero())); + } + + // 3. If number is +∞𝔽, return +∞. + if number.is_pos_infinity(agent) { + return Some(Ok(Number::pos_inf())); + } + + // 4. If number is -∞𝔽, return -∞. + if number.is_neg_infinity(agent) { + return Some(Ok(Number::neg_inf())); + } + + // 5. Return truncate(ℝ(number)). + Some(Ok(number.truncate(agent))) +} + /// ### [7.1.6 ToInt32 ( argument )](https://tc39.es/ecma262/#sec-toint32) pub(crate) fn to_int32(agent: &mut Agent, gc: GcScope<'_, '_>, argument: Value) -> JsResult { if let Value::Integer(int) = argument { @@ -584,7 +711,6 @@ pub(crate) fn to_uint8(agent: &mut Agent, gc: GcScope<'_, '_>, argument: Value) pub(crate) fn to_uint8_clamp( agent: &mut Agent, gc: GcScope<'_, '_>, - argument: Value, ) -> JsResult { if let Value::Integer(int) = argument { @@ -639,20 +765,21 @@ pub(crate) fn to_uint8_clamp( #[inline(always)] pub(crate) fn to_big_int( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, argument: Value, ) -> JsResult { // 1. Let prim be ? ToPrimitive(argument, number). - let prim = to_primitive(agent, gc, argument, Some(PreferredType::Number))?; + let prim = to_primitive(agent, gc.reborrow(), argument, Some(PreferredType::Number))?; // 2. Return the value that prim corresponds to in Table 12. match prim { Primitive::Undefined => Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid primitive 'undefined'", )), Primitive::Null => Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid primitive 'null'", )), @@ -667,6 +794,7 @@ pub(crate) fn to_big_int( let result = string_to_big_int(agent, idx.into()); let Some(result) = result else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid BigInt string", )); @@ -677,6 +805,7 @@ pub(crate) fn to_big_int( let result = string_to_big_int(agent, data.into()); let Some(result) = result else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid BigInt string", )); @@ -684,11 +813,13 @@ pub(crate) fn to_big_int( Ok(result) } Primitive::Symbol(_) => Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Cannot convert Symbol to BigInt", )), Primitive::Number(_) | Primitive::Integer(_) | Primitive::SmallF64(_) => Err(agent .throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Cannot convert Number to BigInt", )), @@ -780,21 +911,53 @@ pub(crate) fn to_big_uint64( } } +pub(crate) fn try_to_string<'gc>( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + argument: impl IntoValue, +) -> Option>> { + let argument = argument.into_value(); + if let Ok(argument) = Primitive::try_from(argument) { + Some(to_string_primitive(agent, gc, argument)) + } else { + None + } +} + /// ### [7.1.17 ToString ( argument )](https://tc39.es/ecma262/#sec-tostring) -pub(crate) fn to_string( +pub(crate) fn to_string<'gc>( agent: &mut Agent, - mut gc: GcScope<'_, '_>, + mut gc: GcScope<'gc, '_>, + argument: impl IntoValue, +) -> JsResult> { + let argument = argument.into_value(); + // 1. If argument is a String, return argument. + if let Ok(argument) = Primitive::try_from(argument) { + to_string_primitive(agent, gc.into_nogc(), argument) + } 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, gc.reborrow(), argument, Some(PreferredType::String))?; + // 11. Assert: primValue is not an Object. + // 12. Return ? ToString(primValue). + to_string(agent, gc, prim_value) + } +} - argument: impl Into + Copy, -) -> JsResult { - let argument: Value = argument.into(); +/// ### [7.1.17 ToString ( argument )](https://tc39.es/ecma262/#sec-tostring) +pub(crate) fn to_string_primitive<'gc>( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + argument: Primitive, +) -> JsResult> { // 1. If argument is a String, return argument. match argument { // 3. If argument is undefined, return "undefined". - Value::Undefined => Ok(BUILTIN_STRING_MEMORY.undefined), + Primitive::Undefined => Ok(BUILTIN_STRING_MEMORY.undefined), // 4. If argument is null, return "null". - Value::Null => Ok(BUILTIN_STRING_MEMORY.null), - Value::Boolean(value) => { + Primitive::Null => Ok(BUILTIN_STRING_MEMORY.null), + Primitive::Boolean(value) => { if value { // 5. If argument is true, return "true". Ok(BUILTIN_STRING_MEMORY.r#true) @@ -803,30 +966,21 @@ pub(crate) fn to_string( Ok(BUILTIN_STRING_MEMORY.r#false) } } - Value::String(idx) => Ok(String::String(idx)), - Value::SmallString(data) => Ok(String::SmallString(data)), + Primitive::String(idx) => Ok(String::String(idx)), + Primitive::SmallString(data) => Ok(String::SmallString(data)), // 2. If argument is a Symbol, throw a TypeError exception. - Value::Symbol(_) => Err(agent.throw_exception_with_static_message( + Primitive::Symbol(_) => Err(agent.throw_exception_with_static_message( + gc, ExceptionType::TypeError, "Cannot turn Symbol into string", )), // 7. If argument is a Number, return Number::toString(argument, 10). - Value::Number(_) | Value::Integer(_) | Value::SmallF64(_) => Ok( - Number::to_string_radix_10(agent, Number::try_from(argument).unwrap()), - ), - // 8. If argument is a BigInt, return BigInt::toString(argument, 10). - Value::BigInt(_) | Value::SmallBigInt(_) => { - BigInt::to_string_radix_10(agent, BigInt::try_from(argument).unwrap()) + Primitive::Number(_) | Primitive::Integer(_) | Primitive::SmallF64(_) => { + Ok(Number::to_string_radix_10(agent, gc, Number::try_from(argument).unwrap()).unbind()) } - _ => { - // 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, gc.reborrow(), argument, Some(PreferredType::String))?; - // 11. Assert: primValue is not an Object. - // 12. Return ? ToString(primValue). - to_string(agent, gc, prim_value) + // 8. If argument is a BigInt, return BigInt::toString(argument, 10). + Primitive::BigInt(_) | Primitive::SmallBigInt(_) => { + Ok(BigInt::to_string_radix_10(agent, gc, BigInt::try_from(argument).unwrap()).unbind()) } } } @@ -837,9 +991,10 @@ pub(crate) fn to_string( /// language value) and returns either a normal completion containing an Object /// or a throw completion. It converts argument to a value of type Object /// according to [Table 13](https://tc39.es/ecma262/#table-toobject-conversions): -pub(crate) fn to_object(agent: &mut Agent, argument: Value) -> JsResult { +pub(crate) fn to_object(agent: &mut Agent, gc: NoGcScope, argument: Value) -> JsResult { match argument { Value::Undefined | Value::Null => Err(agent.throw_exception_with_static_message( + gc, ExceptionType::TypeError, "Argument cannot be converted into an object", )), @@ -919,7 +1074,6 @@ pub(crate) fn to_object(agent: &mut Agent, argument: Value) -> JsResult pub(crate) fn to_property_key( agent: &mut Agent, mut gc: GcScope<'_, '_>, - argument: Value, ) -> JsResult { // Note: Fast path and non-standard special case combined. Usually the @@ -1057,8 +1211,7 @@ pub(crate) fn to_length(agent: &mut Agent, gc: GcScope<'_, '_>, argument: Value) /// ### [7.1.21 CanonicalNumericIndexString ( argument )](https://tc39.es/ecma262/#sec-canonicalnumericindexstring) pub(crate) fn canonical_numeric_index_string( agent: &mut Agent, - mut gc: GcScope<'_, '_>, - + gc: NoGcScope<'_, '_>, argument: String, ) -> Option { // 1. If argument is "-0", return -0𝔽. @@ -1067,10 +1220,10 @@ pub(crate) fn canonical_numeric_index_string( } // 2. Let n be ! ToNumber(argument). - let n = to_number(agent, gc.reborrow(), argument).unwrap(); + let n = to_number_primitive(agent, gc, argument.into_primitive()).unwrap(); // 3. If ! ToString(n) is argument, return n. - if to_string(agent, gc, n).unwrap() == argument { + if to_string_primitive(agent, gc, n.into_primitive()).unwrap() == argument { return Some(n); } @@ -1079,12 +1232,17 @@ pub(crate) fn canonical_numeric_index_string( } /// ### [7.1.22 ToIndex ( value )](https://tc39.es/ecma262/#sec-toindex) -pub(crate) fn to_index(agent: &mut Agent, gc: GcScope<'_, '_>, argument: Value) -> JsResult { +pub(crate) fn to_index( + agent: &mut Agent, + mut gc: GcScope<'_, '_>, + argument: Value, +) -> 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( + gc.nogc(), ExceptionType::RangeError, "Index is out of range", )); @@ -1094,13 +1252,14 @@ pub(crate) fn to_index(agent: &mut Agent, gc: GcScope<'_, '_>, argument: Value) // TODO: This can be heavily optimized by inlining `to_integer_or_infinity`. // 1. Let integer be ? ToIntegerOrInfinity(value). - let integer = to_integer_or_infinity(agent, gc, argument)?; + let integer = to_integer_or_infinity(agent, gc.reborrow(), argument)?; // 2. If integer is not in the inclusive interval from 0 to 2**53 - 1, throw a RangeError exception. let integer = if let Number::Integer(n) = integer { let integer = n.into_i64(); if !(0..=(SmallInteger::MAX_NUMBER)).contains(&integer) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Index is out of range", )); @@ -1109,6 +1268,7 @@ pub(crate) fn to_index(agent: &mut Agent, gc: GcScope<'_, '_>, argument: Value) } else { // to_integer_or_infinity returns +0, +Infinity, -Infinity, or an integer. return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Index is out of range", )); diff --git a/nova_vm/src/ecmascript/builders/builtin_function_builder.rs b/nova_vm/src/ecmascript/builders/builtin_function_builder.rs index ef6c758ba..63e43008b 100644 --- a/nova_vm/src/ecmascript/builders/builtin_function_builder.rs +++ b/nova_vm/src/ecmascript/builders/builtin_function_builder.rs @@ -38,7 +38,7 @@ pub struct CreatorLength(u8); pub struct NoName; #[derive(Clone, Copy)] -pub struct CreatorName(String); +pub struct CreatorName(String<'static>); #[derive(Default, Clone, Copy)] pub struct NoBehaviour; @@ -258,7 +258,7 @@ impl<'agent, P, L, B, Pr> BuiltinFunctionBuilder<'agent, P, L, NoName, B, Pr> { #[must_use] pub fn with_name( self, - name: String, + name: String<'static>, ) -> BuiltinFunctionBuilder<'agent, P, L, CreatorName, B, Pr> { BuiltinFunctionBuilder { agent: self.agent, diff --git a/nova_vm/src/ecmascript/builtins/arguments.rs b/nova_vm/src/ecmascript/builtins/arguments.rs index 67c82f10c..a502c7b7f 100644 --- a/nova_vm/src/ecmascript/builtins/arguments.rs +++ b/nova_vm/src/ecmascript/builtins/arguments.rs @@ -124,7 +124,6 @@ use super::ordinary::ordinary_object_create_with_intrinsics; pub(crate) fn create_unmapped_arguments_object( agent: &mut Agent, mut gc: GcScope<'_, '_>, - arguments_list: &[Value], ) -> Object { // 1. Let len be the number of elements in argumentsList. diff --git a/nova_vm/src/ecmascript/builtins/array.rs b/nova_vm/src/ecmascript/builtins/array.rs index 445e6af52..2cb4f2da1 100644 --- a/nova_vm/src/ecmascript/builtins/array.rs +++ b/nova_vm/src/ecmascript/builtins/array.rs @@ -12,7 +12,7 @@ mod data; use std::ops::{Index, IndexMut, RangeInclusive}; use super::{array_set_length, ordinary::ordinary_define_own_property}; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -51,8 +51,8 @@ impl Array { /// This is equal to the [CreateArrayFromList](https://tc39.es/ecma262/#sec-createarrayfromlist) /// abstract operation. #[inline] - pub fn from_slice(agent: &mut Agent, elements: &[Value]) -> Self { - create_array_from_list(agent, elements) + pub fn from_slice(agent: &mut Agent, gc: NoGcScope, elements: &[Value]) -> Self { + create_array_from_list(agent, gc, elements) } pub(crate) fn get_index(self) -> usize { @@ -63,6 +63,10 @@ impl Array { agent[*self].elements.len() } + pub fn length_writable(&self, agent: &impl Index) -> bool { + agent[*self].elements.len_writable + } + pub fn is_empty(&self, agent: &impl Index) -> bool { agent[*self].elements.len() == 0 } @@ -99,7 +103,6 @@ impl Array { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -223,7 +226,6 @@ impl InternalMethods for Array { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { if let PropertyKey::Integer(index) = property_key { @@ -277,7 +279,6 @@ impl InternalMethods for Array { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -363,7 +364,6 @@ impl InternalMethods for Array { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { let has_own = self.internal_get_own_property(agent, gc.reborrow(), property_key)?; @@ -388,7 +388,6 @@ impl InternalMethods for Array { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -444,7 +443,6 @@ impl InternalMethods for Array { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { diff --git a/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs index a5c9c8ebd..7bc32c4fd 100644 --- a/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/array/abstract_operations.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::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -27,6 +27,7 @@ use super::{data::SealableElementsVector, Array, ArrayHeapData}; /// It is used to specify the creation of new Arrays. pub fn array_create( agent: &mut Agent, + gc: NoGcScope, length: usize, capacity: usize, proto: Option, @@ -34,6 +35,7 @@ pub fn array_create( // 1. If length > 2**32 - 1, throw a RangeError exception. if length > (2usize.pow(32) - 1) { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "invalid array length", )); @@ -90,7 +92,6 @@ pub fn array_create( pub(crate) fn array_species_create( agent: &mut Agent, mut gc: GcScope<'_, '_>, - original_array: Object, length: usize, ) -> JsResult { @@ -98,7 +99,7 @@ pub(crate) fn array_species_create( let original_is_array = is_array(agent, original_array.into_value())?; // 2. If isArray is false, return ? ArrayCreate(length). if !original_is_array { - let new_array = array_create(agent, length, length, None)?; + let new_array = array_create(agent, gc.nogc(), length, length, None)?; return Ok(new_array.into_object()); } // 3. Let C be ? Get(originalArray, "constructor"). @@ -138,13 +139,16 @@ pub(crate) fn array_species_create( } // 6. If C is undefined, return ? ArrayCreate(length). if c.is_undefined() { - let new_array = array_create(agent, length, length, None)?; + let new_array = array_create(agent, gc.nogc(), length, length, None)?; return Ok(new_array.into_object()); } // 7. If IsConstructor(C) is false, throw a TypeError exception. let Some(c) = is_constructor(agent, c) else { - return Err(agent - .throw_exception_with_static_message(ExceptionType::TypeError, "Not a constructor")); + return Err(agent.throw_exception_with_static_message( + gc.nogc(), + ExceptionType::TypeError, + "Not a constructor", + )); }; // 8. Return ? Construct(C, « 𝔽(length) »). let length = Value::from_f64(agent, length as f64); @@ -157,7 +161,6 @@ pub(crate) fn array_species_create( pub fn array_set_length( agent: &mut Agent, mut gc: GcScope<'_, '_>, - a: Array, desc: PropertyDescriptor, ) -> JsResult { @@ -191,10 +194,11 @@ pub fn array_set_length( // 3. Let newLen be ? ToUint32(Desc.[[Value]]). let new_len = to_uint32(agent, gc.reborrow(), desc_value)?; // 4. Let numberLen be ? ToNumber(Desc.[[Value]]). - let number_len = to_number(agent, gc, desc_value)?; + let number_len = to_number(agent, gc.reborrow(), desc_value)?; // 5. If SameValueZero(newLen, numberLen) is false, throw a RangeError exception. if !Number::same_value_zero(agent, number_len, new_len.into()) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "invalid array length", )); 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 a37396782..cc6cbef77 100644 --- a/nova_vm/src/ecmascript/builtins/array_buffer/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/array_buffer/abstract_operations.rs @@ -4,7 +4,7 @@ use super::{ArrayBuffer, ArrayBufferHeapData}; use crate::ecmascript::types::Viewable; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_objects::get, @@ -37,6 +37,7 @@ pub(crate) enum Ordering { /// completion. It is used to create an ArrayBuffer. pub(crate) fn allocate_array_buffer( agent: &mut Agent, + gc: NoGcScope, // TODO: Verify that constructor is %ArrayBuffer% and if not, // create the `ObjectHeapData` for obj. _constructor: Function, @@ -51,6 +52,7 @@ pub(crate) fn allocate_array_buffer( // a. If byteLength > maxByteLength, throw a RangeError exception. if byte_length > max_byte_length.unwrap() { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "Byte length is over maximumm byte length", )); @@ -63,7 +65,7 @@ pub(crate) fn allocate_array_buffer( // a. If it is not possible to create a Data Block block consisting of maxByteLength bytes, throw a RangeError exception. // b. NOTE: Resizable ArrayBuffers are designed to be implementable with in-place growth. Implementations may throw if, for example, virtual memory cannot be reserved up front. // c. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength. - let block = DataBlock::create_byte_data_block(agent, byte_length)?; + let block = DataBlock::create_byte_data_block(agent, gc, byte_length)?; // 6. Set obj.[[ArrayBufferData]] to block. // 7. Set obj.[[ArrayBufferByteLength]] to byteLength. let obj = if allocating_resizable_buffer { @@ -143,6 +145,7 @@ pub(crate) fn detach_array_buffer( /// range starting at srcByteOffset and continuing for srcLength bytes. pub(crate) fn clone_array_buffer( agent: &mut Agent, + gc: NoGcScope, src_buffer: ArrayBuffer, src_byte_offset: usize, src_length: usize, @@ -153,6 +156,7 @@ pub(crate) fn clone_array_buffer( // 2. Let targetBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, srcLength). let target_buffer = allocate_array_buffer( agent, + gc, array_buffer_constructor.into_function(), src_length as u64, None, @@ -184,7 +188,6 @@ pub(crate) fn clone_array_buffer( pub(crate) fn get_array_buffer_max_byte_length_option( agent: &mut Agent, mut gc: GcScope<'_, '_>, - options: Value, ) -> JsResult> { // 1. If options is not an Object, return EMPTY. @@ -202,7 +205,7 @@ pub(crate) fn get_array_buffer_max_byte_length_option( } // 4. Return ? ToIndex(maxByteLength). // TODO: Consider de-inlining this once ToIndex is implemented. - let number = max_byte_length.to_number(agent, gc)?; + let number = max_byte_length.to_number(agent, gc.reborrow())?; let integer = if number.is_nan(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { 0 @@ -216,8 +219,11 @@ pub(crate) fn get_array_buffer_max_byte_length_option( if (0..=(2i64.pow(53) - 1)).contains(&integer) { Ok(Some(integer)) } else { - Err(agent - .throw_exception_with_static_message(ExceptionType::RangeError, "Not a SafeInteger")) + Err(agent.throw_exception_with_static_message( + gc.nogc(), + ExceptionType::RangeError, + "Not a SafeInteger", + )) } } diff --git a/nova_vm/src/ecmascript/builtins/bound_function.rs b/nova_vm/src/ecmascript/builtins/bound_function.rs index b6db23c5a..8198e3c9d 100644 --- a/nova_vm/src/ecmascript/builtins/bound_function.rs +++ b/nova_vm/src/ecmascript/builtins/bound_function.rs @@ -77,7 +77,6 @@ impl IntoFunction for BoundFunction { pub(crate) fn bound_function_create( agent: &mut Agent, mut gc: GcScope<'_, '_>, - target_function: Function, bound_this: Value, bound_args: &[Value], @@ -150,7 +149,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, _gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { function_internal_get_own_property(self, agent, property_key) @@ -160,7 +158,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -177,7 +174,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_has_property(self, agent, gc.reborrow(), property_key) @@ -187,7 +183,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -198,7 +193,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -210,7 +204,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_delete(self, agent, gc.reborrow(), property_key) @@ -235,7 +228,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - _: Value, arguments_list: ArgumentsList, ) -> JsResult { @@ -275,7 +267,6 @@ impl InternalMethods for BoundFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { diff --git a/nova_vm/src/ecmascript/builtins/builtin_constructor.rs b/nova_vm/src/ecmascript/builtins/builtin_constructor.rs index dc3375cbc..a95c65a0e 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_constructor.rs @@ -194,7 +194,6 @@ impl InternalMethods for BuiltinConstructorFunction { self, agent: &mut Agent, _gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { function_internal_get_own_property(self, agent, property_key) @@ -204,7 +203,6 @@ impl InternalMethods for BuiltinConstructorFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -221,7 +219,6 @@ impl InternalMethods for BuiltinConstructorFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_has_property(self, agent, gc.reborrow(), property_key) @@ -231,7 +228,6 @@ impl InternalMethods for BuiltinConstructorFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -242,7 +238,6 @@ impl InternalMethods for BuiltinConstructorFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -254,7 +249,6 @@ impl InternalMethods for BuiltinConstructorFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_delete(self, agent, gc.reborrow(), property_key) @@ -278,14 +272,14 @@ impl InternalMethods for BuiltinConstructorFunction { fn internal_call( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, _: Value, _: ArgumentsList, ) -> JsResult { // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). // ii. If NewTarget is undefined, throw a TypeError exception. Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "class constructors must be invoked with 'new'", )) @@ -301,7 +295,6 @@ impl InternalMethods for BuiltinConstructorFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { @@ -320,7 +313,6 @@ impl InternalMethods for BuiltinConstructorFunction { fn builtin_call_or_construct( agent: &mut Agent, mut gc: GcScope<'_, '_>, - f: BuiltinConstructorFunction, arguments_list: ArgumentsList, new_target: Function, @@ -378,9 +370,9 @@ fn builtin_call_or_construct( result } -pub(crate) struct BuiltinConstructorArgs { +pub(crate) struct BuiltinConstructorArgs<'a> { pub(crate) is_derived: bool, - pub(crate) class_name: String, + pub(crate) class_name: String<'a>, pub(crate) prototype: Option, pub(crate) prototype_property: Object, pub(crate) compiled_initializer_bytecode: Option, diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index 0f660e8f6..0f0a8e9a6 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -4,7 +4,7 @@ use std::ops::{Deref, Index, IndexMut}; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ execution::{ @@ -62,7 +62,7 @@ impl Behaviour { } pub trait Builtin { - const NAME: String; + const NAME: String<'static>; const LENGTH: u8; const BEHAVIOUR: Behaviour; @@ -236,7 +236,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, _gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { function_internal_get_own_property(self, agent, property_key) @@ -246,7 +245,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -257,7 +255,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_has_property(self, agent, gc, property_key) @@ -267,7 +264,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -278,7 +274,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -290,7 +285,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_delete(self, agent, gc, property_key) @@ -315,7 +309,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - this_argument: Value, arguments_list: ArgumentsList, ) -> JsResult { @@ -333,7 +326,6 @@ impl InternalMethods for BuiltinFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { @@ -353,7 +345,6 @@ impl InternalMethods for BuiltinFunction { pub(crate) fn builtin_call_or_construct( agent: &mut Agent, gc: GcScope<'_, '_>, - f: BuiltinFunction, this_argument: Option, arguments_list: ArgumentsList, @@ -394,6 +385,7 @@ pub(crate) fn builtin_call_or_construct( Behaviour::Regular(func) => { if new_target.is_some() { Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Not a constructor", )) @@ -440,6 +432,7 @@ pub(crate) fn builtin_call_or_construct( /// built-in function object. pub fn create_builtin_function( agent: &mut Agent, + gc: NoGcScope, behaviour: Behaviour, args: BuiltinFunctionArgs, ) -> BuiltinFunction { @@ -451,11 +444,11 @@ pub fn create_builtin_function( let initial_name = if let Some(prefix) = args.prefix { // 12. Else, // a. Perform SetFunctionName(func, name, prefix). - String::from_string(agent, format!("{} {}", args.name, prefix)) + String::from_string(agent, gc, format!("{} {}", args.name, prefix)) } else { // 11. If prefix is not present, then // a. Perform SetFunctionName(func, name). - String::from_str(agent, args.name) + String::from_str(agent, gc, args.name) }; // 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]]. @@ -520,7 +513,7 @@ pub fn create_builtin_function( // 13. Return func. agent.heap.create(BuiltinFunctionHeapData { behaviour, - initial_name: Some(initial_name), + initial_name: Some(initial_name.unbind()), // 10. Perform SetFunctionLength(func, length). length: args.length as u8, // 8. Set func.[[Realm]] to realm. 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 9b70884b0..eb249f590 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 @@ -18,7 +18,7 @@ use crate::{ pub(crate) struct AsyncFunctionConstructor; impl Builtin for AsyncFunctionConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.AsyncFunction; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.AsyncFunction; const LENGTH: u8 = 1; @@ -32,7 +32,6 @@ impl AsyncFunctionConstructor { fn behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, 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 f25644d54..c71661a57 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 @@ -58,7 +58,6 @@ impl AwaitReactionIdentifier { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - reaction_type: PromiseReactionType, value: Value, ) { 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 8a6303e9c..b69e218ff 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 @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct AsyncGeneratorFunctionConstructor; impl Builtin for AsyncGeneratorFunctionConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.AsyncGeneratorFunction; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.AsyncGeneratorFunction; const LENGTH: u8 = 1; @@ -30,7 +30,6 @@ impl AsyncGeneratorFunctionConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, 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 dae355189..fffbb5a08 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 @@ -17,7 +17,7 @@ pub(crate) struct AsyncGeneratorPrototype; struct AsyncGeneratorPrototypeNext; impl Builtin for AsyncGeneratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 1; @@ -25,7 +25,7 @@ impl Builtin for AsyncGeneratorPrototypeNext { } struct AsyncGeneratorPrototypeReturn; impl Builtin for AsyncGeneratorPrototypeReturn { - const NAME: String = BUILTIN_STRING_MEMORY.r#return; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#return; const LENGTH: u8 = 1; @@ -33,7 +33,7 @@ impl Builtin for AsyncGeneratorPrototypeReturn { } struct AsyncGeneratorPrototypeThrow; impl Builtin for AsyncGeneratorPrototypeThrow { - const NAME: String = BUILTIN_STRING_MEMORY.throw; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.throw; const LENGTH: u8 = 1; @@ -44,7 +44,6 @@ impl AsyncGeneratorPrototype { fn next( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -54,7 +53,6 @@ impl AsyncGeneratorPrototype { fn r#return( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -64,7 +62,6 @@ impl AsyncGeneratorPrototype { fn throw( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { 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 56081d8e5..b59011735 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 @@ -25,7 +25,7 @@ use crate::{ pub(crate) struct GeneratorFunctionConstructor; impl Builtin for GeneratorFunctionConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.GeneratorFunction; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.GeneratorFunction; const LENGTH: u8 = 1; @@ -39,7 +39,6 @@ impl GeneratorFunctionConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, 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 35b029527..3bbe94039 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 @@ -43,7 +43,6 @@ impl Generator { mut self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - value: Value, ) -> JsResult { // 1. Let state be ? GeneratorValidate(generator, generatorBrand). @@ -53,6 +52,7 @@ impl Generator { } GeneratorState::Executing => { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "The generator is currently running", )) @@ -82,7 +82,7 @@ impl Generator { // execution context. agent.execution_context_stack.push(execution_context); - let saved = Scoped::new(agent, self); + let saved = Scoped::new(agent, gc.nogc(), self); // 9. 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 @@ -157,7 +157,6 @@ impl Generator { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - value: Value, ) -> JsResult { // 1. Let state be ? GeneratorValidate(generator, generatorBrand). @@ -183,6 +182,7 @@ impl Generator { } GeneratorState::Executing => { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "The generator is currently running", )); 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 98408398e..3d325b119 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 @@ -20,7 +20,7 @@ pub(crate) struct GeneratorPrototype; pub(crate) struct GeneratorPrototypeNext; impl Builtin for GeneratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 1; @@ -32,7 +32,7 @@ impl BuiltinIntrinsic for GeneratorPrototypeNext { } pub(crate) struct GeneratorPrototypeReturn; impl Builtin for GeneratorPrototypeReturn { - const NAME: String = BUILTIN_STRING_MEMORY.r#return; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#return; const LENGTH: u8 = 1; @@ -40,7 +40,7 @@ impl Builtin for GeneratorPrototypeReturn { } pub(crate) struct GeneratorPrototypeThrow; impl Builtin for GeneratorPrototypeThrow { - const NAME: String = BUILTIN_STRING_MEMORY.throw; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.throw; const LENGTH: u8 = 1; @@ -51,13 +51,13 @@ impl GeneratorPrototype { fn next( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // GeneratorResume: 1. Let state be ? GeneratorValidate(generator, generatorBrand). let Value::Generator(generator) = this_value else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Generator expected", )); @@ -69,8 +69,7 @@ impl GeneratorPrototype { fn r#return( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -82,6 +81,7 @@ impl GeneratorPrototype { // 1. Let state be ? GeneratorValidate(generator, generatorBrand). let Value::Generator(generator) = this_value else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Generator expected", )); @@ -107,6 +107,7 @@ impl GeneratorPrototype { } GeneratorState::Executing => { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "The generator is currently running", )) @@ -124,13 +125,13 @@ impl GeneratorPrototype { fn throw( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // GeneratorResumeAbrupt: 1. Let state be ? GeneratorValidate(generator, generatorBrand). let Value::Generator(generator) = this_value else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Generator expected", )); 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 d58863201..0a4b0b19c 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 @@ -16,19 +16,19 @@ pub(crate) struct AsyncFromSyncIteratorPrototype; struct AsyncFromSyncIteratorPrototypeNext; impl Builtin for AsyncFromSyncIteratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncFromSyncIteratorPrototype::next); } struct AsyncFromSyncIteratorPrototypeReturn; impl Builtin for AsyncFromSyncIteratorPrototypeReturn { - const NAME: String = BUILTIN_STRING_MEMORY.r#return; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#return; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncFromSyncIteratorPrototype::r#return); } struct AsyncFromSyncIteratorPrototypeThrow; impl Builtin for AsyncFromSyncIteratorPrototypeThrow { - const NAME: String = BUILTIN_STRING_MEMORY.throw; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.throw; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncFromSyncIteratorPrototype::throw); } @@ -37,7 +37,6 @@ impl AsyncFromSyncIteratorPrototype { fn next( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -47,7 +46,6 @@ impl AsyncFromSyncIteratorPrototype { fn r#return( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -57,7 +55,6 @@ impl AsyncFromSyncIteratorPrototype { fn throw( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 99b96e3d2..461387dae 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 @@ -17,7 +17,7 @@ pub(crate) struct AsyncIteratorPrototype; struct AsyncIteratorPrototypeIterator; impl Builtin for AsyncIteratorPrototypeIterator { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_asyncIterator_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_asyncIterator_; const KEY: Option = Some(WellKnownSymbolIndexes::AsyncIterator.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncIteratorPrototype::iterator); @@ -27,7 +27,6 @@ impl AsyncIteratorPrototype { fn iterator( _agent: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { 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 2aeda82ec..a62e87d4c 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 @@ -17,7 +17,7 @@ pub(crate) struct IteratorPrototype; struct IteratorPrototypeIterator; impl Builtin for IteratorPrototypeIterator { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_iterator_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_iterator_; const KEY: Option = Some(WellKnownSymbolIndexes::Iterator.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(IteratorPrototype::iterator); @@ -27,7 +27,6 @@ impl IteratorPrototype { fn iterator( _agent: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { 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 ce6e39c7b..0031fe54e 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 @@ -157,6 +157,7 @@ impl PromiseCapability { // a. Let selfResolutionError be a newly created TypeError object. // b. Perform RejectPromise(promise, selfResolutionError). let exception = agent.create_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Tried to resolve a promise with itself.", ); diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs index 72cd5c94a..aa150901e 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs @@ -65,7 +65,7 @@ pub struct PromiseReactionRecord { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] -pub struct PromiseReaction(BaseIndex); +pub struct PromiseReaction(BaseIndex<'static, PromiseReactionRecord>); impl PromiseReaction { pub(crate) const fn get_index(self) -> usize { 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 e2f6a99a1..9f11fff67 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 @@ -37,7 +37,8 @@ pub struct PromiseResolvingFunctionHeapData { pub(crate) resolve_type: PromiseResolvingFunctionType, } -pub(crate) type BuiltinPromiseResolvingFunctionIndex = BaseIndex; +pub(crate) type BuiltinPromiseResolvingFunctionIndex = + BaseIndex<'static, PromiseResolvingFunctionHeapData>; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct BuiltinPromiseResolvingFunction(pub(crate) BuiltinPromiseResolvingFunctionIndex); @@ -120,7 +121,6 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { self, agent: &mut Agent, _: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { function_internal_get_own_property(self, agent, property_key) @@ -130,7 +130,6 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -141,7 +140,6 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_has_property(self, agent, gc, property_key) @@ -151,7 +149,6 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -162,7 +159,6 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -174,7 +170,6 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_delete(self, agent, gc, property_key) @@ -192,7 +187,6 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, args: ArgumentsList, ) -> JsResult { 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 1bf72e774..8d2b654e2 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 @@ -34,7 +34,7 @@ use super::promise_abstract_operations::{ pub(crate) struct PromiseConstructor; impl Builtin for PromiseConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Promise; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Promise; const LENGTH: u8 = 1; @@ -47,55 +47,55 @@ struct PromiseAll; impl Builtin for PromiseAll { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::all); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.all; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.all; } struct PromiseAllSettled; impl Builtin for PromiseAllSettled { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::all_settled); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.allSettled; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.allSettled; } struct PromiseAny; impl Builtin for PromiseAny { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::any); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.any; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.any; } struct PromiseRace; impl Builtin for PromiseRace { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::race); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.race; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.race; } struct PromiseReject; impl Builtin for PromiseReject { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::reject); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.reject; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.reject; } struct PromiseResolve; impl Builtin for PromiseResolve { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::resolve); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.resolve; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.resolve; } struct PromiseTry; impl Builtin for PromiseTry { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::r#try); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.r#try; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#try; } struct PromiseWithResolvers; impl Builtin for PromiseWithResolvers { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::with_resolvers); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.withResolvers; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.withResolvers; } struct PromiseGetSpecies; impl Builtin for PromiseGetSpecies { const BEHAVIOUR: Behaviour = Behaviour::Regular(PromiseConstructor::get_species); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); } impl BuiltinGetter for PromiseGetSpecies {} @@ -104,7 +104,6 @@ impl PromiseConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, args: ArgumentsList, new_target: Option, @@ -112,6 +111,7 @@ impl PromiseConstructor { // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Promise Constructor requires 'new'", )); @@ -127,6 +127,7 @@ impl PromiseConstructor { // TODO: Callable proxies let Ok(executor) = Function::try_from(args.get(0)) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Not a callable value", )); @@ -186,7 +187,6 @@ impl PromiseConstructor { fn all( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -196,7 +196,6 @@ impl PromiseConstructor { fn all_settled( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -205,7 +204,6 @@ impl PromiseConstructor { fn any( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -214,7 +212,6 @@ impl PromiseConstructor { fn race( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -224,7 +221,6 @@ impl PromiseConstructor { fn reject( agent: &mut Agent, _: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -253,7 +249,6 @@ impl PromiseConstructor { fn resolve( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -271,7 +266,6 @@ impl PromiseConstructor { fn r#try( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -279,6 +273,7 @@ impl PromiseConstructor { // 2. If C is not an Object, throw a TypeError exception. if is_constructor(agent, this_value).is_none() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Expected the this value to be a constructor.", )); @@ -323,14 +318,14 @@ impl PromiseConstructor { fn with_resolvers( agent: &mut Agent, - _: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _arguments: ArgumentsList, ) -> 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( + gc.nogc(), ExceptionType::TypeError, "Expected the this value to be a constructor.", )); @@ -394,7 +389,6 @@ impl PromiseConstructor { fn get_species( _: &mut Agent, _: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { 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 ad8d9b718..4e9d0c785 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 @@ -35,19 +35,19 @@ pub(crate) struct PromisePrototype; struct PromisePrototypeCatch; impl Builtin for PromisePrototypeCatch { - const NAME: String = BUILTIN_STRING_MEMORY.catch; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.catch; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(PromisePrototype::catch); } struct PromisePrototypeFinally; impl Builtin for PromisePrototypeFinally { - const NAME: String = BUILTIN_STRING_MEMORY.finally; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.finally; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(PromisePrototype::finally); } struct PromisePrototypeThen; impl Builtin for PromisePrototypeThen { - const NAME: String = BUILTIN_STRING_MEMORY.then; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.then; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(PromisePrototype::then); } @@ -56,7 +56,6 @@ impl PromisePrototype { fn catch( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { @@ -77,7 +76,6 @@ impl PromisePrototype { fn finally( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -86,8 +84,7 @@ impl PromisePrototype { fn then( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, args: ArgumentsList, ) -> JsResult { @@ -95,6 +92,7 @@ impl PromisePrototype { // 2. If IsPromise(promise) is false, throw a TypeError exception. let Value::Promise(promise) = this_value else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "'this' is not a promise", )); diff --git a/nova_vm/src/ecmascript/builtins/data_view.rs b/nova_vm/src/ecmascript/builtins/data_view.rs index a26cacc70..af729b234 100644 --- a/nova_vm/src/ecmascript/builtins/data_view.rs +++ b/nova_vm/src/ecmascript/builtins/data_view.rs @@ -74,7 +74,7 @@ impl From for DataView { } } -impl IntoBaseIndex for DataView { +impl IntoBaseIndex<'_, DataViewHeapData> for DataView { fn into_base_index(self) -> DataViewIndex { self.0 } 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 65e56948b..4f0850622 100644 --- a/nova_vm/src/ecmascript/builtins/data_view/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/data_view/abstract_operations.rs @@ -165,7 +165,7 @@ pub(crate) fn is_view_out_of_bounds( /// retrieve values from the view's buffer. pub(crate) fn get_view_value( agent: &mut Agent, - gc: GcScope<'_, '_>, + mut gc: GcScope<'_, '_>, view: Value, request_index: Value, // 4. Set isLittleEndian to ToBoolean(isLittleEndian). @@ -173,10 +173,10 @@ pub(crate) fn get_view_value( ) -> JsResult { // 1. Perform ? RequireInternalSlot(view, [[DataView]]). // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. - let view = require_internal_slot_data_view(agent, view)?; + let view = require_internal_slot_data_view(agent, gc.nogc(), view)?; // 3. Let getIndex be ? ToIndex(requestIndex). - let get_index = to_index(agent, gc, request_index)? as usize; + let get_index = to_index(agent, gc.reborrow(), request_index)? as usize; // 5. Let viewOffset be view.[[ByteOffset]]. let view_offset = view.byte_offset(agent); @@ -187,6 +187,7 @@ pub(crate) fn get_view_value( // 8. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception. if is_view_out_of_bounds(agent, &view_record) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "DataView is out of bounds", )); @@ -201,6 +202,7 @@ pub(crate) fn get_view_value( // 11. If getIndex + elementSize > viewSize, throw a RangeError exception. if get_index + element_size > view_size { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Index out of bounds", )); @@ -239,7 +241,7 @@ pub(crate) fn set_view_value( ) -> JsResult { // 1. Perform ? RequireInternalSlot(view, [[DataView]]). // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. - let view = require_internal_slot_data_view(agent, view)?; + let view = require_internal_slot_data_view(agent, gc.nogc(), view)?; // 3. Let getIndex be ? ToIndex(requestIndex). let get_index = to_index(agent, gc.reborrow(), request_index)? as usize; @@ -262,6 +264,7 @@ pub(crate) fn set_view_value( // 10. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception. if is_view_out_of_bounds(agent, &view_record) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "DataView is out of bounds", )); @@ -275,6 +278,7 @@ pub(crate) fn set_view_value( // 13. If getIndex + elementSize > viewSize, throw a RangeError exception. if get_index + element_size > view_size { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Index out of bounds", )); diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index 4d4309433..3e901e822 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -11,7 +11,7 @@ use oxc_ast::ast::{FormalParameters, FunctionBody}; use oxc_ecmascript::IsSimpleParameterList; use oxc_span::Span; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_object, @@ -345,7 +345,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, _gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { function_internal_get_own_property(self, agent, property_key) @@ -355,7 +354,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -366,7 +364,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_has_property(self, agent, gc, property_key) @@ -376,7 +373,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -387,7 +383,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -399,7 +394,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { function_internal_delete(self, agent, gc, property_key) @@ -424,7 +418,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, gc: GcScope<'_, '_>, - this_argument: Value, arguments_list: ArgumentsList<'_>, ) -> JsResult { @@ -450,6 +443,7 @@ impl InternalMethods for ECMAScriptFunction { // a. Let error be a newly created TypeError object. // b. NOTE: error is created in calleeContext with F's associated Realm Record. let error = agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "class constructors must be invoked with 'new'", ); @@ -462,7 +456,7 @@ impl InternalMethods for ECMAScriptFunction { let EnvironmentIndex::Function(local_env) = local_env else { panic!("localEnv is not a Function Environment Record"); }; - ordinary_call_bind_this(agent, self, local_env, this_argument); + ordinary_call_bind_this(agent, gc.nogc(), self, local_env, this_argument); // 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)). let result = ordinary_call_evaluate_body(agent, gc, self, arguments_list); // 7. Remove calleeContext from the execution context stack and restore callerContext as the running execution context. @@ -478,7 +472,6 @@ impl InternalMethods for ECMAScriptFunction { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { @@ -519,6 +512,7 @@ impl InternalMethods for ECMAScriptFunction { // a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). ordinary_call_bind_this( agent, + gc.nogc(), self, constructor_env, this_argument.unwrap().into_value(), @@ -553,14 +547,15 @@ impl InternalMethods for ECMAScriptFunction { if !value.is_undefined() { let message = format!( "derived class constructor returned invalid value {}", - value.string_repr(agent, gc).as_str(agent) + value.string_repr(agent, gc.reborrow()).as_str(agent) ); - let message = String::from_string(agent, message); + let message = String::from_string(agent, gc.nogc(), message); Err(agent.throw_exception_with_message(ExceptionType::TypeError, message)) } else { // 12. Let thisBinding be ? constructorEnv.GetThisBinding(). // 13. Assert: thisBinding is an Object. - let Ok(this_binding) = Object::try_from(constructor_env.get_this_binding(agent)?) + let Ok(this_binding) = + Object::try_from(constructor_env.get_this_binding(agent, gc.nogc())?) else { unreachable!(); }; @@ -629,6 +624,7 @@ pub(crate) fn prepare_for_ordinary_call( /// truly used for. pub(crate) fn ordinary_call_bind_this( agent: &mut Agent, + gc: NoGcScope, f: ECMAScriptFunction, local_env: FunctionEnvironmentIndex, this_argument: Value, @@ -660,7 +656,7 @@ pub(crate) fn ordinary_call_bind_this( } else { // b. Else, // i. Let thisValue be ! ToObject(thisArgument). - to_object(agent, this_argument).unwrap().into_value() + to_object(agent, gc, this_argument).unwrap().into_value() // ii. NOTE: ToObject produces wrapper objects using calleeRealm. } }; @@ -671,7 +667,7 @@ pub(crate) fn ordinary_call_bind_this( ThisBindingStatus::Initialized ); // 9. Perform ! localEnv.BindThisValue(thisValue). - local_env.bind_this_value(agent, this_value).unwrap(); + local_env.bind_this_value(agent, gc, this_value).unwrap(); // 10. Return UNUSED. } @@ -684,7 +680,6 @@ pub(crate) fn ordinary_call_bind_this( pub(crate) fn evaluate_body( agent: &mut Agent, gc: GcScope<'_, '_>, - function_object: ECMAScriptFunction, arguments_list: ArgumentsList, ) -> JsResult { @@ -755,7 +750,6 @@ pub(crate) fn evaluate_body( pub(crate) fn ordinary_call_evaluate_body( agent: &mut Agent, gc: GcScope<'_, '_>, - f: ECMAScriptFunction, arguments_list: ArgumentsList, ) -> JsResult { @@ -777,6 +771,7 @@ pub(crate) fn ordinary_call_evaluate_body( /// the syntactic definition of the function to be created. pub(crate) fn ordinary_function_create<'agent, 'program>( agent: &'agent mut Agent, + gc: NoGcScope, params: OrdinaryFunctionCreateParams<'agent, 'program>, ) -> ECMAScriptFunction { let (source_code, outer_env_is_strict) = if let Some(source_code) = params.source_code { @@ -876,7 +871,7 @@ pub(crate) fn ordinary_function_create<'agent, 'program>( .filter(|par| !par.pattern.kind.is_assignment_pattern()) .count(); // 22. Perform SetFunctionLength(F, len). - set_ecmascript_function_length(agent, &mut function, len).unwrap(); + set_ecmascript_function_length(agent, gc, &mut function, len).unwrap(); // 23. Return F. agent.heap.create(function) } @@ -974,6 +969,7 @@ pub(crate) fn make_method(agent: &mut Agent, f: ECMAScriptFunction, home_object: /// prefix (a String) and returns UNUSED. It adds a "name" property to F. pub(crate) fn set_function_name( agent: &mut Agent, + gc: NoGcScope, function: impl IntoFunction, name: PropertyKey, _prefix: Option, @@ -989,14 +985,14 @@ pub(crate) fn set_function_name( .descriptor .map_or(String::EMPTY_STRING, |descriptor| { let descriptor = descriptor.as_str(agent); - String::from_string(agent, format!("[{}]", descriptor)) + String::from_string(agent, gc, format!("[{}]", descriptor)) }) } // TODO: Private Name // 3. Else if name is a Private Name, then // a. Set name to name.[[Description]]. PropertyKey::Integer(integer) => { - String::from_string(agent, format!("{}", integer.into_i64())) + String::from_string(agent, gc, format!("{}", integer.into_i64())) } PropertyKey::SmallString(str) => str.into(), PropertyKey::String(str) => str.into(), @@ -1012,7 +1008,7 @@ pub(crate) fn set_function_name( // with a non-default prototype. In that case, object_index is // already set. assert!(function.name.is_none()); - function.name = Some(name); + function.name = Some(name.unbind()); } Function::BuiltinFunction(_idx) => unreachable!(), Function::ECMAScriptFunction(idx) => { @@ -1020,7 +1016,7 @@ pub(crate) fn set_function_name( // 1. Assert: F is an extensible object that does not have a "name" own property. assert!(function.name.is_none()); // 6. Perform ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). - function.name = Some(name); + function.name = Some(name.unbind()); // 7. Return UNUSED. } Function::BuiltinGeneratorFunction => todo!(), @@ -1034,6 +1030,7 @@ pub(crate) fn set_function_name( /// ### [10.2.10 SetFunctionLength ( F, length )](https://tc39.es/ecma262/#sec-setfunctionlength) fn set_ecmascript_function_length( agent: &mut Agent, + gc: NoGcScope, function: &mut ECMAScriptFunctionHeapData, length: usize, ) -> JsResult<()> { @@ -1042,6 +1039,7 @@ fn set_ecmascript_function_length( // 2. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). if length > u8::MAX as usize { return Err(agent.throw_exception_with_static_message( + gc, SyntaxError, "Too many arguments in function call (only 255 allowed)", )); diff --git a/nova_vm/src/ecmascript/builtins/embedder_object.rs b/nova_vm/src/ecmascript/builtins/embedder_object.rs index 4a5657fe6..146b33b99 100644 --- a/nova_vm/src/ecmascript/builtins/embedder_object.rs +++ b/nova_vm/src/ecmascript/builtins/embedder_object.rs @@ -116,7 +116,6 @@ impl InternalMethods for EmbedderObject { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _prototype: Option, ) -> JsResult { todo!(); @@ -139,7 +138,6 @@ impl InternalMethods for EmbedderObject { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, ) -> JsResult> { todo!(); @@ -149,7 +147,6 @@ impl InternalMethods for EmbedderObject { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, _property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -160,7 +157,6 @@ impl InternalMethods for EmbedderObject { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, ) -> JsResult { todo!(); @@ -170,7 +166,6 @@ impl InternalMethods for EmbedderObject { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, _receiver: Value, ) -> JsResult { @@ -181,7 +176,6 @@ impl InternalMethods for EmbedderObject { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, _value: Value, _receiver: Value, @@ -193,7 +187,6 @@ impl InternalMethods for EmbedderObject { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, ) -> JsResult { todo!(); diff --git a/nova_vm/src/ecmascript/builtins/error.rs b/nova_vm/src/ecmascript/builtins/error.rs index 9ed87575c..9d664f2a8 100644 --- a/nova_vm/src/ecmascript/builtins/error.rs +++ b/nova_vm/src/ecmascript/builtins/error.rs @@ -167,7 +167,6 @@ impl InternalMethods for Error { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { match self.get_backing_object(agent) { @@ -199,7 +198,6 @@ impl InternalMethods for Error { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { match self.get_backing_object(agent) { @@ -220,7 +218,6 @@ impl InternalMethods for Error { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { diff --git a/nova_vm/src/ecmascript/builtins/error/data.rs b/nova_vm/src/ecmascript/builtins/error/data.rs index 544e1ef88..349ae80b9 100644 --- a/nova_vm/src/ecmascript/builtins/error/data.rs +++ b/nova_vm/src/ecmascript/builtins/error/data.rs @@ -14,13 +14,17 @@ use crate::{ pub struct ErrorHeapData { pub(crate) object_index: Option, pub(crate) kind: ExceptionType, - pub(crate) message: Option, + pub(crate) message: Option>, pub(crate) cause: Option, // TODO: stack? name? } impl ErrorHeapData { - pub(crate) fn new(kind: ExceptionType, message: Option, cause: Option) -> Self { + pub(crate) fn new( + kind: ExceptionType, + message: Option>, + cause: Option, + ) -> Self { Self { object_index: None, kind, 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 a13c0b6a1..639141d01 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 @@ -27,7 +27,7 @@ use crate::heap::IntrinsicConstructorIndexes; pub(crate) struct BooleanConstructor; impl Builtin for BooleanConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Boolean; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Boolean; const LENGTH: u8 = 1; @@ -41,7 +41,6 @@ impl BooleanConstructor { fn behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, 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 c7e16d5db..680de11f6 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 @@ -12,14 +12,14 @@ use crate::{ execution::{agent::ExceptionType, Agent, JsResult, RealmIdentifier}, types::{String, Value, BUILTIN_STRING_MEMORY}, }, - engine::context::GcScope, + engine::context::{GcScope, NoGcScope}, }; pub(crate) struct BooleanPrototype; struct BooleanPrototypeToString; impl Builtin for BooleanPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; @@ -29,7 +29,7 @@ impl Builtin for BooleanPrototypeToString { struct BooleanPrototypeValueOf; impl Builtin for BooleanPrototypeValueOf { - const NAME: String = BUILTIN_STRING_MEMORY.valueOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.valueOf; const LENGTH: u8 = 0; @@ -40,12 +40,11 @@ impl Builtin for BooleanPrototypeValueOf { impl BooleanPrototype { fn to_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let b = this_boolean_value(agent, this_value)?; + let b = this_boolean_value(agent, gc.nogc(), this_value)?; if b { Ok(BUILTIN_STRING_MEMORY.r#true.into()) } else { @@ -55,12 +54,11 @@ impl BooleanPrototype { fn value_of( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - this_boolean_value(agent, this_value).map(|result| result.into()) + this_boolean_value(agent, gc.nogc(), this_value).map(|result| result.into()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -96,7 +94,7 @@ impl BooleanPrototype { /// The abstract operation ThisBooleanValue takes argument value (an /// ECMAScript language value) and returns either a normal completion /// containing a Boolean or a throw completion. -fn this_boolean_value(agent: &mut Agent, value: Value) -> JsResult { +fn this_boolean_value(agent: &mut Agent, gc: NoGcScope, value: Value) -> JsResult { // 1. If value is a Boolean, return value. if let Value::Boolean(value) = value { return Ok(value); @@ -111,6 +109,7 @@ fn this_boolean_value(agent: &mut Agent, value: Value) -> JsResult { } // 3. Throw a TypeError exception. Err(agent.throw_exception_with_static_message( + gc, ExceptionType::TypeError, "Not a Boolean or Boolean object", )) 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 42313ff0d..b46f53951 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 @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct AggregateErrorConstructor; impl Builtin for AggregateErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.AggregateError; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.AggregateError; const LENGTH: u8 = 1; @@ -29,7 +29,6 @@ impl AggregateErrorConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, 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 044af778e..2f5f93ac8 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 @@ -31,7 +31,7 @@ use crate::heap::IntrinsicConstructorIndexes; pub(crate) struct ErrorConstructor; impl Builtin for ErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Error; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Error; const LENGTH: u8 = 1; @@ -46,7 +46,6 @@ impl ErrorConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -57,7 +56,11 @@ impl ErrorConstructor { // 3. If message is not undefined, then let message = if !message.is_undefined() { // a. Let msg be ? ToString(message). - Some(to_string(agent, gc.reborrow(), message)?) + Some( + to_string(agent, gc.reborrow(), message)? + .unbind() + .scope(agent, gc.nogc()), + ) } else { None }; @@ -78,6 +81,7 @@ impl ErrorConstructor { )?; let o = Error::try_from(o).unwrap(); // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + let message = message.map(|message| message.get(agent)); let heap_data = &mut agent[o]; heap_data.kind = ExceptionType::Error; heap_data.message = message; @@ -100,7 +104,6 @@ impl ErrorConstructor { pub(super) fn get_error_cause( agent: &mut Agent, mut gc: GcScope<'_, '_>, - options: Value, ) -> JsResult> { let Ok(options) = Object::try_from(options) else { 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 779cff91e..1e21c688a 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 @@ -18,7 +18,7 @@ pub(crate) struct ErrorPrototype; struct ErrorPrototypeToString; impl Builtin for ErrorPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; @@ -31,7 +31,6 @@ impl ErrorPrototype { fn to_string( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -39,6 +38,7 @@ impl ErrorPrototype { // 2. If O is not an Object, throw a TypeError exception. let Ok(o) = Object::try_from(this_value) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "'this' is not an object", )); @@ -52,19 +52,37 @@ impl ErrorPrototype { )?; // 4. If name is undefined, set name to "Error"; otherwise set name to ? ToString(name). let name = if name.is_undefined() { - BUILTIN_STRING_MEMORY.Error + None } else { - to_string(agent, gc.reborrow(), name)? + Some( + to_string(agent, gc.reborrow(), name)? + .unbind() + .scope(agent, gc.nogc()), + ) }; // 5. Let msg be ? Get(O, "message"). let key = PropertyKey::from(BUILTIN_STRING_MEMORY.message); let msg = get(agent, gc.reborrow(), o, key)?; // 6. If msg is undefined, set msg to the empty String; otherwise set msg to ? ToString(msg). let msg = if msg.is_undefined() { - String::EMPTY_STRING + None } else { - to_string(agent, gc, msg)? + Some( + to_string(agent, gc.reborrow(), msg)? + .unbind() + .scope(agent, gc.nogc()), + ) }; + // 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); + // 4. If name is undefined, set name to "Error" + let name = name + .map_or(BUILTIN_STRING_MEMORY.Error, |name| name.get(agent)) + .bind(gc); if name.is_empty_string() { // 7. If name is the empty String, return msg. Ok(msg.into_value()) @@ -74,7 +92,7 @@ impl ErrorPrototype { } else { // 9. Return the string-concatenation of name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg. let result = format!("{}: {}", name.as_str(agent), msg.as_str(agent)); - Ok(String::from_string(agent, result).into_value()) + Ok(String::from_string(agent, gc, result).into_value()) } } 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 19f89ebc0..bb5ed56ad 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 @@ -21,7 +21,7 @@ use super::error_constructor::get_error_cause; struct EvalErrorConstructor; impl Builtin for EvalErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.EvalError; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.EvalError; const LENGTH: u8 = 1; @@ -32,7 +32,7 @@ impl BuiltinIntrinsicConstructor for EvalErrorConstructor { } struct RangeErrorConstructor; impl Builtin for RangeErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.RangeError; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.RangeError; const LENGTH: u8 = 1; @@ -43,7 +43,7 @@ impl BuiltinIntrinsicConstructor for RangeErrorConstructor { } struct ReferenceErrorConstructor; impl Builtin for ReferenceErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.ReferenceError; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.ReferenceError; const LENGTH: u8 = 1; @@ -55,7 +55,7 @@ impl BuiltinIntrinsicConstructor for ReferenceErrorConstructor { } struct SyntaxErrorConstructor; impl Builtin for SyntaxErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.SyntaxError; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.SyntaxError; const LENGTH: u8 = 1; @@ -66,7 +66,7 @@ impl BuiltinIntrinsicConstructor for SyntaxErrorConstructor { } struct TypeErrorConstructor; impl Builtin for TypeErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.TypeError; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.TypeError; const LENGTH: u8 = 1; @@ -77,7 +77,7 @@ impl BuiltinIntrinsicConstructor for TypeErrorConstructor { } struct URIErrorConstructor; impl Builtin for URIErrorConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.URIError; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.URIError; const LENGTH: u8 = 1; @@ -93,7 +93,6 @@ impl NativeErrorConstructors { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - error_kind: ExceptionType, arguments: ArgumentsList, new_target: Option, @@ -126,13 +125,18 @@ impl NativeErrorConstructors { intrinsic, )?; let msg = if !message.is_undefined() { - Some(to_string(agent, gc.reborrow(), message)?) + Some( + to_string(agent, gc.reborrow(), message)? + .unbind() + .scope(agent, gc.nogc()), + ) } else { None }; let cause = get_error_cause(agent, gc.reborrow(), options)?; let o = Error::try_from(o).unwrap(); // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + let msg = msg.map(|msg| msg.get(agent)); let heap_data = &mut agent[o]; heap_data.kind = error_kind; heap_data.message = msg; @@ -143,7 +147,6 @@ impl NativeErrorConstructors { fn eval_behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -154,7 +157,6 @@ impl NativeErrorConstructors { fn range_behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -165,7 +167,6 @@ impl NativeErrorConstructors { fn reference_behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -182,7 +183,6 @@ impl NativeErrorConstructors { fn syntax_behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -193,7 +193,6 @@ impl NativeErrorConstructors { fn type_behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -204,7 +203,6 @@ impl NativeErrorConstructors { fn uri_behaviour( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, 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 7f7ce2273..1b2acfdd9 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 @@ -4,9 +4,9 @@ use oxc_span::SourceType; -use crate::heap::IntrinsicConstructorIndexes; use crate::{ ecmascript::{ + abstract_operations::type_conversion::{to_string, to_string_primitive}, builders::builtin_function_builder::BuiltinFunctionBuilder, builtins::{ make_constructor, ordinary::get_prototype_from_constructor, ordinary_function_create, @@ -18,15 +18,19 @@ use crate::{ RealmIdentifier, }, scripts_and_modules::source_code::SourceCode, - types::{Function, IntoObject, IntoValue, Object, String, Value, BUILTIN_STRING_MEMORY}, + types::{ + Function, IntoObject, IntoValue, Object, Primitive, String, Value, + BUILTIN_STRING_MEMORY, + }, }, engine::context::GcScope, + heap::IntrinsicConstructorIndexes, }; pub(crate) struct FunctionConstructor; impl Builtin for FunctionConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Function; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Function; const LENGTH: u8 = 1; @@ -40,7 +44,6 @@ impl FunctionConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -87,6 +90,7 @@ impl FunctionConstructor { } } +#[derive(Clone, Copy)] pub(crate) enum DynamicFunctionKind { Normal, Generator, @@ -124,7 +128,6 @@ impl DynamicFunctionKind { pub(crate) fn create_dynamic_function( agent: &mut Agent, mut gc: GcScope<'_, '_>, - constructor: Function, kind: DynamicFunctionKind, parameter_args: &[Value], @@ -136,27 +139,73 @@ pub(crate) fn create_dynamic_function( .host_ensure_can_compile_strings(agent.current_realm_mut())?; let source_string = { - // format!("{} anonymous({}\n) {{\n{}\n}}", kind.prefix(), parameters, body_arg) - let parameter_strings = parameter_args - .iter() - .map(|param| param.to_string(agent, gc.reborrow())) - .collect::>>()?; - let body_string = body_arg.to_string(agent, gc.reborrow())?; + let parameter_strings_vec; + let parameter_strings_slice; + let body_string; + if body_arg.is_string() && parameter_args.iter().all(|arg| arg.is_string()) { + body_string = String::try_from(body_arg).unwrap().bind(gc.nogc()); + parameter_strings_slice = + // Safety: All the strings were checked to be strings. + unsafe { std::mem::transmute::<&[Value], &[String<'_>]>(parameter_args) }; + } else if body_arg.is_primitive() && parameter_args.iter().all(|arg| arg.is_primitive()) { + // We don't need to call JavaScript here. Nice. + let gc = gc.nogc(); + let mut parameter_strings = Vec::with_capacity(parameter_args.len()); + for param in parameter_args { + parameter_strings.push(to_string_primitive( + agent, + gc, + Primitive::try_from(*param).unwrap(), + )?); + } + parameter_strings_vec = parameter_strings; + parameter_strings_slice = ¶meter_strings_vec; + body_string = + to_string_primitive(agent, gc, Primitive::try_from(body_arg).unwrap())?.bind(gc); + } else { + // Some of the parameters are non-primitives. This means we'll be + // calling into JavaScript during this work. + let mut parameter_string_roots = Vec::with_capacity(parameter_args.len()); + for param in parameter_args { + // Each parameter has to be rooted in case the next parameter + // or the body argument is the one that calls to JavaScript. + parameter_string_roots.push( + to_string(agent, gc.reborrow(), *param)? + .unbind() + .scope(agent, gc.nogc()), + ); + } + let body_string_unbound = body_arg.to_string(agent, gc.reborrow())?.unbind(); + // We've done all our potential JavaScript calling: Now we rest. + let gc = gc.nogc(); + body_string = body_string_unbound.bind(gc); + let parameter_strings = parameter_string_roots + .into_iter() + .map(|param_root| param_root.get(agent).bind(gc)) + .collect::>(); - let mut str_len = kind.prefix().len() + body_string.len(agent) + 18; - if !parameter_strings.is_empty() { - // Separated by a single comma character - str_len += parameter_strings - .iter() - .map(|str| str.len(agent) + 1) - .sum::() - - 1; + parameter_strings_vec = parameter_strings; + parameter_strings_slice = ¶meter_strings_vec; } + // format!("{} anonymous({}\n) {{\n{}\n}}", kind.prefix(), parameters, body_arg) + let str_len = kind.prefix().len() + + 18 + + body_string.len(agent) + + if !parameter_strings_slice.is_empty() { + // Separated by a single comma character + parameter_strings_slice + .iter() + .map(|str| str.len(agent) + 1) + .sum::() + - 1 + } else { + 0 + }; let mut string = std::string::String::with_capacity(str_len); string.push_str(kind.prefix()); string.push_str(" anonymous("); - for (i, parameter) in parameter_strings.iter().enumerate() { + for (i, parameter) in parameter_strings_slice.iter().enumerate() { if i != 0 { string.push(','); } @@ -165,9 +214,10 @@ pub(crate) fn create_dynamic_function( string.push_str("\n) {\n"); string.push_str(body_string.as_str(agent)); string.push_str("\n}"); + debug_assert_eq!(string.len(), str_len); - String::from_string(agent, string) + String::from_string(agent, gc.nogc(), string) }; // The spec says to parse the parameters and the function body separately to @@ -183,7 +233,8 @@ pub(crate) fn create_dynamic_function( // GC'd before the program is dropped. If this function returns // successfully, then the program's AST and the SourceCode will both be // kept alive in the returned function object. - let parsed_result = unsafe { SourceCode::parse_source(agent, source_string, source_type) }; + let parsed_result = + unsafe { SourceCode::parse_source(agent, gc.nogc(), source_string, source_type) }; if let Ok((program, sc)) = parsed_result { source_code = Some(sc); @@ -221,6 +272,7 @@ pub(crate) fn create_dynamic_function( ); } return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::SyntaxError, "Invalid function source text.", )); @@ -230,7 +282,7 @@ pub(crate) fn create_dynamic_function( let params = OrdinaryFunctionCreateParams { function_prototype: get_prototype_from_constructor( agent, - gc, + gc.reborrow(), constructor, kind.intrinsic_prototype(), )?, @@ -245,10 +297,11 @@ pub(crate) fn create_dynamic_function( env: EnvironmentIndex::Global(agent.current_realm().global_env.unwrap()), private_env: None, }; - let f = ordinary_function_create(agent, params); + let f = ordinary_function_create(agent, gc.nogc(), params); set_function_name( agent, + gc.nogc(), f, BUILTIN_STRING_MEMORY.anonymous.to_property_key(), None, 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 3ac701fce..80b199f70 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 @@ -32,7 +32,7 @@ use crate::{ pub(crate) struct FunctionPrototype; impl Builtin for FunctionPrototype { - const NAME: String = String::EMPTY_STRING; + const NAME: String<'static> = String::EMPTY_STRING; const LENGTH: u8 = 0; @@ -44,7 +44,7 @@ impl BuiltinIntrinsicConstructor for FunctionPrototype { struct FunctionPrototypeApply; impl Builtin for FunctionPrototypeApply { - const NAME: String = BUILTIN_STRING_MEMORY.apply; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.apply; const LENGTH: u8 = 2; @@ -54,7 +54,7 @@ impl Builtin for FunctionPrototypeApply { struct FunctionPrototypeBind; impl Builtin for FunctionPrototypeBind { - const NAME: String = BUILTIN_STRING_MEMORY.bind; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.bind; const LENGTH: u8 = 1; @@ -64,7 +64,7 @@ impl Builtin for FunctionPrototypeBind { struct FunctionPrototypeCall; impl Builtin for FunctionPrototypeCall { - const NAME: String = BUILTIN_STRING_MEMORY.call; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.call; const LENGTH: u8 = 1; @@ -74,7 +74,7 @@ impl Builtin for FunctionPrototypeCall { struct FunctionPrototypeToString; impl Builtin for FunctionPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; @@ -84,7 +84,7 @@ impl Builtin for FunctionPrototypeToString { struct FunctionPrototypeHasInstance; impl Builtin for FunctionPrototypeHasInstance { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_hasInstance_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_hasInstance_; const KEY: Option = Some(WellKnownSymbolIndexes::HasInstance.to_property_key()); @@ -106,7 +106,6 @@ impl FunctionPrototype { fn apply( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { @@ -114,6 +113,7 @@ impl FunctionPrototype { let Some(func) = is_callable(this_value) else { // 2. If IsCallable(func) is false, throw a TypeError exception. return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Not a callable value", )); @@ -149,7 +149,6 @@ impl FunctionPrototype { fn bind( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { @@ -160,6 +159,7 @@ impl FunctionPrototype { // 2. If IsCallable(Target) is false, throw a TypeError exception. let Some(target) = is_callable(target) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Cannot bind a non-callable object", )); @@ -236,6 +236,7 @@ impl FunctionPrototype { // 10. Perform SetFunctionName(F, targetName, "bound"). set_function_name( agent, + gc.nogc(), f, target_name.into(), Some(BUILTIN_STRING_MEMORY.bound), @@ -248,12 +249,12 @@ impl FunctionPrototype { fn call( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { let Some(func) = is_callable(this_value) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Not a callable value", )); @@ -266,8 +267,7 @@ impl FunctionPrototype { fn to_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -275,6 +275,7 @@ impl FunctionPrototype { let Ok(func) = Function::try_from(this_value) else { // 5. Throw a TypeError exception. return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Not a callable value", )); @@ -291,7 +292,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)) + Ok(Value::from_string(agent, gc.nogc(), source_text)) } // 4. If func is an Object and IsCallable(func) is true, return an // implementation-defined String source code representation of func. @@ -317,16 +318,19 @@ impl FunctionPrototype { } }, ); - Ok(Value::from_string(agent, initial_name)) + Ok(Value::from_string(agent, gc.nogc(), initial_name)) } Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(_) => { - Ok(Value::from_static_str(agent, "class { [ native code ] }")) - } + Function::BuiltinConstructorFunction(_) => Ok(Value::from_static_str( + agent, + gc.nogc(), + "class { [ native code ] }", + )), Function::BuiltinPromiseResolvingFunction(_) => { // Promise resolving functions have no initial name. Ok(Value::from_static_str( agent, + gc.nogc(), "function () { [ native code ] }", )) } @@ -342,7 +346,6 @@ impl FunctionPrototype { fn has_instance( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { @@ -397,7 +400,7 @@ impl FunctionPrototype { struct ThrowTypeError; impl Builtin for ThrowTypeError { - const NAME: String = String::EMPTY_STRING; + const NAME: String<'static> = String::EMPTY_STRING; const LENGTH: u8 = 0; @@ -410,12 +413,11 @@ impl BuiltinIntrinsic for ThrowTypeError { impl ThrowTypeError { fn behaviour( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, _: Value, _: ArgumentsList, ) -> 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")) + Err(agent.throw_exception_with_static_message(gc.nogc(),ExceptionType::TypeError, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { 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 3f18af122..9cb3f2763 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 @@ -36,7 +36,7 @@ use crate::{ pub(crate) struct ObjectConstructor; impl Builtin for ObjectConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Object; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Object; const LENGTH: u8 = 1; @@ -49,7 +49,7 @@ impl BuiltinIntrinsicConstructor for ObjectConstructor { struct ObjectAssign; impl Builtin for ObjectAssign { - const NAME: String = BUILTIN_STRING_MEMORY.assign; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.assign; const LENGTH: u8 = 2; @@ -59,7 +59,7 @@ impl Builtin for ObjectAssign { struct ObjectCreate; impl Builtin for ObjectCreate { - const NAME: String = BUILTIN_STRING_MEMORY.create; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.create; const LENGTH: u8 = 2; @@ -68,7 +68,7 @@ impl Builtin for ObjectCreate { struct ObjectDefineProperties; impl Builtin for ObjectDefineProperties { - const NAME: String = BUILTIN_STRING_MEMORY.defineProperties; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.defineProperties; const LENGTH: u8 = 2; @@ -77,7 +77,7 @@ impl Builtin for ObjectDefineProperties { struct ObjectDefineProperty; impl Builtin for ObjectDefineProperty { - const NAME: String = BUILTIN_STRING_MEMORY.defineProperty; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.defineProperty; const LENGTH: u8 = 2; @@ -86,7 +86,7 @@ impl Builtin for ObjectDefineProperty { struct ObjectEntries; impl Builtin for ObjectEntries { - const NAME: String = BUILTIN_STRING_MEMORY.entries; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.entries; const LENGTH: u8 = 1; @@ -95,7 +95,7 @@ impl Builtin for ObjectEntries { struct ObjectFreeze; impl Builtin for ObjectFreeze { - const NAME: String = BUILTIN_STRING_MEMORY.freeze; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.freeze; const LENGTH: u8 = 1; @@ -104,7 +104,7 @@ impl Builtin for ObjectFreeze { struct ObjectFromEntries; impl Builtin for ObjectFromEntries { - const NAME: String = BUILTIN_STRING_MEMORY.fromEntries; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.fromEntries; const LENGTH: u8 = 1; @@ -113,7 +113,7 @@ impl Builtin for ObjectFromEntries { struct ObjectGetOwnPropertyDescriptor; impl Builtin for ObjectGetOwnPropertyDescriptor { - const NAME: String = BUILTIN_STRING_MEMORY.getOwnPropertyDescriptor; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getOwnPropertyDescriptor; const LENGTH: u8 = 2; @@ -122,7 +122,7 @@ impl Builtin for ObjectGetOwnPropertyDescriptor { struct ObjectGetOwnPropertyDescriptors; impl Builtin for ObjectGetOwnPropertyDescriptors { - const NAME: String = BUILTIN_STRING_MEMORY.getOwnPropertyDescriptors; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getOwnPropertyDescriptors; const LENGTH: u8 = 1; @@ -132,7 +132,7 @@ impl Builtin for ObjectGetOwnPropertyDescriptors { struct ObjectGetOwnPropertyNames; impl Builtin for ObjectGetOwnPropertyNames { - const NAME: String = BUILTIN_STRING_MEMORY.getOwnPropertyNames; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getOwnPropertyNames; const LENGTH: u8 = 1; @@ -141,7 +141,7 @@ impl Builtin for ObjectGetOwnPropertyNames { struct ObjectGetOwnPropertySymbols; impl Builtin for ObjectGetOwnPropertySymbols { - const NAME: String = BUILTIN_STRING_MEMORY.getOwnPropertySymbols; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getOwnPropertySymbols; const LENGTH: u8 = 1; @@ -150,7 +150,7 @@ impl Builtin for ObjectGetOwnPropertySymbols { struct ObjectGetPrototypeOf; impl Builtin for ObjectGetPrototypeOf { - const NAME: String = BUILTIN_STRING_MEMORY.getPrototypeOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getPrototypeOf; const LENGTH: u8 = 1; @@ -159,7 +159,7 @@ impl Builtin for ObjectGetPrototypeOf { struct ObjectGroupBy; impl Builtin for ObjectGroupBy { - const NAME: String = BUILTIN_STRING_MEMORY.groupBy; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.groupBy; const LENGTH: u8 = 2; @@ -168,7 +168,7 @@ impl Builtin for ObjectGroupBy { struct ObjectHasOwn; impl Builtin for ObjectHasOwn { - const NAME: String = BUILTIN_STRING_MEMORY.hasOwn; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.hasOwn; const LENGTH: u8 = 2; @@ -177,7 +177,7 @@ impl Builtin for ObjectHasOwn { struct ObjectIs; impl Builtin for ObjectIs { - const NAME: String = BUILTIN_STRING_MEMORY.is; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.is; const LENGTH: u8 = 2; @@ -186,7 +186,7 @@ impl Builtin for ObjectIs { struct ObjectIsExtensible; impl Builtin for ObjectIsExtensible { - const NAME: String = BUILTIN_STRING_MEMORY.isExtensible; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isExtensible; const LENGTH: u8 = 1; @@ -195,7 +195,7 @@ impl Builtin for ObjectIsExtensible { struct ObjectIsFrozen; impl Builtin for ObjectIsFrozen { - const NAME: String = BUILTIN_STRING_MEMORY.isFrozen; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isFrozen; const LENGTH: u8 = 1; @@ -204,7 +204,7 @@ impl Builtin for ObjectIsFrozen { struct ObjectIsSealed; impl Builtin for ObjectIsSealed { - const NAME: String = BUILTIN_STRING_MEMORY.isSealed; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isSealed; const LENGTH: u8 = 1; @@ -213,7 +213,7 @@ impl Builtin for ObjectIsSealed { struct ObjectKeys; impl Builtin for ObjectKeys { - const NAME: String = BUILTIN_STRING_MEMORY.keys; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.keys; const LENGTH: u8 = 1; @@ -222,7 +222,7 @@ impl Builtin for ObjectKeys { struct ObjectPreventExtensions; impl Builtin for ObjectPreventExtensions { - const NAME: String = BUILTIN_STRING_MEMORY.preventExtensions; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.preventExtensions; const LENGTH: u8 = 1; @@ -232,7 +232,7 @@ impl Builtin for ObjectPreventExtensions { struct ObjectSeal; impl Builtin for ObjectSeal { - const NAME: String = BUILTIN_STRING_MEMORY.seal; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.seal; const LENGTH: u8 = 1; @@ -241,7 +241,7 @@ impl Builtin for ObjectSeal { struct ObjectSetPrototypeOf; impl Builtin for ObjectSetPrototypeOf { - const NAME: String = BUILTIN_STRING_MEMORY.setPrototypeOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setPrototypeOf; const LENGTH: u8 = 2; @@ -250,7 +250,7 @@ impl Builtin for ObjectSetPrototypeOf { struct ObjectValues; impl Builtin for ObjectValues { - const NAME: String = BUILTIN_STRING_MEMORY.values; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.values; const LENGTH: u8 = 1; @@ -262,7 +262,6 @@ impl ObjectConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -293,7 +292,7 @@ impl ObjectConstructor { ) } else { // 3. Return ! ToObject(value). - Ok(to_object(agent, value).unwrap().into_value()) + Ok(to_object(agent, gc.nogc(), value).unwrap().into_value()) } } @@ -304,13 +303,12 @@ impl ObjectConstructor { fn assign( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let target = arguments.get(0); // 1. Let to be ? ToObject(target). - let to = to_object(agent, target)?; + let to = to_object(agent, gc.nogc(), target)?; // 2. If only one argument was passed, return to. if arguments.len() <= 1 { return Ok(to.into_value()); @@ -323,7 +321,7 @@ impl ObjectConstructor { continue; } // i. Let from be ! ToObject(nextSource). - let from = to_object(agent, *next_source)?; + let from = to_object(agent, gc.nogc(), *next_source)?; // ii. Let keys be ? from.[[OwnPropertyKeys]](). let keys = from.internal_own_property_keys(agent, gc.reborrow())?; // iii. For each element nextKey of keys, do @@ -350,7 +348,6 @@ impl ObjectConstructor { fn create( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -364,7 +361,7 @@ impl ObjectConstructor { "{} is not an object or null", o.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; let properties = arguments.get(1); if properties != Value::Undefined { @@ -380,7 +377,6 @@ impl ObjectConstructor { fn define_properties( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -392,7 +388,7 @@ impl ObjectConstructor { "{} is not an object", o.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 2. Return ? ObjectDefineProperties(O, Properties). let result = object_define_properties(agent, gc.reborrow(), o, properties)?; @@ -406,7 +402,6 @@ impl ObjectConstructor { fn define_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -419,7 +414,7 @@ impl ObjectConstructor { "{} is not an object", o.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 2. Let key be ? ToPropertyKey(P). let key = to_property_key(agent, gc.reborrow(), p)?; @@ -434,26 +429,24 @@ impl ObjectConstructor { fn entries( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); // 1. Let obj be ? ToObject(O). - let obj = to_object(agent, o)?; + let obj = to_object(agent, gc.nogc(), o)?; // 2. Let entryList be ? EnumerableOwnProperties(obj, KEY+VALUE). let entry_list = enumerable_own_properties::< enumerable_properties_kind::EnumerateKeysAndValues, >(agent, gc.reborrow(), obj)?; // 3. Return CreateArrayFromList(entryList). - Ok(create_array_from_list(agent, &entry_list).into_value()) + Ok(create_array_from_list(agent, gc.nogc(), &entry_list).into_value()) } /// ### [20.1.2.6 Object.freeze ( O )](https://tc39.es/ecma262/#sec-object.freeze) fn freeze( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -467,6 +460,7 @@ impl ObjectConstructor { if !status { // 3. If status is false, throw a TypeError exception. Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Could not freeze object", )) @@ -480,7 +474,6 @@ impl ObjectConstructor { fn from_entries( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -577,7 +570,7 @@ impl ObjectConstructor { } } // 1. Perform ? RequireObjectCoercible(iterable). - require_object_coercible(agent, iterable)?; + require_object_coercible(agent, gc.nogc(), iterable)?; // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%). let obj = ordinary_object_create_with_intrinsics(agent, Some(ProtoIntrinsics::Object), None); @@ -600,14 +593,13 @@ impl ObjectConstructor { fn get_own_property_descriptor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); let p = arguments.get(1); // 1. Let obj be ? ToObject(O). - let obj = to_object(agent, o)?; + let obj = to_object(agent, gc.nogc(), o)?; // 2. Let key be ? ToPropertyKey(P). let key = to_property_key(agent, gc.reborrow(), p)?; // 3. Let desc be ? obj.[[GetOwnProperty]](key). @@ -621,13 +613,12 @@ impl ObjectConstructor { fn get_own_property_descriptors( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); // 1. Let obj be ? ToObject(O). - let obj = to_object(agent, o)?; + let obj = to_object(agent, gc.nogc(), o)?; // 2. Let ownKeys be ? obj.[[OwnPropertyKeys]](). let own_keys = obj.internal_own_property_keys(agent, gc.reborrow())?; @@ -660,39 +651,36 @@ impl ObjectConstructor { fn get_own_property_names( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); // 1. Return CreateArrayFromList(? GetOwnPropertyKeys(O, STRING)). let keys = get_own_string_property_keys(agent, gc.reborrow(), o)?; - Ok(create_array_from_list(agent, &keys).into_value()) + Ok(create_array_from_list(agent, gc.nogc(), &keys).into_value()) } /// ### [20.1.2.11 Object.getOwnPropertySymbols ( O )](https://tc39.es/ecma262/#sec-object.getownpropertysymbols) fn get_own_property_symbols( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); // 1. Return CreateArrayFromList(? GetOwnPropertyKeys(O, SYMBOL)). let keys = get_own_symbol_property_keys(agent, gc.reborrow(), o)?; - Ok(create_array_from_list(agent, &keys).into_value()) + Ok(create_array_from_list(agent, gc.nogc(), &keys).into_value()) } /// ### [20.1.2.12 Object.getPrototypeOf ( O )](https://tc39.es/ecma262/#sec-object.getprototypeof) fn get_prototype_of( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { - let obj = to_object(agent, arguments.get(0))?; + let obj = to_object(agent, gc.nogc(), arguments.get(0))?; obj.internal_get_prototype_of(agent, gc) .map(|proto| proto.map_or(Value::Null, |proto| proto.into_value())) } @@ -701,7 +689,6 @@ impl ObjectConstructor { fn group_by( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -718,7 +705,7 @@ impl ObjectConstructor { // 3. For each Record { [[Key]], [[Elements]] } g of groups, do for g in groups { // a. Let elements be CreateArrayFromList(g.[[Elements]]). - let elements = create_array_from_list(agent, &g.elements).into_value(); + let elements = create_array_from_list(agent, gc.nogc(), &g.elements).into_value(); // b. Perform ! CreateDataPropertyOrThrow(obj, g.[[Key]], elements). create_data_property_or_throw(agent, gc.reborrow(), object, g.key, elements)?; @@ -731,11 +718,10 @@ impl ObjectConstructor { fn has_own( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { - let obj = to_object(agent, arguments.get(0))?; + let obj = to_object(agent, gc.nogc(), arguments.get(0))?; let key = to_property_key(agent, gc.reborrow(), arguments.get(1))?; has_own_property(agent, gc.reborrow(), obj, key).map(|result| result.into()) } @@ -743,7 +729,6 @@ impl ObjectConstructor { fn is( agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -753,7 +738,6 @@ impl ObjectConstructor { fn is_extensible( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -768,7 +752,6 @@ impl ObjectConstructor { fn is_frozen( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -783,7 +766,6 @@ impl ObjectConstructor { fn is_sealed( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -799,14 +781,13 @@ impl ObjectConstructor { fn keys( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); println!("key target: {:?}", o); // 1. Let obj be ? ToObject(O). - let obj = to_object(agent, o)?; + let obj = to_object(agent, gc.nogc(), o)?; // 2. Let keyList be ? EnumerableOwnProperties(obj, KEY). let key_list = enumerable_own_properties::( agent, @@ -814,14 +795,13 @@ impl ObjectConstructor { obj, )?; // 3. Return CreateArrayFromList(keyList). - Ok(create_array_from_list(agent, &key_list).into_value()) + Ok(create_array_from_list(agent, gc.nogc(), &key_list).into_value()) } /// ### [20.1.2.20 Object.preventExtensions ( O )](https://tc39.es/ecma262/#sec-object.preventextensions) fn prevent_extensions( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -835,6 +815,7 @@ impl ObjectConstructor { // 3. If status is false, throw a TypeError exception. if !status { Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Could not prevent extensions", )) @@ -848,7 +829,6 @@ impl ObjectConstructor { fn seal( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -862,6 +842,7 @@ impl ObjectConstructor { if !status { // 3. If status is false, throw a TypeError exception. Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Could not seal object", )) @@ -875,14 +856,13 @@ impl ObjectConstructor { fn set_prototype_of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); let proto = arguments.get(1); // 1. Set O to ? RequireObjectCoercible(O). - let o = require_object_coercible(agent, o)?; + let o = require_object_coercible(agent, gc.nogc(), o)?; // 2. If proto is not an Object and proto is not null, throw a TypeError exception. let proto = if let Ok(proto) = Object::try_from(proto) { Some(proto) @@ -893,7 +873,7 @@ impl ObjectConstructor { "{} is not an object or null", proto.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 3. If O is not an Object, return O. let Ok(o) = Object::try_from(o) else { @@ -904,6 +884,7 @@ impl ObjectConstructor { // 5. If status is false, throw a TypeError exception. if !status { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Could not set prototype", )); @@ -915,13 +896,12 @@ impl ObjectConstructor { fn values( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { let o = arguments.get(0); // 1. Let obj be ? ToObject(O). - let obj = to_object(agent, o)?; + let obj = to_object(agent, gc.nogc(), o)?; // 2. Let valueList be ? EnumerableOwnProperties(obj, VALUE). let value_list = enumerable_own_properties::( agent, @@ -929,7 +909,7 @@ impl ObjectConstructor { obj, )?; // 3. Return CreateArrayFromList(valueList). - Ok(create_array_from_list(agent, &value_list).into_value()) + Ok(create_array_from_list(agent, gc.nogc(), &value_list).into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -974,12 +954,11 @@ impl ObjectConstructor { fn object_define_properties( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: T, properties: Value, ) -> JsResult { // 1. Let props be ? ToObject(Properties). - let props = to_object(agent, properties)?; + let props = to_object(agent, gc.nogc(), properties)?; // 2. Let keys be ? props.[[OwnPropertyKeys]](). let keys = props.internal_own_property_keys(agent, gc.reborrow())?; // 3. Let descriptors be a new empty List. @@ -1033,7 +1012,6 @@ fn object_define_properties( pub fn add_entries_from_iterable_from_entries( agent: &mut Agent, mut gc: GcScope<'_, '_>, - target: OrdinaryObject, iterable: Value, ) -> JsResult { @@ -1055,7 +1033,7 @@ pub fn add_entries_from_iterable_from_entries( "Invalid iterator next return value: {} is not an object", next.string_repr(agent, gc.reborrow(),).as_str(agent) ); - let error = agent.throw_exception(ExceptionType::TypeError, error_message); + let error = agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message); // ii. Return ? IteratorClose(iteratorRecord, error). iterator_close(agent, gc.reborrow(), &iterator_record, Err(error))?; return Ok(target); @@ -1097,11 +1075,10 @@ pub fn add_entries_from_iterable_from_entries( fn get_own_string_property_keys( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Value, ) -> JsResult> { // 1. Let obj be ? ToObject(O). - let obj = to_object(agent, o)?; + let obj = to_object(agent, gc.nogc(), o)?; // 2. Let keys be ? obj.[[OwnPropertyKeys]](). let keys = obj.internal_own_property_keys(agent, gc.reborrow())?; // 3. Let nameList be a new empty List. @@ -1113,7 +1090,7 @@ fn get_own_string_property_keys( // i. Append nextKey to nameList. PropertyKey::Integer(next_key) => { let next_key = format!("{}", next_key.into_i64()); - name_list.push(Value::from_string(agent, next_key)); + name_list.push(Value::from_string(agent, gc.nogc(), next_key)); } PropertyKey::SmallString(next_key) => name_list.push(Value::SmallString(next_key)), PropertyKey::String(next_key) => name_list.push(Value::String(next_key)), @@ -1127,11 +1104,10 @@ fn get_own_string_property_keys( fn get_own_symbol_property_keys( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Value, ) -> JsResult> { // 1. Let obj be ? ToObject(O). - let obj = to_object(agent, o)?; + let obj = to_object(agent, gc.nogc(), o)?; // 2. Let keys be ? obj.[[OwnPropertyKeys]](). let keys = obj.internal_own_property_keys(agent, gc.reborrow())?; // 3. Let nameList be a new empty List. 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 8677603ec..d148a1269 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 @@ -25,7 +25,7 @@ pub(crate) struct ObjectPrototype; struct ObjectPrototypeHasOwnProperty; impl Builtin for ObjectPrototypeHasOwnProperty { - const NAME: String = BUILTIN_STRING_MEMORY.hasOwnProperty; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.hasOwnProperty; const LENGTH: u8 = 1; @@ -34,7 +34,7 @@ impl Builtin for ObjectPrototypeHasOwnProperty { struct ObjectPrototypeIsPrototypeOf; impl Builtin for ObjectPrototypeIsPrototypeOf { - const NAME: String = BUILTIN_STRING_MEMORY.isPrototypeOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isPrototypeOf; const LENGTH: u8 = 1; @@ -43,7 +43,7 @@ impl Builtin for ObjectPrototypeIsPrototypeOf { struct ObjectPrototypePropertyIsEnumerable; impl Builtin for ObjectPrototypePropertyIsEnumerable { - const NAME: String = BUILTIN_STRING_MEMORY.propertyIsEnumerable; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.propertyIsEnumerable; const LENGTH: u8 = 1; @@ -52,7 +52,7 @@ impl Builtin for ObjectPrototypePropertyIsEnumerable { struct ObjectPrototypeToLocaleString; impl Builtin for ObjectPrototypeToLocaleString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleString; const LENGTH: u8 = 0; @@ -61,7 +61,7 @@ impl Builtin for ObjectPrototypeToLocaleString { struct ObjectPrototypeToString; impl Builtin for ObjectPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; @@ -73,7 +73,7 @@ impl BuiltinIntrinsic for ObjectPrototypeToString { struct ObjectPrototypeValueOf; impl Builtin for ObjectPrototypeValueOf { - const NAME: String = BUILTIN_STRING_MEMORY.valueOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.valueOf; const LENGTH: u8 = 0; @@ -84,19 +84,17 @@ impl ObjectPrototype { fn has_own_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { let p = to_property_key(agent, gc.reborrow(), arguments.get(0))?; - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; has_own_property(agent, gc.reborrow(), o, p).map(|result| result.into()) } fn is_prototype_of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -104,7 +102,7 @@ impl ObjectPrototype { let Ok(mut v) = Object::try_from(v) else { return Ok(false.into()); }; - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; loop { let proto = v.internal_get_prototype_of(agent, gc.reborrow())?; if let Some(proto) = proto { @@ -121,12 +119,11 @@ impl ObjectPrototype { fn property_is_enumerable( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { let p = to_property_key(agent, gc.reborrow(), arguments.get(0))?; - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; let desc = o.internal_get_own_property(agent, gc.reborrow(), p)?; if let Some(desc) = desc { Ok(desc.enumerable.unwrap_or(false).into()) @@ -138,7 +135,6 @@ impl ObjectPrototype { fn to_locale_string( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -150,7 +146,6 @@ impl ObjectPrototype { fn to_string( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -209,7 +204,7 @@ impl ObjectPrototype { PrimitiveObjectData::Symbol(_) | PrimitiveObjectData::BigInt(_) | PrimitiveObjectData::SmallBigInt(_) => { - let o = to_object(agent, this_value).unwrap(); + let o = to_object(agent, gc.nogc(), this_value).unwrap(); let tag = get( agent, gc.reborrow(), @@ -218,11 +213,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)) + Ok(Value::from_string(agent, gc.nogc(), str)) } else { let str = format!("[object {}]", BUILTIN_STRING_MEMORY.Object.as_str(agent)); - Ok(Value::from_string(agent, str)) + Ok(Value::from_string(agent, gc.nogc(), str)) } } }, @@ -230,7 +225,7 @@ impl ObjectPrototype { // 3. Let O be ! ToObject(this value). // 15. Let tag be ? Get(O, @@toStringTag). // 16. If tag is not a String, set tag to builtinTag. - let o = to_object(agent, this_value).unwrap(); + let o = to_object(agent, gc.nogc(), this_value).unwrap(); let tag = get( agent, gc.reborrow(), @@ -239,11 +234,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)) + Ok(Value::from_string(agent, gc.nogc(), str)) } else { // 14. Else, let builtinTag be "Object". let str = format!("[object {}]", BUILTIN_STRING_MEMORY.Object.as_str(agent)); - Ok(Value::from_string(agent, str)) + Ok(Value::from_string(agent, gc.nogc(), str)) } } } @@ -251,12 +246,11 @@ impl ObjectPrototype { fn value_of( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _arguments: ArgumentsList, ) -> JsResult { - to_object(agent, this_value).map(|result| result.into_value()) + to_object(agent, gc.nogc(), this_value).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 90f9183a8..9e43f8482 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 @@ -28,7 +28,7 @@ use crate::heap::WellKnownSymbolIndexes; pub(crate) struct SymbolConstructor; impl Builtin for SymbolConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Symbol; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Symbol; const LENGTH: u8 = 0; @@ -41,7 +41,7 @@ impl BuiltinIntrinsicConstructor for SymbolConstructor { struct SymbolFor; impl Builtin for SymbolFor { - const NAME: String = BUILTIN_STRING_MEMORY.r#for; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#for; const LENGTH: u8 = 1; @@ -51,7 +51,7 @@ impl Builtin for SymbolFor { struct SymbolKeyFor; impl Builtin for SymbolKeyFor { - const NAME: String = BUILTIN_STRING_MEMORY.keyFor; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.keyFor; const LENGTH: u8 = 1; @@ -61,14 +61,14 @@ impl Builtin for SymbolKeyFor { impl SymbolConstructor { fn behaviour( agent: &mut Agent, - mut gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, _this_value: Value, arguments: ArgumentsList, new_target: Option, ) -> JsResult { if new_target.is_some() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Symbol is not a constructor", )); @@ -77,7 +77,7 @@ impl SymbolConstructor { let desc_string = if description.is_undefined() { None } else { - Some(to_string(agent, gc.reborrow(), description)?) + Some(to_string(agent, gc, description)?.unbind()) }; Ok(agent @@ -91,7 +91,6 @@ impl SymbolConstructor { fn r#for( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -101,7 +100,6 @@ impl SymbolConstructor { fn key_for( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { 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 566f0d2ce..9331697da 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,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::{GcScope, NoGcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -19,7 +19,7 @@ pub(crate) struct SymbolPrototype; struct SymbolPrototypeGetDescription; impl Builtin for SymbolPrototypeGetDescription { - const NAME: String = BUILTIN_STRING_MEMORY.get_description; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_description; const KEY: Option = Some(BUILTIN_STRING_MEMORY.description.to_property_key()); @@ -32,7 +32,7 @@ impl BuiltinGetter for SymbolPrototypeGetDescription {} struct SymbolPrototypeToString; impl Builtin for SymbolPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; @@ -42,7 +42,7 @@ impl Builtin for SymbolPrototypeToString { struct SymbolPrototypeValueOf; impl Builtin for SymbolPrototypeValueOf { - const NAME: String = BUILTIN_STRING_MEMORY.valueOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.valueOf; const LENGTH: u8 = 0; @@ -52,7 +52,7 @@ impl Builtin for SymbolPrototypeValueOf { struct SymbolPrototypeToPrimitive; impl Builtin for SymbolPrototypeToPrimitive { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_toPrimitive_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_toPrimitive_; const KEY: Option = Some(WellKnownSymbolIndexes::ToPrimitive.to_property_key()); @@ -71,14 +71,13 @@ impl SymbolPrototype { /// function is undefined. fn get_description( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let s be the this value. // 2. Let sym be ? ThisSymbolValue(s). - let sym = this_symbol_value(agent, this_value)?; + let sym = this_symbol_value(agent, gc.nogc(), this_value)?; // 3. Return sym.[[Description]]. agent[sym] .descriptor @@ -87,23 +86,21 @@ impl SymbolPrototype { fn to_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let symb = this_symbol_value(agent, this_value)?; - Ok(symbol_descriptive_string(agent, symb).into_value()) + let symb = this_symbol_value(agent, gc.nogc(), this_value)?; + Ok(symbol_descriptive_string(agent, gc.nogc(), symb).into_value()) } fn value_of( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - this_symbol_value(agent, this_value).map(|res| res.into_value()) + this_symbol_value(agent, gc.nogc(), this_value).map(|res| res.into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -177,15 +174,18 @@ impl SymbolPrototype { } #[inline(always)] -fn this_symbol_value(agent: &mut Agent, value: Value) -> JsResult { +fn this_symbol_value(agent: &mut Agent, gc: NoGcScope, value: Value) -> JsResult { match value { Value::Symbol(symbol) => Ok(symbol), Value::PrimitiveObject(object) if object.is_symbol_object(agent) => { let s: Symbol = agent[object].data.try_into().unwrap(); Ok(s) } - _ => Err(agent - .throw_exception_with_static_message(ExceptionType::TypeError, "this is not a symbol")), + _ => Err(agent.throw_exception_with_static_message( + gc, + ExceptionType::TypeError, + "this is not a symbol", + )), } } @@ -193,7 +193,11 @@ fn this_symbol_value(agent: &mut Agent, value: Value) -> JsResult { /// /// The abstract operation SymbolDescriptiveString takes argument sym (a Symbol) /// and returns a String. -fn symbol_descriptive_string(agent: &mut Agent, sym: Symbol) -> String { +fn symbol_descriptive_string<'gc>( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + sym: Symbol, +) -> String<'gc> { // 1. Let desc be sym's [[Description]] value. let desc = agent[sym].descriptor; // 2. If desc is undefined, set desc to the empty String. @@ -201,7 +205,7 @@ fn symbol_descriptive_string(agent: &mut Agent, sym: Symbol) -> String { // 3. Assert: desc is a String. // 4. Return the string-concatenation of "Symbol(", desc, and ")". let result = format!("Symbol({})", desc.as_str(agent)); - String::from_string(agent, result) + String::from_string(agent, gc, result) } else { BUILTIN_STRING_MEMORY.Symbol__ } diff --git a/nova_vm/src/ecmascript/builtins/global_object.rs b/nova_vm/src/ecmascript/builtins/global_object.rs index 009369a3d..8aa5caa64 100644 --- a/nova_vm/src/ecmascript/builtins/global_object.rs +++ b/nova_vm/src/ecmascript/builtins/global_object.rs @@ -8,8 +8,9 @@ use oxc_ecmascript::BoundNames; use oxc_span::SourceType; use crate::ecmascript::abstract_operations::type_conversion::{ - is_trimmable_whitespace, to_int32, to_string, + 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::{ ecmascript::{ @@ -40,7 +41,7 @@ pub(crate) struct GlobalObject; struct GlobalObjectEval; impl Builtin for GlobalObjectEval { - const NAME: String = BUILTIN_STRING_MEMORY.eval; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.eval; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::eval); } @@ -49,7 +50,7 @@ impl BuiltinIntrinsic for GlobalObjectEval { } struct GlobalObjectIsFinite; impl Builtin for GlobalObjectIsFinite { - const NAME: String = BUILTIN_STRING_MEMORY.isFinite; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isFinite; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::is_finite); } @@ -58,7 +59,7 @@ impl BuiltinIntrinsic for GlobalObjectIsFinite { } struct GlobalObjectIsNaN; impl Builtin for GlobalObjectIsNaN { - const NAME: String = BUILTIN_STRING_MEMORY.isNaN; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isNaN; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::is_nan); } @@ -67,7 +68,7 @@ impl BuiltinIntrinsic for GlobalObjectIsNaN { } struct GlobalObjectParseFloat; impl Builtin for GlobalObjectParseFloat { - const NAME: String = BUILTIN_STRING_MEMORY.parseFloat; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.parseFloat; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::parse_float); } @@ -76,7 +77,7 @@ impl BuiltinIntrinsic for GlobalObjectParseFloat { } struct GlobalObjectParseInt; impl Builtin for GlobalObjectParseInt { - const NAME: String = BUILTIN_STRING_MEMORY.parseInt; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.parseInt; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::parse_int); } @@ -85,7 +86,7 @@ impl BuiltinIntrinsic for GlobalObjectParseInt { } struct GlobalObjectDecodeURI; impl Builtin for GlobalObjectDecodeURI { - const NAME: String = BUILTIN_STRING_MEMORY.decodeURI; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.decodeURI; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::decode_uri); } @@ -94,7 +95,7 @@ impl BuiltinIntrinsic for GlobalObjectDecodeURI { } struct GlobalObjectDecodeURIComponent; impl Builtin for GlobalObjectDecodeURIComponent { - const NAME: String = BUILTIN_STRING_MEMORY.decodeURIComponent; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.decodeURIComponent; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::decode_uri_component); } @@ -103,7 +104,7 @@ impl BuiltinIntrinsic for GlobalObjectDecodeURIComponent { } struct GlobalObjectEncodeURI; impl Builtin for GlobalObjectEncodeURI { - const NAME: String = BUILTIN_STRING_MEMORY.encodeURI; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.encodeURI; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::encode_uri); } @@ -112,7 +113,7 @@ impl BuiltinIntrinsic for GlobalObjectEncodeURI { } struct GlobalObjectEncodeURIComponent; impl Builtin for GlobalObjectEncodeURIComponent { - const NAME: String = BUILTIN_STRING_MEMORY.encodeURIComponent; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.encodeURIComponent; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::encode_uri_component); } @@ -121,7 +122,7 @@ impl BuiltinIntrinsic for GlobalObjectEncodeURIComponent { } struct GlobalObjectEscape; impl Builtin for GlobalObjectEscape { - const NAME: String = BUILTIN_STRING_MEMORY.escape; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.escape; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::escape); } @@ -130,7 +131,7 @@ impl BuiltinIntrinsic for GlobalObjectEscape { } struct GlobalObjectUnescape; impl Builtin for GlobalObjectUnescape { - const NAME: String = BUILTIN_STRING_MEMORY.unescape; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.unescape; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(GlobalObject::unescape); } @@ -147,7 +148,6 @@ impl BuiltinIntrinsic for GlobalObjectUnescape { pub fn perform_eval( agent: &mut Agent, mut gc: GcScope<'_, '_>, - x: Value, direct: bool, strict_caller: bool, @@ -219,12 +219,13 @@ pub fn perform_eval( // call happens. // The Program thus refers to a valid, live Allocator for the duration of // this call. - let parse_result = unsafe { SourceCode::parse_source(agent, x, source_type) }; + let parse_result = unsafe { SourceCode::parse_source(agent, gc.nogc(), x, source_type) }; // b. If script is a List of errors, throw a SyntaxError exception. let Ok((script, source_code)) = parse_result else { // TODO: Include error messages in the exception. return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::SyntaxError, "Invalid eval source text.", )); @@ -336,7 +337,7 @@ pub fn perform_eval( // 29. If result is a normal completion, then let result = if result.is_ok() { - let exe = Executable::compile_eval_body(agent, &script.body); + let exe = Executable::compile_eval_body(agent, gc.nogc(), &script.body); // a. Set result to Completion(Evaluation of body). // 30. If result is a normal completion and result.[[Value]] is empty, then // a. Set result to NormalCompletion(undefined). @@ -368,7 +369,6 @@ pub fn perform_eval( pub fn eval_declaration_instantiation( agent: &mut Agent, mut gc: GcScope<'_, '_>, - script: &Program, var_env: EnvironmentIndex, lex_env: EnvironmentIndex, @@ -387,11 +387,12 @@ pub fn eval_declaration_instantiation( if let EnvironmentIndex::Global(var_env) = var_env { // i. For each element name of varNames, do for name in &var_names { - let name = String::from_str(agent, name.as_str()); + let name = String::from_str(agent, gc.nogc(), name.as_str()); // 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. // 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration. if var_env.has_lexical_declaration(agent, name) { return Err(agent.throw_exception( + gc.nogc(), ExceptionType::SyntaxError, format!( "Redeclaration of lexical declaration '{}'", @@ -413,13 +414,19 @@ pub fn eval_declaration_instantiation( // 1. NOTE: The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts. // 2. For each element name of varNames, do for name in &var_names { - let name = String::from_str(agent, name.as_str()); + let name = String::from_str(agent, gc.nogc(), name.as_str()) + .unbind() + .scope(agent, gc.nogc()); // a. If ! thisEnv.HasBinding(name) is true, then // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration. - if this_env.has_binding(agent, gc.reborrow(), name).unwrap() { + if this_env + .has_binding(agent, gc.reborrow(), name.get(agent)) + .unwrap() + { // i. Throw a SyntaxError exception. // ii. NOTE: Annex B.3.4 defines alternate semantics for the above step. return Err(agent.throw_exception( + gc.nogc(), ExceptionType::SyntaxError, format!("Redeclaration of variable '{}'", name.as_str(agent)), )); @@ -479,13 +486,18 @@ pub fn eval_declaration_instantiation( // 1. If varEnv is a Global Environment Record, then if let EnvironmentIndex::Global(var_env) = var_env { // a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn). - let function_name = String::from_str(agent, function_name.as_str()); - let fn_definable = - var_env.can_declare_global_function(agent, gc.reborrow(), function_name)?; + let function_name = String::from_str(agent, gc.nogc(), function_name.as_str()) + .scope(agent, gc.nogc()); + let fn_definable = var_env.can_declare_global_function( + agent, + gc.reborrow(), + function_name.get(agent), + )?; // b. If fnDefinable is false, throw a TypeError exception. if !fn_definable { return Err(agent.throw_exception( + gc.nogc(), ExceptionType::TypeError, format!( "Cannot declare global function '{}'.", @@ -503,7 +515,8 @@ pub fn eval_declaration_instantiation( } // 11. Let declaredVarNames be a new empty List. - let mut declared_var_names = AHashSet::default(); + let mut declared_var_names_strings = AHashSet::with_capacity(var_declarations.len()); + let mut declared_var_names = Vec::with_capacity(var_declarations.len()); // 12. For each element d of varDeclarations, do for d in var_declarations { @@ -514,31 +527,37 @@ pub fn eval_declaration_instantiation( d.id.bound_names(&mut |identifier| { bound_names.push(identifier.name.clone()); }); - for vn in bound_names { + for vn_string in bound_names { // 1. If declaredFunctionNames does not contain vn, then - if !declared_function_names.contains(&vn) { - let vn = String::from_str(agent, vn.as_str()); + if !declared_function_names.contains(&vn_string) { + let vn = String::from_str(agent, gc.nogc(), vn_string.as_str()) + .scope(agent, gc.nogc()); // a. If varEnv is a Global Environment Record, then if let EnvironmentIndex::Global(var_env) = var_env { // i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn). let vn_definable = - var_env.can_declare_global_var(agent, gc.reborrow(), vn)?; + var_env.can_declare_global_var(agent, gc.reborrow(), vn.get(agent))?; // ii. If vnDefinable is false, throw a TypeError exception. if !vn_definable { return Err(agent.throw_exception( + gc.nogc(), ExceptionType::TypeError, format!("Cannot declare global variable '{}'.", vn.as_str(agent)), )); } } // b. If declaredVarNames does not contain vn, then - // i. Append vn to declaredVarNames. - declared_var_names.insert(vn); + if declared_var_names_strings.insert(vn_string) { + // i. Append vn to declaredVarNames. + declared_var_names.push(vn); + } } } } } + drop(declared_var_names_strings); + // 13. NOTE: Annex B.3.2.3 adds additional steps at this point. // 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a Global Environment Record and the global object is a Proxy exotic object. @@ -551,13 +570,20 @@ pub fn eval_declaration_instantiation( let mut bound_names = vec![]; let mut const_bound_names = vec![]; let mut closure = |identifier: &BindingIdentifier| { - bound_names.push(String::from_str(agent, identifier.name.as_str())); + bound_names.push( + String::from_str(agent, gc.nogc(), identifier.name.as_str()) + .scope(agent, gc.nogc()), + ); }; match d { LexicallyScopedDeclaration::Variable(decl) => { if decl.kind == VariableDeclarationKind::Const { decl.id.bound_names(&mut |identifier| { - const_bound_names.push(String::from_str(agent, identifier.name.as_str())) + const_bound_names.push(String::from_str( + agent, + gc.nogc(), + identifier.name.as_str(), + )) }); } else { decl.id.bound_names(&mut closure) @@ -566,19 +592,19 @@ pub fn eval_declaration_instantiation( LexicallyScopedDeclaration::Function(decl) => decl.bound_names(&mut closure), LexicallyScopedDeclaration::Class(decl) => decl.bound_names(&mut closure), LexicallyScopedDeclaration::DefaultExport => { - bound_names.push(BUILTIN_STRING_MEMORY._default_) + bound_names.push(BUILTIN_STRING_MEMORY._default_.scope(agent, gc.nogc())) } } // b. For each element dn of the BoundNames of d, do for dn in const_bound_names { // i. If IsConstantDeclaration of d is true, then // 1. Perform ? lexEnv.CreateImmutableBinding(dn, true). - lex_env.create_immutable_binding(agent, dn, true)?; + lex_env.create_immutable_binding(agent, gc.nogc(), dn, true)?; } for dn in bound_names { // ii. Else, // 1. Perform ? lexEnv.CreateMutableBinding(dn, false). - lex_env.create_mutable_binding(agent, gc.reborrow(), dn, false)?; + lex_env.create_mutable_binding(agent, gc.reborrow(), dn.get(agent), false)?; } } @@ -590,7 +616,6 @@ pub fn eval_declaration_instantiation( assert!(function_name.is_none()); function_name = Some(identifier.name.clone()); }); - let function_name = String::from_str(agent, function_name.unwrap().as_str()); // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. let fo = @@ -598,6 +623,8 @@ pub fn eval_declaration_instantiation( // c. If varEnv is a Global Environment Record, then if let EnvironmentIndex::Global(var_env) = var_env { + let function_name = + String::from_str(agent, gc.nogc(), function_name.unwrap().as_str()).unbind(); // i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true). var_env.create_global_function_binding( agent, @@ -609,8 +636,10 @@ pub fn eval_declaration_instantiation( } else { // d. Else, // i. Let bindingExists be ! varEnv.HasBinding(fn). + let function_name = String::from_str(agent, gc.nogc(), function_name.unwrap().as_str()) + .scope(agent, gc.nogc()); let binding_exists = var_env - .has_binding(agent, gc.reborrow(), function_name) + .has_binding(agent, gc.reborrow(), function_name.get(agent)) .unwrap(); // ii. If bindingExists is false, then @@ -618,17 +647,17 @@ 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, gc.reborrow(), function_name, true) + .create_mutable_binding(agent, gc.reborrow(), function_name.get(agent), true) .unwrap(); // 3. Perform ! varEnv.InitializeBinding(fn, fo). var_env - .initialize_binding(agent, gc.reborrow(), function_name, fo) + .initialize_binding(agent, gc.reborrow(), function_name.get(agent), fo) .unwrap(); } else { // iii. Else, // 1. Perform ! varEnv.SetMutableBinding(fn, fo, false). var_env - .set_mutable_binding(agent, gc.reborrow(), function_name, fo, false) + .set_mutable_binding(agent, gc.reborrow(), function_name.get(agent), fo, false) .unwrap(); } } @@ -638,22 +667,24 @@ pub fn eval_declaration_instantiation( // a. If varEnv is a Global Environment Record, then if let EnvironmentIndex::Global(var_env) = var_env { // i. Perform ? varEnv.CreateGlobalVarBinding(vn, true). - var_env.create_global_var_binding(agent, gc.reborrow(), vn, true)?; + var_env.create_global_var_binding(agent, gc.reborrow(), vn.get(agent), true)?; } else { // b. Else, // i. Let bindingExists be ! varEnv.HasBinding(vn). - let binding_exists = var_env.has_binding(agent, gc.reborrow(), vn).unwrap(); + let binding_exists = var_env + .has_binding(agent, gc.reborrow(), vn.get(agent)) + .unwrap(); // ii. If bindingExists is false, then if !binding_exists { // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14. // 2. Perform ! varEnv.CreateMutableBinding(vn, true). var_env - .create_mutable_binding(agent, gc.reborrow(), vn, true) + .create_mutable_binding(agent, gc.reborrow(), vn.get(agent), true) .unwrap(); // 3. Perform ! varEnv.InitializeBinding(vn, undefined). var_env - .initialize_binding(agent, gc.reborrow(), vn, Value::Undefined) + .initialize_binding(agent, gc.reborrow(), vn.get(agent), Value::Undefined) .unwrap(); } } @@ -670,7 +701,6 @@ impl GlobalObject { fn eval( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -686,7 +716,6 @@ impl GlobalObject { fn is_finite( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -708,7 +737,6 @@ impl GlobalObject { fn is_nan( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, ) -> JsResult { @@ -727,7 +755,6 @@ impl GlobalObject { fn parse_float( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -797,7 +824,6 @@ impl GlobalObject { fn parse_int( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -822,10 +848,24 @@ impl GlobalObject { } // 1. Let inputString be ? ToString(string). - let s = to_string(agent, gc.reborrow(), string)?; + let mut s = to_string(agent, gc.reborrow(), string)? + .unbind() + .bind(gc.nogc()); // 6. Let R be ℝ(? ToInt32(radix)). - let r = to_int32(agent, gc.reborrow(), radix)?; + let r = if let Value::Integer(radix) = radix { + radix.into_i64() as i32 + } else if radix.is_undefined() { + 0 + } else if let Ok(radix) = Primitive::try_from(radix) { + let radix = to_number_primitive(agent, gc.nogc(), radix)?; + to_int32_number(agent, radix) + } else { + let s_root = s.scope(agent, gc.nogc()); + let radix = to_int32(agent, gc.reborrow(), radix)?; + s = s_root.get(agent).bind(gc.nogc()); + radix + }; // 2. Let S be ! TrimString(inputString, start). let s = s.as_str(agent).trim_start_matches(is_trimmable_whitespace); @@ -961,7 +1001,6 @@ impl GlobalObject { fn decode_uri( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -970,7 +1009,6 @@ impl GlobalObject { fn decode_uri_component( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -979,7 +1017,6 @@ impl GlobalObject { fn encode_uri( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -988,7 +1025,6 @@ impl GlobalObject { fn encode_uri_component( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -997,7 +1033,6 @@ impl GlobalObject { fn escape( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1006,7 +1041,6 @@ impl GlobalObject { fn unescape( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 772d1caf4..782c915d7 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 @@ -54,7 +54,7 @@ pub struct ArrayConstructor; impl Builtin for ArrayConstructor { const BEHAVIOUR: Behaviour = Behaviour::Constructor(Self::behaviour); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Array; } impl BuiltinIntrinsicConstructor for ArrayConstructor { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::Array; @@ -64,25 +64,25 @@ struct ArrayFrom; impl Builtin for ArrayFrom { const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayConstructor::from); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.from; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.from; } struct ArrayIsArray; impl Builtin for ArrayIsArray { const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayConstructor::is_array); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.isArray; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isArray; } struct ArrayOf; impl Builtin for ArrayOf { const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayConstructor::of); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.of; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.of; } struct ArrayGetSpecies; impl Builtin for ArrayGetSpecies { const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayConstructor::get_species); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); } impl BuiltinGetter for ArrayGetSpecies {} @@ -93,7 +93,6 @@ impl ArrayConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -118,7 +117,9 @@ impl ArrayConstructor { // 4. If numberOfArgs = 0, then if number_of_args == 0 { // a. Return ! ArrayCreate(0, proto). - return Ok(array_create(agent, 0, 0, proto).unwrap().into_value()); + return Ok(array_create(agent, gc.nogc(), 0, 0, proto) + .unwrap() + .into_value()); } // 5. Else if numberOfArgs = 1, then @@ -129,7 +130,7 @@ impl ArrayConstructor { // 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).unwrap(); + let array = array_create(agent, gc.nogc(), 1, 1, proto).unwrap(); // i. Perform ! CreateDataPropertyOrThrow(array, "0", len). create_data_property_or_throw( agent, @@ -150,11 +151,14 @@ impl ArrayConstructor { // 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( + gc.nogc(), ExceptionType::RangeError, "Invalid array length", )); } - let array = array_create(agent, int_len as usize, int_len as usize, proto).unwrap(); + let array = + array_create(agent, gc.nogc(), int_len as usize, int_len as usize, proto) + .unwrap(); // e. Perform ! Set(array, "length", intLen, true). debug_assert_eq!(agent[array].elements.len(), int_len); array @@ -169,7 +173,7 @@ impl ArrayConstructor { 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)?; + let array = array_create(agent, gc.nogc(), number_of_args, number_of_args, proto)?; // NOTE: `array_create` guarantees that it is less than `u32::MAX` let number_of_args = number_of_args as u32; @@ -203,7 +207,6 @@ impl ArrayConstructor { fn from( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -221,6 +224,7 @@ impl ArrayConstructor { // a. If IsCallable(mapfn) is false, throw a TypeError exception. let Some(mapfn) = is_callable(mapfn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "The map function of Array.from is not callable", )); @@ -247,7 +251,9 @@ impl ArrayConstructor { } else { // b. Else, // i. Let A be ! ArrayCreate(0). - array_create(agent, 0, 0, None).unwrap().into_object() + array_create(agent, gc.nogc(), 0, 0, None) + .unwrap() + .into_object() }; // c. Let iteratorRecord be ? GetIteratorFromMethod(items, usingIterator). @@ -264,6 +270,7 @@ impl ArrayConstructor { if k >= u32::MAX as usize { // 1. Let error be ThrowCompletion(a newly created TypeError object). let error = agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Maximum array size of 2**53-1 exceeded", ); @@ -341,7 +348,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).unwrap(); + let array_like = to_object(agent, gc.nogc(), items).unwrap(); // 8. Let len be ? LengthOfArrayLike(arrayLike). let len = length_of_array_like(agent, gc.reborrow(), array_like)?; @@ -360,7 +367,7 @@ impl ArrayConstructor { } else { // 10. Else, // a. Let A be ? ArrayCreate(len). - array_create(agent, len as usize, len as usize, None)?.into_object() + array_create(agent, gc.nogc(), len as usize, len as usize, None)?.into_object() }; // 11. Let k be 0. @@ -419,7 +426,6 @@ impl ArrayConstructor { fn is_array( agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -430,7 +436,6 @@ impl ArrayConstructor { fn of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -454,7 +459,7 @@ impl ArrayConstructor { } else { // 5. Else, // a. Let A be ? ArrayCreate(len). - array_create(agent, len, len, None)?.into_object() + array_create(agent, gc.nogc(), len, len, None)?.into_object() }; // 6. Let k be 0. @@ -493,7 +498,6 @@ impl ArrayConstructor { fn get_species( _: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { 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 34df6f1c1..69ce8527a 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 @@ -26,7 +26,7 @@ pub(crate) struct ArrayIteratorPrototype; struct ArrayIteratorPrototypeNext; impl Builtin for ArrayIteratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 0; @@ -38,7 +38,6 @@ impl ArrayIteratorPrototype { fn next( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -46,6 +45,7 @@ impl ArrayIteratorPrototype { // 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( + gc.nogc(), ExceptionType::TypeError, "ArrayIterator expected", )); @@ -131,7 +131,8 @@ impl ArrayIteratorPrototype { }; // a. Assert: kind is key+value. // b. Let result be CreateArrayFromList(« indexNumber, elementValue »). - create_array_from_list(agent, &[Value::Integer(index), value]).into_value() + create_array_from_list(agent, gc.nogc(), &[Value::Integer(index), value]) + .into_value() } }; 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 855a5c108..1d8d8266e 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 @@ -6,6 +6,7 @@ use std::cmp::Ordering; use small_string::SmallString; +use crate::ecmascript::abstract_operations::type_conversion::try_to_string; use crate::engine::context::GcScope; use crate::{ ecmascript::{ @@ -45,175 +46,175 @@ pub(crate) struct ArrayPrototype; struct ArrayPrototypeAt; impl Builtin for ArrayPrototypeAt { - const NAME: String = BUILTIN_STRING_MEMORY.at; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.at; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::at); } struct ArrayPrototypeConcat; impl Builtin for ArrayPrototypeConcat { - const NAME: String = BUILTIN_STRING_MEMORY.concat; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.concat; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::concat); } struct ArrayPrototypeCopyWithin; impl Builtin for ArrayPrototypeCopyWithin { - const NAME: String = BUILTIN_STRING_MEMORY.copyWithin; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.copyWithin; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::copy_within); } struct ArrayPrototypeEntries; impl Builtin for ArrayPrototypeEntries { - const NAME: String = BUILTIN_STRING_MEMORY.entries; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.entries; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::entries); } struct ArrayPrototypeEvery; impl Builtin for ArrayPrototypeEvery { - const NAME: String = BUILTIN_STRING_MEMORY.every; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.every; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::every); } struct ArrayPrototypeFill; impl Builtin for ArrayPrototypeFill { - const NAME: String = BUILTIN_STRING_MEMORY.fill; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.fill; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::fill); } struct ArrayPrototypeFilter; impl Builtin for ArrayPrototypeFilter { - const NAME: String = BUILTIN_STRING_MEMORY.filter; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.filter; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::filter); } struct ArrayPrototypeFind; impl Builtin for ArrayPrototypeFind { - const NAME: String = BUILTIN_STRING_MEMORY.find; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.find; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::find); } struct ArrayPrototypeFindIndex; impl Builtin for ArrayPrototypeFindIndex { - const NAME: String = BUILTIN_STRING_MEMORY.findIndex; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.findIndex; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::find_index); } struct ArrayPrototypeFindLast; impl Builtin for ArrayPrototypeFindLast { - const NAME: String = BUILTIN_STRING_MEMORY.findLast; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.findLast; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::find_last); } struct ArrayPrototypeFindLastIndex; impl Builtin for ArrayPrototypeFindLastIndex { - const NAME: String = BUILTIN_STRING_MEMORY.findLastIndex; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.findLastIndex; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::find_last_index); } struct ArrayPrototypeFlat; impl Builtin for ArrayPrototypeFlat { - const NAME: String = BUILTIN_STRING_MEMORY.flat; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.flat; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::flat); } struct ArrayPrototypeFlatMap; impl Builtin for ArrayPrototypeFlatMap { - const NAME: String = BUILTIN_STRING_MEMORY.flatMap; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.flatMap; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::flat_map); } struct ArrayPrototypeForEach; impl Builtin for ArrayPrototypeForEach { - const NAME: String = BUILTIN_STRING_MEMORY.forEach; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.forEach; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::for_each); } struct ArrayPrototypeIncludes; impl Builtin for ArrayPrototypeIncludes { - const NAME: String = BUILTIN_STRING_MEMORY.includes; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.includes; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::includes); } struct ArrayPrototypeIndexOf; impl Builtin for ArrayPrototypeIndexOf { - const NAME: String = BUILTIN_STRING_MEMORY.indexOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.indexOf; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::index_of); } struct ArrayPrototypeJoin; impl Builtin for ArrayPrototypeJoin { - const NAME: String = BUILTIN_STRING_MEMORY.join; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.join; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::join); } struct ArrayPrototypeKeys; impl Builtin for ArrayPrototypeKeys { - const NAME: String = BUILTIN_STRING_MEMORY.keys; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.keys; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::keys); } struct ArrayPrototypeLastIndexOf; impl Builtin for ArrayPrototypeLastIndexOf { - const NAME: String = BUILTIN_STRING_MEMORY.lastIndexOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.lastIndexOf; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::last_index_of); } struct ArrayPrototypeMap; impl Builtin for ArrayPrototypeMap { - const NAME: String = BUILTIN_STRING_MEMORY.map; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.map; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::map); } struct ArrayPrototypePop; impl Builtin for ArrayPrototypePop { - const NAME: String = BUILTIN_STRING_MEMORY.pop; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.pop; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::pop); } struct ArrayPrototypePush; impl Builtin for ArrayPrototypePush { - const NAME: String = BUILTIN_STRING_MEMORY.push; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.push; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::push); } struct ArrayPrototypeReduce; impl Builtin for ArrayPrototypeReduce { - const NAME: String = BUILTIN_STRING_MEMORY.reduce; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.reduce; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::reduce); } struct ArrayPrototypeReduceRight; impl Builtin for ArrayPrototypeReduceRight { - const NAME: String = BUILTIN_STRING_MEMORY.reduceRight; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.reduceRight; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::reduce_right); } struct ArrayPrototypeReverse; impl Builtin for ArrayPrototypeReverse { - const NAME: String = BUILTIN_STRING_MEMORY.reverse; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.reverse; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::reverse); } struct ArrayPrototypeShift; impl Builtin for ArrayPrototypeShift { - const NAME: String = BUILTIN_STRING_MEMORY.shift; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.shift; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::shift); } struct ArrayPrototypeSlice; impl Builtin for ArrayPrototypeSlice { - const NAME: String = BUILTIN_STRING_MEMORY.slice; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.slice; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::slice); } struct ArrayPrototypeSome; impl Builtin for ArrayPrototypeSome { - const NAME: String = BUILTIN_STRING_MEMORY.some; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.some; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::some); } struct ArrayPrototypeSort; impl Builtin for ArrayPrototypeSort { - const NAME: String = BUILTIN_STRING_MEMORY.sort; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.sort; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::sort); } @@ -222,37 +223,37 @@ impl BuiltinIntrinsic for ArrayPrototypeSort { } struct ArrayPrototypeSplice; impl Builtin for ArrayPrototypeSplice { - const NAME: String = BUILTIN_STRING_MEMORY.splice; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.splice; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::splice); } struct ArrayPrototypeToLocaleString; impl Builtin for ArrayPrototypeToLocaleString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::to_locale_string); } struct ArrayPrototypeToReversed; impl Builtin for ArrayPrototypeToReversed { - const NAME: String = BUILTIN_STRING_MEMORY.toReversed; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toReversed; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::to_reversed); } struct ArrayPrototypeToSorted; impl Builtin for ArrayPrototypeToSorted { - const NAME: String = BUILTIN_STRING_MEMORY.toSorted; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toSorted; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::to_sorted); } struct ArrayPrototypeToSpliced; impl Builtin for ArrayPrototypeToSpliced { - const NAME: String = BUILTIN_STRING_MEMORY.toSpliced; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toSpliced; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::to_spliced); } struct ArrayPrototypeToString; impl Builtin for ArrayPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::to_string); } @@ -261,13 +262,13 @@ impl BuiltinIntrinsic for ArrayPrototypeToString { } struct ArrayPrototypeUnshift; impl Builtin for ArrayPrototypeUnshift { - const NAME: String = BUILTIN_STRING_MEMORY.unshift; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.unshift; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::unshift); } struct ArrayPrototypeValues; impl Builtin for ArrayPrototypeValues { - const NAME: String = BUILTIN_STRING_MEMORY.values; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.values; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::values); } @@ -276,7 +277,7 @@ impl BuiltinIntrinsic for ArrayPrototypeValues { } struct ArrayPrototypeWith; impl Builtin for ArrayPrototypeWith { - const NAME: String = BUILTIN_STRING_MEMORY.with; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.with; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayPrototype::with); } @@ -286,12 +287,11 @@ impl ArrayPrototype { fn at( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; let index = arguments.get(0); @@ -344,12 +344,11 @@ impl ArrayPrototype { fn concat( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, items: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let A be ? ArraySpeciesCreate(O, 0). let a = array_species_create(agent, gc.reborrow(), o, 0)?; // 3. Let n be 0. @@ -368,6 +367,7 @@ impl ArrayPrototype { // ii. If n + len > 2**53 - 1, throw a TypeError exception. if (n + len) > SmallInteger::MAX_NUMBER { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Array overflow", )); @@ -404,6 +404,7 @@ impl ArrayPrototype { // ii. If n ≥ 2**53 - 1, throw a TypeError exception. if n >= SmallInteger::MAX_NUMBER { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Array overflow", )); @@ -454,7 +455,6 @@ impl ArrayPrototype { fn copy_within( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -511,7 +511,7 @@ impl ArrayPrototype { } } // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len: i64 = length_of_array_like(agent, gc.reborrow(), o)?; let len_f64 = len as f64; @@ -609,14 +609,14 @@ impl ArrayPrototype { fn entries( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> 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( + gc.nogc(), ExceptionType::TypeError, "Expected this to be an object", )); @@ -664,18 +664,18 @@ impl ArrayPrototype { fn every( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; let callback_fn = arguments.get(0); // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback is not a function", )); @@ -738,7 +738,6 @@ impl ArrayPrototype { fn fill( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -786,7 +785,7 @@ impl ArrayPrototype { } }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. Let relativeStart be ? ToIntegerOrInfinity(start). @@ -870,7 +869,6 @@ impl ArrayPrototype { fn filter( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -878,12 +876,13 @@ impl ArrayPrototype { let this_arg = arguments.get(1); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function is not callable", )); @@ -952,12 +951,11 @@ impl ArrayPrototype { fn find( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; let predicate = arguments.get(0); @@ -988,12 +986,11 @@ impl ArrayPrototype { fn find_index( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; let predicate = arguments.get(0); @@ -1008,12 +1005,11 @@ impl ArrayPrototype { fn find_last( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; let predicate = arguments.get(0); @@ -1029,12 +1025,11 @@ impl ArrayPrototype { fn find_last_index( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; let predicate = arguments.get(0); @@ -1050,13 +1045,12 @@ impl ArrayPrototype { fn flat( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { let depth = arguments.get(0); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let sourceLen be ? LengthOfArrayLike(O). let source_len = length_of_array_like(agent, gc.reborrow(), o)? as usize; // 3. Let depthNum be 1. @@ -1092,7 +1086,6 @@ impl ArrayPrototype { fn flat_map( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1100,12 +1093,13 @@ impl ArrayPrototype { let this_arg = arguments.get(1); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let sourceLen be ? LengthOfArrayLike(O). let source_len = length_of_array_like(agent, gc.reborrow(), o)? as usize; // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception. let Some(mapper_function) = is_callable(mapper_function) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Mapper function is not callable", )); @@ -1165,12 +1159,11 @@ impl ArrayPrototype { fn for_each( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; @@ -1179,6 +1172,7 @@ impl ArrayPrototype { // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function is not a function", )); @@ -1249,7 +1243,6 @@ impl ArrayPrototype { fn includes( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1297,7 +1290,7 @@ impl ArrayPrototype { } }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If len = 0, return false. @@ -1373,7 +1366,6 @@ impl ArrayPrototype { fn index_of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1421,7 +1413,7 @@ impl ArrayPrototype { } }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If len = 0, return -1𝔽. @@ -1487,14 +1479,13 @@ impl ArrayPrototype { fn join( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { let separator = arguments.get(0); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; if len == 0 { @@ -1507,7 +1498,11 @@ impl ArrayPrototype { } else { // 4. Else, let sep be ? ToString(separator). to_string(agent, gc.reborrow(), separator)? + .unbind() + .bind(gc.nogc()) }; + // 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. @@ -1525,7 +1520,7 @@ impl ArrayPrototype { } for k in 1..len { // a. If k > 0, set R to the string-concatenation of R and sep. - r.push_str(separator.as_str(agent)); + r.push_str(separator.get(agent).as_str(agent)); // b. Let element be ? Get(O, ! ToString(𝔽(k))). let element = get( agent, @@ -1543,19 +1538,19 @@ impl ArrayPrototype { // d. Set k to k + 1. } // 8. Return R. - Ok(Value::from_string(agent, r).into_value()) + Ok(Value::from_string(agent, gc.nogc(), r).into_value()) } fn keys( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> 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( + gc.nogc(), ExceptionType::TypeError, "Expected this to be an object", )); @@ -1588,7 +1583,6 @@ impl ArrayPrototype { fn last_index_of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1643,7 +1637,7 @@ impl ArrayPrototype { } }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If len = 0, return -1𝔽. @@ -1728,7 +1722,6 @@ impl ArrayPrototype { fn map( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1736,12 +1729,13 @@ impl ArrayPrototype { let this_arg = arguments.get(1); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function is not a function", )); @@ -1796,7 +1790,6 @@ impl ArrayPrototype { fn pop( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1809,6 +1802,7 @@ impl ArrayPrototype { if len == 0 { return if !length_writable { Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Could not set property.", )) @@ -1824,6 +1818,7 @@ impl ArrayPrototype { agent[array].elements.len -= 1; } else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Could not set property.", )); @@ -1835,7 +1830,7 @@ impl ArrayPrototype { } } // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If len = 0, then @@ -1893,12 +1888,11 @@ impl ArrayPrototype { fn push( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, items: ArgumentsList, ) -> JsResult { // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let mut len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. Let argCount be the number of elements in items. @@ -1906,6 +1900,7 @@ impl ArrayPrototype { // 4. If len + argCount > 2**53 - 1, throw a TypeError exception. if (len + arg_count as i64) > SmallInteger::MAX_NUMBER { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Array length overflow", )); @@ -1987,7 +1982,6 @@ impl ArrayPrototype { fn reduce( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1999,13 +1993,14 @@ impl ArrayPrototype { }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function is not a function", )); @@ -2014,6 +2009,7 @@ impl ArrayPrototype { // 4. If len = 0 and initialValue is not present, throw a TypeError exception. if len == 0 && initial_value.is_none() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Array length is 0 and no initial value provided", )); @@ -2052,6 +2048,7 @@ impl ArrayPrototype { // c. If kPresent is false, throw a TypeError exception. if !k_present { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Array length is 0 and no initial value provided", )); @@ -2134,7 +2131,6 @@ impl ArrayPrototype { fn reduce_right( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -2146,7 +2142,7 @@ impl ArrayPrototype { }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; @@ -2154,6 +2150,7 @@ impl ArrayPrototype { // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function is not a function", )); @@ -2162,6 +2159,7 @@ impl ArrayPrototype { // 4. If len = 0 and initialValue is not present, throw a TypeError exception. if len == 0 && initial_value.is_none() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Array length is 0 and no initial value provided", )); @@ -2200,6 +2198,7 @@ impl ArrayPrototype { // c. If kPresent is false, throw a TypeError exception. if !k_present { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Array length is 0 and no initial value provided", )); @@ -2245,7 +2244,6 @@ impl ArrayPrototype { fn reverse( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -2259,7 +2257,7 @@ impl ArrayPrototype { } // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. Let middle be floor(len / 2). @@ -2333,7 +2331,6 @@ impl ArrayPrototype { fn shift( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -2380,7 +2377,7 @@ impl ArrayPrototype { } } // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If len = 0, then @@ -2459,7 +2456,6 @@ impl ArrayPrototype { fn slice( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -2563,7 +2559,7 @@ impl ArrayPrototype { } } // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)? as usize; // 3. Let relativeStart be ? ToIntegerOrInfinity(start). @@ -2673,7 +2669,6 @@ impl ArrayPrototype { fn some( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -2681,12 +2676,13 @@ impl ArrayPrototype { let this_arg = arguments.get(1); // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function is not callable", )); @@ -2754,7 +2750,6 @@ impl ArrayPrototype { fn sort( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { @@ -2765,10 +2760,14 @@ impl ArrayPrototype { } else if let Some(comparator) = is_callable(comparator) { Some(comparator) } else { - return Err(agent.throw_exception_with_static_message(ExceptionType::TypeError, "")); + return Err(agent.throw_exception_with_static_message( + gc.nogc(), + ExceptionType::TypeError, + "", + )); }; // 2. Let obj be ? ToObject(this value). - let obj = to_object(agent, this_value)?; + let obj = to_object(agent, gc.nogc(), this_value)?; // 3. Let len be ? LengthOfArrayLike(obj). let len = usize::try_from(length_of_array_like(agent, gc.reborrow(), obj)?).unwrap(); // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) @@ -2826,7 +2825,7 @@ impl ArrayPrototype { &[] }; // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. Let relativeStart be ? ToIntegerOrInfinity(start). @@ -2861,6 +2860,7 @@ impl ArrayPrototype { // 11. If len + itemCount - actualDeleteCount > 2**53 - 1, throw a TypeError exception. if len as usize + item_count - actual_delete_count > SmallInteger::MAX_NUMBER as usize { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Target index overflowed", )); @@ -2987,7 +2987,6 @@ impl ArrayPrototype { fn to_locale_string( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -3011,11 +3010,11 @@ impl ArrayPrototype { } // 1. Let O be ? ToObject(this value). - let o = to_object(agent, this_value)?; + let o = to_object(agent, gc.nogc(), this_value)?; // 2. Let len be ? LengthOfArrayLike(O). let len = length_of_array_like(agent, gc.reborrow(), o)?; // 3. Let A be ? ArrayCreate(len). - let a = array_create(agent, len as usize, len as usize, None)?; + let a = array_create(agent, gc.nogc(), len as usize, len as usize, None)?; // 4. Let k be 0. let mut k = 0; // 5. Repeat, while k < len, @@ -3038,7 +3037,6 @@ impl ArrayPrototype { fn to_sorted( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -3048,7 +3046,6 @@ impl ArrayPrototype { fn to_spliced( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -3059,12 +3056,11 @@ impl ArrayPrototype { fn to_string( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let array be ? ToObject(this value). - let array = to_object(agent, this_value)?; + let array = to_object(agent, gc.nogc(), this_value)?; // 2. Let func be ? Get(array, "join"). let func = get( agent, @@ -3084,26 +3080,125 @@ impl ArrayPrototype { call_function(agent, gc, func, array.into_value(), None) } + /// ### [23.1.3.37 Array.prototype.unshift ( ...items )](https://tc39.es/ecma262/#sec-array.prototype.unshift) + /// + /// This method prepends the arguments to the start of the array, such that + /// their order within the array is the same as the order in which they appear + /// in the argument list. + /// + /// > ### Note + /// > + /// > 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( - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - - _this_value: Value, - _: ArgumentsList, + agent: &mut Agent, + mut gc: GcScope<'_, '_>, + this_value: Value, + items: ArgumentsList, ) -> JsResult { - todo!(); + // 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 { + let len = array.len(agent); + let arg_count = items.len(); + let final_len = u32::try_from(len as u64 + arg_count as u64); + if final_len.is_ok() + && array.is_trivial(agent) + && array.is_dense(agent) + && array.length_writable(agent) + { + // Fast path: Reserve enough room in the array and set array length. + let Heap { + arrays, elements, .. + } = &mut agent.heap; + arrays[array].elements.reserve(elements, final_len.unwrap()); + agent[array].elements.len += arg_count as u32; + // Fast path: Copy old items to the end of array, + // copy new items to the front of the array. + let slice = array.as_mut_slice(agent); + slice.copy_within(..len as usize, arg_count); + slice[..arg_count].copy_from_slice(unsafe { + // SAFETY: Option is an extra variant of the Value enum. + // The transmute effectively turns Value into Some(Value). + std::mem::transmute::<&[Value], &[Option]>(items.0) + }); + return Ok(final_len.unwrap().into()); + } + } + // 1. Let O be ? ToObject(this value). + let o = to_object(agent, gc.nogc(), this_value)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = length_of_array_like(agent, gc.reborrow(), o)?; + // 3. Let argCount be the number of elements in items. + let arg_count = items.len(); + // 4. If argCount > 0, then + if arg_count > 0 { + // a. If len + argCount > 2**53 - 1, throw a TypeError exception. + if (len + arg_count as i64) > SmallInteger::MAX_NUMBER { + return Err(agent.throw_exception_with_static_message( + gc.nogc(), + ExceptionType::TypeError, + "Array length overflow", + )); + } + // b. Let k be len. + let mut k = len; + // c. Repeat, while k > 0, + while k > 0 { + // i. Let from be ! ToString(𝔽(k - 1)). + let from = (k - 1).try_into().unwrap(); + // ii. Let to be ! ToString(𝔽(k + argCount - 1)). + let to = (k + arg_count as i64 - 1).try_into().unwrap(); + // iii. Let fromPresent be ? HasProperty(O, from). + let from_present = has_property(agent, gc.reborrow(), o, from)?; + // iv. If fromPresent is true, then + if from_present { + // 1. Let fromValue be ? Get(O, from). + let from_value = get(agent, gc.reborrow(), o, from)?; + // 2. Perform ? Set(O, to, fromValue, true). + set(agent, gc.reborrow(), o, to, from_value, true)?; + } else { + // v. Else, + // 1. Assert: fromPresent is false. + // 2. Perform ? DeletePropertyOrThrow(O, to). + delete_property_or_throw(agent, gc.reborrow(), o, to)?; + } + // vi. Set k to k - 1. + k -= 1; + } + // d. Let j be +0𝔽. + // e. For each element E of items, do + for (j, e) in items.iter().enumerate() { + // i. Perform ? Set(O, ! ToString(j), E, true). + // ii. Set j to j + 1𝔽. + set(agent, gc.reborrow(), o, j.try_into().unwrap(), *e, true)?; + } + } + // 5. Perform ? Set(O, "length", 𝔽(len + argCount), true). + let len: Value = (len + arg_count as i64).try_into().unwrap(); + set( + agent, + gc.reborrow(), + o, + BUILTIN_STRING_MEMORY.length.into(), + len, + true, + )?; + // 6. Return 𝔽(len + argCount). + Ok(len) } fn values( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> 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( + gc.nogc(), ExceptionType::TypeError, "Expected this to be an object", )); @@ -3115,7 +3210,6 @@ impl ArrayPrototype { fn with( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -3243,7 +3337,6 @@ impl ArrayPrototype { fn is_concat_spreadable( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Value, ) -> JsResult> { // 1. If O is not an Object, return false. @@ -3314,7 +3407,6 @@ fn is_concat_spreadable( fn find_via_predicate( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Object, len: i64, ascending: bool, @@ -3324,6 +3416,7 @@ fn find_via_predicate( // 1. If IsCallable(predicate) is false, throw a TypeError exception. let Some(predicate) = is_callable(predicate) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Predicate is not a function", )); @@ -3331,7 +3424,6 @@ fn find_via_predicate( // 4. For each integer k of indices, do let check = |agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Object, predicate: Function, this_arg: Value, @@ -3393,7 +3485,6 @@ fn find_via_predicate( fn flatten_into_array( agent: &mut Agent, mut gc: GcScope<'_, '_>, - target: Object, source: Object, source_len: usize, @@ -3471,6 +3562,7 @@ fn flatten_into_array( // 1. If targetIndex ≥ 2**53 - 1, throw a TypeError exception. if target_index >= SmallInteger::MAX_NUMBER as usize { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Target index overflowed", )); @@ -3548,7 +3640,6 @@ fn flatten_into_array( fn sort_indexed_properties( agent: &mut Agent, mut gc: GcScope<'_, '_>, - obj: Object, len: usize, comparator: Option, @@ -3617,7 +3708,6 @@ fn sort_indexed_properties( fn compare_array_elements( agent: &mut Agent, mut gc: GcScope<'_, '_>, - x: Value, y: Value, comparator: Option, @@ -3663,11 +3753,24 @@ fn compare_array_elements( Ok(x.into_f64(agent).total_cmp(&y.into_f64(agent))) } else { // 5. Let xString be ? ToString(x). - let x = to_string(agent, gc.reborrow(), x)?; + let x = if let Some(x) = try_to_string(agent, gc.nogc(), x) { + x? + } else { + // TODO: Should root y before calling. + let x = to_string(agent, gc.reborrow(), x)?.unbind().bind(gc.nogc()); + x + }; // 6. Let yString be ? ToString(y). - let y = to_string(agent, gc.reborrow(), y)?; + let (x, y) = if let Some(y) = try_to_string(agent, gc.nogc(), y) { + (x, y?) + } else { + let x = x.scope(agent, gc.nogc()); + let y = to_string(agent, gc.reborrow(), y)?.unbind().bind(gc.nogc()); + (x.get(agent).bind(gc.nogc()), y) + }; // 7. Let xSmaller be ! IsLessThan(xString, yString, true). // 8. If xSmaller is true, return -1𝔽. + let (x, y) = (x.unbind(), y.unbind()); if is_less_than::(agent, gc.reborrow(), x, y).unwrap() == Some(true) { Ok(Ordering::Less) } else 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 616dd8d19..5518f1423 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 @@ -35,7 +35,7 @@ pub(crate) struct TypedArrayConstructors; struct Int8ArrayConstructor; impl Builtin for Int8ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Int8Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Int8Array; const LENGTH: u8 = 3; @@ -47,7 +47,7 @@ impl BuiltinIntrinsicConstructor for Int8ArrayConstructor { } struct Uint8ArrayConstructor; impl Builtin for Uint8ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Uint8Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Uint8Array; const LENGTH: u8 = 3; @@ -59,7 +59,7 @@ impl BuiltinIntrinsicConstructor for Uint8ArrayConstructor { } struct Uint8ClampedArrayConstructor; impl Builtin for Uint8ClampedArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Uint8ClampedArray; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Uint8ClampedArray; const LENGTH: u8 = 3; @@ -71,7 +71,7 @@ impl BuiltinIntrinsicConstructor for Uint8ClampedArrayConstructor { } struct Int16ArrayConstructor; impl Builtin for Int16ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Int16Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Int16Array; const LENGTH: u8 = 3; @@ -83,7 +83,7 @@ impl BuiltinIntrinsicConstructor for Int16ArrayConstructor { } struct Uint16ArrayConstructor; impl Builtin for Uint16ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Uint16Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Uint16Array; const LENGTH: u8 = 3; @@ -95,7 +95,7 @@ impl BuiltinIntrinsicConstructor for Uint16ArrayConstructor { } struct Int32ArrayConstructor; impl Builtin for Int32ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Int32Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Int32Array; const LENGTH: u8 = 3; @@ -107,7 +107,7 @@ impl BuiltinIntrinsicConstructor for Int32ArrayConstructor { } struct Uint32ArrayConstructor; impl Builtin for Uint32ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Uint32Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Uint32Array; const LENGTH: u8 = 3; @@ -119,7 +119,7 @@ impl BuiltinIntrinsicConstructor for Uint32ArrayConstructor { } struct BigInt64ArrayConstructor; impl Builtin for BigInt64ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.BigInt64Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.BigInt64Array; const LENGTH: u8 = 3; @@ -131,7 +131,7 @@ impl BuiltinIntrinsicConstructor for BigInt64ArrayConstructor { } struct BigUint64ArrayConstructor; impl Builtin for BigUint64ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.BigUint64Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.BigUint64Array; const LENGTH: u8 = 3; @@ -143,7 +143,7 @@ impl BuiltinIntrinsicConstructor for BigUint64ArrayConstructor { } struct Float32ArrayConstructor; impl Builtin for Float32ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Float32Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Float32Array; const LENGTH: u8 = 3; @@ -155,7 +155,7 @@ impl BuiltinIntrinsicConstructor for Float32ArrayConstructor { } struct Float64ArrayConstructor; impl Builtin for Float64ArrayConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Float64Array; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Float64Array; const LENGTH: u8 = 3; 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 20fb67feb..ba2627bdf 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 @@ -43,7 +43,7 @@ pub struct TypedArrayIntrinsicObject; impl Builtin for TypedArrayIntrinsicObject { const BEHAVIOUR: Behaviour = Behaviour::Constructor(Self::behaviour); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.TypedArray; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.TypedArray; } impl BuiltinIntrinsicConstructor for TypedArrayIntrinsicObject { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::TypedArray; @@ -53,32 +53,32 @@ struct TypedArrayFrom; impl Builtin for TypedArrayFrom { const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayIntrinsicObject::from); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.from; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.from; } struct TypedArrayOf; impl Builtin for TypedArrayOf { const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayIntrinsicObject::of); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.fromCodePoint; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.fromCodePoint; } struct TypedArrayGetSpecies; impl Builtin for TypedArrayGetSpecies { const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayIntrinsicObject::get_species); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); } impl BuiltinGetter for TypedArrayGetSpecies {} impl TypedArrayIntrinsicObject { fn behaviour( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, _this_value: Value, _arguments: ArgumentsList, _new_target: Option, ) -> JsResult { Err(agent.throw_exception_with_static_message( + gc.nogc(), crate::ecmascript::execution::agent::ExceptionType::TypeError, "Abstract class TypedArray not directly constructable", )) @@ -87,7 +87,6 @@ impl TypedArrayIntrinsicObject { fn from( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -97,7 +96,6 @@ impl TypedArrayIntrinsicObject { fn is_array( agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -107,7 +105,6 @@ impl TypedArrayIntrinsicObject { fn of( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -117,7 +114,6 @@ impl TypedArrayIntrinsicObject { fn get_species( _: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -144,13 +140,13 @@ pub(crate) struct TypedArrayPrototype; struct TypedArrayPrototypeAt; impl Builtin for TypedArrayPrototypeAt { - const NAME: String = BUILTIN_STRING_MEMORY.at; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.at; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::at); } struct TypedArrayPrototypeGetBuffer; impl Builtin for TypedArrayPrototypeGetBuffer { - const NAME: String = BUILTIN_STRING_MEMORY.get_buffer; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_buffer; const KEY: Option = Some(BUILTIN_STRING_MEMORY.buffer.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::get_buffer); @@ -158,7 +154,7 @@ impl Builtin for TypedArrayPrototypeGetBuffer { impl BuiltinGetter for TypedArrayPrototypeGetBuffer {} struct TypedArrayPrototypeGetByteLength; impl Builtin for TypedArrayPrototypeGetByteLength { - const NAME: String = BUILTIN_STRING_MEMORY.get_byteLength; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_byteLength; const KEY: Option = Some(BUILTIN_STRING_MEMORY.byteLength.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::get_byte_length); @@ -166,7 +162,7 @@ impl Builtin for TypedArrayPrototypeGetByteLength { impl BuiltinGetter for TypedArrayPrototypeGetByteLength {} struct TypedArrayPrototypeGetByteOffset; impl Builtin for TypedArrayPrototypeGetByteOffset { - const NAME: String = BUILTIN_STRING_MEMORY.get_byteOffset; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_byteOffset; const KEY: Option = Some(BUILTIN_STRING_MEMORY.byteOffset.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::get_byte_offset); @@ -174,97 +170,97 @@ impl Builtin for TypedArrayPrototypeGetByteOffset { impl BuiltinGetter for TypedArrayPrototypeGetByteOffset {} struct TypedArrayPrototypeCopyWithin; impl Builtin for TypedArrayPrototypeCopyWithin { - const NAME: String = BUILTIN_STRING_MEMORY.copyWithin; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.copyWithin; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::copy_within); } struct TypedArrayPrototypeEntries; impl Builtin for TypedArrayPrototypeEntries { - const NAME: String = BUILTIN_STRING_MEMORY.entries; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.entries; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::entries); } struct TypedArrayPrototypeEvery; impl Builtin for TypedArrayPrototypeEvery { - const NAME: String = BUILTIN_STRING_MEMORY.every; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.every; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::every); } struct TypedArrayPrototypeFill; impl Builtin for TypedArrayPrototypeFill { - const NAME: String = BUILTIN_STRING_MEMORY.fill; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.fill; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::fill); } struct TypedArrayPrototypeFilter; impl Builtin for TypedArrayPrototypeFilter { - const NAME: String = BUILTIN_STRING_MEMORY.filter; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.filter; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::filter); } struct TypedArrayPrototypeFind; impl Builtin for TypedArrayPrototypeFind { - const NAME: String = BUILTIN_STRING_MEMORY.find; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.find; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::find); } struct TypedArrayPrototypeFindIndex; impl Builtin for TypedArrayPrototypeFindIndex { - const NAME: String = BUILTIN_STRING_MEMORY.findIndex; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.findIndex; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::find_index); } struct TypedArrayPrototypeFindLast; impl Builtin for TypedArrayPrototypeFindLast { - const NAME: String = BUILTIN_STRING_MEMORY.findLast; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.findLast; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::find_last); } struct TypedArrayPrototypeFindLastIndex; impl Builtin for TypedArrayPrototypeFindLastIndex { - const NAME: String = BUILTIN_STRING_MEMORY.findLastIndex; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.findLastIndex; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::find_last_index); } struct TypedArrayPrototypeForEach; impl Builtin for TypedArrayPrototypeForEach { - const NAME: String = BUILTIN_STRING_MEMORY.forEach; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.forEach; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::for_each); } struct TypedArrayPrototypeIncludes; impl Builtin for TypedArrayPrototypeIncludes { - const NAME: String = BUILTIN_STRING_MEMORY.includes; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.includes; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::includes); } struct TypedArrayPrototypeIndexOf; impl Builtin for TypedArrayPrototypeIndexOf { - const NAME: String = BUILTIN_STRING_MEMORY.indexOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.indexOf; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::index_of); } struct TypedArrayPrototypeJoin; impl Builtin for TypedArrayPrototypeJoin { - const NAME: String = BUILTIN_STRING_MEMORY.join; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.join; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::join); } struct TypedArrayPrototypeKeys; impl Builtin for TypedArrayPrototypeKeys { - const NAME: String = BUILTIN_STRING_MEMORY.keys; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.keys; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::keys); } struct TypedArrayPrototypeLastIndexOf; impl Builtin for TypedArrayPrototypeLastIndexOf { - const NAME: String = BUILTIN_STRING_MEMORY.lastIndexOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.lastIndexOf; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::last_index_of); } struct TypedArrayPrototypeGetLength; impl Builtin for TypedArrayPrototypeGetLength { - const NAME: String = BUILTIN_STRING_MEMORY.get_length; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_length; const KEY: Option = Some(BUILTIN_STRING_MEMORY.length.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::get_length); @@ -272,79 +268,79 @@ impl Builtin for TypedArrayPrototypeGetLength { impl BuiltinGetter for TypedArrayPrototypeGetLength {} struct TypedArrayPrototypeMap; impl Builtin for TypedArrayPrototypeMap { - const NAME: String = BUILTIN_STRING_MEMORY.map; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.map; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::map); } struct TypedArrayPrototypeReduce; impl Builtin for TypedArrayPrototypeReduce { - const NAME: String = BUILTIN_STRING_MEMORY.reduce; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.reduce; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::reduce); } struct TypedArrayPrototypeReduceRight; impl Builtin for TypedArrayPrototypeReduceRight { - const NAME: String = BUILTIN_STRING_MEMORY.reduceRight; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.reduceRight; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::reduce_right); } struct TypedArrayPrototypeReverse; impl Builtin for TypedArrayPrototypeReverse { - const NAME: String = BUILTIN_STRING_MEMORY.reverse; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.reverse; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::reverse); } struct TypedArrayPrototypeSet; impl Builtin for TypedArrayPrototypeSet { - const NAME: String = BUILTIN_STRING_MEMORY.set; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.set; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::set); } struct TypedArrayPrototypeSlice; impl Builtin for TypedArrayPrototypeSlice { - const NAME: String = BUILTIN_STRING_MEMORY.slice; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.slice; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::slice); } struct TypedArrayPrototypeSome; impl Builtin for TypedArrayPrototypeSome { - const NAME: String = BUILTIN_STRING_MEMORY.some; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.some; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::some); } struct TypedArrayPrototypeSort; impl Builtin for TypedArrayPrototypeSort { - const NAME: String = BUILTIN_STRING_MEMORY.sort; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.sort; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::sort); } struct TypedArrayPrototypeSubarray; impl Builtin for TypedArrayPrototypeSubarray { - const NAME: String = BUILTIN_STRING_MEMORY.subarray; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.subarray; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::subarray); } struct TypedArrayPrototypeToLocaleString; impl Builtin for TypedArrayPrototypeToLocaleString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::to_locale_string); } struct TypedArrayPrototypeToReversed; impl Builtin for TypedArrayPrototypeToReversed { - const NAME: String = BUILTIN_STRING_MEMORY.toReversed; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toReversed; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::to_reversed); } struct TypedArrayPrototypeToSorted; impl Builtin for TypedArrayPrototypeToSorted { - const NAME: String = BUILTIN_STRING_MEMORY.toSorted; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toSorted; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::to_sorted); } struct TypedArrayPrototypeValues; impl Builtin for TypedArrayPrototypeValues { - const NAME: String = BUILTIN_STRING_MEMORY.values; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.values; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::values); } @@ -353,13 +349,13 @@ impl BuiltinIntrinsic for TypedArrayPrototypeValues { } struct TypedArrayPrototypeWith; impl Builtin for TypedArrayPrototypeWith { - const NAME: String = BUILTIN_STRING_MEMORY.with; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.with; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::with); } struct TypedArrayPrototypeGetToStringTag; impl Builtin for TypedArrayPrototypeGetToStringTag { - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_toStringTag_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_toStringTag_; const KEY: Option = Some(WellKnownSymbolIndexes::ToStringTag.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(TypedArrayPrototype::get_to_string_tag); @@ -370,7 +366,6 @@ impl TypedArrayPrototype { fn at( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -484,7 +479,6 @@ impl TypedArrayPrototype { fn copy_within( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -514,7 +508,6 @@ impl TypedArrayPrototype { fn every( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -524,7 +517,6 @@ impl TypedArrayPrototype { fn fill( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -534,7 +526,6 @@ impl TypedArrayPrototype { fn filter( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -544,7 +535,6 @@ impl TypedArrayPrototype { fn find( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -554,7 +544,6 @@ impl TypedArrayPrototype { fn find_index( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -564,7 +553,6 @@ impl TypedArrayPrototype { fn find_last( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -574,7 +562,6 @@ impl TypedArrayPrototype { fn find_last_index( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -584,7 +571,6 @@ impl TypedArrayPrototype { fn for_each( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -594,7 +580,6 @@ impl TypedArrayPrototype { fn includes( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -604,7 +589,6 @@ impl TypedArrayPrototype { fn index_of( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -614,7 +598,6 @@ impl TypedArrayPrototype { fn join( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -642,7 +625,6 @@ impl TypedArrayPrototype { fn last_index_of( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -702,7 +684,6 @@ impl TypedArrayPrototype { fn map( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -712,7 +693,6 @@ impl TypedArrayPrototype { fn reduce( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -722,7 +702,6 @@ impl TypedArrayPrototype { fn reduce_right( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -732,7 +711,6 @@ impl TypedArrayPrototype { fn reverse( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -742,7 +720,6 @@ impl TypedArrayPrototype { fn set( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -752,7 +729,6 @@ impl TypedArrayPrototype { fn slice( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -762,7 +738,6 @@ impl TypedArrayPrototype { fn some( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -772,7 +747,6 @@ impl TypedArrayPrototype { fn sort( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -782,7 +756,6 @@ impl TypedArrayPrototype { fn subarray( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -792,7 +765,6 @@ impl TypedArrayPrototype { fn to_locale_string( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -802,7 +774,6 @@ impl TypedArrayPrototype { fn to_reversed( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -812,7 +783,6 @@ impl TypedArrayPrototype { fn to_sorted( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -822,7 +792,6 @@ impl TypedArrayPrototype { fn to_spliced( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -854,7 +823,6 @@ impl TypedArrayPrototype { fn with( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 87c16e318..c45994373 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 @@ -37,7 +37,7 @@ use crate::{ pub(crate) struct MapConstructor; impl Builtin for MapConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Map; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Map; const LENGTH: u8 = 0; @@ -50,14 +50,14 @@ struct MapGroupBy; impl Builtin for MapGroupBy { const BEHAVIOUR: Behaviour = Behaviour::Regular(MapConstructor::group_by); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.groupBy; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.groupBy; } struct MapGetSpecies; impl Builtin for MapGetSpecies { const BEHAVIOUR: Behaviour = Behaviour::Regular(MapConstructor::get_species); const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; } impl BuiltinGetter for MapGetSpecies {} @@ -65,7 +65,6 @@ impl MapConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, new_target: Option, @@ -73,6 +72,7 @@ impl MapConstructor { // If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Constructor Map requires 'new'", )); @@ -110,6 +110,7 @@ impl MapConstructor { // 6. If IsCallable(adder) is false, throw a TypeError exception. let Some(adder) = is_callable(adder) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Map.prototype.set is not callable", )); @@ -123,7 +124,6 @@ impl MapConstructor { fn group_by( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -133,7 +133,6 @@ impl MapConstructor { fn get_species( _: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -161,7 +160,6 @@ impl MapConstructor { pub fn add_entries_from_iterable_map_constructor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - target: Map, iterable: Value, adder: Function, @@ -288,7 +286,6 @@ pub fn add_entries_from_iterable_map_constructor( pub(crate) fn add_entries_from_iterable( agent: &mut Agent, mut gc: GcScope<'_, '_>, - target: Object, iterable: Value, adder: Function, @@ -307,6 +304,7 @@ pub(crate) fn add_entries_from_iterable( let Ok(next) = Object::try_from(next) else { // i. Let error be ThrowCompletion(a newly created TypeError object). let error = agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid iterator next return value", ); 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 067cfa4f3..3fb711d03 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 @@ -24,7 +24,7 @@ pub(crate) struct MapIteratorPrototype; struct MapIteratorPrototypeNext; impl Builtin for MapIteratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 0; @@ -35,8 +35,7 @@ impl Builtin for MapIteratorPrototypeNext { impl MapIteratorPrototype { fn next( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -44,6 +43,7 @@ impl MapIteratorPrototype { // 3. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception. let Value::MapIterator(iterator) = this_value else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "MapIterator expected", )); @@ -92,7 +92,7 @@ impl MapIteratorPrototype { continue; }; let value = agent[map].values()[index].unwrap(); - create_array_from_list(agent, &[key, value]).into_value() + create_array_from_list(agent, gc.nogc(), &[key, value]).into_value() } }; 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 69d92a4cb..00d9fd55f 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 std::{hash::Hasher, ops::Index}; use ahash::AHasher; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -30,19 +30,19 @@ pub(crate) struct MapPrototype; struct MapPrototypeClear; impl Builtin for MapPrototypeClear { - const NAME: String = BUILTIN_STRING_MEMORY.clear; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.clear; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::clear); } struct MapPrototypeDelete; impl Builtin for MapPrototypeDelete { - const NAME: String = BUILTIN_STRING_MEMORY.delete; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.delete; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::delete); } struct MapPrototypeEntries; impl Builtin for MapPrototypeEntries { - const NAME: String = BUILTIN_STRING_MEMORY.entries; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.entries; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::entries); } @@ -51,37 +51,37 @@ impl BuiltinIntrinsic for MapPrototypeEntries { } struct MapPrototypeForEach; impl Builtin for MapPrototypeForEach { - const NAME: String = BUILTIN_STRING_MEMORY.forEach; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.forEach; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::for_each); } struct MapPrototypeGet; impl Builtin for MapPrototypeGet { - const NAME: String = BUILTIN_STRING_MEMORY.get; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::get); } struct MapPrototypeHas; impl Builtin for MapPrototypeHas { - const NAME: String = BUILTIN_STRING_MEMORY.has; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.has; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::has); } struct MapPrototypeKeys; impl Builtin for MapPrototypeKeys { - const NAME: String = BUILTIN_STRING_MEMORY.keys; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.keys; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::keys); } pub(super) struct MapPrototypeSet; impl Builtin for MapPrototypeSet { - const NAME: String = BUILTIN_STRING_MEMORY.set; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.set; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::set); } struct MapPrototypeGetSize; impl Builtin for MapPrototypeGetSize { - const NAME: String = BUILTIN_STRING_MEMORY.get_size; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_size; const KEY: Option = Some(BUILTIN_STRING_MEMORY.size.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::get_size); @@ -89,7 +89,7 @@ impl Builtin for MapPrototypeGetSize { impl BuiltinGetter for MapPrototypeGetSize {} struct MapPrototypeValues; impl Builtin for MapPrototypeValues { - const NAME: String = BUILTIN_STRING_MEMORY.values; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.values; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(MapPrototype::values); } @@ -103,14 +103,13 @@ impl MapPrototype { /// > iterating over that List. fn clear( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; // 3. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do // a. Set p.[[Key]] to EMPTY. // b. Set p.[[Value]] to EMPTY. @@ -127,14 +126,13 @@ impl MapPrototype { /// > such as physically removing the entry from internal data structures. fn delete( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; let Heap { bigints, @@ -183,8 +181,7 @@ impl MapPrototype { fn entries( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -193,7 +190,7 @@ impl MapPrototype { // 24.1.5.1 CreateMapIterator ( map, kind ) // 1. Perform ? RequireInternalSlot(map, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; Ok(MapIterator::from_map(agent, m, CollectionIteratorKind::KeyAndValue).into_value()) } @@ -225,7 +222,6 @@ impl MapPrototype { fn for_each( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -233,10 +229,11 @@ impl MapPrototype { let this_arg = arguments.get(1); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function parameter is not callable", )); @@ -277,14 +274,13 @@ impl MapPrototype { /// ### [24.1.3.6 Map.prototype.get ( key )](https://tc39.es/ecma262/#sec-map.prototype.get) fn get( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; let Heap { bigints, @@ -328,14 +324,13 @@ impl MapPrototype { /// ### [24.1.3.7 Map.prototype.has ( key )](https://tc39.es/ecma262/#sec-map.prototype.has) fn has( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; let Heap { bigints, @@ -371,8 +366,7 @@ impl MapPrototype { fn keys( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -381,22 +375,21 @@ impl MapPrototype { // 24.1.5.1 CreateMapIterator ( map, kind ) // 1. Perform ? RequireInternalSlot(map, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; 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( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { let value = arguments.get(1); // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; let Heap { bigints, @@ -457,20 +450,18 @@ impl MapPrototype { fn get_size( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; let count = agent[m].size(); Ok(count.into()) } fn values( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -479,7 +470,7 @@ impl MapPrototype { // 24.1.5.1 CreateMapIterator ( map, kind ) // 1. Perform ? RequireInternalSlot(map, [[MapData]]). - let m = require_map_data_internal_slot(agent, this_value)?; + let m = require_map_data_internal_slot(agent, gc.nogc(), this_value)?; Ok(MapIterator::from_map(agent, m, CollectionIteratorKind::Value).into_value()) } @@ -525,11 +516,14 @@ impl MapPrototype { } #[inline(always)] -fn require_map_data_internal_slot(agent: &mut Agent, value: Value) -> JsResult { +fn require_map_data_internal_slot(agent: &mut Agent, gc: NoGcScope, value: Value) -> JsResult { match value { Value::Map(map) => Ok(map), - _ => Err(agent - .throw_exception_with_static_message(ExceptionType::TypeError, "Object is not a Map")), + _ => Err(agent.throw_exception_with_static_message( + gc, + ExceptionType::TypeError, + "Object is not a Map", + )), } } 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 155840053..c5f1dbd9c 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 @@ -34,7 +34,7 @@ use crate::{ pub(crate) struct SetConstructor; impl Builtin for SetConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Set; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Set; const LENGTH: u8 = 0; @@ -48,7 +48,7 @@ impl Builtin for SetGetSpecies { const BEHAVIOUR: Behaviour = Behaviour::Regular(SetConstructor::get_species); const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; } impl BuiltinGetter for SetGetSpecies {} @@ -57,7 +57,6 @@ impl SetConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _: Value, arguments: ArgumentsList, new_target: Option, @@ -66,6 +65,7 @@ impl SetConstructor { // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Cannot call Set as a function", )); @@ -89,6 +89,7 @@ impl SetConstructor { // 6. If IsCallable(adder) is false, throw a TypeError exception. let Some(adder) = is_callable(adder) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Invalid adder function", )); @@ -192,7 +193,6 @@ impl SetConstructor { fn get_species( _: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { 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 10c34e4c6..6a15aa88d 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 @@ -24,7 +24,7 @@ pub(crate) struct SetIteratorPrototype; struct SetIteratorPrototypeNext; impl Builtin for SetIteratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 0; @@ -35,8 +35,7 @@ impl Builtin for SetIteratorPrototypeNext { impl SetIteratorPrototype { fn next( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -44,6 +43,7 @@ impl SetIteratorPrototype { // 3. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception. let Value::SetIterator(iterator) = this_value else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "SetIterator expected", )); @@ -75,7 +75,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]).into_value() + create_array_from_list(agent, gc.nogc(), &[e, e]).into_value() } CollectionIteratorKind::Value => { // 2. Else, 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 acdc6f006..4d43b14ce 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 std::hash::Hasher; use ahash::AHasher; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -34,43 +34,43 @@ pub(crate) struct SetPrototype; struct SetPrototypeAdd; impl Builtin for SetPrototypeAdd { - const NAME: String = BUILTIN_STRING_MEMORY.add; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.add; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::add); } struct SetPrototypeClear; impl Builtin for SetPrototypeClear { - const NAME: String = BUILTIN_STRING_MEMORY.clear; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.clear; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::clear); } struct SetPrototypeDelete; impl Builtin for SetPrototypeDelete { - const NAME: String = BUILTIN_STRING_MEMORY.delete; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.delete; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::delete); } struct SetPrototypeEntries; impl Builtin for SetPrototypeEntries { - const NAME: String = BUILTIN_STRING_MEMORY.entries; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.entries; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::entries); } struct SetPrototypeForEach; impl Builtin for SetPrototypeForEach { - const NAME: String = BUILTIN_STRING_MEMORY.forEach; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.forEach; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::for_each); } struct SetPrototypeHas; impl Builtin for SetPrototypeHas { - const NAME: String = BUILTIN_STRING_MEMORY.has; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.has; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::has); } struct SetPrototypeGetSize; impl Builtin for SetPrototypeGetSize { - const NAME: String = BUILTIN_STRING_MEMORY.get_size; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_size; const KEY: Option = Some(BUILTIN_STRING_MEMORY.size.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::get_size); @@ -78,7 +78,7 @@ impl Builtin for SetPrototypeGetSize { impl BuiltinGetter for SetPrototypeGetSize {} struct SetPrototypeValues; impl Builtin for SetPrototypeValues { - const NAME: String = BUILTIN_STRING_MEMORY.values; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.values; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(SetPrototype::values); } @@ -90,14 +90,13 @@ impl SetPrototype { /// #### [24.2.4.1 Set.prototype.add ( value )](https://tc39.es/ecma262/#sec-set.prototype.add) fn add( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; let Heap { bigints, @@ -152,14 +151,13 @@ impl SetPrototype { /// > iterating over that List. fn clear( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; // 3. For each element e of S.[[SetData]], do // a. Replace the element of S.[[SetData]] whose value is e with an // element whose value is EMPTY. @@ -177,14 +175,13 @@ impl SetPrototype { /// > such as physically removing the entry from internal data structures. fn delete( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; let Heap { bigints, @@ -228,8 +225,7 @@ impl SetPrototype { fn entries( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -238,7 +234,7 @@ impl SetPrototype { // 24.2.6.1 CreateSetIterator ( set, kind ) // 1. Perform ? RequireInternalSlot(set, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; Ok(SetIterator::from_set(agent, s, CollectionIteratorKind::KeyAndValue).into_value()) } @@ -278,7 +274,6 @@ impl SetPrototype { fn for_each( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -286,10 +281,11 @@ impl SetPrototype { let this_arg = arguments.get(1); // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let Some(callback_fn) = is_callable(callback_fn) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Callback function is not a function", )); @@ -329,14 +325,13 @@ impl SetPrototype { /// ### [24.2.4.8 Set.prototype.has ( value )](https://tc39.es/ecma262/#sec-set.prototype.has) fn has( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; let Heap { bigints, @@ -377,14 +372,13 @@ impl SetPrototype { /// is undefined. fn get_size( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let S be the this value. // 2. Perform ? RequireInternalSlot(S, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; // 3. Let size be SetDataSize(S.[[SetData]]). let size = agent[s].size(); // 4. Return 𝔽(size). @@ -393,8 +387,7 @@ impl SetPrototype { fn values( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -403,7 +396,7 @@ impl SetPrototype { // 24.2.6.1 CreateSetIterator ( set, kind ) // 1. Perform ? RequireInternalSlot(set, [[SetData]]). - let s = require_set_data_internal_slot(agent, this_value)?; + let s = require_set_data_internal_slot(agent, gc.nogc(), this_value)?; Ok(SetIterator::from_set(agent, s, CollectionIteratorKind::Value).into_value()) } @@ -455,11 +448,14 @@ impl SetPrototype { } #[inline(always)] -fn require_set_data_internal_slot(agent: &mut Agent, value: Value) -> JsResult { +fn require_set_data_internal_slot(agent: &mut Agent, gc: NoGcScope, value: Value) -> JsResult { match value { Value::Set(map) => Ok(map), - _ => Err(agent - .throw_exception_with_static_message(ExceptionType::TypeError, "Object is not a Set")), + _ => Err(agent.throw_exception_with_static_message( + gc, + 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 242391972..b01915577 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 @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct WeakMapConstructor; impl Builtin for WeakMapConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.WeakMap; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.WeakMap; const LENGTH: u8 = 0; @@ -29,7 +29,6 @@ impl WeakMapConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, 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 11a25bed6..14fd4c49e 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 @@ -17,25 +17,25 @@ pub(crate) struct WeakMapPrototype; struct WeakMapPrototypeDelete; impl Builtin for WeakMapPrototypeDelete { - const NAME: String = BUILTIN_STRING_MEMORY.delete; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.delete; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakMapPrototype::delete); } struct WeakMapPrototypeGet; impl Builtin for WeakMapPrototypeGet { - const NAME: String = BUILTIN_STRING_MEMORY.get; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakMapPrototype::get); } struct WeakMapPrototypeHas; impl Builtin for WeakMapPrototypeHas { - const NAME: String = BUILTIN_STRING_MEMORY.has; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.has; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakMapPrototype::has); } struct WeakMapPrototypeSet; impl Builtin for WeakMapPrototypeSet { - const NAME: String = BUILTIN_STRING_MEMORY.set; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.set; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakMapPrototype::set); } @@ -44,7 +44,6 @@ impl WeakMapPrototype { fn delete( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -54,7 +53,6 @@ impl WeakMapPrototype { fn get( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -64,7 +62,6 @@ impl WeakMapPrototype { fn has( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -74,7 +71,6 @@ impl WeakMapPrototype { fn set( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 3f4cf937b..be1282213 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 @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct WeakSetConstructor; impl Builtin for WeakSetConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.WeakSet; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.WeakSet; const LENGTH: u8 = 0; @@ -29,7 +29,6 @@ impl WeakSetConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, 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 b77eebc11..3f7e19e66 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 @@ -17,19 +17,19 @@ pub(crate) struct WeakSetPrototype; struct WeakSetPrototypeAdd; impl Builtin for WeakSetPrototypeAdd { - const NAME: String = BUILTIN_STRING_MEMORY.add; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.add; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakSetPrototype::add); } struct WeakSetPrototypeDelete; impl Builtin for WeakSetPrototypeDelete { - const NAME: String = BUILTIN_STRING_MEMORY.delete; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.delete; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakSetPrototype::delete); } struct WeakSetPrototypeHas; impl Builtin for WeakSetPrototypeHas { - const NAME: String = BUILTIN_STRING_MEMORY.has; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.has; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakSetPrototype::has); } @@ -38,7 +38,6 @@ impl WeakSetPrototype { fn add( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -48,7 +47,6 @@ impl WeakSetPrototype { fn delete( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -58,7 +56,6 @@ impl WeakSetPrototype { fn has( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 e9dee67dc..62219aacc 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 @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct FinalizationRegistryConstructor; impl Builtin for FinalizationRegistryConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.FinalizationRegistry; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.FinalizationRegistry; const LENGTH: u8 = 1; @@ -29,7 +29,6 @@ impl FinalizationRegistryConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, 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 28ab70593..2d86bd163 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 @@ -17,13 +17,13 @@ pub(crate) struct FinalizationRegistryPrototype; struct FinalizationRegistryPrototypeRegister; impl Builtin for FinalizationRegistryPrototypeRegister { - const NAME: String = BUILTIN_STRING_MEMORY.register; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.register; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(FinalizationRegistryPrototype::register); } struct FinalizationRegistryPrototypeUnregister; impl Builtin for FinalizationRegistryPrototypeUnregister { - const NAME: String = BUILTIN_STRING_MEMORY.unregister; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.unregister; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(FinalizationRegistryPrototype::unregister); } @@ -32,7 +32,6 @@ impl FinalizationRegistryPrototype { fn register( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -42,7 +41,6 @@ impl FinalizationRegistryPrototype { fn unregister( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 69a779c8f..964e20735 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 @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct WeakRefConstructor; impl Builtin for WeakRefConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.WeakRef; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.WeakRef; const LENGTH: u8 = 1; @@ -29,7 +29,6 @@ impl WeakRefConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, 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 e1f5a57fe..3da3b80f2 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 @@ -17,7 +17,7 @@ pub(crate) struct WeakRefPrototype; struct WeakRefPrototypeDeref; impl Builtin for WeakRefPrototypeDeref { - const NAME: String = BUILTIN_STRING_MEMORY.deref; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.deref; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(WeakRefPrototype::deref); } @@ -26,7 +26,6 @@ impl WeakRefPrototype { fn deref( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { diff --git a/nova_vm/src/ecmascript/builtins/module.rs b/nova_vm/src/ecmascript/builtins/module.rs index 3c0a77347..2de94feaa 100644 --- a/nova_vm/src/ecmascript/builtins/module.rs +++ b/nova_vm/src/ecmascript/builtins/module.rs @@ -153,7 +153,6 @@ impl InternalMethods for Module { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - prototype: Option, ) -> JsResult { set_immutable_prototype(agent, gc.reborrow(), self.into_object(), prototype) @@ -178,7 +177,6 @@ impl InternalMethods for Module { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { match property_key { @@ -225,7 +223,6 @@ impl InternalMethods for Module { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -285,7 +282,6 @@ impl InternalMethods for Module { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { match property_key { @@ -321,7 +317,6 @@ impl InternalMethods for Module { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -387,6 +382,7 @@ impl InternalMethods for Module { // 11. If targetEnv is EMPTY, throw a ReferenceError exception. match target_env { None => Err(agent.throw_exception( + gc.nogc(), ExceptionType::ReferenceError, format!("Could not resolve module '{}'.", key.as_str(agent)), )), @@ -405,7 +401,6 @@ impl InternalMethods for Module { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, _value: Value, _receiver: Value, @@ -418,7 +413,6 @@ impl InternalMethods for Module { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { match property_key { diff --git a/nova_vm/src/ecmascript/builtins/module/data.rs b/nova_vm/src/ecmascript/builtins/module/data.rs index d60d7cb83..1eb4cbd43 100644 --- a/nova_vm/src/ecmascript/builtins/module/data.rs +++ b/nova_vm/src/ecmascript/builtins/module/data.rs @@ -19,7 +19,7 @@ use super::Module; pub struct ModuleHeapData { pub(crate) object_index: Option, pub(crate) module: ModuleRecord, - pub(crate) exports: Box<[String]>, + pub(crate) exports: Box<[String<'static>]>, } #[derive(Debug, Clone, Copy)] @@ -47,7 +47,7 @@ pub(crate) struct ModuleRecord { #[derive(Debug, Clone, Copy)] pub(crate) enum ResolvedBindingName { - String(HeapString), + String(HeapString<'static>), SmallString(SmallString), Namespace, } 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 67e1e6dd9..a36d1a1b8 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 @@ -40,7 +40,7 @@ pub struct BigIntConstructor; impl Builtin for BigIntConstructor { const BEHAVIOUR: Behaviour = Behaviour::Constructor(Self::behaviour); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.BigInt; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.BigInt; } impl BuiltinIntrinsicConstructor for BigIntConstructor { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::BigInt; @@ -50,26 +50,26 @@ struct BigIntAsIntN; impl Builtin for BigIntAsIntN { const BEHAVIOUR: Behaviour = Behaviour::Regular(BigIntConstructor::as_int_n); const LENGTH: u8 = 2; - const NAME: String = BUILTIN_STRING_MEMORY.asIntN; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.asIntN; } struct BigIntAsUintN; impl Builtin for BigIntAsUintN { const BEHAVIOUR: Behaviour = Behaviour::Regular(BigIntConstructor::as_uint_n); const LENGTH: u8 = 2; - const NAME: String = BUILTIN_STRING_MEMORY.asUintN; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.asUintN; } impl BigIntConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, ) -> JsResult { if new_target.is_some() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "BigInt is not a constructor", )); @@ -79,6 +79,7 @@ impl BigIntConstructor { if let Ok(prim) = Number::try_from(prim) { if !prim.is_integer(agent) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Can't convert number to BigInt because it isn't an integer", )); @@ -93,13 +94,13 @@ impl BigIntConstructor { fn as_int_n( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { let bits = to_index(agent, gc.reborrow(), arguments.get(0))?; let Ok(bits) = u32::try_from(bits) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Ridiculous bits value for BigInt.asIntN", )); @@ -171,13 +172,13 @@ impl BigIntConstructor { fn as_uint_n( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { let bits = to_index(agent, gc.reborrow(), arguments.get(0))?; let Ok(bits) = u32::try_from(bits) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Ridiculous bits value for BigInt.asUintN", )); @@ -209,11 +210,14 @@ impl BigIntConstructor { fn number_to_big_int( agent: &mut Agent, mut gc: GcScope<'_, '_>, - value: Number, ) -> JsResult { if !is_integral_number(agent, gc.reborrow(), value) { - Err(agent.throw_exception_with_static_message(ExceptionType::RangeError, "Not an integer")) + Err(agent.throw_exception_with_static_message( + gc.nogc(), + ExceptionType::RangeError, + "Not an integer", + )) } else { match value { Number::Number(idx) => { 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 234e89679..4af7445c3 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,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::{GcScope, NoGcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -17,7 +17,7 @@ pub(crate) struct BigIntPrototype; struct BigIntPrototypeToLocaleString; impl Builtin for BigIntPrototypeToLocaleString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleString; const LENGTH: u8 = 0; @@ -27,7 +27,7 @@ impl Builtin for BigIntPrototypeToLocaleString { struct BigIntPrototypeToString; impl Builtin for BigIntPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; @@ -37,7 +37,7 @@ impl Builtin for BigIntPrototypeToString { struct BigIntPrototypeValueOf; impl Builtin for BigIntPrototypeValueOf { - const NAME: String = BUILTIN_STRING_MEMORY.valueOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.valueOf; const LENGTH: u8 = 0; @@ -48,22 +48,20 @@ impl Builtin for BigIntPrototypeValueOf { impl BigIntPrototype { fn to_locale_string( agent: &mut Agent, - mut gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { - Self::to_string(agent, gc.reborrow(), this_value, arguments) + Self::to_string(agent, gc, this_value, arguments) } fn to_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { - let _x = this_big_int_value(agent, this_value)?; + let _x = this_big_int_value(agent, gc.nogc(), this_value)?; let radix = arguments.get(0); if radix.is_undefined() || radix == Value::from(10u8) { // BigInt::to_string_radix_10(agent, x).map(|result| result.into_value()) @@ -75,12 +73,11 @@ impl BigIntPrototype { fn value_of( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - this_big_int_value(agent, this_value).map(|result| result.into_value()) + this_big_int_value(agent, gc.nogc(), this_value).map(|result| result.into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -113,7 +110,7 @@ impl BigIntPrototype { /// The abstract operation ThisBigIntValue takes argument value (an ECMAScript /// language value) and returns either a normal completion containing a BigInt /// or a throw completion. -fn this_big_int_value(agent: &mut Agent, value: Value) -> JsResult { +fn this_big_int_value(agent: &mut Agent, gc: NoGcScope, value: Value) -> JsResult { match value { // 1. If value is a BigInt, return value. Value::BigInt(value) => Ok(value.into()), @@ -129,8 +126,10 @@ fn this_big_int_value(agent: &mut Agent, value: Value) -> JsResult { } } // 3. Throw a TypeError exception. - _ => { - Err(agent.throw_exception_with_static_message(ExceptionType::TypeError, "Not a BigInt")) - } + _ => Err(agent.throw_exception_with_static_message( + gc, + ExceptionType::TypeError, + "Not a BigInt", + )), } } 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 461f57411..a556bda2e 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 @@ -32,7 +32,7 @@ pub struct DateConstructor; impl Builtin for DateConstructor { const BEHAVIOUR: Behaviour = Behaviour::Constructor(Self::behaviour); const LENGTH: u8 = 7; - const NAME: String = BUILTIN_STRING_MEMORY.Date; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Date; } impl BuiltinIntrinsicConstructor for DateConstructor { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::Date; @@ -42,25 +42,24 @@ struct DateNow; impl Builtin for DateNow { const BEHAVIOUR: Behaviour = Behaviour::Regular(DateConstructor::now); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.now; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.now; } struct DateParse; impl Builtin for DateParse { const BEHAVIOUR: Behaviour = Behaviour::Regular(DateConstructor::parse); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.parse; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.parse; } struct DateUTC; impl Builtin for DateUTC { const BEHAVIOUR: Behaviour = Behaviour::Regular(DateConstructor::utc); const LENGTH: u8 = 7; - const NAME: String = BUILTIN_STRING_MEMORY.utc; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.utc; } impl DateConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -129,7 +128,6 @@ impl DateConstructor { fn now( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -149,7 +147,6 @@ impl DateConstructor { fn parse( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -160,7 +157,6 @@ impl DateConstructor { fn utc( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { 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 9df6c462a..631fa13c1 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; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::{ordinary_to_primitive, PreferredType}, @@ -21,253 +21,253 @@ pub(crate) struct DatePrototype; struct DatePrototypeGetDate; impl Builtin for DatePrototypeGetDate { - const NAME: String = BUILTIN_STRING_MEMORY.getDate; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getDate; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_date); } struct DatePrototypeGetDay; impl Builtin for DatePrototypeGetDay { - const NAME: String = BUILTIN_STRING_MEMORY.getDay; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getDay; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_day); } struct DatePrototypeGetFullYear; impl Builtin for DatePrototypeGetFullYear { - const NAME: String = BUILTIN_STRING_MEMORY.getFullYear; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getFullYear; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_full_year); } struct DatePrototypeGetHours; impl Builtin for DatePrototypeGetHours { - const NAME: String = BUILTIN_STRING_MEMORY.getHours; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getHours; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_hours); } struct DatePrototypeGetMilliseconds; impl Builtin for DatePrototypeGetMilliseconds { - const NAME: String = BUILTIN_STRING_MEMORY.getMilliseconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getMilliseconds; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_milliseconds); } struct DatePrototypeGetMinutes; impl Builtin for DatePrototypeGetMinutes { - const NAME: String = BUILTIN_STRING_MEMORY.getMinutes; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getMinutes; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_minutes); } struct DatePrototypeGetMonth; impl Builtin for DatePrototypeGetMonth { - const NAME: String = BUILTIN_STRING_MEMORY.getMonth; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getMonth; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_month); } struct DatePrototypeGetSeconds; impl Builtin for DatePrototypeGetSeconds { - const NAME: String = BUILTIN_STRING_MEMORY.getSeconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getSeconds; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_seconds); } struct DatePrototypeGetTime; impl Builtin for DatePrototypeGetTime { - const NAME: String = BUILTIN_STRING_MEMORY.getTime; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getTime; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_time); } struct DatePrototypeGetTimezoneOffset; impl Builtin for DatePrototypeGetTimezoneOffset { - const NAME: String = BUILTIN_STRING_MEMORY.getTimezoneOffset; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getTimezoneOffset; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_timezone_offset); } struct DatePrototypeGetUTCDate; impl Builtin for DatePrototypeGetUTCDate { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCDate; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCDate; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_date); } struct DatePrototypeGetUTCDay; impl Builtin for DatePrototypeGetUTCDay { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCDay; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCDay; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_day); } struct DatePrototypeGetUTCFullYear; impl Builtin for DatePrototypeGetUTCFullYear { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCFullYear; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCFullYear; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_full_year); } struct DatePrototypeGetUTCHours; impl Builtin for DatePrototypeGetUTCHours { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCHours; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCHours; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_hours); } struct DatePrototypeGetUTCMilliseconds; impl Builtin for DatePrototypeGetUTCMilliseconds { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCMilliseconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCMilliseconds; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_milliseconds); } struct DatePrototypeGetUTCMinutes; impl Builtin for DatePrototypeGetUTCMinutes { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCMinutes; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCMinutes; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_minutes); } struct DatePrototypeGetUTCMonth; impl Builtin for DatePrototypeGetUTCMonth { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCMonth; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCMonth; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_month); } struct DatePrototypeGetUTCSeconds; impl Builtin for DatePrototypeGetUTCSeconds { - const NAME: String = BUILTIN_STRING_MEMORY.getUTCSeconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUTCSeconds; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::get_utc_seconds); } struct DatePrototypeSetDate; impl Builtin for DatePrototypeSetDate { - const NAME: String = BUILTIN_STRING_MEMORY.setDate; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setDate; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_date); } struct DatePrototypeSetFullYear; impl Builtin for DatePrototypeSetFullYear { - const NAME: String = BUILTIN_STRING_MEMORY.setFullYear; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setFullYear; const LENGTH: u8 = 3; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_full_year); } struct DatePrototypeSetHours; impl Builtin for DatePrototypeSetHours { - const NAME: String = BUILTIN_STRING_MEMORY.setHours; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setHours; const LENGTH: u8 = 4; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_hours); } struct DatePrototypeSetMilliseconds; impl Builtin for DatePrototypeSetMilliseconds { - const NAME: String = BUILTIN_STRING_MEMORY.setMilliseconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setMilliseconds; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_milliseconds); } struct DatePrototypeSetMinutes; impl Builtin for DatePrototypeSetMinutes { - const NAME: String = BUILTIN_STRING_MEMORY.setMinutes; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setMinutes; const LENGTH: u8 = 3; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_minutes); } struct DatePrototypeSetMonth; impl Builtin for DatePrototypeSetMonth { - const NAME: String = BUILTIN_STRING_MEMORY.setMonth; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setMonth; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_month); } struct DatePrototypeSetSeconds; impl Builtin for DatePrototypeSetSeconds { - const NAME: String = BUILTIN_STRING_MEMORY.setSeconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setSeconds; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_seconds); } struct DatePrototypeSetTime; impl Builtin for DatePrototypeSetTime { - const NAME: String = BUILTIN_STRING_MEMORY.setTime; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setTime; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_time); } struct DatePrototypeSetUTCDate; impl Builtin for DatePrototypeSetUTCDate { - const NAME: String = BUILTIN_STRING_MEMORY.setUTCDate; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUTCDate; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_utc_date); } struct DatePrototypeSetUTCFullYear; impl Builtin for DatePrototypeSetUTCFullYear { - const NAME: String = BUILTIN_STRING_MEMORY.setUTCFullYear; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUTCFullYear; const LENGTH: u8 = 3; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_utc_full_year); } struct DatePrototypeSetUTCHours; impl Builtin for DatePrototypeSetUTCHours { - const NAME: String = BUILTIN_STRING_MEMORY.setUTCHours; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUTCHours; const LENGTH: u8 = 4; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_utc_hours); } struct DatePrototypeSetUTCMilliseconds; impl Builtin for DatePrototypeSetUTCMilliseconds { - const NAME: String = BUILTIN_STRING_MEMORY.setUTCMilliseconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUTCMilliseconds; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_utc_milliseconds); } struct DatePrototypeSetUTCMinutes; impl Builtin for DatePrototypeSetUTCMinutes { - const NAME: String = BUILTIN_STRING_MEMORY.setUTCMinutes; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUTCMinutes; const LENGTH: u8 = 3; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_utc_minutes); } struct DatePrototypeSetUTCMonth; impl Builtin for DatePrototypeSetUTCMonth { - const NAME: String = BUILTIN_STRING_MEMORY.setUTCMonth; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUTCMonth; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_utc_month); } struct DatePrototypeSetUTCSeconds; impl Builtin for DatePrototypeSetUTCSeconds { - const NAME: String = BUILTIN_STRING_MEMORY.setUTCSeconds; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUTCSeconds; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::set_utc_seconds); } struct DatePrototypeToDateString; impl Builtin for DatePrototypeToDateString { - const NAME: String = BUILTIN_STRING_MEMORY.toDateString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toDateString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_date_string); } struct DatePrototypeToISOString; impl Builtin for DatePrototypeToISOString { - const NAME: String = BUILTIN_STRING_MEMORY.toISOString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toISOString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_iso_string); } struct DatePrototypeToJSON; impl Builtin for DatePrototypeToJSON { - const NAME: String = BUILTIN_STRING_MEMORY.toJSON; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toJSON; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_json); } struct DatePrototypeToLocaleDateString; impl Builtin for DatePrototypeToLocaleDateString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleDateString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleDateString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_locale_date_string); } struct DatePrototypeToLocaleString; impl Builtin for DatePrototypeToLocaleString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_locale_string); } struct DatePrototypeToLocaleTimeString; impl Builtin for DatePrototypeToLocaleTimeString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleTimeString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleTimeString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_locale_time_string); } struct DatePrototypeToString; impl Builtin for DatePrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_string); } struct DatePrototypeToTimeString; impl Builtin for DatePrototypeToTimeString { - const NAME: String = BUILTIN_STRING_MEMORY.toTimeString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toTimeString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_time_string); } struct DatePrototypeToUTCString; impl Builtin for DatePrototypeToUTCString { - const NAME: String = BUILTIN_STRING_MEMORY.toUTCString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toUTCString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::to_utc_string); } @@ -276,13 +276,13 @@ impl BuiltinIntrinsic for DatePrototypeToUTCString { } struct DatePrototypeValueOf; impl Builtin for DatePrototypeValueOf { - const NAME: String = BUILTIN_STRING_MEMORY.valueOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.valueOf; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DatePrototype::value_of); } struct DatePrototypeToPrimitive; impl Builtin for DatePrototypeToPrimitive { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_toPrimitive_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_toPrimitive_; const KEY: Option = Some(WellKnownSymbolIndexes::ToPrimitive.to_property_key()); @@ -299,393 +299,357 @@ const MAX_SYSTEM_TIME_VALUE: u128 = SmallInteger::MAX_NUMBER as u128; impl DatePrototype { fn get_date( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_day( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_full_year( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_hours( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_milliseconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_minutes( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_month( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_seconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_time( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_timezone_offset( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_date( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_day( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_full_year( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_hours( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_milliseconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_minutes( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_month( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn get_utc_seconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_date( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_full_year( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_hours( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_milliseconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_minutes( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_month( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_seconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_time( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_utc_date( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_utc_full_year( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_utc_hours( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_utc_milliseconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_utc_minutes( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_utc_month( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn set_utc_seconds( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn to_date_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn to_iso_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn to_json( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -695,7 +659,6 @@ impl DatePrototype { fn to_locale_date_string( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -705,7 +668,6 @@ impl DatePrototype { fn to_locale_string( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -715,7 +677,6 @@ impl DatePrototype { fn to_locale_time_string( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -724,45 +685,41 @@ impl DatePrototype { fn to_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn to_time_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn to_utc_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let _date_object = check_date_object(agent, this_value)?; + let _date_object = check_date_object(agent, gc.nogc(), this_value)?; todo!() } fn value_of( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - let date_object = check_date_object(agent, this_value)?; + let date_object = check_date_object(agent, gc.nogc(), this_value)?; let data = &agent[date_object].date; match data { Some(system_time) => { @@ -814,7 +771,6 @@ impl DatePrototype { fn to_primitive( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -826,7 +782,7 @@ impl DatePrototype { "{} is not an object", this_value.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 3. If hint is either "string" or "default", then let try_first = if hint == BUILTIN_STRING_MEMORY.string.into_value() @@ -845,7 +801,7 @@ impl DatePrototype { "Expected 'hint' to be \"string\", \"default\", or \"number\", got {}", hint.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 6. Return ? OrdinaryToPrimitive(O, tryFirst). ordinary_to_primitive(agent, gc.reborrow(), o, try_first).map(|result| result.into_value()) @@ -910,10 +866,11 @@ impl DatePrototype { } #[inline(always)] -fn check_date_object(agent: &mut Agent, this_value: Value) -> JsResult { +fn check_date_object(agent: &mut Agent, gc: NoGcScope, this_value: Value) -> JsResult { match this_value { Value::Date(date) => Ok(date), _ => Err(agent.throw_exception_with_static_message( + gc, 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 1d27c5f60..fd70fbdcc 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 @@ -20,7 +20,7 @@ pub(crate) struct MathObject; struct MathObjectAbs; impl Builtin for MathObjectAbs { - const NAME: String = BUILTIN_STRING_MEMORY.abs; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.abs; const LENGTH: u8 = 1; @@ -30,7 +30,7 @@ impl Builtin for MathObjectAbs { struct MathObjectAcos; impl Builtin for MathObjectAcos { - const NAME: String = BUILTIN_STRING_MEMORY.acos; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.acos; const LENGTH: u8 = 1; @@ -39,7 +39,7 @@ impl Builtin for MathObjectAcos { } struct MathObjectAcosh; impl Builtin for MathObjectAcosh { - const NAME: String = BUILTIN_STRING_MEMORY.acosh; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.acosh; const LENGTH: u8 = 1; @@ -48,7 +48,7 @@ impl Builtin for MathObjectAcosh { } struct MathObjectAsin; impl Builtin for MathObjectAsin { - const NAME: String = BUILTIN_STRING_MEMORY.asin; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.asin; const LENGTH: u8 = 1; @@ -57,7 +57,7 @@ impl Builtin for MathObjectAsin { } struct MathObjectAsinh; impl Builtin for MathObjectAsinh { - const NAME: String = BUILTIN_STRING_MEMORY.asinh; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.asinh; const LENGTH: u8 = 1; @@ -66,7 +66,7 @@ impl Builtin for MathObjectAsinh { } struct MathObjectAtan; impl Builtin for MathObjectAtan { - const NAME: String = BUILTIN_STRING_MEMORY.atan; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.atan; const LENGTH: u8 = 1; @@ -75,7 +75,7 @@ impl Builtin for MathObjectAtan { } struct MathObjectAtanh; impl Builtin for MathObjectAtanh { - const NAME: String = BUILTIN_STRING_MEMORY.atanh; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.atanh; const LENGTH: u8 = 1; @@ -84,7 +84,7 @@ impl Builtin for MathObjectAtanh { } struct MathObjectAtan2; impl Builtin for MathObjectAtan2 { - const NAME: String = BUILTIN_STRING_MEMORY.atan2; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.atan2; const LENGTH: u8 = 2; @@ -93,7 +93,7 @@ impl Builtin for MathObjectAtan2 { } struct MathObjectCbrt; impl Builtin for MathObjectCbrt { - const NAME: String = BUILTIN_STRING_MEMORY.cbrt; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.cbrt; const LENGTH: u8 = 1; @@ -102,7 +102,7 @@ impl Builtin for MathObjectCbrt { } struct MathObjectCeil; impl Builtin for MathObjectCeil { - const NAME: String = BUILTIN_STRING_MEMORY.ceil; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.ceil; const LENGTH: u8 = 1; @@ -111,7 +111,7 @@ impl Builtin for MathObjectCeil { } struct MathObjectClz32; impl Builtin for MathObjectClz32 { - const NAME: String = BUILTIN_STRING_MEMORY.clz32; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.clz32; const LENGTH: u8 = 1; @@ -120,7 +120,7 @@ impl Builtin for MathObjectClz32 { } struct MathObjectCos; impl Builtin for MathObjectCos { - const NAME: String = BUILTIN_STRING_MEMORY.cos; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.cos; const LENGTH: u8 = 1; @@ -129,7 +129,7 @@ impl Builtin for MathObjectCos { } struct MathObjectCosh; impl Builtin for MathObjectCosh { - const NAME: String = BUILTIN_STRING_MEMORY.cosh; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.cosh; const LENGTH: u8 = 1; @@ -138,7 +138,7 @@ impl Builtin for MathObjectCosh { } struct MathObjectExp; impl Builtin for MathObjectExp { - const NAME: String = BUILTIN_STRING_MEMORY.exp; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.exp; const LENGTH: u8 = 1; @@ -147,7 +147,7 @@ impl Builtin for MathObjectExp { } struct MathObjectExpm1; impl Builtin for MathObjectExpm1 { - const NAME: String = BUILTIN_STRING_MEMORY.expm1; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.expm1; const LENGTH: u8 = 1; @@ -156,7 +156,7 @@ impl Builtin for MathObjectExpm1 { } struct MathObjectFloor; impl Builtin for MathObjectFloor { - const NAME: String = BUILTIN_STRING_MEMORY.floor; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.floor; const LENGTH: u8 = 1; @@ -165,7 +165,7 @@ impl Builtin for MathObjectFloor { } struct MathObjectFround; impl Builtin for MathObjectFround { - const NAME: String = BUILTIN_STRING_MEMORY.fround; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.fround; const LENGTH: u8 = 1; @@ -174,7 +174,7 @@ impl Builtin for MathObjectFround { } struct MathObjectHypot; impl Builtin for MathObjectHypot { - const NAME: String = BUILTIN_STRING_MEMORY.hypot; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.hypot; const LENGTH: u8 = 2; @@ -183,7 +183,7 @@ impl Builtin for MathObjectHypot { } struct MathObjectImul; impl Builtin for MathObjectImul { - const NAME: String = BUILTIN_STRING_MEMORY.imul; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.imul; const LENGTH: u8 = 2; @@ -192,7 +192,7 @@ impl Builtin for MathObjectImul { } struct MathObjectLog; impl Builtin for MathObjectLog { - const NAME: String = BUILTIN_STRING_MEMORY.log; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.log; const LENGTH: u8 = 1; @@ -201,7 +201,7 @@ impl Builtin for MathObjectLog { } struct MathObjectLog1p; impl Builtin for MathObjectLog1p { - const NAME: String = BUILTIN_STRING_MEMORY.log1p; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.log1p; const LENGTH: u8 = 1; @@ -210,7 +210,7 @@ impl Builtin for MathObjectLog1p { } struct MathObjectLog10; impl Builtin for MathObjectLog10 { - const NAME: String = BUILTIN_STRING_MEMORY.log10; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.log10; const LENGTH: u8 = 1; @@ -219,7 +219,7 @@ impl Builtin for MathObjectLog10 { } struct MathObjectLog2; impl Builtin for MathObjectLog2 { - const NAME: String = BUILTIN_STRING_MEMORY.log2; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.log2; const LENGTH: u8 = 1; @@ -228,7 +228,7 @@ impl Builtin for MathObjectLog2 { } struct MathObjectMax; impl Builtin for MathObjectMax { - const NAME: String = BUILTIN_STRING_MEMORY.max; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.max; const LENGTH: u8 = 2; @@ -237,7 +237,7 @@ impl Builtin for MathObjectMax { } struct MathObjectMin; impl Builtin for MathObjectMin { - const NAME: String = BUILTIN_STRING_MEMORY.min; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.min; const LENGTH: u8 = 2; @@ -246,7 +246,7 @@ impl Builtin for MathObjectMin { } struct MathObjectPow; impl Builtin for MathObjectPow { - const NAME: String = BUILTIN_STRING_MEMORY.pow; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.pow; const LENGTH: u8 = 2; @@ -255,7 +255,7 @@ impl Builtin for MathObjectPow { } struct MathObjectRandom; impl Builtin for MathObjectRandom { - const NAME: String = BUILTIN_STRING_MEMORY.random; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.random; const LENGTH: u8 = 0; @@ -264,7 +264,7 @@ impl Builtin for MathObjectRandom { } struct MathObjectRound; impl Builtin for MathObjectRound { - const NAME: String = BUILTIN_STRING_MEMORY.round; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.round; const LENGTH: u8 = 1; @@ -273,7 +273,7 @@ impl Builtin for MathObjectRound { } struct MathObjectSign; impl Builtin for MathObjectSign { - const NAME: String = BUILTIN_STRING_MEMORY.sign; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.sign; const LENGTH: u8 = 1; @@ -282,7 +282,7 @@ impl Builtin for MathObjectSign { } struct MathObjectSin; impl Builtin for MathObjectSin { - const NAME: String = BUILTIN_STRING_MEMORY.sin; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.sin; const LENGTH: u8 = 1; @@ -291,7 +291,7 @@ impl Builtin for MathObjectSin { } struct MathObjectSinh; impl Builtin for MathObjectSinh { - const NAME: String = BUILTIN_STRING_MEMORY.sinh; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.sinh; const LENGTH: u8 = 1; @@ -300,7 +300,7 @@ impl Builtin for MathObjectSinh { } struct MathObjectSqrt; impl Builtin for MathObjectSqrt { - const NAME: String = BUILTIN_STRING_MEMORY.sqrt; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.sqrt; const LENGTH: u8 = 1; @@ -309,7 +309,7 @@ impl Builtin for MathObjectSqrt { } struct MathObjectTan; impl Builtin for MathObjectTan { - const NAME: String = BUILTIN_STRING_MEMORY.tan; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.tan; const LENGTH: u8 = 1; @@ -318,7 +318,7 @@ impl Builtin for MathObjectTan { } struct MathObjectTanh; impl Builtin for MathObjectTanh { - const NAME: String = BUILTIN_STRING_MEMORY.tanh; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.tanh; const LENGTH: u8 = 1; @@ -327,7 +327,7 @@ impl Builtin for MathObjectTanh { } struct MathObjectTrunc; impl Builtin for MathObjectTrunc { - const NAME: String = BUILTIN_STRING_MEMORY.trunc; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.trunc; const LENGTH: u8 = 1; @@ -339,7 +339,6 @@ impl MathObject { fn abs( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -350,7 +349,6 @@ impl MathObject { fn acos( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -365,7 +363,6 @@ impl MathObject { fn acosh( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -392,7 +389,6 @@ impl MathObject { fn asin( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -418,7 +414,6 @@ impl MathObject { fn asinh( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -437,7 +432,6 @@ impl MathObject { fn atan( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -466,7 +460,6 @@ impl MathObject { fn atanh( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -502,7 +495,6 @@ impl MathObject { fn atan2( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -630,7 +622,6 @@ impl MathObject { fn cbrt( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -649,7 +640,6 @@ impl MathObject { fn ceil( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -680,7 +670,6 @@ impl MathObject { fn clz32( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -697,7 +686,6 @@ impl MathObject { fn cos( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -721,7 +709,6 @@ impl MathObject { fn cosh( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -750,7 +737,6 @@ impl MathObject { fn exp( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -779,7 +765,6 @@ impl MathObject { fn expm1( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -807,7 +792,6 @@ impl MathObject { fn floor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -838,7 +822,6 @@ impl MathObject { fn fround( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -872,7 +855,6 @@ impl MathObject { fn hypot( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -931,7 +913,6 @@ impl MathObject { fn imul( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -951,7 +932,6 @@ impl MathObject { fn log( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -985,7 +965,6 @@ impl MathObject { fn log1p( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1014,7 +993,6 @@ impl MathObject { fn log10( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1044,7 +1022,6 @@ impl MathObject { fn log2( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1073,7 +1050,6 @@ impl MathObject { fn max( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1155,7 +1131,6 @@ impl MathObject { fn min( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1237,7 +1212,6 @@ impl MathObject { fn pow( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1269,7 +1243,6 @@ impl MathObject { fn random( agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1279,7 +1252,6 @@ impl MathObject { fn round( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1310,7 +1282,6 @@ impl MathObject { fn sign( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1331,7 +1302,6 @@ impl MathObject { fn sin( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1352,7 +1322,6 @@ impl MathObject { fn sinh( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1369,7 +1338,6 @@ impl MathObject { fn sqrt( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1394,7 +1362,6 @@ impl MathObject { fn tan( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1415,7 +1382,6 @@ impl MathObject { fn tanh( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -1440,7 +1406,6 @@ impl MathObject { fn trunc( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { 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 29a849941..3cf2ad12d 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 @@ -37,7 +37,7 @@ pub struct NumberConstructor; impl Builtin for NumberConstructor { const BEHAVIOUR: Behaviour = Behaviour::Constructor(Self::behaviour); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.Number; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Number; } impl BuiltinIntrinsicConstructor for NumberConstructor { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::Number; @@ -47,32 +47,31 @@ struct NumberIsFinite; impl Builtin for NumberIsFinite { const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberConstructor::is_finite); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.isFinite; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isFinite; } struct NumberIsInteger; impl Builtin for NumberIsInteger { const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberConstructor::is_integer); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.isInteger; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isInteger; } struct NumberIsNaN; impl Builtin for NumberIsNaN { const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberConstructor::is_nan); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.isNaN; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isNaN; } struct NumberIsSafeInteger; impl Builtin for NumberIsSafeInteger { const BEHAVIOUR: Behaviour = Behaviour::Regular(NumberConstructor::is_safe_integer); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.isSafeInteger; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isSafeInteger; } impl NumberConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -129,7 +128,6 @@ impl NumberConstructor { fn is_finite( agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -149,7 +147,6 @@ impl NumberConstructor { fn is_integer( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -163,7 +160,6 @@ impl NumberConstructor { fn is_nan( agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -183,7 +179,6 @@ impl NumberConstructor { fn is_safe_integer( agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { 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 75b040346..d2a7221ed 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,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::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_integer_or_infinity, @@ -21,7 +21,7 @@ pub(crate) struct NumberPrototype; struct NumberPrototypeToExponential; impl Builtin for NumberPrototypeToExponential { - const NAME: String = BUILTIN_STRING_MEMORY.toExponential; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toExponential; const LENGTH: u8 = 1; @@ -31,7 +31,7 @@ impl Builtin for NumberPrototypeToExponential { struct NumberPrototypeToFixed; impl Builtin for NumberPrototypeToFixed { - const NAME: String = BUILTIN_STRING_MEMORY.toFixed; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toFixed; const LENGTH: u8 = 1; @@ -41,7 +41,7 @@ impl Builtin for NumberPrototypeToFixed { struct NumberPrototypeToLocaleString; impl Builtin for NumberPrototypeToLocaleString { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleString; const LENGTH: u8 = 0; @@ -51,7 +51,7 @@ impl Builtin for NumberPrototypeToLocaleString { struct NumberPrototypeToPrecision; impl Builtin for NumberPrototypeToPrecision { - const NAME: String = BUILTIN_STRING_MEMORY.toPrecision; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toPrecision; const LENGTH: u8 = 1; @@ -61,7 +61,7 @@ impl Builtin for NumberPrototypeToPrecision { struct NumberPrototypeToString; impl Builtin for NumberPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 1; @@ -71,7 +71,7 @@ impl Builtin for NumberPrototypeToString { struct NumberPrototypeValueOf; impl Builtin for NumberPrototypeValueOf { - const NAME: String = BUILTIN_STRING_MEMORY.valueOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.valueOf; const LENGTH: u8 = 0; @@ -82,26 +82,26 @@ impl Builtin for NumberPrototypeValueOf { impl NumberPrototype { fn to_exponential( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { let fraction_digits = arguments.get(0); // Let x be ? ThisNumberValue(this value). - let x = this_number_value(agent, this_value)?; + let x = this_number_value(agent, gc.nogc(), this_value)?; // 2. Let f be ? ToIntegerOrInfinity(fractionDigits). - let f = to_integer_or_infinity(agent, gc, fraction_digits)?; + let f = to_integer_or_infinity(agent, gc.reborrow(), fraction_digits)?; // 3. Assert: If fractionDigits is undefined, then f is 0. debug_assert!(!fraction_digits.is_undefined() || f.is_pos_zero(agent)); // 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).into_value()); + return Ok(Number::to_string_radix_10(agent, gc.nogc(), x).into_value()); } let f = f.into_i64(agent); // 5. If f < 0 or f > 100, throw a RangeError exception. if !(0..=100).contains(&f) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Fraction digits count out of range", )); @@ -115,29 +115,29 @@ impl NumberPrototype { x = 0.0; }; if f == 0 { - Ok(f64_to_exponential(agent, x)) + Ok(f64_to_exponential(agent, gc.nogc(), x)) } else { - Ok(f64_to_exponential_with_precision(agent, x, f)) + Ok(f64_to_exponential_with_precision(agent, gc.nogc(), x, f)) } } fn to_fixed( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { let fraction_digits = arguments.get(0); // Let x be ? ThisNumberValue(this value). - let x = this_number_value(agent, this_value)?; + let x = this_number_value(agent, gc.nogc(), this_value)?; // 2. Let f be ? ToIntegerOrInfinity(fractionDigits). - let f = to_integer_or_infinity(agent, gc, fraction_digits)?; + let f = to_integer_or_infinity(agent, gc.reborrow(), fraction_digits)?; // 3. Assert: If fractionDigits is undefined, then f is 0. debug_assert!(!fraction_digits.is_undefined() || f.is_pos_zero(agent)); // 4. If f is not finite, throw a RangeError exception. if !f.is_finite(agent) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Fraction digits count out of range", )); @@ -146,25 +146,25 @@ impl NumberPrototype { // 5. If f < 0 or f > 100, throw a RangeError exception. if !(0..=100).contains(&f) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Fraction digits count out of range", )); } // 6. If x is not finite, return Number::toString(x, 10). if !x.is_finite(agent) { - return Ok(Number::to_string_radix_10(agent, x).into_value()); + return Ok(Number::to_string_radix_10(agent, gc.nogc(), x).into_value()); } // 7. Set x to ℝ(x). let x = x.into_f64(agent); let mut buffer = ryu_js::Buffer::new(); let string = buffer.format_to_fixed(x, f as u8); - Ok(Value::from_str(agent, string)) + Ok(Value::from_str(agent, gc.nogc(), string)) } fn to_locale_string( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -177,36 +177,36 @@ impl NumberPrototype { /// Copyright (c) 2019 Jason Williams fn to_precision( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { let precision = arguments.get(0); // 1. Let x be ? ThisNumberValue(this value). - let x = this_number_value(agent, this_value)?; + let x = this_number_value(agent, gc.nogc(), this_value)?; // 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).into_value()); + return Ok(Number::to_string_radix_10(agent, gc.nogc(), x).into_value()); } // 3. Let p be ? ToIntegerOrInfinity(precision). - let p = to_integer_or_infinity(agent, gc, precision)?; + let p = to_integer_or_infinity(agent, gc.reborrow(), precision)?; // 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).into_value()); + return Ok(Number::to_string_radix_10(agent, gc.nogc(), x).into_value()); } // 5. If p < 1 or p > 100, throw a RangeError exception. let precision = p.into_i64(agent) as i32; if !(1..=100).contains(&precision) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Precision out of range", )); @@ -291,14 +291,14 @@ impl NumberPrototype { // zeroes). m.push_str(&e.to_string()); - return Ok(Value::from_string(agent, s + &m)); + return Ok(Value::from_string(agent, gc.nogc(), s + &m)); } } // 11. If e = p - 1, return the string-concatenation of s and m. let e_inc = e + 1; if e_inc == precision as i32 { - return Ok(String::from_string(agent, s + &m).into_value()); + return Ok(String::from_string(agent, gc.nogc(), s + &m).into_value()); } // 12. If e ≥ 0, then @@ -319,7 +319,7 @@ impl NumberPrototype { } // 14. Return the string-concatenation of s and m. - Ok(String::from_string(agent, s + &m).into_value()) + Ok(String::from_string(agent, gc.nogc(), s + &m).into_value()) } /// round_to_precision - used in to_precision @@ -414,15 +414,14 @@ impl NumberPrototype { fn to_string( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope, this_value: Value, arguments: ArgumentsList, ) -> JsResult { - let x = this_number_value(agent, this_value)?; + let x = this_number_value(agent, gc.nogc(), this_value)?; let radix = arguments.get(0); if radix.is_undefined() || radix == Value::from(10u8) { - Ok(Number::to_string_radix_10(agent, x).into_value()) + Ok(Number::to_string_radix_10(agent, gc.nogc(), x).into_value()) } else { todo!(); } @@ -430,12 +429,11 @@ impl NumberPrototype { fn value_of( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { - this_number_value(agent, this_value).map(|result| result.into_value()) + this_number_value(agent, gc.nogc(), this_value).map(|result| result.into_value()) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { @@ -470,27 +468,29 @@ impl NumberPrototype { } } -fn f64_to_exponential(agent: &mut Agent, x: f64) -> Value { +fn f64_to_exponential(agent: &mut Agent, gc: NoGcScope, x: f64) -> Value { match x.abs() { - x if x >= 1.0 || x == 0.0 => Value::from_string(agent, format!("{x:e}").replace('e', "e+")), - _ => Value::from_string(agent, format!("{x:e}")), + x if x >= 1.0 || x == 0.0 => { + Value::from_string(agent, gc, format!("{x:e}").replace('e', "e+")) + } + _ => Value::from_string(agent, gc, format!("{x:e}")), } } -fn f64_to_exponential_with_precision(agent: &mut Agent, x: f64, f: usize) -> Value { +fn f64_to_exponential_with_precision(agent: &mut Agent, gc: NoGcScope, x: f64, f: usize) -> Value { let mut res = format!("{x:.f$e}"); let idx = res.find('e').unwrap(); if res.as_bytes()[idx + 1] != b'-' { res.insert(idx + 1, '+'); } - Value::from_string(agent, res) + Value::from_string(agent, gc, res) } /// ### [21.1.3.7.1 ThisNumberValue ( value )](https://tc39.es/ecma262/#sec-thisnumbervalue) /// /// The abstract operation ThisNumberValue takes argument value (an ECMAScript language value) and returns either a normal completion containing a Number or a throw completion. It performs the following steps when called: #[inline(always)] -fn this_number_value(agent: &mut Agent, value: Value) -> JsResult { +fn this_number_value(agent: &mut Agent, gc: NoGcScope, value: Value) -> JsResult { // 1. If value is a Number, return value. if let Ok(value) = Number::try_from(value) { return Ok(value); @@ -506,5 +506,5 @@ fn this_number_value(agent: &mut Agent, value: Value) -> JsResult { } } // 3. Throw a TypeError exception. - Err(agent.throw_exception_with_static_message(ExceptionType::TypeError, "Not a Number")) + Err(agent.throw_exception_with_static_message(gc, ExceptionType::TypeError, "Not a Number")) } diff --git a/nova_vm/src/ecmascript/builtins/ordinary.rs b/nova_vm/src/ecmascript/builtins/ordinary.rs index 5972e0585..e286b6182 100644 --- a/nova_vm/src/ecmascript/builtins/ordinary.rs +++ b/nova_vm/src/ecmascript/builtins/ordinary.rs @@ -7,7 +7,7 @@ use std::{ vec, }; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -240,7 +240,6 @@ pub(crate) fn ordinary_get_own_property( pub(crate) fn ordinary_define_own_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - object: Object, property_key: PropertyKey, descriptor: PropertyDescriptor, @@ -265,11 +264,12 @@ pub(crate) fn ordinary_define_own_property( /// ### [10.1.6.2 IsCompatiblePropertyDescriptor ( Extensible, Desc, Current )](https://tc39.es/ecma262/#sec-iscompatiblepropertydescriptor) pub(crate) fn is_compatible_property_descriptor( agent: &mut Agent, + gc: NoGcScope, extensible: bool, descriptor: PropertyDescriptor, current: Option, ) -> JsResult { - let property_key = PropertyKey::from_str(agent, ""); + let property_key = PropertyKey::from_str(agent, gc, ""); validate_and_apply_property_descriptor( agent, None, @@ -517,7 +517,6 @@ fn validate_and_apply_property_descriptor( pub(crate) fn ordinary_has_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - object: Object, property_key: PropertyKey, ) -> JsResult { @@ -546,7 +545,6 @@ pub(crate) fn ordinary_has_property( pub(crate) fn ordinary_get( agent: &mut Agent, mut gc: GcScope<'_, '_>, - object: Object, property_key: PropertyKey, receiver: Value, @@ -588,7 +586,6 @@ pub(crate) fn ordinary_get( pub(crate) fn ordinary_set( agent: &mut Agent, mut gc: GcScope<'_, '_>, - object: Object, property_key: PropertyKey, value: Value, @@ -613,7 +610,6 @@ pub(crate) fn ordinary_set( pub(crate) fn ordinary_set_with_own_descriptor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - object: Object, property_key: PropertyKey, value: Value, @@ -722,7 +718,6 @@ pub(crate) fn ordinary_set_with_own_descriptor( pub(crate) fn ordinary_delete( agent: &mut Agent, gc: GcScope<'_, '_>, - object: Object, property_key: PropertyKey, ) -> JsResult { @@ -1042,7 +1037,6 @@ pub(crate) fn ordinary_object_create_with_intrinsics( pub(crate) fn ordinary_create_from_constructor( agent: &mut Agent, gc: GcScope<'_, '_>, - constructor: Function, intrinsic_default_proto: ProtoIntrinsics, ) -> JsResult { @@ -1079,7 +1073,6 @@ pub(crate) fn ordinary_create_from_constructor( pub(crate) fn get_prototype_from_constructor( agent: &mut Agent, gc: GcScope<'_, '_>, - constructor: Function, intrinsic_default_proto: ProtoIntrinsics, ) -> JsResult> { @@ -1213,7 +1206,6 @@ pub(crate) fn get_prototype_from_constructor( pub(crate) fn set_immutable_prototype( agent: &mut Agent, gc: GcScope<'_, '_>, - o: Object, v: Option, ) -> JsResult { diff --git a/nova_vm/src/ecmascript/builtins/primitive_objects.rs b/nova_vm/src/ecmascript/builtins/primitive_objects.rs index b4ffdf80e..a31804552 100644 --- a/nova_vm/src/ecmascript/builtins/primitive_objects.rs +++ b/nova_vm/src/ecmascript/builtins/primitive_objects.rs @@ -207,7 +207,6 @@ impl InternalMethods for PrimitiveObject { self, agent: &mut Agent, _gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { // For non-string primitive objects: @@ -235,7 +234,6 @@ impl InternalMethods for PrimitiveObject { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -248,6 +246,7 @@ impl InternalMethods for PrimitiveObject { // b. Return IsCompatiblePropertyDescriptor(extensible, Desc, stringDesc). return is_compatible_property_descriptor( agent, + gc.nogc(), self.internal_extensible(agent), property_descriptor, Some(string_desc), @@ -267,7 +266,6 @@ impl InternalMethods for PrimitiveObject { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { if let Ok(string) = String::try_from(agent[self].data) { @@ -304,7 +302,6 @@ impl InternalMethods for PrimitiveObject { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -340,7 +337,6 @@ impl InternalMethods for PrimitiveObject { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -366,7 +362,6 @@ impl InternalMethods for PrimitiveObject { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { if let Ok(string) = String::try_from(agent[self].data) { @@ -441,7 +436,7 @@ impl InternalMethods for PrimitiveObject { #[repr(u8)] pub(crate) enum PrimitiveObjectData { Boolean(bool) = BOOLEAN_DISCRIMINANT, - String(HeapString) = STRING_DISCRIMINANT, + String(HeapString<'static>) = STRING_DISCRIMINANT, SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, Symbol(Symbol) = SYMBOL_DISCRIMINANT, Number(HeapNumber) = NUMBER_DISCRIMINANT, @@ -476,7 +471,7 @@ impl TryFrom for Number { } } -impl TryFrom for String { +impl TryFrom for String<'static> { type Error = (); fn try_from(value: PrimitiveObjectData) -> Result { @@ -536,7 +531,7 @@ impl PrimitiveObjectHeapData { } } - pub(crate) fn new_string_object(string: String) -> Self { + pub(crate) fn new_string_object(string: String<'static>) -> Self { let data = match string { String::String(data) => PrimitiveObjectData::String(data), String::SmallString(data) => PrimitiveObjectData::SmallString(data), diff --git a/nova_vm/src/ecmascript/builtins/proxy.rs b/nova_vm/src/ecmascript/builtins/proxy.rs index 0a4f98afb..a192dcf61 100644 --- a/nova_vm/src/ecmascript/builtins/proxy.rs +++ b/nova_vm/src/ecmascript/builtins/proxy.rs @@ -117,7 +117,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _prototype: Option, ) -> JsResult { todo!(); @@ -140,7 +139,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, ) -> JsResult> { todo!(); @@ -150,7 +148,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, _property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -161,7 +158,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, ) -> JsResult { todo!(); @@ -171,7 +167,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, _receiver: Value, ) -> JsResult { @@ -182,7 +177,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, _value: Value, _receiver: Value, @@ -194,7 +188,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _property_key: PropertyKey, ) -> JsResult { todo!(); @@ -212,7 +205,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments_list: super::ArgumentsList, ) -> JsResult { @@ -223,7 +215,6 @@ impl InternalMethods for Proxy { self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _arguments_list: super::ArgumentsList, _new_target: crate::ecmascript::types::Function, ) -> JsResult { diff --git a/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs b/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs index 7fe18a151..514b3270a 100644 --- a/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/reflection/proxy_constructor.rs @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct ProxyConstructor; impl Builtin for ProxyConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.Proxy; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.Proxy; const LENGTH: u8 = 2; @@ -27,7 +27,7 @@ impl BuiltinIntrinsicConstructor for ProxyConstructor { struct ProxyRevocable; impl Builtin for ProxyRevocable { - const NAME: String = BUILTIN_STRING_MEMORY.revocable; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.revocable; const LENGTH: u8 = 2; @@ -38,7 +38,6 @@ impl ProxyConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, @@ -49,7 +48,6 @@ impl ProxyConstructor { fn revocable( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { diff --git a/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs b/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs index 4ce5b0f2c..d7bf69498 100644 --- a/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs +++ b/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs @@ -27,7 +27,7 @@ pub(crate) struct ReflectObject; struct ReflectObjectApply; impl Builtin for ReflectObjectApply { - const NAME: String = BUILTIN_STRING_MEMORY.apply; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.apply; const LENGTH: u8 = 3; @@ -37,7 +37,7 @@ impl Builtin for ReflectObjectApply { struct ReflectObjectConstruct; impl Builtin for ReflectObjectConstruct { - const NAME: String = BUILTIN_STRING_MEMORY.construct; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.construct; const LENGTH: u8 = 2; @@ -46,7 +46,7 @@ impl Builtin for ReflectObjectConstruct { } struct ReflectObjectDefineProperty; impl Builtin for ReflectObjectDefineProperty { - const NAME: String = BUILTIN_STRING_MEMORY.defineProperty; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.defineProperty; const LENGTH: u8 = 2; @@ -55,7 +55,7 @@ impl Builtin for ReflectObjectDefineProperty { } struct ReflectObjectDeleteProperty; impl Builtin for ReflectObjectDeleteProperty { - const NAME: String = BUILTIN_STRING_MEMORY.deleteProperty; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.deleteProperty; const LENGTH: u8 = 2; @@ -64,7 +64,7 @@ impl Builtin for ReflectObjectDeleteProperty { } struct ReflectObjectGet; impl Builtin for ReflectObjectGet { - const NAME: String = BUILTIN_STRING_MEMORY.get; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get; const LENGTH: u8 = 2; @@ -73,7 +73,7 @@ impl Builtin for ReflectObjectGet { } struct ReflectObjectGetOwnPropertyDescriptor; impl Builtin for ReflectObjectGetOwnPropertyDescriptor { - const NAME: String = BUILTIN_STRING_MEMORY.getOwnPropertyDescriptor; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getOwnPropertyDescriptor; const LENGTH: u8 = 2; @@ -82,7 +82,7 @@ impl Builtin for ReflectObjectGetOwnPropertyDescriptor { } struct ReflectObjectGetPrototypeOf; impl Builtin for ReflectObjectGetPrototypeOf { - const NAME: String = BUILTIN_STRING_MEMORY.getPrototypeOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getPrototypeOf; const LENGTH: u8 = 1; @@ -92,7 +92,7 @@ impl Builtin for ReflectObjectGetPrototypeOf { struct ReflectObjectHas; impl Builtin for ReflectObjectHas { - const NAME: String = BUILTIN_STRING_MEMORY.has; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.has; const LENGTH: u8 = 2; @@ -101,7 +101,7 @@ impl Builtin for ReflectObjectHas { } struct ReflectObjectIsExtensible; impl Builtin for ReflectObjectIsExtensible { - const NAME: String = BUILTIN_STRING_MEMORY.isExtensible; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isExtensible; const LENGTH: u8 = 1; @@ -110,7 +110,7 @@ impl Builtin for ReflectObjectIsExtensible { } struct ReflectObjectOwnKeys; impl Builtin for ReflectObjectOwnKeys { - const NAME: String = BUILTIN_STRING_MEMORY.ownKeys; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.ownKeys; const LENGTH: u8 = 1; @@ -119,7 +119,7 @@ impl Builtin for ReflectObjectOwnKeys { } struct ReflectObjectPreventExtensions; impl Builtin for ReflectObjectPreventExtensions { - const NAME: String = BUILTIN_STRING_MEMORY.preventExtensions; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.preventExtensions; const LENGTH: u8 = 1; @@ -128,7 +128,7 @@ impl Builtin for ReflectObjectPreventExtensions { } struct ReflectObjectSet; impl Builtin for ReflectObjectSet { - const NAME: String = BUILTIN_STRING_MEMORY.set; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.set; const LENGTH: u8 = 3; @@ -137,7 +137,7 @@ impl Builtin for ReflectObjectSet { } struct ReflectObjectSetPrototypeOf; impl Builtin for ReflectObjectSetPrototypeOf { - const NAME: String = BUILTIN_STRING_MEMORY.setPrototypeOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setPrototypeOf; const LENGTH: u8 = 2; @@ -150,7 +150,6 @@ impl ReflectObject { fn apply( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -161,6 +160,7 @@ impl ReflectObject { // 1. If IsCallable(target) is false, throw a TypeError exception. let Some(target) = is_callable(target) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not callable", )); @@ -176,7 +176,6 @@ impl ReflectObject { fn construct( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -186,6 +185,7 @@ impl ReflectObject { // 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( + gc.nogc(), ExceptionType::TypeError, "Value is not a constructor", )); @@ -197,6 +197,7 @@ impl ReflectObject { let new_target = arguments.get(2); let Some(new_target) = is_constructor(agent, new_target) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not a constructor", )); @@ -223,13 +224,13 @@ impl ReflectObject { fn define_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -250,13 +251,13 @@ impl ReflectObject { fn delete_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -274,13 +275,13 @@ impl ReflectObject { fn get( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -304,13 +305,13 @@ impl ReflectObject { fn get_own_property_descriptor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -332,13 +333,13 @@ impl ReflectObject { fn get_prototype_of( agent: &mut Agent, gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -355,13 +356,13 @@ impl ReflectObject { fn has( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -379,13 +380,13 @@ impl ReflectObject { fn is_extensible( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -400,13 +401,13 @@ impl ReflectObject { fn own_keys( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. let Ok(target) = Object::try_from(arguments.get(0)) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -421,20 +422,20 @@ impl ReflectObject { .map(PropertyKey::into_value) .collect(); // 3. Return CreateArrayFromList(keys). - Ok(create_array_from_list(agent, &keys).into_value()) + Ok(create_array_from_list(agent, gc.nogc(), &keys).into_value()) } /// [28.1.11 Reflect.preventExtensions ( target )](https://tc39.es/ecma262/#sec-reflect.preventextensions) fn prevent_extensions( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -449,13 +450,13 @@ impl ReflectObject { fn set( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -483,13 +484,13 @@ impl ReflectObject { fn set_prototype_of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Value is not an object", )); @@ -504,6 +505,7 @@ impl ReflectObject { None } else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Prototype must be an object or null", )); diff --git a/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs index 905fb4252..d671a1e94 100644 --- a/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/regexp/abstract_operations.rs @@ -49,7 +49,6 @@ fn reg_exp_alloc_intrinsic(agent: &mut Agent) -> RegExp { pub(crate) fn reg_exp_alloc( agent: &mut Agent, gc: GcScope<'_, '_>, - new_target: Function, ) -> JsResult { // 1. Let obj be ? OrdinaryCreateFromConstructor(newTarget, "%RegExp.prototype%", « [[OriginalSource]], [[OriginalFlags]], [[RegExpRecord]], [[RegExpMatcher]] »). @@ -95,7 +94,7 @@ pub(crate) fn reg_exp_initialize_from_string( // 14. If parseResult is a non-empty List of SyntaxError objects, throw a SyntaxError exception. // 15. Assert: parseResult is a Pattern Parse Node. // 16. Set obj.[[OriginalSource]] to P. - agent[obj].original_source = p; + agent[obj].original_source = p.unbind(); // 17. Set obj.[[OriginalFlags]] to F. agent[obj].original_flags = f; // 18. Let capturingGroupsCount be CountLeftCapturingParensWithin(parseResult). diff --git a/nova_vm/src/ecmascript/builtins/regexp/data.rs b/nova_vm/src/ecmascript/builtins/regexp/data.rs index 0de69de9a..51be4e29e 100644 --- a/nova_vm/src/ecmascript/builtins/regexp/data.rs +++ b/nova_vm/src/ecmascript/builtins/regexp/data.rs @@ -13,7 +13,7 @@ use crate::{ pub struct RegExpHeapData { pub(crate) object_index: Option, // _regex: RegExp, - pub(crate) original_source: String, + pub(crate) original_source: String<'static>, pub(crate) original_flags: RegExpFlags, } 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 17f936d72..0eebdafaf 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 @@ -22,7 +22,7 @@ use crate::{ pub(crate) struct ArrayBufferConstructor; impl Builtin for ArrayBufferConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.ArrayBuffer; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.ArrayBuffer; const LENGTH: u8 = 1; @@ -34,7 +34,7 @@ impl BuiltinIntrinsicConstructor for ArrayBufferConstructor { struct ArrayBufferIsView; impl Builtin for ArrayBufferIsView { - const NAME: String = BUILTIN_STRING_MEMORY.isView; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isView; const LENGTH: u8 = 1; @@ -43,7 +43,7 @@ impl Builtin for ArrayBufferIsView { struct ArrayBufferGetSpecies; impl Builtin for ArrayBufferGetSpecies { - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); @@ -58,7 +58,6 @@ impl ArrayBufferConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -66,6 +65,7 @@ impl ArrayBufferConstructor { // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Constructor ArrayBuffer requires 'new'", )); @@ -74,13 +74,14 @@ impl ArrayBufferConstructor { let byte_length = to_index(agent, gc.reborrow(), arguments.get(0))? 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, gc, arguments.get(1))? + get_array_buffer_max_byte_length_option(agent, gc.reborrow(), arguments.get(1))? } else { None }; // 4. Return ? AllocateArrayBuffer(NewTarget, byteLength, requestedMaxByteLength). allocate_array_buffer( agent, + gc.nogc(), Function::try_from(new_target).unwrap(), byte_length, requested_max_byte_length, @@ -92,7 +93,6 @@ impl ArrayBufferConstructor { fn is_view( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -131,7 +131,6 @@ impl ArrayBufferConstructor { fn species( _agent: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -162,7 +161,6 @@ impl ArrayBufferConstructor { fn get_array_buffer_max_byte_length_option( agent: &mut Agent, mut gc: GcScope<'_, '_>, - options: Value, ) -> JsResult> { // 1. If options is not an Object, return empty. 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 db7739ad3..7e97843eb 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 @@ -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::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -26,7 +26,7 @@ pub(crate) struct ArrayBufferPrototype; struct ArrayBufferPrototypeGetByteLength; impl Builtin for ArrayBufferPrototypeGetByteLength { - const NAME: String = BUILTIN_STRING_MEMORY.get_byteLength; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_byteLength; const KEY: Option = Some(BUILTIN_STRING_MEMORY.byteLength.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::get_byte_length); @@ -34,7 +34,7 @@ impl Builtin for ArrayBufferPrototypeGetByteLength { impl BuiltinGetter for ArrayBufferPrototypeGetByteLength {} struct ArrayBufferPrototypeGetDetached; impl Builtin for ArrayBufferPrototypeGetDetached { - const NAME: String = BUILTIN_STRING_MEMORY.get_detached; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_detached; const KEY: Option = Some(BUILTIN_STRING_MEMORY.detached.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::get_detached); @@ -42,7 +42,7 @@ impl Builtin for ArrayBufferPrototypeGetDetached { impl BuiltinGetter for ArrayBufferPrototypeGetDetached {} struct ArrayBufferPrototypeGetMaxByteLength; impl Builtin for ArrayBufferPrototypeGetMaxByteLength { - const NAME: String = BUILTIN_STRING_MEMORY.get_maxByteLength; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_maxByteLength; const KEY: Option = Some(BUILTIN_STRING_MEMORY.maxByteLength.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::get_max_byte_length); @@ -50,7 +50,7 @@ impl Builtin for ArrayBufferPrototypeGetMaxByteLength { impl BuiltinGetter for ArrayBufferPrototypeGetMaxByteLength {} struct ArrayBufferPrototypeGetResizable; impl Builtin for ArrayBufferPrototypeGetResizable { - const NAME: String = BUILTIN_STRING_MEMORY.get_resizable; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_resizable; const KEY: Option = Some(BUILTIN_STRING_MEMORY.resizable.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::get_resizable); @@ -58,25 +58,25 @@ impl Builtin for ArrayBufferPrototypeGetResizable { impl BuiltinGetter for ArrayBufferPrototypeGetResizable {} struct ArrayBufferPrototypeResize; impl Builtin for ArrayBufferPrototypeResize { - const NAME: String = BUILTIN_STRING_MEMORY.resize; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.resize; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::resize); } struct ArrayBufferPrototypeSlice; impl Builtin for ArrayBufferPrototypeSlice { - const NAME: String = BUILTIN_STRING_MEMORY.slice; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.slice; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::slice); } struct ArrayBufferPrototypeTransfer; impl Builtin for ArrayBufferPrototypeTransfer { - const NAME: String = BUILTIN_STRING_MEMORY.transfer; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.transfer; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::transfer); } struct ArrayBufferPrototypeTransferToFixedLength; impl Builtin for ArrayBufferPrototypeTransferToFixedLength { - const NAME: String = BUILTIN_STRING_MEMORY.transferToFixedLength; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.transferToFixedLength; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(ArrayBufferPrototype::transfer_to_fixed_length); } @@ -88,15 +88,14 @@ impl ArrayBufferPrototype { /// accessor function is undefined. fn get_byte_length( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. - let o = require_internal_slot_array_buffer(agent, this_value)?; + let o = require_internal_slot_array_buffer(agent, gc.nogc(), this_value)?; // 4. If IsDetachedBuffer(O) is true, return +0𝔽. // 5. Let length be O.[[ArrayBufferByteLength]]. // 6. Return 𝔽(length). @@ -111,15 +110,14 @@ impl ArrayBufferPrototype { /// ArrayBuffer.prototype.detached is an accessor property whose set accessor function is undefined. fn get_detached( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. - let o = require_internal_slot_array_buffer(agent, this_value)?; + let o = require_internal_slot_array_buffer(agent, gc.nogc(), this_value)?; // 4. Return IsDetachedBuffer(O). Ok(is_detached_buffer(agent, o).into()) } @@ -129,15 +127,14 @@ impl ArrayBufferPrototype { /// ArrayBuffer.prototype.maxByteLength is an accessor property whose set accessor function is undefined. fn get_max_byte_length( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. - let o = require_internal_slot_array_buffer(agent, this_value)?; + let o = require_internal_slot_array_buffer(agent, gc.nogc(), this_value)?; // 4. If IsDetachedBuffer(O) is true, return +0𝔽. // 5. If IsFixedLengthArrayBuffer(O) is true, then // a. Let length be O.[[ArrayBufferByteLength]]. @@ -152,15 +149,14 @@ impl ArrayBufferPrototype { /// ArrayBuffer.prototype.resizable is an accessor property whose set accessor function is undefined. fn get_resizable( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.´ - let o = require_internal_slot_array_buffer(agent, this_value)?; + let o = require_internal_slot_array_buffer(agent, gc.nogc(), this_value)?; // 4. If IsFixedLengthArrayBuffer(O) is false, return true; otherwise return false. Ok((!is_fixed_length_array_buffer(agent, o)).into()) } @@ -170,26 +166,27 @@ impl ArrayBufferPrototype { /// This method performs the following steps when called: fn resize( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.´ - let o = require_internal_slot_array_buffer(agent, this_value)?; + let o = require_internal_slot_array_buffer(agent, gc.nogc(), this_value)?; if !o.is_resizable(agent) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Attempted to resize fixed length ArrayBuffer", )); } // 4. Let newByteLength be ? ToIndex(newLength). - let new_byte_length = to_index(agent, gc, arguments.get(0))? as usize; + let new_byte_length = to_index(agent, gc.reborrow(), arguments.get(0))? as usize; // 5. If IsDetachedBuffer(O) is true, throw a TypeError exception. if is_detached_buffer(agent, o) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Cannot resize a detached ArrayBuffer", )); @@ -197,6 +194,7 @@ impl ArrayBufferPrototype { // 6. If newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception. if new_byte_length > o.max_byte_length(agent) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "Attempted to resize beyond ArrayBuffer maxByteLength", )); @@ -226,17 +224,17 @@ impl ArrayBufferPrototype { fn slice( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.´ - let o = require_internal_slot_array_buffer(agent, this_value)?; + let o = require_internal_slot_array_buffer(agent, gc.nogc(), this_value)?; // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. if is_detached_buffer(agent, o) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Cannot slice a detached ArrayBuffer", )); @@ -283,7 +281,7 @@ impl ArrayBufferPrototype { // 16. Let new be ? Construct(ctor, « 𝔽(newLen) »). let Object::ArrayBuffer(new) = construct( agent, - gc, + gc.reborrow(), ctor.into_function(), Some(ArgumentsList(&[(new_len as i64).try_into().unwrap()])), None, @@ -296,6 +294,7 @@ impl ArrayBufferPrototype { // 19. If IsDetachedBuffer(new) is true, throw a TypeError exception. if is_detached_buffer(agent, new) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Construction produced a detached ArrayBuffer", )); @@ -303,6 +302,7 @@ impl ArrayBufferPrototype { // 20. If SameValue(new, O) is true, throw a TypeError exception. if new == o { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Construction returned the original ArrayBuffer", )); @@ -310,6 +310,7 @@ impl ArrayBufferPrototype { // 21. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception. if new.byte_length(agent) < new_len { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Construction returned a smaller ArrayBuffer than requested", )); @@ -318,6 +319,7 @@ impl ArrayBufferPrototype { // 23. If IsDetachedBuffer(O) is true, throw a TypeError exception. if is_detached_buffer(agent, o) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Construction detached ArrayBuffer being sliced", )); @@ -343,7 +345,6 @@ impl ArrayBufferPrototype { fn transfer( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -358,7 +359,6 @@ impl ArrayBufferPrototype { fn transfer_to_fixed_length( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -400,6 +400,7 @@ impl ArrayBufferPrototype { #[inline] pub(crate) fn require_internal_slot_array_buffer( agent: &mut Agent, + gc: NoGcScope, o: Value, ) -> JsResult { match o { @@ -407,6 +408,7 @@ pub(crate) fn require_internal_slot_array_buffer( // 2. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. Value::ArrayBuffer(array_buffer) => Ok(array_buffer), _ => Err(agent.throw_exception_with_static_message( + gc, 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 a8abe65a6..62dfa2c16 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/atomics_object.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/atomics_object.rs @@ -17,7 +17,7 @@ pub(crate) struct AtomicsObject; struct AtomicsObjectAdd; impl Builtin for AtomicsObjectAdd { - const NAME: String = BUILTIN_STRING_MEMORY.add; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.add; const LENGTH: u8 = 3; @@ -27,7 +27,7 @@ impl Builtin for AtomicsObjectAdd { struct AtomicsObjectAnd; impl Builtin for AtomicsObjectAnd { - const NAME: String = BUILTIN_STRING_MEMORY.and; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.and; const LENGTH: u8 = 3; @@ -36,7 +36,7 @@ impl Builtin for AtomicsObjectAnd { } struct AtomicsObjectCompareExchange; impl Builtin for AtomicsObjectCompareExchange { - const NAME: String = BUILTIN_STRING_MEMORY.compareExchange; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.compareExchange; const LENGTH: u8 = 4; @@ -45,7 +45,7 @@ impl Builtin for AtomicsObjectCompareExchange { } struct AtomicsObjectExchange; impl Builtin for AtomicsObjectExchange { - const NAME: String = BUILTIN_STRING_MEMORY.exchange; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.exchange; const LENGTH: u8 = 3; @@ -54,7 +54,7 @@ impl Builtin for AtomicsObjectExchange { } struct AtomicsObjectIsLockFree; impl Builtin for AtomicsObjectIsLockFree { - const NAME: String = BUILTIN_STRING_MEMORY.isLockFree; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isLockFree; const LENGTH: u8 = 1; @@ -63,7 +63,7 @@ impl Builtin for AtomicsObjectIsLockFree { } struct AtomicsObjectLoad; impl Builtin for AtomicsObjectLoad { - const NAME: String = BUILTIN_STRING_MEMORY.load; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.load; const LENGTH: u8 = 2; @@ -72,7 +72,7 @@ impl Builtin for AtomicsObjectLoad { } struct AtomicsObjectOr; impl Builtin for AtomicsObjectOr { - const NAME: String = BUILTIN_STRING_MEMORY.or; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.or; const LENGTH: u8 = 3; @@ -81,7 +81,7 @@ impl Builtin for AtomicsObjectOr { } struct AtomicsObjectStore; impl Builtin for AtomicsObjectStore { - const NAME: String = BUILTIN_STRING_MEMORY.store; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.store; const LENGTH: u8 = 3; @@ -90,7 +90,7 @@ impl Builtin for AtomicsObjectStore { } struct AtomicsObjectSub; impl Builtin for AtomicsObjectSub { - const NAME: String = BUILTIN_STRING_MEMORY.sub; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.sub; const LENGTH: u8 = 3; @@ -99,7 +99,7 @@ impl Builtin for AtomicsObjectSub { } struct AtomicsObjectWait; impl Builtin for AtomicsObjectWait { - const NAME: String = BUILTIN_STRING_MEMORY.wait; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.wait; const LENGTH: u8 = 4; @@ -108,7 +108,7 @@ impl Builtin for AtomicsObjectWait { } struct AtomicsObjectWaitAsync; impl Builtin for AtomicsObjectWaitAsync { - const NAME: String = BUILTIN_STRING_MEMORY.waitAsync; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.waitAsync; const LENGTH: u8 = 4; @@ -117,7 +117,7 @@ impl Builtin for AtomicsObjectWaitAsync { } struct AtomicsObjectNotify; impl Builtin for AtomicsObjectNotify { - const NAME: String = BUILTIN_STRING_MEMORY.notify; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.notify; const LENGTH: u8 = 3; @@ -126,7 +126,7 @@ impl Builtin for AtomicsObjectNotify { } struct AtomicsObjectXor; impl Builtin for AtomicsObjectXor { - const NAME: String = BUILTIN_STRING_MEMORY.xor; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.xor; const LENGTH: u8 = 3; @@ -138,7 +138,6 @@ impl AtomicsObject { fn add( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -148,7 +147,6 @@ impl AtomicsObject { fn and( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -158,7 +156,6 @@ impl AtomicsObject { fn compare_exchange( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -168,7 +165,6 @@ impl AtomicsObject { fn exchange( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -178,7 +174,6 @@ impl AtomicsObject { fn is_lock_free( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -188,7 +183,6 @@ impl AtomicsObject { fn load( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -198,7 +192,6 @@ impl AtomicsObject { fn or( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -208,7 +201,6 @@ impl AtomicsObject { fn store( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -218,7 +210,6 @@ impl AtomicsObject { fn sub( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -228,7 +219,6 @@ impl AtomicsObject { fn wait( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -238,7 +228,6 @@ impl AtomicsObject { fn wait_async( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -248,7 +237,6 @@ impl AtomicsObject { fn notify( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -258,7 +246,6 @@ impl AtomicsObject { fn xor( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { 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 5f8732c87..131e33db2 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 @@ -28,7 +28,7 @@ use crate::{ pub(crate) struct DataViewConstructor; impl Builtin for DataViewConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.DataView; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.DataView; const LENGTH: u8 = 1; @@ -51,6 +51,7 @@ impl DataViewConstructor { // 1. If NewTarget is undefined, throw a TypeError exception. let Some(new_target) = new_target else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "calling a builtin DataView constructor without new is forbidden", )); @@ -62,7 +63,7 @@ impl DataViewConstructor { let byte_length = arguments.get(2); // 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]). - let buffer = require_internal_slot_array_buffer(agent, buffer)?; + let buffer = require_internal_slot_array_buffer(agent, gc.nogc(), buffer)?; // 3. Let offset be ? ToIndex(byteOffset). let offset = to_index(agent, gc.reborrow(), byte_offset)? as usize; @@ -70,6 +71,7 @@ impl DataViewConstructor { // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if is_detached_buffer(agent, buffer) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "attempting to access detached ArrayBuffer", )); @@ -81,6 +83,7 @@ impl DataViewConstructor { // 6. If offset > bufferByteLength, throw a RangeError exception. if offset > buffer_byte_length { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "offset is outside the bounds of the buffer", )); @@ -107,6 +110,7 @@ impl DataViewConstructor { // b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception. if offset + view_byte_length > buffer_byte_length { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "offset is outside the bounds of the buffer", )); @@ -115,11 +119,17 @@ impl DataViewConstructor { }; // 10. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »). - let o = ordinary_create_from_constructor(agent, gc, new_target, ProtoIntrinsics::DataView)?; + let o = ordinary_create_from_constructor( + agent, + gc.reborrow(), + new_target, + ProtoIntrinsics::DataView, + )?; // 11. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if is_detached_buffer(agent, buffer) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "attempting to access detached ArrayBuffer", )); @@ -131,6 +141,7 @@ impl DataViewConstructor { // 13. If offset > bufferByteLength, throw a RangeError exception. if offset > buffer_byte_length { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "offset is outside the bounds of the buffer", )); @@ -141,6 +152,7 @@ impl DataViewConstructor { // a. If offset + viewByteLength > bufferByteLength, throw a RangeError exception. if offset + view_byte_length > buffer_byte_length { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "offset is outside the bounds of the buffer", )); 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 b3f7add19..5c7963682 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 @@ -4,7 +4,7 @@ use crate::ecmascript::abstract_operations::type_conversion::to_boolean; use crate::ecmascript::builtins::data_view::abstract_operations::{get_view_value, set_view_value}; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -30,7 +30,7 @@ pub(crate) struct DataViewPrototype; struct DataViewPrototypeGetBuffer; impl Builtin for DataViewPrototypeGetBuffer { - const NAME: String = BUILTIN_STRING_MEMORY.get_buffer; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_buffer; const KEY: Option = Some(BUILTIN_STRING_MEMORY.buffer.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_buffer); @@ -38,7 +38,7 @@ impl Builtin for DataViewPrototypeGetBuffer { impl BuiltinGetter for DataViewPrototypeGetBuffer {} struct DataViewPrototypeGetByteLength; impl Builtin for DataViewPrototypeGetByteLength { - const NAME: String = BUILTIN_STRING_MEMORY.get_byteLength; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_byteLength; const KEY: Option = Some(BUILTIN_STRING_MEMORY.byteLength.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_byte_length); @@ -46,7 +46,7 @@ impl Builtin for DataViewPrototypeGetByteLength { impl BuiltinGetter for DataViewPrototypeGetByteLength {} struct DataViewPrototypeGetByteOffset; impl Builtin for DataViewPrototypeGetByteOffset { - const NAME: String = BUILTIN_STRING_MEMORY.get_byteOffset; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_byteOffset; const KEY: Option = Some(BUILTIN_STRING_MEMORY.byteOffset.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_byte_offset); @@ -54,121 +54,121 @@ impl Builtin for DataViewPrototypeGetByteOffset { impl BuiltinGetter for DataViewPrototypeGetByteOffset {} struct DataViewPrototypeGetBigInt64; impl Builtin for DataViewPrototypeGetBigInt64 { - const NAME: String = BUILTIN_STRING_MEMORY.getBigInt64; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getBigInt64; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_big_int64); } struct DataViewPrototypeGetBigUint64; impl Builtin for DataViewPrototypeGetBigUint64 { - const NAME: String = BUILTIN_STRING_MEMORY.getBigUint64; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getBigUint64; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_big_uint64); } struct DataViewPrototypeGetFloat32; impl Builtin for DataViewPrototypeGetFloat32 { - const NAME: String = BUILTIN_STRING_MEMORY.getFloat32; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getFloat32; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_float32); } struct DataViewPrototypeGetFloat64; impl Builtin for DataViewPrototypeGetFloat64 { - const NAME: String = BUILTIN_STRING_MEMORY.getFloat64; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getFloat64; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_float64); } struct DataViewPrototypeGetInt8; impl Builtin for DataViewPrototypeGetInt8 { - const NAME: String = BUILTIN_STRING_MEMORY.getInt8; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getInt8; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_int8); } struct DataViewPrototypeGetInt16; impl Builtin for DataViewPrototypeGetInt16 { - const NAME: String = BUILTIN_STRING_MEMORY.getInt16; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getInt16; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_int16); } struct DataViewPrototypeGetInt32; impl Builtin for DataViewPrototypeGetInt32 { - const NAME: String = BUILTIN_STRING_MEMORY.getInt32; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getInt32; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_int32); } struct DataViewPrototypeGetUint8; impl Builtin for DataViewPrototypeGetUint8 { - const NAME: String = BUILTIN_STRING_MEMORY.getUint8; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUint8; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_uint8); } struct DataViewPrototypeGetUint16; impl Builtin for DataViewPrototypeGetUint16 { - const NAME: String = BUILTIN_STRING_MEMORY.getUint16; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUint16; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_uint16); } struct DataViewPrototypeGetUint32; impl Builtin for DataViewPrototypeGetUint32 { - const NAME: String = BUILTIN_STRING_MEMORY.getUint32; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.getUint32; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::get_uint32); } struct DataViewPrototypeSetBigInt64; impl Builtin for DataViewPrototypeSetBigInt64 { - const NAME: String = BUILTIN_STRING_MEMORY.setBigInt64; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setBigInt64; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_big_int64); } struct DataViewPrototypeSetBigUint64; impl Builtin for DataViewPrototypeSetBigUint64 { - const NAME: String = BUILTIN_STRING_MEMORY.setBigUint64; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setBigUint64; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_big_uint64); } struct DataViewPrototypeSetFloat32; impl Builtin for DataViewPrototypeSetFloat32 { - const NAME: String = BUILTIN_STRING_MEMORY.setFloat32; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setFloat32; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_float32); } struct DataViewPrototypeSetFloat64; impl Builtin for DataViewPrototypeSetFloat64 { - const NAME: String = BUILTIN_STRING_MEMORY.setFloat64; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setFloat64; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_float64); } struct DataViewPrototypeSetInt8; impl Builtin for DataViewPrototypeSetInt8 { - const NAME: String = BUILTIN_STRING_MEMORY.setInt8; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setInt8; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_int8); } struct DataViewPrototypeSetInt16; impl Builtin for DataViewPrototypeSetInt16 { - const NAME: String = BUILTIN_STRING_MEMORY.setInt16; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setInt16; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_int16); } struct DataViewPrototypeSetInt32; impl Builtin for DataViewPrototypeSetInt32 { - const NAME: String = BUILTIN_STRING_MEMORY.setInt32; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setInt32; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_int32); } struct DataViewPrototypeSetUint8; impl Builtin for DataViewPrototypeSetUint8 { - const NAME: String = BUILTIN_STRING_MEMORY.setUint8; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUint8; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_uint8); } struct DataViewPrototypeSetUint16; impl Builtin for DataViewPrototypeSetUint16 { - const NAME: String = BUILTIN_STRING_MEMORY.setUint16; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUint16; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_uint16); } struct DataViewPrototypeSetUint32; impl Builtin for DataViewPrototypeSetUint32 { - const NAME: String = BUILTIN_STRING_MEMORY.setUint32; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.setUint32; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(DataViewPrototype::set_uint32); } @@ -180,13 +180,13 @@ impl DataViewPrototype { /// function is undefined. fn get_buffer( agent: &mut Agent, - _gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). - let o = require_internal_slot_data_view(agent, this_value)?; + let o = require_internal_slot_data_view(agent, gc.nogc(), this_value)?; // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 4. Let buffer be O.[[ViewedArrayBuffer]]. // 5. Return buffer. @@ -199,19 +199,20 @@ impl DataViewPrototype { /// function is undefined. fn get_byte_length( agent: &mut Agent, - _gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). - let o = require_internal_slot_data_view(agent, this_value)?; + let o = require_internal_slot_data_view(agent, gc.nogc(), this_value)?; // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 4. Let viewRecord be MakeDataViewWithBufferWitnessRecord(O, seq-cst). let view_record = make_data_view_with_buffer_witness_record(agent, o, Ordering::SeqCst); // 5. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception. if is_view_out_of_bounds(agent, &view_record) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "DataView is out of bounds", )); @@ -228,19 +229,20 @@ impl DataViewPrototype { /// function is undefined. fn get_byte_offset( agent: &mut Agent, - _gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). - let o = require_internal_slot_data_view(agent, this_value)?; + let o = require_internal_slot_data_view(agent, gc.nogc(), this_value)?; // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 4. Let viewRecord be MakeDataViewWithBufferWitnessRecord(O, seq-cst). let view_record = make_data_view_with_buffer_witness_record(agent, o, Ordering::SeqCst); // 5. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception. if is_view_out_of_bounds(agent, &view_record) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "DataView is out of bounds", )); @@ -594,11 +596,16 @@ impl DataViewPrototype { } #[inline] -pub(crate) fn require_internal_slot_data_view(agent: &mut Agent, o: Value) -> JsResult { +pub(crate) fn require_internal_slot_data_view( + agent: &mut Agent, + gc: NoGcScope, + o: Value, +) -> JsResult { match o { // 1. Perform ? RequireInternalSlot(O, [[DataView]]). Value::DataView(array_buffer) => Ok(array_buffer), _ => Err(agent.throw_exception_with_static_message( + gc, 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 974014b72..c2102a98e 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs @@ -33,7 +33,7 @@ pub(crate) struct JSONObject; struct JSONObjectParse; impl Builtin for JSONObjectParse { - const NAME: String = BUILTIN_STRING_MEMORY.parse; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.parse; const LENGTH: u8 = 2; @@ -43,7 +43,7 @@ impl Builtin for JSONObjectParse { struct JSONObjectStringify; impl Builtin for JSONObjectStringify { - const NAME: String = BUILTIN_STRING_MEMORY.stringify; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.stringify; const LENGTH: u8 = 3; @@ -86,7 +86,6 @@ impl JSONObject { fn parse( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -100,7 +99,11 @@ impl JSONObject { let json_value = match sonic_rs::from_str::(json_string.as_str(agent)) { Ok(value) => value, Err(error) => { - return Err(agent.throw_exception(ExceptionType::SyntaxError, error.to_string())); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::SyntaxError, + error.to_string(), + )); } }; @@ -149,7 +152,6 @@ impl JSONObject { fn stringify( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -195,7 +197,6 @@ impl JSONObject { pub(crate) fn internalize_json_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - holder: Object, name: PropertyKey, reviver: Function, @@ -274,18 +275,19 @@ pub(crate) fn internalize_json_property( pub(crate) fn value_from_json( agent: &mut Agent, mut gc: GcScope<'_, '_>, - json: &sonic_rs::Value, ) -> JsResult { match json.get_type() { sonic_rs::JsonType::Null => Ok(Value::Null), sonic_rs::JsonType::Boolean => Ok(Value::Boolean(json.is_true())), sonic_rs::JsonType::Number => Ok(Number::from_f64(agent, json.as_f64().unwrap()).into()), - sonic_rs::JsonType::String => Ok(String::from_str(agent, json.as_str().unwrap()).into()), + sonic_rs::JsonType::String => { + Ok(String::from_str(agent, gc.nogc(), json.as_str().unwrap()).into()) + } sonic_rs::JsonType::Array => { let json_array = json.as_array().unwrap(); let len = json_array.len(); - let array_obj = array_create(agent, len, len, None)?; + let array_obj = array_create(agent, gc.nogc(), len, len, None)?; for (i, value) in json_array.iter().enumerate() { let prop = PropertyKey::from(SmallInteger::try_from(i as i64).unwrap()); let js_value = value_from_json(agent, gc.reborrow(), value)?; @@ -298,7 +300,7 @@ pub(crate) fn value_from_json( let object = ordinary_object_create_with_intrinsics(agent, Some(ProtoIntrinsics::Object), None); for (key, value) in json_object.iter() { - let prop = PropertyKey::from_str(agent, key); + let prop = PropertyKey::from_str(agent, gc.nogc(), key); let js_value = value_from_json(agent, gc.reborrow(), value)?; create_data_property(agent, gc.reborrow(), object, prop, js_value)?; } 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 32b578f4a..c9ff16cb8 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 @@ -15,7 +15,7 @@ use crate::{ pub(crate) struct SharedArrayBufferConstructor; impl Builtin for SharedArrayBufferConstructor { - const NAME: String = BUILTIN_STRING_MEMORY.SharedArrayBuffer; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.SharedArrayBuffer; const LENGTH: u8 = 1; @@ -27,7 +27,7 @@ impl BuiltinIntrinsicConstructor for SharedArrayBufferConstructor { struct SharedArrayBufferGetSpecies; impl Builtin for SharedArrayBufferGetSpecies { - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); @@ -41,7 +41,6 @@ impl SharedArrayBufferConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, @@ -52,7 +51,6 @@ impl SharedArrayBufferConstructor { fn species( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { 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 5816f0e20..f8ae0e84e 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 @@ -17,7 +17,7 @@ pub(crate) struct SharedArrayBufferPrototype; struct SharedArrayBufferPrototypeGetByteLength; impl Builtin for SharedArrayBufferPrototypeGetByteLength { - const NAME: String = BUILTIN_STRING_MEMORY.get_byteLength; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_byteLength; const KEY: Option = Some(BUILTIN_STRING_MEMORY.byteLength.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(SharedArrayBufferPrototype::get_byte_length); @@ -25,13 +25,13 @@ impl Builtin for SharedArrayBufferPrototypeGetByteLength { impl BuiltinGetter for SharedArrayBufferPrototypeGetByteLength {} struct SharedArrayBufferPrototypeGrow; impl Builtin for SharedArrayBufferPrototypeGrow { - const NAME: String = BUILTIN_STRING_MEMORY.grow; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.grow; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(SharedArrayBufferPrototype::grow); } struct SharedArrayBufferPrototypeGetGrowable; impl Builtin for SharedArrayBufferPrototypeGetGrowable { - const NAME: String = BUILTIN_STRING_MEMORY.get_growable; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_growable; const KEY: Option = Some(BUILTIN_STRING_MEMORY.growable.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(SharedArrayBufferPrototype::get_growable); @@ -39,7 +39,7 @@ impl Builtin for SharedArrayBufferPrototypeGetGrowable { impl BuiltinGetter for SharedArrayBufferPrototypeGetGrowable {} struct SharedArrayBufferPrototypeGetMaxByteLength; impl Builtin for SharedArrayBufferPrototypeGetMaxByteLength { - const NAME: String = BUILTIN_STRING_MEMORY.get_maxByteLength; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_maxByteLength; const KEY: Option = Some(BUILTIN_STRING_MEMORY.maxByteLength.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = @@ -48,7 +48,7 @@ impl Builtin for SharedArrayBufferPrototypeGetMaxByteLength { impl BuiltinGetter for SharedArrayBufferPrototypeGetMaxByteLength {} struct SharedArrayBufferPrototypeSlice; impl Builtin for SharedArrayBufferPrototypeSlice { - const NAME: String = BUILTIN_STRING_MEMORY.slice; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.slice; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(SharedArrayBufferPrototype::slice); } @@ -57,7 +57,6 @@ impl SharedArrayBufferPrototype { fn get_byte_length( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -67,7 +66,6 @@ impl SharedArrayBufferPrototype { fn grow( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -77,7 +75,6 @@ impl SharedArrayBufferPrototype { fn get_growable( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -87,7 +84,6 @@ impl SharedArrayBufferPrototype { fn get_max_byte_length( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -97,7 +93,6 @@ impl SharedArrayBufferPrototype { fn slice( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 f70adfe5b..545882a46 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 @@ -27,7 +27,7 @@ pub struct RegExpConstructor; impl Builtin for RegExpConstructor { const BEHAVIOUR: Behaviour = Behaviour::Constructor(Self::behaviour); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.RegExp; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.RegExp; } impl BuiltinIntrinsicConstructor for RegExpConstructor { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::RegExp; @@ -37,7 +37,7 @@ struct RegExpGetSpecies; impl Builtin for RegExpGetSpecies { const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpConstructor::get_species); const LENGTH: u8 = 0; - const NAME: String = BUILTIN_STRING_MEMORY.get__Symbol_species_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get__Symbol_species_; const KEY: Option = Some(WellKnownSymbolIndexes::Species.to_property_key()); } impl BuiltinGetter for RegExpGetSpecies {} @@ -46,7 +46,6 @@ impl RegExpConstructor { fn behaviour( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, _new_target: Option, @@ -57,7 +56,6 @@ impl RegExpConstructor { fn get_species( _: &mut Agent, _gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { 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 07f0cab9e..07e0fbcee 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 @@ -18,7 +18,7 @@ pub(crate) struct RegExpPrototype; struct RegExpPrototypeExec; impl Builtin for RegExpPrototypeExec { - const NAME: String = BUILTIN_STRING_MEMORY.exec; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.exec; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::exec); } @@ -27,7 +27,7 @@ impl BuiltinIntrinsic for RegExpPrototypeExec { } struct RegExpPrototypeGetDotAll; impl Builtin for RegExpPrototypeGetDotAll { - const NAME: String = BUILTIN_STRING_MEMORY.get_dotAll; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_dotAll; const KEY: Option = Some(BUILTIN_STRING_MEMORY.dotAll.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_dot_all); @@ -35,7 +35,7 @@ impl Builtin for RegExpPrototypeGetDotAll { impl BuiltinGetter for RegExpPrototypeGetDotAll {} struct RegExpPrototypeGetFlags; impl Builtin for RegExpPrototypeGetFlags { - const NAME: String = BUILTIN_STRING_MEMORY.get_flags; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_flags; const KEY: Option = Some(BUILTIN_STRING_MEMORY.flags.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_flags); @@ -43,7 +43,7 @@ impl Builtin for RegExpPrototypeGetFlags { impl BuiltinGetter for RegExpPrototypeGetFlags {} struct RegExpPrototypeGetGlobal; impl Builtin for RegExpPrototypeGetGlobal { - const NAME: String = BUILTIN_STRING_MEMORY.get_global; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_global; const KEY: Option = Some(BUILTIN_STRING_MEMORY.global.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_global); @@ -51,7 +51,7 @@ impl Builtin for RegExpPrototypeGetGlobal { impl BuiltinGetter for RegExpPrototypeGetGlobal {} struct RegExpPrototypeGetHasIndices; impl Builtin for RegExpPrototypeGetHasIndices { - const NAME: String = BUILTIN_STRING_MEMORY.get_hasIndices; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_hasIndices; const KEY: Option = Some(BUILTIN_STRING_MEMORY.hasIndices.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_has_indices); @@ -59,7 +59,7 @@ impl Builtin for RegExpPrototypeGetHasIndices { impl BuiltinGetter for RegExpPrototypeGetHasIndices {} struct RegExpPrototypeGetIgnoreCase; impl Builtin for RegExpPrototypeGetIgnoreCase { - const NAME: String = BUILTIN_STRING_MEMORY.get_ignoreCase; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_ignoreCase; const KEY: Option = Some(BUILTIN_STRING_MEMORY.ignoreCase.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_ignore_case); @@ -67,21 +67,21 @@ impl Builtin for RegExpPrototypeGetIgnoreCase { impl BuiltinGetter for RegExpPrototypeGetIgnoreCase {} struct RegExpPrototypeMatch; impl Builtin for RegExpPrototypeMatch { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_match_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_match_; const KEY: Option = Some(WellKnownSymbolIndexes::Match.to_property_key()); const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::r#match); } struct RegExpPrototypeMatchAll; impl Builtin for RegExpPrototypeMatchAll { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_matchAll_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_matchAll_; const KEY: Option = Some(WellKnownSymbolIndexes::MatchAll.to_property_key()); const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::match_all); } struct RegExpPrototypeGetMultiline; impl Builtin for RegExpPrototypeGetMultiline { - const NAME: String = BUILTIN_STRING_MEMORY.get_multiline; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_multiline; const KEY: Option = Some(BUILTIN_STRING_MEMORY.multiline.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_multiline); @@ -89,21 +89,21 @@ impl Builtin for RegExpPrototypeGetMultiline { impl BuiltinGetter for RegExpPrototypeGetMultiline {} struct RegExpPrototypeReplace; impl Builtin for RegExpPrototypeReplace { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_replace_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_replace_; const KEY: Option = Some(WellKnownSymbolIndexes::Replace.to_property_key()); const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::replace); } struct RegExpPrototypeSearch; impl Builtin for RegExpPrototypeSearch { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_search_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_search_; const KEY: Option = Some(WellKnownSymbolIndexes::Search.to_property_key()); const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::search); } struct RegExpPrototypeGetSource; impl Builtin for RegExpPrototypeGetSource { - const NAME: String = BUILTIN_STRING_MEMORY.get_source; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_source; const KEY: Option = Some(BUILTIN_STRING_MEMORY.source.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_source); @@ -111,14 +111,14 @@ impl Builtin for RegExpPrototypeGetSource { impl BuiltinGetter for RegExpPrototypeGetSource {} struct RegExpPrototypeSplit; impl Builtin for RegExpPrototypeSplit { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_split_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_split_; const KEY: Option = Some(WellKnownSymbolIndexes::Split.to_property_key()); const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::split); } struct RegExpPrototypeGetSticky; impl Builtin for RegExpPrototypeGetSticky { - const NAME: String = BUILTIN_STRING_MEMORY.get_sticky; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_sticky; const KEY: Option = Some(BUILTIN_STRING_MEMORY.sticky.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_sticky); @@ -126,19 +126,19 @@ impl Builtin for RegExpPrototypeGetSticky { impl BuiltinGetter for RegExpPrototypeGetSticky {} struct RegExpPrototypeTest; impl Builtin for RegExpPrototypeTest { - const NAME: String = BUILTIN_STRING_MEMORY.test; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.test; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::test); } struct RegExpPrototypeToString; impl Builtin for RegExpPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::to_string); } struct RegExpPrototypeGetUnicode; impl Builtin for RegExpPrototypeGetUnicode { - const NAME: String = BUILTIN_STRING_MEMORY.get_unicode; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_unicode; const KEY: Option = Some(BUILTIN_STRING_MEMORY.unicode.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_unicode); @@ -146,7 +146,7 @@ impl Builtin for RegExpPrototypeGetUnicode { impl BuiltinGetter for RegExpPrototypeGetUnicode {} struct RegExpPrototypeGetUnicodeSets; impl Builtin for RegExpPrototypeGetUnicodeSets { - const NAME: String = BUILTIN_STRING_MEMORY.get_unicodeSets; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_unicodeSets; const KEY: Option = Some(BUILTIN_STRING_MEMORY.unicodeSets.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(RegExpPrototype::get_unicode_sets); @@ -157,7 +157,6 @@ impl RegExpPrototype { fn exec( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -167,7 +166,6 @@ impl RegExpPrototype { fn get_dot_all( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -177,7 +175,6 @@ impl RegExpPrototype { fn get_flags( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -187,7 +184,6 @@ impl RegExpPrototype { fn get_global( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -197,7 +193,6 @@ impl RegExpPrototype { fn get_has_indices( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -207,7 +202,6 @@ impl RegExpPrototype { fn get_ignore_case( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -217,7 +211,6 @@ impl RegExpPrototype { fn r#match( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -227,7 +220,6 @@ impl RegExpPrototype { fn match_all( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -237,7 +229,6 @@ impl RegExpPrototype { fn get_multiline( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -247,7 +238,6 @@ impl RegExpPrototype { fn replace( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -257,7 +247,6 @@ impl RegExpPrototype { fn search( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -267,7 +256,6 @@ impl RegExpPrototype { fn get_source( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -277,7 +265,6 @@ impl RegExpPrototype { fn split( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -287,7 +274,6 @@ impl RegExpPrototype { fn get_sticky( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -297,7 +283,6 @@ impl RegExpPrototype { fn test( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -314,7 +299,6 @@ impl RegExpPrototype { fn to_string( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -325,7 +309,7 @@ impl RegExpPrototype { "{} is not an object", this_value.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; if let Object::RegExp(r) = r { // Fast path for RegExp objects: This is not actually proper as it @@ -341,17 +325,25 @@ impl RegExpPrototype { data.original_flags.iter_names().for_each(|(flag, _)| { regexp_string.push_str(flag); }); - return Ok(String::from_string(agent, regexp_string).into_value()); + return Ok(String::from_string(agent, gc.nogc(), regexp_string).into_value()); } // 3. Let pattern be ? ToString(? Get(R, "source")). let pattern = get(agent, gc.reborrow(), r, BUILTIN_STRING_MEMORY.source.into())?; - let pattern = to_string(agent, gc.reborrow(), pattern)?; + let pattern = to_string(agent, gc.reborrow(), pattern)? + .unbind() + .scope(agent, gc.nogc()); // 4. Let flags be ? ToString(? Get(R, "flags")). let flags = get(agent, gc.reborrow(), r, BUILTIN_STRING_MEMORY.flags.into())?; - let flags = to_string(agent, gc.reborrow(), flags)?; + let flags = to_string(agent, gc.reborrow(), flags)? + .unbind() + .bind(gc.nogc()); // 5. Let result be the string-concatenation of "/", pattern, "/", and flags. - let result = format!("/{}/{}", pattern.as_str(agent), flags.as_str(agent)); - let result = String::from_string(agent, result); + let result = format!( + "/{}/{}", + pattern.get(agent).bind(gc.nogc()).as_str(agent), + flags.as_str(agent) + ); + let result = String::from_string(agent, gc.nogc(), result); // 6. Return result. Ok(result.into_value()) } @@ -359,7 +351,6 @@ impl RegExpPrototype { fn get_unicode( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -369,7 +360,6 @@ impl RegExpPrototype { fn get_unicode_sets( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { 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 dcd6824c3..a894c82c1 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 @@ -17,7 +17,7 @@ pub(crate) struct RegExpStringIteratorPrototype; struct RegExpStringIteratorPrototypeNext; impl Builtin for RegExpStringIteratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 0; @@ -29,7 +29,6 @@ impl RegExpStringIteratorPrototype { fn next( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { 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 74586ca98..195f6dcdc 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 @@ -32,7 +32,7 @@ pub struct StringConstructor; impl Builtin for StringConstructor { const BEHAVIOUR: Behaviour = Behaviour::Constructor(Self::behaviour); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.String; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.String; } impl BuiltinIntrinsicConstructor for StringConstructor { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::String; @@ -42,25 +42,24 @@ struct StringFromCharCode; impl Builtin for StringFromCharCode { const BEHAVIOUR: Behaviour = Behaviour::Regular(StringConstructor::from_char_code); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.fromCharCode; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.fromCharCode; } struct StringFromCodePoint; impl Builtin for StringFromCodePoint { const BEHAVIOUR: Behaviour = Behaviour::Regular(StringConstructor::from_code_point); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.fromCodePoint; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.fromCodePoint; } struct StringRaw; impl Builtin for StringRaw { const BEHAVIOUR: Behaviour = Behaviour::Regular(StringConstructor::raw); const LENGTH: u8 = 1; - const NAME: String = BUILTIN_STRING_MEMORY.raw; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.raw; } impl StringConstructor { fn behaviour( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, arguments: ArgumentsList, new_target: Option, @@ -75,18 +74,20 @@ impl StringConstructor { // 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).into_value()); + return Ok(value.descriptive_string(agent, gc.nogc()).into_value()); } } // b. Let s be ? ToString(value). to_string(agent, gc.reborrow(), value)? + .unbind() + .bind(gc.nogc()) }; // 3. If NewTarget is undefined, return s. let Some(new_target) = new_target else { return Ok(s.into_value()); }; // 4. Return StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, "%String.prototype%")). - let value = s; + let value = s.scope(agent, gc.nogc()); let prototype = get_prototype_from_constructor( agent, gc.reborrow(), @@ -104,8 +105,9 @@ impl StringConstructor { // 2. Set S.[[Prototype]] to prototype. // 3. Set S.[[StringData]] to value. + let value = value.get(agent).bind(gc.nogc()); agent[s].data = match value { - String::String(data) => PrimitiveObjectData::String(data), + String::String(data) => PrimitiveObjectData::String(data.unbind()), String::SmallString(data) => PrimitiveObjectData::SmallString(data), }; // 4. Set S.[[GetOwnProperty]] as specified in 10.4.3.1. @@ -124,7 +126,6 @@ impl StringConstructor { fn from_char_code( agent: &mut Agent, mut gc: GcScope<'_, '_>, - _this_value: Value, code_units: ArgumentsList, ) -> JsResult { @@ -154,7 +155,7 @@ impl StringConstructor { } let result = std::string::String::from_utf16_lossy(&buf); - Ok(String::from_string(agent, result).into()) + Ok(String::from_string(agent, gc.nogc(), result).into()) } /// ### [22.1.2.2 String.fromCodePoint ( ...`codePoints` ) ](https://262.ecma-international.org/15.0/index.html#sec-string.fromcodepoint) @@ -164,7 +165,6 @@ impl StringConstructor { fn from_code_point( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { @@ -183,7 +183,6 @@ impl StringConstructor { fn raw( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { 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 19e6d38e1..40f6adcef 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 @@ -17,7 +17,7 @@ pub(crate) struct StringIteratorPrototype; struct StringIteratorPrototypeNext; impl Builtin for StringIteratorPrototypeNext { - const NAME: String = BUILTIN_STRING_MEMORY.next; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; const LENGTH: u8 = 0; @@ -29,7 +29,6 @@ impl StringIteratorPrototype { fn next( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments: ArgumentsList, ) -> JsResult { 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 d0dd67449..e316e69f4 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 @@ -9,7 +9,12 @@ use unicode_normalization::{ is_nfc_quick, is_nfd_quick, is_nfkc_quick, is_nfkd_quick, IsNormalized, UnicodeNormalization, }; -use crate::engine::context::GcScope; +use crate::ecmascript::abstract_operations::type_conversion::{ + to_string_primitive, try_to_integer_or_infinity, try_to_string, +}; +use crate::ecmascript::types::Primitive; +use crate::engine::context::{GcScope, NoGcScope}; +use crate::SmallInteger; use crate::{ ecmascript::{ abstract_operations::{ @@ -35,193 +40,193 @@ pub(crate) struct StringPrototype; struct StringPrototypeGetAt; impl Builtin for StringPrototypeGetAt { - const NAME: String = BUILTIN_STRING_MEMORY.at; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.at; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::at); } struct StringPrototypeCharAt; impl Builtin for StringPrototypeCharAt { - const NAME: String = BUILTIN_STRING_MEMORY.charAt; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.charAt; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::char_at); } struct StringPrototypeCharCodeAt; impl Builtin for StringPrototypeCharCodeAt { - const NAME: String = BUILTIN_STRING_MEMORY.charCodeAt; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.charCodeAt; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::char_code_at); } struct StringPrototypeCodePointAt; impl Builtin for StringPrototypeCodePointAt { - const NAME: String = BUILTIN_STRING_MEMORY.codePointAt; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.codePointAt; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::code_point_at); } struct StringPrototypeConcat; impl Builtin for StringPrototypeConcat { - const NAME: String = BUILTIN_STRING_MEMORY.concat; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.concat; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::concat); } struct StringPrototypeEndsWith; impl Builtin for StringPrototypeEndsWith { - const NAME: String = BUILTIN_STRING_MEMORY.endsWith; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.endsWith; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::ends_with); } struct StringPrototypeIncludes; impl Builtin for StringPrototypeIncludes { - const NAME: String = BUILTIN_STRING_MEMORY.includes; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.includes; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::includes); } struct StringPrototypeIndexOf; impl Builtin for StringPrototypeIndexOf { - const NAME: String = BUILTIN_STRING_MEMORY.indexOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.indexOf; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::index_of); } struct StringPrototypeIsWellFormed; impl Builtin for StringPrototypeIsWellFormed { - const NAME: String = BUILTIN_STRING_MEMORY.isWellFormed; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.isWellFormed; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::is_well_formed); } struct StringPrototypeLastIndexOf; impl Builtin for StringPrototypeLastIndexOf { - const NAME: String = BUILTIN_STRING_MEMORY.lastIndexOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.lastIndexOf; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::last_index_of); } struct StringPrototypeLocaleCompare; impl Builtin for StringPrototypeLocaleCompare { - const NAME: String = BUILTIN_STRING_MEMORY.localeCompare; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.localeCompare; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::locale_compare); } struct StringPrototypeMatch; impl Builtin for StringPrototypeMatch { - const NAME: String = BUILTIN_STRING_MEMORY.r#match; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#match; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::r#match); } struct StringPrototypeMatchAll; impl Builtin for StringPrototypeMatchAll { - const NAME: String = BUILTIN_STRING_MEMORY.matchAll; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.matchAll; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::match_all); } struct StringPrototypeNormalize; impl Builtin for StringPrototypeNormalize { - const NAME: String = BUILTIN_STRING_MEMORY.normalize; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.normalize; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::normalize); } struct StringPrototypePadEnd; impl Builtin for StringPrototypePadEnd { - const NAME: String = BUILTIN_STRING_MEMORY.padEnd; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.padEnd; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::pad_end); } struct StringPrototypePadStart; impl Builtin for StringPrototypePadStart { - const NAME: String = BUILTIN_STRING_MEMORY.padStart; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.padStart; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::pad_start); } struct StringPrototypeRepeat; impl Builtin for StringPrototypeRepeat { - const NAME: String = BUILTIN_STRING_MEMORY.repeat; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.repeat; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::repeat); } struct StringPrototypeReplace; impl Builtin for StringPrototypeReplace { - const NAME: String = BUILTIN_STRING_MEMORY.replace; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.replace; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::replace); } struct StringPrototypeReplaceAll; impl Builtin for StringPrototypeReplaceAll { - const NAME: String = BUILTIN_STRING_MEMORY.replaceAll; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.replaceAll; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::replace_all); } struct StringPrototypeSearch; impl Builtin for StringPrototypeSearch { - const NAME: String = BUILTIN_STRING_MEMORY.search; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.search; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::search); } struct StringPrototypeSlice; impl Builtin for StringPrototypeSlice { - const NAME: String = BUILTIN_STRING_MEMORY.slice; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.slice; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::slice); } struct StringPrototypeSplit; impl Builtin for StringPrototypeSplit { - const NAME: String = BUILTIN_STRING_MEMORY.split; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.split; const LENGTH: u8 = 2; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::split); } struct StringPrototypeStartsWith; impl Builtin for StringPrototypeStartsWith { - const NAME: String = BUILTIN_STRING_MEMORY.startsWith; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.startsWith; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::starts_with); } struct StringPrototypeSubstring; impl Builtin for StringPrototypeSubstring { - const NAME: String = BUILTIN_STRING_MEMORY.substring; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.substring; const LENGTH: u8 = 1; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::substring); } struct StringPrototypeToLocaleLowerCase; impl Builtin for StringPrototypeToLocaleLowerCase { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleLowerCase; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleLowerCase; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::to_locale_lower_case); } struct StringPrototypeToLocaleUpperCase; impl Builtin for StringPrototypeToLocaleUpperCase { - const NAME: String = BUILTIN_STRING_MEMORY.toLocaleUpperCase; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLocaleUpperCase; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::to_locale_upper_case); } struct StringPrototypeToLowerCase; impl Builtin for StringPrototypeToLowerCase { - const NAME: String = BUILTIN_STRING_MEMORY.toLowerCase; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toLowerCase; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::to_lower_case); } struct StringPrototypeToString; impl Builtin for StringPrototypeToString { - const NAME: String = BUILTIN_STRING_MEMORY.toString; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toString; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::value_of); } struct StringPrototypeToUpperCase; impl Builtin for StringPrototypeToUpperCase { - const NAME: String = BUILTIN_STRING_MEMORY.toUpperCase; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toUpperCase; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::to_upper_case); } struct StringPrototypeToWellFormed; impl Builtin for StringPrototypeToWellFormed { - const NAME: String = BUILTIN_STRING_MEMORY.toWellFormed; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.toWellFormed; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::to_well_formed); } struct StringPrototypeTrim; impl Builtin for StringPrototypeTrim { - const NAME: String = BUILTIN_STRING_MEMORY.trim; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.trim; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::trim); } struct StringPrototypeTrimEnd; impl Builtin for StringPrototypeTrimEnd { - const NAME: String = BUILTIN_STRING_MEMORY.trimEnd; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.trimEnd; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::trim_end); } @@ -230,7 +235,7 @@ impl BuiltinIntrinsic for StringPrototypeTrimEnd { } struct StringPrototypeTrimStart; impl Builtin for StringPrototypeTrimStart { - const NAME: String = BUILTIN_STRING_MEMORY.trimStart; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.trimStart; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::trim_start); } @@ -239,13 +244,13 @@ impl BuiltinIntrinsic for StringPrototypeTrimStart { } struct StringPrototypeValueOf; impl Builtin for StringPrototypeValueOf { - const NAME: String = BUILTIN_STRING_MEMORY.valueOf; + const NAME: String<'static> = BUILTIN_STRING_MEMORY.valueOf; const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::value_of); } struct StringPrototypeIterator; impl Builtin for StringPrototypeIterator { - const NAME: String = BUILTIN_STRING_MEMORY._Symbol_iterator_; + const NAME: String<'static> = BUILTIN_STRING_MEMORY._Symbol_iterator_; const KEY: Option = Some(WellKnownSymbolIndexes::Iterator.to_property_key()); const LENGTH: u8 = 0; const BEHAVIOUR: Behaviour = Behaviour::Regular(StringPrototype::iterator); @@ -255,19 +260,27 @@ impl StringPrototype { fn at( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + 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, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + // 4. Let relativeIndex be ? ToIntegerOrInfinity(pos). + let relative_index = to_integer_or_infinity(agent, gc.reborrow(), pos)?.into_i64(agent); + (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(); - // 4. Let relativeIndex be ? ToIntegerOrInfinity(pos). - let relative_index = - to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_i64(agent); // 5. If relativeIndex ≥ 0, then let k = if relative_index >= 0 { // a. Let k be relativeIndex. @@ -290,16 +303,25 @@ impl StringPrototype { fn char_at( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; - // 3. Let position be ? ToIntegerOrInfinity(pos). - let position = to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_i64(agent); + let pos = args.get(0); + let (s, position) = + if let (Ok(s), Value::Integer(position)) = (String::try_from(this_value), pos) { + (s.bind(gc.nogc()), position.into_i64()) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + // 3. Let position be ? ToIntegerOrInfinity(pos). + let position = + to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_i64(agent); + (s.get(agent).bind(gc.nogc()), position) + }; // 4. Let size be the length of S. let size = s.utf16_len(agent); // 5. If position < 0 or position ≥ size, return the empty String. @@ -315,16 +337,25 @@ impl StringPrototype { fn char_code_at( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; - // 3. Let position be ? ToIntegerOrInfinity(pos). - let position = to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_i64(agent); + let pos = args.get(0); + let (s, position) = + if let (Ok(s), Value::Integer(position)) = (String::try_from(this_value), pos) { + (s.bind(gc.nogc()), position.into_i64()) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + // 3. Let position be ? ToIntegerOrInfinity(pos). + let position = + to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_i64(agent); + (s.get(agent).bind(gc.nogc()), position) + }; // 4. Let size be the length of S. let size = s.utf16_len(agent); // 5. If position < 0 or position ≥ size, return NaN. @@ -341,16 +372,25 @@ impl StringPrototype { fn code_point_at( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; - // 3. Let position be ? ToIntegerOrInfinity(pos). - let position = to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_i64(agent); + let pos = args.get(0); + let (s, position) = + if let (Ok(s), Value::Integer(position)) = (String::try_from(this_value), pos) { + (s.bind(gc.nogc()), position.into_i64()) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + // 3. Let position be ? ToIntegerOrInfinity(pos). + let position = + to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_i64(agent); + (s.get(agent).bind(gc.nogc()), position) + }; // 4. Let size be the length of S. let size = s.utf16_len(agent); // 5. If position < 0 or position ≥ size, return undefined. @@ -371,54 +411,121 @@ impl StringPrototype { fn concat( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let nogc = gc.nogc(); + let (s, args) = if let Ok(s) = String::try_from(this_value) { + (s, &args[..]) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, nogc, this_value)?; + if let Some(s) = try_to_string(agent, nogc, o) { + (s?, &args[..]) + } else { + // 2. Let S be ? ToString(O). + // TODO: args should be rooted here. + ( + to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()), + &args[..], + ) + } + }; // 3. Let R be S. - let mut strings = Vec::with_capacity(args.len() + 1); - strings.push(s); + let strings = if args.iter().all(|arg| arg.is_primitive()) { + let mut strings = Vec::with_capacity(args.len() + 1); + strings.push(s); + let nogc = gc.nogc(); + for next in args.iter() { + strings.push(to_string_primitive( + agent, + nogc, + Primitive::try_from(*next).unwrap(), + )?); + } + strings + } else { + let mut string_roots = Vec::with_capacity(args.len() + 1); + string_roots.push(s.scope(agent, gc.nogc())); + for next in args.iter() { + string_roots.push( + to_string(agent, gc.reborrow(), *next)? + .unbind() + .scope(agent, gc.nogc()), + ); + } + let nogc = gc.nogc(); + string_roots + .into_iter() + .map(|string_root| string_root.get(agent).bind(nogc)) + .collect::>() + }; // 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. - for next in args.iter() { - strings.push(to_string(agent, gc.reborrow(), *next)?); - } // 5. Return R. - Ok(String::concat(agent, &strings).into_value()) + Ok(String::concat(agent, gc.nogc(), &strings).into_value()) } fn ends_with( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let search_string = args.get(0); + let end_position = args.get(1); - // 3. Let isRegExp be ? IsRegExp(searchString). - // 4. If isRegExp is true, throw a TypeError exception. - // TODO + let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( + String::try_from(this_value), + String::try_from(search_string), + end_position, + ) { + (s, search_str, usize::MAX) + } else if let (Ok(s), Ok(search_str), Value::Integer(position)) = ( + String::try_from(this_value), + String::try_from(search_string), + end_position, + ) { + (s, search_str, position.into_i64().max(0) as usize) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + + // 3. Let isRegExp be ? IsRegExp(searchString). + // 4. If isRegExp is true, throw a TypeError exception. + // TODO + + // 5. Let searchStr be ? ToString(searchString). + let search_str = to_string(agent, gc.reborrow(), search_string)? + .unbind() + .scope(agent, gc.nogc()); + + // 6. Let len be the length of S. + // 7. If endPosition is undefined, let pos be len; + let pos = if end_position.is_undefined() { + usize::MAX + } else { + // else let pos be ? ToIntegerOrInfinity(endPosition). + to_integer_or_infinity(agent, gc.reborrow(), end_position)?.into_usize(agent) + }; - // 5. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, gc.reborrow(), args.get(0))?; + ( + s.get(agent).bind(gc.nogc()), + search_str.get(agent).bind(gc.nogc()), + pos, + ) + }; // 6. Let len be the length of S. - // 7. If endPosition is undefined, let pos be len; else let pos be ? ToIntegerOrInfinity(endPosition). // 8. Let end be the result of clamping pos between 0 and len. - let end_position = args.get(1); - let haystack_str = if end_position.is_undefined() { + let haystack_str = if pos == usize::MAX { s.as_str(agent) } else { - let pos = to_integer_or_infinity(agent, gc.reborrow(), end_position)?.into_usize(agent); let end = if pos != 0 { // NOTE: `pos` was already clamped to 0 by `Number::into_usize`. pos.min(s.utf16_len(agent)) @@ -444,28 +551,54 @@ impl StringPrototype { fn includes( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let search_string = args.get(0); + let position = args.get(1); - // 3. Let isRegExp be ? IsRegExp(searchString). - // 4. If isRegExp is true, throw a TypeError exception. - // TODO + let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( + String::try_from(this_value), + String::try_from(search_string), + position, + ) { + (s, search_str, 0usize) + } else if let (Ok(s), Ok(search_str), Value::Integer(position)) = ( + String::try_from(this_value), + String::try_from(search_string), + position, + ) { + (s, search_str, position.into_i64().max(0) as usize) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + + // 3. Let isRegExp be ? IsRegExp(searchString). + // 4. If isRegExp is true, throw a TypeError exception. + // TODO + + // 5. Let searchStr be ? ToString(searchString). + let search_str = to_string(agent, gc.reborrow(), search_string)? + .unbind() + .scope(agent, gc.nogc()); + // 6. Let pos be ? ToIntegerOrInfinity(position). + let pos = to_integer_or_infinity(agent, gc.reborrow(), position)?.into_usize(agent); + // 7. Assert: If position is undefined, then pos is 0. + assert!(!position.is_undefined() || pos == 0); + + (s.get(agent), search_str.get(agent), pos) + }; - // 5. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, gc.reborrow(), args.get(0))?; + let s = s.bind(gc.nogc()); + let search_str = search_str.bind(gc.nogc()); - // 6. Let pos be ? ToIntegerOrInfinity(position). - // 7. Assert: If position is undefined, then pos is 0. // 8. Let len be the length of S. // 9. Let start be the result of clamping pos between 0 and len. let haystack_str = { - let pos = to_integer_or_infinity(agent, gc.reborrow(), args.get(0))?.into_usize(agent); let start = if pos != 0 { // NOTE: `pos` was already clamped to 0 by `Number::into_usize`. pos.min(s.utf16_len(agent)) @@ -484,19 +617,41 @@ impl StringPrototype { fn index_of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; - // 3. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, gc.reborrow(), args.get(0))?; - // 4. Let pos be ? ToIntegerOrInfinity(position). - // 5. Assert: If position is undefined, then pos is 0. - let pos = to_integer_or_infinity(agent, gc.reborrow(), args.get(1))?.into_usize(agent); + let search_string = args.get(0); + let position = args.get(1); + + let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( + String::try_from(this_value), + String::try_from(search_string), + position, + ) { + (s, search_str, 0usize) + } else if let (Ok(s), Ok(search_str), Value::Integer(position)) = ( + String::try_from(this_value), + String::try_from(search_string), + position, + ) { + (s, search_str, position.into_i64().max(0) as usize) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + // 3. Let searchStr be ? ToString(searchString). + let search_str = to_string(agent, gc.reborrow(), search_string)? + .unbind() + .scope(agent, gc.nogc()); + // 4. Let pos be ? ToIntegerOrInfinity(position). + // 5. Assert: If position is undefined, then pos is 0. + let pos = to_integer_or_infinity(agent, gc.reborrow(), position)?.into_usize(agent); + + (s.get(agent), search_str.get(agent), pos) + }; // 6. Let len be the length of S. // 7. Let start be the result of clamping pos between 0 and len. @@ -526,12 +681,11 @@ impl StringPrototype { fn is_well_formed( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). let s = to_string(agent, gc.reborrow(), o)?; @@ -555,39 +709,62 @@ impl StringPrototype { fn last_index_of( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { let search_string = args.get(0); let position = args.get(1); - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; - // 3. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, gc.reborrow(), search_string)?; - let pos = if position.is_undefined() { - // 5. Assert: If position is undefined, then numPos is NaN. - // 6. If numPos is NaN, let pos be +∞; - usize::MAX - } else if let Value::Integer(position) = position { - position.into_i64().max(0) as usize + let (s, search_str, pos) = if let (Ok(s), Ok(search_str), Value::Undefined) = ( + String::try_from(this_value), + String::try_from(search_string), + position, + ) { + (s, search_str, usize::MAX) + } else if let (Ok(s), Ok(search_str), Value::Integer(position)) = ( + String::try_from(this_value), + String::try_from(search_string), + position, + ) { + (s, search_str, position.into_i64().max(0) as usize) } else { - // 4. Let numPos be ? ToNumber(position). - let num_pos = to_number(agent, gc.reborrow(), args.get(1))?; - if num_pos.is_nan(agent) { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, gc.nogc(), this_value)?; + // 2. Let S be ? ToString(O). + let s = to_string(agent, gc.reborrow(), o)? + .unbind() + .scope(agent, gc.nogc()); + // 3. Let searchStr be ? ToString(searchString). + let search_str = to_string(agent, gc.reborrow(), search_string)? + .unbind() + .scope(agent, 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 +∞; usize::MAX + } else if let Value::Integer(position) = position { + position.into_i64().max(0) as usize } else { - // otherwise, let pos be! ToIntegerOrInfinity(numPos). - to_integer_or_infinity(agent, gc.reborrow(), num_pos.into_value()) - .unwrap() - .into_usize(agent) - } + // 4. Let numPos be ? ToNumber(position). + let num_pos = to_number(agent, gc.reborrow(), args.get(1))?; + if num_pos.is_nan(agent) { + // 6. If numPos is NaN, let pos be +∞; + usize::MAX + } else { + // otherwise, let pos be! ToIntegerOrInfinity(numPos). + to_integer_or_infinity(agent, gc.reborrow(), num_pos.into_value()) + .unwrap() + .into_usize(agent) + } + }; + + (s.get(agent), search_str.get(agent), 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. @@ -623,7 +800,6 @@ impl StringPrototype { fn locale_compare( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -633,7 +809,6 @@ impl StringPrototype { fn r#match( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -643,7 +818,6 @@ impl StringPrototype { fn match_all( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -654,29 +828,38 @@ impl StringPrototype { fn normalize( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { + let form = arguments.get(0); + // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = if let Some(s) = try_to_string(agent, gc.nogc(), o) { + s? + } else { + to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()) + }; // 3. If form is undefined, let f be "NFC". - let form = arguments.get(0); let f = if form.is_undefined() { NormalizeForm::Nfc } else { // 4. Else, let f be ? ToString(form). - let f = to_string(agent, gc.reborrow(), form)?; + let s_root = s.scope(agent, gc.nogc()); + let f = to_string(agent, gc.reborrow(), form)? + .unbind() + .bind(gc.nogc()); + s = s_root.get(agent).bind(gc.nogc()); let form_result = NormalizeForm::from_str(f.as_str(agent)); match form_result { Ok(form) => form, // 5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception. Err(()) => { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "The normalization form should be one of NFC, NFD, NFKC, NFKD.", )) @@ -688,7 +871,7 @@ impl StringPrototype { match unicode_normalize(s.as_str(agent), f) { // 7. Return ns. None => Ok(s.into_value()), - Some(ns) => Ok(Value::from_string(agent, ns).into_value()), + Some(ns) => Ok(Value::from_string(agent, gc.nogc(), ns).into_value()), } } @@ -696,7 +879,6 @@ impl StringPrototype { fn pad_end( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -704,7 +886,7 @@ impl StringPrototype { let fill_string = arguments.get(1); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Return ? StringPaddingBuiltinsImpl(O, maxLength, fillString, end). string_padding_builtins_impl(agent, gc.reborrow(), o, max_length, fill_string, false) @@ -714,7 +896,6 @@ impl StringPrototype { fn pad_start( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { @@ -722,7 +903,7 @@ impl StringPrototype { let fill_string = arguments.get(1); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Return ? StringPaddingBuiltinsImpl(O, maxLength, fillString, start). string_padding_builtins_impl(agent, gc.reborrow(), o, max_length, fill_string, true) @@ -732,24 +913,31 @@ impl StringPrototype { fn repeat( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, arguments: ArgumentsList, ) -> JsResult { let count = arguments.get(0); // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()); // 3. Let n be ? ToIntegerOrInfinity(count). - let n = to_integer_or_infinity(agent, gc.reborrow(), count)?; + let n = if let Some(n) = try_to_integer_or_infinity(agent, gc.nogc(), count) { + n? + } else { + let s_root = s.scope(agent, gc.nogc()); + let result = to_integer_or_infinity(agent, gc.reborrow(), count)?; + s = s_root.get(agent).bind(gc.nogc()); + result + }; // 4. If n < 0 or n = +∞, throw a RangeError exception. if n.is_pos_infinity(agent) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "count must be less than infinity", )); @@ -759,6 +947,7 @@ impl StringPrototype { if n < 0 { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::RangeError, "count must not be negative", )); @@ -776,6 +965,7 @@ impl StringPrototype { // 6. Return the String value that is made from n copies of S appended together. Ok(Value::from_string( agent, + gc.nogc(), s.as_str(agent).repeat(n as usize), )) } @@ -784,12 +974,11 @@ impl StringPrototype { fn replace( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; let search_value = args.get(0); let replace_value = args.get(1); @@ -813,10 +1002,15 @@ impl StringPrototype { } // 3. Let s be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()); + let s_root = s.scope(agent, gc.nogc()); // 4. Let searchString be ? ToString(searchValue). - let search_string = to_string(agent, gc.reborrow(), search_value)?; + let search_string = to_string(agent, gc.reborrow(), search_value)? + .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) { @@ -833,19 +1027,24 @@ impl StringPrototype { }; // 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(), + ]; let result = call_function( agent, gc.reborrow(), functional_replace, Value::Undefined, - Some(ArgumentsList(&[ - search_string.into_value(), - Number::from(position as u32).into_value(), - s.into_value(), - ])), + Some(ArgumentsList(args)), )?; - let result = to_string(agent, gc.reborrow(), result)?; + let result = to_string(agent, gc.reborrow(), result)? + .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. @@ -855,28 +1054,35 @@ 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).into_value()); + return Ok(String::from_string(agent, gc.nogc(), concatenated_result).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, gc.reborrow(), replace_value)?; + let replace_string = to_string(agent, gc.reborrow(), replace_value)? + .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).into_value()) + Ok(String::from_string(agent, gc.nogc(), result).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( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; let search_value = args.get(0); let replace_value = args.get(1); @@ -911,10 +1117,16 @@ impl StringPrototype { } // 3. Let s be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()); + let s_root = s.scope(agent, gc.nogc()); // 4. Let searchString be ? ToString(searchValue). - let search_string = to_string(agent, gc.reborrow(), search_value)?; + let mut search_string = to_string(agent, gc.reborrow(), search_value)? + .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) { @@ -951,6 +1163,8 @@ impl StringPrototype { // 14. For each element p of matchPositions, do 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, @@ -982,22 +1196,25 @@ impl StringPrototype { } // 16. Return result. - return Ok(String::from_string(agent, result).into_value()); + return Ok(String::from_string(agent, gc.nogc(), result).into_value()); } // 6. If functionalReplace is false, Set replaceValue to ? ToString(replaceValue). - let replace_string = to_string(agent, gc.reborrow(), replace_value)?; + let replace_string = to_string(agent, gc.reborrow(), replace_value)? + .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 result = s .as_str(agent) .replace(search_string.as_str(agent), replace_string.as_str(agent)); - Ok(String::from_string(agent, result).into_value()) + Ok(String::from_string(agent, gc.nogc(), result).into_value()) } fn search( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1007,18 +1224,19 @@ impl StringPrototype { fn slice( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()); + let s_root = s.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, gc.reborrow(), args.get(0))?; + s = s_root.get(agent).bind(gc.nogc()); // 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(agent) { @@ -1045,6 +1263,7 @@ impl StringPrototype { None } else { let int_end = to_integer_or_infinity(agent, gc.reborrow(), args.get(1))?; + s = s_root.get(agent).bind(gc.nogc()); // 9. If intEnd = -∞, let to be 0. if int_end.is_neg_infinity(agent) { Some(0) @@ -1084,19 +1303,18 @@ 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 { std::mem::transmute(substring) }; - Ok(String::from_str(agent, substring).into_value()) + Ok(String::from_str(agent, gc.nogc(), substring).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( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. If separator is neither undefined nor null, then let separator = args.get(0); @@ -1117,7 +1335,8 @@ impl StringPrototype { } // 3. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()); + let s_root = s.scope(agent, gc.nogc()); let limit = args.get(1); let lim = match limit { @@ -1130,16 +1349,23 @@ impl StringPrototype { }; // 5. Let R be ? ToString(separator). - let r = to_string(agent, gc.reborrow(), separator)?; + let r = to_string(agent, gc.reborrow(), separator)? + .unbind() + .bind(gc.nogc()); // 6. If lim is zero, return an empty array if lim == 0 { - return Ok(create_array_from_list(agent, &[]).into_value()); + return Ok(create_array_from_list(agent, gc.nogc(), &[]).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.into_value()]).into_value()); + return Ok(create_array_from_list( + agent, + gc.nogc(), + &[s_root.get(agent).bind(gc.nogc()).into_value()], + ) + .into_value()); } // 8. Let separatorLength be the length of R. @@ -1147,6 +1373,7 @@ impl StringPrototype { // 9. If separatorLength = 0, the split by characters if separator_length == 0 { + s = s_root.get(agent).bind(gc.nogc()); let subject = s.as_str(agent); let head = subject.split(""); @@ -1162,14 +1389,15 @@ impl StringPrototype { results.pop(); } - let results = Array::from_slice(agent, results.as_slice()); + let results = Array::from_slice(agent, gc.nogc(), results.as_slice()); return Ok(results.into_value()); } // 10. If S is the empty String, return CreateArrayFromList(« S »). + let s = s_root.get(agent).bind(gc.nogc()); if s.is_empty_string() { let list: [Value; 1] = [s.into_value()]; - return Ok(create_array_from_list(agent, &list).into_value()); + return Ok(create_array_from_list(agent, gc.nogc(), &list).into_value()); } // 11-17. Normal split @@ -1182,40 +1410,45 @@ impl StringPrototype { if lim as usize == i { break; } - results.push(Value::from_str(agent, part)); + results.push(Value::from_str(agent, gc.nogc(), part)); } - let results = Array::from_slice(agent, results.as_slice()); + let results = Array::from_slice(agent, gc.nogc(), results.as_slice()); Ok(results.into_value()) } fn starts_with( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()); + + let s_root = s.scope(agent, gc.nogc()); // 3. Let isRegExp be ? IsRegExp(searchString). // 4. If isRegExp is true, throw a TypeError exception. // TODO // 5. Let searchStr be ? ToString(searchString). - let search_str = to_string(agent, gc.reborrow(), args.get(0))?; + let search_str = to_string(agent, gc.reborrow(), args.get(0))? + .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, gc.reborrow(), position)?; + s = s_root.get(agent).bind(gc.nogc()); if pos.is_sign_negative(agent) || pos.is_pos_zero(agent) { s.as_str(agent) } else { @@ -1238,32 +1471,42 @@ impl StringPrototype { // 14. If substring is searchStr, return true. // 15. Return false. Ok(Value::from( - haystack_str.starts_with(search_str.as_str(agent)), + haystack_str.starts_with(search_str.get(agent).as_str(agent)), )) } fn substring( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, args: ArgumentsList, ) -> JsResult { - // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; - // 2. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + 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) { + s.bind(nogc) + } else { + // 1. Let O be ? RequireObjectCoercible(this value). + let o = require_object_coercible(agent, nogc, this_value)?; + // 2. Let S be ? ToString(O). + to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()) + }; + + let s = s.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, gc.reborrow(), args.get(0))?; + let int_start = to_integer_or_infinity(agent, gc.reborrow(), start)?; // 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end). - let int_end = if args.get(1).is_undefined() { + let int_end = if end.is_undefined() { None } else { - Some(to_integer_or_infinity(agent, gc.reborrow(), args.get(1))?) + Some(to_integer_or_infinity(agent, gc.reborrow(), 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 +inf). @@ -1305,13 +1548,12 @@ 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 { std::mem::transmute(substring) }; - Ok(String::from_str(agent, substring).into_value()) + Ok(String::from_str(agent, gc.nogc(), substring).into_value()) } fn to_locale_lower_case( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1321,7 +1563,6 @@ impl StringPrototype { fn to_locale_upper_case( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1332,12 +1573,11 @@ impl StringPrototype { fn to_lower_case( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). let s = to_string(agent, gc.reborrow(), o)?; @@ -1346,19 +1586,18 @@ impl StringPrototype { // 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).into_value()) + Ok(String::from_string(agent, gc.nogc(), lower_case_string).into_value()) } /// > NOTE: The implementation might not reflect the spec. fn to_upper_case( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). let s = to_string(agent, gc.reborrow(), o)?; @@ -1367,18 +1606,17 @@ impl StringPrototype { // 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).into_value()) + Ok(String::from_string(agent, gc.nogc(), upper_case_string).into_value()) } fn to_well_formed( agent: &mut Agent, mut gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = require_object_coercible(agent, this_value)?; + let o = require_object_coercible(agent, gc.nogc(), this_value)?; // 2. Let S be ? ToString(O). let s = to_string(agent, gc.reborrow(), o)?; @@ -1404,7 +1642,6 @@ impl StringPrototype { fn trim( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1417,15 +1654,16 @@ impl StringPrototype { fn trim_string( agent: &mut Agent, mut gc: GcScope<'_, '_>, - value: Value, trim_where: TrimWhere, ) -> JsResult { // 1. Let str be ? RequireObjectCoercible(string). - let str = require_object_coercible(agent, value)?; + let str = require_object_coercible(agent, gc.nogc(), value)?; // 2. Let S be ? ToString(str) - let s = to_string(agent, gc.reborrow(), str)?; + let s = to_string(agent, gc.reborrow(), str)?.unbind(); + let gc = gc.nogc(); + let s = s.bind(gc); let s_str = s.as_str(agent); @@ -1446,7 +1684,7 @@ impl StringPrototype { // No need to allocate a String if the string was not trimmed Ok(s.into_value()) } else { - let t = String::from_string(agent, t.to_string()); + let t = String::from_string(agent, gc, t.to_string()); Ok(t.into_value()) } } @@ -1455,7 +1693,6 @@ impl StringPrototype { fn trim_end( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1468,7 +1705,6 @@ impl StringPrototype { fn trim_start( agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1484,19 +1720,17 @@ impl StringPrototype { /// > different functions but have the exact same steps. fn value_of( agent: &mut Agent, - _gc: GcScope<'_, '_>, - + gc: GcScope<'_, '_>, this_value: Value, _: ArgumentsList, ) -> JsResult { // 1. Return ? ThisStringValue(this value). - this_string_value(agent, this_value).map(|string| string.into_value()) + this_string_value(agent, gc.nogc(), this_value).map(|string| string.into_value()) } fn iterator( _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _: ArgumentsList, ) -> JsResult { @@ -1574,17 +1808,24 @@ impl StringPrototype { fn string_padding_builtins_impl( agent: &mut Agent, mut gc: GcScope<'_, '_>, - o: Value, max_length: Value, fill_string: Value, placement_start: bool, ) -> JsResult { // 1. Let S be ? ToString(O). - let s = to_string(agent, gc.reborrow(), o)?; + let mut s = to_string(agent, gc.reborrow(), o)?.unbind().bind(gc.nogc()); + let mut s_root = None; // 2. Let intMaxLength be ℝ(? ToLength(maxLength)). - let int_max_length = to_length(agent, gc.reborrow(), max_length)?; + let int_max_length = if let Value::Integer(int_max_length) = max_length { + int_max_length.into_i64().clamp(0, SmallInteger::MAX_NUMBER) + } else { + s_root = Some(s.scope(agent, gc.nogc())); + let result = to_length(agent, gc.reborrow(), max_length)?; + s = s_root.as_ref().unwrap().get(agent).bind(gc.nogc()); + result + }; // 3. Let stringLength be the length of S. let string_length = s.utf16_len(agent) as i64; @@ -1597,13 +1838,29 @@ fn string_padding_builtins_impl( // 5. If fillString is undefined, set fillString to the String value consisting solely of the code unit 0x0020 (SPACE). let fill_string = if fill_string.is_undefined() { BUILTIN_STRING_MEMORY.r#__ + } 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())); + } // 6. Else, set fillString to ? ToString(fillString). - to_string(agent, gc.reborrow(), fill_string)? + let result = to_string(agent, gc.reborrow(), fill_string)? + .unbind() + .bind(gc.nogc()); + s = s_root.unwrap().get(agent).bind(gc.nogc()); + result }; // 7. Return StringPad(S, intMaxLength, fillString, placement). - string_pad(agent, s, int_max_length, fill_string, placement_start) + string_pad( + agent, + gc.nogc(), + s, + int_max_length, + fill_string, + placement_start, + ) } /// ### [22.1.3.17.2 StringPad ( S, maxLength, fillString, placement )](https://tc39.es/ecma262/#sec-stringpad) @@ -1613,6 +1870,7 @@ fn string_padding_builtins_impl( /// placement (start or end) and returns a String. fn string_pad( agent: &mut Agent, + gc: NoGcScope, s: String, max_len: i64, fill_string: String, @@ -1655,7 +1913,7 @@ fn string_pad( } let sub_string = std::str::from_utf8(&sub_string).unwrap(); // let sub_string = &fill_string.as_str(agent)[..fill_len as usize]; - vec.push_back(String::from_string(agent, sub_string.to_owned())); + vec.push_back(String::from_string(agent, gc, sub_string.to_owned())); vec } else { let fill_count = (fill_len / fill_string_len) as usize; @@ -1669,7 +1927,7 @@ fn string_pad( .encode_utf8(&mut sub_string[fill_string.utf8_index(agent, i).unwrap()..]); } let sub_string = std::str::from_utf8(&sub_string).unwrap(); - vec.push_back(String::from_string(agent, sub_string.to_owned())); + vec.push_back(String::from_string(agent, gc, sub_string.to_owned())); vec }; @@ -1681,7 +1939,7 @@ fn string_pad( strings.push_front(s); } - Ok(String::concat(agent, strings.into_iter().collect::>()).into_value()) + Ok(String::concat(agent, gc, strings.into_iter().collect::>()).into_value()) } /// ### [22.1.3.35.1 ThisStringValue ( value )](https://tc39.es/ecma262/#sec-thisstringvalue) @@ -1689,7 +1947,11 @@ fn string_pad( /// The abstract operation ThisStringValue takes argument value (an ECMAScript /// language value) and returns either a normal completion containing a String /// or a throw completion. -fn this_string_value(agent: &mut Agent, value: Value) -> JsResult { +fn this_string_value<'gc>( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + value: Value, +) -> JsResult> { match value { // 1. If value is a String, return value. Value::String(data) => Ok(data.into()), @@ -1708,6 +1970,7 @@ fn this_string_value(agent: &mut Agent, value: Value) -> JsResult { _ => { // 3. Throw a TypeError exception. Err(agent.throw_exception_with_static_message( + gc, ExceptionType::TypeError, "Not a string value", )) diff --git a/nova_vm/src/ecmascript/execution/agent.rs b/nova_vm/src/ecmascript/execution/agent.rs index 3f82ec0b1..a41c514bf 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, rootable::HeapRootData, Vm}, heap::{heap_gc::heap_gc, CreateHeapData, PrimitiveHeapIndexable}, Heap + }, engine::{context::{GcScope, NoGcScope}, rootable::HeapRootData, Vm}, heap::{heap_gc::heap_gc, CreateHeapData, PrimitiveHeapIndexable}, Heap }; use std::{any::Any, cell::RefCell, ptr::NonNull}; @@ -42,8 +42,8 @@ impl JsError { self.0 } - pub fn to_string(self, agent: &mut Agent, mut gc: GcScope<'_, '_>) -> String { - to_string(agent, gc.reborrow(), self.0).unwrap() + pub fn to_string<'gc>(self, agent: &mut Agent, gc: GcScope<'gc, '_>) -> String<'gc> { + to_string(agent, gc, self.0).unwrap() } } @@ -367,10 +367,11 @@ impl Agent { pub fn create_exception_with_static_message( &mut self, + gc: NoGcScope, kind: ExceptionType, message: &'static str, ) -> Value { - let message = String::from_static_str(self, message); + let message = String::from_static_str(self, gc, message).unbind(); self.heap .create(ErrorHeapData::new(kind, Some(message), None)) .into_value() @@ -379,18 +380,23 @@ impl Agent { /// ### [5.2.3.2 Throw an Exception](https://tc39.es/ecma262/#sec-throw-an-exception) pub fn throw_exception_with_static_message( &mut self, + gc: NoGcScope, kind: ExceptionType, message: &'static str, ) -> JsError { - JsError(self.create_exception_with_static_message(kind, message)) + JsError( + self.create_exception_with_static_message(gc, kind, message) + .unbind(), + ) } pub fn throw_exception( &mut self, + gc: NoGcScope, kind: ExceptionType, message: std::string::String, ) -> JsError { - let message = String::from_string(self, message); + let message = String::from_string(self, gc, message).unbind(); JsError( self.heap .create(ErrorHeapData::new(kind, Some(message), None)) @@ -405,7 +411,7 @@ impl Agent { ) -> JsError { JsError( self.heap - .create(ErrorHeapData::new(kind, Some(message), None)) + .create(ErrorHeapData::new(kind, Some(message.unbind()), None)) .into_value(), ) } @@ -463,7 +469,6 @@ pub(crate) fn get_active_script_or_module(agent: &mut Agent) -> Option, - name: String, env: Option, ) -> JsResult { diff --git a/nova_vm/src/ecmascript/execution/environments.rs b/nova_vm/src/ecmascript/execution/environments.rs index 0ee7ce56a..5659a1f30 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; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::types::{Base, Object, Reference, String, Value}, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, @@ -222,7 +222,6 @@ impl EnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, ) -> JsResult { match self { @@ -242,7 +241,6 @@ impl EnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, is_deletable: bool, ) -> JsResult<()> { @@ -255,7 +253,9 @@ impl EnvironmentIndex { idx.create_mutable_binding(agent, name, is_deletable); Ok(()) } - EnvironmentIndex::Global(idx) => idx.create_mutable_binding(agent, name, is_deletable), + EnvironmentIndex::Global(idx) => { + idx.create_mutable_binding(agent, gc.nogc(), name, is_deletable) + } EnvironmentIndex::Object(idx) => { idx.create_mutable_binding(agent, gc, name, is_deletable) } @@ -272,6 +272,7 @@ impl EnvironmentIndex { pub(crate) fn create_immutable_binding( self, agent: &mut Agent, + gc: NoGcScope, name: String, is_strict: bool, ) -> JsResult<()> { @@ -284,7 +285,9 @@ impl EnvironmentIndex { idx.create_immutable_binding(agent, name, is_strict); Ok(()) } - EnvironmentIndex::Global(idx) => idx.create_immutable_binding(agent, name, is_strict), + EnvironmentIndex::Global(idx) => { + idx.create_immutable_binding(agent, gc, name, is_strict) + } EnvironmentIndex::Object(idx) => { idx.create_immutable_binding(agent, name, is_strict); Ok(()) @@ -302,7 +305,6 @@ impl EnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, value: Value, ) -> JsResult<()> { @@ -331,17 +333,16 @@ impl EnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, value: Value, is_strict: bool, ) -> JsResult<()> { match self { EnvironmentIndex::Declarative(idx) => { - idx.set_mutable_binding(agent, name, value, is_strict) + idx.set_mutable_binding(agent, gc.nogc(), name, value, is_strict) } EnvironmentIndex::Function(idx) => { - idx.set_mutable_binding(agent, name, value, is_strict) + idx.set_mutable_binding(agent, gc.nogc(), name, value, is_strict) } EnvironmentIndex::Global(idx) => { idx.set_mutable_binding(agent, gc, name, value, is_strict) @@ -365,13 +366,16 @@ impl EnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, is_strict: bool, ) -> JsResult { match self { - EnvironmentIndex::Declarative(idx) => idx.get_binding_value(agent, name, is_strict), - EnvironmentIndex::Function(idx) => idx.get_binding_value(agent, name, is_strict), + EnvironmentIndex::Declarative(idx) => { + idx.get_binding_value(agent, gc.nogc(), name, is_strict) + } + EnvironmentIndex::Function(idx) => { + idx.get_binding_value(agent, gc.nogc(), name, is_strict) + } EnvironmentIndex::Global(idx) => idx.get_binding_value(agent, gc, name, is_strict), EnvironmentIndex::Object(idx) => idx.get_binding_value(agent, gc, name, is_strict), } @@ -387,7 +391,6 @@ impl EnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, ) -> JsResult { match self { @@ -488,7 +491,6 @@ impl Default for Environments { pub(crate) fn get_identifier_reference( agent: &mut Agent, mut gc: GcScope<'_, '_>, - env: Option, name: String, strict: bool, diff --git a/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs b/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs index 7eda1a23b..44ebefd78 100644 --- a/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/declarative_environment.rs @@ -10,6 +10,7 @@ use crate::{ execution::{agent::ExceptionType, Agent, JsResult}, types::{Object, String, Value}, }, + engine::context::NoGcScope, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, }; @@ -27,7 +28,7 @@ pub(crate) struct DeclarativeEnvironment { pub(crate) outer_env: OuterEnv, /// The environment's bindings. - pub(crate) bindings: AHashMap, + pub(crate) bindings: AHashMap, Binding>, } #[derive(Debug, Clone, Copy)] @@ -70,7 +71,7 @@ impl DeclarativeEnvironment { // uninitialized. If D is true, record that the newly created binding // may be deleted by a subsequent DeleteBinding call. self.bindings.insert( - name, + name.unbind(), Binding { value: None, // Strictness only seems to matter for immutable bindings. @@ -91,7 +92,7 @@ impl DeclarativeEnvironment { // uninitialized. If S is true, record that the newly created binding is // a strict binding. self.bindings.insert( - name, + name.unbind(), Binding { value: None, strict: is_strict, @@ -105,7 +106,7 @@ impl DeclarativeEnvironment { /// ### [9.1.1.1.4 InitializeBinding ( N, V )](https://tc39.es/ecma262/#sec-declarative-environment-records-initializebinding-n-v) pub(super) fn initialize_binding(&mut self, name: String, value: Value) { // 1. Assert: envRec must have an uninitialized binding for N. - let binding = self.bindings.get_mut(&name).unwrap(); + let binding = self.bindings.get_mut(&name.unbind()).unwrap(); // 2. Set the bound value for N in envRec to V. // 3. Record that the binding for N in envRec has been initialized. @@ -143,7 +144,7 @@ impl DeclarativeEnvironment { } // 3. Remove the binding for N from envRec. - self.bindings.remove(&name); + self.bindings.remove(&name.unbind()); // 4. Return true. true @@ -252,17 +253,22 @@ impl DeclarativeEnvironmentIndex { pub(crate) fn set_mutable_binding( self, agent: &mut Agent, + gc: NoGcScope, name: String, value: Value, mut is_strict: bool, ) -> JsResult<()> { let env_rec = &mut agent[self]; // 1. If envRec does not have a binding for N, then - let Some(binding) = env_rec.bindings.get_mut(&name) else { + let Some(binding) = env_rec.bindings.get_mut(&name.unbind()) else { // a. If S is true, throw a ReferenceError exception. if is_strict { let error_message = format!("Identifier '{}' does not exist.", name.as_str(agent)); - return Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)); + return Err(agent.throw_exception( + gc, + ExceptionType::ReferenceError, + error_message, + )); } // b. Perform ! envRec.CreateMutableBinding(N, true). @@ -287,7 +293,7 @@ impl DeclarativeEnvironmentIndex { "Identifier '{}' has not been initialized.", name.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)); + return Err(agent.throw_exception(gc, ExceptionType::ReferenceError, error_message)); } // 4. Else if the binding for N in envRec is a mutable binding, then @@ -306,7 +312,7 @@ impl DeclarativeEnvironmentIndex { "Cannot assign to immutable identifier '{}' in strict mode.", name.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc, ExceptionType::TypeError, error_message)); } } @@ -325,6 +331,7 @@ impl DeclarativeEnvironmentIndex { pub(crate) fn get_binding_value( self, agent: &mut Agent, + gc: NoGcScope, name: String, is_strict: bool, ) -> JsResult { @@ -335,7 +342,7 @@ impl DeclarativeEnvironmentIndex { // 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)) + Err(agent.throw_exception(gc, ExceptionType::ReferenceError, error_message)) }, Ok, ) diff --git a/nova_vm/src/ecmascript/execution/environments/function_environment.rs b/nova_vm/src/ecmascript/execution/environments/function_environment.rs index 2fec3fadf..cfa23c8b0 100644 --- a/nova_vm/src/ecmascript/execution/environments/function_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/function_environment.rs @@ -5,7 +5,7 @@ use super::{ DeclarativeEnvironment, DeclarativeEnvironmentIndex, EnvironmentIndex, FunctionEnvironmentIndex, }; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ builtins::{ECMAScriptFunction, ThisMode}, @@ -218,7 +218,7 @@ 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) -> JsResult { + pub(crate) fn get_this_binding(self, agent: &mut Agent, gc: NoGcScope) -> JsResult { // 1. Assert: envRec.[[ThisBindingStatus]] is not lexical. // 2. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception. // 3. Return envRec.[[ThisValue]]. @@ -227,6 +227,7 @@ impl FunctionEnvironmentIndex { ThisBindingStatus::Lexical => unreachable!(), ThisBindingStatus::Initialized => Ok(env_rec.this_value.unwrap()), ThisBindingStatus::Uninitialized => Err(agent.throw_exception_with_static_message( + gc, ExceptionType::ReferenceError, "Uninitialized this binding", )), @@ -268,6 +269,7 @@ impl FunctionEnvironmentIndex { pub(crate) fn set_mutable_binding( self, agent: &mut Agent, + gc: NoGcScope, name: String, value: Value, mut is_strict: bool, @@ -279,7 +281,11 @@ impl FunctionEnvironmentIndex { // a. If S is true, throw a ReferenceError exception. if is_strict { let error_message = format!("Identifier '{}' does not exist.", name.as_str(agent)); - return Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)); + return Err(agent.throw_exception( + gc, + ExceptionType::ReferenceError, + error_message, + )); } // b. Perform ! envRec.CreateMutableBinding(N, true). @@ -292,7 +298,7 @@ impl FunctionEnvironmentIndex { return Ok(()); }; - let binding = agent[dcl_rec].bindings.get_mut(&name).unwrap(); + let binding = agent[dcl_rec].bindings.get_mut(&name.unbind()).unwrap(); // 2. If the binding for N in envRec is a strict binding, set S to true. if binding.strict { @@ -306,7 +312,7 @@ impl FunctionEnvironmentIndex { "Identifier '{}' has not been initialized.", name.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)); + return Err(agent.throw_exception(gc, ExceptionType::ReferenceError, error_message)); } // 4. Else if the binding for N in envRec is a mutable binding, then @@ -325,7 +331,7 @@ impl FunctionEnvironmentIndex { "Cannot assign to immutable identifier '{}' in strict mode.", name.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc, ExceptionType::TypeError, error_message)); } } @@ -337,12 +343,13 @@ impl FunctionEnvironmentIndex { pub(crate) fn get_binding_value( self, agent: &mut Agent, + gc: NoGcScope, name: String, is_strict: bool, ) -> JsResult { agent[self] .declarative_environment - .get_binding_value(agent, name, is_strict) + .get_binding_value(agent, gc, name, is_strict) } /// ### [9.1.1.1.7 DeleteBinding ( N )](https://tc39.es/ecma262/#sec-declarative-environment-records-deletebinding-n) @@ -364,7 +371,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(self, agent: &mut Agent, value: Value) -> JsResult { + pub(crate) fn bind_this_value( + self, + agent: &mut Agent, + gc: NoGcScope, + value: Value, + ) -> JsResult { let env_rec = &mut agent[self]; // 1. Assert: envRec.[[ThisBindingStatus]] is not LEXICAL. debug_assert!(env_rec.this_binding_status != ThisBindingStatus::Lexical); @@ -373,6 +385,7 @@ impl FunctionEnvironmentIndex { // ReferenceError exception. if env_rec.this_binding_status == ThisBindingStatus::Initialized { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::ReferenceError, "[[ThisBindingStatus]] is INITIALIZED", )); diff --git a/nova_vm/src/ecmascript/execution/environments/global_environment.rs b/nova_vm/src/ecmascript/execution/environments/global_environment.rs index 892104765..94c2c27d4 100644 --- a/nova_vm/src/ecmascript/execution/environments/global_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/global_environment.rs @@ -12,7 +12,7 @@ use crate::ecmascript::execution::agent::ExceptionType; use crate::ecmascript::execution::JsResult; use crate::ecmascript::types::{Object, PropertyDescriptor, PropertyKey, String, Value}; use crate::ecmascript::{execution::Agent, types::InternalMethods}; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}; use super::{ @@ -58,7 +58,7 @@ pub struct GlobalEnvironment { /// VariableDeclaration declarations in global code for the associated /// realm. // TODO: Use the Heap to set this. - var_names: AHashSet, + var_names: AHashSet>, } impl GlobalEnvironment { @@ -147,7 +147,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, ) -> JsResult { let env_rec = &agent[self]; @@ -175,6 +174,7 @@ impl GlobalEnvironmentIndex { pub(crate) fn create_mutable_binding( self, agent: &mut Agent, + gc: NoGcScope, name: String, is_deletable: bool, ) -> JsResult<()> { @@ -185,7 +185,7 @@ impl GlobalEnvironmentIndex { if dcl_rec.has_binding(agent, name) { let error_message = format!("Redeclaration of global binding '{}'.", name.as_str(agent)); - Err(agent.throw_exception(ExceptionType::TypeError, error_message)) + Err(agent.throw_exception(gc, ExceptionType::TypeError, error_message)) } else { // 3. Return ! DclRec.CreateMutableBinding(N, D). dcl_rec.create_mutable_binding(agent, name, is_deletable); @@ -205,6 +205,7 @@ impl GlobalEnvironmentIndex { pub(crate) fn create_immutable_binding( self, agent: &mut Agent, + gc: NoGcScope, name: String, is_strict: bool, ) -> JsResult<()> { @@ -215,7 +216,7 @@ impl GlobalEnvironmentIndex { if dcl_rec.has_binding(agent, name) { let error_message = format!("Redeclaration of global binding '{}'.", name.as_str(agent)); - Err(agent.throw_exception(ExceptionType::TypeError, error_message)) + Err(agent.throw_exception(gc, ExceptionType::TypeError, error_message)) } else { // 3. Return ! DclRec.CreateImmutableBinding(N, S). dcl_rec.create_immutable_binding(agent, name, is_strict); @@ -235,7 +236,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, value: Value, ) -> JsResult<()> { @@ -270,7 +270,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, value: Value, is_strict: bool, @@ -281,7 +280,7 @@ impl GlobalEnvironmentIndex { // 2. If ! DclRec.HasBinding(N) is true, then if dcl_rec.has_binding(agent, name) { // a. Return ? DclRec.SetMutableBinding(N, V, S). - dcl_rec.set_mutable_binding(agent, name, value, is_strict) + dcl_rec.set_mutable_binding(agent, gc.nogc(), name, value, is_strict) } else { // 3. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; @@ -304,7 +303,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - n: String, s: bool, ) -> JsResult { @@ -314,7 +312,7 @@ impl GlobalEnvironmentIndex { // 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) + dcl_rec.get_binding_value(agent, gc.nogc(), n, s) } else { // 3. Let ObjRec be envRec.[[ObjectRecord]]. let obj_rec = env_rec.object_record; @@ -333,7 +331,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - name: String, ) -> JsResult { let env_rec = &agent[self]; @@ -360,7 +357,7 @@ impl GlobalEnvironmentIndex { 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); + env_rec.var_names.remove(&name.unbind()); } } // c. Return status. @@ -455,7 +452,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, ) -> JsResult { let env_rec = &agent[self]; @@ -487,7 +483,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - name: String, ) -> JsResult { let env_rec = &agent[self]; @@ -518,7 +513,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - name: String, ) -> JsResult { let env_rec = &agent[self]; @@ -560,7 +554,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - name: String, is_deletable: bool, ) -> JsResult<()> { @@ -585,7 +578,7 @@ impl GlobalEnvironmentIndex { // 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); + env_rec.var_names.insert(name.unbind()); // 7. Return UNUSED. Ok(()) @@ -604,7 +597,6 @@ impl GlobalEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - name: String, value: Value, d: bool, @@ -647,7 +639,7 @@ impl GlobalEnvironmentIndex { // 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); + env_rec.var_names.insert(name.unbind()); // 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 c9cf7bb8e..cc00cb438 100644 --- a/nova_vm/src/ecmascript/execution/environments/object_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/object_environment.rs @@ -107,7 +107,6 @@ impl ObjectEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - n: String, ) -> JsResult { let env_rec = &agent[self]; @@ -157,7 +156,6 @@ impl ObjectEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - n: String, d: bool, ) -> JsResult<()> { @@ -204,7 +202,6 @@ impl ObjectEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - n: String, v: Value, ) -> JsResult<()> { @@ -234,7 +231,6 @@ impl ObjectEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - n: String, v: Value, s: bool, @@ -247,13 +243,15 @@ impl ObjectEnvironmentIndex { let still_exists = has_property(agent, gc.reborrow(), binding_object, n)?; // 3. If stillExists is false and S is true, throw a ReferenceError exception. if !still_exists && s { - let binding_object_repr = binding_object.into_value().string_repr(agent, gc); + let binding_object_repr = binding_object + .into_value() + .string_repr(agent, gc.reborrow()); let error_message = format!( "Property '{}' does not exist in {}.", n.as_display(agent), binding_object_repr.as_str(agent) ); - Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)) + Err(agent.throw_exception(gc.nogc(), ExceptionType::ReferenceError, error_message)) } else { // 4. Perform ? Set(bindingObject, N, V, S). set(agent, gc, binding_object, n, v, s)?; @@ -273,7 +271,6 @@ impl ObjectEnvironmentIndex { self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - n: String, s: bool, ) -> JsResult { @@ -289,13 +286,15 @@ impl ObjectEnvironmentIndex { if !s { Ok(Value::Undefined) } else { - let binding_object_repr = binding_object.into_value().string_repr(agent, gc); + let binding_object_repr = binding_object + .into_value() + .string_repr(agent, gc.reborrow()); let error_message = format!( "Property '{}' does not exist in {}.", name.as_display(agent), binding_object_repr.as_str(agent) ); - Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)) + Err(agent.throw_exception(gc.nogc(), ExceptionType::ReferenceError, error_message)) } } else { // 4. Return ? Get(bindingObject, N). @@ -314,7 +313,6 @@ impl ObjectEnvironmentIndex { self, agent: &mut Agent, gc: GcScope<'_, '_>, - name: String, ) -> JsResult { let env_rec = &agent[self]; diff --git a/nova_vm/src/ecmascript/execution/realm.rs b/nova_vm/src/ecmascript/execution/realm.rs index 6dbea0b0f..c3891f8ea 100644 --- a/nova_vm/src/ecmascript/execution/realm.rs +++ b/nova_vm/src/ecmascript/execution/realm.rs @@ -321,7 +321,6 @@ pub(crate) fn set_realm_global_object( pub(crate) fn set_default_global_bindings( agent: &mut Agent, mut gc: GcScope<'_, '_>, - realm_id: RealmIdentifier, ) -> JsResult { // 1. Let global be realmRec.[[GlobalObject]]. @@ -1069,7 +1068,6 @@ pub(crate) fn set_default_global_bindings( pub(crate) fn initialize_host_defined_realm( agent: &mut Agent, mut gc: GcScope<'_, '_>, - create_global_object: Option) -> Object>, create_global_this_value: Option) -> Object>, initialize_global_object: Option, Object)>, diff --git a/nova_vm/src/ecmascript/scripts_and_modules/script.rs b/nova_vm/src/ecmascript/scripts_and_modules/script.rs index 765b5d772..b30519809 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; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ execution::{ @@ -198,6 +198,7 @@ impl HeapMarkAndSweep for Script { /// Script. pub fn parse_script( agent: &mut Agent, + gc: NoGcScope, source_text: String, realm: RealmIdentifier, strict_mode: bool, @@ -217,7 +218,7 @@ pub fn parse_script( // SAFETY: Script keeps the SourceCode reference alive in the Heap, thus // making the Program's references point to a live Allocator. - let parse_result = unsafe { SourceCode::parse_source(agent, source_text, source_type) }; + let parse_result = unsafe { SourceCode::parse_source(agent, gc, source_text, source_type) }; let (program, source_code) = match parse_result { // 2. If script is a List of errors, return script. @@ -250,7 +251,6 @@ pub fn parse_script( pub fn script_evaluation( agent: &mut Agent, mut gc: GcScope<'_, '_>, - script: Script, ) -> JsResult { let realm_id = script.realm; @@ -303,7 +303,7 @@ pub fn script_evaluation( // 13. If result.[[Type]] is normal, then let result: JsResult = if result.is_ok() { - let bytecode = Executable::compile_script(agent, script); + let bytecode = Executable::compile_script(agent, gc.nogc(), script); // a. Set result to Completion(Evaluation of script). // b. If result.[[Type]] is normal and result.[[Value]] is empty, then // i. Set result to NormalCompletion(undefined). @@ -341,7 +341,6 @@ pub fn script_evaluation( pub(crate) fn global_declaration_instantiation( agent: &mut Agent, mut gc: GcScope<'_, '_>, - script: ScriptIdentifier, env: GlobalEnvironmentIndex, ) -> JsResult<()> { @@ -372,7 +371,7 @@ pub(crate) fn global_declaration_instantiation( // 3. For each element name of lexNames, do for name in lex_names { - let name = String::from_str(agent, name.as_str()); + let name = String::from_str(agent, gc.nogc(), name.as_str()).unbind(); if // a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception. env.has_var_declaration(agent, name) @@ -386,18 +385,26 @@ pub(crate) fn global_declaration_instantiation( "Redeclaration of restricted global property '{}'.", name.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::SyntaxError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::SyntaxError, + error_message, + )); } } // 4. For each element name of varNames, do for name in &var_names { // a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. - let name = String::from_str(agent, name.as_str()); + let name = String::from_str(agent, gc.nogc(), name.as_str()); if env.has_lexical_declaration(agent, name) { let error_message = format!("Redeclaration of lexical binding '{}'.", name.as_str(agent)); - return Err(agent.throw_exception(ExceptionType::SyntaxError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::SyntaxError, + error_message, + )); } } @@ -421,7 +428,8 @@ pub(crate) fn global_declaration_instantiation( // iv. If declaredFunctionNames does not contain fn, then if declared_function_names.insert(function_name.clone()) { // 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn). - let function_name = String::from_str(agent, function_name.as_str()); + let function_name = + String::from_str(agent, gc.nogc(), function_name.as_str()).unbind(); let fn_definable = env.can_declare_global_function(agent, gc.reborrow(), function_name)?; // 2. If fnDefinable is false, throw a TypeError exception. @@ -430,7 +438,11 @@ pub(crate) fn global_declaration_instantiation( "Cannot declare of global function '{}'.", function_name.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::TypeError, + error_message, + )); } // 3. Append fn to declaredFunctionNames. // 4. Insert d as the first element of functionsToInitialize. @@ -454,13 +466,20 @@ pub(crate) fn global_declaration_instantiation( // 1. If declaredFunctionNames does not contain vn, then if !declared_function_names.contains(&vn) { // a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn). - let vn = String::from_str(agent, vn.as_str()); + // TODO: This is a very problematic area for lifetimes. + // CanDeclareGlobalVar can trigger GC, but we also need to + // hash the strings to eliminate duplicates... + let vn = String::from_str(agent, gc.nogc(), vn.as_str()).unbind(); let vn_definable = env.can_declare_global_var(agent, gc.reborrow(), vn)?; // b. If vnDefinable is false, throw a TypeError exception. if !vn_definable { let error_message = format!("Cannot declare global variable '{}'.", vn.as_str(agent)); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::TypeError, + error_message, + )); } // c. If declaredVarNames does not contain vn, then // i. Append vn to declaredVarNames. @@ -484,13 +503,17 @@ pub(crate) fn global_declaration_instantiation( let mut bound_names = vec![]; let mut const_bound_names = vec![]; let mut closure = |identifier: &BindingIdentifier| { - bound_names.push(String::from_str(agent, identifier.name.as_str())); + bound_names.push(String::from_str(agent, gc.nogc(), identifier.name.as_str())); }; match d { LexicallyScopedDeclaration::Variable(decl) => { if decl.kind == VariableDeclarationKind::Const { decl.id.bound_names(&mut |identifier| { - const_bound_names.push(String::from_str(agent, identifier.name.as_str())) + const_bound_names.push(String::from_str( + agent, + gc.nogc(), + identifier.name.as_str(), + )) }); } else { decl.id.bound_names(&mut closure) @@ -506,12 +529,12 @@ pub(crate) fn global_declaration_instantiation( for dn in const_bound_names { // i. If IsConstantDeclaration of d is true, then // 1. Perform ? env.CreateImmutableBinding(dn, true). - env.create_immutable_binding(agent, dn, true)?; + env.create_immutable_binding(agent, gc.nogc(), dn, true)?; } for dn in bound_names { // ii. Else, // 1. Perform ? env.CreateMutableBinding(dn, false). - env.create_mutable_binding(agent, dn, false)?; + env.create_mutable_binding(agent, gc.nogc(), dn, false)?; } } @@ -523,7 +546,6 @@ pub(crate) fn global_declaration_instantiation( assert!(function_name.is_none()); function_name = Some(identifier.name.clone()); }); - let function_name = String::from_str(agent, function_name.unwrap().as_str()); // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. let fo = instantiate_function_object( agent, @@ -532,6 +554,8 @@ pub(crate) fn global_declaration_instantiation( EnvironmentIndex::Global(env), private_env, ); + let function_name = + String::from_str(agent, gc.nogc(), function_name.unwrap().as_str()).unbind(); // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). env.create_global_function_binding( agent, @@ -577,8 +601,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, ""); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), ""); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -593,8 +617,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "true"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "true"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -609,8 +633,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "-2"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "-2"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -625,8 +649,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "void (2 + 2 + 6)"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "void (2 + 2 + 6)"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -641,8 +665,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "+(54)"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "+(54)"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -657,8 +681,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "!true"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "!true"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -673,8 +697,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "~0b1111"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "~0b1111"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -689,50 +713,77 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "typeof undefined"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof undefined"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "undefined")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "undefined") + ); - let source_text = String::from_static_str(&mut agent, "typeof null"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof null"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "object")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "object") + ); - let source_text = String::from_static_str(&mut agent, "typeof \"string\""); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof \"string\""); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "string")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "string") + ); - let source_text = String::from_static_str(&mut agent, "typeof Symbol()"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof Symbol()"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "symbol")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "symbol") + ); - let source_text = String::from_static_str(&mut agent, "typeof true"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof true"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "boolean")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "boolean") + ); - let source_text = String::from_static_str(&mut agent, "typeof 3"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof 3"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "number")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "number") + ); - let source_text = String::from_static_str(&mut agent, "typeof 3n"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof 3n"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "bigint")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "bigint") + ); - let source_text = String::from_static_str(&mut agent, "typeof {}"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof {}"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "object")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "object") + ); - let source_text = String::from_static_str(&mut agent, "typeof (function() {})"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "typeof (function() {})"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "function")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "function") + ); } #[test] @@ -743,8 +794,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "2 + 2 + 6"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "2 + 2 + 6"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); @@ -759,8 +810,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "var foo = 3;"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "var foo = 3;"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Undefined); } @@ -773,11 +824,11 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "var foo = {};"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "var foo = {};"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_undefined()); - let key = PropertyKey::from_static_str(&mut agent, "foo"); + let key = PropertyKey::from_static_str(&mut agent, gc.nogc(), "foo"); let foo = agent .get_realm(realm) .global_object @@ -802,11 +853,11 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "var foo = { a: 3 };"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "var foo = { a: 3 };"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_undefined()); - let key = PropertyKey::from_static_str(&mut agent, "foo"); + let key = PropertyKey::from_static_str(&mut agent, gc.nogc(), "foo"); let foo = agent .get_realm(realm) .global_object @@ -817,7 +868,7 @@ mod test { .unwrap(); assert!(foo.is_object()); let result = Object::try_from(foo).unwrap(); - let key = PropertyKey::from_static_str(&mut agent, "a"); + let key = PropertyKey::from_static_str(&mut agent, gc.nogc(), "a"); assert!(result .internal_has_property(&mut agent, gc.reborrow(), key) .unwrap()); @@ -846,11 +897,11 @@ mod test { script_or_module: None, }); - let source_text = String::from_static_str(&mut agent, "var foo = [];"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "var foo = [];"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_undefined()); - let foo_key = String::from_static_str(&mut agent, "foo"); + let foo_key = String::from_static_str(&mut agent, gc.nogc(), "foo").unbind(); let foo = agent .get_realm(realm) .global_env @@ -873,11 +924,11 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "var foo = [ 'a', 3 ];"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "var foo = [ 'a', 3 ];"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_undefined()); - let foo_key = String::from_static_str(&mut agent, "foo"); + let foo_key = String::from_static_str(&mut agent, gc.nogc(), "foo").unbind(); let foo = agent .get_realm(realm) .global_env @@ -896,7 +947,7 @@ mod test { .unwrap() .unwrap() .value, - Some(Value::from_static_str(&mut agent, "a")) + Some(Value::from_static_str(&mut agent, gc.nogc(), "a")) ); let key = PropertyKey::Integer(1.into()); assert!(result @@ -920,8 +971,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "function foo() {}"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "function foo() {}"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_function()); } @@ -934,8 +985,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "(function() {})()"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "(function() {})()"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_undefined()); } @@ -948,8 +999,9 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "var f = function() {}; f();"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "var f = function() {}; f();"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_undefined()); } @@ -962,8 +1014,8 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "function f() {}; f();"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "function f() {}; f();"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_undefined()); } @@ -976,8 +1028,9 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "(function() { return 3 })()"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "(function() { return 3 })()"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Number::from(3).into_value()); } @@ -998,12 +1051,12 @@ mod test { script_or_module: None, }); - let key = PropertyKey::from_static_str(&mut agent, "test"); + let key = PropertyKey::from_static_str(&mut agent, gc.nogc(), "test"); struct TestBuiltinFunction; impl Builtin for TestBuiltinFunction { - const NAME: String = String::from_small_string("test"); + const NAME: String<'static> = String::from_small_string("test"); const LENGTH: u8 = 1; @@ -1024,18 +1077,18 @@ mod test { create_data_property_or_throw(&mut agent, gc.reborrow(), global, key, func.into_value()) .unwrap(); - let source_text = String::from_static_str(&mut agent, "test(true)"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "test(true)"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::from(3)); - let source_text = String::from_static_str(&mut agent, "test()"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "test()"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Null); - let source_text = String::from_static_str(&mut agent, "test({})"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "test({})"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Null); } @@ -1048,13 +1101,13 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "if (true) 3"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "if (true) 3"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Number::from(3).into_value()); - let source_text = String::from_static_str(&mut agent, "if (false) 3"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "if (false) 3"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Undefined); } @@ -1069,17 +1122,19 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "var foo = function() { if (true) { return 3; } else { return 5; } }; foo()", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Number::from(3).into_value()); let source_text = String::from_static_str( &mut agent, + gc.nogc(), "var bar = function() { if (false) { return 3; } else { return 5; } }; bar()", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Number::from(5).into_value()); } @@ -1092,8 +1147,9 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "var foo = { a: 3 }; foo.a"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "var foo = { a: 3 }; foo.a"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Number::from(3).into_value()); } @@ -1108,9 +1164,10 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "var fn = function() { return 3; }; var foo = { a: { b: fn } }; foo.a.b()", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Number::from(3).into_value()); } @@ -1123,9 +1180,12 @@ mod test { let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = - String::from_static_str(&mut agent, "var foo = { a: 3 }; var prop = 'a'; foo[prop]"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str( + &mut agent, + gc.nogc(), + "var foo = { a: 3 }; var prop = 'a'; foo[prop]", + ); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Number::from(3).into_value()); } @@ -1136,11 +1196,12 @@ mod test { let mut agent = Agent::new(Options::default(), &DefaultHostHooks); let realm = create_realm(&mut agent); set_realm_global_object(&mut agent, realm, None, None); - let source_text = String::from_static_str(&mut agent, "var i = 0; for (; i < 3; i++) {}"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "var i = 0; for (; i < 3; i++) {}"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Undefined); - let key = PropertyKey::from_static_str(&mut agent, "i"); + let key = PropertyKey::from_static_str(&mut agent, gc.nogc(), "i"); let i: Value = agent .get_realm(realm) .global_object @@ -1160,13 +1221,14 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "let i = 0; const a = 'foo'; i = 3;"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "let i = 0; const a = 'foo'; i = 3;"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); let global_env = agent.get_realm(realm).global_env.unwrap(); - let a_key = String::from_static_str(&mut agent, "a"); - let i_key = String::from_static_str(&mut agent, "i"); + let a_key = String::from_static_str(&mut agent, gc.nogc(), "a").unbind(); + let i_key = String::from_static_str(&mut agent, gc.nogc(), "i").unbind(); assert!(global_env .has_binding(&mut agent, gc.reborrow(), a_key) .unwrap()); @@ -1195,14 +1257,17 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = - String::from_static_str(&mut agent, "{ let i = 0; const a = 'foo'; i = 3; }"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str( + &mut agent, + gc.nogc(), + "{ let i = 0; const a = 'foo'; i = 3; }", + ); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Undefined); - let a_key = String::from_static_str(&mut agent, "a"); - let i_key = String::from_static_str(&mut agent, "i"); + let a_key = String::from_static_str(&mut agent, gc.nogc(), "a"); + let i_key = String::from_static_str(&mut agent, gc.nogc(), "i"); let global_env = agent.get_realm(realm).global_env.unwrap(); assert!(!global_env.has_lexical_declaration(&agent, a_key)); assert!(!global_env.has_lexical_declaration(&agent, i_key)); @@ -1216,12 +1281,13 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "var foo = {}; foo.a = 42; foo"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "var foo = {}; foo.a = 42; foo"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); let object = Object::try_from(result).unwrap(); - let pk = PropertyKey::from_static_str(&mut agent, "a"); + let pk = PropertyKey::from_static_str(&mut agent, gc.nogc(), "a"); assert_eq!( object .internal_get(&mut agent, gc, pk, object.into_value()) @@ -1240,9 +1306,10 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "let a = 0; try { a++; } catch { a = 500; }; a++; a", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc, script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(2))); } @@ -1257,9 +1324,10 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "let a = 0; try { throw null; a = 500 } catch { a++; }; a++; a", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(2))); } @@ -1274,11 +1342,15 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "let err; try { throw 'thrown'; } catch(e) { err = e; }; err", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "thrown")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "thrown") + ); } #[test] @@ -1291,9 +1363,10 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "let a = 42; try { let a = 62; throw 'thrown'; } catch { }; a", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(42))); } @@ -1308,9 +1381,10 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "const foo = function (a) { return a + 10; }; foo(32)", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(42))); } @@ -1323,13 +1397,13 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "true && true"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "true && true"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Boolean(true)); - let source_text = String::from_static_str(&mut agent, "true && false && true"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "true && false && true"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Boolean(false)); } @@ -1342,13 +1416,13 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "false || false"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "false || false"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Boolean(false)); - let source_text = String::from_static_str(&mut agent, "true || false || true"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "true || false || true"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Boolean(true)); } @@ -1361,18 +1435,18 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "null ?? 42"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "null ?? 42"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(42))); - let source_text = String::from_static_str(&mut agent, "'foo' ?? 12"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "'foo' ?? 12"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "foo")); + assert_eq!(result, Value::from_static_str(&mut agent, gc.nogc(), "foo")); - let source_text = String::from_static_str(&mut agent, "undefined ?? null"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "undefined ?? null"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Null); } @@ -1385,26 +1459,33 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "'foo' + '' + 'bar'"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "'foo' + '' + 'bar'"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "foobar")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "foobar") + ); - let source_text = String::from_static_str(&mut agent, "'foo' + ' a heap string'"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "'foo' + ' a heap string'"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!( result, - Value::from_static_str(&mut agent, "foo a heap string") + Value::from_static_str(&mut agent, gc.nogc(), "foo a heap string") ); - let source_text = - String::from_static_str(&mut agent, "'Concatenating ' + 'two heap strings'"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str( + &mut agent, + gc.nogc(), + "'Concatenating ' + 'two heap strings'", + ); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!( result, - Value::from_static_str(&mut agent, "Concatenating two heap strings") + Value::from_static_str(&mut agent, gc.nogc(), "Concatenating two heap strings") ); } @@ -1416,28 +1497,29 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "function foo() {}; foo.bar"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "function foo() {}; foo.bar"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Undefined); - let source_text = String::from_static_str(&mut agent, "foo.bar = 42; foo.bar"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "foo.bar = 42; foo.bar"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(42))); - let source_text = String::from_static_str(&mut agent, "foo.name"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "foo.name"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "foo")); + assert_eq!(result, Value::from_static_str(&mut agent, gc.nogc(), "foo")); - let source_text = String::from_static_str(&mut agent, "foo.length"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "foo.length"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::zero())); - let source_text = String::from_static_str(&mut agent, "foo.prototype"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "foo.prototype"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert!(result.is_object()) } @@ -1450,13 +1532,16 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "TypeError.name"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "TypeError.name"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(result, Value::from_static_str(&mut agent, "TypeError")); + assert_eq!( + result, + Value::from_static_str(&mut agent, gc.nogc(), "TypeError") + ); - let source_text = String::from_static_str(&mut agent, "TypeError.length"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "TypeError.length"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(1))); } @@ -1469,13 +1554,14 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "function foo() {}; foo.prototype"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "function foo() {}; foo.prototype"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); let foo_prototype = Object::try_from(result).unwrap(); - let source_text = String::from_static_str(&mut agent, "new foo()"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "new foo()"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = match script_evaluation(&mut agent, gc.reborrow(), script) { Ok(result) => result, Err(err) => panic!( @@ -1500,17 +1586,19 @@ mod test { let source_text = String::from_static_str( &mut agent, + gc.nogc(), "function foo() { this.bar = 42; }; new foo().bar", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(42))); let source_text = String::from_static_str( &mut agent, + gc.nogc(), "foo.prototype.baz = function() { return this.bar + 10; }; (new foo()).baz()", ); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Integer(SmallInteger::from(52))); } @@ -1523,23 +1611,29 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "+Symbol()"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "+Symbol()"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert!(script_evaluation(&mut agent, gc.reborrow(), script).is_err()); - let source_text = String::from_static_str(&mut agent, "+Symbol('foo')"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "+Symbol('foo')"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert!(script_evaluation(&mut agent, gc.reborrow(), script).is_err()); - let source_text = String::from_static_str(&mut agent, "String(Symbol())"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "String(Symbol())"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let value = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(value, Value::from_static_str(&mut agent, "Symbol()")); + assert_eq!( + value, + Value::from_static_str(&mut agent, gc.nogc(), "Symbol()") + ); - let source_text = String::from_static_str(&mut agent, "String(Symbol('foo'))"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "String(Symbol('foo'))"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let value = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - assert_eq!(value, Value::from_static_str(&mut agent, "Symbol(foo)")); + assert_eq!( + value, + Value::from_static_str(&mut agent, gc.nogc(), "Symbol(foo)") + ); } #[test] @@ -1550,43 +1644,43 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "3 instanceof Number"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "3 instanceof Number"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert_eq!( script_evaluation(&mut agent, gc.reborrow(), script).unwrap(), false.into() ); - let source_text = String::from_static_str(&mut agent, "'foo' instanceof String"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "'foo' instanceof String"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert_eq!( script_evaluation(&mut agent, gc.reborrow(), script).unwrap(), false.into() ); - let source_text = String::from_static_str(&mut agent, "({}) instanceof Object"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "({}) instanceof Object"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert_eq!( script_evaluation(&mut agent, gc.reborrow(), script).unwrap(), true.into() ); - let source_text = String::from_static_str(&mut agent, "({}) instanceof Array"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "({}) instanceof Array"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert_eq!( script_evaluation(&mut agent, gc.reborrow(), script).unwrap(), false.into() ); - let source_text = String::from_static_str(&mut agent, "([]) instanceof Object"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "([]) instanceof Object"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert_eq!( script_evaluation(&mut agent, gc.reborrow(), script).unwrap(), true.into() ); - let source_text = String::from_static_str(&mut agent, "([]) instanceof Array"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_static_str(&mut agent, gc.nogc(), "([]) instanceof Array"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); assert_eq!( script_evaluation(&mut agent, gc.reborrow(), script).unwrap(), true.into() @@ -1601,12 +1695,13 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "const [a, b, , c] = [1, 2, 3, 4];"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "const [a, b, , c] = [1, 2, 3, 4];"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - let a_key = String::from_static_str(&mut agent, "a"); - let b_key = String::from_static_str(&mut agent, "b"); - let c_key = String::from_static_str(&mut agent, "c"); + let a_key = String::from_static_str(&mut agent, gc.nogc(), "a").unbind(); + let b_key = String::from_static_str(&mut agent, gc.nogc(), "b").unbind(); + let c_key = String::from_static_str(&mut agent, gc.nogc(), "c").unbind(); let global_env = agent.get_realm(realm).global_env.unwrap(); assert!(global_env.has_lexical_declaration(&agent, a_key)); assert!(global_env.has_lexical_declaration(&agent, b_key)); @@ -1640,13 +1735,14 @@ mod test { let realm = agent.current_realm_id(); let source_text = - String::from_static_str(&mut agent, "let i = 0; do { i++ } while(i < 10)"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + String::from_static_str(&mut agent, gc.nogc(), "let i = 0; do { i++ } while(i < 10)"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); - let i_key = String::from_static_str(&mut agent, "i"); + let i_key = String::from_static_str(&mut agent, gc.nogc(), "i"); let global_env = agent.get_realm(realm).global_env.unwrap(); assert!(global_env.has_lexical_declaration(&agent, i_key)); + let i_key = i_key.unbind(); assert_eq!( global_env .get_binding_value(&mut agent, gc, i_key, true) @@ -1663,8 +1759,9 @@ mod test { initialize_default_realm(&mut agent, gc.reborrow()); let realm = agent.current_realm_id(); - let source_text = String::from_static_str(&mut agent, "function foo() { 42; }; foo()"); - let script = parse_script(&mut agent, source_text, realm, false, None).unwrap(); + let source_text = + String::from_static_str(&mut agent, gc.nogc(), "function foo() { 42; }; foo()"); + let script = parse_script(&mut agent, gc.nogc(), source_text, realm, false, None).unwrap(); let result = script_evaluation(&mut agent, gc.reborrow(), script).unwrap(); assert_eq!(result, Value::Undefined); } 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 1194b5fe8..87f51d3ea 100644 --- a/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs +++ b/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs @@ -21,12 +21,13 @@ use crate::{ execution::Agent, types::{HeapString, String}, }, + engine::context::NoGcScope, heap::{ indexes::BaseIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, }; -type SourceCodeIndex = BaseIndex; +type SourceCodeIndex = BaseIndex<'static, SourceCodeHeapData>; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub(crate) struct SourceCode(SourceCodeIndex); @@ -41,6 +42,7 @@ impl SourceCode { /// they drop the parsed code. pub(crate) unsafe fn parse_source( agent: &mut Agent, + gc: NoGcScope, source: String, source_type: SourceType, ) -> Result<(Program<'static>, Self), Vec> { @@ -55,14 +57,14 @@ impl SourceCode { // Thus the source text is kept from garbage collection. let source_text = unsafe { std::mem::transmute::<&str, &'static str>(source.as_str(agent)) }; - (source, source_text) + (source.unbind(), source_text) } String::SmallString(source) => { // Add 10 whitespace bytes to the end of the eval string. This // should guarantee that the string gets heap-allocated. let original_length = source.len(); let data = format!("{} ", source.as_str()); - let source = String::from_string(agent, data); + let source = String::from_string(agent, gc, data); let String::String(source) = source else { unreachable!() }; @@ -115,7 +117,10 @@ impl SourceCode { // SAFETY: Caller guarantees that they will drop the Program before // SourceCode can be garbage collected. let program = unsafe { std::mem::transmute::, Program<'static>>(program) }; - let source_code = agent.heap.create(SourceCodeHeapData { source, allocator }); + let source_code = agent.heap.create(SourceCodeHeapData { + source: source.unbind(), + allocator, + }); Ok((program, source_code)) } @@ -135,7 +140,7 @@ pub(crate) struct SourceCodeHeapData { /// in the eval call may keep references to the string data. If the eval /// string was small-string optimised and on the stack, then those /// references would necessarily and definitely be invalid. - source: HeapString, + source: HeapString<'static>, /// The arena that contains the parsed data of the eval source. allocator: NonNull, } 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 4f9f3435a..290e528d8 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/class_definitions.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/class_definitions.rs @@ -20,7 +20,6 @@ use crate::{ pub(crate) fn base_class_default_constructor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - new_target: Object, ) -> JsResult { // ii. If NewTarget is undefined, throw a TypeError exception. @@ -49,7 +48,6 @@ pub(crate) fn base_class_default_constructor( pub(crate) fn derived_class_default_constructor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - args: ArgumentsList, new_target: Object, ) -> JsResult { @@ -71,6 +69,7 @@ pub(crate) fn derived_class_default_constructor( // 3. If IsConstructor(func) is false, throw a TypeError exception. let Some(func) = func.and_then(|func| is_constructor(agent, func)) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Expected callable function", )); 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 d1159408b..deb8bafad 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.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::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::operations_on_objects::define_property_or_throw, @@ -96,7 +96,6 @@ impl ContainsExpression for ast::ArrayPattern<'_> { pub(crate) fn instantiate_ordinary_function_object( agent: &mut Agent, gc: GcScope<'_, '_>, - function: &ast::Function<'_>, env: EnvironmentIndex, private_env: Option, @@ -106,7 +105,7 @@ pub(crate) fn instantiate_ordinary_function_object( // 1. Let name be StringValue of BindingIdentifier. let name = &id.name; // 4. Perform SetFunctionName(F, name). - PropertyKey::from_str(agent, name) + PropertyKey::from_str(agent, gc.nogc(), name) } else { // 3. Perform SetFunctionName(F, "default"). PropertyKey::from(BUILTIN_STRING_MEMORY.default) @@ -128,10 +127,10 @@ pub(crate) fn instantiate_ordinary_function_object( env, private_env, }; - let f = ordinary_function_create(agent, params); + let f = ordinary_function_create(agent, gc.nogc(), params); // 4. Perform SetFunctionName(F, name). - set_function_name(agent, f, pk_name, None); + set_function_name(agent, gc.nogc(), f, pk_name, None); // 5. Perform MakeConstructor(F). if !function.r#async && !function.generator { make_constructor(agent, f, None, None); @@ -182,6 +181,7 @@ pub(crate) fn instantiate_ordinary_function_object( pub(crate) fn instantiate_ordinary_function_expression( agent: &mut Agent, + gc: NoGcScope, function: &FunctionExpression, name: Option, ) -> ECMAScriptFunction { @@ -217,10 +217,10 @@ pub(crate) fn instantiate_ordinary_function_expression( env: lexical_environment, private_env: private_environment, }; - let closure = ordinary_function_create(agent, params); + let closure = ordinary_function_create(agent, gc, params); // 6. Perform SetFunctionName(closure, name). let name = PropertyKey::from(name); - set_function_name(agent, closure, name, None); + set_function_name(agent, gc, closure, name, None); // 7. Perform MakeConstructor(closure). if !function.expression.get().r#async && !function.expression.get().generator { make_constructor(agent, closure, None, None); @@ -266,7 +266,6 @@ impl CompileFunctionBodyData<'static> { pub(crate) fn evaluate_function_body( agent: &mut Agent, gc: GcScope<'_, '_>, - function_object: ECMAScriptFunction, arguments_list: ArgumentsList, ) -> JsResult { @@ -277,7 +276,7 @@ pub(crate) fn evaluate_function_body( exe } else { let data = CompileFunctionBodyData::new(agent, function_object); - let exe = Executable::compile_function_body(agent, data); + let exe = Executable::compile_function_body(agent, gc.nogc(), data); agent[function_object].compiled_bytecode = Some(exe); exe }; @@ -288,7 +287,6 @@ pub(crate) fn evaluate_function_body( pub(crate) fn evaluate_async_function_body( agent: &mut Agent, mut gc: GcScope<'_, '_>, - function_object: ECMAScriptFunction, arguments_list: ArgumentsList, ) -> Promise { @@ -306,7 +304,7 @@ pub(crate) fn evaluate_async_function_body( exe } else { let data = CompileFunctionBodyData::new(agent, function_object); - let exe = Executable::compile_function_body(agent, data); + let exe = Executable::compile_function_body(agent, gc.nogc(), data); agent[function_object].compiled_bytecode = Some(exe); exe }; @@ -361,8 +359,7 @@ pub(crate) fn evaluate_async_function_body( /// completion. pub(crate) fn evaluate_generator_body( agent: &mut Agent, - gc: GcScope<'_, '_>, - + mut gc: GcScope<'_, '_>, function_object: ECMAScriptFunction, arguments_list: ArgumentsList, ) -> JsResult { @@ -375,7 +372,7 @@ pub(crate) fn evaluate_generator_body( // 3. Set G.[[GeneratorBrand]] to empty. let generator = ordinary_create_from_constructor( agent, - gc, + gc.reborrow(), function_object.into_function(), ProtoIntrinsics::Generator, )?; @@ -386,7 +383,7 @@ pub(crate) fn evaluate_generator_body( // 4. Perform GeneratorStart(G, FunctionBody). // SAFETY: We're alive so SourceCode must be too. let data = CompileFunctionBodyData::new(agent, function_object); - let executable = Executable::compile_function_body(agent, data); + let executable = Executable::compile_function_body(agent, gc.nogc(), data); agent[generator].generator_state = Some(GeneratorState::Suspended { vm_or_args: VmOrArguments::Arguments(arguments_list.0.into()), executable, diff --git a/nova_vm/src/ecmascript/syntax_directed_operations/miscellaneous.rs b/nova_vm/src/ecmascript/syntax_directed_operations/miscellaneous.rs index aeb17f41f..66016f77b 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/miscellaneous.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/miscellaneous.rs @@ -24,7 +24,6 @@ use oxc_ast::ast; pub(crate) fn instantiate_function_object( agent: &mut Agent, gc: GcScope<'_, '_>, - function: &ast::Function<'_>, env: EnvironmentIndex, private_env: Option, diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index c9ee08d9f..c8e3d3389 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -11,7 +11,10 @@ use super::{ }; use crate::{ ecmascript::execution::{agent::ExceptionType, Agent, JsResult}, - engine::rootable::{HeapRootData, HeapRootRef, Rootable}, + engine::{ + context::{GcScope, NoGcScope}, + rootable::{HeapRootData, HeapRootRef, Rootable}, + }, heap::{ indexes::BigIntIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, PrimitiveHeap, WorkQueues, @@ -207,6 +210,26 @@ pub enum BigIntRootRepr { } impl BigInt { + /// Unbind this BigInt from its current lifetime. This is necessary to use + /// the BigInt as a parameter in a call that can perform garbage + /// collection. + pub fn unbind(self) -> Self { + self + } + + // Bind this BigInt to the garbage collection lifetime. This enables Rust's + // borrow checker to verify that your BigInts cannot not be invalidated by + // garbage collection being performed. + // + // This function is best called with the form + // ```rs + // let bigint = bigint.bind(&gc); + // ``` + // to make sure that the unbound BigInt cannot be used after binding. + pub fn bind(self, _: &GcScope<'_, '_>) -> Self { + self + } + pub const fn zero() -> Self { Self::SmallBigInt(SmallBigInt::zero()) } @@ -277,6 +300,7 @@ impl BigInt { /// containing a BigInt or a throw completion. pub(crate) fn exponentiate( agent: &mut Agent, + gc: NoGcScope, base: BigInt, exponent: BigInt, ) -> JsResult { @@ -287,6 +311,7 @@ impl BigInt { _ => false, } { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "exponent must be positive", )); @@ -294,12 +319,14 @@ impl BigInt { let BigInt::SmallBigInt(exponent) = exponent else { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "exponent over bounds", )); }; let Ok(exponent) = u32::try_from(exponent.into_i64()) else { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "exponent over bounds", )); @@ -424,12 +451,18 @@ impl BigInt { } /// ### [6.1.6.2.5 BigInt::divide ( x, y )](https://tc39.es/ecma262/#sec-numeric-types-bigint-divide) - pub(crate) fn divide(agent: &mut Agent, x: BigInt, y: BigInt) -> JsResult { + pub(crate) fn divide( + agent: &mut Agent, + gc: NoGcScope, + x: BigInt, + y: BigInt, + ) -> JsResult { match (x, y) { (BigInt::SmallBigInt(x), BigInt::SmallBigInt(y)) => { let y = y.into_i64(); match y { 0 => Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "Division by zero", )), @@ -449,6 +482,7 @@ impl BigInt { let y = y.into_i64(); match y { 0 => Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "Division by zero", )), @@ -481,11 +515,17 @@ impl BigInt { } /// ### [6.1.6.2.6 BigInt::remainder ( n, d )](https://tc39.es/ecma262/#sec-numeric-types-bigint-remainder) - pub(crate) fn remainder(agent: &mut Agent, n: BigInt, d: BigInt) -> JsResult { + pub(crate) fn remainder( + agent: &mut Agent, + gc: NoGcScope, + n: BigInt, + d: BigInt, + ) -> JsResult { match (n, d) { (BigInt::SmallBigInt(n), BigInt::SmallBigInt(d)) => { if d == SmallBigInt::zero() { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "Division by zero", )); @@ -501,6 +541,7 @@ impl BigInt { (BigInt::BigInt(n), BigInt::SmallBigInt(d)) => { if d == SmallBigInt::zero() { return Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "Division by zero", )); @@ -538,14 +579,19 @@ impl BigInt { } // ### [6.1.6.2.21 BigInt::toString ( x, radix )](https://tc39.es/ecma262/#sec-numeric-types-bigint-tostring) - pub(crate) fn to_string_radix_10(agent: &mut Agent, x: Self) -> JsResult { - Ok(String::from_string( + pub(crate) fn to_string_radix_10<'gc>( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + x: Self, + ) -> String<'gc> { + String::from_string( agent, + gc, match x { BigInt::SmallBigInt(x) => x.into_i64().to_string(), BigInt::BigInt(x) => agent[x].data.to_string(), }, - )) + ) } } diff --git a/nova_vm/src/ecmascript/types/language/function.rs b/nova_vm/src/ecmascript/types/language/function.rs index 22a169ada..2783a14a8 100644 --- a/nova_vm/src/ecmascript/types/language/function.rs +++ b/nova_vm/src/ecmascript/types/language/function.rs @@ -173,6 +173,26 @@ impl From for Value { } impl Function { + /// 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) -> Self { + 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 fn bind(self, _: &GcScope<'_, '_>) -> Self { + self + } + pub(crate) const fn new_builtin_function(idx: BuiltinFunctionIndex) -> Self { Self::BuiltinFunction(BuiltinFunction(idx)) } @@ -265,7 +285,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - prototype: Option, ) -> JsResult { match self { @@ -316,7 +335,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { match self { @@ -339,7 +357,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -369,7 +386,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { match self { @@ -392,7 +408,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -416,7 +431,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -445,7 +459,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { match self { @@ -483,7 +496,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - this_argument: Value, arguments_list: ArgumentsList, ) -> JsResult { @@ -511,7 +523,6 @@ impl InternalMethods for Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { @@ -571,7 +582,6 @@ impl Function { self, agent: &mut Agent, gc: GcScope<'_, '_>, - this_argument: Value, args: &[Value], ) -> JsResult { diff --git a/nova_vm/src/ecmascript/types/language/function/data.rs b/nova_vm/src/ecmascript/types/language/function/data.rs index 7b482b0e2..ba4347dcd 100644 --- a/nova_vm/src/ecmascript/types/language/function/data.rs +++ b/nova_vm/src/ecmascript/types/language/function/data.rs @@ -35,7 +35,7 @@ pub struct BoundFunctionHeapData { /// A list of values whose elements are used as the first arguments to any /// call to the wrapped function. pub(crate) bound_arguments: ElementsVector, - pub(crate) name: Option, + pub(crate) name: Option>, } #[derive(Debug, Clone)] @@ -49,7 +49,7 @@ pub struct BuiltinFunctionHeapData { /// #### \[\[InitialName]] /// A String that is the initial name of the function. It is used by /// 20.2.3.5 (`Function.prototype.toString()`). - pub(crate) initial_name: Option, + pub(crate) initial_name: Option>, pub(crate) behaviour: Behaviour, } @@ -92,7 +92,7 @@ pub struct ECMAScriptFunctionHeapData { pub(crate) ecmascript_function: ECMAScriptFunctionObjectHeapData, /// Stores the compiled bytecode of an ECMAScript function. pub(crate) compiled_bytecode: Option, - pub(crate) name: Option, + pub(crate) name: Option>, } unsafe impl Send for ECMAScriptFunctionHeapData {} 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 d14f06e0d..07728f20b 100644 --- a/nova_vm/src/ecmascript/types/language/function/into_function.rs +++ b/nova_vm/src/ecmascript/types/language/function/into_function.rs @@ -110,7 +110,6 @@ pub(crate) fn function_internal_define_own_property( func: impl FunctionInternalProperties, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -124,7 +123,6 @@ pub(crate) fn function_internal_has_property( func: impl FunctionInternalProperties, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { if let Some(backing_object) = func.get_backing_object(agent) { @@ -145,7 +143,6 @@ pub(crate) fn function_internal_get( func: impl FunctionInternalProperties, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -167,7 +164,6 @@ pub(crate) fn function_internal_set( func: impl FunctionInternalProperties, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -189,7 +185,6 @@ pub(crate) fn function_internal_delete( func: impl FunctionInternalProperties, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { if let Some(backing_object) = func.get_backing_object(agent) { diff --git a/nova_vm/src/ecmascript/types/language/number.rs b/nova_vm/src/ecmascript/types/language/number.rs index 02332ce67..2332cdd77 100644 --- a/nova_vm/src/ecmascript/types/language/number.rs +++ b/nova_vm/src/ecmascript/types/language/number.rs @@ -10,7 +10,10 @@ use super::{ value::{FLOAT_DISCRIMINANT, INTEGER_DISCRIMINANT, NUMBER_DISCRIMINANT}, IntoNumeric, IntoPrimitive, IntoValue, Numeric, Primitive, String, Value, }; -use crate::ecmascript::abstract_operations::type_conversion::{to_int32_number, to_uint32_number}; +use crate::{ + ecmascript::abstract_operations::type_conversion::{to_int32_number, to_uint32_number}, + engine::context::{GcScope, NoGcScope}, +}; use crate::{ ecmascript::execution::Agent, engine::{ @@ -232,6 +235,26 @@ impl TryFrom for Number { } impl Number { + /// Unbind this Number from its current lifetime. This is necessary to use + /// the Number as a parameter in a call that can perform garbage + /// collection. + pub fn unbind(self) -> Self { + self + } + + // Bind this Number to the garbage collection lifetime. This enables Rust's + // borrow checker to verify that your Numbers 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 Number cannot be used after binding. + pub fn bind(self, _: &GcScope<'_, '_>) -> Self { + self + } + pub fn from_f64(agent: &mut Agent, value: f64) -> Self { if let Ok(value) = Number::try_from(value) { value @@ -1230,19 +1253,24 @@ impl Number { } // ### [6.1.6.1.20 Number::toString ( x, radix )](https://tc39.es/ecma262/#sec-numeric-types-number-tostring) - pub(crate) fn to_string_radix_10(agent: &mut Agent, x: Self) -> String { + pub(crate) fn to_string_radix_10<'gc>( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + x: Self, + ) -> String<'gc> { match x { Number::Number(_) => { let mut buffer = ryu_js::Buffer::new(); - String::from_string(agent, buffer.format(x.into_f64(agent)).to_string()) + String::from_string(agent, gc, buffer.format(x.into_f64(agent)).to_string()) + .bind(gc) } Number::Integer(x) => { let x = x.into_i64(); - String::from_string(agent, format!("{x}")) + String::from_string(agent, gc, format!("{x}")).bind(gc) } Number::SmallF64(x) => { let mut buffer = ryu_js::Buffer::new(); - String::from_string(agent, buffer.format(x.into_f64()).to_string()) + String::from_string(agent, gc, buffer.format(x.into_f64()).to_string()).bind(gc) } } } diff --git a/nova_vm/src/ecmascript/types/language/numeric.rs b/nova_vm/src/ecmascript/types/language/numeric.rs index 6b6ed62f3..50ee9d6b7 100644 --- a/nova_vm/src/ecmascript/types/language/numeric.rs +++ b/nova_vm/src/ecmascript/types/language/numeric.rs @@ -45,6 +45,26 @@ pub enum NumericRootRepr { } impl Numeric { + /// Unbind this Numeric from its current lifetime. This is necessary to use + /// the Numeric as a parameter in a call that can perform garbage + /// collection. + pub fn unbind(self) -> Self { + self + } + + // Bind this Numeric to the garbage collection lifetime. This enables + // Rust's borrow checker to verify that your Numerics cannot not be + // invalidated by garbage collection being performed. + // + // This function is best called with the form + // ```rs + // let numeric = numeric.bind(&gc); + // ``` + // to make sure that the unbound Numeric cannot be used after binding. + pub fn bind(self, _: &GcScope<'_, '_>) -> Self { + self + } + pub fn is_bigint(self) -> bool { matches!(self, Self::BigInt(_) | Self::SmallBigInt(_)) } diff --git a/nova_vm/src/ecmascript/types/language/object.rs b/nova_vm/src/ecmascript/types/language/object.rs index 74a807dea..745ce5b76 100644 --- a/nova_vm/src/ecmascript/types/language/object.rs +++ b/nova_vm/src/ecmascript/types/language/object.rs @@ -532,6 +532,26 @@ impl TryFrom for Object { } impl Object { + /// 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) -> Self { + 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 fn bind(self, _: &GcScope<'_, '_>) -> Self { + self + } + pub fn into_value(self) -> Value { self.into() } @@ -1073,7 +1093,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - prototype: Option, ) -> JsResult { match self { @@ -1365,7 +1384,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { match self { @@ -1476,7 +1494,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -1638,7 +1655,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { match self { @@ -1742,7 +1758,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -1849,7 +1864,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -1995,7 +2009,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { match self { @@ -2195,7 +2208,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - this_value: Value, arguments_list: ArgumentsList, ) -> JsResult { @@ -2218,7 +2230,6 @@ impl InternalMethods for Object { self, agent: &mut Agent, gc: GcScope<'_, '_>, - arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { 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 a214172cb..e4d59293f 100644 --- a/nova_vm/src/ecmascript/types/language/object/internal_methods.rs +++ b/nova_vm/src/ecmascript/types/language/object/internal_methods.rs @@ -48,7 +48,6 @@ where agent: &mut Agent, // Note: Because of Proxies, this can trigger GC. _gc: GcScope<'_, '_>, - prototype: Option, ) -> JsResult { match self.get_backing_object(agent) { @@ -130,7 +129,6 @@ where self, agent: &mut Agent, _gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult> { // 1. Return OrdinaryGetOwnProperty(O, P). @@ -149,7 +147,6 @@ where self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -165,7 +162,6 @@ where self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { // 1. Return ? OrdinaryHasProperty(O, P). @@ -194,7 +190,6 @@ where self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -225,7 +220,6 @@ where self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, value: Value, receiver: Value, @@ -243,7 +237,6 @@ where self, agent: &mut Agent, gc: GcScope<'_, '_>, - property_key: PropertyKey, ) -> JsResult { // 1. Return ? OrdinaryDelete(O, P). @@ -273,7 +266,6 @@ where self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _this_value: Value, _arguments_list: ArgumentsList, ) -> JsResult { @@ -285,7 +277,6 @@ where self, _agent: &mut Agent, _gc: GcScope<'_, '_>, - _arguments_list: ArgumentsList, _new_target: Function, ) -> JsResult { 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 b7dcb767a..4d883595b 100644 --- a/nova_vm/src/ecmascript/types/language/object/property_key.rs +++ b/nova_vm/src/ecmascript/types/language/object/property_key.rs @@ -17,6 +17,7 @@ use crate::{ String, Symbol, Value, }, }, + engine::context::NoGcScope, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, SmallInteger, SmallString, }; @@ -26,26 +27,26 @@ use crate::{ pub enum PropertyKey { Integer(SmallInteger) = INTEGER_DISCRIMINANT, SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, - String(HeapString) = STRING_DISCRIMINANT, + String(HeapString<'static>) = STRING_DISCRIMINANT, Symbol(Symbol) = SYMBOL_DISCRIMINANT, // TODO: PrivateKey } impl PropertyKey { // FIXME: This API is not necessarily in the right place. - pub fn from_str(agent: &mut Agent, str: &str) -> Self { + pub fn from_str(agent: &mut Agent, gc: NoGcScope, str: &str) -> Self { parse_string_to_integer_property_key(str) - .unwrap_or_else(|| String::from_str(agent, str).into()) + .unwrap_or_else(|| String::from_str(agent, gc, str).into()) } - pub fn from_static_str(agent: &mut Agent, str: &'static str) -> Self { + pub fn from_static_str(agent: &mut Agent, gc: NoGcScope, str: &'static str) -> Self { parse_string_to_integer_property_key(str) - .unwrap_or_else(|| String::from_static_str(agent, str).into()) + .unwrap_or_else(|| String::from_static_str(agent, gc, str).into()) } - pub fn from_string(agent: &mut Agent, string: std::string::String) -> Self { + pub fn from_string(agent: &mut Agent, gc: NoGcScope, string: std::string::String) -> Self { parse_string_to_integer_property_key(&string) - .unwrap_or_else(|| String::from_string(agent, string).into()) + .unwrap_or_else(|| String::from_string(agent, gc, string).into()) } pub fn into_value(self) -> Value { @@ -81,7 +82,7 @@ impl PropertyKey { s1.as_str() == s2.as_str() } (PropertyKey::String(s), PropertyKey::Integer(n)) => { - let s = agent[s].as_str(); + let s = agent[s.unbind()].as_str(); Self::is_str_eq_num(s, n.into_i64()) } @@ -177,10 +178,10 @@ impl From for PropertyKey { } } -impl From for PropertyKey { +impl From> for PropertyKey { fn from(value: String) -> Self { match value { - String::String(x) => PropertyKey::String(x), + String::String(x) => PropertyKey::String(x.unbind()), String::SmallString(x) => PropertyKey::SmallString(x), } } diff --git a/nova_vm/src/ecmascript/types/language/primitive.rs b/nova_vm/src/ecmascript/types/language/primitive.rs index 75e154c2c..b08000bc4 100644 --- a/nova_vm/src/ecmascript/types/language/primitive.rs +++ b/nova_vm/src/ecmascript/types/language/primitive.rs @@ -6,6 +6,7 @@ use small_string::SmallString; use crate::{ engine::{ + context::GcScope, rootable::{HeapRootData, HeapRootRef, Rootable}, small_f64::SmallF64, }, @@ -39,7 +40,7 @@ pub enum Primitive { /// 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) = STRING_DISCRIMINANT, + String(HeapString<'static>) = STRING_DISCRIMINANT, /// ### [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 @@ -98,7 +99,7 @@ pub(crate) enum HeapPrimitive { /// 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) = STRING_DISCRIMINANT, + String(HeapString<'static>) = 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. @@ -152,6 +153,26 @@ impl IntoValue for Primitive { } impl Primitive { + /// Unbind this Primitive from its current lifetime. This is necessary to + /// use the Primitive as a parameter in a call that can perform garbage + /// collection. + pub fn unbind(self) -> Self { + self + } + + // Bind this Primitive to the garbage collection lifetime. This enables + // Rust's borrow checker to verify that your Primitives 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 Primitive cannot be used after binding. + pub fn bind(self, _: &GcScope<'_, '_>) -> Self { + self + } + pub fn is_boolean(self) -> bool { matches!(self, Self::Boolean(_)) } diff --git a/nova_vm/src/ecmascript/types/language/string.rs b/nova_vm/src/ecmascript/types/language/string.rs index 12ba710db..36d609251 100644 --- a/nova_vm/src/ecmascript/types/language/string.rs +++ b/nova_vm/src/ecmascript/types/language/string.rs @@ -13,7 +13,11 @@ use super::{ }; use crate::{ ecmascript::{execution::Agent, types::PropertyDescriptor}, - engine::rootable::{HeapRootData, HeapRootRef, Rootable}, + engine::{ + context::NoGcScope, + rootable::{HeapRootData, HeapRootRef, Rootable}, + Scoped, + }, heap::{ indexes::StringIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, PrimitiveHeap, WorkQueues, @@ -25,9 +29,29 @@ pub use data::StringHeapData; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] -pub struct HeapString(pub(crate) StringIndex); +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 { std::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 { std::mem::transmute::, HeapString<'gc>>(self) } + } -impl HeapString { pub fn len(self, agent: &Agent) -> usize { agent[self].len() } @@ -45,32 +69,32 @@ impl HeapString { } } -impl Index for PrimitiveHeap<'_> { +impl Index> for PrimitiveHeap<'_> { type Output = StringHeapData; - fn index(&self, index: HeapString) -> &Self::Output { + fn index(&self, index: HeapString<'_>) -> &Self::Output { &self.strings[index] } } -impl Index for Agent { +impl Index> for Agent { type Output = StringHeapData; - fn index(&self, index: HeapString) -> &Self::Output { + fn index(&self, index: HeapString<'_>) -> &Self::Output { &self.heap.strings[index] } } -impl IndexMut for Agent { - fn index_mut(&mut self, index: HeapString) -> &mut Self::Output { +impl IndexMut> for Agent { + fn index_mut(&mut self, index: HeapString<'_>) -> &mut Self::Output { &mut self.heap.strings[index] } } -impl Index for Vec> { +impl Index> for Vec> { type Output = StringHeapData; - fn index(&self, index: HeapString) -> &Self::Output { + fn index(&self, index: HeapString<'_>) -> &Self::Output { self.get(index.get_index()) .expect("HeapString out of bounds") .as_ref() @@ -78,8 +102,8 @@ impl Index for Vec> { } } -impl IndexMut for Vec> { - fn index_mut(&mut self, index: HeapString) -> &mut Self::Output { +impl IndexMut> for Vec> { + fn index_mut(&mut self, index: HeapString<'_>) -> &mut Self::Output { self.get_mut(index.get_index()) .expect("HeapString out of bounds") .as_mut() @@ -90,8 +114,8 @@ impl IndexMut for Vec> { /// ### [6.1.4 The String Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] -pub enum String { - String(HeapString) = STRING_DISCRIMINANT, +pub enum String<'a> { + String(HeapString<'a>) = STRING_DISCRIMINANT, SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, } @@ -102,13 +126,13 @@ pub enum StringRootRepr { HeapRef(HeapRootRef) = 0x80, } -impl IntoValue for HeapString { +impl IntoValue for HeapString<'_> { fn into_value(self) -> Value { - Value::String(self) + Value::String(self.unbind()) } } -impl TryFrom for HeapString { +impl TryFrom for HeapString<'_> { type Error = (); fn try_from(value: Value) -> Result { @@ -120,44 +144,44 @@ impl TryFrom for HeapString { } } -impl IntoValue for String { +impl IntoValue for String<'_> { fn into_value(self) -> Value { match self { - String::String(idx) => Value::String(idx), + String::String(idx) => Value::String(idx.unbind()), String::SmallString(data) => Value::SmallString(data), } } } -impl IntoPrimitive for String { +impl IntoPrimitive for String<'_> { fn into_primitive(self) -> Primitive { match self { - String::String(idx) => Primitive::String(idx), + String::String(idx) => Primitive::String(idx.unbind()), String::SmallString(data) => Primitive::SmallString(data), } } } -impl From for String { - fn from(value: HeapString) -> Self { +impl<'a> From> for String<'a> { + fn from(value: HeapString<'a>) -> Self { String::String(value) } } -impl From for Primitive { - fn from(value: HeapString) -> Self { - Self::String(value) +impl From> for Primitive { + fn from(value: HeapString<'_>) -> Self { + Self::String(value.unbind()) } } -impl TryFrom<&str> for String { +impl TryFrom<&str> for String<'static> { type Error = (); fn try_from(value: &str) -> Result { SmallString::try_from(value).map(String::SmallString) } } -impl TryFrom for String { +impl TryFrom for String<'_> { type Error = (); fn try_from(value: Value) -> Result { match value { @@ -168,7 +192,7 @@ impl TryFrom for String { } } -impl TryFrom for String { +impl TryFrom for String<'_> { type Error = (); fn try_from(value: Primitive) -> Result { match value { @@ -179,10 +203,10 @@ impl TryFrom for String { } } -impl From for Value { +impl From> for Value { fn from(value: String) -> Self { match value { - String::String(x) => Value::String(x), + String::String(x) => Value::String(x.unbind()), String::SmallString(x) => Value::SmallString(x), } } @@ -194,7 +218,7 @@ impl From for Value { } } -impl From for String { +impl From for String<'static> { fn from(value: SmallString) -> Self { Self::SmallString(value) } @@ -218,38 +242,49 @@ impl IntoPrimitive for SmallString { } } -impl String { - pub const EMPTY_STRING: String = String::from_small_string(""); +impl String<'_> { + pub const EMPTY_STRING: String<'static> = String::from_small_string(""); - pub fn is_empty_string(self) -> bool { - self == Self::EMPTY_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 { std::mem::transmute::, String<'static>>(self) } } - pub fn from_str(agent: &mut Agent, str: &str) -> String { - agent.heap.create(str) + // 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 { std::mem::transmute::, String<'gc>>(self) } } - pub fn from_string(agent: &mut Agent, string: std::string::String) -> String { - agent.heap.create(string) + pub fn scope<'scope>( + self, + agent: &mut Agent, + gc: NoGcScope<'_, 'scope>, + ) -> Scoped<'scope, String<'static>> { + Scoped::new(agent, gc, self.unbind()) + } + + pub fn is_empty_string(self) -> bool { + self == Self::EMPTY_STRING } pub const fn to_property_key(self) -> PropertyKey { match self { - String::String(data) => PropertyKey::String(data), + String::String(data) => PropertyKey::String(data.unbind()), String::SmallString(data) => PropertyKey::SmallString(data), } } - pub fn from_static_str(agent: &mut Agent, str: &'static str) -> Self { - if let Ok(value) = String::try_from(str) { - value - } else { - // SAFETY: String couldn't be represented as a SmallString. - unsafe { agent.heap.alloc_static_str(str) } - } - } - - pub const fn from_small_string(message: &'static str) -> String { + pub const fn from_small_string(message: &'static str) -> Self { assert!( message.len() < 8 && (message.is_empty() || message.as_bytes()[message.as_bytes().len() - 1] != 0) @@ -257,16 +292,20 @@ impl String { String::SmallString(SmallString::from_str_unchecked(message)) } - pub fn concat(agent: &mut Agent, strings: impl AsRef<[String]>) -> String { + pub fn concat<'gc>( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + strings: impl AsRef<[Self]>, + ) -> String<'gc> { // TODO: This function will need heavy changes once we support creating // WTF-8 strings, since WTF-8 concatenation isn't byte concatenation. // We use this status enum so we can reuse one of the heap string inputs // if the output would be identical, and so we don't allocate at all // until it's clear we need a new heap string. - enum Status { + enum Status<'a> { Empty, - ExistingString(HeapString), + ExistingString(HeapString<'a>), SmallString { data: [u8; 7], len: usize }, String(std::string::String), } @@ -318,14 +357,14 @@ impl String { match status { Status::Empty => String::EMPTY_STRING, - Status::ExistingString(idx) => String::String(idx), + Status::ExistingString(idx) => String::String(idx.bind(gc)), Status::SmallString { data, len } => { // SAFETY: Since SmallStrings are guaranteed UTF-8, `&data[..len]` is the result of // concatenating UTF-8 strings, which is always valid UTF-8. let str_slice = unsafe { std::str::from_utf8_unchecked(&data[..len]) }; SmallString::from_str_unchecked(str_slice).into() } - Status::String(string) => agent.heap.create(string), + Status::String(string) => agent.heap.create(string).bind(gc), } } @@ -334,17 +373,20 @@ impl String { } /// Byte length of the string. - pub fn len(self, agent: &impl Index) -> usize { + pub fn len(self, agent: &impl Index, Output = StringHeapData>) -> usize { match self { - String::String(s) => agent[s].len(), + String::String(s) => agent[s.unbind()].len(), String::SmallString(s) => s.len(), } } /// UTF-16 length of the string. - pub fn utf16_len(self, agent: &impl Index) -> usize { + pub fn utf16_len( + self, + agent: &impl Index, Output = StringHeapData>, + ) -> usize { match self { - String::String(s) => agent[s].utf16_len(), + String::String(s) => agent[s.unbind()].utf16_len(), String::SmallString(s) => s.utf16_len(), } } @@ -352,11 +394,11 @@ impl String { // TODO: This should return a wtf8::CodePoint. pub fn utf16_char( self, - agent: &impl Index, + agent: &impl Index, Output = StringHeapData>, idx: usize, ) -> char { match self { - String::String(s) => agent[s].utf16_char(idx), + String::String(s) => agent[s.unbind()].utf16_char(idx), String::SmallString(s) => s.utf16_char(idx), } } @@ -371,11 +413,11 @@ impl String { /// UTF-16 string length. pub fn utf8_index( self, - agent: &impl Index, + agent: &impl Index, Output = StringHeapData>, utf16_idx: usize, ) -> Option { match self { - String::String(s) => agent[s].utf8_index(utf16_idx), + String::String(s) => agent[s.unbind()].utf8_index(utf16_idx), String::SmallString(s) => s.utf8_index(utf16_idx), } } @@ -389,21 +431,21 @@ impl String { /// or if it is past the end (but not *at* the end) of the UTF-8 string. pub fn utf16_index( self, - agent: &impl Index, + agent: &impl Index, Output = StringHeapData>, utf8_idx: usize, ) -> usize { match self { - String::String(s) => agent[s].utf16_index(utf8_idx), + String::String(s) => agent[s.unbind()].utf16_index(utf8_idx), String::SmallString(s) => s.utf16_index(utf8_idx), } } pub fn as_str<'string, 'agent: 'string>( &'string self, - agent: &'agent impl Index, + agent: &'agent impl Index, Output = StringHeapData>, ) -> &'string str { match self { - String::String(s) => agent[*s].as_str(), + String::String(s) => agent[s.unbind()].as_str(), String::SmallString(s) => s.as_str(), } } @@ -411,17 +453,17 @@ impl String { /// If x and y have the same length and the same code units in the same /// positions, return true; otherwise, return false. pub fn eq( - agent: &impl Index, - x: String, - y: String, + agent: &impl Index, Output = StringHeapData>, + x: Self, + y: Self, ) -> bool { match (x, y) { - (String::String(x), String::String(y)) => { - let x = &agent[x]; - let y = &agent[y]; + (Self::String(x), Self::String(y)) => { + let x = &agent[x.unbind()]; + let y = &agent[y.unbind()]; x == y } - (String::SmallString(x), String::SmallString(y)) => x == y, + (Self::SmallString(x), Self::SmallString(y)) => x == y, // The string heap guarantees that small strings must never equal // heap strings. _ => false, @@ -465,14 +507,51 @@ impl String { } } -impl CreateHeapData for Heap { - fn create(&mut self, data: StringHeapData) -> String { +impl<'gc> String<'gc> { + pub fn from_str(agent: &mut Agent, _gc: NoGcScope<'gc, '_>, str: &str) -> Self { + agent.heap.create(str) + } + + pub fn from_string( + agent: &mut Agent, + gc: NoGcScope<'gc, '_>, + string: std::string::String, + ) -> Self { + agent.heap.create(string).bind(gc) + } + + pub fn from_static_str(agent: &mut Agent, _gc: NoGcScope<'gc, '_>, str: &'static str) -> Self { + if let Ok(value) = String::try_from(str) { + value + } else { + // SAFETY: String couldn't be represented as a SmallString. + unsafe { agent.heap.alloc_static_str(str) } + } + } +} + +impl Scoped<'_, String<'static>> { + pub fn as_str<'string, 'agent: 'string>(&'string self, agent: &'agent Agent) -> &'string str { + match &self.inner { + StringRootRepr::SmallString(small_string) => small_string.as_str(), + StringRootRepr::HeapRef(_) => { + let String::String(string) = self.get(agent) else { + unreachable!(); + }; + string.as_str(agent) + } + } + } +} + +impl CreateHeapData> for Heap { + fn create(&mut self, data: StringHeapData) -> String<'static> { self.strings.push(Some(data)); String::String(HeapString(StringIndex::last(&self.strings))) } } -impl HeapMarkAndSweep for String { +impl HeapMarkAndSweep for String<'static> { #[inline(always)] fn mark_values(&self, queues: &mut WorkQueues) { if let Self::String(idx) = self { @@ -488,7 +567,7 @@ impl HeapMarkAndSweep for String { } } -impl HeapMarkAndSweep for HeapString { +impl HeapMarkAndSweep for HeapString<'static> { fn mark_values(&self, queues: &mut WorkQueues) { queues.strings.push(*self); } @@ -498,7 +577,7 @@ impl HeapMarkAndSweep for HeapString { } } -impl Rootable for String { +impl Rootable for String<'static> { type RootRepr = StringRootRepr; #[inline] diff --git a/nova_vm/src/ecmascript/types/language/symbol.rs b/nova_vm/src/ecmascript/types/language/symbol.rs index bc19cb5e0..e89ce6403 100644 --- a/nova_vm/src/ecmascript/types/language/symbol.rs +++ b/nova_vm/src/ecmascript/types/language/symbol.rs @@ -10,7 +10,10 @@ pub use data::SymbolHeapData; use crate::{ ecmascript::{execution::Agent, types::String}, - engine::rootable::{HeapRootData, HeapRootRef, Rootable}, + engine::{ + context::{GcScope, NoGcScope}, + rootable::{HeapRootData, HeapRootRef, Rootable}, + }, heap::{ indexes::SymbolIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WellKnownSymbolIndexes, WorkQueues, LAST_WELL_KNOWN_SYMBOL_INDEX, @@ -36,6 +39,26 @@ enum SymbolRootReprInner { pub struct SymbolRootRepr(SymbolRootReprInner); impl Symbol { + /// Unbind this Symbol from its current lifetime. This is necessary to use + /// the Symbol as a parameter in a call that can perform garbage + /// collection. + pub fn unbind(self) -> Self { + self + } + + // Bind this Symbol to the garbage collection lifetime. This enables Rust's + // borrow checker to verify that your Symbols cannot not be invalidated by + // garbage collection being performed. + // + // This function is best called with the form + // ```rs + // let symbol = symbol.bind(&gc); + // ``` + // to make sure that the unbound Symbol cannot be used after binding. + pub fn bind(self, _: &GcScope<'_, '_>) -> Self { + self + } + pub(crate) const fn _def() -> Self { Self(SymbolIndex::from_u32_index(0)) } @@ -45,10 +68,11 @@ impl Symbol { } /// ### [20.4.3.3.1 SymbolDescriptiveString ( sym )](https://tc39.es/ecma262/#sec-symboldescriptivestring) - pub fn descriptive_string(self, agent: &mut Agent) -> String { + pub fn descriptive_string<'gc>(self, agent: &mut Agent, gc: NoGcScope<'gc, '_>) -> String<'gc> { if let Some(descriptor) = agent[self].descriptor { String::concat( agent, + gc, [ String::from_small_string("Symbol("), descriptor, diff --git a/nova_vm/src/ecmascript/types/language/symbol/data.rs b/nova_vm/src/ecmascript/types/language/symbol/data.rs index 455d51d6a..05d5e34f6 100644 --- a/nova_vm/src/ecmascript/types/language/symbol/data.rs +++ b/nova_vm/src/ecmascript/types/language/symbol/data.rs @@ -9,7 +9,7 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub struct SymbolHeapData { - pub(crate) descriptor: Option, + pub(crate) descriptor: Option>, } impl HeapMarkAndSweep for SymbolHeapData { diff --git a/nova_vm/src/ecmascript/types/language/value.rs b/nova_vm/src/ecmascript/types/language/value.rs index 439401579..6e085a4db 100644 --- a/nova_vm/src/ecmascript/types/language/value.rs +++ b/nova_vm/src/ecmascript/types/language/value.rs @@ -6,8 +6,8 @@ use super::{ bigint::{HeapBigInt, SmallBigInt}, number::HeapNumber, string::HeapString, - BigInt, BigIntHeapData, IntoValue, Number, Numeric, OrdinaryObject, String, StringHeapData, - Symbol, + BigInt, BigIntHeapData, IntoValue, Number, Numeric, OrdinaryObject, Primitive, String, + StringHeapData, Symbol, }; #[cfg(feature = "date")] use crate::ecmascript::builtins::date::Date; @@ -53,9 +53,10 @@ use crate::{ types::BUILTIN_STRING_MEMORY, }, engine::{ - context::GcScope, + context::{GcScope, NoGcScope}, rootable::{HeapRootData, HeapRootRef, Rootable}, small_f64::SmallF64, + Scoped, }, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, SmallInteger, SmallString, @@ -86,7 +87,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), + String(HeapString<'static>), /// ### [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 @@ -351,16 +352,43 @@ pub(crate) const EMBEDDER_OBJECT_DISCRIMINANT: u8 = value_discriminant(Value::EmbedderObject(EmbedderObject::_def())); impl Value { - pub fn from_str(agent: &mut Agent, str: &str) -> Value { - String::from_str(agent, str).into_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 + } + + pub fn scope<'scope>( + self, + agent: &mut Agent, + gc: NoGcScope<'_, 'scope>, + ) -> Scoped<'scope, Value> { + Scoped::new(agent, gc, self.unbind()) } - pub fn from_string(agent: &mut Agent, string: std::string::String) -> Value { - String::from_string(agent, string).into_value() + pub fn from_str(agent: &mut Agent, gc: NoGcScope<'_, '_>, str: &str) -> Value { + String::from_str(agent, gc, str).into_value() } - pub fn from_static_str(agent: &mut Agent, str: &'static str) -> Value { - String::from_static_str(agent, str).into_value() + pub fn from_string(agent: &mut Agent, gc: NoGcScope, string: std::string::String) -> Value { + String::from_string(agent, gc, string).into_value() + } + + pub fn from_static_str(agent: &mut Agent, gc: NoGcScope, str: &'static str) -> Value { + String::from_static_str(agent, gc, str).into_value() } pub fn from_f64(agent: &mut Agent, value: f64) -> Value { @@ -406,6 +434,10 @@ impl Value { ) } + pub fn is_primitive(self) -> bool { + Primitive::try_from(self).is_ok() + } + pub fn is_string(self) -> bool { matches!(self, Value::String(_) | Value::SmallString(_)) } @@ -467,6 +499,10 @@ impl Value { ) } + pub fn is_integer(self) -> bool { + matches!(self, Value::Integer(_)) + } + pub fn is_empty_string(self) -> bool { if let Value::SmallString(s) = self { s.is_empty() @@ -503,17 +539,17 @@ impl Value { to_uint16(agent, gc, self) } - pub fn to_string(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> JsResult { + pub fn to_string<'gc>(self, agent: &mut Agent, gc: GcScope<'gc, '_>) -> JsResult> { to_string(agent, gc, self) } /// A string conversion that will never throw, meant for things like /// displaying exceptions. - pub fn string_repr(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> String { + pub fn string_repr<'gc>(self, agent: &mut Agent, gc: GcScope<'gc, '_>) -> String<'gc> { 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); + return symbol_idx.descriptive_string(agent, gc.into_nogc()); }; match self.to_string(agent, gc) { Ok(result) => result, @@ -538,7 +574,7 @@ impl Value { pub(crate) fn hash(self, arena: &A, hasher: &mut H) where H: Hasher, - A: Index + A: Index, Output = StringHeapData> + Index + Index, { diff --git a/nova_vm/src/ecmascript/types/spec/data_block.rs b/nova_vm/src/ecmascript/types/spec/data_block.rs index d29f1e16e..d2e660d36 100644 --- a/nova_vm/src/ecmascript/types/spec/data_block.rs +++ b/nova_vm/src/ecmascript/types/spec/data_block.rs @@ -18,7 +18,7 @@ use crate::{ execution::{agent::ExceptionType, Agent, JsResult, ProtoIntrinsics}, types::{BigInt, IntoValue, Value}, }, - engine::context::GcScope, + engine::context::{GcScope, NoGcScope}, }; /// Sentinel pointer for a detached data block. @@ -488,11 +488,12 @@ impl DataBlock { /// The abstract operation CreateByteDataBlock takes argument size (a /// non-negative integer) and returns either a normal completion containing /// a Data Block or a throw completion. - pub fn create_byte_data_block(agent: &mut Agent, size: u64) -> JsResult { + pub fn create_byte_data_block(agent: &mut Agent, gc: NoGcScope, size: u64) -> JsResult { // 1. If size > 2**53 - 1, throw a RangeError exception. if size > u64::pow(2, 53) - 1 { // TODO: throw a RangeError exception Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "Not a safe integer", )) @@ -505,6 +506,7 @@ impl DataBlock { // 2. cont: If it is impossible to create such a Data Block, throw a RangeError exception. // TODO: throw a RangeError exception Err(agent.throw_exception_with_static_message( + gc, ExceptionType::RangeError, "Invalid Data Block length", )) @@ -516,7 +518,11 @@ impl DataBlock { /// The abstract operation CreateSharedByteDataBlock takes argument size (a /// non-negative integer) and returns either a normal completion containing /// a Shared Data Block or a throw completion. - pub fn create_shared_byte_data_block(agent: &mut Agent, size: u64) -> JsResult { + pub fn create_shared_byte_data_block( + agent: &mut Agent, + gc: NoGcScope, + size: u64, + ) -> JsResult { // 1. Let db be a new Shared Data Block value consisting of size bytes. If it is impossible to create such a Shared Data Block, throw a RangeError exception. if let Ok(size) = usize::try_from(size) { // 2. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record. @@ -527,6 +533,7 @@ impl DataBlock { Ok(Self::new(size)) } else { Err(agent.throw_exception_with_static_message( + gc, ExceptionType::TypeError, "Invalid Shared Data Block length", )) diff --git a/nova_vm/src/ecmascript/types/spec/property_descriptor.rs b/nova_vm/src/ecmascript/types/spec/property_descriptor.rs index 03ae6080e..810179f2d 100644 --- a/nova_vm/src/ecmascript/types/spec/property_descriptor.rs +++ b/nova_vm/src/ecmascript/types/spec/property_descriptor.rs @@ -179,17 +179,16 @@ impl PropertyDescriptor { pub fn to_property_descriptor( agent: &mut Agent, mut gc: GcScope<'_, '_>, - obj: Value, ) -> JsResult { // 1. If Obj is not an Object, throw a TypeError exception. let Ok(obj) = Object::try_from(obj) else { - let obj_repr = obj.string_repr(agent, gc); + let obj_repr = obj.string_repr(agent, gc.reborrow()); let error_message = format!( "Property descriptor must be an object, got '{}'.", obj_repr.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 2. Let desc be a new Property Descriptor that initially has no // fields. @@ -284,6 +283,7 @@ impl PropertyDescriptor { if !getter.is_undefined() { let Some(getter) = is_callable(getter) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "getter is not callable", )); @@ -297,12 +297,13 @@ impl PropertyDescriptor { // 14. If hasSet is true, then if has_set { // a. Let setter be ? Get(Obj, "set"). - let setter = get(agent, gc, obj, BUILTIN_STRING_MEMORY.set.into())?; + let setter = get(agent, gc.reborrow(), obj, BUILTIN_STRING_MEMORY.set.into())?; // b. If IsCallable(setter) is false and setter is not undefined, // throw a TypeError exception. if !setter.is_undefined() { let Some(setter) = is_callable(setter) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "setter is not callable", )); @@ -317,6 +318,7 @@ impl PropertyDescriptor { // field, throw a TypeError exception. if desc.writable.is_some() || desc.writable.is_some() { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Over-defined property descriptor", )); diff --git a/nova_vm/src/ecmascript/types/spec/reference.rs b/nova_vm/src/ecmascript/types/spec/reference.rs index e5c027cbf..a1a1a6667 100644 --- a/nova_vm/src/ecmascript/types/spec/reference.rs +++ b/nova_vm/src/ecmascript/types/spec/reference.rs @@ -102,7 +102,6 @@ pub(crate) fn is_private_reference(_: &Reference) -> bool { pub(crate) fn get_value( agent: &mut Agent, mut gc: GcScope<'_, '_>, - reference: &Reference, ) -> JsResult { let referenced_name = reference.referenced_name; @@ -133,14 +132,22 @@ pub(crate) fn get_value( "Cannot read property '{}' of undefined.", referenced_name.as_display(agent) ); - Err(agent.throw_exception(ExceptionType::TypeError, error_message)) + Err(agent.throw_exception( + gc.nogc(), + ExceptionType::TypeError, + error_message, + )) } Value::Null => { let error_message = format!( "Cannot read property '{}' of null.", referenced_name.as_display(agent) ); - Err(agent.throw_exception(ExceptionType::TypeError, error_message)) + Err(agent.throw_exception( + gc.nogc(), + ExceptionType::TypeError, + error_message, + )) } Value::Boolean(_) => agent .current_realm() @@ -198,7 +205,7 @@ pub(crate) fn get_value( "Cannot access undeclared variable '{}'.", referenced_name.as_display(agent) ); - Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)) + Err(agent.throw_exception(gc.nogc(), ExceptionType::ReferenceError, error_message)) } } } @@ -211,7 +218,6 @@ pub(crate) fn get_value( pub(crate) fn put_value( agent: &mut Agent, mut gc: GcScope<'_, '_>, - v: &Reference, w: Value, ) -> JsResult<()> { @@ -224,7 +230,11 @@ pub(crate) fn put_value( "Cannot assign to undeclared variable '{}'.", v.referenced_name.as_display(agent) ); - return Err(agent.throw_exception(ExceptionType::ReferenceError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::ReferenceError, + error_message, + )); } // b. Let globalObj be GetGlobalObject(). let global_obj = get_global_object(agent); @@ -240,7 +250,7 @@ pub(crate) fn put_value( Base::Value(value) => value, Base::Environment(_) | Base::Unresolvable => unreachable!(), }; - let base_obj = to_object(agent, base)?; + let base_obj = to_object(agent, gc.nogc(), base)?; // b. If IsPrivateReference(V) is true, then if is_private_reference(v) { // i. Return ? PrivateSet(baseObj, V.[[ReferencedName]], W). @@ -253,13 +263,13 @@ pub(crate) fn put_value( base_obj.internal_set(agent, gc.reborrow(), referenced_name, w, this_value)?; if !succeeded && v.strict { // d. If succeeded is false and V.[[Strict]] is true, throw a TypeError exception. - let base_obj_repr = base_obj.into_value().string_repr(agent, gc); + let base_obj_repr = base_obj.into_value().string_repr(agent, gc.reborrow()); let error_message = format!( "Could not set property '{}' of {}.", referenced_name.as_display(agent), base_obj_repr.as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); } // e. Return UNUSED. Ok(()) @@ -290,7 +300,6 @@ pub(crate) fn put_value( pub(crate) fn initialize_referenced_binding( agent: &mut Agent, mut gc: GcScope<'_, '_>, - v: Reference, w: Value, ) -> JsResult<()> { diff --git a/nova_vm/src/engine/bytecode/bytecode_compiler.rs b/nova_vm/src/engine/bytecode/bytecode_compiler.rs index daa95a595..899e785a5 100644 --- a/nova_vm/src/engine/bytecode/bytecode_compiler.rs +++ b/nova_vm/src/engine/bytecode/bytecode_compiler.rs @@ -21,6 +21,7 @@ use crate::{ }, types::{BigInt, IntoValue, Number, PropertyKey, String, Value, BUILTIN_STRING_MEMORY}, }, + engine::context::NoGcScope, heap::CreateHeapData, }; use num_traits::Num; @@ -45,8 +46,9 @@ pub(crate) enum NamedEvaluationParameter { ReferenceStack, } -pub(crate) struct CompileContext<'agent> { +pub(crate) struct CompileContext<'agent, 'gc, 'scope> { pub(crate) agent: &'agent mut Agent, + pub(crate) gc: NoGcScope<'gc, 'scope>, /// Instructions being built instructions: Vec, /// Constants being built @@ -73,10 +75,14 @@ pub(crate) struct CompileContext<'agent> { is_call_optional_chain_this: bool, } -impl CompileContext<'_> { - pub(super) fn new(agent: &'_ mut Agent) -> CompileContext<'_> { +impl<'a, 'gc, 'scope> CompileContext<'a, 'gc, 'scope> { + pub(super) fn new( + agent: &'a mut Agent, + gc: NoGcScope<'gc, 'scope>, + ) -> CompileContext<'a, 'gc, 'scope> { CompileContext { agent, + gc, instructions: Vec::new(), constants: Vec::new(), function_expressions: Vec::new(), @@ -100,7 +106,7 @@ impl CompileContext<'_> { ) { let identifier = match property_key { ast::PropertyKey::StaticIdentifier(identifier_name) => { - String::from_str(self.agent, identifier_name.name.as_str()) + String::from_str(self.agent, self.gc, identifier_name.name.as_str()) } ast::PropertyKey::PrivateIdentifier(_private_identifier) => todo!(), ast::PropertyKey::BooleanLiteral(_boolean_literal) => todo!(), @@ -143,7 +149,7 @@ impl CompileContext<'_> { /// current context. pub(crate) fn compile_class_computed_field( &mut self, - property_key_id: String, + property_key_id: String<'gc>, value: &Option>, ) { // Resolve 'this' into the stack. @@ -228,7 +234,7 @@ impl CompileContext<'_> { }) } - pub(crate) fn create_identifier(&mut self, atom: &Atom<'_>) -> String { + pub(crate) fn create_identifier(&mut self, atom: &Atom<'_>) -> String<'gc> { let existing = self.constants.iter().find_map(|constant| { if let Ok(existing_identifier) = String::try_from(*constant) { if existing_identifier.as_str(self.agent) == atom.as_str() { @@ -243,7 +249,7 @@ impl CompileContext<'_> { if let Some(existing) = existing { existing } else { - String::from_str(self.agent, atom.as_str()) + String::from_str(self.agent, self.gc, atom.as_str()) } } @@ -341,7 +347,11 @@ impl CompileContext<'_> { self.add_index(constant); } - fn add_instruction_with_identifier(&mut self, instruction: Instruction, identifier: String) { + fn add_instruction_with_identifier( + &mut self, + instruction: Instruction, + identifier: String<'gc>, + ) { debug_assert_eq!(instruction.argument_count(), 1); debug_assert!(instruction.has_identifier_index()); self._push_instruction(instruction); @@ -352,7 +362,7 @@ impl CompileContext<'_> { fn add_instruction_with_identifier_and_constant( &mut self, instruction: Instruction, - identifier: String, + identifier: String<'gc>, constant: impl Into, ) { debug_assert_eq!(instruction.argument_count(), 2); @@ -528,21 +538,21 @@ impl CompileEvaluation for ast::NullLiteral { impl CompileEvaluation for ast::StringLiteral<'_> { fn compile(&self, ctx: &mut CompileContext) { - let constant = String::from_str(ctx.agent, self.value.as_str()); + let constant = String::from_str(ctx.agent, ctx.gc, self.value.as_str()); ctx.add_instruction_with_constant(Instruction::StoreConstant, constant); } } impl CompileEvaluation for ast::IdentifierReference<'_> { fn compile(&self, ctx: &mut CompileContext) { - let identifier = String::from_str(ctx.agent, self.name.as_str()); + let identifier = String::from_str(ctx.agent, ctx.gc, self.name.as_str()); ctx.add_instruction_with_identifier(Instruction::ResolveBinding, identifier); } } impl CompileEvaluation for ast::BindingIdentifier<'_> { fn compile(&self, ctx: &mut CompileContext) { - let identifier = String::from_str(ctx.agent, self.name.as_str()); + let identifier = String::from_str(ctx.agent, ctx.gc, self.name.as_str()); ctx.add_instruction_with_identifier(Instruction::ResolveBinding, identifier); } } @@ -1001,7 +1011,7 @@ impl CompileEvaluation for ast::ObjectExpression<'_> { ); } } else { - let identifier = PropertyKey::from_str(ctx.agent, &id.name); + let identifier = PropertyKey::from_str(ctx.agent, ctx.gc, &id.name); ctx.add_instruction_with_constant( Instruction::StoreConstant, identifier, @@ -1010,7 +1020,7 @@ impl CompileEvaluation for ast::ObjectExpression<'_> { } ast::PropertyKey::StaticMemberExpression(init) => init.compile(ctx), ast::PropertyKey::StringLiteral(init) => { - let identifier = PropertyKey::from_str(ctx.agent, &init.value); + let identifier = PropertyKey::from_str(ctx.agent, ctx.gc, &init.value); ctx.add_instruction_with_constant( Instruction::StoreConstant, identifier, @@ -1419,7 +1429,7 @@ impl CompileEvaluation for ast::StaticMemberExpression<'_> { } // 4. Return EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict). - let identifier = String::from_str(ctx.agent, self.property.name.as_str()); + let identifier = String::from_str(ctx.agent, ctx.gc, self.property.name.as_str()); ctx.add_instruction_with_identifier( Instruction::EvaluatePropertyAccessWithIdentifierKey, identifier, @@ -1568,7 +1578,7 @@ impl CompileEvaluation for ast::RegExpLiteral<'_> { // We probably shouldn't be getting parsed RegExps? ast::RegExpPattern::Pattern(_) => unreachable!(), }; - let pattern = String::from_str(ctx.agent, pattern); + let pattern = String::from_str(ctx.agent, ctx.gc, pattern); let regexp = reg_exp_create(ctx.agent, pattern, Some(self.regex.flags)).unwrap(); ctx.add_instruction_with_constant(Instruction::StoreConstant, regexp); } @@ -1599,6 +1609,7 @@ impl CompileEvaluation for ast::TemplateLiteral<'_> { if self.is_no_substitution_template() { let constant = String::from_str( ctx.agent, + ctx.gc, self.quasi() .as_ref() .expect("Invalid escape sequence in template literal") @@ -1612,8 +1623,11 @@ impl CompileEvaluation for ast::TemplateLiteral<'_> { while let Some((head, rest)) = quasis.split_first() { quasis = rest; // 1. Let head be the TV of TemplateHead as defined in 12.9.6. - let head = - String::from_str(ctx.agent, head.value.cooked.as_ref().unwrap().as_str()); + let head = String::from_str( + ctx.agent, + ctx.gc, + head.value.cooked.as_ref().unwrap().as_str(), + ); ctx.add_instruction_with_constant(Instruction::LoadConstant, head); count += 1; if let Some((expression, rest)) = expressions.split_first() { @@ -2053,18 +2067,18 @@ fn simple_object_pattern( } else { let key_string = match &ele.key { ast::PropertyKey::StaticIdentifier(identifier) => { - PropertyKey::from_str(ctx.agent, &identifier.name).into_value() + PropertyKey::from_str(ctx.agent, ctx.gc, &identifier.name).into_value() } ast::PropertyKey::NumericLiteral(literal) => { let numeric_value = Number::from_f64(ctx.agent, literal.value); if let Number::Integer(_) = numeric_value { numeric_value.into_value() } else { - Number::to_string_radix_10(ctx.agent, numeric_value).into_value() + Number::to_string_radix_10(ctx.agent, ctx.gc, numeric_value).into_value() } } ast::PropertyKey::StringLiteral(literal) => { - PropertyKey::from_str(ctx.agent, &literal.value).into_value() + PropertyKey::from_str(ctx.agent, ctx.gc, &literal.value).into_value() } _ => unreachable!(), }; @@ -2273,7 +2287,8 @@ impl CompileEvaluation for ast::VariableDeclaration<'_> { // 1. Let bindingId be StringValue of BindingIdentifier. // 2. Let lhs be ? ResolveBinding(bindingId). - let identifier_string = String::from_str(ctx.agent, identifier.name.as_str()); + let identifier_string = + String::from_str(ctx.agent, ctx.gc, identifier.name.as_str()); ctx.add_instruction_with_identifier( Instruction::ResolveBinding, identifier_string, @@ -2330,7 +2345,8 @@ impl CompileEvaluation for ast::VariableDeclaration<'_> { }; // 1. Let lhs be ! ResolveBinding(StringValue of BindingIdentifier). - let identifier_string = String::from_str(ctx.agent, identifier.name.as_str()); + let identifier_string = + String::from_str(ctx.agent, ctx.gc, identifier.name.as_str()); ctx.add_instruction_with_identifier( Instruction::ResolveBinding, identifier_string, @@ -2412,7 +2428,8 @@ impl CompileEvaluation for ast::BlockStatement<'_> { LexicallyScopedDeclaration::Variable(decl) => { if decl.kind.is_const() { decl.id.bound_names(&mut |name| { - let identifier = String::from_str(ctx.agent, name.name.as_str()); + let identifier = + String::from_str(ctx.agent, ctx.gc, name.name.as_str()); ctx.add_instruction_with_identifier( Instruction::CreateImmutableBinding, identifier, @@ -2420,7 +2437,8 @@ impl CompileEvaluation for ast::BlockStatement<'_> { }); } else if decl.kind.is_lexical() { decl.id.bound_names(&mut |name| { - let identifier = String::from_str(ctx.agent, name.name.as_str()); + let identifier = + String::from_str(ctx.agent, ctx.gc, name.name.as_str()); ctx.add_instruction_with_identifier( Instruction::CreateMutableBinding, identifier, @@ -2431,7 +2449,7 @@ impl CompileEvaluation for ast::BlockStatement<'_> { LexicallyScopedDeclaration::Function(decl) => { // TODO: InstantiateFunctionObject and InitializeBinding decl.bound_names(&mut |name| { - let identifier = String::from_str(ctx.agent, name.name.as_str()); + let identifier = String::from_str(ctx.agent, ctx.gc, name.name.as_str()); ctx.add_instruction_with_identifier( Instruction::CreateMutableBinding, identifier, @@ -2440,7 +2458,7 @@ impl CompileEvaluation for ast::BlockStatement<'_> { } LexicallyScopedDeclaration::Class(decl) => { decl.bound_names(&mut |name| { - let identifier = String::from_str(ctx.agent, name.name.as_str()); + let identifier = String::from_str(ctx.agent, ctx.gc, name.name.as_str()); ctx.add_instruction_with_identifier( Instruction::CreateMutableBinding, identifier, @@ -2462,11 +2480,11 @@ impl CompileEvaluation for ast::BlockStatement<'_> { } impl CompileEvaluation for ast::ForStatement<'_> { - fn compile(&self, ctx: &mut CompileContext) { + fn compile<'gc>(&self, ctx: &mut CompileContext<'_, 'gc, '_>) { let previous_continue = ctx.current_continue.replace(vec![]); let previous_break = ctx.current_break.replace(vec![]); - let mut per_iteration_lets = vec![]; + let mut per_iteration_lets: Vec> = vec![]; let mut is_lexical = false; if let Some(init) = &self.init { @@ -2522,7 +2540,8 @@ impl CompileEvaluation for ast::ForStatement<'_> { if is_const { init.bound_names(&mut |dn| { // i. Perform ! loopEnv.CreateImmutableBinding(dn, true). - let identifier = String::from_str(ctx.agent, dn.name.as_str()); + let identifier = + String::from_str(ctx.agent, ctx.gc, dn.name.as_str()); ctx.add_instruction_with_identifier( Instruction::CreateImmutableBinding, identifier, @@ -2532,7 +2551,8 @@ impl CompileEvaluation for ast::ForStatement<'_> { // b. Else, // i. Perform ! loopEnv.CreateMutableBinding(dn, false). init.bound_names(&mut |dn| { - let identifier = String::from_str(ctx.agent, dn.name.as_str()); + let identifier = + String::from_str(ctx.agent, ctx.gc, dn.name.as_str()); // 9. If isConst is false, let perIterationLets // be boundNames; otherwise let perIterationLets // be a new empty List. @@ -2559,7 +2579,7 @@ impl CompileEvaluation for ast::ForStatement<'_> { } // 2. Perform ? CreatePerIterationEnvironment(perIterationBindings). let create_per_iteration_env = if !per_iteration_lets.is_empty() { - Some(|ctx: &mut CompileContext| { + Some(|ctx: &mut CompileContext<'_, 'gc, '_>| { if per_iteration_lets.len() == 1 { // NOTE: Optimization for the usual case of a single let // binding. We do not need to push and pop from the stack @@ -2774,7 +2794,7 @@ impl CompileEvaluation for ast::TryStatement<'_> { todo!("{:?}", exception_param.pattern.kind); }; ctx.add_instruction(Instruction::EnterDeclarativeEnvironment); - let identifier_string = String::from_str(ctx.agent, identifier.name.as_str()); + let identifier_string = String::from_str(ctx.agent, ctx.gc, identifier.name.as_str()); ctx.add_instruction_with_identifier(Instruction::CreateCatchBinding, identifier_string); } catch_clause.body.compile(ctx); diff --git a/nova_vm/src/engine/bytecode/bytecode_compiler/class_definition_evaluation.rs b/nova_vm/src/engine/bytecode/bytecode_compiler/class_definition_evaluation.rs index 97acf3d04..be601f3a8 100644 --- a/nova_vm/src/engine/bytecode/bytecode_compiler/class_definition_evaluation.rs +++ b/nova_vm/src/engine/bytecode/bytecode_compiler/class_definition_evaluation.rs @@ -42,7 +42,7 @@ impl CompileEvaluation for ast::Class<'_> { let mut class_identifier = None; if let Some(class_binding) = &self.id { // a. Perform ! classEnv.CreateImmutableBinding(classBinding, true). - let identifier = String::from_str(ctx.agent, class_binding.name.as_str()); + let identifier = String::from_str(ctx.agent, ctx.gc, class_binding.name.as_str()); class_identifier = Some(identifier); ctx.add_instruction_with_identifier(Instruction::CreateImmutableBinding, identifier); } else if let Some(anonymous_class_name) = anonymous_class_name { @@ -73,7 +73,7 @@ impl CompileEvaluation for ast::Class<'_> { .filter(|class_element| class_element.private_bound_identifiers().is_some()) { let dn = dn.private_bound_identifiers().unwrap(); - let _dn = String::from_str(ctx.agent, dn.name.as_str()); + let _dn = String::from_str(ctx.agent, ctx.gc, dn.name.as_str()); // TODO: Private elements. // a. For each String dn of the PrivateBoundIdentifiers of ClassBody, do // i. If classPrivateEnvironment.[[Names]] contains a Private Name pn such that pn.[[Description]] is dn, then @@ -146,8 +146,11 @@ impl CompileEvaluation for ast::Class<'_> { // Pop the superclass from the stack. ctx.add_instruction(Instruction::Store); // i. Throw a TypeError exception. - let error_message = - String::from_static_str(ctx.agent, "class heritage is not a constructor"); + let error_message = String::from_static_str( + ctx.agent, + ctx.gc, + "class heritage is not a constructor", + ); ctx.add_instruction_with_constant(Instruction::StoreConstant, error_message); ctx.add_instruction_with_immediate( Instruction::ThrowError, @@ -178,8 +181,11 @@ impl CompileEvaluation for ast::Class<'_> { let jump_over_throw = ctx.add_instruction_with_jump_slot(Instruction::JumpIfTrue); // ... throw a TypeError exception. - let error_message = - String::from_static_str(ctx.agent, "class heritage is not an object or null"); + let error_message = String::from_static_str( + ctx.agent, + ctx.gc, + "class heritage is not an object or null", + ); ctx.add_instruction_with_constant(Instruction::StoreConstant, error_message); ctx.add_instruction_with_immediate( Instruction::ThrowError, @@ -425,7 +431,7 @@ impl CompileEvaluation for ast::Class<'_> { // 28. Set F.[[PrivateMethods]] to instancePrivateMethods. // 29. Set F.[[Fields]] to instanceFields. if !instance_fields.is_empty() { - let mut constructor_ctx = CompileContext::new(ctx.agent); + let mut constructor_ctx = CompileContext::new(ctx.agent, ctx.gc); for ele in instance_fields { match ele { PropertyInitializerField::Static((property_key, value)) => { @@ -508,18 +514,20 @@ impl CompileEvaluation for ast::Class<'_> { } #[derive(Debug)] -enum PropertyInitializerField<'a> { +enum PropertyInitializerField<'a, 'gc> { Static((&'a ast::PropertyKey<'a>, &'a Option>)), - Computed((String, &'a Option>)), + Computed((String<'gc>, &'a Option>)), } -fn compile_computed_field_name<'a>( - ctx: &mut CompileContext, - property_fields: &mut Vec>, +fn compile_computed_field_name<'a, 'gc>( + ctx: &mut CompileContext<'_, 'gc, '_>, + property_fields: &mut Vec>, key: &ast::PropertyKey<'_>, value: &'a Option>, ) { - let computed_key_id = String::from_string(ctx.agent, format!("^{}", property_fields.len())); + // TODO: Handle lifetime logic. + let computed_key_id = + String::from_string(ctx.agent, ctx.gc, format!("^{}", property_fields.len())); let key = match key { // These should not show up as computed ast::PropertyKey::StaticMemberExpression(_) @@ -596,7 +604,7 @@ fn define_constructor_method( fn define_method(class_element: &ast::MethodDefinition, ctx: &mut CompileContext) -> IndexType { // 1. Let propKey be ? Evaluation of ClassElementName. if let Some(prop_name) = class_element.prop_name() { - let prop_name = String::from_str(ctx.agent, prop_name.0); + let prop_name = String::from_str(ctx.agent, ctx.gc, prop_name.0); ctx.add_instruction_with_constant(Instruction::LoadConstant, prop_name); } else { // Computed method name. @@ -682,7 +690,7 @@ impl CompileEvaluation for ast::StaticBlock<'_> { continue; } // 1. Append n to instantiatedVarNames. - let n_string = String::from_str(ctx.agent, &n); + let n_string = String::from_str(ctx.agent, ctx.gc, &n); instantiated_var_names.insert(n); // 2. Perform ! env.CreateMutableBinding(n, false). ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, n_string); @@ -701,7 +709,7 @@ impl CompileEvaluation for ast::StaticBlock<'_> { LexicallyScopedDeclaration::Variable(decl) if decl.kind.is_const() => { { decl.id.bound_names(&mut |identifier| { - let dn = String::from_str(ctx.agent, &identifier.name); + let dn = String::from_str(ctx.agent, ctx.gc, &identifier.name); // 1. Perform ! lexEnv.CreateImmutableBinding(dn, true). ctx.add_instruction_with_identifier( Instruction::CreateImmutableBinding, @@ -714,16 +722,16 @@ impl CompileEvaluation for ast::StaticBlock<'_> { // 1. Perform ! lexEnv.CreateMutableBinding(dn, false). LexicallyScopedDeclaration::Variable(decl) => { decl.id.bound_names(&mut |identifier| { - let dn = String::from_str(ctx.agent, &identifier.name); + let dn = String::from_str(ctx.agent, ctx.gc, &identifier.name); ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); }) } LexicallyScopedDeclaration::Function(decl) => { - let dn = String::from_str(ctx.agent, &decl.id.as_ref().unwrap().name); + let dn = String::from_str(ctx.agent, ctx.gc, &decl.id.as_ref().unwrap().name); ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); } LexicallyScopedDeclaration::Class(decl) => { - let dn = String::from_str(ctx.agent, &decl.id.as_ref().unwrap().name); + let dn = String::from_str(ctx.agent, ctx.gc, &decl.id.as_ref().unwrap().name); ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); } LexicallyScopedDeclaration::DefaultExport => { @@ -738,7 +746,7 @@ impl CompileEvaluation for ast::StaticBlock<'_> { // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. f.compile(ctx); // a. Let fn be the sole element of the BoundNames of f. - let f_name = String::from_str(ctx.agent, &f.id.as_ref().unwrap().name); + let f_name = String::from_str(ctx.agent, ctx.gc, &f.id.as_ref().unwrap().name); // c. Perform ! varEnv.SetMutableBinding(fn, fo, false). // TODO: This compilation is incorrect if !strict, when varEnv != lexEnv. ctx.add_instruction_with_identifier(Instruction::ResolveBinding, f_name); diff --git a/nova_vm/src/engine/bytecode/bytecode_compiler/for_in_of_statement.rs b/nova_vm/src/engine/bytecode/bytecode_compiler/for_in_of_statement.rs index 5bc785e34..a765ef2fb 100644 --- a/nova_vm/src/engine/bytecode/bytecode_compiler/for_in_of_statement.rs +++ b/nova_vm/src/engine/bytecode/bytecode_compiler/for_in_of_statement.rs @@ -27,9 +27,9 @@ enum IteratorKind { Async, } -fn for_in_of_head_evaluation( - ctx: &mut CompileContext, - uninitialized_bound_names: Vec, +fn for_in_of_head_evaluation<'gc>( + ctx: &mut CompileContext<'_, 'gc, '_>, + uninitialized_bound_names: Vec>, expr: &ast::Expression<'_>, iteration_kind: IterationKind, ) -> Option { @@ -186,8 +186,11 @@ fn for_in_of_body_evaluation( let declaration = decl.declarations.first().unwrap(); match &declaration.id.kind { ast::BindingPatternKind::BindingIdentifier(binding_identifier) => { - let identifier = - String::from_str(ctx.agent, binding_identifier.name.as_str()); + let identifier = String::from_str( + ctx.agent, + ctx.gc, + binding_identifier.name.as_str(), + ); ctx.add_instruction_with_identifier( Instruction::ResolveBinding, identifier, @@ -200,7 +203,7 @@ fn for_in_of_body_evaluation( } ast::ForStatementLeft::AssignmentTargetIdentifier(identifier_reference) => { let identifier = - String::from_str(ctx.agent, identifier_reference.name.as_str()); + String::from_str(ctx.agent, ctx.gc, identifier_reference.name.as_str()); ctx.add_instruction_with_identifier( Instruction::ResolveBinding, identifier, @@ -233,7 +236,8 @@ fn for_in_of_body_evaluation( ctx.add_instruction(Instruction::EnterDeclarativeEnvironment); entered_declarative_environment = true; } - let identifier = String::from_str(ctx.agent, binding_identifier.name.as_str()); + let identifier = + String::from_str(ctx.agent, ctx.gc, binding_identifier.name.as_str()); ctx.add_instruction_with_identifier( if lhs.kind.is_const() { Instruction::CreateImmutableBinding @@ -256,7 +260,8 @@ fn for_in_of_body_evaluation( assert!(!bound); bound = true; // 2. Let lhsName be the sole element of the BoundNames of lhs. - let lhs_name = String::from_str(ctx.agent, binding_identifier.name.as_str()); + let lhs_name = + String::from_str(ctx.agent, ctx.gc, binding_identifier.name.as_str()); // 3. Let lhsRef be ! ResolveBinding(lhsName). ctx.add_instruction_with_identifier(Instruction::ResolveBinding, lhs_name); // 4. Let status be Completion(InitializeReferencedBinding(lhsRef, nextValue)). @@ -341,6 +346,7 @@ impl CompileEvaluation for ast::ForInStatement<'_> { var_decl.bound_names(&mut |binding_identifier| { uninitialized_bound_names.push(String::from_str( ctx.agent, + ctx.gc, binding_identifier.name.as_str(), )); }); @@ -393,6 +399,7 @@ impl CompileEvaluation for ast::ForOfStatement<'_> { var_decl.bound_names(&mut |binding_identifier| { uninitialized_bound_names.push(String::from_str( ctx.agent, + ctx.gc, binding_identifier.name.as_str(), )); }); diff --git a/nova_vm/src/engine/bytecode/bytecode_compiler/function_declaration_instantiation.rs b/nova_vm/src/engine/bytecode/bytecode_compiler/function_declaration_instantiation.rs index 76256573f..353a0e275 100644 --- a/nova_vm/src/engine/bytecode/bytecode_compiler/function_declaration_instantiation.rs +++ b/nova_vm/src/engine/bytecode/bytecode_compiler/function_declaration_instantiation.rs @@ -106,7 +106,7 @@ pub(crate) fn instantiation( // NOTE: Since `parameter_names` is a set, `alreadyDeclared` here should always be false. // i. Perform ! env.CreateMutableBinding(paramName, false). - let param_name = String::from_str(ctx.agent, param_name); + let param_name = String::from_str(ctx.agent, ctx.gc, param_name); ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, param_name); // ii. If hasDuplicates is true, then if has_duplicates { @@ -194,7 +194,7 @@ pub(crate) fn instantiation( continue; } // 1. Append n to instantiatedVarNames. - let n_string = String::from_str(ctx.agent, &n); + let n_string = String::from_str(ctx.agent, ctx.gc, &n); instantiated_var_names.insert(n); // 2. Perform ! env.CreateMutableBinding(n, false). ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, n_string); @@ -235,7 +235,7 @@ pub(crate) fn instantiation( // 1. Append n to instantiatedVarNames. instantiated_var_names.insert(n.clone()); // 3. If parameterBindings does not contain n, or if functionNames contains n, then - let n_string = String::from_str(ctx.agent, &n); + let n_string = String::from_str(ctx.agent, ctx.gc, &n); if !parameter_names.contains(&n) || functions.contains_key(&n) { // a. Let initialValue be undefined. ctx.add_instruction_with_constant(Instruction::LoadConstant, Value::Undefined); @@ -279,7 +279,7 @@ pub(crate) fn instantiation( // i. If IsConstantDeclaration of d is true, then LexicallyScopedDeclaration::Variable(decl) if decl.kind.is_const() => { decl.id.bound_names(&mut |identifier| { - let dn = String::from_str(ctx.agent, &identifier.name); + let dn = String::from_str(ctx.agent, ctx.gc, &identifier.name); // 1. Perform ! lexEnv.CreateImmutableBinding(dn, true). ctx.add_instruction_with_identifier(Instruction::CreateImmutableBinding, dn); }) @@ -287,15 +287,15 @@ pub(crate) fn instantiation( // ii. Else, // 1. Perform ! lexEnv.CreateMutableBinding(dn, false). LexicallyScopedDeclaration::Variable(decl) => decl.id.bound_names(&mut |identifier| { - let dn = String::from_str(ctx.agent, &identifier.name); + let dn = String::from_str(ctx.agent, ctx.gc, &identifier.name); ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); }), LexicallyScopedDeclaration::Function(decl) => { - let dn = String::from_str(ctx.agent, &decl.id.as_ref().unwrap().name); + let dn = String::from_str(ctx.agent, ctx.gc, &decl.id.as_ref().unwrap().name); ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); } LexicallyScopedDeclaration::Class(decl) => { - let dn = String::from_str(ctx.agent, &decl.id.as_ref().unwrap().name); + let dn = String::from_str(ctx.agent, ctx.gc, &decl.id.as_ref().unwrap().name); ctx.add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); } LexicallyScopedDeclaration::DefaultExport => { @@ -310,7 +310,7 @@ pub(crate) fn instantiation( // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. f.compile(ctx); // a. Let fn be the sole element of the BoundNames of f. - let f_name = String::from_str(ctx.agent, &f.id.as_ref().unwrap().name); + let f_name = String::from_str(ctx.agent, ctx.gc, &f.id.as_ref().unwrap().name); // c. Perform ! varEnv.SetMutableBinding(fn, fo, false). // TODO: This compilation is incorrect if !strict, when varEnv != lexEnv. ctx.add_instruction_with_identifier(Instruction::ResolveBinding, f_name); diff --git a/nova_vm/src/engine/bytecode/executable.rs b/nova_vm/src/engine/bytecode/executable.rs index 1f296e078..299ffea05 100644 --- a/nova_vm/src/engine/bytecode/executable.rs +++ b/nova_vm/src/engine/bytecode/executable.rs @@ -15,6 +15,7 @@ use crate::{ syntax_directed_operations::function_definitions::CompileFunctionBodyData, types::{String, Value}, }, + engine::context::NoGcScope, heap::{CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues}, }; use oxc_ast::ast::{self, Statement}; @@ -105,7 +106,11 @@ pub(crate) struct ExecutableHeapData { } impl Executable { - pub(crate) fn compile_script(agent: &mut Agent, script: ScriptIdentifier) -> Self { + pub(crate) fn compile_script( + agent: &mut Agent, + gc: NoGcScope, + script: ScriptIdentifier, + ) -> Self { if agent.options.print_internals { eprintln!(); eprintln!("=== Compiling Script ==="); @@ -115,7 +120,7 @@ impl Executable { // not move under any circumstances during heap operations. let body: &[Statement] = unsafe { std::mem::transmute(agent[script].ecmascript_code.body.as_slice()) }; - let mut ctx = CompileContext::new(agent); + let mut ctx = CompileContext::new(agent, gc); ctx.compile_statements(body); ctx.do_implicit_return(); @@ -124,9 +129,10 @@ impl Executable { pub(crate) fn compile_function_body( agent: &mut Agent, + gc: NoGcScope, data: CompileFunctionBodyData<'_>, ) -> Self { - let mut ctx = CompileContext::new(agent); + let mut ctx = CompileContext::new(agent, gc); let is_concise = data.is_concise_body; @@ -139,13 +145,13 @@ impl Executable { ctx.finish() } - pub(crate) fn compile_eval_body(agent: &mut Agent, body: &[Statement]) -> Self { + pub(crate) fn compile_eval_body(agent: &mut Agent, gc: NoGcScope, body: &[Statement]) -> Self { if agent.options.print_internals { eprintln!(); eprintln!("=== Compiling Eval Body ==="); eprintln!(); } - let mut ctx = CompileContext::new(agent); + let mut ctx = CompileContext::new(agent, gc); ctx.compile_statements(body); ctx.do_implicit_return(); @@ -196,7 +202,7 @@ impl Executable { } #[inline] - pub(super) fn fetch_identifier(self, agent: &Agent, index: usize) -> String { + pub(super) fn fetch_identifier(self, agent: &Agent, index: usize) -> String<'static> { // 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 @@ -269,60 +275,6 @@ pub(super) fn get_instruction(instructions: &[u8], ip: &mut usize) -> Option Option { - get_instruction(&self.instructions, ip) - } - - pub(crate) fn compile_script(agent: &mut Agent, script: ScriptIdentifier) -> Executable { - if agent.options.print_internals { - eprintln!(); - eprintln!("=== Compiling Script ==="); - eprintln!(); - } - // SAFETY: Script uniquely owns the Program and the body buffer does - // not move under any circumstances during heap operations. - let body: &[Statement] = - unsafe { std::mem::transmute(agent[script].ecmascript_code.body.as_slice()) }; - let mut ctx = CompileContext::new(agent); - - ctx.compile_statements(body); - ctx.do_implicit_return(); - ctx.finish() - } - - pub(crate) fn compile_function_body( - agent: &mut Agent, - data: CompileFunctionBodyData<'_>, - ) -> Executable { - let mut ctx = CompileContext::new(agent); - - let is_concise = data.is_concise_body; - - ctx.compile_function_body(data); - - if is_concise { - ctx.do_implicit_return(); - } - - ctx.finish() - } - - pub(crate) fn compile_eval_body(agent: &mut Agent, body: &[Statement]) -> Executable { - if agent.options.print_internals { - eprintln!(); - eprintln!("=== Compiling Eval Body ==="); - eprintln!(); - } - let mut ctx = CompileContext::new(agent); - - ctx.compile_statements(body); - ctx.do_implicit_return(); - ctx.finish() - } -} - impl Index for Agent { type Output = ExecutableHeapData; diff --git a/nova_vm/src/engine/bytecode/iterator.rs b/nova_vm/src/engine/bytecode/iterator.rs index 0699a0793..576b9ef9b 100644 --- a/nova_vm/src/engine/bytecode/iterator.rs +++ b/nova_vm/src/engine/bytecode/iterator.rs @@ -47,7 +47,7 @@ impl VmIterator { if let Some(result) = result { Ok(Some(match result { PropertyKey::Integer(int) => { - Value::from_string(agent, format!("{}", int.into_i64())) + Value::from_string(agent, gc.nogc(), format!("{}", int.into_i64())) } PropertyKey::SmallString(data) => Value::SmallString(data), PropertyKey::String(data) => Value::String(data), @@ -68,6 +68,7 @@ impl VmIterator { )?; let Ok(result) = Object::try_from(result) else { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Iterator returned a non-object result", )); @@ -120,7 +121,6 @@ impl VmIterator { pub(super) fn from_value( agent: &mut Agent, mut gc: GcScope<'_, '_>, - value: Value, ) -> JsResult { // a. Let method be ? GetMethod(obj, %Symbol.iterator%). @@ -133,6 +133,7 @@ impl VmIterator { let Some(method) = method else { // 3. If method is undefined, throw a TypeError exception. return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Iterator method cannot be undefined", )); diff --git a/nova_vm/src/engine/bytecode/vm.rs b/nova_vm/src/engine/bytecode/vm.rs index 9c40ddafe..05d20137d 100644 --- a/nova_vm/src/engine/bytecode/vm.rs +++ b/nova_vm/src/engine/bytecode/vm.rs @@ -149,7 +149,6 @@ impl SuspendedVm { self, agent: &mut Agent, gc: GcScope<'_, '_>, - executable: Executable, value: Value, ) -> ExecutionResult { @@ -161,7 +160,6 @@ impl SuspendedVm { self, agent: &mut Agent, gc: GcScope<'_, '_>, - executable: Executable, err: Value, ) -> ExecutionResult { @@ -217,7 +215,6 @@ impl Vm { pub(crate) fn execute( agent: &mut Agent, gc: GcScope<'_, '_>, - executable: Executable, arguments: Option<&[Value]>, ) -> ExecutionResult { @@ -266,7 +263,6 @@ impl Vm { mut self, agent: &mut Agent, gc: GcScope<'_, '_>, - executable: Executable, value: Value, ) -> ExecutionResult { @@ -278,7 +274,6 @@ impl Vm { mut self, agent: &mut Agent, gc: GcScope<'_, '_>, - executable: Executable, err: Value, ) -> ExecutionResult { @@ -293,7 +288,6 @@ impl Vm { mut self, agent: &mut Agent, mut gc: GcScope<'_, '_>, - executable: Executable, ) -> ExecutionResult { #[cfg(feature = "interleaved-gc")] @@ -373,7 +367,6 @@ impl Vm { fn execute_instruction( agent: &mut Agent, mut gc: GcScope<'_, '_>, - vm: &mut Vm, executable: Executable, instr: &Instr, @@ -384,7 +377,8 @@ impl Vm { match instr.kind { Instruction::ArrayCreate => { vm.stack.push( - array_create(agent, 0, instr.args[0].unwrap() as usize, None)?.into_value(), + array_create(agent, gc.nogc(), 0, instr.args[0].unwrap() as usize, None)? + .into_value(), ); } Instruction::ArrayPush => { @@ -450,7 +444,7 @@ impl Vm { // 2. Return ? envRec.GetThisBinding(). vm.result = Some(match env_rec { EnvironmentIndex::Declarative(_) => unreachable!(), - EnvironmentIndex::Function(idx) => idx.get_this_binding(agent)?, + EnvironmentIndex::Function(idx) => idx.get_this_binding(agent, gc.nogc())?, EnvironmentIndex::Global(idx) => idx.get_this_binding(agent).into_value(), EnvironmentIndex::Object(_) => unreachable!(), }); @@ -514,7 +508,7 @@ impl Vm { ); } Instruction::ToObject => { - vm.result = Some(to_object(agent, vm.result.unwrap())?.into_value()); + vm.result = Some(to_object(agent, gc.nogc(), vm.result.unwrap())?.into_value()); } Instruction::ApplyStringOrNumericBinaryOperator(op_text) => { let lval = vm.stack.pop().unwrap(); @@ -579,11 +573,11 @@ impl Vm { // env, // privateEnv // ). - let closure = ordinary_function_create(agent, params); + let closure = ordinary_function_create(agent, gc.nogc(), params); // 8. Perform MakeMethod(closure, object). make_method(agent, closure, object); // 2. Perform SetFunctionName(closure, propKey). - set_function_name(agent, closure, prop_key, None); + set_function_name(agent, gc.nogc(), closure, prop_key, None); // 3. Return ? DefineMethodProperty( // object, // methodDef.[[Key]], @@ -672,12 +666,18 @@ impl Vm { // env, // privateEnv // ). - let closure = ordinary_function_create(agent, params); + let closure = ordinary_function_create(agent, gc.nogc(), params); // 7. Perform MakeMethod(closure, object). let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); make_method(agent, closure, object); // 8. Perform SetFunctionName(closure, propKey, "get"). - set_function_name(agent, closure, prop_key, Some(BUILTIN_STRING_MEMORY.get)); + set_function_name( + agent, + gc.nogc(), + closure, + prop_key, + Some(BUILTIN_STRING_MEMORY.get), + ); // 9. If propKey is a Private Name, then // a. Return PrivateElement { [[Key]]: propKey, [[Kind]]: accessor, [[Get]]: closure, [[Set]]: undefined }. // 10. Else, @@ -735,12 +735,18 @@ impl Vm { // env, // privateEnv // ). - let closure = ordinary_function_create(agent, params); + let closure = ordinary_function_create(agent, gc.nogc(), params); // 6. Perform MakeMethod(closure, object). let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); make_method(agent, closure, object); // 7. Perform SetFunctionName(closure, propKey, "set"). - set_function_name(agent, closure, prop_key, Some(BUILTIN_STRING_MEMORY.set)); + set_function_name( + agent, + gc.nogc(), + closure, + prop_key, + Some(BUILTIN_STRING_MEMORY.set), + ); // 8. If propKey is a Private Name, then // a. Return PrivateElement { [[Key]]: propKey, [[Kind]]: accessor, [[Get]]: undefined, [[Set]]: closure }. // 9. Else, @@ -883,7 +889,7 @@ impl Vm { env: lexical_environment, private_env: private_environment, }; - let function = ordinary_function_create(agent, params); + let function = ordinary_function_create(agent, gc.nogc(), params); let name = if let Some(parameter) = &identifier { match parameter { NamedEvaluationParameter::Result => { @@ -902,7 +908,7 @@ impl Vm { } else { String::EMPTY_STRING.into() }; - set_function_name(agent, function, name, None); + set_function_name(agent, gc.nogc(), function, name, None); vm.result = Some(function.into_value()); } Instruction::InstantiateOrdinaryFunctionExpression => { @@ -942,7 +948,7 @@ impl Vm { }; (name, lexical_environment, false) } else if let Some(binding_identifier) = &function_expression.id { - let name = String::from_str(agent, &binding_identifier.name); + let name = String::from_str(agent, gc.nogc(), &binding_identifier.name); let func_env = new_declarative_environment(agent, Some(lexical_environment)); func_env.create_immutable_binding(agent, name, false); (name.into(), EnvironmentIndex::Declarative(func_env), true) @@ -962,11 +968,11 @@ impl Vm { env, private_env: private_environment, }; - let function = ordinary_function_create(agent, params); + let function = ordinary_function_create(agent, gc.nogc(), params); if let Some(compiled_bytecode) = compiled_bytecode { agent[function].compiled_bytecode = Some(compiled_bytecode); } - set_function_name(agent, function, name, None); + set_function_name(agent, gc.nogc(), function, name, None); if !function_expression.r#async && !function_expression.generator { make_constructor(agent, function, None, None); } @@ -1065,11 +1071,11 @@ impl Vm { env: lexical_environment, private_env: private_environment, }; - let function = ordinary_function_create(agent, params); + let function = ordinary_function_create(agent, gc.nogc(), params); if let Some(compiled_bytecode) = compiled_bytecode { agent[function].compiled_bytecode = Some(compiled_bytecode); } - set_function_name(agent, function, class_name.into(), None); + set_function_name(agent, gc.nogc(), function, class_name.into(), None); make_constructor(agent, function, Some(false), Some(proto)); agent[function].ecmascript_function.home_object = Some(proto); agent[function].ecmascript_function.constructor_status = @@ -1285,7 +1291,11 @@ impl Vm { "'{}' is not a constructor.", constructor.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::TypeError, + error_message, + )); }; if cfg!(feature = "interleaved-gc") { @@ -1342,7 +1352,11 @@ impl Vm { .string_repr(agent, gc.reborrow(),) .as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::TypeError, + error_message, + )); }; // 6. Let result be ? Construct(func, argList, newTarget). let result = construct( @@ -1357,7 +1371,7 @@ impl Vm { unreachable!(); }; // 8. Perform ? thisER.BindThisValue(result). - this_er.bind_this_value(agent, result.into_value())?; + this_er.bind_this_value(agent, gc.nogc(), result.into_value())?; // 9. Let F be thisER.[[FunctionObject]]. // 10. Assert: F is an ECMAScript function object. let Function::ECMAScriptFunction(_f) = agent[this_er].function_object else { @@ -1482,7 +1496,11 @@ impl Vm { "The right-hand side of an `in` expression must be an object, got '{}'.", rval.string_repr(agent, gc.reborrow(),).as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception( + gc.nogc(), + ExceptionType::TypeError, + error_message, + )); }; // 6. Return ? HasProperty(rval, ? ToPropertyKey(lval)). let property_key = to_property_key(agent, gc.reborrow(), lval)?; @@ -1677,7 +1695,9 @@ impl Vm { .unwrap() .lexical_environment; let name = executable.fetch_identifier(agent, instr.args[0].unwrap() as usize); - lex_env.create_immutable_binding(agent, name, true).unwrap(); + lex_env + .create_immutable_binding(agent, gc.nogc(), name, true) + .unwrap(); } Instruction::CreateCatchBinding => { let lex_env = agent @@ -1771,7 +1791,7 @@ impl Vm { // Var binding, var {} = a; None }; - let object = to_object(agent, vm.stack.pop().unwrap())?; + let object = to_object(agent, gc.nogc(), vm.stack.pop().unwrap())?; Self::execute_simple_object_binding(agent, gc, vm, executable, object, env)? } Instruction::BindingPatternBind @@ -1801,7 +1821,7 @@ impl Vm { result_string.push_str(string.as_str(agent)); } vm.stack.truncate(last_item); - vm.result = Some(String::from_string(agent, result_string).into_value()); + vm.result = Some(String::from_string(agent, gc.nogc(), result_string).into_value()); } Instruction::Delete => { let refer = vm.reference.take().unwrap(); @@ -1820,12 +1840,13 @@ impl Vm { // b. If IsSuperReference(ref) is true, throw a ReferenceError exception. if is_super_reference(&refer) { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::ReferenceError, "Invalid delete involving 'super'.", )); } // c. Let baseObj be ? ToObject(ref.[[Base]]). - let base_obj = to_object(agent, base)?; + let base_obj = to_object(agent, gc.nogc(), base)?; // d. If ref.[[ReferencedName]] is not a property key, then // TODO: Is this relevant? // i. Set ref.[[ReferencedName]] to ? ToPropertyKey(ref.[[ReferencedName]]). @@ -1838,6 +1859,7 @@ impl Vm { // f. If deleteStatus is false and ref.[[Strict]] is true, throw a TypeError exception. if !delete_status && refer.strict { return Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "Cannot delete property", )); @@ -1877,7 +1899,7 @@ impl Vm { // choose to avoid the actual creation of that object. } Instruction::EnumerateObjectProperties => { - let object = to_object(agent, vm.result.take().unwrap()).unwrap(); + let object = to_object(agent, gc.nogc(), vm.result.take().unwrap()).unwrap(); vm.iterator_stack .push(VmIterator::ObjectProperties(ObjectPropertiesIterator::new( object, @@ -1923,7 +1945,7 @@ impl Vm { Instruction::IteratorRestIntoArray => { let mut iterator = vm.iterator_stack.pop().unwrap(); let capacity = iterator.remaining_length_estimate(agent).unwrap_or(0); - let array = array_create(agent, 0, capacity, None)?; + let array = array_create(agent, gc.nogc(), 0, capacity, None)?; let mut idx: u32 = 0; while let Some(value) = iterator.step_value(agent, gc.reborrow())? { @@ -1977,7 +1999,6 @@ impl Vm { fn execute_simple_array_binding( agent: &mut Agent, mut gc: GcScope<'_, '_>, - vm: &mut Vm, executable: Executable, mut iterator: VmIterator, @@ -2004,10 +2025,12 @@ impl Vm { Instruction::BindingPatternBindRest | Instruction::BindingPatternGetRestValue => { break_after_bind = true; if iterator_is_done { - array_create(agent, 0, 0, None).unwrap().into_value() + array_create(agent, gc.nogc(), 0, 0, None) + .unwrap() + .into_value() } else { let capacity = iterator.remaining_length_estimate(agent).unwrap_or(0); - let rest = array_create(agent, 0, capacity, None).unwrap(); + let rest = array_create(agent, gc.nogc(), 0, capacity, None).unwrap(); let mut idx = 0u32; while let Some(result) = iterator.step_value(agent, gc.reborrow())? { create_data_property_or_throw( @@ -2074,7 +2097,6 @@ impl Vm { fn execute_simple_object_binding( agent: &mut Agent, mut gc: GcScope<'_, '_>, - vm: &mut Vm, executable: Executable, object: Object, @@ -2155,7 +2177,6 @@ impl Vm { fn execute_nested_simple_binding( agent: &mut Agent, mut gc: GcScope<'_, '_>, - vm: &mut Vm, executable: Executable, value: Value, @@ -2175,7 +2196,7 @@ impl Vm { ) } Instruction::BeginSimpleObjectBindingPattern => { - let object = to_object(agent, value)?; + let object = to_object(agent, gc.nogc(), value)?; Vm::execute_simple_object_binding( agent, gc.reborrow(), @@ -2201,7 +2222,6 @@ impl Vm { fn apply_string_or_numeric_binary_operator( agent: &mut Agent, mut gc: GcScope<'_, '_>, - lval: Value, op_text: BinaryOperator, rval: Value, @@ -2217,15 +2237,40 @@ fn apply_string_or_numeric_binary_operator( let rprim = to_primitive(agent, gc.reborrow(), rval, None)?; // c. If lprim is a String or rprim is a String, then - if lprim.is_string() || rprim.is_string() { - // i. Let lstr be ? ToString(lprim). - let lstr = to_string(agent, gc.reborrow(), lprim)?; - - // ii. Let rstr be ? ToString(rprim). - let rstr = to_string(agent, gc.reborrow(), rprim)?; - - // iii. Return the string-concatenation of lstr and rstr. - return Ok(String::concat(agent, [lstr, rstr]).into_value()); + match (String::try_from(lprim), String::try_from(rprim)) { + (Ok(lstr), Ok(rstr)) => { + // iii. Return the string-concatenation of lstr and rstr. + return Ok(String::concat(agent, gc.nogc(), [lstr, rstr]).into_value()); + } + (Ok(lstr), Err(_)) => { + let lstr = lstr.bind(gc.nogc()).scope(agent, gc.nogc()); + // ii. Let rstr be ? ToString(rprim). + let rstr = to_string(agent, gc.reborrow(), rprim)? + .unbind() + .bind(gc.nogc()); + // iii. Return the string-concatenation of lstr and rstr. + return Ok(String::concat( + agent, + gc.nogc(), + [lstr.get(agent).bind(gc.nogc()), rstr], + ) + .into_value()); + } + (Err(_), Ok(rstr)) => { + let rstr = rstr.bind(gc.nogc()).scope(agent, gc.nogc()); + // i. Let lstr be ? ToString(lprim). + let lstr = to_string(agent, gc.reborrow(), lprim)? + .unbind() + .bind(gc.nogc()); + // iii. Return the string-concatenation of lstr and rstr. + return Ok(String::concat( + agent, + gc.nogc(), + [lstr, rstr.get(agent).bind(gc.nogc())], + ) + .into_value()); + } + (Err(_), Err(_)) => {} } // d. Set lval to lprim. @@ -2246,16 +2291,15 @@ fn apply_string_or_numeric_binary_operator( if let (Ok(lnum), Ok(rnum)) = (BigInt::try_from(lnum), BigInt::try_from(rnum)) { Ok(match op_text { // a. If opText is **, return ? BigInt::exponentiate(lnum, rnum). - BinaryOperator::Exponential => { - BigInt::exponentiate(agent, lnum, rnum).map(|bigint| bigint.into_value())? - } + BinaryOperator::Exponential => BigInt::exponentiate(agent, gc.nogc(), lnum, rnum) + .map(|bigint| bigint.into_value())?, // b. If opText is /, return ? BigInt::divide(lnum, rnum). BinaryOperator::Division => { - BigInt::divide(agent, lnum, rnum).map(|bigint| bigint.into_value())? + BigInt::divide(agent, gc.nogc(), lnum, rnum).map(|bigint| bigint.into_value())? } // c. If opText is %, return ? BigInt::remainder(lnum, rnum). BinaryOperator::Remainder => { - BigInt::remainder(agent, lnum, rnum).map(|bigint| bigint.into_value())? + BigInt::remainder(agent, gc.nogc(), lnum, rnum).map(|bigint| bigint.into_value())? } // d. If opText is >>>, return ? BigInt::unsignedRightShift(lnum, rnum). BinaryOperator::ShiftRightZeroFill => todo!(), @@ -2317,6 +2361,7 @@ fn apply_string_or_numeric_binary_operator( } else { // 5. If Type(lnum) is not Type(rnum), throw a TypeError exception. Err(agent.throw_exception_with_static_message( + gc.nogc(), ExceptionType::TypeError, "The left and right-hand sides do not have the same type.", )) @@ -2420,7 +2465,6 @@ fn typeof_operator(_: &mut Agent, val: Value) -> String { pub(crate) fn instanceof_operator( agent: &mut Agent, mut gc: GcScope<'_, '_>, - value: impl IntoValue, target: impl IntoValue, ) -> JsResult { @@ -2433,7 +2477,7 @@ pub(crate) fn instanceof_operator( .string_repr(agent, gc.reborrow(),) .as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 2. Let instOfHandler be ? GetMethod(target, @@hasInstance). let inst_of_handler = get_method( @@ -2463,7 +2507,7 @@ pub(crate) fn instanceof_operator( .string_repr(agent, gc.reborrow(),) .as_str(agent) ); - return Err(agent.throw_exception(ExceptionType::TypeError, error_message)); + return Err(agent.throw_exception(gc.nogc(), ExceptionType::TypeError, error_message)); }; // 5. Return ? OrdinaryHasInstance(target, V). Ok(ordinary_has_instance(agent, gc, target, value)?) diff --git a/nova_vm/src/engine/context.rs b/nova_vm/src/engine/context.rs index 120a3894c..9c8efb005 100644 --- a/nova_vm/src/engine/context.rs +++ b/nova_vm/src/engine/context.rs @@ -24,24 +24,57 @@ pub(crate) struct GcToken; #[derive(Debug)] pub(crate) struct ScopeToken; -/// # Access to garbage collector +/// # JavaScript call scope that may trigger garbage collection /// -/// Holding this token is required for garbage collection. +/// This marker represents access to the JavaScript call stack and specifically +/// gives the call stack the possibility of performing garbage collection. +/// In the engine, most values are by-default not rooted during operations +/// which means that garbage collection invalidates them. This GcScope marker +/// is a way for the borrow checker to ensure that all values are rooted / +/// returned to the heap / registered with the heap before the garbage +/// collector potentially runs. +/// +/// In essence, this is a compile-time method of ensuring safepoint garbage +/// collection safety. #[derive(Debug)] pub struct GcScope<'a, 'b> { + /// A GcScope "owns" the GC access: There is only ever one "active" GcToken + /// in the world at a time. Reborrowing a GcScope binds the previous one, + /// so its contained GcToken is "inactive" during the lifetime of the + /// reborrowed one. gc: GcToken, + /// A GcScope also "owns" the scope access: This is the access to the + /// Scoped roots stack. This is not yet well-defined but probably only + /// GC scopes are allowed to shrink the Scoped roots stack. scope: ScopeToken, + /// We must also keep an exclusive borrow on a GcToken. This enables + /// various engine values to reborrow this lifetime as shared and that way + /// have the borrow checker check that those values are not used while + /// garbage collection may run. _gc_marker: PhantomData<&'a mut GcToken>, + /// We keep a shared borrow on the ScopeToken. This is not yet well-defined + /// but probably we'll create new ScopeToken borrow lifetimes using the + /// for<'a> closure trick. _scope_marker: PhantomData<&'b ScopeToken>, } -/// # Access to the JavaScript call stack +/// # JavaScript call scope that may not trigger garbage collection /// -/// Holding this token is required for JavaScript calls. -#[derive(Debug)] -pub struct Scope<'a> { - inner: ScopeToken, - _marker: PhantomData<&'a ScopeToken>, +/// This marker represents access to the JavaScript call stack in a way that +/// cannot trigger garbage collection. Actions like working with primitive +/// JavaScript Values and accessing non-Proxy object prototypes are examples of +/// actions that can never trigger garbage collection. +/// +/// This marker allows performing these sort of actions without rooting other +/// values held on the stack. +#[derive(Debug, Clone, Copy)] +pub struct NoGcScope<'a, 'b> { + /// A NoGcScope does not own the GC access, and naturally cannot trigger + /// garbage collection. We keep a shared borrow on this lifetime to ensure + /// that the GcScope we derive from cannot be used concurrently. + _gc_marker: PhantomData<&'a GcToken>, + /// We also don't own scope access. This is not yet well-defined. + _scope_marker: PhantomData<&'b ScopeToken>, } impl GcToken { @@ -76,8 +109,12 @@ impl<'a, 'b> GcScope<'a, 'b> { } } + /// Create a GcScope marker that inherits the current GcScope's lifetimes. + /// This reborrowing is necessary to ensure that only one GcScope is active + /// at any point in time, and the existence of the active GcScope binds any + /// "parent" GcScopes from being used concurrently. #[inline] - pub fn reborrow(&mut self) -> Self { + pub fn reborrow(&mut self) -> GcScope<'_, 'b> { Self { gc: GcToken, scope: ScopeToken, @@ -86,29 +123,41 @@ impl<'a, 'b> GcScope<'a, 'b> { } } - pub(crate) fn print(&mut self) { - println!("GC!"); + /// Create a NoGcScope marker that is used to bind the garbage collector + /// lifetime to various engine values. Existence of the NoGcScope is a + /// build-time proof that garbage collection cannot happen. + /// + /// When a garbage collection can happen, the borrow checker will ensure + /// that all engine values that were boudn to the NoGcScope are dropped or + /// are registered with the heap using Scoped or Global roots. + #[inline] + pub fn nogc(&self) -> NoGcScope<'_, 'b> { + NoGcScope::from_gc(self) } -} -impl Scope<'_> { + /// Turn a GcScope marker into a NoGcScope. This is otherwise equivalent to + /// [the `nogc()` method](Self::nogc) with the exception that this consumes + /// the parent GcScope. + /// + /// This is useful when a method ends in a NoGC scope based return within + /// an if/else branch while another branch still uses the GcScope. The + /// borrow checker does not like this with the `nogc()` method but allows + /// it with this method. #[inline] - pub(crate) fn new(_: &mut ScopeToken) -> Self { - Self { - inner: ScopeToken, - _marker: PhantomData, + pub fn into_nogc(self) -> NoGcScope<'a, 'b> { + NoGcScope { + _gc_marker: PhantomData, + _scope_marker: PhantomData, } } +} +impl<'a, 'b> NoGcScope<'a, 'b> { #[inline] - pub fn reborrow(&self) -> Self { + pub(crate) fn from_gc(_: &GcScope<'a, 'b>) -> Self { Self { - inner: ScopeToken, - _marker: PhantomData, + _gc_marker: PhantomData, + _scope_marker: PhantomData, } } - - pub(crate) fn print(&self) { - println!("GC!"); - } } diff --git a/nova_vm/src/engine/rootable.rs b/nova_vm/src/engine/rootable.rs index 5e3614706..230aa4a16 100644 --- a/nova_vm/src/engine/rootable.rs +++ b/nova_vm/src/engine/rootable.rs @@ -149,7 +149,7 @@ mod private { impl RootableSealed for SetIterator {} #[cfg(feature = "shared-array-buffer")] impl RootableSealed for SharedArrayBuffer {} - impl RootableSealed for String {} + impl RootableSealed for String<'_> {} impl RootableSealed for Symbol {} #[cfg(feature = "array-buffer")] impl RootableSealed for TypedArray {} @@ -204,7 +204,7 @@ pub trait Rootable: std::fmt::Debug + Copy + RootableSealed { pub enum HeapRootData { // First the Value variants: This list should match 1-to-1 the list in // value.rs, but with the - String(HeapString) = STRING_DISCRIMINANT, + String(HeapString<'static>) = STRING_DISCRIMINANT, Symbol(Symbol) = SYMBOL_DISCRIMINANT, Number(HeapNumber) = NUMBER_DISCRIMINANT, BigInt(HeapBigInt) = BIGINT_DISCRIMINANT, @@ -295,7 +295,7 @@ pub enum HeapRootData { /// as-is. It only make sense within some root list referring type, /// specifically `Local` and `Global`, and then those types should never /// appear within the heap directly. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] #[repr(transparent)] pub struct HeapRootRef(u32); diff --git a/nova_vm/src/engine/rootable/scoped.rs b/nova_vm/src/engine/rootable/scoped.rs index 1d8e8405f..5dfbb2550 100644 --- a/nova_vm/src/engine/rootable/scoped.rs +++ b/nova_vm/src/engine/rootable/scoped.rs @@ -6,7 +6,10 @@ use std::marker::PhantomData; use crate::{ ecmascript::execution::Agent, - engine::rootable::{HeapRootRef, Rootable}, + engine::{ + context::NoGcScope, + rootable::{HeapRootRef, Rootable}, + }, }; /// # Scoped heap root @@ -18,21 +21,23 @@ use crate::{ /// current call context. This type is intended for cheap rooting of JavaScript /// Values that need to be used after calling into functions that may trigger /// garbage collection. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Hash)] #[repr(transparent)] -pub struct Scoped { - inner: T::RootRepr, +pub struct Scoped<'a, T: 'static + Rootable> { + pub(crate) inner: T::RootRepr, _marker: PhantomData, + _scope: PhantomData<&'a ()>, } -impl Scoped { - pub fn new(agent: &Agent, value: T) -> Self { +impl<'scope, T: 'static + Rootable> Scoped<'scope, T> { + pub fn new(agent: &Agent, _gc: NoGcScope<'_, 'scope>, value: T) -> Self { let value = match T::to_root_repr(value) { Ok(stack_repr) => { // The value doesn't need rooting. return Self { inner: stack_repr, _marker: PhantomData, + _scope: PhantomData, }; } Err(heap_data) => heap_data, @@ -43,10 +48,11 @@ impl Scoped { Self { inner: T::from_heap_ref(HeapRootRef::from_index(next_index)), _marker: PhantomData, + _scope: PhantomData, } } - pub fn get(self, agent: &Agent) -> T { + pub fn get(&self, agent: &Agent) -> T { match T::from_root_repr(&self.inner) { Ok(value) => value, Err(heap_root_ref) => { @@ -61,6 +67,77 @@ impl Scoped { } } } + + pub 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. + *self = Self { + inner: stack_repr, + _marker: PhantomData, + _scope: PhantomData, + }; + return; + } + Err(heap_data) => heap_data, + }; + match T::from_root_repr(&self.inner) { + Ok(_) => { + // We do not have an existing 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); + *self = Self { + inner: T::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; + } + } + } + + pub fn from_scoped( + agent: &Agent, + gc: NoGcScope<'_, 'scope>, + scoped: Scoped<'scope, U>, + value: T, + ) -> Self { + let heap_data = match T::to_root_repr(value) { + Ok(stack_repr) => { + // The value doesn't need rooting. + return Self { + inner: stack_repr, + _marker: PhantomData, + _scope: PhantomData, + }; + } + Err(heap_data) => heap_data, + }; + let Err(heap_root_ref) = U::from_root_repr(&scoped.inner) else { + // Previous Scoped is an on-stack value, we can't reuse its heap + // slot. + return Self::new(agent, gc, value); + }; + // Previous Scoped had a heap slot, we can reuse it. + 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; + Self { + inner: T::from_heap_ref(heap_root_ref), + _marker: PhantomData, + _scope: PhantomData, + } + } } #[cold] diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index d6326bfa8..293202469 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -170,8 +170,8 @@ pub trait CreateHeapData { fn create(&mut self, data: T) -> F; } -impl CreateHeapData<&str, String> for Heap { - fn create(&mut self, data: &str) -> String { +impl CreateHeapData<&str, String<'static>> for Heap { + fn create(&mut self, data: &str) -> String<'static> { if let Ok(value) = String::try_from(data) { value } else { @@ -181,8 +181,8 @@ impl CreateHeapData<&str, String> for Heap { } } -impl CreateHeapData for Heap { - fn create(&mut self, data: std::string::String) -> String { +impl CreateHeapData> for Heap { + fn create(&mut self, data: std::string::String) -> String<'static> { if let Ok(value) = String::try_from(data.as_str()) { value } else { @@ -302,7 +302,7 @@ impl Heap { /// SmallString. All SmallStrings must be kept on the stack to ensure that /// comparison between heap allocated strings and SmallStrings can be /// guaranteed to never equal true. - pub(crate) unsafe fn alloc_str(&mut self, message: &str) -> String { + pub(crate) unsafe fn alloc_str(&mut self, message: &str) -> String<'static> { let found = self.find_equal_string(message); if let Some(idx) = found { return idx; @@ -323,7 +323,7 @@ impl Heap { /// SmallString. All SmallStrings must be kept on the stack to ensure that /// comparison between heap allocated strings and SmallStrings can be /// guaranteed to never equal true. - unsafe fn alloc_string(&mut self, message: std::string::String) -> String { + unsafe fn alloc_string(&mut self, message: std::string::String) -> String<'static> { let found = self.find_equal_string(message.as_str()); if let Some(idx) = found { return idx; @@ -344,7 +344,7 @@ impl Heap { /// SmallString. All SmallStrings must be kept on the stack to ensure that /// comparison between heap allocated strings and SmallStrings can be /// guaranteed to never equal true. - pub(crate) unsafe fn alloc_static_str(&mut self, message: &'static str) -> String { + pub(crate) unsafe fn alloc_static_str(&mut self, message: &'static str) -> String<'static> { let found = self.find_equal_string(message); if let Some(idx) = found { return idx; @@ -353,7 +353,7 @@ impl Heap { self.create(data) } - fn find_equal_string(&self, message: &str) -> Option { + fn find_equal_string(&self, message: &str) -> Option> { debug_assert!(message.len() > 7); self.strings .iter() @@ -434,7 +434,7 @@ impl PrimitiveHeap<'_> { /// Helper trait for primitive heap data indexing. pub(crate) trait PrimitiveHeapIndexable: Index - + Index + + Index, Output = StringHeapData> + Index { } diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index 6e713f0f7..e08e7d011 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -177,7 +177,7 @@ pub(crate) struct WorkQueues { pub set_iterators: Vec, #[cfg(feature = "shared-array-buffer")] pub shared_array_buffers: Vec, - pub strings: Vec, + pub strings: Vec>, pub symbols: Vec, #[cfg(feature = "array-buffer")] pub typed_arrays: Vec, @@ -1075,13 +1075,13 @@ pub(crate) fn sweep_heap_elements_vector_descriptors( } } -pub(crate) fn sweep_side_table_values( +pub(crate) fn sweep_side_table_values<'a, T, K, V>( side_table: &mut AHashMap, compactions: &CompactionList, marks: &[bool], ) where - T: ?Sized, - K: IntoBaseIndex + From> + Copy + Ord + Hash, + T: 'a + ?Sized, + K: IntoBaseIndex<'a, T> + From> + Copy + Ord + Hash, { let mut keys_to_remove = Vec::with_capacity(marks.len() / 4); let mut keys_to_reassign = Vec::with_capacity(marks.len() / 4); diff --git a/nova_vm/src/heap/indexes.rs b/nova_vm/src/heap/indexes.rs index c819755b9..b924eacbb 100644 --- a/nova_vm/src/heap/indexes.rs +++ b/nova_vm/src/heap/indexes.rs @@ -17,29 +17,32 @@ use crate::ecmascript::builtins::{ weak_map::data::WeakMapHeapData, weak_ref::data::WeakRefHeapData, weak_set::data::WeakSetHeapData, }; -use crate::ecmascript::{ - builtins::{ - control_abstraction_objects::generator_objects::GeneratorHeapData, - embedder_object::data::EmbedderObjectHeapData, - error::ErrorHeapData, - finalization_registry::data::FinalizationRegistryHeapData, - indexed_collections::array_objects::array_iterator_objects::array_iterator::ArrayIteratorHeapData, - keyed_collections::{ - map_objects::map_iterator_objects::map_iterator::MapIteratorHeapData, - set_objects::set_iterator_objects::set_iterator::SetIteratorHeapData, +use crate::{ + ecmascript::{ + builtins::{ + control_abstraction_objects::generator_objects::GeneratorHeapData, + embedder_object::data::EmbedderObjectHeapData, + error::ErrorHeapData, + finalization_registry::data::FinalizationRegistryHeapData, + indexed_collections::array_objects::array_iterator_objects::array_iterator::ArrayIteratorHeapData, + keyed_collections::{ + map_objects::map_iterator_objects::map_iterator::MapIteratorHeapData, + set_objects::set_iterator_objects::set_iterator::SetIteratorHeapData, + }, + map::data::MapHeapData, + primitive_objects::PrimitiveObjectHeapData, + promise::data::PromiseHeapData, + proxy::data::ProxyHeapData, + set::data::SetHeapData, + ArrayHeapData, + }, + types::{ + BigIntHeapData, BoundFunctionHeapData, BuiltinConstructorHeapData, + BuiltinFunctionHeapData, ECMAScriptFunctionHeapData, NumberHeapData, ObjectHeapData, + StringHeapData, SymbolHeapData, Value, }, - map::data::MapHeapData, - primitive_objects::PrimitiveObjectHeapData, - promise::data::PromiseHeapData, - proxy::data::ProxyHeapData, - set::data::SetHeapData, - ArrayHeapData, - }, - types::{ - BigIntHeapData, BoundFunctionHeapData, BuiltinConstructorHeapData, BuiltinFunctionHeapData, - ECMAScriptFunctionHeapData, NumberHeapData, ObjectHeapData, StringHeapData, SymbolHeapData, - Value, }, + engine::context::GcToken, }; use core::fmt::Debug; use std::{ @@ -54,58 +57,58 @@ use std::{marker::PhantomData, mem::size_of, num::NonZeroU32}; /// /// This index implies a tracing reference count from this /// struct to T at the given index. -pub struct BaseIndex(NonZeroU32, PhantomData); +pub struct BaseIndex<'a, T: ?Sized>(NonZeroU32, PhantomData, PhantomData<&'a GcToken>); const _INDEX_SIZE_IS_U32: () = assert!(size_of::>() == size_of::()); const _OPTION_INDEX_SIZE_IS_U32: () = assert!(size_of::>>() == size_of::()); -pub(crate) trait IntoBaseIndex { - fn into_base_index(self) -> BaseIndex; +pub(crate) trait IntoBaseIndex<'a, T: ?Sized> { + fn into_base_index(self) -> BaseIndex<'a, T>; } -impl Debug for BaseIndex { +impl<'a, T: ?Sized> Debug for BaseIndex<'a, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { assert!(self.0.get() != 0); (&self.0.get() - 1).fmt(f) } } -impl Clone for BaseIndex { +impl<'a, T: ?Sized> Clone for BaseIndex<'a, T> { fn clone(&self) -> Self { *self } } -impl Copy for BaseIndex {} +impl<'a, T: ?Sized> Copy for BaseIndex<'a, T> {} -impl PartialEq for BaseIndex { +impl<'a, T: ?Sized> PartialEq for BaseIndex<'a, T> { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } -impl Eq for BaseIndex {} +impl<'a, T: ?Sized> Eq for BaseIndex<'a, T> {} -impl PartialOrd for BaseIndex { +impl<'a, T: ?Sized> PartialOrd for BaseIndex<'a, T> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for BaseIndex { +impl<'a, T: ?Sized> Ord for BaseIndex<'a, T> { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.0.cmp(&other.0) } } -impl Hash for BaseIndex { +impl<'a, T: ?Sized> Hash for BaseIndex<'a, T> { fn hash(&self, state: &mut H) { self.0.hash(state); } } -impl BaseIndex { +impl<'a, T: ?Sized> BaseIndex<'a, T> { pub const fn into_index(self) -> usize { self.0.get() as usize - 1 } @@ -127,14 +130,22 @@ impl BaseIndex { assert!(value != u32::MAX); // SAFETY: Number is not max value and will not overflow to zero. // This check is done manually to allow const context. - Self(unsafe { NonZeroU32::new_unchecked(value + 1) }, PhantomData) + Self( + unsafe { NonZeroU32::new_unchecked(value + 1) }, + PhantomData, + PhantomData, + ) } pub const fn from_u32_index(value: u32) -> Self { assert!(value != u32::MAX); // SAFETY: Number is not max value and will not overflow to zero. // This check is done manually to allow const context. - Self(unsafe { NonZeroU32::new_unchecked(value + 1) }, PhantomData) + Self( + unsafe { NonZeroU32::new_unchecked(value + 1) }, + PhantomData, + PhantomData, + ) } pub const fn from_usize(value: usize) -> Self { @@ -142,14 +153,22 @@ impl BaseIndex { assert!(value != 0); // SAFETY: Number is not zero. // This check is done manually to allow const context. - Self(unsafe { NonZeroU32::new_unchecked(value) }, PhantomData) + Self( + unsafe { NonZeroU32::new_unchecked(value) }, + PhantomData, + PhantomData, + ) } pub const fn from_u32(value: u32) -> Self { assert!(value != 0); // SAFETY: Number is not zero. // This check is done manually to allow const context. - Self(unsafe { NonZeroU32::new_unchecked(value) }, PhantomData) + Self( + unsafe { NonZeroU32::new_unchecked(value) }, + PhantomData, + PhantomData, + ) } pub fn last(vec: &[Option]) -> Self @@ -161,59 +180,63 @@ impl BaseIndex { } } -impl Default for BaseIndex { +impl<'a, T> Default for BaseIndex<'a, T> { fn default() -> Self { Self::from_u32_index(0) } } #[cfg(feature = "array-buffer")] -pub type ArrayBufferIndex = BaseIndex; -pub type ArrayIndex = BaseIndex; -pub type ArrayIteratorIndex = BaseIndex; -pub type BigIntIndex = BaseIndex; -pub type BoundFunctionIndex = BaseIndex; -pub type BuiltinFunctionIndex = BaseIndex; -pub type BuiltinConstructorIndex = BaseIndex; +pub type ArrayBufferIndex = BaseIndex<'static, ArrayBufferHeapData>; +pub type ArrayIndex = BaseIndex<'static, ArrayHeapData>; +pub type ArrayIteratorIndex = BaseIndex<'static, ArrayIteratorHeapData>; +pub type BigIntIndex = BaseIndex<'static, BigIntHeapData>; +pub type BoundFunctionIndex = BaseIndex<'static, BoundFunctionHeapData>; +pub type BuiltinFunctionIndex = BaseIndex<'static, BuiltinFunctionHeapData>; +pub type BuiltinConstructorIndex = BaseIndex<'static, BuiltinConstructorHeapData>; #[cfg(feature = "array-buffer")] -pub type DataViewIndex = BaseIndex; +pub type DataViewIndex = BaseIndex<'static, DataViewHeapData>; #[cfg(feature = "date")] -pub type DateIndex = BaseIndex; -pub type ECMAScriptFunctionIndex = BaseIndex; -pub type ElementIndex = BaseIndex<[Option]>; -pub type EmbedderObjectIndex = BaseIndex; -pub type ErrorIndex = BaseIndex; -pub type FinalizationRegistryIndex = BaseIndex; -pub type GeneratorIndex = BaseIndex; -pub type MapIndex = BaseIndex; -pub type MapIteratorIndex = BaseIndex; -pub type NumberIndex = BaseIndex; -pub type ObjectIndex = BaseIndex; -pub type PrimitiveObjectIndex = BaseIndex; -pub type PromiseIndex = BaseIndex; -pub type ProxyIndex = BaseIndex; +pub type DateIndex = BaseIndex<'static, DateHeapData>; +pub type ECMAScriptFunctionIndex = BaseIndex<'static, ECMAScriptFunctionHeapData>; +pub type ElementIndex = BaseIndex<'static, [Option]>; +pub type EmbedderObjectIndex = BaseIndex<'static, EmbedderObjectHeapData>; +pub type ErrorIndex = BaseIndex<'static, ErrorHeapData>; +pub type FinalizationRegistryIndex = BaseIndex<'static, FinalizationRegistryHeapData>; +pub type GeneratorIndex = BaseIndex<'static, GeneratorHeapData>; +pub type MapIndex = BaseIndex<'static, MapHeapData>; +pub type MapIteratorIndex = BaseIndex<'static, MapIteratorHeapData>; +pub type NumberIndex = BaseIndex<'static, NumberHeapData>; +pub type ObjectIndex = BaseIndex<'static, ObjectHeapData>; +pub type PrimitiveObjectIndex = BaseIndex<'static, PrimitiveObjectHeapData>; +pub type PromiseIndex = BaseIndex<'static, PromiseHeapData>; +pub type ProxyIndex = BaseIndex<'static, ProxyHeapData>; #[cfg(feature = "regexp")] -pub type RegExpIndex = BaseIndex; -pub type SetIndex = BaseIndex; -pub type SetIteratorIndex = BaseIndex; +pub type RegExpIndex = BaseIndex<'static, RegExpHeapData>; +pub type SetIndex = BaseIndex<'static, SetHeapData>; +pub type SetIteratorIndex = BaseIndex<'static, SetIteratorHeapData>; #[cfg(feature = "shared-array-buffer")] -pub type SharedArrayBufferIndex = BaseIndex; -pub type StringIndex = BaseIndex; -pub type SymbolIndex = BaseIndex; +pub type SharedArrayBufferIndex = BaseIndex<'static, SharedArrayBufferHeapData>; +pub type StringIndex<'a> = BaseIndex<'a, StringHeapData>; +pub type SymbolIndex = BaseIndex<'static, SymbolHeapData>; #[cfg(feature = "array-buffer")] -pub type TypedArrayIndex = BaseIndex; +pub type TypedArrayIndex = BaseIndex<'static, TypedArrayHeapData>; #[cfg(feature = "weak-refs")] -pub type WeakMapIndex = BaseIndex; +pub type WeakMapIndex = BaseIndex<'static, WeakMapHeapData>; #[cfg(feature = "weak-refs")] -pub type WeakRefIndex = BaseIndex; +pub type WeakRefIndex = BaseIndex<'static, WeakRefHeapData>; #[cfg(feature = "weak-refs")] -pub type WeakSetIndex = BaseIndex; +pub type WeakSetIndex = BaseIndex<'static, WeakSetHeapData>; // Implement Default for ElementIndex: This is done to support Default // constructor of ElementsVector. impl Default for ElementIndex { fn default() -> Self { - Self(unsafe { NonZeroU32::new_unchecked(1) }, Default::default()) + Self( + unsafe { NonZeroU32::new_unchecked(1) }, + PhantomData, + PhantomData, + ) } } diff --git a/nova_vm/tests/garbage_collection_tests.rs b/nova_vm/tests/garbage_collection_tests.rs index 30fd30aea..1e1908fac 100644 --- a/nova_vm/tests/garbage_collection_tests.rs +++ b/nova_vm/tests/garbage_collection_tests.rs @@ -23,7 +23,6 @@ fn initialize_global_object(agent: &mut Agent, gc: GcScope<'_, '_>, global: Obje fn print( agent: &mut Agent, gc: GcScope<'_, '_>, - _this: Value, args: ArgumentsList, ) -> JsResult { @@ -36,10 +35,11 @@ fn initialize_global_object(agent: &mut Agent, gc: GcScope<'_, '_>, global: Obje } let function = create_builtin_function( agent, + gc.nogc(), Behaviour::Regular(print), BuiltinFunctionArgs::new(1, "print", agent.current_realm_id()), ); - let property_key = PropertyKey::from_static_str(agent, "print"); + let property_key = PropertyKey::from_static_str(agent, gc.nogc(), "print"); global .internal_define_own_property( agent, @@ -86,8 +86,8 @@ fn garbage_collection_tests() { ); agent.run_in_realm(&realm, |agent, mut gc| { let realm = agent.current_realm_id(); - let source_text = String::from_string(agent, header_contents); - let script = parse_script(agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_string(agent, gc.nogc(), header_contents); + let script = parse_script(agent, gc.nogc(), source_text, realm, false, None).unwrap(); let _ = script_evaluation(agent, gc.reborrow(), script).unwrap_or_else(|err| { panic!( "Header evaluation failed: '{}' failed: {:?}", @@ -101,8 +101,8 @@ fn garbage_collection_tests() { for i in 0..2 { agent.run_in_realm(&realm, |agent, mut gc| { let realm = agent.current_realm_id(); - let source_text = String::from_string(agent, call_contents.clone()); - let script = parse_script(agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_string(agent, gc.nogc(), call_contents.clone()); + let script = parse_script(agent, gc.nogc(), source_text, realm, false, None).unwrap(); let _ = script_evaluation(agent, gc.reborrow(), script).unwrap_or_else(|err| { println!("Error kind: {:?}", err.value()); panic!( diff --git a/nova_vm/tests/object_prototype_tests.rs b/nova_vm/tests/object_prototype_tests.rs index 1a9e67071..b038ac77c 100644 --- a/nova_vm/tests/object_prototype_tests.rs +++ b/nova_vm/tests/object_prototype_tests.rs @@ -29,8 +29,8 @@ fn object_prototype_tests() { let realm = agent.create_default_realm(); agent.run_in_realm(&realm, |agent, mut gc| { let realm = agent.current_realm_id(); - let source_text = String::from_string(agent, contents); - let script = parse_script(agent, source_text, realm, false, None).unwrap(); + let source_text = String::from_string(agent, gc.nogc(), contents); + let script = parse_script(agent, gc.nogc(), source_text, realm, false, None).unwrap(); let _ = script_evaluation(agent, gc.reborrow(), script).unwrap_or_else(|err| { panic!( "Test '{}' failed: {:?}", diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 5e9f2deac..542c3b9a6 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -3,14 +3,13 @@ name = "tests" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] name = "test262" path = "test262_runner.rs" [dependencies] -clap = { version = "4.5.7", features = ["derive"] } +clap = { workspace = true} rayon = "1.10.0" serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" diff --git a/tests/expectations.json b/tests/expectations.json index fb1a73843..1ec3e5473 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -455,23 +455,8 @@ "built-ins/Array/prototype/toSpliced/this-value-nullish.js": "CRASH", "built-ins/Array/prototype/toSpliced/unmodified.js": "CRASH", "built-ins/Array/prototype/toString/non-callable-join-string-tag.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A1_T1.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A1_T2.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A2_T1.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A2_T2.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A2_T3.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A3_T2.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A4_T1.js": "CRASH", - "built-ins/Array/prototype/unshift/S15.4.4.13_A4_T2.js": "CRASH", - "built-ins/Array/prototype/unshift/call-with-boolean.js": "CRASH", - "built-ins/Array/prototype/unshift/clamps-to-integer-limit.js": "CRASH", - "built-ins/Array/prototype/unshift/length-near-integer-limit.js": "CRASH", "built-ins/Array/prototype/unshift/set-length-array-is-frozen.js": "CRASH", "built-ins/Array/prototype/unshift/set-length-array-length-is-non-writable.js": "CRASH", - "built-ins/Array/prototype/unshift/set-length-zero-array-is-frozen.js": "CRASH", - "built-ins/Array/prototype/unshift/set-length-zero-array-length-is-non-writable.js": "CRASH", - "built-ins/Array/prototype/unshift/throws-if-integer-limit-exceeded.js": "CRASH", - "built-ins/Array/prototype/unshift/throws-with-string-receiver.js": "CRASH", "built-ins/Array/prototype/values/resizable-buffer-grow-mid-iteration.js": "CRASH", "built-ins/Array/prototype/values/resizable-buffer-shrink-mid-iteration.js": "CRASH", "built-ins/Array/prototype/values/resizable-buffer.js": "CRASH", @@ -5678,15 +5663,8 @@ "built-ins/String/prototype/codePointAt/return-single-code-unit.js": "FAIL", "built-ins/String/prototype/endsWith/return-abrupt-from-searchstring-regexp-test.js": "FAIL", "built-ins/String/prototype/endsWith/searchstring-is-regexp-throws.js": "FAIL", - "built-ins/String/prototype/includes/String.prototype.includes_FailBadLocation.js": "FAIL", - "built-ins/String/prototype/includes/String.prototype.includes_FailLocation.js": "FAIL", - "built-ins/String/prototype/includes/coerced-values-of-position.js": "FAIL", - "built-ins/String/prototype/includes/return-abrupt-from-position-as-symbol.js": "FAIL", - "built-ins/String/prototype/includes/return-abrupt-from-position.js": "FAIL", "built-ins/String/prototype/includes/return-abrupt-from-searchstring-regexp-test.js": "FAIL", - "built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js": "FAIL", "built-ins/String/prototype/includes/searchstring-is-regexp-throws.js": "FAIL", - "built-ins/String/prototype/includes/searchstring-not-found-with-position.js": "FAIL", "built-ins/String/prototype/indexOf/searchstring-tostring-bigint.js": "FAIL", "built-ins/String/prototype/isWellFormed/returns-boolean.js": "FAIL", "built-ins/String/prototype/isWellFormed/to-string-primitive.js": "CRASH", @@ -13078,17 +13056,14 @@ "language/expressions/bitwise-and/bigint-toprimitive.js": "CRASH", "language/expressions/bitwise-and/bigint-wrapped-values.js": "FAIL", "language/expressions/bitwise-and/bigint.js": "CRASH", - "language/expressions/bitwise-and/order-of-evaluation.js": "FAIL", "language/expressions/bitwise-or/bigint-non-primitive.js": "CRASH", "language/expressions/bitwise-or/bigint-toprimitive.js": "CRASH", "language/expressions/bitwise-or/bigint-wrapped-values.js": "FAIL", "language/expressions/bitwise-or/bigint.js": "CRASH", - "language/expressions/bitwise-or/order-of-evaluation.js": "FAIL", "language/expressions/bitwise-xor/bigint-non-primitive.js": "CRASH", "language/expressions/bitwise-xor/bigint-toprimitive.js": "CRASH", "language/expressions/bitwise-xor/bigint-wrapped-values.js": "FAIL", "language/expressions/bitwise-xor/bigint.js": "CRASH", - "language/expressions/bitwise-xor/order-of-evaluation.js": "FAIL", "language/expressions/call/eval-realm-indirect.js": "FAIL", "language/expressions/call/eval-strictness-inherit-non-strict.js": "CRASH", "language/expressions/call/tco-call-args.js": "CRASH", @@ -16000,7 +15975,6 @@ "language/expressions/division/S11.5.2_A4_T10.js": "FAIL", "language/expressions/division/S11.5.2_A4_T5.js": "FAIL", "language/expressions/division/S11.5.2_A4_T8.js": "FAIL", - "language/expressions/division/order-of-evaluation.js": "FAIL", "language/expressions/does-not-equals/S11.9.2_A5.2.js": "FAIL", "language/expressions/does-not-equals/bigint-and-boolean.js": "CRASH", "language/expressions/does-not-equals/bigint-and-incomparable-primitive.js": "FAIL", @@ -16573,7 +16547,6 @@ "language/expressions/exponentiation/bigint-arithmetic.js": "CRASH", "language/expressions/exponentiation/exp-operator-evaluation-order.js": "FAIL", "language/expressions/exponentiation/exp-operator-precedence-unary-expression-semantics.js": "FAIL", - "language/expressions/exponentiation/order-of-evaluation.js": "FAIL", "language/expressions/function/dstr/ary-ptrn-elem-ary-elem-init.js": "CRASH", "language/expressions/function/dstr/ary-ptrn-elem-ary-elem-iter.js": "CRASH", "language/expressions/function/dstr/ary-ptrn-elem-ary-rest-init.js": "CRASH", @@ -16841,7 +16814,6 @@ "language/expressions/left-shift/bigint-toprimitive.js": "CRASH", "language/expressions/left-shift/bigint-wrapped-values.js": "FAIL", "language/expressions/left-shift/bigint.js": "CRASH", - "language/expressions/left-shift/order-of-evaluation.js": "FAIL", "language/expressions/less-than-or-equal/S11.8.3_A4.9.js": "FAIL", "language/expressions/less-than-or-equal/bigint-and-bigint.js": "CRASH", "language/expressions/less-than-or-equal/bigint-and-incomparable-string.js": "CRASH", @@ -16889,10 +16861,8 @@ "language/expressions/logical-not/bigint.js": "FAIL", "language/expressions/logical-or/tco-right.js": "CRASH", "language/expressions/member-expression/computed-reference-null-or-undefined.js": "FAIL", - "language/expressions/modulus/order-of-evaluation.js": "FAIL", "language/expressions/multiplication/S11.5.1_A4_T5.js": "FAIL", "language/expressions/multiplication/S11.5.1_A4_T7.js": "FAIL", - "language/expressions/multiplication/order-of-evaluation.js": "FAIL", "language/expressions/new.target/asi.js": "CRASH", "language/expressions/new.target/unary-expr.js": "CRASH", "language/expressions/new.target/value-via-call.js": "CRASH", @@ -17463,8 +17433,6 @@ "language/expressions/right-shift/bigint-toprimitive.js": "CRASH", "language/expressions/right-shift/bigint-wrapped-values.js": "FAIL", "language/expressions/right-shift/bigint.js": "CRASH", - "language/expressions/right-shift/order-of-evaluation.js": "FAIL", - "language/expressions/subtraction/order-of-evaluation.js": "FAIL", "language/expressions/super/call-construct-invocation.js": "CRASH", "language/expressions/super/prop-dot-cls-null-proto.js": "CRASH", "language/expressions/super/prop-dot-cls-ref-strict.js": "CRASH", @@ -17563,7 +17531,6 @@ "language/expressions/unsigned-right-shift/bigint-toprimitive.js": "CRASH", "language/expressions/unsigned-right-shift/bigint-wrapped-values.js": "CRASH", "language/expressions/unsigned-right-shift/bigint.js": "CRASH", - "language/expressions/unsigned-right-shift/order-of-evaluation.js": "FAIL", "language/expressions/yield/formal-parameters-after-reassignment-non-strict.js": "CRASH", "language/expressions/yield/from-with.js": "CRASH", "language/expressions/yield/rhs-regexp.js": "CRASH",