From 5d25b5152f66475abd438ecd967695b78c1136f3 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 14 Sep 2022 23:12:22 -0500 Subject: [PATCH 1/5] Lazily create Error objects --- boa_cli/src/main.rs | 8 +- boa_engine/src/bigint.rs | 24 +- .../src/builtins/array/array_iterator.rs | 11 +- boa_engine/src/builtins/array/mod.rs | 115 +-- boa_engine/src/builtins/array/tests.rs | 100 +-- boa_engine/src/builtins/array_buffer/mod.rs | 58 +- boa_engine/src/builtins/array_buffer/tests.rs | 8 +- .../src/builtins/async_generator/mod.rs | 46 +- boa_engine/src/builtins/bigint/mod.rs | 48 +- boa_engine/src/builtins/boolean/mod.rs | 25 +- boa_engine/src/builtins/dataview/mod.rs | 72 +- boa_engine/src/builtins/date/mod.rs | 92 ++- boa_engine/src/builtins/date/tests.rs | 130 ++- boa_engine/src/builtins/error/aggregate.rs | 4 +- boa_engine/src/builtins/error/eval.rs | 4 +- boa_engine/src/builtins/error/mod.rs | 19 +- boa_engine/src/builtins/error/range.rs | 4 +- boa_engine/src/builtins/error/reference.rs | 4 +- boa_engine/src/builtins/error/syntax.rs | 4 +- boa_engine/src/builtins/error/type.rs | 9 +- boa_engine/src/builtins/error/uri.rs | 4 +- boa_engine/src/builtins/eval/mod.rs | 11 +- boa_engine/src/builtins/function/mod.rs | 66 +- boa_engine/src/builtins/function/tests.rs | 18 +- boa_engine/src/builtins/generator/mod.rs | 22 +- .../src/builtins/intl/date_time_format.rs | 9 +- boa_engine/src/builtins/intl/mod.rs | 19 +- boa_engine/src/builtins/intl/tests.rs | 66 +- boa_engine/src/builtins/iterable/mod.rs | 15 +- boa_engine/src/builtins/json/mod.rs | 15 +- boa_engine/src/builtins/map/map_iterator.rs | 7 +- boa_engine/src/builtins/map/mod.rs | 73 +- boa_engine/src/builtins/number/mod.rs | 48 +- boa_engine/src/builtins/number/tests.rs | 3 +- .../src/builtins/object/for_in_iterator.rs | 3 +- boa_engine/src/builtins/object/mod.rs | 98 ++- boa_engine/src/builtins/object/tests.rs | 6 +- boa_engine/src/builtins/promise/mod.rs | 79 +- .../src/builtins/promise/promise_job.rs | 7 +- boa_engine/src/builtins/proxy/mod.rs | 15 +- boa_engine/src/builtins/reflect/mod.rs | 47 +- boa_engine/src/builtins/regexp/mod.rs | 102 ++- .../builtins/regexp/regexp_string_iterator.rs | 5 +- boa_engine/src/builtins/regexp/tests.rs | 3 +- boa_engine/src/builtins/set/mod.rs | 100 ++- boa_engine/src/builtins/set/set_iterator.rs | 5 +- boa_engine/src/builtins/string/mod.rs | 130 +-- .../src/builtins/string/string_iterator.rs | 3 +- boa_engine/src/builtins/symbol/mod.rs | 47 +- boa_engine/src/builtins/typed_array/mod.rs | 738 ++++++++++-------- boa_engine/src/bytecompiler/function.rs | 2 +- boa_engine/src/bytecompiler/mod.rs | 99 ++- boa_engine/src/class.rs | 31 +- boa_engine/src/context/mod.rs | 191 +---- boa_engine/src/environments/runtime.rs | 15 +- boa_engine/src/error/mod.rs | 333 ++++++++ boa_engine/src/error/vos.rs | 84 ++ boa_engine/src/lib.rs | 25 +- .../src/object/internal_methods/array.rs | 5 +- .../src/object/internal_methods/proxy.rs | 169 ++-- boa_engine/src/object/jsarray.rs | 15 +- boa_engine/src/object/jsarraybuffer.rs | 7 +- boa_engine/src/object/jsmap.rs | 17 +- boa_engine/src/object/jsmap_iterator.rs | 7 +- boa_engine/src/object/jsobject.rs | 21 +- boa_engine/src/object/jsset.rs | 21 +- boa_engine/src/object/jsset_iterator.rs | 7 +- boa_engine/src/object/jstypedarray.rs | 7 +- boa_engine/src/object/mod.rs | 27 +- boa_engine/src/object/operations.rs | 48 +- boa_engine/src/tests.rs | 93 +-- boa_engine/src/value/mod.rs | 65 +- boa_engine/src/value/operations.rs | 132 ++-- boa_engine/src/value/serde_json.rs | 21 +- boa_engine/src/value/tests.rs | 60 +- boa_engine/src/vm/code_block.rs | 38 +- boa_engine/src/vm/mod.rs | 249 ++++-- boa_engine/src/vm/tests.rs | 37 +- boa_examples/src/bin/classes.rs | 7 +- boa_examples/src/bin/closures.rs | 4 +- boa_examples/src/bin/jsset.rs | 18 +- boa_examples/src/bin/loadfile.rs | 2 +- boa_examples/src/bin/loadstring.rs | 2 +- boa_tester/src/exec/js262.rs | 26 +- boa_tester/src/exec/mod.rs | 63 +- boa_tester/src/main.rs | 3 +- boa_tester/src/read.rs | 28 +- boa_wasm/src/lib.rs | 2 +- 88 files changed, 2721 insertions(+), 1809 deletions(-) create mode 100644 boa_engine/src/error/mod.rs create mode 100644 boa_engine/src/error/vos.rs diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index cd01b629900..9084d1d7164 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -198,7 +198,7 @@ pub fn main() -> Result<(), io::Error> { } else { match context.eval(&buffer) { Ok(v) => println!("{}", v.display()), - Err(v) => eprintln!("Uncaught {}", v.display()), + Err(v) => eprintln!("Uncaught {v}"), } } } @@ -245,11 +245,7 @@ pub fn main() -> Result<(), io::Error> { match context.eval(line.trim_end()) { Ok(v) => println!("{}", v.display()), Err(v) => { - eprintln!( - "{}: {}", - "Uncaught".red(), - v.display().to_string().red() - ); + eprintln!("{}: {}", "Uncaught".red(), v.to_string().red()); } } } diff --git a/boa_engine/src/bigint.rs b/boa_engine/src/bigint.rs index b06b921ea32..5ab9221ed1b 100644 --- a/boa_engine/src/bigint.rs +++ b/boa_engine/src/bigint.rs @@ -1,6 +1,6 @@ //! This module implements the JavaScript bigint primitive rust type. -use crate::{builtins::Number, Context, JsValue}; +use crate::{builtins::Number, error::JsNativeError, JsResult}; use num_integer::Integer; use num_traits::{pow::Pow, FromPrimitive, One, ToPrimitive, Zero}; use std::{ @@ -148,11 +148,13 @@ impl JsBigInt { } #[inline] - pub fn pow(x: &Self, y: &Self, context: &mut Context) -> Result { + pub fn pow(x: &Self, y: &Self) -> JsResult { let y = if let Some(y) = y.inner.to_biguint() { y } else { - return context.throw_range_error("BigInt negative exponent"); + return Err(JsNativeError::range() + .with_message("BigInt negative exponent") + .into()); }; let num_bits = (x.inner.bits() as f64 @@ -161,14 +163,16 @@ impl JsBigInt { + 1f64; if num_bits > 1_000_000_000f64 { - return context.throw_range_error("Maximum BigInt size exceeded"); + return Err(JsNativeError::range() + .with_message("Maximum BigInt size exceeded") + .into()); } Ok(Self::new(x.inner.as_ref().clone().pow(y))) } #[inline] - pub fn shift_right(x: &Self, y: &Self, context: &mut Context) -> Result { + pub fn shift_right(x: &Self, y: &Self) -> JsResult { if let Some(n) = y.inner.to_i32() { let inner = if n > 0 { x.inner.as_ref().clone().shr(n as usize) @@ -178,12 +182,14 @@ impl JsBigInt { Ok(Self::new(inner)) } else { - context.throw_range_error("Maximum BigInt size exceeded") + Err(JsNativeError::range() + .with_message("Maximum BigInt size exceeded") + .into()) } } #[inline] - pub fn shift_left(x: &Self, y: &Self, context: &mut Context) -> Result { + pub fn shift_left(x: &Self, y: &Self) -> JsResult { if let Some(n) = y.inner.to_i32() { let inner = if n > 0 { x.inner.as_ref().clone().shl(n as usize) @@ -193,7 +199,9 @@ impl JsBigInt { Ok(Self::new(inner)) } else { - context.throw_range_error("Maximum BigInt size exceeded") + Err(JsNativeError::range() + .with_message("Maximum BigInt size exceeded") + .into()) } } diff --git a/boa_engine/src/builtins/array/array_iterator.rs b/boa_engine/src/builtins/array/array_iterator.rs index 882a47c425a..67e985b94e2 100644 --- a/boa_engine/src/builtins/array/array_iterator.rs +++ b/boa_engine/src/builtins/array/array_iterator.rs @@ -1,5 +1,6 @@ use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue}, + error::JsNativeError, object::{JsObject, ObjectData}, property::{PropertyDescriptor, PropertyNameKind}, symbol::WellKnownSymbols, @@ -72,7 +73,7 @@ impl ArrayIterator { let array_iterator = array_iterator .as_mut() .and_then(|obj| obj.as_array_iterator_mut()) - .ok_or_else(|| context.construct_type_error("`this` is not an ArrayIterator"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not an ArrayIterator"))?; let index = array_iterator.next_index; if array_iterator.done { return Ok(create_iter_result_object( @@ -84,9 +85,11 @@ impl ArrayIterator { let len = if let Some(f) = array_iterator.array.borrow().as_typed_array() { if f.is_detached() { - return context.throw_type_error( - "Cannot get value from typed array that has a detached array buffer", - ); + return Err(JsNativeError::typ() + .with_message( + "Cannot get value from typed array that has a detached array buffer", + ) + .into()); } f.array_length() diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index 54f22800296..80056f17fdc 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -23,6 +23,7 @@ use crate::{ builtins::BuiltIn, builtins::Number, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsFunction, JsObject, ObjectData, @@ -176,7 +177,9 @@ impl Array { .expect("this ToUint32 call must not fail"); // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception. if !JsValue::same_value_zero(&int_len.into(), len) { - return context.throw_range_error("invalid array length"); + return Err(JsNativeError::range() + .with_message("invalid array length") + .into()); } int_len }; @@ -223,7 +226,9 @@ impl Array { ) -> JsResult { // 1. If length > 2^32 - 1, throw a RangeError exception. if length > 2u64.pow(32) - 1 { - return context.throw_range_error("array exceeded max size"); + return Err(JsNativeError::range() + .with_message("array exceeded max size") + .into()); } // 7. Return A. // 2. If proto is not present, set proto to %Array.prototype%. @@ -376,7 +381,9 @@ impl Array { // 8. Return ? Construct(C, « 𝔽(length) »). c.construct(&[JsValue::new(length)], Some(c), context) } else { - context.throw_type_error("Symbol.species must be a constructor") + Err(JsNativeError::typ() + .with_message("Symbol.species must be a constructor") + .into()) } } @@ -407,7 +414,11 @@ impl Array { let mapping = match mapfn { JsValue::Undefined => None, JsValue::Object(o) if o.is_callable() => Some(o), - _ => return context.throw_type_error(format!("{} is not a function", mapfn.type_of())), + _ => { + return Err(JsNativeError::typ() + .with_message(format!("{} is not a function", mapfn.type_of())) + .into()) + } }; // 4. Let usingIterator be ? GetMethod(items, @@iterator). @@ -477,7 +488,9 @@ impl Array { // which is why it's safe to have this as the fallback return // // 1. Let error be ThrowCompletion(a newly created TypeError object). - let error = context.throw_type_error("Invalid array length"); + let error = Err(JsNativeError::typ() + .with_message("Invalid array length") + .into()); // 2. Return ? IteratorClose(iteratorRecord, error). iterator_record.close(error, context) @@ -668,9 +681,11 @@ impl Array { let len = item.length_of_array_like(context)?; // iii. If n + len > 2^53 - 1, throw a TypeError exception. if n + len > Number::MAX_SAFE_INTEGER as u64 { - return context.throw_type_error( - "length + number of arguments exceeds the max safe integer limit", - ); + return Err(JsNativeError::typ() + .with_message( + "length + number of arguments exceeds the max safe integer limit", + ) + .into()); } // iv. Repeat, while k < len, for k in 0..len { @@ -694,7 +709,9 @@ impl Array { // i. NOTE: E is added as a single item rather than spread. // ii. If n ≥ 2^53 - 1, throw a TypeError exception. if n >= Number::MAX_SAFE_INTEGER as u64 { - return context.throw_type_error("length exceeds the max safe integer limit"); + return Err(JsNativeError::typ() + .with_message("length exceeds the max safe integer limit") + .into()); } // iii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), E). arr.create_data_property_or_throw(n, item, context)?; @@ -734,9 +751,11 @@ impl Array { let arg_count = args.len() as u64; // 4. If len + argCount > 2^53 - 1, throw a TypeError exception. if len + arg_count > 2u64.pow(53) - 1 { - return context.throw_type_error( - "the length + the number of arguments exceed the maximum safe integer limit", - ); + return Err(JsNativeError::typ() + .with_message( + "the length + the number of arguments exceed the maximum safe integer limit", + ) + .into()); } // 5. For each element E of items, do for element in args.iter().cloned() { @@ -811,7 +830,7 @@ impl Array { let len = o.length_of_array_like(context)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.forEach: invalid callback function") + JsNativeError::typ().with_message("Array.prototype.forEach: invalid callback function") })?; // 4. Let k be 0. // 5. Repeat, while k < len, @@ -1085,9 +1104,9 @@ impl Array { if arg_count > 0 { // a. If len + argCount > 2^53 - 1, throw a TypeError exception. if len + arg_count > 2u64.pow(53) - 1 { - return context.throw_type_error( - "length + number of arguments exceeds the max safe integer limit", - ); + return Err(JsNativeError::typ() + .with_message("length + number of arguments exceeds the max safe integer limit") + .into()); } // b. Let k be len. let mut k = len; @@ -1152,7 +1171,7 @@ impl Array { let len = o.length_of_array_like(context)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.every: callback is not callable") + JsNativeError::typ().with_message("Array.prototype.every: callback is not callable") })?; let this_arg = args.get_or_undefined(1); @@ -1204,7 +1223,7 @@ impl Array { let len = o.length_of_array_like(context)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.map: Callbackfn is not callable") + JsNativeError::typ().with_message("Array.prototype.map: Callbackfn is not callable") })?; // 4. Let A be ? ArraySpeciesCreate(O, len). @@ -1412,7 +1431,7 @@ impl Array { // 3. If IsCallable(predicate) is false, throw a TypeError exception. let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.find: predicate is not callable") + JsNativeError::typ().with_message("Array.prototype.find: predicate is not callable") })?; let this_arg = args.get_or_undefined(1); @@ -1469,7 +1488,8 @@ impl Array { // 3. If IsCallable(predicate) is false, throw a TypeError exception. let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.findIndex: predicate is not callable") + JsNativeError::typ() + .with_message("Array.prototype.findIndex: predicate is not callable") })?; let this_arg = args.get_or_undefined(1); @@ -1520,7 +1540,7 @@ impl Array { // 3. If IsCallable(predicate) is false, throw a TypeError exception. let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.findLast: predicate is not callable") + JsNativeError::typ().with_message("Array.prototype.findLast: predicate is not callable") })?; let this_arg = args.get_or_undefined(1); @@ -1572,7 +1592,8 @@ impl Array { // 3. If IsCallable(predicate) is false, throw a TypeError exception. let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.findLastIndex: predicate is not callable") + JsNativeError::typ() + .with_message("Array.prototype.findLastIndex: predicate is not callable") })?; let this_arg = args.get_or_undefined(1); @@ -1677,7 +1698,7 @@ impl Array { // 3. If ! IsCallable(mapperFunction) is false, throw a TypeError exception. let mapper_function = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("flatMap mapper function is not callable") + JsNativeError::typ().with_message("flatMap mapper function is not callable") })?; // 4. Let A be ? ArraySpeciesCreate(O, 0). @@ -1793,8 +1814,9 @@ impl Array { } else { // 1. If targetIndex >= 2^53 - 1, throw a TypeError exception if target_index >= Number::MAX_SAFE_INTEGER as u64 { - return context - .throw_type_error("Target index exceeded max safe integer value"); + return Err(JsNativeError::typ() + .with_message("Target index exceeded max safe integer value") + .into()); } // 2. Perform ? CreateDataPropertyOrThrow(target, targetIndex, element) @@ -2064,7 +2086,9 @@ impl Array { // 10. If len + insertCount - actualDeleteCount > 2^53 - 1, throw a TypeError exception. if len + insert_count - actual_delete_count > Number::MAX_SAFE_INTEGER as u64 { - return context.throw_type_error("Target splice exceeded max safe integer value"); + return Err(JsNativeError::typ() + .with_message("Target splice exceeded max safe integer value") + .into()); } // 11. Let A be ? ArraySpeciesCreate(O, actualDeleteCount). @@ -2206,7 +2230,7 @@ impl Array { // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.filter: `callback` must be callable") + JsNativeError::typ().with_message("Array.prototype.filter: `callback` must be callable") })?; let this_arg = args.get_or_undefined(1); @@ -2270,7 +2294,7 @@ impl Array { let len = o.length_of_array_like(context)?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error("Array.prototype.some: callback is not callable") + JsNativeError::typ().with_message("Array.prototype.some: callback is not callable") })?; // 4. Let k be 0. @@ -2321,9 +2345,9 @@ impl Array { JsValue::Object(ref obj) if obj.is_callable() => Some(obj), JsValue::Undefined => None, _ => { - return context.throw_type_error( - "The comparison function must be either a function or undefined", - ) + return Err(JsNativeError::typ() + .with_message("The comparison function must be either a function or undefined") + .into()) } }; @@ -2455,15 +2479,17 @@ impl Array { // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context - .construct_type_error("Array.prototype.reduce: callback function is not callable") + JsNativeError::typ() + .with_message("Array.prototype.reduce: callback function is not callable") })?; // 4. If len = 0 and initialValue is not present, throw a TypeError exception. if len == 0 && args.get(1).is_none() { - return context.throw_type_error( - "Array.prototype.reduce: called on an empty array and with no initial value", - ); + return Err(JsNativeError::typ() + .with_message( + "Array.prototype.reduce: called on an empty array and with no initial value", + ) + .into()); } // 5. Let k be 0. @@ -2495,9 +2521,9 @@ impl Array { } // c. If kPresent is false, throw a TypeError exception. if !k_present { - return context.throw_type_error( + return Err(JsNativeError::typ().with_message( "Array.prototype.reduce: called on an empty array and with no initial value", - ); + ).into()); } } @@ -2550,16 +2576,15 @@ impl Array { // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { - context.construct_type_error( - "Array.prototype.reduceRight: callback function is not callable", - ) + JsNativeError::typ() + .with_message("Array.prototype.reduceRight: callback function is not callable") })?; // 4. If len is 0 and initialValue is not present, throw a TypeError exception. if len == 0 && args.get(1).is_none() { - return context.throw_type_error( + return Err(JsNativeError::typ().with_message( "Array.prototype.reduceRight: called on an empty array and with no initial value", - ); + ).into()); } // 5. Let k be len - 1. @@ -2590,9 +2615,9 @@ impl Array { } // c. If kPresent is false, throw a TypeError exception. if !k_present { - return context.throw_type_error( + return Err(JsNativeError::typ().with_message( "Array.prototype.reduceRight: called on an empty array and with no initial value", - ); + ).into()); } } diff --git a/boa_engine/src/builtins/array/tests.rs b/boa_engine/src/builtins/array/tests.rs index 54499815370..67bda0387d6 100644 --- a/boa_engine/src/builtins/array/tests.rs +++ b/boa_engine/src/builtins/array/tests.rs @@ -1421,50 +1421,55 @@ fn array_spread_non_iterable() { fn get_relative_start() { let mut context = Context::default(); - assert_eq!(Array::get_relative_start(&mut context, None, 10), Ok(0)); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::undefined()), 10), - Ok(0) + Array::get_relative_start(&mut context, None, 10).unwrap(), + 0 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(f64::NEG_INFINITY)), 10), - Ok(0) + Array::get_relative_start(&mut context, Some(&JsValue::undefined()), 10).unwrap(), + 0 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(f64::INFINITY)), 10), - Ok(10) + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::NEG_INFINITY)), 10) + .unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::INFINITY)), 10).unwrap(), + 10 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(-1)), 10), - Ok(9) + Array::get_relative_start(&mut context, Some(&JsValue::new(-1)), 10).unwrap(), + 9 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(1)), 10), - Ok(1) + Array::get_relative_start(&mut context, Some(&JsValue::new(1)), 10).unwrap(), + 1 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(-11)), 10), - Ok(0) + Array::get_relative_start(&mut context, Some(&JsValue::new(-11)), 10).unwrap(), + 0 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(11)), 10), - Ok(10) + Array::get_relative_start(&mut context, Some(&JsValue::new(11)), 10).unwrap(), + 10 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(f64::MIN)), 10), - Ok(0) + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::MIN)), 10).unwrap(), + 0 ); assert_eq!( Array::get_relative_start( &mut context, Some(&JsValue::new(Number::MIN_SAFE_INTEGER)), 10 - ), - Ok(0) + ) + .unwrap(), + 0 ); assert_eq!( - Array::get_relative_start(&mut context, Some(&JsValue::new(f64::MAX)), 10), - Ok(10) + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::MAX)), 10).unwrap(), + 10 ); // This test is relevant only on 32-bit archs (where usize == u32 thus `len` is u32) @@ -1473,8 +1478,9 @@ fn get_relative_start() { &mut context, Some(&JsValue::new(Number::MAX_SAFE_INTEGER)), 10 - ), - Ok(10) + ) + .unwrap(), + 10 ); } @@ -1482,50 +1488,51 @@ fn get_relative_start() { fn get_relative_end() { let mut context = Context::default(); - assert_eq!(Array::get_relative_end(&mut context, None, 10), Ok(10)); + assert_eq!(Array::get_relative_end(&mut context, None, 10).unwrap(), 10); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::undefined()), 10), - Ok(10) + Array::get_relative_end(&mut context, Some(&JsValue::undefined()), 10).unwrap(), + 10 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(f64::NEG_INFINITY)), 10), - Ok(0) + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::NEG_INFINITY)), 10).unwrap(), + 0 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(f64::INFINITY)), 10), - Ok(10) + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::INFINITY)), 10).unwrap(), + 10 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(-1)), 10), - Ok(9) + Array::get_relative_end(&mut context, Some(&JsValue::new(-1)), 10).unwrap(), + 9 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(1)), 10), - Ok(1) + Array::get_relative_end(&mut context, Some(&JsValue::new(1)), 10).unwrap(), + 1 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(-11)), 10), - Ok(0) + Array::get_relative_end(&mut context, Some(&JsValue::new(-11)), 10).unwrap(), + 0 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(11)), 10), - Ok(10) + Array::get_relative_end(&mut context, Some(&JsValue::new(11)), 10).unwrap(), + 10 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(f64::MIN)), 10), - Ok(0) + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::MIN)), 10).unwrap(), + 0 ); assert_eq!( Array::get_relative_end( &mut context, Some(&JsValue::new(Number::MIN_SAFE_INTEGER)), 10 - ), - Ok(0) + ) + .unwrap(), + 0 ); assert_eq!( - Array::get_relative_end(&mut context, Some(&JsValue::new(f64::MAX)), 10), - Ok(10) + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::MAX)), 10).unwrap(), + 10 ); // This test is relevant only on 32-bit archs (where usize == u32 thus `len` is u32) @@ -1534,8 +1541,9 @@ fn get_relative_end() { &mut context, Some(&JsValue::new(Number::MAX_SAFE_INTEGER)), 10 - ), - Ok(10) + ) + .unwrap(), + 10 ); } diff --git a/boa_engine/src/builtins/array_buffer/mod.rs b/boa_engine/src/builtins/array_buffer/mod.rs index 576bd53b2c1..b024a529682 100644 --- a/boa_engine/src/builtins/array_buffer/mod.rs +++ b/boa_engine/src/builtins/array_buffer/mod.rs @@ -4,6 +4,7 @@ mod tests; use crate::{ builtins::{typed_array::TypedArrayKind, BuiltIn, JsArgs}, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsObject, ObjectData, @@ -91,8 +92,9 @@ impl ArrayBuffer { ) -> JsResult { // 1. If NewTarget is undefined, throw a TypeError exception. if new_target.is_undefined() { - return context - .throw_type_error("ArrayBuffer.constructor called with undefined new target"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.constructor called with undefined new target") + .into()); } // 2. Let byteLength be ? ToIndex(length). @@ -142,20 +144,24 @@ impl ArrayBuffer { pub(crate) fn get_byte_length( this: &JsValue, _args: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). let obj = if let Some(obj) = this.as_object() { obj } else { - return context.throw_type_error("ArrayBuffer.byteLength called with non-object value"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.byteLength called with non-object value") + .into()); }; let obj = obj.borrow(); let o = if let Some(o) = obj.as_array_buffer() { o } else { - return context.throw_type_error("ArrayBuffer.byteLength called with invalid object"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.byteLength called with invalid object") + .into()); }; // TODO: Shared Array Buffer @@ -183,13 +189,17 @@ impl ArrayBuffer { let obj = if let Some(obj) = this.as_object() { obj } else { - return context.throw_type_error("ArrayBuffer.slice called with non-object value"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.slice called with non-object value") + .into()); }; let obj_borrow = obj.borrow(); let o = if let Some(o) = obj_borrow.as_array_buffer() { o } else { - return context.throw_type_error("ArrayBuffer.slice called with invalid object"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.slice called with invalid object") + .into()); }; // TODO: Shared Array Buffer @@ -197,7 +207,9 @@ impl ArrayBuffer { // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. if Self::is_detached_buffer(o) { - return context.throw_type_error("ArrayBuffer.slice called with detached buffer"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.slice called with detached buffer") + .into()); } // 5. Let len be O.[[ArrayBufferByteLength]]. @@ -247,7 +259,7 @@ impl ArrayBuffer { let new_obj = new.borrow(); // 17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]). let new_array_buffer = new_obj.as_array_buffer().ok_or_else(|| { - context.construct_type_error("ArrayBuffer constructor returned invalid object") + JsNativeError::typ().with_message("ArrayBuffer constructor returned invalid object") })?; // TODO: Shared Array Buffer @@ -255,8 +267,9 @@ impl ArrayBuffer { // 19. If IsDetachedBuffer(new) is true, throw a TypeError exception. if new_array_buffer.is_detached_buffer() { - return context - .throw_type_error("ArrayBuffer constructor returned detached ArrayBuffer"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer constructor returned detached ArrayBuffer") + .into()); } } // 20. If SameValue(new, O) is true, throw a TypeError exception. @@ -265,7 +278,9 @@ impl ArrayBuffer { .map(|obj| JsObject::equals(obj, &new)) .unwrap_or_default() { - return context.throw_type_error("New ArrayBuffer is the same as this ArrayBuffer"); + return Err(JsNativeError::typ() + .with_message("New ArrayBuffer is the same as this ArrayBuffer") + .into()); } { @@ -276,14 +291,17 @@ impl ArrayBuffer { // 21. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception. if new_array_buffer.array_buffer_byte_length < new_len { - return context.throw_type_error("New ArrayBuffer length too small"); + return Err(JsNativeError::typ() + .with_message("New ArrayBuffer length too small") + .into()); } // 22. NOTE: Side-effects of the above steps may have detached O. // 23. If IsDetachedBuffer(O) is true, throw a TypeError exception. if Self::is_detached_buffer(o) { - return context - .throw_type_error("ArrayBuffer detached while ArrayBuffer.slice was running"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer detached while ArrayBuffer.slice was running") + .into()); } // 24. Let fromBuf be O.[[ArrayBufferData]]. @@ -327,7 +345,7 @@ impl ArrayBuffer { obj.set_prototype(prototype.into()); // 2. Let block be ? CreateByteDataBlock(byteLength). - let block = create_byte_data_block(byte_length, context)?; + let block = create_byte_data_block(byte_length)?; // 3. Set obj.[[ArrayBufferData]] to block. // 4. Set obj.[[ArrayBufferByteLength]] to byteLength. @@ -374,7 +392,9 @@ impl ArrayBuffer { let src_block = if let Some(b) = &self.array_buffer_data { b } else { - return context.throw_syntax_error("Cannot clone detached array buffer"); + return Err(JsNativeError::syntax() + .with_message("Cannot clone detached array buffer") + .into()); }; { @@ -745,12 +765,12 @@ impl ArrayBuffer { /// integer). For more information, check the [spec][spec]. /// /// [spec]: https://tc39.es/ecma262/#sec-createbytedatablock -pub fn create_byte_data_block(size: u64, context: &mut Context) -> JsResult> { +pub fn create_byte_data_block(size: u64) -> JsResult> { // 1. Let db be a new Data Block value consisting of size bytes. If it is impossible to // create such a Data Block, throw a RangeError exception. let mut data_block = Vec::new(); data_block.try_reserve(size as usize).map_err(|e| { - context.construct_range_error(format!("couldn't allocate the data block: {e}")) + JsNativeError::range().with_message(format!("couldn't allocate the data block: {e}")) })?; // 2. Set all of the bytes of db to 0. diff --git a/boa_engine/src/builtins/array_buffer/tests.rs b/boa_engine/src/builtins/array_buffer/tests.rs index 2670308c918..ae4bcd7da71 100644 --- a/boa_engine/src/builtins/array_buffer/tests.rs +++ b/boa_engine/src/builtins/array_buffer/tests.rs @@ -2,14 +2,10 @@ use super::*; #[test] fn ut_sunny_day_create_byte_data_block() { - let mut context = Context::default(); - - assert!(create_byte_data_block(100, &mut context).is_ok()); + assert!(create_byte_data_block(100).is_ok()); } #[test] fn ut_rainy_day_create_byte_data_block() { - let mut context = Context::default(); - - assert!(create_byte_data_block(u64::MAX, &mut context).is_err()); + assert!(create_byte_data_block(u64::MAX).is_err()); } diff --git a/boa_engine/src/builtins/async_generator/mod.rs b/boa_engine/src/builtins/async_generator/mod.rs index 021141007aa..a42c2a5912a 100644 --- a/boa_engine/src/builtins/async_generator/mod.rs +++ b/boa_engine/src/builtins/async_generator/mod.rs @@ -10,6 +10,7 @@ use crate::{ generator::GeneratorContext, iterable::create_iter_result_object, promise::if_abrupt_reject_promise, promise::PromiseCapability, BuiltIn, JsArgs, Promise, }, + error::JsNativeError, object::{ConstructorBuilder, FunctionBuilder, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor}, symbol::WellKnownSymbols, @@ -172,13 +173,17 @@ impl AsyncGenerator { // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). // 4. IfAbruptRejectPromise(result, promiseCapability). - let generator_object = generator.as_object().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + let generator_object: JsResult<_> = generator.as_object().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() }); if_abrupt_reject_promise!(generator_object, promise_capability, context); let mut generator_obj_mut = generator_object.borrow_mut(); - let generator = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + let generator: JsResult<_> = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() }); if_abrupt_reject_promise!(generator, promise_capability, context); @@ -261,13 +266,17 @@ impl AsyncGenerator { // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). // 4. IfAbruptRejectPromise(result, promiseCapability). - let generator_object = generator.as_object().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + let generator_object: JsResult<_> = generator.as_object().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() }); if_abrupt_reject_promise!(generator_object, promise_capability, context); let mut generator_obj_mut = generator_object.borrow_mut(); - let generator = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + let generator: JsResult<_> = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() }); if_abrupt_reject_promise!(generator, promise_capability, context); @@ -345,13 +354,17 @@ impl AsyncGenerator { // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). // 4. IfAbruptRejectPromise(result, promiseCapability). - let generator_object = generator.as_object().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + let generator_object: JsResult<_> = generator.as_object().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() }); if_abrupt_reject_promise!(generator_object, promise_capability, context); let mut generator_obj_mut = generator_object.borrow_mut(); - let generator = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + let generator: JsResult<_> = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() }); if_abrupt_reject_promise!(generator, promise_capability, context); @@ -386,7 +399,7 @@ impl AsyncGenerator { } // 8. Let completion be ThrowCompletion(exception). - let completion = (Err(args.get_or_undefined(0).clone()), false); + let completion = (Err(args.get_or_undefined(0).clone().into()), false); // 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). generator.enqueue(completion.clone(), promise_capability.clone()); @@ -456,11 +469,11 @@ impl AsyncGenerator { // 6. Let value be completion.[[Value]]. match completion { // 7. If completion.[[Type]] is throw, then - Err(value) => { + Err(e) => { // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »). promise_capability .reject() - .call(&JsValue::undefined(), &[value], context) + .call(&JsValue::undefined(), &[e.to_value(context)], context) .expect("cannot fail per spec"); } // 8. Else, @@ -539,6 +552,7 @@ impl AsyncGenerator { } } (Err(value), _) => { + let value = value.to_value(context); context.vm.push(value); context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; } @@ -655,7 +669,7 @@ impl AsyncGenerator { gen.state = AsyncGeneratorState::Completed; // b. Let result be ThrowCompletion(reason). - let result = Err(args.get_or_undefined(0).clone()); + let result = Err(args.get_or_undefined(0).clone().into()); // c. Perform AsyncGeneratorCompleteStep(generator, result, true). let next = gen.queue.pop_front().expect("must have one entry"); diff --git a/boa_engine/src/builtins/bigint/mod.rs b/boa_engine/src/builtins/bigint/mod.rs index dc1ddd3485a..ded2c5135a4 100644 --- a/boa_engine/src/builtins/bigint/mod.rs +++ b/boa_engine/src/builtins/bigint/mod.rs @@ -14,6 +14,7 @@ use crate::{ builtins::{BuiltIn, JsArgs}, + error::JsNativeError, object::ConstructorBuilder, property::Attribute, symbol::WellKnownSymbols, @@ -84,7 +85,9 @@ impl BigInt { ) -> JsResult { // 1. If NewTarget is not undefined, throw a TypeError exception. if !new_target.is_undefined() { - return context.throw_type_error("BigInt is not a constructor"); + return Err(JsNativeError::typ() + .with_message("BigInt is not a constructor") + .into()); } let value = args.get_or_undefined(0); @@ -94,7 +97,7 @@ impl BigInt { // 3. If Type(prim) is Number, return ? NumberToBigInt(prim). if let Some(number) = prim.as_number() { - return Self::number_to_bigint(number, context); + return Self::number_to_bigint(number); } // 4. Otherwise, return ? ToBigInt(value). @@ -108,10 +111,12 @@ impl BigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-numbertobigint #[inline] - fn number_to_bigint(number: f64, context: &mut Context) -> JsResult { + fn number_to_bigint(number: f64) -> JsResult { // 1. If IsIntegralNumber(number) is false, throw a RangeError exception. if number.is_nan() || number.is_infinite() || number.fract() != 0.0 { - return context.throw_range_error(format!("Cannot convert {number} to BigInt")); + return Err(JsNativeError::range() + .with_message(format!("Cannot convert {number} to BigInt")) + .into()); } // 2. Return the BigInt value that represents ℝ(number). @@ -129,7 +134,7 @@ impl BigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue #[inline] - fn this_bigint_value(value: &JsValue, context: &mut Context) -> JsResult { + fn this_bigint_value(value: &JsValue) -> JsResult { value // 1. If Type(value) is BigInt, return value. .as_bigint() @@ -143,7 +148,11 @@ impl BigInt { .and_then(|obj| obj.borrow().as_bigint().cloned()) }) // 3. Throw a TypeError exception. - .ok_or_else(|| context.construct_type_error("'this' is not a BigInt")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a BigInt") + .into() + }) } /// `BigInt.prototype.toString( [radix] )` @@ -163,7 +172,7 @@ impl BigInt { context: &mut Context, ) -> JsResult { // 1. Let x be ? thisBigIntValue(this value). - let x = Self::this_bigint_value(this, context)?; + let x = Self::this_bigint_value(this)?; let radix = args.get_or_undefined(0); @@ -181,9 +190,9 @@ impl BigInt { let radix_mv = match radix_mv { IntegerOrInfinity::Integer(i) if (2..=36).contains(&i) => i, _ => { - return context.throw_range_error( - "radix must be an integer at least 2 and no greater than 36", - ) + return Err(JsNativeError::range() + .with_message("radix must be an integer at least 2 and no greater than 36") + .into()) } }; @@ -209,12 +218,8 @@ impl BigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf - pub(crate) fn value_of( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { - Ok(JsValue::new(Self::this_bigint_value(this, context)?)) + pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + Ok(JsValue::new(Self::this_bigint_value(this)?)) } /// `BigInt.asIntN()` @@ -232,16 +237,11 @@ impl BigInt { let (modulo, bits) = Self::calculate_as_uint_n(args, context)?; if bits > 0 - && modulo - >= JsBigInt::pow( - &JsBigInt::new(2), - &JsBigInt::new(i64::from(bits) - 1), - context, - )? + && modulo >= JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits) - 1))? { Ok(JsValue::new(JsBigInt::sub( &modulo, - &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)), context)?, + &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)))?, ))) } else { Ok(JsValue::new(modulo)) @@ -282,7 +282,7 @@ impl BigInt { Ok(( JsBigInt::mod_floor( &bigint, - &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)), context)?, + &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)))?, ), bits, )) diff --git a/boa_engine/src/builtins/boolean/mod.rs b/boa_engine/src/builtins/boolean/mod.rs index 4377cbd8dab..9a70f288794 100644 --- a/boa_engine/src/builtins/boolean/mod.rs +++ b/boa_engine/src/builtins/boolean/mod.rs @@ -15,6 +15,7 @@ mod tests; use crate::{ builtins::BuiltIn, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, }, @@ -79,11 +80,15 @@ impl Boolean { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue - fn this_boolean_value(value: &JsValue, context: &mut Context) -> JsResult { + fn this_boolean_value(value: &JsValue) -> JsResult { value .as_boolean() .or_else(|| value.as_object().and_then(|obj| obj.borrow().as_boolean())) - .ok_or_else(|| context.construct_type_error("'this' is not a boolean")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a boolean") + .into() + }) } /// The `toString()` method returns a string representing the specified `Boolean` object. @@ -95,12 +100,8 @@ impl Boolean { /// [spec]: https://tc39.es/ecma262/#sec-boolean-object /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { - let boolean = Self::this_boolean_value(this, context)?; + pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + let boolean = Self::this_boolean_value(this)?; Ok(JsValue::new(boolean.to_string())) } @@ -113,11 +114,7 @@ impl Boolean { /// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf #[inline] - pub(crate) fn value_of( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { - Ok(JsValue::new(Self::this_boolean_value(this, context)?)) + pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + Ok(JsValue::new(Self::this_boolean_value(this)?)) } } diff --git a/boa_engine/src/builtins/dataview/mod.rs b/boa_engine/src/builtins/dataview/mod.rs index 0128d9c4bb5..ebcb26e5110 100644 --- a/boa_engine/src/builtins/dataview/mod.rs +++ b/boa_engine/src/builtins/dataview/mod.rs @@ -1,6 +1,7 @@ use crate::{ builtins::{array_buffer::SharedMemoryOrder, typed_array::TypedArrayKind, BuiltIn, JsArgs}, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsObject, ObjectData, @@ -103,31 +104,36 @@ impl DataView { let buffer_obj = args .get_or_undefined(0) .as_object() - .ok_or_else(|| context.construct_type_error("buffer must be an ArrayBuffer"))?; + .ok_or_else(|| JsNativeError::typ().with_message("buffer must be an ArrayBuffer"))?; // 1. If NewTarget is undefined, throw a TypeError exception. let (offset, view_byte_length) = { if new_target.is_undefined() { - return context.throw_type_error("new target is undefined"); + return Err(JsNativeError::typ() + .with_message("new target is undefined") + .into()); } // 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]). let buffer_borrow = buffer_obj.borrow(); - let buffer = buffer_borrow - .as_array_buffer() - .ok_or_else(|| context.construct_type_error("buffer must be an ArrayBuffer"))?; + let buffer = buffer_borrow.as_array_buffer().ok_or_else(|| { + JsNativeError::typ().with_message("buffer must be an ArrayBuffer") + })?; // 3. Let offset be ? ToIndex(byteOffset). let offset = args.get_or_undefined(1).to_index(context)?; // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if buffer.is_detached_buffer() { - return context.throw_type_error("ArrayBuffer is detached"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); } // 5. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. let buffer_byte_length = buffer.array_buffer_byte_length(); // 6. If offset > bufferByteLength, throw a RangeError exception. if offset > buffer_byte_length { - return context - .throw_range_error("Start offset is outside the bounds of the buffer"); + return Err(JsNativeError::range() + .with_message("Start offset is outside the bounds of the buffer") + .into()); } // 7. If byteLength is undefined, then let view_byte_length = if byte_length.is_undefined() { @@ -138,7 +144,9 @@ impl DataView { let view_byte_length = byte_length.to_index(context)?; // 8.b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception. if offset + view_byte_length > buffer_byte_length { - return context.throw_range_error("Invalid data view length"); + return Err(JsNativeError::range() + .with_message("Invalid data view length") + .into()); } view_byte_length @@ -154,10 +162,12 @@ impl DataView { if buffer_obj .borrow() .as_array_buffer() - .ok_or_else(|| context.construct_type_error("buffer must be an ArrayBuffer"))? + .ok_or_else(|| JsNativeError::typ().with_message("buffer must be an ArrayBuffer"))? .is_detached_buffer() { - return context.throw_type_error("ArrayBuffer can't be detached"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer can't be detached") + .into()); } let obj = JsObject::from_proto_and_data( @@ -190,7 +200,7 @@ impl DataView { pub(crate) fn get_buffer( this: &JsValue, _args: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). @@ -198,7 +208,7 @@ impl DataView { let dataview = dataview .as_ref() .and_then(|obj| obj.as_data_view()) - .ok_or_else(|| context.construct_type_error("`this` is not a DataView"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 4. Let buffer be O.[[ViewedArrayBuffer]]. let buffer = dataview.viewed_array_buffer.clone(); @@ -219,7 +229,7 @@ impl DataView { pub(crate) fn get_byte_length( this: &JsValue, _args: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). @@ -227,7 +237,7 @@ impl DataView { let dataview = dataview .as_ref() .and_then(|obj| obj.as_data_view()) - .ok_or_else(|| context.construct_type_error("`this` is not a DataView"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 4. Let buffer be O.[[ViewedArrayBuffer]]. let buffer_borrow = dataview.viewed_array_buffer.borrow(); @@ -236,7 +246,9 @@ impl DataView { .expect("DataView must be constructed with an ArrayBuffer"); // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if borrow.is_detached_buffer() { - return context.throw_type_error("ArrayBuffer is detached"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); } // 6. Let size be O.[[ByteLength]]. let size = dataview.byte_length; @@ -258,7 +270,7 @@ impl DataView { pub(crate) fn get_byte_offset( this: &JsValue, _args: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[DataView]]). @@ -266,7 +278,7 @@ impl DataView { let dataview = dataview .as_ref() .and_then(|obj| obj.as_data_view()) - .ok_or_else(|| context.construct_type_error("`this` is not a DataView"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 4. Let buffer be O.[[ViewedArrayBuffer]]. let buffer_borrow = dataview.viewed_array_buffer.borrow(); @@ -275,7 +287,9 @@ impl DataView { .expect("DataView must be constructed with an ArrayBuffer"); // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if borrow.is_detached_buffer() { - return context.throw_type_error("ArrayBuffer is detached"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); } // 6. Let offset be O.[[ByteOffset]]. let offset = dataview.byte_offset; @@ -306,7 +320,7 @@ impl DataView { let view = view .as_ref() .and_then(|obj| obj.as_data_view()) - .ok_or_else(|| context.construct_type_error("`this` is not a DataView"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; // 3. Let getIndex be ? ToIndex(requestIndex). let get_index = request_index.to_index(context)?; @@ -322,7 +336,9 @@ impl DataView { // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if buffer.is_detached_buffer() { - return context.throw_type_error("ArrayBuffer is detached"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); } // 7. Let viewOffset be view.[[ByteOffset]]. let view_offset = view.byte_offset; @@ -335,7 +351,9 @@ impl DataView { // 10. If getIndex + elementSize > viewSize, throw a RangeError exception. if get_index + element_size > view_size { - return context.throw_range_error("Offset is outside the bounds of the DataView"); + return Err(JsNativeError::range() + .with_message("Offset is outside the bounds of the DataView") + .into()); } // 11. Let bufferIndex be getIndex + viewOffset. @@ -665,7 +683,7 @@ impl DataView { let view = view .as_ref() .and_then(|obj| obj.as_data_view()) - .ok_or_else(|| context.construct_type_error("`this` is not a DataView"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; // 3. Let getIndex be ? ToIndex(requestIndex). let get_index = request_index.to_index(context)?; @@ -688,7 +706,9 @@ impl DataView { // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if buffer.is_detached_buffer() { - return context.throw_type_error("ArrayBuffer is detached"); + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); } // 9. Let viewOffset be view.[[ByteOffset]]. @@ -702,7 +722,9 @@ impl DataView { // 12. If getIndex + elementSize > viewSize, throw a RangeError exception. if get_index + element_size > view_size { - return context.throw_range_error("Offset is outside the bounds of DataView"); + return Err(JsNativeError::range() + .with_message("Offset is outside the bounds of DataView") + .into()); } // 13. Let bufferIndex be getIndex + viewOffset. diff --git a/boa_engine/src/builtins/date/mod.rs b/boa_engine/src/builtins/date/mod.rs index 95fc226d9ee..3e938333196 100644 --- a/boa_engine/src/builtins/date/mod.rs +++ b/boa_engine/src/builtins/date/mod.rs @@ -5,6 +5,7 @@ use super::JsArgs; use crate::{ builtins::BuiltIn, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, }, @@ -47,8 +48,8 @@ fn ignore_ambiguity(result: LocalResult) -> Option { macro_rules! getter_method { ($name:ident) => {{ - fn get_value(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - Ok(JsValue::new(this_time_value(this, context)?.$name())) + fn get_value(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + Ok(JsValue::new(this_time_value(this)?.$name())) } get_value }}; @@ -390,7 +391,7 @@ impl Date { context: &mut Context, ) -> JsResult { let value = &args[0]; - let tv = match this_time_value(value, context) { + let tv = match this_time_value(value) { Ok(dt) => dt.0, _ => match value.to_primitive(context, PreferredType::Default)? { JsValue::String(ref str) => match chrono::DateTime::parse_from_rfc3339(str) { @@ -507,7 +508,9 @@ impl Date { let o = if let Some(o) = this.as_object() { o } else { - return context.throw_type_error("Date.prototype[@@toPrimitive] called on non object"); + return Err(JsNativeError::typ() + .with_message("Date.prototype[@@toPrimitive] called on non object") + .into()); }; let hint = args.get_or_undefined(0); @@ -521,8 +524,9 @@ impl Date { Some("number") => PreferredType::Number, // 5. Else, throw a TypeError exception. _ => { - return context - .throw_type_error("Date.prototype[@@toPrimitive] called with invalid hint") + return Err(JsNativeError::typ() + .with_message("Date.prototype[@@toPrimitive] called with invalid hint") + .into()) } }; @@ -700,10 +704,10 @@ impl Date { pub fn get_timezone_offset( this: &JsValue, _: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let t = this_time_value(this, context)?; + let t = this_time_value(this)?; // 2. If t is NaN, return NaN. if t.0.is_none() { @@ -849,7 +853,7 @@ impl Date { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setDate pub fn set_date(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let dt be ? ToNumber(date). let dt = args @@ -888,7 +892,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). if t.0.is_none() { @@ -946,7 +950,7 @@ impl Date { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setHours pub fn set_hours(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let h be ? ToNumber(hour). let h = args @@ -1005,7 +1009,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Set ms to ? ToNumber(ms). let ms = args @@ -1043,7 +1047,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let m be ? ToNumber(min). let m = args @@ -1091,7 +1095,7 @@ impl Date { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth pub fn set_month(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let m be ? ToNumber(month). let m = args @@ -1136,7 +1140,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let s be ? ToNumber(sec). let s = args @@ -1177,7 +1181,7 @@ impl Date { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear pub fn set_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). if t.0.is_none() { @@ -1234,7 +1238,7 @@ impl Date { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime pub fn set_time(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Perform ? thisTimeValue(this value). - this_time_value(this, context)?; + this_time_value(this)?; // 2. Let t be ? ToNumber(time). let t = if let Some(t) = args.get(0) { @@ -1275,7 +1279,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let dt be ? ToNumber(date). let dt = args @@ -1314,7 +1318,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. If t is NaN, set t to +0𝔽. if t.0.is_none() { @@ -1376,7 +1380,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let h be ? ToNumber(hour). let h = args @@ -1435,7 +1439,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let milli be ? ToNumber(ms). let ms = args @@ -1473,7 +1477,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let m be ? ToNumber(min). let m = args @@ -1529,7 +1533,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let m be ? ToNumber(month). let m = args @@ -1576,7 +1580,7 @@ impl Date { context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this, context)?; + let mut t = this_time_value(this)?; // 2. Let s be ? ToNumber(sec). let s = args @@ -1618,14 +1622,10 @@ impl Date { /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.todatestring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toDateString #[allow(clippy::wrong_self_convention)] - pub fn to_date_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub fn to_date_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let O be this Date object. // 2. Let tv be ? thisTimeValue(O). - let tv = this_time_value(this, context)?; + let tv = this_time_value(this)?; // 3. If tv is NaN, return "Invalid Date". // 4. Let t be LocalTime(tv). @@ -1669,12 +1669,8 @@ impl Date { /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toisostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString #[allow(clippy::wrong_self_convention)] - pub fn to_iso_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this, context)?.0 { + pub fn to_iso_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + if let Some(t) = this_time_value(this)?.0 { Ok(Utc::now() .timezone() .from_utc_datetime(&t) @@ -1682,7 +1678,9 @@ impl Date { .to_string() .into()) } else { - context.throw_range_error("Invalid time value") + Err(JsNativeError::range() + .with_message("Invalid time value") + .into()) } } @@ -1727,9 +1725,9 @@ impl Date { /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString #[allow(clippy::wrong_self_convention)] - pub fn to_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let tv be ? thisTimeValue(this value). - let tv = this_time_value(this, context)?; + let tv = this_time_value(this)?; // 2. Return ToDateString(tv). if let Some(t) = tv.0 { @@ -1756,14 +1754,10 @@ impl Date { /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.totimestring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString #[allow(clippy::wrong_self_convention)] - pub fn to_time_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub fn to_time_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let O be this Date object. // 2. Let tv be ? thisTimeValue(O). - let tv = this_time_value(this, context)?; + let tv = this_time_value(this)?; // 3. If tv is NaN, return "Invalid Date". // 4. Let t be LocalTime(tv). @@ -1923,9 +1917,13 @@ impl Date { /// /// [spec]: https://tc39.es/ecma262/#sec-thistimevalue #[inline] -pub fn this_time_value(value: &JsValue, context: &mut Context) -> JsResult { +pub fn this_time_value(value: &JsValue) -> JsResult { value .as_object() .and_then(|obj| obj.borrow().as_date().copied()) - .ok_or_else(|| context.construct_type_error("'this' is not a Date")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a Date") + .into() + }) } diff --git a/boa_engine/src/builtins/date/tests.rs b/boa_engine/src/builtins/date/tests.rs index 057fd576fd1..ec59ae5fca2 100644 --- a/boa_engine/src/builtins/date/tests.rs +++ b/boa_engine/src/builtins/date/tests.rs @@ -7,21 +7,10 @@ use chrono::prelude::*; // this. fn forward_dt_utc(context: &mut Context, src: &str) -> Option { - let date_time = if let Ok(v) = forward_val(context, src) { - v - } else { - panic!("expected success") - }; - - if let JsValue::Object(ref date_time) = date_time { - if let Some(date_time) = date_time.borrow().as_date() { - date_time.0 - } else { - panic!("expected date") - } - } else { - panic!("expected object") - } + let date_time = forward_val(context, src).unwrap(); + let date_time = date_time.as_object().unwrap(); + let date_time = date_time.borrow(); + date_time.as_date().unwrap().0 } fn forward_dt_local(context: &mut Context, src: &str) -> Option { @@ -59,14 +48,9 @@ fn date_this_time_value() { &mut context, "({toString: Date.prototype.toString}).toString()", ) - .expect_err("Expected error"); - let message_property = &error - .get_property("message") - .expect("Expected 'message' property") - .expect_value() - .clone(); - - assert_eq!(JsValue::new("\'this\' is not a Date"), *message_property); + .unwrap_err(); + let error = error.as_native().unwrap(); + assert_eq!("\'this\' is not a Date", error.message()); } #[test] @@ -201,7 +185,7 @@ fn date_ctor_parse_call() { let date_time = forward_val(&mut context, "Date.parse('2020-06-08T09:16:15.779-07:30')"); - assert_eq!(Ok(JsValue::new(1591634775779f64)), date_time); + assert_eq!(JsValue::new(1591634775779f64), date_time.unwrap()); } #[test] @@ -210,14 +194,14 @@ fn date_ctor_utc_call() { let date_time = forward_val(&mut context, "Date.UTC(2020, 06, 08, 09, 16, 15, 779)"); - assert_eq!(Ok(JsValue::new(1594199775779f64)), date_time); + assert_eq!(JsValue::new(1594199775779f64), date_time.unwrap()); } #[test] fn date_ctor_utc_call_nan() { fn check(src: &str) { let mut context = Context::default(); - let date_time = forward_val(&mut context, src).expect("Expected Success"); + let date_time = forward_val(&mut context, src).unwrap(); assert_eq!(JsValue::nan(), date_time); } @@ -238,10 +222,10 @@ fn date_proto_get_date_call() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getDate()", ); - assert_eq!(Ok(JsValue::new(08f64)), actual); + assert_eq!(JsValue::new(08f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getDate()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -252,10 +236,10 @@ fn date_proto_get_day_call() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getDay()", ); - assert_eq!(Ok(JsValue::new(3f64)), actual); + assert_eq!(JsValue::new(3f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getDay()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -266,10 +250,10 @@ fn date_proto_get_full_year_call() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getFullYear()", ); - assert_eq!(Ok(JsValue::new(2020f64)), actual); + assert_eq!(JsValue::new(2020f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getFullYear()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -280,10 +264,10 @@ fn date_proto_get_hours_call() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getHours()", ); - assert_eq!(Ok(JsValue::new(09f64)), actual); + assert_eq!(JsValue::new(09f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getHours()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -294,10 +278,10 @@ fn date_proto_get_milliseconds_call() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getMilliseconds()", ); - assert_eq!(Ok(JsValue::new(779f64)), actual); + assert_eq!(JsValue::new(779f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getMilliseconds()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -308,10 +292,10 @@ fn date_proto_get_minutes_call() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getMinutes()", ); - assert_eq!(Ok(JsValue::new(16f64)), actual); + assert_eq!(JsValue::new(16f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getMinutes()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -322,10 +306,10 @@ fn date_proto_get_month() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getMonth()", ); - assert_eq!(Ok(JsValue::new(06f64)), actual); + assert_eq!(JsValue::new(06f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getMonth()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -336,10 +320,10 @@ fn date_proto_get_seconds() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getSeconds()", ); - assert_eq!(Ok(JsValue::new(15f64)), actual); + assert_eq!(JsValue::new(15f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getSeconds()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -355,10 +339,10 @@ fn date_proto_get_time() { .ymd(2020, 07, 08) .and_hms_milli(09, 16, 15, 779) .timestamp_millis() as f64; - assert_eq!(Ok(JsValue::new(ts)), actual); + assert_eq!(JsValue::new(ts), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getTime()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -369,10 +353,10 @@ fn date_proto_get_year() { &mut context, "new Date(2020, 06, 08, 09, 16, 15, 779).getYear()", ); - assert_eq!(Ok(JsValue::new(120f64)), actual); + assert_eq!(JsValue::new(120f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getYear()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -385,7 +369,7 @@ fn date_proto_get_timezone_offset() { ); // NB: Host Settings, not TZ specified in the DateTime. - assert_eq!(Ok(JsValue::new(true)), actual); + assert_eq!(JsValue::new(true), actual.unwrap()); let actual = forward_val( &mut context, @@ -395,13 +379,13 @@ fn date_proto_get_timezone_offset() { // The value of now().offset() depends on the host machine, so we have to replicate the method code here. let offset_seconds = f64::from(chrono::Local::now().offset().local_minus_utc()); let offset_minutes = -offset_seconds / 60f64; - assert_eq!(Ok(JsValue::new(offset_minutes)), actual); + assert_eq!(JsValue::new(offset_minutes), actual.unwrap()); let actual = forward_val( &mut context, "new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset()", ); - assert_eq!(Ok(JsValue::new(offset_minutes)), actual); + assert_eq!(JsValue::new(offset_minutes), actual.unwrap()); } #[test] @@ -412,10 +396,10 @@ fn date_proto_get_utc_date_call() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCDate()", ); - assert_eq!(Ok(JsValue::new(08f64)), actual); + assert_eq!(JsValue::new(08f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCDate()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -426,10 +410,10 @@ fn date_proto_get_utc_day_call() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCDay()", ); - assert_eq!(Ok(JsValue::new(3f64)), actual); + assert_eq!(JsValue::new(3f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCDay()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -440,10 +424,10 @@ fn date_proto_get_utc_full_year_call() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCFullYear()", ); - assert_eq!(Ok(JsValue::new(2020f64)), actual); + assert_eq!(JsValue::new(2020f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCFullYear()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -454,10 +438,10 @@ fn date_proto_get_utc_hours_call() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCHours()", ); - assert_eq!(Ok(JsValue::new(09f64)), actual); + assert_eq!(JsValue::new(09f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCHours()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -468,10 +452,10 @@ fn date_proto_get_utc_milliseconds_call() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCMilliseconds()", ); - assert_eq!(Ok(JsValue::new(779f64)), actual); + assert_eq!(JsValue::new(779f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCMilliseconds()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -482,10 +466,10 @@ fn date_proto_get_utc_minutes_call() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCMinutes()", ); - assert_eq!(Ok(JsValue::new(16f64)), actual); + assert_eq!(JsValue::new(16f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCMinutes()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -496,10 +480,10 @@ fn date_proto_get_utc_month() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCMonth()", ); - assert_eq!(Ok(JsValue::new(06f64)), actual); + assert_eq!(JsValue::new(06f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCMonth()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -510,10 +494,10 @@ fn date_proto_get_utc_seconds() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCSeconds()", ); - assert_eq!(Ok(JsValue::new(15f64)), actual); + assert_eq!(JsValue::new(15f64), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCSeconds()"); - assert_eq!(Ok(JsValue::nan()), actual); + assert_eq!(JsValue::nan(), actual.unwrap()); } #[test] @@ -1145,7 +1129,7 @@ fn date_proto_to_date_string() { &mut context, "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.toDateString()", ) - .expect("Successful eval"); + .unwrap(); assert_eq!(JsValue::new("Wed Jul 08 2020"), actual); } @@ -1157,7 +1141,7 @@ fn date_proto_to_gmt_string() { &mut context, "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toGMTString()", ) - .expect("Successful eval"); + .unwrap(); assert_eq!(JsValue::new("Wed, 08 Jul 2020 09:16:15 GMT"), actual); } @@ -1169,7 +1153,7 @@ fn date_proto_to_iso_string() { &mut context, "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toISOString()", ) - .expect("Successful eval"); + .unwrap(); assert_eq!(JsValue::new("2020-07-08T09:16:15.779Z"), actual); } @@ -1181,7 +1165,7 @@ fn date_proto_to_json() { &mut context, "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toJSON()", ) - .expect("Successful eval"); + .unwrap(); assert_eq!(JsValue::new("2020-07-08T09:16:15.779Z"), actual); } @@ -1245,7 +1229,7 @@ fn date_proto_to_utc_string() { &mut context, "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toUTCString()", ) - .expect("Successful eval"); + .unwrap(); assert_eq!(JsValue::new("Wed, 08 Jul 2020 09:16:15 GMT"), actual); } @@ -1257,7 +1241,7 @@ fn date_proto_value_of() { &mut context, "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).valueOf()", ) - .expect("Successful eval"); + .unwrap(); assert_eq!(JsValue::new(1594199775779f64), actual); } @@ -1269,7 +1253,7 @@ fn date_neg() { &mut context, "-new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779))", ) - .expect("Successful eval"); + .unwrap(); assert_eq!(JsValue::new(-1594199775779f64), actual); } @@ -1281,7 +1265,7 @@ fn date_json() { &mut context, "JSON.stringify({ date: new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)) })", ) - .expect("Successful eval"); + .unwrap(); assert_eq!( JsValue::new(r#"{"date":"2020-07-08T09:16:15.779Z"}"#), actual diff --git a/boa_engine/src/builtins/error/aggregate.rs b/boa_engine/src/builtins/error/aggregate.rs index 5c384177c1d..433a8e1fe02 100644 --- a/boa_engine/src/builtins/error/aggregate.rs +++ b/boa_engine/src/builtins/error/aggregate.rs @@ -19,7 +19,7 @@ use crate::{ use boa_profiler::Profiler; use tap::{Conv, Pipe}; -use super::Error; +use super::{Error, ErrorKind}; #[derive(Debug, Clone, Copy)] pub(crate) struct AggregateError; @@ -73,7 +73,7 @@ impl AggregateError { StandardConstructors::aggregate_error, context, )?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Aggregate)); // 3. If message is not undefined, then let message = args.get_or_undefined(1); diff --git a/boa_engine/src/builtins/error/eval.rs b/boa_engine/src/builtins/error/eval.rs index 563749207c4..0451756fe1a 100644 --- a/boa_engine/src/builtins/error/eval.rs +++ b/boa_engine/src/builtins/error/eval.rs @@ -23,7 +23,7 @@ use crate::{ use boa_profiler::Profiler; use tap::{Conv, Pipe}; -use super::Error; +use super::{Error, ErrorKind}; /// JavaScript `EvalError` implementation. #[derive(Debug, Clone, Copy)] @@ -70,7 +70,7 @@ impl EvalError { // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). let prototype = get_prototype_from_constructor(new_target, StandardConstructors::eval_error, context)?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Eval)); // 3. If message is not undefined, then let message = args.get_or_undefined(0); diff --git a/boa_engine/src/builtins/error/mod.rs b/boa_engine/src/builtins/error/mod.rs index c68d1854446..9e1ad67563a 100644 --- a/boa_engine/src/builtins/error/mod.rs +++ b/boa_engine/src/builtins/error/mod.rs @@ -13,6 +13,7 @@ use crate::{ builtins::BuiltIn, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, }, @@ -43,6 +44,18 @@ pub(crate) use self::uri::UriError; use super::JsArgs; +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ErrorKind { + Aggregate, + Error, + Eval, + Type, + Range, + Reference, + Syntax, + Uri, +} + /// Built-in `Error` object. #[derive(Debug, Clone, Copy)] pub(crate) struct Error; @@ -107,7 +120,7 @@ impl Error { // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »). let prototype = get_prototype_from_constructor(new_target, StandardConstructors::error, context)?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Error)); // 3. If message is not undefined, then let message = args.get_or_undefined(0); @@ -147,7 +160,9 @@ impl Error { o // 2. If Type(O) is not Object, throw a TypeError exception. } else { - return context.throw_type_error("'this' is not an Object"); + return Err(JsNativeError::typ() + .with_message("'this' is not an Object") + .into()); }; // 3. Let name be ? Get(O, "name"). diff --git a/boa_engine/src/builtins/error/range.rs b/boa_engine/src/builtins/error/range.rs index 1fa1258b0a8..3c5de072bf1 100644 --- a/boa_engine/src/builtins/error/range.rs +++ b/boa_engine/src/builtins/error/range.rs @@ -21,7 +21,7 @@ use crate::{ use boa_profiler::Profiler; use tap::{Conv, Pipe}; -use super::Error; +use super::{Error, ErrorKind}; /// JavaScript `RangeError` implementation. #[derive(Debug, Clone, Copy)] @@ -68,7 +68,7 @@ impl RangeError { // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). let prototype = get_prototype_from_constructor(new_target, StandardConstructors::range_error, context)?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Range)); // 3. If message is not undefined, then let message = args.get_or_undefined(0); diff --git a/boa_engine/src/builtins/error/reference.rs b/boa_engine/src/builtins/error/reference.rs index e130678b955..4eb341cf5c1 100644 --- a/boa_engine/src/builtins/error/reference.rs +++ b/boa_engine/src/builtins/error/reference.rs @@ -21,7 +21,7 @@ use crate::{ use boa_profiler::Profiler; use tap::{Conv, Pipe}; -use super::Error; +use super::{Error, ErrorKind}; #[derive(Debug, Clone, Copy)] pub(crate) struct ReferenceError; @@ -74,7 +74,7 @@ impl ReferenceError { StandardConstructors::reference_error, context, )?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Reference)); // 3. If message is not undefined, then let message = args.get_or_undefined(0); diff --git a/boa_engine/src/builtins/error/syntax.rs b/boa_engine/src/builtins/error/syntax.rs index c7dfb012d37..abc7f07429a 100644 --- a/boa_engine/src/builtins/error/syntax.rs +++ b/boa_engine/src/builtins/error/syntax.rs @@ -23,7 +23,7 @@ use crate::{ use boa_profiler::Profiler; use tap::{Conv, Pipe}; -use super::Error; +use super::{Error, ErrorKind}; /// JavaScript `SyntaxError` implementation. #[derive(Debug, Clone, Copy)] @@ -73,7 +73,7 @@ impl SyntaxError { StandardConstructors::syntax_error, context, )?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Syntax)); // 3. If message is not undefined, then let message = args.get_or_undefined(0); diff --git a/boa_engine/src/builtins/error/type.rs b/boa_engine/src/builtins/error/type.rs index 1f39077784e..55c262ea5b4 100644 --- a/boa_engine/src/builtins/error/type.rs +++ b/boa_engine/src/builtins/error/type.rs @@ -18,6 +18,7 @@ use crate::{ builtins::{function::Function, BuiltIn, JsArgs}, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, }, @@ -27,7 +28,7 @@ use crate::{ use boa_profiler::Profiler; use tap::{Conv, Pipe}; -use super::Error; +use super::{Error, ErrorKind}; /// JavaScript `TypeError` implementation. #[derive(Debug, Clone, Copy)] @@ -74,7 +75,7 @@ impl TypeError { // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). let prototype = get_prototype_from_constructor(new_target, StandardConstructors::type_error, context)?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Type)); // 3. If message is not undefined, then let message = args.get_or_undefined(0); @@ -95,8 +96,8 @@ impl TypeError { } pub(crate) fn create_throw_type_error(context: &mut Context) -> JsObject { - fn throw_type_error(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - context.throw_type_error("'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them") + fn throw_type_error(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + Err(JsNativeError::typ().with_message("'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them").into()) } let function = JsObject::from_proto_and_data( diff --git a/boa_engine/src/builtins/error/uri.rs b/boa_engine/src/builtins/error/uri.rs index 21469f338df..cfdcad54daa 100644 --- a/boa_engine/src/builtins/error/uri.rs +++ b/boa_engine/src/builtins/error/uri.rs @@ -22,7 +22,7 @@ use crate::{ use boa_profiler::Profiler; use tap::{Conv, Pipe}; -use super::Error; +use super::{Error, ErrorKind}; /// JavaScript `URIError` implementation. #[derive(Debug, Clone, Copy)] @@ -69,7 +69,7 @@ impl UriError { // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). let prototype = get_prototype_from_constructor(new_target, StandardConstructors::uri_error, context)?; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error()); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Uri)); // 3. If message is not undefined, then let message = args.get_or_undefined(0); diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 71b82951fad..0292bf6d4ee 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -11,9 +11,10 @@ use crate::{ builtins::{BuiltIn, JsArgs}, + error::JsNativeError, object::FunctionBuilder, property::Attribute, - Context, JsValue, + Context, JsResult, JsValue, }; use boa_profiler::Profiler; use rustc_hash::FxHashSet; @@ -48,7 +49,7 @@ impl Eval { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-eval-x - fn eval(_: &JsValue, args: &[JsValue], context: &mut Context) -> Result { + fn eval(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Return ? PerformEval(x, false, false). Self::perform_eval(args.get_or_undefined(0), false, false, context) } @@ -64,7 +65,7 @@ impl Eval { direct: bool, strict: bool, context: &mut Context, - ) -> Result { + ) -> JsResult { // 1. Assert: If direct is false, then strictCaller is also false. if !direct { debug_assert!(!strict); @@ -82,7 +83,7 @@ impl Eval { // Parse the script body and handle early errors (6 - 11) let body = match context.parse_eval(x.as_bytes(), direct, strict) { Ok(body) => body, - Err(e) => return context.throw_syntax_error(e.to_string()), + Err(e) => return Err(JsNativeError::syntax().with_message(e.to_string()).into()), }; // 12 - 13 are implicit in the call of `Context::compile_with_new_declarative`. @@ -109,7 +110,7 @@ impl Eval { { let name = context.interner().resolve_expect(name); let msg = format!("variable declaration {name} in eval function already exists as lexically declaration"); - return context.throw_syntax_error(msg); + return Err(JsNativeError::syntax().with_message(msg).into()); } // Compile and execute the eval statement list. diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index d5c23ea1bcd..0a7034dbeba 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -16,6 +16,7 @@ use crate::{ bytecompiler::{FunctionCompiler, FunctionKind}, context::intrinsics::StandardConstructors, environments::DeclarativeEnvironmentStack, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, JsObject, NativeObject, Object, ObjectData, @@ -510,16 +511,16 @@ impl BuiltInFunctionObject { ) { Ok(parameters) => parameters, Err(e) => { - return context.throw_syntax_error(format!( - "failed to parse function parameters: {e}" - )) + return Err(JsNativeError::syntax() + .with_message(format!("failed to parse function parameters: {e}")) + .into()) } }; if generator && parameters.contains_yield_expression() { - return context.throw_syntax_error( + return Err(JsNativeError::syntax().with_message( "yield expression is not allowed in formal parameter list of generator function", - ); + ).into()); } parameters @@ -527,16 +528,16 @@ impl BuiltInFunctionObject { // It is a Syntax Error if FormalParameters Contains YieldExpression is true. if generator && r#async && parameters.contains_yield_expression() { - return context.throw_syntax_error( - "yield expression not allowed in async generator parameters", - ); + return Err(JsNativeError::syntax() + .with_message("yield expression not allowed in async generator parameters") + .into()); } // It is a Syntax Error if FormalParameters Contains AwaitExpression is true. if generator && r#async && parameters.contains_await_expression() { - return context.throw_syntax_error( - "await expression not allowed in async generator parameters", - ); + return Err(JsNativeError::syntax() + .with_message("await expression not allowed in async generator parameters") + .into()); } let body_arg = body_arg.to_string(context)?; @@ -548,8 +549,9 @@ impl BuiltInFunctionObject { ) { Ok(statement_list) => statement_list, Err(e) => { - return context - .throw_syntax_error(format!("failed to parse function body: {e}")) + return Err(JsNativeError::syntax() + .with_message(format!("failed to parse function body: {e}")) + .into()) } }; @@ -559,9 +561,9 @@ impl BuiltInFunctionObject { for parameter in parameters.parameters.iter() { for name in parameter.names() { if name == Sym::ARGUMENTS || name == Sym::EVAL { - return context.throw_syntax_error( - " Unexpected 'eval' or 'arguments' in strict mode", - ); + return Err(JsNativeError::syntax() + .with_message(" Unexpected 'eval' or 'arguments' in strict mode") + .into()); } } } @@ -570,16 +572,19 @@ impl BuiltInFunctionObject { // Early Error: If the source code matching FormalParameters is strict mode code, // the Early Error rules for UniqueFormalParameters : FormalParameters are applied. if (body.strict()) && parameters.has_duplicates() { - return context - .throw_syntax_error("Duplicate parameter name not allowed in this context"); + return Err(JsNativeError::syntax() + .with_message("Duplicate parameter name not allowed in this context") + .into()); } // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true // and IsSimpleParameterList of FormalParameters is false. if body.strict() && !parameters.is_simple() { - return context.throw_syntax_error( - "Illegal 'use strict' directive in function with non-simple parameter list", - ); + return Err(JsNativeError::syntax() + .with_message( + "Illegal 'use strict' directive in function with non-simple parameter list", + ) + .into()); } // It is a Syntax Error if any element of the BoundNames of FormalParameters @@ -593,10 +598,12 @@ impl BuiltInFunctionObject { .iter() .any(|(name, _)| *name == param_name) { - return context.throw_syntax_error(format!( - "Redeclaration of formal parameter `{}`", - context.interner().resolve_expect(param_name) - )); + return Err(JsNativeError::syntax() + .with_message(format!( + "Redeclaration of formal parameter `{}`", + context.interner().resolve_expect(param_name) + )) + .into()); } } } @@ -671,7 +678,7 @@ impl BuiltInFunctionObject { // 1. Let func be the this value. // 2. If IsCallable(func) is false, throw a TypeError exception. let func = this.as_callable().ok_or_else(|| { - context.construct_type_error(format!("{} is not a function", this.display())) + JsNativeError::typ().with_message(format!("{} is not a function", this.display())) })?; let this_arg = args.get_or_undefined(0); @@ -711,7 +718,8 @@ impl BuiltInFunctionObject { // 1. Let Target be the this value. // 2. If IsCallable(Target) is false, throw a TypeError exception. let target = this.as_callable().ok_or_else(|| { - context.construct_type_error("cannot bind `this` without a `[[Call]]` internal method") + JsNativeError::typ() + .with_message("cannot bind `this` without a `[[Call]]` internal method") })?; let this_arg = args.get_or_undefined(0).clone(); @@ -792,7 +800,7 @@ impl BuiltInFunctionObject { // 1. Let func be the this value. // 2. If IsCallable(func) is false, throw a TypeError exception. let func = this.as_callable().ok_or_else(|| { - context.construct_type_error(format!("{} is not a function", this.display())) + JsNativeError::typ().with_message(format!("{} is not a function", this.display())) })?; let this_arg = args.get_or_undefined(0); @@ -809,7 +817,7 @@ impl BuiltInFunctionObject { let function = object .as_deref() .and_then(Object::as_function) - .ok_or_else(|| context.construct_type_error("Not a function"))?; + .ok_or_else(|| JsNativeError::typ().with_message("Not a function"))?; let name = { // Is there a case here where if there is no name field on a value diff --git a/boa_engine/src/builtins/function/tests.rs b/boa_engine/src/builtins/function/tests.rs index f1833439739..f661164b73c 100644 --- a/boa_engine/src/builtins/function/tests.rs +++ b/boa_engine/src/builtins/function/tests.rs @@ -1,8 +1,9 @@ use crate::{ + error::JsNativeError, forward, forward_val, object::FunctionBuilder, property::{Attribute, PropertyDescriptor}, - Context, JsString, + Context, JsNativeErrorKind, JsString, }; #[allow(clippy::float_cmp)] @@ -139,10 +140,15 @@ fn function_prototype_call_throw() { let call = Function.prototype.call; call(call) "#; - let value = forward_val(&mut context, throw).unwrap_err(); - assert!(value.is_object()); - let string = value.to_string(&mut context).unwrap(); - assert!(string.starts_with("TypeError")); + let err = forward_val(&mut context, throw).unwrap_err(); + let err = err.as_native().unwrap(); + assert!(matches!( + err, + JsNativeError { + kind: JsNativeErrorKind::Type, + .. + } + )); } #[test] @@ -247,7 +253,7 @@ fn closure_capture_clone() { .__get_own_property__(&"key".into(), context)? .and_then(|prop| prop.value().cloned()) .and_then(|val| val.as_string().cloned()) - .ok_or_else(|| context.construct_type_error("invalid `key` property"))?, + .ok_or_else(|| JsNativeError::typ().with_message("invalid `key` property"))?, ); Ok(hw.into()) }, diff --git a/boa_engine/src/builtins/generator/mod.rs b/boa_engine/src/builtins/generator/mod.rs index c4460785006..0a737a7ea74 100644 --- a/boa_engine/src/builtins/generator/mod.rs +++ b/boa_engine/src/builtins/generator/mod.rs @@ -12,6 +12,7 @@ use crate::{ builtins::{iterable::create_iter_result_object, BuiltIn, JsArgs}, environments::DeclarativeEnvironmentStack, + error::JsNativeError, object::{ConstructorBuilder, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor}, symbol::WellKnownSymbols, @@ -151,7 +152,9 @@ impl Generator { Some(obj) if obj.is_generator() => { Self::generator_resume(obj, args.get_or_undefined(0), context) } - _ => context.throw_type_error("Generator.prototype.next called on non generator"), + _ => Err(JsNativeError::typ() + .with_message("Generator.prototype.next called on non generator") + .into()), } } @@ -195,7 +198,7 @@ impl Generator { // 1. Let g be the this value. // 2. Let C be ThrowCompletion(exception). // 3. Return ? GeneratorResumeAbrupt(g, C, empty). - Self::generator_resume_abrupt(this, Err(args.get_or_undefined(0).clone()), context) + Self::generator_resume_abrupt(this, Err(args.get_or_undefined(0).clone().into()), context) } /// `27.5.3.3 GeneratorResume ( generator, value, generatorBrand )` @@ -212,12 +215,14 @@ impl Generator { // 1. Let state be ? GeneratorValidate(generator, generatorBrand). let mut generator_obj_mut = generator_obj.borrow_mut(); let generator = generator_obj_mut.as_generator_mut().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + JsNativeError::typ().with_message("generator resumed on non generator object") })?; let state = generator.state; if state == GeneratorState::Executing { - return Err(context.construct_type_error("Generator should not be executing")); + return Err(JsNativeError::typ() + .with_message("Generator should not be executing") + .into()); } // 2. If state is completed, return CreateIterResultObject(undefined, true). @@ -314,16 +319,18 @@ impl Generator { ) -> JsResult { // 1. Let state be ? GeneratorValidate(generator, generatorBrand). let generator_obj = this.as_object().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + JsNativeError::typ().with_message("generator resumed on non generator object") })?; let mut generator_obj_mut = generator_obj.borrow_mut(); let generator = generator_obj_mut.as_generator_mut().ok_or_else(|| { - context.construct_type_error("generator resumed on non generator object") + JsNativeError::typ().with_message("generator resumed on non generator object") })?; let mut state = generator.state; if state == GeneratorState::Executing { - return Err(context.construct_type_error("Generator should not be executing")); + return Err(JsNativeError::typ() + .with_message("Generator should not be executing") + .into()); } // 2. If state is suspendedStart, then @@ -379,6 +386,7 @@ impl Generator { context.run() } Err(value) => { + let value = value.to_value(context); context.vm.push(value); context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; context.run() diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index 333c224bc41..df470f9691f 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -9,6 +9,7 @@ use crate::{ context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsFunction, JsObject, ObjectData, @@ -204,13 +205,17 @@ pub(crate) fn to_date_time_options( // 9. If required is "date" and timeStyle is not undefined, then if required == &DateTimeReqs::Date && !time_style.is_undefined() { // a. Throw a TypeError exception. - return context.throw_type_error("'date' is required, but timeStyle was defined"); + return Err(JsNativeError::typ() + .with_message("'date' is required, but timeStyle was defined") + .into()); } // 10. If required is "time" and dateStyle is not undefined, then if required == &DateTimeReqs::Time && !date_style.is_undefined() { // a. Throw a TypeError exception. - return context.throw_type_error("'time' is required, but dateStyle was defined"); + return Err(JsNativeError::typ() + .with_message("'time' is required, but dateStyle was defined") + .into()); } // 11. If needDefaults is true and defaults is either "date" or "all", then diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index 116dbfbe702..4eca78b7bdc 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -10,6 +10,7 @@ use crate::{ builtins::intl::date_time_format::DateTimeFormat, builtins::{Array, BuiltIn, JsArgs}, + error::JsNativeError, object::{JsObject, ObjectInitializer}, property::Attribute, symbol::WellKnownSymbols, @@ -459,7 +460,9 @@ fn canonicalize_locale_list(args: &[JsValue], context: &mut Context) -> JsResult let k_value = o.get(k, context)?; // ii. If Type(kValue) is not String or Object, throw a TypeError exception. if !(k_value.is_object() || k_value.is_string()) { - return context.throw_type_error("locale should be a String or Object"); + return Err(JsNativeError::typ() + .with_message("locale should be a String or Object") + .into()); } // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then // TODO: handle checks for InitializedLocale internal slot (there should be an if statement here) @@ -468,8 +471,10 @@ fn canonicalize_locale_list(args: &[JsValue], context: &mut Context) -> JsResult // 1. Let tag be ? ToString(kValue). let tag = k_value.to_string(context)?; // v. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception. - let mut tag = tag.parse().map_err(|_| { - context.construct_range_error("locale is not a structurally valid language tag") + let mut tag = tag.parse().map_err(|e| { + JsNativeError::range().with_message(format!( + "locale is not a structurally valid language tag: {e}" + )) })?; // vi. Let canonicalizedTag be CanonicalizeUnicodeLocaleId(tag). @@ -751,7 +756,9 @@ pub(crate) fn get_option( GetOptionType::String => { let string_value = value.to_string(context)?; if !values.is_empty() && !values.contains(&string_value) { - return context.throw_range_error("GetOption: values array does not contain value"); + return Err(JsNativeError::range() + .with_message("GetOption: values array does not contain value") + .into()); } JsValue::String(string_value) } @@ -815,7 +822,9 @@ pub(crate) fn default_number_option( // 3. If value is NaN or less than minimum or greater than maximum, throw a RangeError exception. if value.is_nan() || value < minimum || value > maximum { - return context.throw_range_error("DefaultNumberOption: value is out of range."); + return Err(JsNativeError::range() + .with_message("DefaultNumberOption: value is out of range.") + .into()); } // 4. Return floor(value). diff --git a/boa_engine/src/builtins/intl/tests.rs b/boa_engine/src/builtins/intl/tests.rs index 6abfb0c6002..151d5a7e945 100644 --- a/boa_engine/src/builtins/intl/tests.rs +++ b/boa_engine/src/builtins/intl/tests.rs @@ -380,8 +380,9 @@ fn get_opt() { let maximum = 10.0; let fallback_val = 5.0; let fallback = Some(fallback_val); - let get_option_result = default_number_option(&value, minimum, maximum, fallback, &mut context); - assert_eq!(get_option_result, Ok(fallback)); + let get_option_result = + default_number_option(&value, minimum, maximum, fallback, &mut context).unwrap(); + assert_eq!(get_option_result, fallback); let value = JsValue::nan(); let minimum = 1.0; @@ -409,8 +410,9 @@ fn get_opt() { let minimum = 1.0; let maximum = 10.0; let fallback = Some(5.0); - let get_option_result = default_number_option(&value, minimum, maximum, fallback, &mut context); - assert_eq!(get_option_result, Ok(Some(value_f64))); + let get_option_result = + default_number_option(&value, minimum, maximum, fallback, &mut context).unwrap(); + assert_eq!(get_option_result, Some(value_f64)); let options = JsObject::empty(); let property = "fractionalSecondDigits"; @@ -419,8 +421,8 @@ fn get_opt() { let fallback_val = 5.0; let fallback = Some(fallback_val); let get_option_result = - get_number_option(&options, property, minimum, maximum, fallback, &mut context); - assert_eq!(get_option_result, Ok(fallback)); + get_number_option(&options, property, minimum, maximum, fallback, &mut context).unwrap(); + assert_eq!(get_option_result, fallback); let options = JsObject::empty(); let value_f64 = 8.0; @@ -433,8 +435,8 @@ fn get_opt() { let maximum = 10.0; let fallback = Some(5.0); let get_option_result = - get_number_option(&options, property, minimum, maximum, fallback, &mut context); - assert_eq!(get_option_result, Ok(Some(value_f64))); + get_number_option(&options, property, minimum, maximum, fallback, &mut context).unwrap(); + assert_eq!(get_option_result, Some(value_f64)); } #[test] @@ -475,16 +477,16 @@ fn to_date_time_opts() { let numeric_jsstring = JsValue::String(JsString::new("numeric")); assert_eq!( - date_time_opts.get("year", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("year", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("month", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("month", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("day", &mut context), - Ok(numeric_jsstring) + date_time_opts.get("day", &mut context).unwrap(), + numeric_jsstring ); let date_time_opts = to_date_time_options( @@ -497,16 +499,16 @@ fn to_date_time_opts() { let numeric_jsstring = JsValue::String(JsString::new("numeric")); assert_eq!( - date_time_opts.get("hour", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("hour", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("minute", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("minute", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("second", &mut context), - Ok(numeric_jsstring) + date_time_opts.get("second", &mut context).unwrap(), + numeric_jsstring ); let date_time_opts = to_date_time_options( @@ -519,27 +521,27 @@ fn to_date_time_opts() { let numeric_jsstring = JsValue::String(JsString::new("numeric")); assert_eq!( - date_time_opts.get("year", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("year", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("month", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("month", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("day", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("day", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("hour", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("hour", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("minute", &mut context), - Ok(numeric_jsstring.clone()) + date_time_opts.get("minute", &mut context).unwrap(), + numeric_jsstring.clone() ); assert_eq!( - date_time_opts.get("second", &mut context), - Ok(numeric_jsstring) + date_time_opts.get("second", &mut context).unwrap(), + numeric_jsstring ); } diff --git a/boa_engine/src/builtins/iterable/mod.rs b/boa_engine/src/builtins/iterable/mod.rs index 78bbfc97f49..9b22444b774 100644 --- a/boa_engine/src/builtins/iterable/mod.rs +++ b/boa_engine/src/builtins/iterable/mod.rs @@ -4,6 +4,7 @@ use crate::{ string::string_iterator::StringIterator, ArrayIterator, ForInIterator, MapIterator, SetIterator, }, + error::JsNativeError, object::{JsObject, ObjectInitializer}, symbol::WellKnownSymbols, Context, JsResult, JsValue, @@ -173,7 +174,7 @@ impl JsValue { // 4. If Type(iterator) is not Object, throw a TypeError exception. let iterator_obj = iterator .as_object() - .ok_or_else(|| context.construct_type_error("the iterator is not an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("the iterator is not an object"))?; // 5. Let nextMethod be ? GetV(iterator, "next"). let next_method = iterator.get_v("next", context)?; @@ -333,7 +334,9 @@ impl IteratorRecord { let next_method = if let Some(next_method) = self.next_method.as_callable() { next_method } else { - return context.throw_type_error("iterable next method not a function"); + return Err(JsNativeError::typ() + .with_message("iterable next method not a function") + .into()); }; let result = if let Some(value) = value { @@ -351,7 +354,9 @@ impl IteratorRecord { if let Some(o) = result.as_object() { Ok(IteratorResult { object: o.clone() }) } else { - context.throw_type_error("next value should be an object") + Err(JsNativeError::typ() + .with_message("next value should be an object") + .into()) } } @@ -446,7 +451,9 @@ impl IteratorRecord { Ok(completion) } else { // 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception. - context.throw_type_error("inner result was not an object") + Err(JsNativeError::typ() + .with_message("inner result was not an object") + .into()) } } } diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index e51e10110cf..f0f8b0a0daf 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -19,6 +19,7 @@ use crate::{ string::{is_leading_surrogate, is_trailing_surrogate}, BuiltIn, }, + error::JsNativeError, object::{JsObject, ObjectInitializer, RecursionLimiter}, property::{Attribute, PropertyNameKind}, symbol::WellKnownSymbols, @@ -79,7 +80,7 @@ impl Json { // 2. Parse ! StringToCodePoints(jsonString) as a JSON text as specified in ECMA-404. // Throw a SyntaxError exception if it is not a valid JSON text as defined in that specification. if let Err(e) = serde_json::from_str::(&json_string) { - return context.throw_syntax_error(e.to_string()); + return Err(JsNativeError::syntax().with_message(e.to_string()).into()); } // 3. Let scriptString be the string-concatenation of "(", jsonString, and ");". @@ -448,7 +449,9 @@ impl Json { // 10. If Type(value) is BigInt, throw a TypeError exception. if value.is_bigint() { - return context.throw_type_error("cannot serialize bigint to JSON"); + return Err(JsNativeError::typ() + .with_message("cannot serialize bigint to JSON") + .into()); } // 11. If Type(value) is Object and IsCallable(value) is false, then @@ -531,7 +534,9 @@ impl Json { // 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical. let limiter = RecursionLimiter::new(value); if limiter.live { - return context.throw_type_error("cyclic object value"); + return Err(JsNativeError::typ() + .with_message("cyclic object value") + .into()); } // 2. Append value to state.[[Stack]]. @@ -646,7 +651,9 @@ impl Json { // 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical. let limiter = RecursionLimiter::new(value); if limiter.live { - return context.throw_type_error("cyclic object value"); + return Err(JsNativeError::typ() + .with_message("cyclic object value") + .into()); } // 2. Append value to state.[[Stack]]. diff --git a/boa_engine/src/builtins/map/map_iterator.rs b/boa_engine/src/builtins/map/map_iterator.rs index 80cc1697ced..12d48fdc1f4 100644 --- a/boa_engine/src/builtins/map/map_iterator.rs +++ b/boa_engine/src/builtins/map/map_iterator.rs @@ -1,6 +1,7 @@ use super::ordered_map::MapLock; use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue}, + error::JsNativeError, object::{JsObject, ObjectData}, property::{PropertyDescriptor, PropertyNameKind}, symbol::WellKnownSymbols, @@ -60,7 +61,9 @@ impl MapIterator { return Ok(map_iterator.into()); } } - context.throw_type_error("`this` is not a Map") + Err(JsNativeError::typ() + .with_message("`this` is not a Map") + .into()) } /// %MapIteratorPrototype%.next( ) @@ -76,7 +79,7 @@ impl MapIterator { let map_iterator = map_iterator .as_mut() .and_then(|obj| obj.as_map_iterator_mut()) - .ok_or_else(|| context.construct_type_error("`this` is not a MapIterator"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a MapIterator"))?; let item_kind = map_iterator.map_iteration_kind; diff --git a/boa_engine/src/builtins/map/mod.rs b/boa_engine/src/builtins/map/mod.rs index 8e8920744be..9a128d1746f 100644 --- a/boa_engine/src/builtins/map/mod.rs +++ b/boa_engine/src/builtins/map/mod.rs @@ -15,6 +15,7 @@ use super::JsArgs; use crate::{ builtins::BuiltIn, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsObject, ObjectData, @@ -121,8 +122,9 @@ impl Map { ) -> JsResult { // 1. If NewTarget is undefined, throw a TypeError exception. if new_target.is_undefined() { - return context - .throw_type_error("calling a builtin Map constructor without new is forbidden"); + return Err(JsNativeError::typ() + .with_message("calling a builtin Map constructor without new is forbidden") + .into()); } // 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%Map.prototype%", « [[MapData]] »). @@ -206,11 +208,7 @@ impl Map { /// /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.set /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set - pub(crate) fn set( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn set(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let key = args.get_or_undefined(0); let value = args.get_or_undefined(1); @@ -241,7 +239,9 @@ impl Map { return Ok(this.clone()); } } - context.throw_type_error("'this' is not a Map") + Err(JsNativeError::typ() + .with_message("'this' is not a Map") + .into()) } /// `get Map.prototype.size` @@ -255,11 +255,7 @@ impl Map { /// /// [spec]: https://tc39.es/ecma262/#sec-get-map.prototype.size /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size - pub(crate) fn get_size( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn get_size(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let M be the this value. if let Some(object) = this.as_object() { // 2. Perform ? RequireInternalSlot(M, [[MapData]]). @@ -272,7 +268,9 @@ impl Map { return Ok(map.len().into()); } } - context.throw_type_error("'this' is not a Map") + Err(JsNativeError::typ() + .with_message("'this' is not a Map") + .into()) } /// `Map.prototype.delete( key )` @@ -286,11 +284,7 @@ impl Map { /// /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.delete /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete - pub(crate) fn delete( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn delete(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let key = args.get_or_undefined(0); // 1. Let M be the this value. @@ -306,7 +300,9 @@ impl Map { return Ok(map.remove(key).is_some().into()); } } - context.throw_type_error("'this' is not a Map") + Err(JsNativeError::typ() + .with_message("'this' is not a Map") + .into()) } /// `Map.prototype.get( key )` @@ -319,11 +315,7 @@ impl Map { /// /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.get /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get - pub(crate) fn get( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn get(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { const JS_ZERO: &JsValue = &JsValue::Rational(0f64); let key = args.get_or_undefined(0); @@ -350,7 +342,9 @@ impl Map { } } - context.throw_type_error("'this' is not a Map") + Err(JsNativeError::typ() + .with_message("'this' is not a Map") + .into()) } /// `Map.prototype.clear( )` @@ -363,7 +357,7 @@ impl Map { /// /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear - pub(crate) fn clear(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let M be the this value. // 2. Perform ? RequireInternalSlot(M, [[MapData]]). if let Some(object) = this.as_object() { @@ -378,7 +372,9 @@ impl Map { return Ok(JsValue::undefined()); } } - context.throw_type_error("'this' is not a Map") + Err(JsNativeError::typ() + .with_message("'this' is not a Map") + .into()) } /// `Map.prototype.has( key )` @@ -391,11 +387,7 @@ impl Map { /// /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has - pub(crate) fn has( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn has(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { const JS_ZERO: &JsValue = &JsValue::Rational(0f64); let key = args.get_or_undefined(0); @@ -422,7 +414,9 @@ impl Map { } } - context.throw_type_error("'this' is not a Map") + Err(JsNativeError::typ() + .with_message("'this' is not a Map") + .into()) } /// `Map.prototype.forEach( callbackFn [ , thisArg ] )` @@ -445,12 +439,12 @@ impl Map { let map = this .as_object() .filter(|obj| obj.is_map()) - .ok_or_else(|| context.construct_type_error("`this` is not a Map"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a Map"))?; // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. let callback = args.get_or_undefined(0); let callback = callback.as_callable().ok_or_else(|| { - context.construct_type_error(format!("{} is not a function", callback.display())) + JsNativeError::typ().with_message(format!("{} is not a function", callback.display())) })?; let this_arg = args.get_or_undefined(1); @@ -533,7 +527,7 @@ pub(crate) fn add_entries_from_iterable( ) -> JsResult { // 1. If IsCallable(adder) is false, throw a TypeError exception. let adder = adder.as_callable().ok_or_else(|| { - context.construct_type_error("property `set` of `NewTarget` is not callable") + JsNativeError::typ().with_message("property `set` of `NewTarget` is not callable") })?; // 2. Let iteratorRecord be ? GetIterator(iterable). @@ -557,8 +551,9 @@ pub(crate) fn add_entries_from_iterable( // d. If Type(nextItem) is not Object, then } else { // i. Let error be ThrowCompletion(a newly created TypeError object). - let err = context - .throw_type_error("cannot get key and value from primitive item of `iterable`"); + let err = Err(JsNativeError::typ() + .with_message("cannot get key and value from primitive item of `iterable`") + .into()); // ii. Return ? IteratorClose(iteratorRecord, error). return iterator_record.close(err, context); diff --git a/boa_engine/src/builtins/number/mod.rs b/boa_engine/src/builtins/number/mod.rs index e39ade669c2..d6ecd7545b3 100644 --- a/boa_engine/src/builtins/number/mod.rs +++ b/boa_engine/src/builtins/number/mod.rs @@ -16,6 +16,7 @@ use crate::{ builtins::{string::is_trimmable_whitespace, BuiltIn, JsArgs}, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsObject, ObjectData, @@ -194,11 +195,15 @@ impl Number { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue - fn this_number_value(value: &JsValue, context: &mut Context) -> JsResult { + fn this_number_value(value: &JsValue) -> JsResult { value .as_number() .or_else(|| value.as_object().and_then(|obj| obj.borrow().as_number())) - .ok_or_else(|| context.construct_type_error("'this' is not a number")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a number") + .into() + }) } /// `Number.prototype.toExponential( [fractionDigits] )` @@ -218,7 +223,7 @@ impl Number { context: &mut Context, ) -> JsResult { // 1. Let x be ? thisNumberValue(this value). - let this_num = Self::this_number_value(this, context)?; + let this_num = Self::this_number_value(this)?; let precision = match args.get(0) { None | Some(JsValue::Undefined) => None, // 2. Let f be ? ToIntegerOrInfinity(fractionDigits). @@ -238,8 +243,9 @@ impl Number { f64_to_exponential_with_precision(this_num, precision as usize) } _ => { - return context - .throw_range_error("toExponential() argument must be between 0 and 100") + return Err(JsNativeError::range() + .with_message("toExponential() argument must be between 0 and 100") + .into()) } }; Ok(JsValue::new(this_str_num)) @@ -262,7 +268,7 @@ impl Number { context: &mut Context, ) -> JsResult { // 1. Let this_num be ? thisNumberValue(this value). - let this_num = Self::this_number_value(this, context)?; + let this_num = Self::this_number_value(this)?; // 2. Let f be ? ToIntegerOrInfinity(fractionDigits). // 3. Assert: If fractionDigits is undefined, then f is 0. @@ -273,7 +279,8 @@ impl Number { .as_integer() .filter(|i| (0..=100).contains(i)) .ok_or_else(|| { - context.construct_range_error("toFixed() digits argument must be between 0 and 100") + JsNativeError::range() + .with_message("toFixed() digits argument must be between 0 and 100") })? as usize; // 6. If x is not finite, return ! Number::toString(x). @@ -307,9 +314,9 @@ impl Number { pub(crate) fn to_locale_string( this: &JsValue, _: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { - let this_num = Self::this_number_value(this, context)?; + let this_num = Self::this_number_value(this)?; let this_str_num = this_num.to_string(); Ok(JsValue::new(this_str_num)) } @@ -422,7 +429,7 @@ impl Number { let precision = args.get_or_undefined(0); // 1 & 6 - let mut this_num = Self::this_number_value(this, context)?; + let mut this_num = Self::this_number_value(this)?; // 2 if precision.is_undefined() { return Self::to_string(this, &[], context); @@ -440,9 +447,9 @@ impl Number { IntegerOrInfinity::Integer(x) if (1..=100).contains(&x) => x as usize, _ => { // 5 - return context.throw_range_error( - "precision must be an integer at least 1 and no greater than 100", - ); + return Err(JsNativeError::range() + .with_message("precision must be an integer at least 1 and no greater than 100") + .into()); } }; let precision_i32 = precision as i32; @@ -672,7 +679,7 @@ impl Number { context: &mut Context, ) -> JsResult { // 1. Let x be ? thisNumberValue(this value). - let x = Self::this_number_value(this, context)?; + let x = Self::this_number_value(this)?; let radix = args.get_or_undefined(0); let radix_number = if radix.is_undefined() { @@ -686,9 +693,8 @@ impl Number { // 4. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. .filter(|i| (2..=36).contains(i)) .ok_or_else(|| { - context.construct_range_error( - "radix must be an integer at least 2 and no greater than 36", - ) + JsNativeError::range() + .with_message("radix must be an integer at least 2 and no greater than 36") })? } as u8; @@ -729,12 +735,8 @@ impl Number { /// /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf - pub(crate) fn value_of( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { - Ok(JsValue::new(Self::this_number_value(this, context)?)) + pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + Ok(JsValue::new(Self::this_number_value(this)?)) } /// Builtin javascript 'parseInt(str, radix)' function. diff --git a/boa_engine/src/builtins/number/tests.rs b/boa_engine/src/builtins/number/tests.rs index f7ea2226980..a513364a105 100644 --- a/boa_engine/src/builtins/number/tests.rs +++ b/boa_engine/src/builtins/number/tests.rs @@ -170,7 +170,8 @@ fn to_precision() { String::from("\"0.333333333333333314829616256247390992939472198486328125000000\"") ); - let expected = "Uncaught \"RangeError\": \"precision must be an integer at least 1 and no greater than 100\""; + let expected = + "Uncaught RangeError: precision must be an integer at least 1 and no greater than 100"; let range_error_1 = r#"(1).toPrecision(101);"#; let range_error_2 = r#"(1).toPrecision(0);"#; diff --git a/boa_engine/src/builtins/object/for_in_iterator.rs b/boa_engine/src/builtins/object/for_in_iterator.rs index 612daa0b499..3d26c956fee 100644 --- a/boa_engine/src/builtins/object/for_in_iterator.rs +++ b/boa_engine/src/builtins/object/for_in_iterator.rs @@ -1,5 +1,6 @@ use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object}, + error::JsNativeError, object::{JsObject, ObjectData}, property::PropertyDescriptor, property::PropertyKey, @@ -71,7 +72,7 @@ impl ForInIterator { let iterator = iterator .as_mut() .and_then(|obj| obj.as_for_in_iterator_mut()) - .ok_or_else(|| context.construct_type_error("`this` is not a ForInIterator"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a ForInIterator"))?; let mut object = iterator.object.to_object(context)?; loop { if !iterator.object_was_visited { diff --git a/boa_engine/src/builtins/object/mod.rs b/boa_engine/src/builtins/object/mod.rs index 891360ded7e..2a5212b1958 100644 --- a/boa_engine/src/builtins/object/mod.rs +++ b/boa_engine/src/builtins/object/mod.rs @@ -17,6 +17,7 @@ use super::Array; use crate::{ builtins::{map, BuiltIn, JsArgs}, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, IntegrityLevel, JsObject, ObjectData, ObjectKind, @@ -175,7 +176,7 @@ impl Object { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. If Type(proto) is neither Object nor Null, return undefined. let proto = match args.get_or_undefined(0) { @@ -195,7 +196,9 @@ impl Object { // 5. If status is false, throw a TypeError exception. if !status { - return context.throw_type_error("__proto__ called on null or undefined"); + return Err(JsNativeError::typ() + .with_message("__proto__ called on null or undefined") + .into()); } // 6. Return undefined. @@ -224,8 +227,9 @@ impl Object { // 2. If IsCallable(getter) is false, throw a TypeError exception. if !getter.is_callable() { - return context - .throw_type_error("Object.prototype.__defineGetter__: Expecting function"); + return Err(JsNativeError::typ() + .with_message("Object.prototype.__defineGetter__: Expecting function") + .into()); } // 3. Let desc be PropertyDescriptor { [[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true }. @@ -266,8 +270,9 @@ impl Object { // 2. If IsCallable(setter) is false, throw a TypeError exception. if !setter.is_callable() { - return context - .throw_type_error("Object.prototype.__defineSetter__: Expecting function"); + return Err(JsNativeError::typ() + .with_message("Object.prototype.__defineSetter__: Expecting function") + .into()); } // 3. Let desc be PropertyDescriptor { [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: true }. @@ -395,10 +400,12 @@ impl Object { ObjectData::ordinary(), ), _ => { - return context.throw_type_error(format!( - "Object prototype may only be an Object or null: {}", - prototype.display() - )) + return Err(JsNativeError::typ() + .with_message(format!( + "Object prototype may only be an Object or null: {}", + prototype.display() + )) + .into()) } }; @@ -561,19 +568,25 @@ impl Object { /// [More information][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-object.setprototypeof - pub fn get_prototype_of(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { + pub fn get_prototype_of( + _: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { if args.is_empty() { - return ctx.throw_type_error( - "Object.getPrototypeOf: At least 1 argument required, but only 0 passed", - ); + return Err(JsNativeError::typ() + .with_message( + "Object.getPrototypeOf: At least 1 argument required, but only 0 passed", + ) + .into()); } // 1. Let obj be ? ToObject(O). - let obj = args[0].clone().to_object(ctx)?; + let obj = args[0].clone().to_object(context)?; // 2. Return ? obj.[[GetPrototypeOf]](). Ok(obj - .__get_prototype_of__(ctx)? + .__get_prototype_of__(context)? .map_or(JsValue::Null, JsValue::new)) } @@ -582,12 +595,18 @@ impl Object { /// [More information][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-object.setprototypeof - pub fn set_prototype_of(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { + pub fn set_prototype_of( + _: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { if args.len() < 2 { - return ctx.throw_type_error(format!( - "Object.setPrototypeOf: At least 2 arguments required, but only {} passed", - args.len() - )); + return Err(JsNativeError::typ() + .with_message(format!( + "Object.setPrototypeOf: At least 2 arguments required, but only {} passed", + args.len() + )) + .into()); } // 1. Set O to ? RequireObjectCoercible(O). @@ -595,7 +614,7 @@ impl Object { .get(0) .cloned() .unwrap_or_default() - .require_object_coercible(ctx)? + .require_object_coercible()? .clone(); let proto = match args.get_or_undefined(1) { @@ -603,8 +622,9 @@ impl Object { JsValue::Null => None, // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. val => { - return ctx - .throw_type_error(format!("expected an object or null, got {}", val.type_of())) + return Err(JsNativeError::typ() + .with_message(format!("expected an object or null, got {}", val.type_of())) + .into()) } }; @@ -616,11 +636,13 @@ impl Object { }; // 4. Let status be ? O.[[SetPrototypeOf]](proto). - let status = obj.__set_prototype_of__(proto, ctx)?; + let status = obj.__set_prototype_of__(proto, context)?; // 5. If status is false, throw a TypeError exception. if !status { - return ctx.throw_type_error("can't set prototype of this object"); + return Err(JsNativeError::typ() + .with_message("can't set prototype of this object") + .into()); } // 6. Return O. @@ -680,7 +702,9 @@ impl Object { Ok(object.clone().into()) } else { - context.throw_type_error("Object.defineProperty called on non-object") + Err(JsNativeError::typ() + .with_message("Object.defineProperty called on non-object") + .into()) } } @@ -705,7 +729,9 @@ impl Object { object_define_properties(obj, props, context)?; Ok(arg.clone()) } else { - context.throw_type_error("Expected an object") + Err(JsNativeError::typ() + .with_message("Expected an object") + .into()) } } @@ -764,7 +790,7 @@ impl Object { ObjectKind::Array => "Array", ObjectKind::Arguments(_) => "Arguments", ObjectKind::Function(_) => "Function", - ObjectKind::Error => "Error", + ObjectKind::Error(_) => "Error", ObjectKind::Boolean(_) => "Boolean", ObjectKind::Number(_) => "Number", ObjectKind::String(_) => "String", @@ -1012,7 +1038,9 @@ impl Object { let status = o.set_integrity_level(IntegrityLevel::Sealed, context)?; // 3. If status is false, throw a TypeError exception. if !status { - return context.throw_type_error("cannot seal object"); + return Err(JsNativeError::typ() + .with_message("cannot seal object") + .into()); } } // 1. If Type(O) is not Object, return O. @@ -1057,7 +1085,9 @@ impl Object { let status = o.set_integrity_level(IntegrityLevel::Frozen, context)?; // 3. If status is false, throw a TypeError exception. if !status { - return context.throw_type_error("cannot freeze object"); + return Err(JsNativeError::typ() + .with_message("cannot freeze object") + .into()); } } // 1. If Type(O) is not Object, return O. @@ -1106,7 +1136,9 @@ impl Object { let status = o.__prevent_extensions__(context)?; // 3. If status is false, throw a TypeError exception. if !status { - return context.throw_type_error("cannot prevent extensions"); + return Err(JsNativeError::typ() + .with_message("cannot prevent extensions") + .into()); } } // 1. If Type(O) is not Object, return O. @@ -1202,7 +1234,7 @@ impl Object { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries pub fn from_entries(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Perform ? RequireObjectCoercible(iterable). - let iterable = args.get_or_undefined(0).require_object_coercible(context)?; + let iterable = args.get_or_undefined(0).require_object_coercible()?; // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%). // 3. Assert: obj is an extensible ordinary object with no own properties. diff --git a/boa_engine/src/builtins/object/tests.rs b/boa_engine/src/builtins/object/tests.rs index 8c41b307013..cc7bd37fe38 100644 --- a/boa_engine/src/builtins/object/tests.rs +++ b/boa_engine/src/builtins/object/tests.rs @@ -323,7 +323,7 @@ fn object_is_prototype_of() { #[test] fn object_get_own_property_names_invalid_args() { - let error_message = r#"Uncaught "TypeError": "cannot convert 'null' or 'undefined' to object""#; + let error_message = "Uncaught TypeError: cannot convert 'null' or 'undefined' to object"; check_output(&[ TestAction::TestEq("Object.getOwnPropertyNames()", error_message), @@ -358,7 +358,7 @@ fn object_get_own_property_names() { #[test] fn object_get_own_property_symbols_invalid_args() { - let error_message = r#"Uncaught "TypeError": "cannot convert 'null' or 'undefined' to object""#; + let error_message = "Uncaught TypeError: cannot convert 'null' or 'undefined' to object"; check_output(&[ TestAction::TestEq("Object.getOwnPropertySymbols()", error_message), @@ -391,7 +391,7 @@ fn object_get_own_property_symbols() { #[test] fn object_from_entries_invalid_args() { - let error_message = r#"Uncaught "TypeError": "cannot convert null or undefined to Object""#; + let error_message = "Uncaught TypeError: cannot convert null or undefined to Object"; check_output(&[ TestAction::TestEq("Object.fromEntries()", error_message), diff --git a/boa_engine/src/builtins/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index 42189781a6b..02c5470eb66 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -8,8 +8,9 @@ mod promise_job; use self::promise_job::PromiseJob; use super::{iterable::IteratorRecord, JsArgs}; use crate::{ - builtins::{Array, BuiltIn}, + builtins::{error::ErrorKind, Array, BuiltIn}, context::intrinsics::StandardConstructors, + error::JsNativeError, job::JobCallback, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, @@ -37,12 +38,13 @@ macro_rules! if_abrupt_reject_promise { ($value:ident, $capability:expr, $context: expr) => { let $value = match $value { // 1. If value is an abrupt completion, then - Err(value) => { + Err(err) => { + let err = err.to_value($context); // a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). $context.call( &$capability.reject().clone().into(), &JsValue::undefined(), - &[value], + &[err], )?; // b. Return capability.[[Promise]]. @@ -110,7 +112,9 @@ impl PromiseCapability { match c.as_constructor() { // 1. If IsConstructor(C) is false, throw a TypeError exception. - None => context.throw_type_error("PromiseCapability: expected constructor"), + None => Err(JsNativeError::typ() + .with_message("PromiseCapability: expected constructor") + .into()), Some(c) => { let c = c.clone(); @@ -125,19 +129,20 @@ impl PromiseCapability { // 5. Let executor be CreateBuiltinFunction(executorClosure, 2, "", « »). let executor = FunctionBuilder::closure_with_captures( context, - |_this, args: &[JsValue], captures, context| { + |_this, args: &[JsValue], captures, _| { let mut promise_capability = captures.borrow_mut(); // a. If promiseCapability.[[Resolve]] is not undefined, throw a TypeError exception. if !promise_capability.resolve.is_undefined() { - return context.throw_type_error( - "promiseCapability.[[Resolve]] is not undefined", - ); + return Err(JsNativeError::typ() + .with_message("promiseCapability.[[Resolve]] is not undefined") + .into()); } // b. If promiseCapability.[[Reject]] is not undefined, throw a TypeError exception. if !promise_capability.reject.is_undefined() { - return context - .throw_type_error("promiseCapability.[[Reject]] is not undefined"); + return Err(JsNativeError::typ() + .with_message("promiseCapability.[[Reject]] is not undefined") + .into()); } let resolve = args.get_or_undefined(0); @@ -173,8 +178,8 @@ impl PromiseCapability { .cloned() .and_then(JsFunction::from_object) .ok_or_else(|| { - context - .construct_type_error("promiseCapability.[[Resolve]] is not callable") + JsNativeError::typ() + .with_message("promiseCapability.[[Resolve]] is not callable") })?; // 8. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception. @@ -183,7 +188,8 @@ impl PromiseCapability { .cloned() .and_then(JsFunction::from_object) .ok_or_else(|| { - context.construct_type_error("promiseCapability.[[Reject]] is not callable") + JsNativeError::typ() + .with_message("promiseCapability.[[Reject]] is not callable") })?; // 9. Set promiseCapability.[[Promise]] to promise. @@ -284,14 +290,18 @@ impl Promise { ) -> JsResult { // 1. If NewTarget is undefined, throw a TypeError exception. if new_target.is_undefined() { - return context.throw_type_error("Promise NewTarget cannot be undefined"); + return Err(JsNativeError::typ() + .with_message("Promise NewTarget cannot be undefined") + .into()); } let executor = args.get_or_undefined(0); // 2. If IsCallable(executor) is false, throw a TypeError exception. if !executor.is_callable() { - return context.throw_type_error("Promise executor is not callable"); + return Err(JsNativeError::typ() + .with_message("Promise executor is not callable") + .into()); } // 3. Let promise be ? OrdinaryCreateFromConstructor(NewTarget, "%Promise.prototype%", « [[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]] »). @@ -327,9 +337,10 @@ impl Promise { ); // 10. If completion is an abrupt completion, then - if let Err(value) = completion { + if let Err(e) = completion { + let e = e.to_value(context); // a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »). - context.call(&resolving_functions.reject, &JsValue::Undefined, &[value])?; + context.call(&resolving_functions.reject, &JsValue::Undefined, &[e])?; } // 11. Return promise. @@ -1033,7 +1044,7 @@ impl Promise { .constructors() .aggregate_error() .prototype(), - ObjectData::error(), + ObjectData::error(ErrorKind::Aggregate), ); // 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }). @@ -1128,7 +1139,7 @@ impl Promise { .constructors() .aggregate_error() .prototype(), - ObjectData::error(), + ObjectData::error(ErrorKind::Aggregate), ); // b. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }). @@ -1146,7 +1157,6 @@ impl Promise { context, ) .expect("cannot fail per spec"); - // c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »). return captures.capability_reject.call( &JsValue::undefined(), @@ -1243,8 +1253,9 @@ impl Promise { // 7. If SameValue(resolution, promise) is true, then if JsValue::same_value(resolution, &promise.clone().into()) { // a. Let selfResolutionError be a newly created TypeError object. - let self_resolution_error = - context.construct_type_error("SameValue(resolution, promise) is true"); + let self_resolution_error = JsNativeError::typ() + .with_message("SameValue(resolution, promise) is true") + .to_value(context); // b. Perform RejectPromise(promise, selfResolutionError). promise @@ -1275,13 +1286,13 @@ impl Promise { let then_action = match then { // 10. If then is an abrupt completion, then - Err(value) => { + Err(e) => { // a. Perform RejectPromise(promise, then.[[Value]]). promise .borrow_mut() .as_promise_mut() .expect("Expected promise to be a Promise") - .reject_promise(&value, context); + .reject_promise(&e.to_value(context), context); // b. Return undefined. return Ok(JsValue::Undefined); @@ -1674,7 +1685,9 @@ impl Promise { Self::promise_resolve(c.clone(), x.clone(), context) } else { // 2. If Type(C) is not Object, throw a TypeError exception. - context.throw_type_error("Promise.resolve() called on a non-object") + Err(JsNativeError::typ() + .with_message("Promise.resolve() called on a non-object") + .into()) } } @@ -1731,7 +1744,9 @@ impl Promise { let promise_obj = if let Some(p) = promise.as_object() { p } else { - return context.throw_type_error("finally called with a non-object promise"); + return Err(JsNativeError::typ() + .with_message("finally called with a non-object promise") + .into()); }; // 3. Let C be ? SpeciesConstructor(promise, %Promise%). @@ -1819,7 +1834,7 @@ impl Promise { context, |_this, _args, captures, _context| { // 1. Return ThrowCompletion(reason). - Err(captures.reason.clone()) + Err(captures.reason.clone().into()) }, ThrowReasonCaptures { reason: reason.clone(), @@ -1868,7 +1883,11 @@ impl Promise { // 2. If IsPromise(promise) is false, throw a TypeError exception. let promise_obj = match promise.as_promise() { Some(obj) => obj, - None => return context.throw_type_error("IsPromise(promise) is false"), + None => { + return Err(JsNativeError::typ() + .with_message("IsPromise(promise) is false") + .into()) + } }; // 3. Let C be ? SpeciesConstructor(promise, %Promise%). @@ -2068,7 +2087,9 @@ impl Promise { // 3. Return promiseResolve. Ok(promise_resolve.clone()) } else { - context.throw_type_error("retrieving a non-callable promise resolver") + Err(JsNativeError::typ() + .with_message("retrieving a non-callable promise resolver") + .into()) } } } diff --git a/boa_engine/src/builtins/promise/promise_job.rs b/boa_engine/src/builtins/promise/promise_job.rs index 30b486e93e4..8361e947aa3 100644 --- a/boa_engine/src/builtins/promise/promise_job.rs +++ b/boa_engine/src/builtins/promise/promise_job.rs @@ -54,9 +54,9 @@ impl PromiseJob { } }, // e. Else, let handlerResult be Completion(HostCallJobCallback(handler, undefined, « argument »)). - Some(handler) => { - handler.call_job_callback(&JsValue::Undefined, &[argument.clone()], context) - } + Some(handler) => handler + .call_job_callback(&JsValue::Undefined, &[argument.clone()], context) + .map_err(|e| e.to_value(context)), }; match promise_capability { @@ -146,6 +146,7 @@ impl PromiseJob { // c. If thenCallResult is an abrupt completion, then if let Err(value) = then_call_result { + let value = value.to_value(context); // i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »). return context.call( &resolving_functions.reject, diff --git a/boa_engine/src/builtins/proxy/mod.rs b/boa_engine/src/builtins/proxy/mod.rs index 91f58fa2721..a9d498ec6ee 100644 --- a/boa_engine/src/builtins/proxy/mod.rs +++ b/boa_engine/src/builtins/proxy/mod.rs @@ -12,6 +12,7 @@ use crate::{ builtins::{BuiltIn, JsArgs}, + error::JsNativeError, object::{ConstructorBuilder, FunctionBuilder, JsFunction, JsObject, ObjectData}, Context, JsResult, JsValue, }; @@ -58,9 +59,11 @@ impl Proxy { /// This is an internal method only built for usage in the proxy internal methods. /// /// It returns the (target, handler) of the proxy. - pub(crate) fn try_data(&self, context: &mut Context) -> JsResult<(JsObject, JsObject)> { + pub(crate) fn try_data(&self) -> JsResult<(JsObject, JsObject)> { self.data.clone().ok_or_else(|| { - context.construct_type_error("Proxy object has empty handler and target") + JsNativeError::typ() + .with_message("Proxy object has empty handler and target") + .into() }) } @@ -77,7 +80,9 @@ impl Proxy { ) -> JsResult { // 1. If NewTarget is undefined, throw a TypeError exception. if new_target.is_undefined() { - return context.throw_type_error("Proxy constructor called on undefined new target"); + return Err(JsNativeError::typ() + .with_message("Proxy constructor called on undefined new target") + .into()); } // 2. Return ? ProxyCreate(target, handler). @@ -97,12 +102,12 @@ impl Proxy { ) -> JsResult { // 1. If Type(target) is not Object, throw a TypeError exception. let target = target.as_object().ok_or_else(|| { - context.construct_type_error("Proxy constructor called with non-object target") + JsNativeError::typ().with_message("Proxy constructor called with non-object target") })?; // 2. If Type(handler) is not Object, throw a TypeError exception. let handler = handler.as_object().ok_or_else(|| { - context.construct_type_error("Proxy constructor called with non-object handler") + JsNativeError::typ().with_message("Proxy constructor called with non-object handler") })?; // 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »). diff --git a/boa_engine/src/builtins/reflect/mod.rs b/boa_engine/src/builtins/reflect/mod.rs index 52da25618f9..afa7ab2c8f9 100644 --- a/boa_engine/src/builtins/reflect/mod.rs +++ b/boa_engine/src/builtins/reflect/mod.rs @@ -13,6 +13,7 @@ use super::{Array, JsArgs}; use crate::{ builtins::{self, BuiltIn}, + error::JsNativeError, object::ObjectInitializer, property::Attribute, symbol::WellKnownSymbols, @@ -78,12 +79,14 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be a function"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be a function"))?; let this_arg = args.get_or_undefined(1); let args_list = args.get_or_undefined(2); if !target.is_callable() { - return context.throw_type_error("target must be a function"); + return Err(JsNativeError::typ() + .with_message("target must be a function") + .into()); } let args = args_list.create_list_from_array_like(&[], context)?; target.call(this_arg, &args, context) @@ -106,14 +109,16 @@ impl Reflect { let target = args .get_or_undefined(0) .as_constructor() - .ok_or_else(|| context.construct_type_error("target must be a constructor"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be a constructor"))?; let new_target = if let Some(new_target) = args.get(2) { // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception. if let Some(new_target) = new_target.as_constructor() { new_target } else { - return context.throw_type_error("newTarget must be a constructor"); + return Err(JsNativeError::typ() + .with_message("newTarget must be a constructor") + .into()); } } else { // 2. If newTarget is not present, set newTarget to target. @@ -147,12 +152,14 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; let key = args.get_or_undefined(1).to_property_key(context)?; let prop_desc: JsValue = args .get(2) .and_then(|v| v.as_object().cloned()) - .ok_or_else(|| context.construct_type_error("property descriptor must be an object"))? + .ok_or_else(|| { + JsNativeError::typ().with_message("property descriptor must be an object") + })? .into(); target @@ -176,7 +183,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; let key = args.get_or_undefined(1).to_property_key(context)?; Ok(target.__delete__(&key, context)?.into()) @@ -195,7 +202,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; // 2. Let key be ? ToPropertyKey(propertyKey). let key = args.get_or_undefined(1).to_property_key(context)?; // 3. If receiver is not present, then @@ -231,7 +238,9 @@ impl Reflect { context, ) } else { - context.throw_type_error("target must be an object") + Err(JsNativeError::typ() + .with_message("target must be an object") + .into()) } } @@ -251,7 +260,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; Ok(target .__get_prototype_of__(context)? .map_or(JsValue::Null, JsValue::new)) @@ -269,7 +278,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; let key = args .get(1) .unwrap_or(&JsValue::undefined()) @@ -293,7 +302,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; Ok(target.__is_extensible__(context)?.into()) } @@ -313,7 +322,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; let keys: Vec = target .__own_property_keys__(context)? @@ -340,7 +349,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; Ok(target.__prevent_extensions__(context)?.into()) } @@ -357,7 +366,7 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; let key = args.get_or_undefined(1).to_property_key(context)?; let value = args.get_or_undefined(2); let receiver = if let Some(receiver) = args.get(3).cloned() { @@ -386,11 +395,15 @@ impl Reflect { let target = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| context.construct_type_error("target must be an object"))?; + .ok_or_else(|| JsNativeError::typ().with_message("target must be an object"))?; let proto = match args.get_or_undefined(1) { JsValue::Object(obj) => Some(obj.clone()), JsValue::Null => None, - _ => return context.throw_type_error("proto must be an object or null"), + _ => { + return Err(JsNativeError::typ() + .with_message("proto must be an object or null") + .into()) + } }; Ok(target.__set_prototype_of__(proto, context)?.into()) } diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index 8ca3f71ea95..f973c90ad4c 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -16,6 +16,7 @@ use super::JsArgs; use crate::{ builtins::{array::Array, string, BuiltIn}, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsObject, ObjectData, @@ -282,7 +283,7 @@ impl RegExp { // 5. If F contains any code unit other than "g", "i", "m", "s", "u", or "y" // or if it contains the same code unit more than once, throw a SyntaxError exception. let flags = match RegExpFlags::from_str(&f) { - Err(msg) => return context.throw_syntax_error(msg), + Err(msg) => return Err(JsNativeError::syntax().with_message(msg).into()), Ok(result) => result, }; @@ -297,8 +298,9 @@ impl RegExp { // 15. Set obj.[[RegExpMatcher]] to CompilePattern of parseResult. let matcher = match Regex::with_flags(&p, f.as_ref()) { Err(error) => { - return context - .throw_syntax_error(format!("failed to create matcher: {}", error.text)); + return Err(JsNativeError::syntax() + .with_message(format!("failed to create matcher: {}", error.text)) + .into()); } Ok(val) => val, }; @@ -385,9 +387,11 @@ impl RegExp { _ => unreachable!(), }; - context.throw_type_error(format!( - "RegExp.prototype.{name} getter called on non-RegExp object", - )) + Err(JsNativeError::typ() + .with_message(format!( + "RegExp.prototype.{name} getter called on non-RegExp object", + )) + .into()) } /// `get RegExp.prototype.hasIndices` @@ -582,7 +586,9 @@ impl RegExp { return Ok(result.into()); } - context.throw_type_error("RegExp.prototype.flags getter called on non-object") + Err(JsNativeError::typ() + .with_message("RegExp.prototype.flags getter called on non-object") + .into()) } /// `get RegExp.prototype.source` @@ -617,9 +623,11 @@ impl RegExp { ) { Ok(JsValue::new("(?:)")) } else { - context.throw_type_error( - "RegExp.prototype.source method called on incompatible value", - ) + Err(JsNativeError::typ() + .with_message( + "RegExp.prototype.source method called on incompatible value", + ) + .into()) } } // 4. Assert: R has an [[OriginalFlags]] internal slot. @@ -634,7 +642,9 @@ impl RegExp { } } } else { - context.throw_type_error("RegExp.prototype.source method called on incompatible value") + Err(JsNativeError::typ() + .with_message("RegExp.prototype.source method called on incompatible value") + .into()) } } @@ -683,8 +693,8 @@ impl RegExp { // 1. Let R be the this value. // 2. If Type(R) is not Object, throw a TypeError exception. let this = this.as_object().ok_or_else(|| { - context - .construct_type_error("RegExp.prototype.test method called on incompatible value") + JsNativeError::typ() + .with_message("RegExp.prototype.test method called on incompatible value") })?; // 3. Let string be ? ToString(S). @@ -728,7 +738,7 @@ impl RegExp { .as_object() .filter(|obj| obj.is_regexp()) .ok_or_else(|| { - context.construct_type_error("RegExp.prototype.exec called with invalid value") + JsNativeError::typ().with_message("RegExp.prototype.exec called with invalid value") })?; // 3. Let S be ? ToString(string). @@ -766,7 +776,9 @@ impl RegExp { // b. If Type(result) is neither Object nor Null, throw a TypeError exception. if !result.is_object() && !result.is_null() { - return context.throw_type_error("regexp exec returned neither object nor null"); + return Err(JsNativeError::typ() + .with_message("regexp exec returned neither object nor null") + .into()); } // c. Return result. @@ -775,7 +787,9 @@ impl RegExp { // 5. Perform ? RequireInternalSlot(R, [[RegExpMatcher]]). if !this.is_regexp() { - return context.throw_type_error("RegExpExec called with invalid value"); + return Err(JsNativeError::typ() + .with_message("RegExpExec called with invalid value") + .into()); } // 6. Return ? RegExpBuiltinExec(R, S). @@ -799,7 +813,9 @@ impl RegExp { if let Some(rx) = obj.as_regexp() { rx.clone() } else { - return context.throw_type_error("RegExpBuiltinExec called with invalid value"); + return Err(JsNativeError::typ() + .with_message("RegExpBuiltinExec called with invalid value") + .into()); } }; @@ -856,8 +872,9 @@ impl RegExp { ) { Ok(s) => s.len(), Err(_) => { - return context - .throw_type_error("Failed to get byte index from utf16 encoded string") + return Err(JsNativeError::typ() + .with_message("Failed to get byte index from utf16 encoded string") + .into()) } }; let r = matcher.find_from(input, last_byte_index).next(); @@ -1032,8 +1049,9 @@ impl RegExp { let rx = if let Some(rx) = this.as_object() { rx } else { - return context - .throw_type_error("RegExp.prototype.match method called on incompatible value"); + return Err(JsNativeError::typ() + .with_message("RegExp.prototype.match method called on incompatible value") + .into()); }; // 3. Let S be ? ToString(string). @@ -1124,25 +1142,23 @@ impl RegExp { /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { let (body, flags) = if let Some(object) = this.as_object() { let object = object.borrow(); let regex = object.as_regexp().ok_or_else(|| { - context.construct_type_error(format!( + JsNativeError::typ().with_message(format!( "Method RegExp.prototype.toString called on incompatible receiver {}", this.display() )) })?; (regex.original_source.clone(), regex.original_flags.clone()) } else { - return context.throw_type_error(format!( - "Method RegExp.prototype.toString called on incompatible receiver {}", - this.display() - )); + return Err(JsNativeError::typ() + .with_message(format!( + "Method RegExp.prototype.toString called on incompatible receiver {}", + this.display() + )) + .into()); }; Ok(format!("/{body}/{flags}").into()) } @@ -1165,9 +1181,8 @@ impl RegExp { // 1. Let R be the this value. // 2. If Type(R) is not Object, throw a TypeError exception. let regexp = this.as_object().ok_or_else(|| { - context.construct_type_error( - "RegExp.prototype.match_all method called on incompatible value", - ) + JsNativeError::typ() + .with_message("RegExp.prototype.match_all method called on incompatible value") })?; // 3. Let S be ? ToString(string). @@ -1228,9 +1243,11 @@ impl RegExp { let rx = if let Some(rx) = this.as_object() { rx } else { - return context.throw_type_error( - "RegExp.prototype[Symbol.replace] method called on incompatible value", - ); + return Err(JsNativeError::typ() + .with_message( + "RegExp.prototype[Symbol.replace] method called on incompatible value", + ) + .into()); }; // 3. Let S be ? ToString(string). @@ -1476,9 +1493,9 @@ impl RegExp { let rx = if let Some(rx) = this.as_object() { rx } else { - return context.throw_type_error( - "RegExp.prototype[Symbol.search] method called on incompatible value", - ); + return Err(JsNativeError::typ() + .with_message("RegExp.prototype[Symbol.search] method called on incompatible value") + .into()); }; // 3. Let S be ? ToString(string). @@ -1538,8 +1555,9 @@ impl RegExp { let rx = if let Some(rx) = this.as_object() { rx } else { - return context - .throw_type_error("RegExp.prototype.split method called on incompatible value"); + return Err(JsNativeError::typ() + .with_message("RegExp.prototype.split method called on incompatible value") + .into()); }; // 3. Let S be ? ToString(string). diff --git a/boa_engine/src/builtins/regexp/regexp_string_iterator.rs b/boa_engine/src/builtins/regexp/regexp_string_iterator.rs index 620d26640b8..7c4724b5c35 100644 --- a/boa_engine/src/builtins/regexp/regexp_string_iterator.rs +++ b/boa_engine/src/builtins/regexp/regexp_string_iterator.rs @@ -12,6 +12,7 @@ use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, regexp}, + error::JsNativeError, object::{JsObject, ObjectData}, property::PropertyDescriptor, symbol::WellKnownSymbols, @@ -85,7 +86,9 @@ impl RegExpStringIterator { let iterator = iterator .as_mut() .and_then(|obj| obj.as_regexp_string_iterator_mut()) - .ok_or_else(|| context.construct_type_error("`this` is not a RegExpStringIterator"))?; + .ok_or_else(|| { + JsNativeError::typ().with_message("`this` is not a RegExpStringIterator") + })?; if iterator.completed { return Ok(create_iter_result_object( JsValue::undefined(), diff --git a/boa_engine/src/builtins/regexp/tests.rs b/boa_engine/src/builtins/regexp/tests.rs index 66c156bfc1b..3dd09c6b307 100644 --- a/boa_engine/src/builtins/regexp/tests.rs +++ b/boa_engine/src/builtins/regexp/tests.rs @@ -214,7 +214,8 @@ fn search() { assert_eq!(forward(&mut context, "/c/[Symbol.search]('abc')"), "2"); // this-val-non-obj - let error = "Uncaught \"TypeError\": \"RegExp.prototype[Symbol.search] method called on incompatible value\""; + let error = + "Uncaught TypeError: RegExp.prototype[Symbol.search] method called on incompatible value"; let init = "var search = RegExp.prototype[Symbol.search]"; eprintln!("{}", forward(&mut context, init)); assert_eq!(forward(&mut context, "search.call()"), error); diff --git a/boa_engine/src/builtins/set/mod.rs b/boa_engine/src/builtins/set/mod.rs index 282f2f7d3d0..7362701d743 100644 --- a/boa_engine/src/builtins/set/mod.rs +++ b/boa_engine/src/builtins/set/mod.rs @@ -15,6 +15,7 @@ use super::JsArgs; use crate::{ builtins::BuiltIn, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsObject, ObjectData, @@ -117,8 +118,9 @@ impl Set { ) -> JsResult { // 1. If NewTarget is undefined, throw a TypeError exception. if new_target.is_undefined() { - return context - .throw_type_error("calling a builtin Set constructor without new is forbidden"); + return Err(JsNativeError::typ() + .with_message("calling a builtin Set constructor without new is forbidden") + .into()); } // 2. Let set be ? OrdinaryCreateFromConstructor(NewTarget, "%Set.prototype%", « [[SetData]] »). @@ -138,7 +140,7 @@ impl Set { // 6. If IsCallable(adder) is false, throw a TypeError exception. let adder = adder.as_callable().ok_or_else(|| { - context.construct_type_error("'add' of 'newTarget' is not a function") + JsNativeError::typ().with_message("'add' of 'newTarget' is not a function") })?; // 7. Let iteratorRecord be ? GetIterator(iterable). @@ -214,11 +216,7 @@ impl Set { /// /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.add /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add - pub(crate) fn add( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn add(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let value = args.get_or_undefined(0); if let Some(object) = this.as_object() { @@ -229,10 +227,14 @@ impl Set { value.clone() }); } else { - return context.throw_type_error("'this' is not a Set"); + return Err(JsNativeError::typ() + .with_message("'this' is not a Set") + .into()); } } else { - return context.throw_type_error("'this' is not a Set"); + return Err(JsNativeError::typ() + .with_message("'this' is not a Set") + .into()); }; Ok(this.clone()) @@ -248,16 +250,20 @@ impl Set { /// /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear - pub(crate) fn clear(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { if let Some(object) = this.as_object() { if object.borrow().is_set() { this.set_data(ObjectData::set(OrderedSet::new())); Ok(JsValue::undefined()) } else { - context.throw_type_error("'this' is not a Set") + Err(JsNativeError::typ() + .with_message("'this' is not a Set") + .into()) } } else { - context.throw_type_error("'this' is not a Set") + Err(JsNativeError::typ() + .with_message("'this' is not a Set") + .into()) } } @@ -272,21 +278,21 @@ impl Set { /// /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.delete /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete - pub(crate) fn delete( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn delete(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let value = args.get_or_undefined(0); let res = if let Some(object) = this.as_object() { if let Some(set) = object.borrow_mut().as_set_mut() { set.delete(value) } else { - return context.throw_type_error("'this' is not a Set"); + return Err(JsNativeError::typ() + .with_message("'this' is not a Set") + .into()); } } else { - return context.throw_type_error("'this' is not a Set"); + return Err(JsNativeError::typ() + .with_message("'this' is not a Set") + .into()); }; Ok(res.into()) @@ -310,13 +316,14 @@ impl Set { if let Some(object) = this.as_object() { let object = object.borrow(); if !object.is_set() { - return context.throw_type_error( - "Method Set.prototype.entries called on incompatible receiver", - ); + return Err(JsNativeError::typ() + .with_message("Method Set.prototype.entries called on incompatible receiver") + .into()); } } else { - return context - .throw_type_error("Method Set.prototype.entries called on incompatible receiver"); + return Err(JsNativeError::typ() + .with_message("Method Set.prototype.entries called on incompatible receiver") + .into()); } Ok(SetIterator::create_set_iterator( @@ -342,7 +349,9 @@ impl Set { context: &mut Context, ) -> JsResult { if args.is_empty() { - return Err(JsValue::new("Missing argument for Set.prototype.forEach")); + return Err(JsNativeError::typ() + .with_message("Missing argument for Set.prototype.forEach") + .into()); } let callback_arg = &args[0]; @@ -357,7 +366,7 @@ impl Set { let mut index = 0; - while index < Self::get_size(this, context)? { + while index < Self::get_size(this)? { let arguments = this .as_object() .and_then(|obj| { @@ -366,7 +375,7 @@ impl Set { .map(|value| [value.clone(), value.clone(), this.clone()]) }) }) - .ok_or_else(|| context.construct_type_error("'this' is not a Set"))?; + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?; if let Some(arguments) = arguments { context.call(callback_arg, &this_arg, &arguments)?; @@ -388,11 +397,7 @@ impl Set { /// /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has - pub(crate) fn has( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn has(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let value = args.get_or_undefined(0); this.as_object() @@ -401,7 +406,11 @@ impl Set { .as_set_ref() .map(|set| set.contains(value).into()) }) - .ok_or_else(|| context.construct_type_error("'this' is not a Set")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a Set") + .into() + }) } /// `Set.prototype.values( )` @@ -422,13 +431,14 @@ impl Set { if let Some(object) = this.as_object() { let object = object.borrow(); if !object.is_set() { - return context.throw_type_error( - "Method Set.prototype.values called on incompatible receiver", - ); + return Err(JsNativeError::typ() + .with_message("Method Set.prototype.values called on incompatible receiver") + .into()); } } else { - return context - .throw_type_error("Method Set.prototype.values called on incompatible receiver"); + return Err(JsNativeError::typ() + .with_message("Method Set.prototype.values called on incompatible receiver") + .into()); } Ok(SetIterator::create_set_iterator( @@ -438,14 +448,18 @@ impl Set { )) } - fn size_getter(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - Self::get_size(this, context).map(JsValue::from) + fn size_getter(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + Self::get_size(this).map(JsValue::from) } /// Helper function to get the size of the `Set` object. - pub(crate) fn get_size(set: &JsValue, context: &mut Context) -> JsResult { + pub(crate) fn get_size(set: &JsValue) -> JsResult { set.as_object() .and_then(|obj| obj.borrow().as_set_ref().map(OrderedSet::size)) - .ok_or_else(|| context.construct_type_error("'this' is not a Set")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a Set") + .into() + }) } } diff --git a/boa_engine/src/builtins/set/set_iterator.rs b/boa_engine/src/builtins/set/set_iterator.rs index 4d3e86fc1b7..653458b81c1 100644 --- a/boa_engine/src/builtins/set/set_iterator.rs +++ b/boa_engine/src/builtins/set/set_iterator.rs @@ -1,5 +1,6 @@ use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue}, + error::JsNativeError, object::{JsObject, ObjectData}, property::{PropertyDescriptor, PropertyNameKind}, symbol::WellKnownSymbols, @@ -72,7 +73,7 @@ impl SetIterator { let set_iterator = set_iterator .as_mut() .and_then(|obj| obj.as_set_iterator_mut()) - .ok_or_else(|| context.construct_type_error("`this` is not an SetIterator"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not an SetIterator"))?; { let m = &set_iterator.iterated_set; let mut index = set_iterator.next_index; @@ -90,7 +91,7 @@ impl SetIterator { let entries = entries .as_ref() .and_then(|obj| obj.as_set_ref()) - .ok_or_else(|| context.construct_type_error("'this' is not a Set"))?; + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?; let num_entries = entries.size(); while index < num_entries { diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index f77315dd40f..2b3ea69dd7f 100644 --- a/boa_engine/src/builtins/string/mod.rs +++ b/boa_engine/src/builtins/string/mod.rs @@ -17,6 +17,7 @@ use super::JsArgs; use crate::{ builtins::{string::string_iterator::StringIterator, Array, BuiltIn, Number, RegExp}, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, }, @@ -241,7 +242,7 @@ impl String { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#thisstringvalue - fn this_string_value(this: &JsValue, context: &mut Context) -> JsResult { + fn this_string_value(this: &JsValue) -> JsResult { // 1. If Type(value) is String, return value. this.as_string() .cloned() @@ -251,7 +252,11 @@ impl String { // c. Return s. .or_else(|| this.as_object().and_then(|obj| obj.borrow().as_string())) // 3. Throw a TypeError exception. - .ok_or_else(|| context.construct_type_error("'this' is not a string")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a string") + .into() + }) } /// `String.fromCodePoint(num1[, ...[, numN]])` @@ -279,12 +284,16 @@ impl String { // b. If ! IsIntegralNumber(nextCP) is false, throw a RangeError exception. if !Number::is_float_integer(nextcp) { - return Err(context.construct_range_error(format!("invalid code point: {nextcp}"))); + return Err(JsNativeError::range() + .with_message(format!("invalid code point: {nextcp}")) + .into()); } // c. If ℝ(nextCP) < 0 or ℝ(nextCP) > 0x10FFFF, throw a RangeError exception. if nextcp < 0.0 || nextcp > f64::from(0x10FFFF) { - return Err(context.construct_range_error(format!("invalid code point: {nextcp}"))); + return Err(JsNativeError::range() + .with_message(format!("invalid code point: {nextcp}")) + .into()); } // TODO: Full UTF-16 support @@ -406,13 +415,9 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tostring #[allow(clippy::wrong_self_convention)] #[inline] - pub(crate) fn to_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Return ? thisStringValue(this value). - Ok(Self::this_string_value(this, context)?.into()) + Ok(Self::this_string_value(this)?.into()) } /// `String.prototype.charAt( index )` @@ -437,7 +442,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -478,7 +483,7 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/at pub(crate) fn at(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let s = this.to_string(context)?; @@ -529,7 +534,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -571,7 +576,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -618,7 +623,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let mut string = this.to_string(context)?.to_string(); @@ -652,7 +657,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -680,10 +685,12 @@ impl String { // 5. If n is 0, return the empty String. IntegerOrInfinity::Integer(n) if n == 0 => Ok("".into()), // 4. If n < 0 or n is +∞, throw a RangeError exception. - _ => context.throw_range_error( - "repeat count must be a positive finite number \ + _ => Err(JsNativeError::range() + .with_message( + "repeat count must be a positive finite number \ that doesn't overflow the maximum string length (2^32 - 1)", - ), + ) + .into()), } } @@ -703,7 +710,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -771,7 +778,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -781,9 +788,9 @@ impl String { // 3. Let isRegExp be ? IsRegExp(searchString). // 4. If isRegExp is true, throw a TypeError exception. if is_reg_exp(search_string, context)? { - context.throw_type_error( + return Err(JsNativeError::typ().with_message( "First argument to String.prototype.startsWith must not be a regular expression", - )?; + ).into()); } // 5. Let searchStr be ? ToString(searchString). @@ -842,7 +849,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -851,9 +858,9 @@ impl String { // 3. Let isRegExp be ? IsRegExp(searchString). // 4. If isRegExp is true, throw a TypeError exception. search_string if is_reg_exp(search_string, context)? => { - return context.throw_type_error( + return Err(JsNativeError::typ().with_message( "First argument to String.prototype.endsWith must not be a regular expression", - ); + ).into()); } // 5. Let searchStr be ? ToString(searchString). search_string => search_string.to_string(context)?, @@ -912,7 +919,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -920,10 +927,10 @@ impl String { let search_str = match args.get_or_undefined(0) { // 3. Let isRegExp be ? IsRegExp(searchString). search_string if is_reg_exp(search_string, context)? => { - return context.throw_type_error( + return Err(JsNativeError::typ().with_message( // 4. If isRegExp is true, throw a TypeError exception. "First argument to String.prototype.includes must not be a regular expression", - ); + ).into()); } // 5. Let searchStr be ? ToString(searchString). search_string => search_string.to_string(context)?, @@ -964,7 +971,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - this.require_object_coercible(context)?; + this.require_object_coercible()?; let search_value = args.get_or_undefined(0); @@ -1080,7 +1087,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = this.require_object_coercible(context)?; + let o = this.require_object_coercible()?; let search_value = args.get_or_undefined(0); let replace_value = args.get_or_undefined(1); @@ -1095,13 +1102,13 @@ impl String { let flags = obj.get("flags", context)?; // ii. Perform ? RequireObjectCoercible(flags). - flags.require_object_coercible(context)?; + flags.require_object_coercible()?; // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. if !flags.to_string(context)?.contains('g') { - return context.throw_type_error( + return Err(JsNativeError::typ().with_message( "String.prototype.replaceAll called with a non-global RegExp argument", - ); + ).into()); } } } @@ -1252,7 +1259,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -1296,7 +1303,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -1362,7 +1369,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = this.require_object_coercible(context)?; + let o = this.require_object_coercible()?; // 2. If regexp is neither undefined nor null, then let regexp = args.get_or_undefined(0); @@ -1479,7 +1486,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; let max_length = args.get_or_undefined(0); let fill_string = args.get_or_undefined(1); @@ -1506,7 +1513,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; let max_length = args.get_or_undefined(0); let fill_string = args.get_or_undefined(1); @@ -1528,7 +1535,7 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim pub(crate) fn trim(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let object = this.require_object_coercible(context)?; + let object = this.require_object_coercible()?; let string = object.to_string(context)?; Ok(JsValue::new(string.trim_matches(is_trimmable_whitespace))) } @@ -1550,7 +1557,7 @@ impl String { _: &[JsValue], context: &mut Context, ) -> JsResult { - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; let string = this.to_string(context)?; Ok(JsValue::new( string.trim_start_matches(is_trimmable_whitespace), @@ -1574,7 +1581,7 @@ impl String { _: &[JsValue], context: &mut Context, ) -> JsResult { - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; let string = this.to_string(context)?; Ok(JsValue::new( string.trim_end_matches(is_trimmable_whitespace), @@ -1598,7 +1605,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -1635,7 +1642,7 @@ impl String { // Comments below are an adaptation of the `String.prototype.toLowerCase` documentation. // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -1664,7 +1671,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -1720,7 +1727,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let string = this.to_string(context)?; @@ -1786,7 +1793,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; let separator = args.get_or_undefined(0); let limit = args.get_or_undefined(1); @@ -1931,13 +1938,9 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf - pub(crate) fn value_of( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Return ? thisStringValue(this value). - Self::this_string_value(this, context).map(JsValue::from) + Self::this_string_value(this).map(JsValue::from) } /// `String.prototype.matchAll( regexp )` @@ -1958,7 +1961,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = this.require_object_coercible(context)?; + let o = this.require_object_coercible()?; // 2. If regexp is neither undefined nor null, then let regexp = args.get_or_undefined(0); @@ -1970,13 +1973,15 @@ impl String { let flags = regexp_obj.get("flags", context)?; // ii. Perform ? RequireObjectCoercible(flags). - flags.require_object_coercible(context)?; + flags.require_object_coercible()?; // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. if !flags.to_string(context)?.contains('g') { - return context.throw_type_error( - "String.prototype.matchAll called with a non-global RegExp argument", - ); + return Err(JsNativeError::typ() + .with_message( + "String.prototype.matchAll called with a non-global RegExp argument", + ) + .into()); } } // c. Let matcher be ? GetMethod(regexp, @@matchAll). @@ -2013,7 +2018,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let this = this.require_object_coercible(context)?; + let this = this.require_object_coercible()?; // 2. Let S be ? ToString(O). let s = this.to_string(context)?; @@ -2041,8 +2046,9 @@ impl String { "NFKC" => Ok(JsValue::new(s.nfkc().collect::())), "NFKD" => Ok(JsValue::new(s.nfkd().collect::())), // 5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception. - _ => context - .throw_range_error("The normalization form should be one of NFC, NFD, NFKC, NFKD."), + _ => Err(JsNativeError::range() + .with_message("The normalization form should be one of NFC, NFD, NFKC, NFKD.") + .into()), } } @@ -2062,7 +2068,7 @@ impl String { context: &mut Context, ) -> JsResult { // 1. Let O be ? RequireObjectCoercible(this value). - let o = this.require_object_coercible(context)?; + let o = this.require_object_coercible()?; // 2. If regexp is neither undefined nor null, then let regexp = args.get_or_undefined(0); diff --git a/boa_engine/src/builtins/string/string_iterator.rs b/boa_engine/src/builtins/string/string_iterator.rs index 09983f532ad..b1402aa028e 100644 --- a/boa_engine/src/builtins/string/string_iterator.rs +++ b/boa_engine/src/builtins/string/string_iterator.rs @@ -2,6 +2,7 @@ use crate::{ builtins::{ function::make_builtin_fn, iterable::create_iter_result_object, string::code_point_at, }, + error::JsNativeError, object::{JsObject, ObjectData}, property::PropertyDescriptor, symbol::WellKnownSymbols, @@ -41,7 +42,7 @@ impl StringIterator { let string_iterator = string_iterator .as_mut() .and_then(|obj| obj.as_string_iterator_mut()) - .ok_or_else(|| context.construct_type_error("`this` is not an ArrayIterator"))?; + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not an ArrayIterator"))?; if string_iterator.string.is_undefined() { return Ok(create_iter_result_object( diff --git a/boa_engine/src/builtins/symbol/mod.rs b/boa_engine/src/builtins/symbol/mod.rs index 3dd58078e45..5ed87e055da 100644 --- a/boa_engine/src/builtins/symbol/mod.rs +++ b/boa_engine/src/builtins/symbol/mod.rs @@ -21,6 +21,7 @@ mod tests; use super::JsArgs; use crate::{ builtins::BuiltIn, + error::JsNativeError, object::{ConstructorBuilder, FunctionBuilder}, property::Attribute, symbol::{JsSymbol, WellKnownSymbols}, @@ -175,7 +176,9 @@ impl Symbol { ) -> JsResult { // 1. If NewTarget is not undefined, throw a TypeError exception. if !new_target.is_undefined() { - return context.throw_type_error("Symbol is not a constructor"); + return Err(JsNativeError::typ() + .with_message("Symbol is not a constructor") + .into()); } // 2. If description is undefined, let descString be undefined. @@ -189,11 +192,15 @@ impl Symbol { Ok(JsSymbol::new(description).into()) } - fn this_symbol_value(value: &JsValue, context: &mut Context) -> JsResult { + fn this_symbol_value(value: &JsValue) -> JsResult { value .as_symbol() .or_else(|| value.as_object().and_then(|obj| obj.borrow().as_symbol())) - .ok_or_else(|| context.construct_type_error("'this' is not a Symbol")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a Symbol") + .into() + }) } /// `Symbol.prototype.toString()` @@ -207,13 +214,9 @@ impl Symbol { /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let sym be ? thisSymbolValue(this value). - let symbol = Self::this_symbol_value(this, context)?; + let symbol = Self::this_symbol_value(this)?; // 2. Return SymbolDescriptiveString(sym). Ok(symbol.descriptive_string().into()) @@ -229,13 +232,9 @@ impl Symbol { /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/valueOf /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.valueof - pub(crate) fn value_of( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Return ? thisSymbolValue(this value). - let symbol = Self::this_symbol_value(this, context)?; + let symbol = Self::this_symbol_value(this)?; Ok(JsValue::Symbol(symbol)) } @@ -252,9 +251,9 @@ impl Symbol { pub(crate) fn get_description( this: &JsValue, _: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { - let symbol = Self::this_symbol_value(this, context)?; + let symbol = Self::this_symbol_value(this)?; if let Some(ref description) = symbol.description() { Ok(description.clone().into()) } else { @@ -300,11 +299,7 @@ impl Symbol { /// /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.keyfor /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/keyFor - pub(crate) fn key_for( - _: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn key_for(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let sym = args.get_or_undefined(0); // 1. If Type(sym) is not Symbol, throw a TypeError exception. if let Some(sym) = sym.as_symbol() { @@ -319,7 +314,9 @@ impl Symbol { Ok(symbol.map(JsValue::from).unwrap_or_default()) } else { - context.throw_type_error("Symbol.keyFor: sym is not a symbol") + Err(JsNativeError::typ() + .with_message("Symbol.keyFor: sym is not a symbol") + .into()) } } @@ -337,9 +334,9 @@ impl Symbol { pub(crate) fn to_primitive( this: &JsValue, _: &[JsValue], - context: &mut Context, + _: &mut Context, ) -> JsResult { - let sym = Self::this_symbol_value(this, context)?; + let sym = Self::this_symbol_value(this)?; // 1. Return ? thisSymbolValue(this value). Ok(sym.into()) } diff --git a/boa_engine/src/builtins/typed_array/mod.rs b/boa_engine/src/builtins/typed_array/mod.rs index 0315cf0693c..1f130a67183 100644 --- a/boa_engine/src/builtins/typed_array/mod.rs +++ b/boa_engine/src/builtins/typed_array/mod.rs @@ -20,6 +20,7 @@ use crate::{ Array, ArrayIterator, BuiltIn, JsArgs, }, context::intrinsics::{StandardConstructor, StandardConstructors}, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder, JsObject, ObjectData, @@ -120,10 +121,12 @@ macro_rules! typed_array { ) -> JsResult { // 1. If NewTarget is undefined, throw a TypeError exception. if new_target.is_undefined() { - return context.throw_type_error(concat!( - "new target was undefined when constructing an ", - $name - )); + return Err(JsNativeError::typ() + .with_message(concat!( + "new target was undefined when constructing an ", + $name + )) + .into()); } // 2. Let constructorName be the String value of the Constructor Name value specified in Table 72 for this TypedArray constructor. @@ -379,13 +382,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray% - fn constructor( - _new_target: &JsValue, - _args: &[JsValue], - context: &mut Context, - ) -> JsResult { + fn constructor(_new_target: &JsValue, _args: &[JsValue], _: &mut Context) -> JsResult { // 1. Throw a TypeError exception. - context.throw_type_error("the TypedArray constructor should never be called directly") + Err(JsNativeError::typ() + .with_message("the TypedArray constructor should never be called directly") + .into()) } /// `23.2.2.1 %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )` @@ -400,8 +401,9 @@ impl TypedArray { let constructor = match this.as_object() { Some(obj) if obj.is_constructor() => obj, _ => { - return context - .throw_type_error("TypedArray.from called on non-constructable value") + return Err(JsNativeError::typ() + .with_message("TypedArray.from called on non-constructable value") + .into()) } }; @@ -414,8 +416,9 @@ impl TypedArray { Some(obj) if obj.is_callable() => Some(obj), // a. If IsCallable(mapfn) is false, throw a TypeError exception. _ => { - return context - .throw_type_error("TypedArray.from called with non-callable mapfn") + return Err(JsNativeError::typ() + .with_message("TypedArray.from called with non-callable mapfn") + .into()) } }, }; @@ -510,7 +513,9 @@ impl TypedArray { let constructor = match this.as_object() { Some(obj) if obj.is_constructor() => obj, _ => { - return context.throw_type_error("TypedArray.of called on non-constructable value") + return Err(JsNativeError::typ() + .with_message("TypedArray.of called on non-constructable value") + .into()) } }; @@ -551,15 +556,17 @@ impl TypedArray { pub(crate) fn at(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -596,17 +603,17 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer - fn buffer(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + fn buffer(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let typed_array = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let typed_array = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; // 4. Let buffer be O.[[ViewedArrayBuffer]]. // 5. Return buffer. @@ -621,21 +628,17 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.bytelength - pub(crate) fn byte_length( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn byte_length(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let typed_array = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let typed_array = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; // 4. Let buffer be O.[[ViewedArrayBuffer]]. // 5. If IsDetachedBuffer(buffer) is true, return +0𝔽. @@ -654,21 +657,17 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.byteoffset - pub(crate) fn byte_offset( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn byte_offset(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let typed_array = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let typed_array = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; // 4. Let buffer be O.[[ViewedArrayBuffer]]. // 5. If IsDetachedBuffer(buffer) is true, return +0𝔽. @@ -689,19 +688,21 @@ impl TypedArray { /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin fn copy_within(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be the this value. - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let len = { let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; // 2. Perform ? ValidateTypedArray(O). if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -756,9 +757,9 @@ impl TypedArray { let count = std::cmp::min(r#final - from, len - to); let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; // 17. If count > 0, then if count > 0 { @@ -766,7 +767,9 @@ impl TypedArray { // b. Let buffer be O.[[ViewedArrayBuffer]]. // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // d. Let typedArrayName be the String value of O.[[TypedArrayName]]. @@ -859,15 +862,17 @@ impl TypedArray { fn entries(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let o = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.borrow() .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))? + .ok_or_else(|| JsNativeError::typ().with_message("Value is not a typed array object"))? .is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Return CreateArrayIterator(O, key+value). @@ -891,15 +896,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -909,9 +916,11 @@ impl TypedArray { let callback_fn = match args.get_or_undefined(0).as_object() { Some(obj) if obj.is_callable() => obj, _ => { - return context.throw_type_error( - "TypedArray.prototype.every called with non-callable callback function", - ) + return Err(JsNativeError::typ() + .with_message( + "TypedArray.prototype.every called with non-callable callback function", + ) + .into()) } }; @@ -954,15 +963,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1007,7 +1018,9 @@ impl TypedArray { // 14. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception. if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 15. Repeat, while k < final, @@ -1038,29 +1051,32 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. let len = o.array_length(); // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. - let callback_fn = match args.get_or_undefined(0).as_object() { - Some(obj) if obj.is_callable() => obj, - _ => { - return context.throw_type_error( - "TypedArray.prototype.filter called with non-callable callback function", - ) - } - }; + let callback_fn = + match args.get_or_undefined(0).as_object() { + Some(obj) if obj.is_callable() => obj, + _ => return Err(JsNativeError::typ() + .with_message( + "TypedArray.prototype.filter called with non-callable callback function", + ) + .into()), + }; // 5. Let kept be a new empty List. let mut kept = Vec::new(); @@ -1123,15 +1139,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1141,9 +1159,11 @@ impl TypedArray { let predicate = match args.get_or_undefined(0).as_object() { Some(obj) if obj.is_callable() => obj, _ => { - return context.throw_type_error( - "TypedArray.prototype.find called with non-callable predicate function", - ) + return Err(JsNativeError::typ() + .with_message( + "TypedArray.prototype.find called with non-callable predicate function", + ) + .into()) } }; @@ -1185,28 +1205,31 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. let len = o.array_length(); // 4. If IsCallable(predicate) is false, throw a TypeError exception. - let predicate = - match args.get_or_undefined(0).as_object() { - Some(obj) if obj.is_callable() => obj, - _ => return context.throw_type_error( + let predicate = match args.get_or_undefined(0).as_object() { + Some(obj) if obj.is_callable() => obj, + _ => return Err(JsNativeError::typ() + .with_message( "TypedArray.prototype.findindex called with non-callable predicate function", - ), - }; + ) + .into()), + }; // 5. Let k be 0. // 6. Repeat, while k < len, @@ -1246,29 +1269,32 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. let len = o.array_length(); // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. - let callback_fn = match args.get_or_undefined(0).as_object() { - Some(obj) if obj.is_callable() => obj, - _ => { - return context.throw_type_error( - "TypedArray.prototype.foreach called with non-callable callback function", - ) - } - }; + let callback_fn = + match args.get_or_undefined(0).as_object() { + Some(obj) if obj.is_callable() => obj, + _ => return Err(JsNativeError::typ() + .with_message( + "TypedArray.prototype.foreach called with non-callable callback function", + ) + .into()), + }; // 5. Let k be 0. // 6. Repeat, while k < len, @@ -1302,15 +1328,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1379,15 +1407,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1465,15 +1495,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1522,15 +1554,17 @@ impl TypedArray { pub(crate) fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let o = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.borrow() .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))? + .ok_or_else(|| JsNativeError::typ().with_message("Value is not a typed array object"))? .is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Return CreateArrayIterator(O, key). @@ -1554,15 +1588,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1625,21 +1661,17 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length - pub(crate) fn length( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { + pub(crate) fn length(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots. - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let typed_array = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let typed_array = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; // 4. Let buffer be O.[[ViewedArrayBuffer]]. // 5. If IsDetachedBuffer(buffer) is true, return +0𝔽. @@ -1665,15 +1697,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1683,9 +1717,11 @@ impl TypedArray { let callback_fn = match args.get_or_undefined(0).as_object() { Some(obj) if obj.is_callable() => obj, _ => { - return context.throw_type_error( - "TypedArray.prototype.map called with non-callable callback function", - ) + return Err(JsNativeError::typ() + .with_message( + "TypedArray.prototype.map called with non-callable callback function", + ) + .into()) } }; @@ -1727,34 +1763,38 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. let len = o.array_length(); // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. - let callback_fn = match args.get_or_undefined(0).as_object() { - Some(obj) if obj.is_callable() => obj, - _ => { - return context.throw_type_error( - "TypedArray.prototype.reduce called with non-callable callback function", - ) - } - }; + let callback_fn = + match args.get_or_undefined(0).as_object() { + Some(obj) if obj.is_callable() => obj, + _ => return Err(JsNativeError::typ() + .with_message( + "TypedArray.prototype.reduce called with non-callable callback function", + ) + .into()), + }; // 5. If len = 0 and initialValue is not present, throw a TypeError exception. if len == 0 && args.get(1).is_none() { - return context - .throw_type_error("Typed array length is 0 and initial value is not present"); + return Err(JsNativeError::typ() + .with_message("Typed array length is 0 and initial value is not present") + .into()); } // 6. Let k be 0. @@ -1808,33 +1848,37 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. let len = o.array_length() as i64; // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. - let callback_fn = - match args.get_or_undefined(0).as_object() { - Some(obj) if obj.is_callable() => obj, - _ => return context.throw_type_error( + let callback_fn = match args.get_or_undefined(0).as_object() { + Some(obj) if obj.is_callable() => obj, + _ => return Err(JsNativeError::typ() + .with_message( "TypedArray.prototype.reduceright called with non-callable callback function", - ), - }; + ) + .into()), + }; // 5. If len = 0 and initialValue is not present, throw a TypeError exception. if len == 0 && args.get(1).is_none() { - return context - .throw_type_error("Typed array length is 0 and initial value is not present"); + return Err(JsNativeError::typ() + .with_message("Typed array length is 0 and initial value is not present") + .into()); } // 6. Let k be len - 1. @@ -1892,15 +1936,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -1953,10 +1999,12 @@ impl TypedArray { // 2. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). // 3. Assert: target has a [[ViewedArrayBuffer]] internal slot. let target = this.as_object().ok_or_else(|| { - context.construct_type_error("TypedArray.set must be called on typed array object") + JsNativeError::typ().with_message("TypedArray.set must be called on typed array object") })?; if !target.is_typed_array() { - return context.throw_type_error("TypedArray.set must be called on typed array object"); + return Err(JsNativeError::typ() + .with_message("TypedArray.set must be called on typed array object") + .into()); } // 4. Let targetOffset be ? ToIntegerOrInfinity(offset). @@ -1965,10 +2013,14 @@ impl TypedArray { // 5. If targetOffset < 0, throw a RangeError exception. match target_offset { IntegerOrInfinity::Integer(i) if i < 0 => { - return context.throw_range_error("TypedArray.set called with negative offset") + return Err(JsNativeError::range() + .with_message("TypedArray.set called with negative offset") + .into()) } IntegerOrInfinity::NegativeInfinity => { - return context.throw_range_error("TypedArray.set called with negative offset") + return Err(JsNativeError::range() + .with_message("TypedArray.set called with negative offset") + .into()) } _ => {} } @@ -2016,7 +2068,9 @@ impl TypedArray { // 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. if target_array.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } let target_buffer_obj = target_array .viewed_array_buffer() @@ -2028,7 +2082,9 @@ impl TypedArray { // 4. Let srcBuffer be source.[[ViewedArrayBuffer]]. // 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. if source_array.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } let mut src_buffer_obj = source_array .viewed_array_buffer() @@ -2062,23 +2118,27 @@ impl TypedArray { let target_offset = match target_offset { IntegerOrInfinity::Integer(i) if i >= 0 => i as u64, IntegerOrInfinity::PositiveInfinity => { - return context.throw_range_error("Target offset cannot be Infinity"); + return Err(JsNativeError::range() + .with_message("Target offset cannot be Infinity") + .into()); } _ => unreachable!(), }; // 16. If srcLength + targetOffset > targetLength, throw a RangeError exception. if src_length + target_offset > target_length { - return context.throw_range_error( - "Source typed array and target offset longer than target typed array", - ); + return Err(JsNativeError::range() + .with_message("Source typed array and target offset longer than target typed array") + .into()); } // 17. If target.[[ContentType]] ≠ source.[[ContentType]], throw a TypeError exception. if target_name.content_type() != src_name.content_type() { - return context.throw_type_error( - "Source typed array and target typed array have different content types", - ); + return Err(JsNativeError::typ() + .with_message( + "Source typed array and target typed array have different content types", + ) + .into()); } // TODO: Shared Array Buffer @@ -2226,7 +2286,9 @@ impl TypedArray { // 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. if target_array.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let targetLength be target.[[ArrayLength]]. @@ -2251,7 +2313,9 @@ impl TypedArray { let target_offset = match target_offset { // 10. If targetOffset is +∞, throw a RangeError exception. IntegerOrInfinity::PositiveInfinity => { - return context.throw_range_error("Target offset cannot be Infinity") + return Err(JsNativeError::range() + .with_message("Target offset cannot be Infinity") + .into()) } IntegerOrInfinity::Integer(i) if i >= 0 => i as u64, _ => unreachable!(), @@ -2259,9 +2323,9 @@ impl TypedArray { // 11. If srcLength + targetOffset > targetLength, throw a RangeError exception. if src_length + target_offset > target_length { - return context.throw_range_error( - "Source object and target offset longer than target typed array", - ); + return Err(JsNativeError::range() + .with_message("Source object and target offset longer than target typed array") + .into()); } // 12. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. @@ -2297,7 +2361,9 @@ impl TypedArray { // e. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. if target_buffer.is_detached_buffer() { - return context.throw_type_error("Cannot set value on detached array buffer"); + return Err(JsNativeError::typ() + .with_message("Cannot set value on detached array buffer") + .into()); } // f. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered). @@ -2333,15 +2399,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -2390,7 +2458,9 @@ impl TypedArray { if count > 0 { // a. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception. if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // b. Let srcName be the String value of O.[[TypedArrayName]]. @@ -2502,15 +2572,17 @@ impl TypedArray { ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Let len be O.[[ArrayLength]]. @@ -2520,9 +2592,11 @@ impl TypedArray { let callback_fn = match args.get_or_undefined(0).as_object() { Some(obj) if obj.is_callable() => obj, _ => { - return context.throw_type_error( - "TypedArray.prototype.some called with non-callable callback function", - ) + return Err(JsNativeError::typ() + .with_message( + "TypedArray.prototype.some called with non-callable callback function", + ) + .into()) } }; @@ -2567,37 +2641,41 @@ impl TypedArray { None | Some(JsValue::Undefined) => None, Some(JsValue::Object(obj)) if obj.is_callable() => Some(obj), _ => { - return context - .throw_type_error("TypedArray.sort called with non-callable comparefn") + return Err(JsNativeError::typ() + .with_message("TypedArray.sort called with non-callable comparefn") + .into()) } }; // 2. Let obj be the this value. let obj = this.as_object().ok_or_else(|| { - context.construct_type_error("TypedArray.sort must be called on typed array object") + JsNativeError::typ() + .with_message("TypedArray.sort must be called on typed array object") })?; // 4. Let buffer be obj.[[ViewedArrayBuffer]]. // 5. Let len be obj.[[ArrayLength]]. - let (buffer, len) = { - // 3. Perform ? ValidateTypedArray(obj). - let obj_borrow = obj.borrow(); - let o = obj_borrow.as_typed_array().ok_or_else(|| { - context.construct_type_error("TypedArray.sort must be called on typed array object") - })?; - if o.is_detached() { - return context.throw_type_error( + let (buffer, len) = + { + // 3. Perform ? ValidateTypedArray(obj). + let obj_borrow = obj.borrow(); + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ() + .with_message("TypedArray.sort must be called on typed array object") + })?; + if o.is_detached() { + return Err(JsNativeError::typ().with_message( "TypedArray.sort called on typed array object with detached array buffer", - ); - } + ).into()); + } - ( - o.viewed_array_buffer() - .expect("Already checked for detached buffer") - .clone(), - o.array_length(), - ) - }; + ( + o.viewed_array_buffer() + .expect("Already checked for detached buffer") + .clone(), + o.array_length(), + ) + }; // 4. Let items be a new empty List. let mut items = Vec::with_capacity(len as usize); @@ -2640,8 +2718,9 @@ impl TypedArray { .expect("Must be array buffer") .is_detached_buffer() { - return context - .throw_type_error("Cannot sort typed array with detached buffer"); + return Err(JsNativeError::typ() + .with_message("Cannot sort typed array with detached buffer") + .into()); } // c. If v is NaN, return +0𝔽. @@ -2777,13 +2856,13 @@ impl TypedArray { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. - let obj = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; let obj_borrow = obj.borrow(); - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; // 4. Let buffer be O.[[ViewedArrayBuffer]]. let buffer = o @@ -2861,15 +2940,17 @@ impl TypedArray { fn values(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). - let o = this - .as_object() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.borrow() .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))? + .ok_or_else(|| JsNativeError::typ().with_message("Value is not a typed array object"))? .is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. Return CreateArrayIterator(O, value). @@ -2947,8 +3028,9 @@ impl TypedArray { .content_type() != typed_array_name.content_type() { - return context - .throw_type_error("New typed array has different context type than exemplar"); + return Err(JsNativeError::typ() + .with_message("New typed array has different context type than exemplar") + .into()); } // 6. Return result. @@ -2971,11 +3053,13 @@ impl TypedArray { let obj_borrow = new_typed_array.borrow(); // 2. Perform ? ValidateTypedArray(newTypedArray). - let o = obj_borrow - .as_typed_array() - .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; + let o = obj_borrow.as_typed_array().ok_or_else(|| { + JsNativeError::typ().with_message("Value is not a typed array object") + })?; if o.is_detached() { - return context.throw_type_error("Buffer of the typed array is detached"); + return Err(JsNativeError::typ() + .with_message("Buffer of the typed array is detached") + .into()); } // 3. If argumentList is a List of a single Number, then @@ -2983,8 +3067,9 @@ impl TypedArray { if let Some(number) = args[0].as_number() { // a. If newTypedArray.[[ArrayLength]] < ℝ(argumentList[0]), throw a TypeError exception. if (o.array_length() as f64) < number { - return context - .throw_type_error("New typed array length is smaller than expected"); + return Err(JsNativeError::typ() + .with_message("New typed array length is smaller than expected") + .into()); } } } @@ -3132,7 +3217,9 @@ impl TypedArray { // 1. Let srcData be srcArray.[[ViewedArrayBuffer]]. // 2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. if src_array.is_detached() { - return context.throw_type_error("Cannot initialize typed array from detached buffer"); + return Err(JsNativeError::typ() + .with_message("Cannot initialize typed array from detached buffer") + .into()); } let src_data_obj = src_array .viewed_array_buffer() @@ -3190,14 +3277,16 @@ impl TypedArray { // b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. if src_data.is_detached_buffer() { - return context - .throw_type_error("Cannot initialize typed array from detached buffer"); + return Err(JsNativeError::typ() + .with_message("Cannot initialize typed array from detached buffer") + .into()); } // c. If srcArray.[[ContentType]] ≠ O.[[ContentType]], throw a TypeError exception. if src_name.content_type() != constructor_name.content_type() { - return context - .throw_type_error("Cannot initialize typed array from different content type"); + return Err(JsNativeError::typ() + .with_message("Cannot initialize typed array from different content type") + .into()); } // d. Let srcByteIndex be srcByteOffset. @@ -3282,7 +3371,9 @@ impl TypedArray { // 4. If offset modulo elementSize ≠ 0, throw a RangeError exception. if offset % constructor_name.element_size() != 0 { - return context.throw_range_error("Invalid length for typed array"); + return Err(JsNativeError::range() + .with_message("Invalid length for typed array") + .into()); } let buffer_byte_length = { @@ -3293,8 +3384,9 @@ impl TypedArray { // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if buffer_array.is_detached_buffer() { - return context - .throw_type_error("Cannot construct typed array from detached buffer"); + return Err(JsNativeError::typ() + .with_message("Cannot construct typed array from detached buffer") + .into()); } // 7. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. @@ -3305,7 +3397,9 @@ impl TypedArray { let new_byte_length = if length.is_undefined() { // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception. if buffer_byte_length % constructor_name.element_size() != 0 { - return context.throw_range_error("Invalid length for typed array"); + return Err(JsNativeError::range() + .with_message("Invalid length for typed array") + .into()); } // b. Let newByteLength be bufferByteLength - offset. @@ -3313,7 +3407,9 @@ impl TypedArray { // c. If newByteLength < 0, throw a RangeError exception. if new_byte_length < 0 { - return context.throw_range_error("Invalid length for typed array"); + return Err(JsNativeError::range() + .with_message("Invalid length for typed array") + .into()); } new_byte_length as u64 @@ -3327,7 +3423,9 @@ impl TypedArray { // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception. if offset + new_byte_length > buffer_byte_length { - return context.throw_range_error("Invalid length for typed array"); + return Err(JsNativeError::range() + .with_message("Invalid length for typed array") + .into()); } new_byte_length diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index 711e23aed02..22d557a61ee 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -161,7 +161,7 @@ impl FunctionCompiler { compiler.emit_opcode(Opcode::Yield); } - compiler.create_declarations(body.items())?; + compiler.create_declarations(body.items()); compiler.compile_statement_list(body.items(), false)?; if let Some(env_label) = env_label { diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 12884706b6e..434c1b1ecf5 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -2,6 +2,7 @@ mod function; use crate::{ environments::{BindingLocator, CompileTimeEnvironment}, + error::JsNativeError, syntax::ast::{ node::{ declaration::{ @@ -589,7 +590,7 @@ impl<'b> ByteCompiler<'b> { self.context.push_compile_time_environment(strict); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(list)?; + self.create_declarations(list); if let Some((last, items)) = list.split_last() { for node in items { @@ -635,8 +636,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Inc, &[]); let access = Self::compile_access(unary.target()).ok_or_else(|| { - self.context - .construct_syntax_error("Invalid increment operand") + JsNativeError::syntax().with_message("Invalid increment operand") })?; self.access_set(access, None, true)?; None @@ -646,8 +646,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Dec, &[]); let access = Self::compile_access(unary.target()).ok_or_else(|| { - self.context - .construct_syntax_error("Invalid decrement operand") + JsNativeError::syntax().with_message("Invalid decrement operand") })?; self.access_set(access, None, true)?; None @@ -657,8 +656,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::IncPost, &[]); let access = Self::compile_access(unary.target()).ok_or_else(|| { - self.context - .construct_syntax_error("Invalid increment operand") + JsNativeError::syntax().with_message("Invalid increment operand") })?; self.access_set(access, None, false)?; @@ -669,8 +667,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::DecPost, &[]); let access = Self::compile_access(unary.target()).ok_or_else(|| { - self.context - .construct_syntax_error("Invalid decrement operand") + JsNativeError::syntax().with_message("Invalid decrement operand") })?; self.access_set(access, None, false)?; @@ -821,9 +818,8 @@ impl<'b> ByteCompiler<'b> { self.compile_expr(binary.rhs(), true)?; let access = Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context.construct_syntax_error( - "Invalid left-hand side in assignment", - ) + JsNativeError::syntax() + .with_message("Invalid left-hand side in assignment") })?; self.access_set(access, None, use_expr)?; self.patch_jump(exit); @@ -834,9 +830,8 @@ impl<'b> ByteCompiler<'b> { self.compile_expr(binary.rhs(), true)?; let access = Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context.construct_syntax_error( - "Invalid left-hand side in assignment", - ) + JsNativeError::syntax() + .with_message("Invalid left-hand side in assignment") })?; self.access_set(access, None, use_expr)?; self.patch_jump(exit); @@ -847,9 +842,8 @@ impl<'b> ByteCompiler<'b> { self.compile_expr(binary.rhs(), true)?; let access = Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context.construct_syntax_error( - "Invalid left-hand side in assignment", - ) + JsNativeError::syntax() + .with_message("Invalid left-hand side in assignment") })?; self.access_set(access, None, use_expr)?; self.patch_jump(exit); @@ -861,8 +855,8 @@ impl<'b> ByteCompiler<'b> { self.compile_expr(binary.rhs(), true)?; self.emit(opcode, &[]); let access = Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context - .construct_syntax_error("Invalid left-hand side in assignment") + JsNativeError::syntax() + .with_message("Invalid left-hand side in assignment") })?; self.access_set(access, None, use_expr)?; } @@ -993,9 +987,9 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); } PropertyDefinition::CoverInitializedName(_, _) => { - return self.context.throw_syntax_error( - "invalid assignment pattern in object literal", - ); + return Err(JsNativeError::syntax() + .with_message("invalid assignment pattern in object literal") + .into()); } } } @@ -1348,7 +1342,7 @@ impl<'b> ByteCompiler<'b> { let jelse = self.jump_if_false(); if !matches!(node.body(), Node::Block(_)) { - self.create_decls_from_stmt(node.body())?; + self.create_decls_from_stmt(node.body()); } self.compile_stmt(node.body(), false)?; @@ -1361,7 +1355,7 @@ impl<'b> ByteCompiler<'b> { let exit = self.jump(); self.patch_jump(jelse); if !matches!(else_body, Node::Block(_)) { - self.create_decls_from_stmt(else_body)?; + self.create_decls_from_stmt(else_body); } self.compile_stmt(else_body, false)?; self.patch_jump(exit); @@ -1374,7 +1368,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); if let Some(init) = for_loop.init() { - self.create_decls_from_stmt(init)?; + self.create_decls_from_stmt(init); self.compile_stmt(init, false)?; } @@ -1399,7 +1393,7 @@ impl<'b> ByteCompiler<'b> { let exit = self.jump_if_false(); if !matches!(for_loop.body(), Node::Block(_)) { - self.create_decls_from_stmt(for_loop.body())?; + self.create_decls_from_stmt(for_loop.body()); } self.compile_stmt(for_loop.body(), false)?; @@ -1711,7 +1705,7 @@ impl<'b> ByteCompiler<'b> { } let address = address_info .ok_or_else(|| { - self.context.construct_syntax_error(format!( + JsNativeError::syntax().with_message(format!( "Cannot use the undeclared label '{}'", self.context.interner().resolve_expect(label_name) )) @@ -1730,8 +1724,7 @@ impl<'b> ByteCompiler<'b> { items .next() .ok_or_else(|| { - self.context - .construct_syntax_error("continue must be inside loop") + JsNativeError::syntax().with_message("continue must be inside loop") })? .start_address }; @@ -1775,18 +1768,19 @@ impl<'b> ByteCompiler<'b> { } } if !found { - return self.context.throw_syntax_error(format!( - "Cannot use the undeclared label '{}'", - self.interner().resolve_expect(label_name) - )); + return Err(JsNativeError::syntax() + .with_message(format!( + "Cannot use the undeclared label '{}'", + self.interner().resolve_expect(label_name) + )) + .into()); } } else { self.jump_info .last_mut() .ok_or_else(|| { - self.context.construct_syntax_error( - "unlabeled break must be inside loop or switch", - ) + JsNativeError::syntax() + .with_message("unlabeled break must be inside loop or switch") })? .breaks .push(label); @@ -1796,7 +1790,7 @@ impl<'b> ByteCompiler<'b> { self.context.push_compile_time_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(block.items())?; + self.create_declarations(block.items()); self.compile_statement_list(block.items(), use_expr)?; let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); @@ -1814,7 +1808,7 @@ impl<'b> ByteCompiler<'b> { let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); for case in switch.cases() { - self.create_declarations(case.body().items())?; + self.create_declarations(case.body().items()); } self.emit_opcode(Opcode::LoopStart); @@ -1837,7 +1831,7 @@ impl<'b> ByteCompiler<'b> { self.patch_jump(exit); if let Some(body) = switch.default() { - self.create_declarations(body)?; + self.create_declarations(body); self.compile_statement_list(body, false)?; } @@ -1869,7 +1863,7 @@ impl<'b> ByteCompiler<'b> { let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(t.block().items())?; + self.create_declarations(t.block().items()); self.compile_statement_list(t.block().items(), use_expr)?; let (num_bindings, compile_environment) = @@ -1910,7 +1904,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); } - self.create_declarations(catch.block().items())?; + self.create_declarations(catch.block().items()); self.compile_statement_list(catch.block().items(), use_expr)?; let (num_bindings, compile_environment) = @@ -1947,7 +1941,7 @@ impl<'b> ByteCompiler<'b> { let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(finally.items())?; + self.create_declarations(finally.items()); self.compile_statement_list(finally.items(), false)?; let (num_bindings, compile_environment) = @@ -2507,14 +2501,13 @@ impl<'b> ByteCompiler<'b> { Ok(()) } - pub(crate) fn create_declarations(&mut self, stmt_list: &[Node]) -> JsResult<()> { + pub(crate) fn create_declarations(&mut self, stmt_list: &[Node]) { for node in stmt_list { - self.create_decls_from_stmt(node)?; + self.create_decls_from_stmt(node); } - Ok(()) } - pub(crate) fn create_decls_from_stmt(&mut self, node: &Node) -> JsResult { + pub(crate) fn create_decls_from_stmt(&mut self, node: &Node) -> bool { let mut has_identifier_argument = false; match node { @@ -2614,22 +2607,22 @@ impl<'b> ByteCompiler<'b> { } Node::DoWhileLoop(do_while_loop) => { if !matches!(do_while_loop.body(), Node::Block(_)) { - self.create_decls_from_stmt(do_while_loop.body())?; + self.create_decls_from_stmt(do_while_loop.body()); } } Node::ForInLoop(for_in_loop) => { if !matches!(for_in_loop.body(), Node::Block(_)) { - self.create_decls_from_stmt(for_in_loop.body())?; + self.create_decls_from_stmt(for_in_loop.body()); } } Node::ForOfLoop(for_of_loop) => { if !matches!(for_of_loop.body(), Node::Block(_)) { - self.create_decls_from_stmt(for_of_loop.body())?; + self.create_decls_from_stmt(for_of_loop.body()); } } _ => {} } - Ok(has_identifier_argument) + has_identifier_argument } /// This function compiles a class declaration or expression. @@ -2697,7 +2690,7 @@ impl<'b> ByteCompiler<'b> { } else { None }; - compiler.create_declarations(expr.body().items())?; + compiler.create_declarations(expr.body().items()); compiler.compile_statement_list(expr.body().items(), false)?; if let Some(env_label) = env_label { let (num_bindings, compile_environment) = @@ -2992,7 +2985,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Dup); let mut compiler = ByteCompiler::new(Sym::EMPTY_STRING, true, self.context); compiler.context.push_compile_time_environment(true); - compiler.create_declarations(statement_list.items())?; + compiler.create_declarations(statement_list.items()); compiler.compile_statement_list(statement_list.items(), false)?; let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); diff --git a/boa_engine/src/class.rs b/boa_engine/src/class.rs index cbbb8a2eda4..860e5ae147d 100644 --- a/boa_engine/src/class.rs +++ b/boa_engine/src/class.rs @@ -63,6 +63,7 @@ use crate::{ builtins::function::NativeFunctionSignature, + error::JsNativeError, object::{ConstructorBuilder, JsFunction, JsObject, NativeObject, ObjectData, PROTOTYPE}, property::{Attribute, PropertyDescriptor, PropertyKey}, Context, JsResult, JsValue, @@ -104,29 +105,35 @@ impl ClassConstructor for T { Self: Sized, { if this.is_undefined() { - return context.throw_type_error(format!( - "cannot call constructor of native class `{}` without new", - T::NAME - )); + return Err(JsNativeError::typ() + .with_message(format!( + "cannot call constructor of native class `{}` without new", + T::NAME + )) + .into()); } let class_constructor = context.global_object().clone().get(T::NAME, context)?; let class_constructor = if let JsValue::Object(ref obj) = class_constructor { obj } else { - return context.throw_type_error(format!( - "invalid constructor for native class `{}` ", - T::NAME - )); + return Err(JsNativeError::typ() + .with_message(format!( + "invalid constructor for native class `{}` ", + T::NAME + )) + .into()); }; let class_prototype = if let JsValue::Object(ref obj) = class_constructor.get(PROTOTYPE, context)? { obj.clone() } else { - return context.throw_type_error(format!( - "invalid default prototype for native class `{}`", - T::NAME - )); + return Err(JsNativeError::typ() + .with_message(format!( + "invalid default prototype for native class `{}`", + T::NAME + )) + .into()); }; let prototype = this diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index d2d35e873ae..6899e711b74 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -15,6 +15,7 @@ use crate::{ builtins::{self, function::NativeFunctionSignature}, bytecompiler::ByteCompiler, class::{Class, ClassBuilder}, + error::JsNativeError, job::JobCallback, object::{FunctionBuilder, GlobalPropertyMap, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor, PropertyKey}, @@ -210,7 +211,11 @@ impl Context { // 2. If IsCallable(F) is false, throw a TypeError exception. // 3. Return ? F.[[Call]](V, argumentsList). f.as_callable() - .ok_or_else(|| self.construct_type_error("Value is not callable")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("Value is not callable") + .into() + }) .and_then(|f| f.call(v, arguments_list, self)) } @@ -245,179 +250,6 @@ impl Context { .expect("Into used as message") } - /// Throws a `Error` with the specified message. - #[inline] - pub fn throw_error(&mut self, message: M) -> JsResult - where - M: Into>, - { - Err(self.construct_error(message)) - } - - /// Constructs a `RangeError` with the specified message. - #[inline] - pub fn construct_range_error(&mut self, message: M) -> JsValue - where - M: Into>, - { - crate::builtins::error::RangeError::constructor( - &self - .intrinsics() - .constructors() - .range_error() - .constructor() - .into(), - &[message.into().into()], - self, - ) - .expect("Into used as message") - } - - /// Throws a `RangeError` with the specified message. - #[inline] - pub fn throw_range_error(&mut self, message: M) -> JsResult - where - M: Into>, - { - Err(self.construct_range_error(message)) - } - - /// Constructs a `TypeError` with the specified message. - #[inline] - pub fn construct_type_error(&mut self, message: M) -> JsValue - where - M: Into>, - { - crate::builtins::error::TypeError::constructor( - &self - .intrinsics() - .constructors() - .type_error() - .constructor() - .into(), - &[message.into().into()], - self, - ) - .expect("Into used as message") - } - - /// Throws a `TypeError` with the specified message. - #[inline] - pub fn throw_type_error(&mut self, message: M) -> JsResult - where - M: Into>, - { - Err(self.construct_type_error(message)) - } - - /// Constructs a `ReferenceError` with the specified message. - #[inline] - pub fn construct_reference_error(&mut self, message: M) -> JsValue - where - M: Into>, - { - crate::builtins::error::ReferenceError::constructor( - &self - .intrinsics() - .constructors() - .reference_error() - .constructor() - .into(), - &[message.into().into()], - self, - ) - .expect("Into used as message") - } - - /// Throws a `ReferenceError` with the specified message. - #[inline] - pub fn throw_reference_error(&mut self, message: M) -> JsResult - where - M: Into>, - { - Err(self.construct_reference_error(message)) - } - - /// Constructs a `SyntaxError` with the specified message. - #[inline] - pub fn construct_syntax_error(&mut self, message: M) -> JsValue - where - M: Into>, - { - crate::builtins::error::SyntaxError::constructor( - &self - .intrinsics() - .constructors() - .syntax_error() - .constructor() - .into(), - &[message.into().into()], - self, - ) - .expect("Into used as message") - } - - /// Throws a `SyntaxError` with the specified message. - #[inline] - pub fn throw_syntax_error(&mut self, message: M) -> JsResult - where - M: Into>, - { - Err(self.construct_syntax_error(message)) - } - - /// Constructs a `EvalError` with the specified message. - pub fn construct_eval_error(&mut self, message: M) -> JsValue - where - M: Into>, - { - crate::builtins::error::EvalError::constructor( - &self - .intrinsics() - .constructors() - .eval_error() - .constructor() - .into(), - &[message.into().into()], - self, - ) - .expect("Into used as message") - } - - /// Constructs a `URIError` with the specified message. - pub fn construct_uri_error(&mut self, message: M) -> JsValue - where - M: Into>, - { - crate::builtins::error::UriError::constructor( - &self - .intrinsics() - .constructors() - .uri_error() - .constructor() - .into(), - &[message.into().into()], - self, - ) - .expect("Into used as message") - } - - /// Throws a `EvalError` with the specified message. - pub fn throw_eval_error(&mut self, message: M) -> JsResult - where - M: Into>, - { - Err(self.construct_eval_error(message)) - } - - /// Throws a `URIError` with the specified message. - pub fn throw_uri_error(&mut self, message: M) -> JsResult - where - M: Into>, - { - Err(self.construct_uri_error(message)) - } - /// Register a global native function. /// /// This is more efficient that creating a closure function, since this does not allocate, @@ -657,14 +489,7 @@ impl Context { { let main_timer = Profiler::global().start_event("Evaluation", "Main"); - let parsing_result = Parser::new(src.as_ref()) - .parse_all(self) - .map_err(|e| e.to_string()); - - let statement_list = match parsing_result { - Ok(statement_list) => statement_list, - Err(e) => return self.throw_syntax_error(e), - }; + let statement_list = Parser::new(src.as_ref()).parse_all(self)?; let code_block = self.compile(&statement_list)?; let result = self.execute(code_block); @@ -681,7 +506,7 @@ impl Context { pub fn compile(&mut self, statement_list: &StatementList) -> JsResult> { let _timer = Profiler::global().start_event("Compilation", "Main"); let mut compiler = ByteCompiler::new(Sym::MAIN, statement_list.strict(), self); - compiler.create_declarations(statement_list.items())?; + compiler.create_declarations(statement_list.items()); compiler.compile_statement_list(statement_list.items(), true)?; Ok(Gc::new(compiler.finish())) } diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index 21ecf3aed0f..510da1d9a45 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -1,4 +1,7 @@ -use crate::{environments::CompileTimeEnvironment, object::JsObject, Context, JsResult, JsValue}; +use crate::{ + environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, + JsResult, JsValue, +}; use boa_gc::{Cell, Finalize, Gc, Trace}; use boa_interner::Sym; use rustc_hash::FxHashSet; @@ -795,10 +798,12 @@ impl BindingLocator { #[inline] pub(crate) fn throw_mutate_immutable(&self, context: &mut Context) -> JsResult<()> { if self.mutate_immutable { - context.throw_type_error(format!( - "cannot mutate an immutable binding '{}'", - context.interner().resolve_expect(self.name) - )) + Err(JsNativeError::typ() + .with_message(format!( + "cannot mutate an immutable binding '{}'", + context.interner().resolve_expect(self.name) + )) + .into()) } else { Ok(()) } diff --git a/boa_engine/src/error/mod.rs b/boa_engine/src/error/mod.rs new file mode 100644 index 00000000000..ea24664dd43 --- /dev/null +++ b/boa_engine/src/error/mod.rs @@ -0,0 +1,333 @@ +mod vos; + +use std::borrow::Cow; + +use boa_gc::{Finalize, Trace}; + +use crate::{ + builtins::{error::ErrorKind, Array}, + object::JsObject, + object::ObjectData, + property::PropertyDescriptor, + syntax::parser, + Context, JsResult, JsValue, +}; + +use vos::ViewStr; + +#[derive(Debug, Clone, Trace, Finalize)] +pub struct JsError { + inner: Repr, +} + +#[derive(Debug, Clone, Trace, Finalize)] +enum Repr { + Native(JsNativeError), + Opaque(JsValue), +} + +impl JsError { + pub fn to_value(&self, context: &mut Context) -> JsValue { + match &self.inner { + Repr::Native(e) => e.to_value(context), + Repr::Opaque(v) => v.clone(), + } + } + + // TODO: Should probably change this to return a custom error instead + pub fn try_native(&self, context: &mut Context) -> JsResult { + match &self.inner { + Repr::Native(e) => Ok(e.clone()), + Repr::Opaque(val) => { + if let Some(obj) = val.as_object() { + if let Some(error) = obj.borrow().as_error() { + let message = if obj.has_property("message", context)? { + obj.get("message", context)? + .as_string() + .map(ToString::to_string) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("invalid type for field `message`") + })? + .into() + } else { + "".into() + }; + + let cause = if obj.has_property("cause", context)? { + Some(obj.get("cause", context)?) + } else { + None + }; + + let kind = match error { + ErrorKind::Error => JsNativeErrorKind::Error, + ErrorKind::Eval => JsNativeErrorKind::Eval, + ErrorKind::Type => JsNativeErrorKind::Type, + ErrorKind::Range => JsNativeErrorKind::Range, + ErrorKind::Reference => JsNativeErrorKind::Reference, + ErrorKind::Syntax => JsNativeErrorKind::Syntax, + ErrorKind::Uri => JsNativeErrorKind::Uri, + ErrorKind::Aggregate => { + let errors = obj.get("errors", context)?; + let mut error_list = Vec::new(); + match errors.as_object() { + Some(errors) if errors.is_array() => { + let length = errors.length_of_array_like(context)?; + for i in 0..length { + error_list.push(errors.get(i, context)?.into()); + } + } + _ => { + return Err(JsNativeError::typ() + .with_message( + "field `errors` must be a valid Array object", + ) + .into()) + } + } + + JsNativeErrorKind::Aggregate(error_list) + } + }; + + return Ok(JsNativeError { + kind, + message, + cause, + }); + } + } + Err(JsNativeError::typ() + .with_message("failed to convert value to native error") + .with_cause(val) + .into()) + } + } + } + + pub fn as_opaque(&self) -> Option<&JsValue> { + match self.inner { + Repr::Native(_) => None, + Repr::Opaque(ref v) => Some(v), + } + } + + pub fn as_native(&self) -> Option<&JsNativeError> { + match self.inner { + Repr::Native(ref e) => Some(e), + Repr::Opaque(_) => None, + } + } +} + +impl From for JsError { + fn from(err: parser::ParseError) -> Self { + Self::from(JsNativeError::from(err)) + } +} + +impl From for JsError { + fn from(error: JsNativeError) -> Self { + Self { + inner: Repr::Native(error), + } + } +} + +impl From for JsError { + fn from(value: JsValue) -> Self { + Self { + inner: Repr::Opaque(value), + } + } +} + +impl From for JsError { + fn from(object: JsObject) -> Self { + Self { + inner: Repr::Opaque(object.into()), + } + } +} + +impl std::fmt::Display for JsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.inner { + Repr::Native(e) => e.fmt(f), + Repr::Opaque(v) => v.display().fmt(f), + } + } +} + +#[derive(Debug, Clone, Trace, Finalize)] +pub struct JsNativeError { + pub kind: JsNativeErrorKind, + #[unsafe_ignore_trace] + message: ViewStr, + cause: Option, +} + +impl JsNativeError { + fn new(kind: JsNativeErrorKind, message: ViewStr, cause: Option) -> Self { + Self { + kind, + message, + cause, + } + } + + pub fn aggregate(errors: Vec) -> Self { + Self::new(JsNativeErrorKind::Aggregate(errors), "".into(), None) + } + pub fn error() -> Self { + Self::new(JsNativeErrorKind::Error, "".into(), None) + } + pub fn eval() -> Self { + Self::new(JsNativeErrorKind::Eval, "".into(), None) + } + pub fn range() -> Self { + Self::new(JsNativeErrorKind::Range, "".into(), None) + } + pub fn reference() -> Self { + Self::new(JsNativeErrorKind::Reference, "".into(), None) + } + pub fn syntax() -> Self { + Self::new(JsNativeErrorKind::Syntax, "".into(), None) + } + pub fn typ() -> Self { + Self::new(JsNativeErrorKind::Type, "".into(), None) + } + pub fn uri() -> Self { + Self::new(JsNativeErrorKind::Uri, "".into(), None) + } + + #[must_use] + pub fn with_message(mut self, message: S) -> Self + where + S: Into>, + { + self.message = message.into().into(); + self + } + + #[must_use] + pub fn with_cause(mut self, cause: V) -> Self + where + V: Into, + { + self.cause = Some(cause.into()); + self + } + + pub fn message(&self) -> &str { + &self.message + } + + pub fn cause(&self) -> Option<&JsValue> { + self.cause.as_ref() + } + + pub fn to_value(&self, context: &mut Context) -> JsValue { + let Self { + kind, + message, + cause, + } = self; + let constructors = context.intrinsics().constructors(); + let prototype = match kind { + JsNativeErrorKind::Aggregate(_) => constructors.aggregate_error().prototype(), + JsNativeErrorKind::Error => constructors.error().prototype(), + JsNativeErrorKind::Eval => constructors.eval_error().prototype(), + JsNativeErrorKind::Range => constructors.range_error().prototype(), + JsNativeErrorKind::Reference => constructors.reference_error().prototype(), + JsNativeErrorKind::Syntax => constructors.syntax_error().prototype(), + JsNativeErrorKind::Type => constructors.type_error().prototype(), + JsNativeErrorKind::Uri => constructors.uri_error().prototype(), + }; + + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(kind.as_error_kind())); + + o.create_non_enumerable_data_property_or_throw("message", &**message, context); + + if let Some(cause) = cause { + o.create_non_enumerable_data_property_or_throw("cause", cause.clone(), context); + } + + if let JsNativeErrorKind::Aggregate(errors) = kind { + let errors = errors + .iter() + .map(|e| e.to_value(context)) + .collect::>(); + let errors = Array::create_array_from_list(errors, context); + o.define_property_or_throw( + "errors", + PropertyDescriptor::builder() + .configurable(true) + .enumerable(false) + .writable(true) + .value(errors), + context, + ) + .expect("The spec guarantees this succeeds for a newly created object "); + } + o.into() + } +} + +impl std::fmt::Display for JsNativeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { kind, message, .. } = self; + write!(f, "{kind}: {message}") + } +} + +impl From for JsNativeError { + fn from(err: parser::ParseError) -> Self { + Self::syntax().with_message(err.to_string()) + } +} + +#[derive(Debug, Clone, Trace, Finalize)] +#[non_exhaustive] +pub enum JsNativeErrorKind { + Aggregate(Vec), + Error, + Eval, + Range, + Reference, + Syntax, + Type, + Uri, +} + +impl JsNativeErrorKind { + fn as_error_kind(&self) -> ErrorKind { + match self { + JsNativeErrorKind::Aggregate(_) => ErrorKind::Aggregate, + JsNativeErrorKind::Error => ErrorKind::Error, + JsNativeErrorKind::Eval => ErrorKind::Eval, + JsNativeErrorKind::Range => ErrorKind::Range, + JsNativeErrorKind::Reference => ErrorKind::Reference, + JsNativeErrorKind::Syntax => ErrorKind::Syntax, + JsNativeErrorKind::Type => ErrorKind::Type, + JsNativeErrorKind::Uri => ErrorKind::Uri, + } + } +} + +impl std::fmt::Display for JsNativeErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + JsNativeErrorKind::Aggregate(_) => "AggregateError", + JsNativeErrorKind::Error => "Error", + JsNativeErrorKind::Eval => "EvalError", + JsNativeErrorKind::Range => "RangeError", + JsNativeErrorKind::Reference => "ReferenceError", + JsNativeErrorKind::Syntax => "SyntaxError", + JsNativeErrorKind::Type => "TypeError", + JsNativeErrorKind::Uri => "UriError", + } + .fmt(f) + } +} diff --git a/boa_engine/src/error/vos.rs b/boa_engine/src/error/vos.rs new file mode 100644 index 00000000000..cb528f290cf --- /dev/null +++ b/boa_engine/src/error/vos.rs @@ -0,0 +1,84 @@ +use std::{ + borrow::{Borrow, Cow}, + fmt::Display, + ops::Deref, + rc::Rc, +}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) enum ViewStr { + Static(&'static str), + Shared(Rc>), +} + +impl Display for ViewStr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (**self).fmt(f) + } +} + +impl From<&'static str> for ViewStr { + fn from(str: &'static str) -> Self { + Self::Static(str) + } +} + +impl From for ViewStr { + fn from(string: String) -> Self { + Self::Shared(Rc::new(string.into_boxed_str())) + } +} + +impl From> for ViewStr { + fn from(cow: Cow<'static, str>) -> Self { + match cow { + Cow::Borrowed(str) => str.into(), + Cow::Owned(string) => string.into(), + } + } +} + +impl Deref for ViewStr { + type Target = str; + + fn deref(&self) -> &Self::Target { + match self { + ViewStr::Static(s) => s, + ViewStr::Shared(sh) => sh, + } + } +} + +impl AsRef for ViewStr { + fn as_ref(&self) -> &str { + self + } +} + +impl Borrow for ViewStr { + fn borrow(&self) -> &str { + self + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use super::ViewStr; + + #[test] + fn create() { + let stat = ViewStr::from("hello"); + + assert!(matches!(stat, ViewStr::Static(_))); + + let shared = ViewStr::from(String::from("world")); + + assert!(matches!(shared, ViewStr::Shared(_))); + + let clone = shared.clone(); + + assert!(matches!(clone, ViewStr::Shared(rc) if Rc::strong_count(&rc) == 2)); + } +} diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index 9e6e3ea26ce..79597a68430 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -81,6 +81,7 @@ pub mod bytecompiler; pub mod class; pub mod context; pub mod environments; +pub mod error; pub mod job; pub mod object; pub mod property; @@ -96,7 +97,11 @@ mod tests; /// A convenience module that re-exports the most commonly-used Boa APIs pub mod prelude { - pub use crate::{object::JsObject, Context, JsBigInt, JsResult, JsString, JsValue}; + pub use crate::{ + error::{JsError, JsNativeError, JsNativeErrorKind}, + object::JsObject, + Context, JsBigInt, JsResult, JsString, JsValue, + }; } use std::result::Result as StdResult; @@ -104,11 +109,16 @@ use std::result::Result as StdResult; // Export things to root level #[doc(inline)] pub use crate::{ - bigint::JsBigInt, context::Context, string::JsString, symbol::JsSymbol, value::JsValue, + bigint::JsBigInt, + context::Context, + error::{JsError, JsNativeError, JsNativeErrorKind}, + string::JsString, + symbol::JsSymbol, + value::JsValue, }; /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) -pub type JsResult = StdResult; +pub type JsResult = StdResult; /// Execute the code using an existing `Context`. /// @@ -118,10 +128,9 @@ pub(crate) fn forward(context: &mut Context, src: S) -> String where S: AsRef<[u8]>, { - context.eval(src.as_ref()).map_or_else( - |e| format!("Uncaught {}", e.display()), - |v| v.display().to_string(), - ) + context + .eval(src.as_ref()) + .map_or_else(|e| format!("Uncaught {}", e), |v| v.display().to_string()) } /// Execute the code using an existing Context. @@ -152,7 +161,7 @@ pub(crate) fn exec>(src: T) -> String { match Context::default().eval(src_bytes) { Ok(value) => value.display().to_string(), - Err(error) => error.display().to_string(), + Err(error) => error.to_string(), } } diff --git a/boa_engine/src/object/internal_methods/array.rs b/boa_engine/src/object/internal_methods/array.rs index 2abc0b2de6a..d425a9301d0 100644 --- a/boa_engine/src/object/internal_methods/array.rs +++ b/boa_engine/src/object/internal_methods/array.rs @@ -1,4 +1,5 @@ use crate::{ + error::JsNativeError, object::JsObject, property::{PropertyDescriptor, PropertyKey}, Context, JsResult, @@ -126,7 +127,9 @@ fn array_set_length( // 5. If SameValueZero(newLen, numberLen) is false, throw a RangeError exception. #[allow(clippy::float_cmp)] if f64::from(new_len) != number_len { - return context.throw_range_error("bad length for array"); + return Err(JsNativeError::range() + .with_message("bad length for array") + .into()); } // 2. Let newLenDesc be a copy of Desc. diff --git a/boa_engine/src/object/internal_methods/proxy.rs b/boa_engine/src/object/internal_methods/proxy.rs index c70175923dc..2fbacd8e9c0 100644 --- a/boa_engine/src/object/internal_methods/proxy.rs +++ b/boa_engine/src/object/internal_methods/proxy.rs @@ -1,5 +1,6 @@ use crate::{ builtins::{array, object::Object}, + error::JsNativeError, object::{InternalObjectMethods, JsObject, JsPrototype}, property::{PropertyDescriptor, PropertyKey}, value::Type, @@ -61,7 +62,7 @@ pub(crate) fn proxy_exotic_get_prototype_of( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). let trap = if let Some(trap) = handler.get_method("getPrototypeOf", context)? { @@ -79,7 +80,11 @@ pub(crate) fn proxy_exotic_get_prototype_of( let handler_proto = match &handler_proto { JsValue::Object(obj) => Some(obj.clone()), JsValue::Null => None, - _ => return context.throw_type_error("Proxy trap result is neither object nor null"), + _ => { + return Err(JsNativeError::typ() + .with_message("Proxy trap result is neither object nor null") + .into()) + } }; // 9. Let extensibleTarget be ? IsExtensible(target). @@ -93,7 +98,9 @@ pub(crate) fn proxy_exotic_get_prototype_of( // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception. if handler_proto != target_proto { - return context.throw_type_error("Proxy trap returned unexpected prototype"); + return Err(JsNativeError::typ() + .with_message("Proxy trap returned unexpected prototype") + .into()); } // 13. Return handlerProto. @@ -120,7 +127,7 @@ pub(crate) fn proxy_exotic_set_prototype_of( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "setPrototypeOf"). let trap = if let Some(trap) = handler.get_method("setPrototypeOf", context)? { @@ -158,7 +165,9 @@ pub(crate) fn proxy_exotic_set_prototype_of( // 12. If SameValue(V, targetProto) is false, throw a TypeError exception. if val != target_proto { - return context.throw_type_error("Proxy trap failed to set prototype"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to set prototype") + .into()); } // 13. Return true. @@ -181,7 +190,7 @@ pub(crate) fn proxy_exotic_is_extensible(obj: &JsObject, context: &mut Context) .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "isExtensible"). let trap = if let Some(trap) = handler.get_method("isExtensible", context)? { @@ -202,7 +211,9 @@ pub(crate) fn proxy_exotic_is_extensible(obj: &JsObject, context: &mut Context) // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. if boolean_trap_result != target_result { - return context.throw_type_error("Proxy trap returned unexpected extensible value"); + return Err(JsNativeError::typ() + .with_message("Proxy trap returned unexpected extensible value") + .into()); } // 10. Return booleanTrapResult. @@ -228,7 +239,7 @@ pub(crate) fn proxy_exotic_prevent_extensions( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "preventExtensions"). let trap = if let Some(trap) = handler.get_method("preventExtensions", context)? { @@ -248,7 +259,9 @@ pub(crate) fn proxy_exotic_prevent_extensions( if boolean_trap_result && target.is_extensible(context)? { // a. Let extensibleTarget be ? IsExtensible(target). // b. If extensibleTarget is true, throw a TypeError exception. - return context.throw_type_error("Proxy trap failed to set extensible"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to set extensible") + .into()); } // 9. Return booleanTrapResult. @@ -275,7 +288,7 @@ pub(crate) fn proxy_exotic_get_own_property( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). let trap = if let Some(trap) = handler.get_method("getOwnPropertyDescriptor", context)? { @@ -295,7 +308,9 @@ pub(crate) fn proxy_exotic_get_own_property( // 8. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception. if !trap_result_obj.is_object() && !trap_result_obj.is_undefined() { - return context.throw_type_error("Proxy trap result is neither object nor undefined"); + return Err(JsNativeError::typ() + .with_message("Proxy trap result is neither object nor undefined") + .into()); } // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). @@ -306,17 +321,19 @@ pub(crate) fn proxy_exotic_get_own_property( if let Some(desc) = target_desc { // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. if !desc.expect_configurable() { - return context.throw_type_error( - "Proxy trap result is undefined adn target result is not configurable", - ); + return Err(JsNativeError::typ() + .with_message( + "Proxy trap result is undefined adn target result is not configurable", + ) + .into()); } // c. Let extensibleTarget be ? IsExtensible(target). // d. If extensibleTarget is false, throw a TypeError exception. if !target.is_extensible(context)? { - return context.throw_type_error( - "Proxy trap result is undefined and target is not extensible", - ); + return Err(JsNativeError::typ() + .with_message("Proxy trap result is undefined and target is not extensible") + .into()); } // e. Return undefined. return Ok(None); @@ -342,7 +359,9 @@ pub(crate) fn proxy_exotic_get_own_property( result_desc.clone(), target_desc.clone(), ) { - return context.throw_type_error("Proxy trap returned unexpected property"); + return Err(JsNativeError::typ() + .with_message("Proxy trap returned unexpected property") + .into()); } // 16. If resultDesc.[[Configurable]] is false, then @@ -355,16 +374,18 @@ pub(crate) fn proxy_exotic_get_own_property( // i. If targetDesc.[[Writable]] is true, throw a TypeError exception. if desc.expect_writable() { return - context.throw_type_error("Proxy trap result is writable and not configurable while target result is not configurable") + Err(JsNativeError::typ().with_message("Proxy trap result is writable and not configurable while target result is not configurable").into()) ; } } } // i. Throw a TypeError exception. _ => { - return context.throw_type_error( - "Proxy trap result is not configurable and target result is undefined", - ) + return Err(JsNativeError::typ() + .with_message( + "Proxy trap result is not configurable and target result is undefined", + ) + .into()) } } } @@ -394,7 +415,7 @@ pub(crate) fn proxy_exotic_define_own_property( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "defineProperty"). let trap = if let Some(trap) = handler.get_method("defineProperty", context)? { @@ -435,12 +456,16 @@ pub(crate) fn proxy_exotic_define_own_property( None => { // a. If extensibleTarget is false, throw a TypeError exception. if !extensible_target { - return context.throw_type_error("Proxy trap failed to set property"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to set property") + .into()); } // b. If settingConfigFalse is true, throw a TypeError exception. if setting_config_false { - return context.throw_type_error("Proxy trap failed to set property"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to set property") + .into()); } } // 15. Else, @@ -451,14 +476,16 @@ pub(crate) fn proxy_exotic_define_own_property( desc.clone(), Some(target_desc.clone()), ) { - return context.throw_type_error("Proxy trap set property to unexpected value"); + return Err(JsNativeError::typ() + .with_message("Proxy trap set property to unexpected value") + .into()); } // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception. if setting_config_false && target_desc.expect_configurable() { - return context.throw_type_error( - "Proxy trap set property with unexpected configurable field", - ); + return Err(JsNativeError::typ() + .with_message("Proxy trap set property with unexpected configurable field") + .into()); } // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then @@ -469,9 +496,9 @@ pub(crate) fn proxy_exotic_define_own_property( // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. if let Some(writable) = desc.writable() { if !writable { - return context.throw_type_error( - "Proxy trap set property with unexpected writable field", - ); + return Err(JsNativeError::typ() + .with_message("Proxy trap set property with unexpected writable field") + .into()); } } } @@ -502,7 +529,7 @@ pub(crate) fn proxy_exotic_has_property( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "has"). let trap = if let Some(trap) = handler.get_method("has", context)? { @@ -531,13 +558,17 @@ pub(crate) fn proxy_exotic_has_property( if let Some(target_desc) = target_desc { // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. if !target_desc.expect_configurable() { - return context.throw_type_error("Proxy trap returned unexpected property"); + return Err(JsNativeError::typ() + .with_message("Proxy trap returned unexpected property") + .into()); } // ii. Let extensibleTarget be ? IsExtensible(target). // iii. If extensibleTarget is false, throw a TypeError exception. if !target.is_extensible(context)? { - return context.throw_type_error("Proxy trap returned unexpected property"); + return Err(JsNativeError::typ() + .with_message("Proxy trap returned unexpected property") + .into()); } } } @@ -567,7 +598,7 @@ pub(crate) fn proxy_exotic_get( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "get"). let trap = if let Some(trap) = handler.get_method("get", context)? { @@ -595,8 +626,9 @@ pub(crate) fn proxy_exotic_get( if target_desc.is_data_descriptor() && !target_desc.expect_writable() { // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. if !JsValue::same_value(&trap_result, target_desc.expect_value()) { - return context - .throw_type_error("Proxy trap returned unexpected data descriptor"); + return Err(JsNativeError::typ() + .with_message("Proxy trap returned unexpected data descriptor") + .into()); } } @@ -604,8 +636,9 @@ pub(crate) fn proxy_exotic_get( if target_desc.is_accessor_descriptor() && target_desc.expect_get().is_undefined() { // i. If trapResult is not undefined, throw a TypeError exception. if !trap_result.is_undefined() { - return context - .throw_type_error("Proxy trap returned unexpected accessor descriptor"); + return Err(JsNativeError::typ() + .with_message("Proxy trap returned unexpected accessor descriptor") + .into()); } } } @@ -637,7 +670,7 @@ pub(crate) fn proxy_exotic_set( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "set"). let trap = if let Some(trap) = handler.get_method("set", context)? { @@ -671,7 +704,9 @@ pub(crate) fn proxy_exotic_set( if target_desc.is_data_descriptor() && !target_desc.expect_writable() { // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception. if !JsValue::same_value(&value, target_desc.expect_value()) { - return context.throw_type_error("Proxy trap set unexpected data descriptor"); + return Err(JsNativeError::typ() + .with_message("Proxy trap set unexpected data descriptor") + .into()); } } @@ -680,8 +715,9 @@ pub(crate) fn proxy_exotic_set( // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. match target_desc.set() { None | Some(&JsValue::Undefined) => { - return context - .throw_type_error("Proxy trap set unexpected accessor descriptor"); + return Err(JsNativeError::typ() + .with_message("Proxy trap set unexpected accessor descriptor") + .into()); } _ => {} } @@ -713,7 +749,7 @@ pub(crate) fn proxy_exotic_delete( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "deleteProperty"). let trap = if let Some(trap) = handler.get_method("deleteProperty", context)? { @@ -744,7 +780,9 @@ pub(crate) fn proxy_exotic_delete( // 11. If targetDesc.[[Configurable]] is false, throw a TypeError exception. Some(target_desc) => { if !target_desc.expect_configurable() { - return context.throw_type_error("Proxy trap failed to delete property"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to delete property") + .into()); } } } @@ -752,7 +790,9 @@ pub(crate) fn proxy_exotic_delete( // 12. Let extensibleTarget be ? IsExtensible(target). // 13. If extensibleTarget is false, throw a TypeError exception. if !target.is_extensible(context)? { - return context.throw_type_error("Proxy trap failed to delete property"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to delete property") + .into()); } // 14. Return true. @@ -778,7 +818,7 @@ pub(crate) fn proxy_exotic_own_property_keys( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "ownKeys"). let trap = if let Some(trap) = handler.get_method("ownKeys", context)? { @@ -803,17 +843,17 @@ pub(crate) fn proxy_exotic_own_property_keys( match value { JsValue::String(s) => { if !unchecked_result_keys.insert(s.clone().into()) { - return context.throw_type_error( - "Proxy trap result contains duplicate string property keys", - ); + return Err(JsNativeError::typ() + .with_message("Proxy trap result contains duplicate string property keys") + .into()); } trap_result.push(s.clone().into()); } JsValue::Symbol(s) => { if !unchecked_result_keys.insert(s.clone().into()) { - return context.throw_type_error( - "Proxy trap result contains duplicate symbol property keys", - ); + return Err(JsNativeError::typ() + .with_message("Proxy trap result contains duplicate symbol property keys") + .into()); } trap_result.push(s.clone().into()); } @@ -863,9 +903,9 @@ pub(crate) fn proxy_exotic_own_property_keys( // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. // b. Remove key from uncheckedResultKeys. if !unchecked_result_keys.remove(&key) { - return context.throw_type_error( - "Proxy trap failed to return all non-configurable property keys", - ); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to return all non-configurable property keys") + .into()); } } @@ -879,14 +919,17 @@ pub(crate) fn proxy_exotic_own_property_keys( // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. // b. Remove key from uncheckedResultKeys. if !unchecked_result_keys.remove(&key) { - return context - .throw_type_error("Proxy trap failed to return all configurable property keys"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to return all configurable property keys") + .into()); } } // 22. If uncheckedResultKeys is not empty, throw a TypeError exception. if !unchecked_result_keys.is_empty() { - return context.throw_type_error("Proxy trap failed to return all property keys"); + return Err(JsNativeError::typ() + .with_message("Proxy trap failed to return all property keys") + .into()); } // 23. Return trapResult. @@ -913,7 +956,7 @@ fn proxy_exotic_call( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Let trap be ? GetMethod(handler, "apply"). let trap = if let Some(trap) = handler.get_method("apply", context)? { @@ -955,7 +998,7 @@ fn proxy_exotic_construct( .borrow() .as_proxy() .expect("Proxy object internal internal method called on non-proxy object") - .try_data(context)?; + .try_data()?; // 5. Assert: IsConstructor(target) is true. assert!(target.is_constructor()); @@ -985,7 +1028,7 @@ fn proxy_exotic_construct( // 10. If Type(newObj) is not Object, throw a TypeError exception. let new_obj = new_obj.as_object().cloned().ok_or_else(|| { - context.construct_type_error("Proxy trap constructor returned non-object value") + JsNativeError::typ().with_message("Proxy trap constructor returned non-object value") })?; // 11. Return newObj. diff --git a/boa_engine/src/object/jsarray.rs b/boa_engine/src/object/jsarray.rs index ddb7fcedd55..90770738818 100644 --- a/boa_engine/src/object/jsarray.rs +++ b/boa_engine/src/object/jsarray.rs @@ -1,5 +1,6 @@ use crate::{ builtins::Array, + error::JsNativeError, object::{JsFunction, JsObject, JsObjectType}, value::IntoOrUndefined, Context, JsResult, JsString, JsValue, @@ -38,11 +39,13 @@ impl JsArray { /// /// This does not clone the fields of the array, it only does a shallow clone of the object. #[inline] - pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_array() { Ok(Self { inner: object }) } else { - context.throw_type_error("object is not an Array") + Err(JsNativeError::typ() + .with_message("object is not an Array") + .into()) } } @@ -112,7 +115,7 @@ impl JsArray { .cloned() .expect("Array.prototype.filter should always return object"); - Self::from_object(object, context) + Self::from_object(object) } #[inline] @@ -234,7 +237,7 @@ impl JsArray { .cloned() .expect("Array.prototype.filter should always return object"); - Self::from_object(object, context) + Self::from_object(object) } #[inline] @@ -253,7 +256,7 @@ impl JsArray { .cloned() .expect("Array.prototype.map should always return object"); - Self::from_object(object, context) + Self::from_object(object) } #[inline] @@ -319,7 +322,7 @@ impl JsArray { .cloned() .expect("Array.prototype.slice should always return object"); - Self::from_object(object, context) + Self::from_object(object) } #[inline] diff --git a/boa_engine/src/object/jsarraybuffer.rs b/boa_engine/src/object/jsarraybuffer.rs index 95d6514af06..f09c2c71ff3 100644 --- a/boa_engine/src/object/jsarraybuffer.rs +++ b/boa_engine/src/object/jsarraybuffer.rs @@ -1,6 +1,7 @@ use crate::{ builtins::array_buffer::ArrayBuffer, context::intrinsics::StandardConstructors, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, JsObject, JsObjectType, ObjectData, }, @@ -79,11 +80,13 @@ impl JsArrayBuffer { /// /// This does not clone the fields of the array buffer, it only does a shallow clone of the object. #[inline] - pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_array_buffer() { Ok(Self { inner: object }) } else { - context.throw_type_error("object is not an ArrayBuffer") + Err(JsNativeError::typ() + .with_message("object is not an ArrayBuffer") + .into()) } } diff --git a/boa_engine/src/object/jsmap.rs b/boa_engine/src/object/jsmap.rs index 885929c708d..71969e29e23 100644 --- a/boa_engine/src/object/jsmap.rs +++ b/boa_engine/src/object/jsmap.rs @@ -2,6 +2,7 @@ use crate::{ builtins::map::{add_entries_from_iterable, ordered_map::OrderedMap}, builtins::Map, + error::JsNativeError, object::{JsFunction, JsMapIterator, JsObject, JsObjectType, ObjectData}, Context, JsResult, JsValue, }; @@ -149,7 +150,7 @@ impl JsMap { /// ); /// /// // Create `JsMap` object with incoming object. - /// let js_map = JsMap::from_object(some_object, context).unwrap(); + /// let js_map = JsMap::from_object(some_object).unwrap(); /// /// ``` /// @@ -165,15 +166,17 @@ impl JsMap { /// let some_object = JsArray::new(context); /// /// // Some object is an Array object, not a map object - /// assert!(JsMap::from_object(some_object.into(), context).is_err()); + /// assert!(JsMap::from_object(some_object.into()).is_err()); /// /// ``` #[inline] - pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_map() { Ok(Self { inner: object }) } else { - context.throw_type_error("object is not a Map") + Err(JsNativeError::typ() + .with_message("object is not a Map") + .into()) } } @@ -192,7 +195,7 @@ impl JsMap { let iterator_record = Map::entries(&self.inner.clone().into(), &[], context)? .get_iterator(context, None, None)?; let map_iterator_object = iterator_record.iterator(); - JsMapIterator::from_object(map_iterator_object.clone(), context) + JsMapIterator::from_object(map_iterator_object.clone()) } /// Returns a new [`JsMapIterator`] object that yields the `key` for each element within the [`JsMap`] in insertion order. @@ -201,7 +204,7 @@ impl JsMap { let iterator_record = Map::keys(&self.inner.clone().into(), &[], context)? .get_iterator(context, None, None)?; let map_iterator_object = iterator_record.iterator(); - JsMapIterator::from_object(map_iterator_object.clone(), context) + JsMapIterator::from_object(map_iterator_object.clone()) } /// Inserts a new entry into the [`JsMap`] object @@ -396,7 +399,7 @@ impl JsMap { let iterator_record = Map::values(&self.inner.clone().into(), &[], context)? .get_iterator(context, None, None)?; let map_iterator_object = iterator_record.iterator(); - JsMapIterator::from_object(map_iterator_object.clone(), context) + JsMapIterator::from_object(map_iterator_object.clone()) } } diff --git a/boa_engine/src/object/jsmap_iterator.rs b/boa_engine/src/object/jsmap_iterator.rs index ae19562cc2d..dffa23c1d3b 100644 --- a/boa_engine/src/object/jsmap_iterator.rs +++ b/boa_engine/src/object/jsmap_iterator.rs @@ -1,6 +1,7 @@ //! This module implements a wrapper for the `MapIterator` object use crate::{ builtins::map::map_iterator::MapIterator, + error::JsNativeError, object::{JsObject, JsObjectType}, Context, JsResult, JsValue, }; @@ -17,11 +18,13 @@ pub struct JsMapIterator { impl JsMapIterator { /// Create a [`JsMapIterator`] from a [`JsObject`]. If object is not a `MapIterator`, throw `TypeError` #[inline] - pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_map_iterator() { Ok(Self { inner: object }) } else { - context.throw_type_error("object is not a MapIterator") + Err(JsNativeError::typ() + .with_message("object is not a MapIterator") + .into()) } } diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index 4f1b2f4809b..d259b3012cf 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -4,6 +4,7 @@ use super::{JsPrototype, NativeObject, Object, PropertyMap}; use crate::{ + error::JsNativeError, object::{ObjectData, ObjectKind}, property::{PropertyDescriptor, PropertyKey}, value::PreferredType, @@ -186,7 +187,9 @@ impl JsObject { } // 6. Throw a TypeError exception. - context.throw_type_error("cannot convert object to primitive value") + Err(JsNativeError::typ() + .with_message("cannot convert object to primitive value") + .into()) } /// Return `true` if it is a native object and the native type is `T`. @@ -546,7 +549,9 @@ impl JsObject { // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception. // todo: extract IsCallable to be callable from Value if !getter.is_undefined() && getter.as_object().map_or(true, |o| !o.is_callable()) { - return context.throw_type_error("Property descriptor getter must be callable"); + return Err(JsNativeError::typ() + .with_message("Property descriptor getter must be callable") + .into()); } // c. Set desc.[[Get]] to getter. Some(getter) @@ -562,7 +567,9 @@ impl JsObject { // 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception. // todo: extract IsCallable to be callable from Value if !setter.is_undefined() && setter.as_object().map_or(true, |o| !o.is_callable()) { - return context.throw_type_error("Property descriptor setter must be callable"); + return Err(JsNativeError::typ() + .with_message("Property descriptor setter must be callable") + .into()); } // 14.c. Set desc.[[Set]] to setter. Some(setter) @@ -573,10 +580,12 @@ impl JsObject { // 15. If desc.[[Get]] is present or desc.[[Set]] is present, then ... // a. If desc.[[Value]] is present or desc.[[Writable]] is present, throw a TypeError exception. if get.as_ref().or(set.as_ref()).is_some() && desc.inner().is_data_descriptor() { - return context.throw_type_error( - "Invalid property descriptor.\ + return Err(JsNativeError::typ() + .with_message( + "Invalid property descriptor.\ Cannot both specify accessors and a value or writable attribute", - ); + ) + .into()); } desc = desc.maybe_get(get).maybe_set(set); diff --git a/boa_engine/src/object/jsset.rs b/boa_engine/src/object/jsset.rs index 4f521a66376..7273594bb56 100644 --- a/boa_engine/src/object/jsset.rs +++ b/boa_engine/src/object/jsset.rs @@ -4,6 +4,7 @@ use boa_gc::{Finalize, Trace}; use crate::{ builtins::Set, + error::JsNativeError, object::{JsFunction, JsObject, JsObjectType, JsSetIterator}, Context, JsResult, JsValue, }; @@ -30,8 +31,8 @@ impl JsSet { /// /// Same as JavaScript's `set.size`. #[inline] - pub fn size(&self, context: &mut Context) -> JsResult { - Set::get_size(&self.inner.clone().into(), context) + pub fn size(&self) -> JsResult { + Set::get_size(&self.inner.clone().into()) } /// Appends value to the Set object. @@ -76,7 +77,8 @@ impl JsSet { { match Set::delete(&self.inner.clone().into(), &[value.into()], context)? { JsValue::Boolean(bool) => Ok(bool), - _ => Err(JsValue::Undefined), + // TODO: find a better solution to this, without impacting perf + _ => unreachable!("`delete` must always return a bool"), } } @@ -91,7 +93,8 @@ impl JsSet { { match Set::has(&self.inner.clone().into(), &[value.into()], context)? { JsValue::Boolean(bool) => Ok(bool), - _ => Err(JsValue::Undefined), + // TODO: find a better solution to this, without impacting perf + _ => unreachable!("`has` must always return a bool"), } } @@ -104,7 +107,7 @@ impl JsSet { let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? .get_iterator(context, None, None)?; - JsSetIterator::from_object(iterator_object.iterator().clone(), context) + JsSetIterator::from_object(iterator_object.iterator().clone()) } /// Alias for `Set.prototype.values()` @@ -117,7 +120,7 @@ impl JsSet { let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? .get_iterator(context, None, None)?; - JsSetIterator::from_object(iterator_object.iterator().clone(), context) + JsSetIterator::from_object(iterator_object.iterator().clone()) } /// Calls callbackFn once for each value present in the Set object, @@ -141,11 +144,13 @@ impl JsSet { /// Utility: Creates `JsSet` from `JsObject`, if not a Set throw `TypeError`. #[inline] - pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_set() { Ok(Self { inner: object }) } else { - context.throw_error("Object is not a Set") + Err(JsNativeError::typ() + .with_message("Object is not a Set") + .into()) } } diff --git a/boa_engine/src/object/jsset_iterator.rs b/boa_engine/src/object/jsset_iterator.rs index 64f06ad0f84..8d893f54991 100644 --- a/boa_engine/src/object/jsset_iterator.rs +++ b/boa_engine/src/object/jsset_iterator.rs @@ -4,6 +4,7 @@ use boa_gc::{Finalize, Trace}; use crate::{ builtins::SetIterator, + error::JsNativeError, object::{JsObject, JsObjectType}, Context, JsResult, JsValue, }; @@ -17,11 +18,13 @@ pub struct JsSetIterator { impl JsSetIterator { /// Create a `JsSetIterator` from a `JsObject`. /// If object is not a `SetIterator`, throw `TypeError`. - pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_set_iterator() { Ok(Self { inner: object }) } else { - context.throw_type_error("object is not a SetIterator") + Err(JsNativeError::typ() + .with_message("object is not a SetIterator") + .into()) } } /// Advances the `JsSetIterator` and gets the next result in the `JsSet`. diff --git a/boa_engine/src/object/jstypedarray.rs b/boa_engine/src/object/jstypedarray.rs index 6fb5bc60bfd..fede5a8b1f7 100644 --- a/boa_engine/src/object/jstypedarray.rs +++ b/boa_engine/src/object/jstypedarray.rs @@ -1,5 +1,6 @@ use crate::{ builtins::typed_array::TypedArray, + error::JsNativeError, object::{JsArrayBuffer, JsFunction, JsObject, JsObjectType}, value::IntoOrUndefined, Context, JsResult, JsString, JsValue, @@ -18,13 +19,15 @@ impl JsTypedArray { /// /// This does not clone the fields of the typed array, it only does a shallow clone of the object. #[inline] - pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_typed_array() { Ok(Self { inner: object.into(), }) } else { - context.throw_type_error("object is not a TypedArray") + Err(JsNativeError::typ() + .with_message("object is not a TypedArray") + .into()) } } diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 082d9264b4d..9c8cf790301 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -27,6 +27,7 @@ use crate::{ array::array_iterator::ArrayIterator, array_buffer::ArrayBuffer, async_generator::AsyncGenerator, + error::ErrorKind, function::arguments::Arguments, function::{ arguments::ParameterMap, BoundFunction, Captures, ConstructorKind, Function, @@ -45,6 +46,7 @@ use crate::{ DataView, Date, Promise, RegExp, }, context::intrinsics::StandardConstructor, + error::JsNativeError, property::{Attribute, PropertyDescriptor, PropertyKey}, Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue, }; @@ -188,7 +190,7 @@ pub enum ObjectKind { StringIterator(StringIterator), Number(f64), Symbol(JsSymbol), - Error, + Error(ErrorKind), Ordinary, Proxy(Proxy), Date(Date), @@ -231,7 +233,7 @@ unsafe impl Trace for ObjectKind { | Self::String(_) | Self::Date(_) | Self::Array - | Self::Error + | Self::Error(_) | Self::Ordinary | Self::Global | Self::Number(_) @@ -450,9 +452,9 @@ impl ObjectData { } /// Create the `Error` object data - pub fn error() -> Self { + pub fn error(error: ErrorKind) -> Self { Self { - kind: ObjectKind::Error, + kind: ObjectKind::Error(error), internal_methods: &ORDINARY_INTERNAL_METHODS, } } @@ -555,7 +557,7 @@ impl Display for ObjectKind { Self::String(_) => "String", Self::StringIterator(_) => "StringIterator", Self::Symbol(_) => "Symbol", - Self::Error => "Error", + Self::Error(_) => "Error", Self::Ordinary => "Ordinary", Self::Proxy(_) => "Proxy", Self::Boolean(_) => "Boolean", @@ -1039,12 +1041,22 @@ impl Object { matches!( self.data, ObjectData { - kind: ObjectKind::Error, + kind: ObjectKind::Error(_), .. } ) } + pub fn as_error(&self) -> Option { + match self.data { + ObjectData { + kind: ObjectKind::Error(e), + .. + } => Some(e), + _ => None, + } + } + /// Checks if it a Boolean object. #[inline] pub fn is_boolean(&self) -> bool { @@ -1647,7 +1659,8 @@ impl<'context> FunctionBuilder<'context> { function: Box::new(move |this, args, captures: Captures, context| { let mut captures = captures.as_mut_any(); let captures = captures.downcast_mut::().ok_or_else(|| { - context.construct_type_error("cannot downcast `Captures` to given type") + JsNativeError::typ() + .with_message("cannot downcast `Captures` to given type") })?; function(this, args, captures, context) }), diff --git a/boa_engine/src/object/operations.rs b/boa_engine/src/object/operations.rs index 85d852edecf..bbe4d286633 100644 --- a/boa_engine/src/object/operations.rs +++ b/boa_engine/src/object/operations.rs @@ -1,6 +1,7 @@ use crate::{ builtins::Array, context::intrinsics::{StandardConstructor, StandardConstructors}, + error::JsNativeError, object::JsObject, property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind}, symbol::WellKnownSymbols, @@ -91,7 +92,9 @@ impl JsObject { let success = self.__set__(key.clone(), value.into(), self.clone().into(), context)?; // 5. If success is false and Throw is true, throw a TypeError exception. if !success && throw { - return context.throw_type_error(format!("cannot set non-writable property: {key}")); + return Err(JsNativeError::typ() + .with_message(format!("cannot set non-writable property: {key}")) + .into()); } // 6. Return success. Ok(success) @@ -150,7 +153,9 @@ impl JsObject { let success = self.create_data_property(key.clone(), value, context)?; // 4. If success is false, throw a TypeError exception. if !success { - return context.throw_type_error(format!("cannot redefine property: {key}")); + return Err(JsNativeError::typ() + .with_message(format!("cannot redefine property: {key}")) + .into()); } // 5. Return success. Ok(success) @@ -217,7 +222,9 @@ impl JsObject { let success = self.__define_own_property__(key.clone(), desc.into(), context)?; // 4. If success is false, throw a TypeError exception. if !success { - return context.throw_type_error(format!("cannot redefine property: {key}")); + return Err(JsNativeError::typ() + .with_message(format!("cannot redefine property: {key}")) + .into()); } // 5. Return success. Ok(success) @@ -241,7 +248,9 @@ impl JsObject { let success = self.__delete__(&key, context)?; // 4. If success is false, throw a TypeError exception. if !success { - return context.throw_type_error(format!("cannot delete property: {key}")); + return Err(JsNativeError::typ() + .with_message(format!("cannot delete property: {key}")) + .into()); } // 5. Return success. Ok(success) @@ -303,7 +312,7 @@ impl JsObject { // 1. If argumentsList is not present, set argumentsList to a new empty List. // 2. If IsCallable(F) is false, throw a TypeError exception. if !self.is_callable() { - return context.throw_type_error("not a function"); + return Err(JsNativeError::typ().with_message("not a function").into()); } // 3. Return ? F.[[Call]](V, argumentsList). self.__call__(this, args, context) @@ -496,7 +505,9 @@ impl JsObject { let c = if let Some(c) = c.as_object() { c } else { - return context.throw_type_error("property 'constructor' is not an object"); + return Err(JsNativeError::typ() + .with_message("property 'constructor' is not an object") + .into()); }; // 5. Let S be ? Get(C, @@species). @@ -511,7 +522,9 @@ impl JsObject { // 8. Throw a TypeError exception. match s.as_object() { Some(obj) if obj.is_constructor() => Ok(obj.clone()), - _ => context.throw_type_error("property 'constructor' is not a constructor"), + _ => Err(JsNativeError::typ() + .with_message("property 'constructor' is not a constructor") + .into()), } } @@ -600,9 +613,9 @@ impl JsObject { // 5. Return func. JsValue::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())), // 4. If IsCallable(func) is false, throw a TypeError exception. - _ => { - context.throw_type_error("value returned for property of object is not a function") - } + _ => Err(JsNativeError::typ() + .with_message("value returned for property of object is not a function") + .into()), } } @@ -628,7 +641,7 @@ impl JsObject { if let Some(proxy) = object.as_proxy() { // a. If argument.[[ProxyHandler]] is null, throw a TypeError exception. // b. Let target be argument.[[ProxyTarget]]. - let (target, _) = proxy.try_data(context)?; + let (target, _) = proxy.try_data()?; // c. Return ? IsArray(target). return target.is_array_abstract(context); @@ -727,9 +740,9 @@ impl JsValue { }; // 2. If Type(obj) is not Object, throw a TypeError exception. - let obj = self - .as_object() - .ok_or_else(|| context.construct_type_error("cannot create list from a primitive"))?; + let obj = self.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("cannot create list from a primitive") + })?; // 3. Let len be ? LengthOfArrayLike(obj). let len = obj.length_of_array_like(context)?; @@ -745,7 +758,7 @@ impl JsValue { let next = obj.get(index, context)?; // c. If Type(next) is not an element of elementTypes, throw a TypeError exception. if !types.contains(&next.get_type()) { - return context.throw_type_error("bad type"); + return Err(JsNativeError::typ().with_message("bad type").into()); } // d. Append next as the last element of list. list.push(next.clone()); @@ -819,8 +832,9 @@ impl JsValue { obj } else { // 5. If Type(P) is not Object, throw a TypeError exception. - return context - .throw_type_error("function has non-object prototype in instanceof check"); + return Err(JsNativeError::typ() + .with_message("function has non-object prototype in instanceof check") + .into()); }; // 6. Repeat, diff --git a/boa_engine/src/tests.rs b/boa_engine/src/tests.rs index 24cbb02005d..2ef2c6c8bac 100644 --- a/boa_engine/src/tests.rs +++ b/boa_engine/src/tests.rs @@ -467,7 +467,7 @@ fn test_invalid_break() { let string = forward(&mut context, src); assert_eq!( string, - "Uncaught \"SyntaxError\": \"unlabeled break must be inside loop or switch\"" + "Uncaught SyntaxError: unlabeled break must be inside loop or switch" ); } @@ -482,7 +482,7 @@ fn test_invalid_continue_target() { let string = forward(&mut context, src); assert_eq!( string, - "Uncaught \"SyntaxError\": \"Cannot use the undeclared label 'nonexistent'\"" + "Uncaught SyntaxError: Cannot use the undeclared label 'nonexistent'" ); } @@ -490,10 +490,7 @@ fn test_invalid_continue_target() { fn test_invalid_continue() { let mut context = Context::default(); let string = forward(&mut context, r"continue;"); - assert_eq!( - string, - "Uncaught \"SyntaxError\": \"continue must be inside loop\"" - ); + assert_eq!(string, "Uncaught SyntaxError: continue must be inside loop"); } #[test] @@ -542,10 +539,10 @@ fn unary_pre() { #[test] fn invalid_unary_access() { check_output(&[ - TestAction::TestStartsWith("++[];", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("[]++;", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("--[];", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("[]--;", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("++[];", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("[]++;", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("--[];", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("[]--;", "Uncaught SyntaxError: "), ]); } @@ -851,7 +848,7 @@ mod in_operator { check_output(&[TestAction::TestEq( scenario, - "Uncaught \"TypeError\": \"not a constructor\"", + "Uncaught TypeError: not a constructor", )]); } @@ -1228,20 +1225,6 @@ fn calling_function_with_unspecified_arguments() { assert_eq!(forward(&mut context, scenario), "undefined"); } -#[test] -fn to_object() { - let mut context = Context::default(); - - assert!(JsValue::undefined() - .to_object(&mut context) - .unwrap_err() - .is_object()); - assert!(JsValue::null() - .to_object(&mut context) - .unwrap_err() - .is_object()); -} - #[test] fn check_this_binding_in_object_literal() { let mut context = Context::default(); @@ -1395,7 +1378,7 @@ fn assignment_to_non_assignable() { for case in &test_cases { let string = forward(&mut context, case); - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + assert!(string.starts_with("Uncaught SyntaxError: ")); assert!(string.contains("1:3")); } } @@ -1403,15 +1386,15 @@ fn assignment_to_non_assignable() { #[test] fn assignment_to_non_assignable_ctd() { check_output(&[ - TestAction::TestStartsWith("(()=>{})() -= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() *= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() /= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() %= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() &= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() ^= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() |= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() += 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() = 5", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("(()=>{})() -= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() *= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() /= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() %= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() &= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() ^= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() |= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() += 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() = 5", "Uncaught SyntaxError: "), ]); } @@ -1426,7 +1409,7 @@ fn multicharacter_assignment_to_non_assignable() { for case in &test_cases { let string = forward(&mut context, case); - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + assert!(string.starts_with("Uncaught SyntaxError: ")); assert!(string.contains("1:3")); } } @@ -1434,9 +1417,9 @@ fn multicharacter_assignment_to_non_assignable() { #[test] fn multicharacter_assignment_to_non_assignable_ctd() { check_output(&[ - TestAction::TestStartsWith("(()=>{})() **= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() <<= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() >>= 5", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("(()=>{})() **= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() <<= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() >>= 5", "Uncaught SyntaxError: "), ]); } @@ -1450,7 +1433,7 @@ fn multicharacter_bitwise_assignment_to_non_assignable() { for case in &test_cases { let string = forward(&mut context, case); - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + assert!(string.starts_with("Uncaught SyntaxError: ")); assert!(string.contains("1:3")); } } @@ -1458,27 +1441,27 @@ fn multicharacter_bitwise_assignment_to_non_assignable() { #[test] fn multicharacter_bitwise_assignment_to_non_assignable_ctd() { check_output(&[ - TestAction::TestStartsWith("(()=>{})() >>>= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() &&= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() ||= 5", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("(()=>{})() ??= 5", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("(()=>{})() >>>= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() &&= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() ||= 5", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("(()=>{})() ??= 5", "Uncaught SyntaxError: "), ]); } #[test] fn assign_to_array_decl() { check_output(&[ - TestAction::TestStartsWith("[1] = [2]", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("[3, 5] = [7, 8]", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("[6, 8] = [2]", "Uncaught \"SyntaxError\": "), - TestAction::TestStartsWith("[6] = [2, 9]", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("[1] = [2]", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("[3, 5] = [7, 8]", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("[6, 8] = [2]", "Uncaught SyntaxError: "), + TestAction::TestStartsWith("[6] = [2, 9]", "Uncaught SyntaxError: "), ]); } #[test] fn assign_to_object_decl() { const ERR_MSG: &str = - "Uncaught \"SyntaxError\": \"unexpected token '=', primary expression at line 1, col 8\""; + "Uncaught SyntaxError: unexpected token '=', primary expression at line 1, col 8"; let mut context = Context::default(); @@ -1522,7 +1505,7 @@ fn test_conditional_op() { #[test] fn test_identifier_op() { let scenario = "break = 1"; - assert_eq!(&exec(scenario), "\"SyntaxError\": \"expected token \'identifier\', got \'=\' in binding identifier at line 1, col 7\""); + assert_eq!(&exec(scenario), "SyntaxError: expected token \'identifier\', got \'=\' in binding identifier at line 1, col 7"); } #[test] @@ -1537,7 +1520,7 @@ fn test_strict_mode_octal() { check_output(&[TestAction::TestStartsWith( scenario, - "Uncaught \"SyntaxError\": ", + "Uncaught SyntaxError: ", )]); } @@ -1557,7 +1540,7 @@ fn test_strict_mode_with() { check_output(&[TestAction::TestStartsWith( scenario, - "Uncaught \"SyntaxError\": ", + "Uncaught SyntaxError: ", )]); } @@ -1574,7 +1557,7 @@ fn test_strict_mode_delete() { check_output(&[TestAction::TestStartsWith( scenario, - "Uncaught \"SyntaxError\": ", + "Uncaught SyntaxError: ", )]); } @@ -1603,7 +1586,7 @@ fn test_strict_mode_reserved_name() { let string = forward(&mut context, &scenario); - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + assert!(string.starts_with("Uncaught SyntaxError: ")); } } @@ -1619,7 +1602,7 @@ fn test_strict_mode_dup_func_parameters() { check_output(&[TestAction::TestStartsWith( scenario, - "Uncaught \"SyntaxError\": ", + "Uncaught SyntaxError: ", )]); } diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index d7798c43129..bedfc7c990a 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -10,6 +10,7 @@ use crate::{ number::{f64_to_int32, f64_to_uint32}, Number, }, + error::JsNativeError, object::{JsObject, ObjectData}, property::{PropertyDescriptor, PropertyKey}, symbol::{JsSymbol, WellKnownSymbols}, @@ -375,7 +376,9 @@ impl JsValue { // v. If Type(result) is not Object, return result. // vi. Throw a TypeError exception. return if result.is_object() { - context.throw_type_error("Symbol.toPrimitive cannot return an object") + Err(JsNativeError::typ() + .with_message("Symbol.toPrimitive cannot return an object") + .into()) } else { Ok(result) }; @@ -405,28 +408,36 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-tobigint pub fn to_bigint(&self, context: &mut Context) -> JsResult { match self { - Self::Null => context.throw_type_error("cannot convert null to a BigInt"), - Self::Undefined => context.throw_type_error("cannot convert undefined to a BigInt"), + Self::Null => Err(JsNativeError::typ() + .with_message("cannot convert null to a BigInt") + .into()), + Self::Undefined => Err(JsNativeError::typ() + .with_message("cannot convert undefined to a BigInt") + .into()), Self::String(ref string) => { if let Some(value) = JsBigInt::from_string(string) { Ok(value) } else { - context.throw_syntax_error(format!( - "cannot convert string '{string}' to bigint primitive", - )) + Err(JsNativeError::syntax() + .with_message(format!( + "cannot convert string '{string}' to bigint primitive", + )) + .into()) } } Self::Boolean(true) => Ok(JsBigInt::one()), Self::Boolean(false) => Ok(JsBigInt::zero()), - Self::Integer(_) | Self::Rational(_) => { - context.throw_type_error("cannot convert Number to a BigInt") - } + Self::Integer(_) | Self::Rational(_) => Err(JsNativeError::typ() + .with_message("cannot convert Number to a BigInt") + .into()), Self::BigInt(b) => Ok(b.clone()), Self::Object(_) => { let primitive = self.to_primitive(context, PreferredType::Number)?; primitive.to_bigint(context) } - Self::Symbol(_) => context.throw_type_error("cannot convert Symbol to a BigInt"), + Self::Symbol(_) => Err(JsNativeError::typ() + .with_message("cannot convert Symbol to a BigInt") + .into()), } } @@ -463,7 +474,9 @@ impl JsValue { Self::Rational(rational) => Ok(Number::to_native_string(*rational).into()), Self::Integer(integer) => Ok(integer.to_string().into()), Self::String(string) => Ok(string.clone()), - Self::Symbol(_) => context.throw_type_error("can't convert symbol to string"), + Self::Symbol(_) => Err(JsNativeError::typ() + .with_message("can't convert symbol to string") + .into()), Self::BigInt(ref bigint) => Ok(bigint.to_string().into()), Self::Object(_) => { let primitive = self.to_primitive(context, PreferredType::String)?; @@ -479,9 +492,9 @@ impl JsValue { /// See: pub fn to_object(&self, context: &mut Context) -> JsResult { match self { - Self::Undefined | Self::Null => { - context.throw_type_error("cannot convert 'null' or 'undefined' to object") - } + Self::Undefined | Self::Null => Err(JsNativeError::typ() + .with_message("cannot convert 'null' or 'undefined' to object") + .into()), Self::Boolean(boolean) => { let prototype = context.intrinsics().constructors().boolean().prototype(); Ok(JsObject::from_proto_and_data( @@ -813,7 +826,9 @@ impl JsValue { // c. If ! SameValue(𝔽(integer), clamped) is false, throw a RangeError exception. if integer != clamped { - return context.throw_range_error("Index must be between 0 and 2^53 - 1"); + return Err(JsNativeError::range() + .with_message("Index must be between 0 and 2^53 - 1") + .into()); } // d. Assert: 0 ≤ integer ≤ 2^53 - 1. @@ -880,8 +895,12 @@ impl JsValue { Self::String(ref string) => Ok(string.string_to_number()), Self::Rational(number) => Ok(number), Self::Integer(integer) => Ok(f64::from(integer)), - Self::Symbol(_) => context.throw_type_error("argument must not be a symbol"), - Self::BigInt(_) => context.throw_type_error("argument must not be a bigint"), + Self::Symbol(_) => Err(JsNativeError::typ() + .with_message("argument must not be a symbol") + .into()), + Self::BigInt(_) => Err(JsNativeError::typ() + .with_message("argument must not be a bigint") + .into()), Self::Object(_) => { let primitive = self.to_primitive(context, PreferredType::Number)?; primitive.to_number(context) @@ -914,9 +933,11 @@ impl JsValue { /// [table]: https://tc39.es/ecma262/#table-14 /// [spec]: https://tc39.es/ecma262/#sec-requireobjectcoercible #[inline] - pub fn require_object_coercible(&self, context: &mut Context) -> JsResult<&Self> { + pub fn require_object_coercible(&self) -> JsResult<&Self> { if self.is_null_or_undefined() { - context.throw_type_error("cannot convert null or undefined to Object") + Err(JsNativeError::typ() + .with_message("cannot convert null or undefined to Object") + .into()) } else { Ok(self) } @@ -927,9 +948,9 @@ impl JsValue { // 1. If Type(Obj) is not Object, throw a TypeError exception. self.as_object() .ok_or_else(|| { - context.construct_type_error( - "Cannot construct a property descriptor from a non-object", - ) + JsNativeError::typ() + .with_message("Cannot construct a property descriptor from a non-object") + .into() }) .and_then(|obj| obj.to_property_descriptor(context)) } diff --git a/boa_engine/src/value/operations.rs b/boa_engine/src/value/operations.rs index 4e45f6790e8..802d9acd776 100644 --- a/boa_engine/src/value/operations.rs +++ b/boa_engine/src/value/operations.rs @@ -2,7 +2,10 @@ use super::{ Context, FromStr, JsBigInt, JsResult, JsString, JsValue, Numeric, PreferredType, WellKnownSymbols, }; -use crate::builtins::number::{f64_to_int32, f64_to_uint32, Number}; +use crate::{ + builtins::number::{f64_to_int32, f64_to_uint32, Number}, + error::JsNativeError, +}; impl JsValue { #[inline] @@ -40,9 +43,11 @@ impl JsValue { Self::new(JsBigInt::add(x, y)) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ) + return Err(JsNativeError::typ() + .with_message( + "cannot mix BigInt and other types, use explicit conversions", + ) + .into()) } }, }, @@ -67,9 +72,9 @@ impl JsValue { (Numeric::Number(a), Numeric::Number(b)) => Self::new(a - b), (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::new(JsBigInt::sub(x, y)), (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -93,9 +98,9 @@ impl JsValue { (Numeric::Number(a), Numeric::Number(b)) => Self::new(a * b), (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::new(JsBigInt::mul(x, y)), (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -115,7 +120,9 @@ impl JsValue { (Self::BigInt(ref x), Self::BigInt(ref y)) => { if y.is_zero() { - return context.throw_range_error("BigInt division by zero"); + return Err(JsNativeError::range() + .with_message("BigInt division by zero") + .into()); } Self::new(JsBigInt::div(x, y)) } @@ -125,14 +132,16 @@ impl JsValue { (Numeric::Number(a), Numeric::Number(b)) => Self::new(a / b), (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { if y.is_zero() { - return context.throw_range_error("BigInt division by zero"); + return Err(JsNativeError::range() + .with_message("BigInt division by zero") + .into()); } Self::new(JsBigInt::div(x, y)) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -162,7 +171,9 @@ impl JsValue { (Self::BigInt(ref x), Self::BigInt(ref y)) => { if y.is_zero() { - return context.throw_range_error("BigInt division by zero"); + return Err(JsNativeError::range() + .with_message("BigInt division by zero") + .into()); } Self::new(JsBigInt::rem(x, y)) } @@ -172,14 +183,16 @@ impl JsValue { (Numeric::Number(a), Numeric::Number(b)) => Self::new(a % b), (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { if y.is_zero() { - return context.throw_range_error("BigInt division by zero"); + return Err(JsNativeError::range() + .with_message("BigInt division by zero") + .into()); } Self::new(JsBigInt::rem(x, y)) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -197,18 +210,16 @@ impl JsValue { (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x).powf(*y)), (Self::Rational(x), Self::Integer(y)) => Self::new(x.powi(*y)), - (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::pow(a, b, context)?), + (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::pow(a, b)?), // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { (Numeric::Number(a), Numeric::Number(b)) => Self::new(a.powf(b)), - (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { - Self::new(JsBigInt::pow(a, b, context)?) - } + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => Self::new(JsBigInt::pow(a, b)?), (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -236,9 +247,9 @@ impl JsValue { Self::new(JsBigInt::bitand(x, y)) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -266,9 +277,9 @@ impl JsValue { Self::new(JsBigInt::bitor(x, y)) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -296,9 +307,9 @@ impl JsValue { Self::new(JsBigInt::bitxor(x, y)) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -317,9 +328,7 @@ impl JsValue { Self::new(f64_to_int32(*x).wrapping_shl(*y as u32)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => { - Self::new(JsBigInt::shift_left(a, b, context)?) - } + (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::shift_left(a, b)?), // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -327,12 +336,12 @@ impl JsValue { Self::new(f64_to_int32(x).wrapping_shl(f64_to_uint32(y))) } (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { - Self::new(JsBigInt::shift_left(x, y, context)?) + Self::new(JsBigInt::shift_left(x, y)?) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -351,9 +360,7 @@ impl JsValue { Self::new(f64_to_int32(*x).wrapping_shr(*y as u32)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => { - Self::new(JsBigInt::shift_right(a, b, context)?) - } + (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::shift_right(a, b)?), // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { @@ -361,12 +368,12 @@ impl JsValue { Self::new(f64_to_int32(x).wrapping_shr(f64_to_uint32(y))) } (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { - Self::new(JsBigInt::shift_right(x, y, context)?) + Self::new(JsBigInt::shift_right(x, y)?) } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -393,13 +400,14 @@ impl JsValue { Self::new(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y))) } (Numeric::BigInt(_), Numeric::BigInt(_)) => { - return context - .throw_type_error("BigInts have no unsigned right shift, use >> instead"); + return Err(JsNativeError::typ() + .with_message("BigInts have no unsigned right shift, use >> instead") + .into()); } (_, _) => { - return context.throw_type_error( - "cannot mix BigInt and other types, use explicit conversions", - ); + return Err(JsNativeError::typ() + .with_message("cannot mix BigInt and other types, use explicit conversions") + .into()); } }, }) @@ -415,10 +423,12 @@ impl JsValue { pub fn instance_of(&self, target: &Self, context: &mut Context) -> JsResult { // 1. If Type(target) is not Object, throw a TypeError exception. if !target.is_object() { - return context.throw_type_error(format!( - "right-hand side of 'instanceof' should be an object, got {}", - target.type_of() - )); + return Err(JsNativeError::typ() + .with_message(format!( + "right-hand side of 'instanceof' should be an object, got {}", + target.type_of() + )) + .into()); } // 2. Let instOfHandler be ? GetMethod(target, @@hasInstance). @@ -436,7 +446,9 @@ impl JsValue { } None => { // 4. If IsCallable(target) is false, throw a TypeError exception. - context.throw_type_error("right-hand side of 'instanceof' is not callable") + Err(JsNativeError::typ() + .with_message("right-hand side of 'instanceof' is not callable") + .into()) } } } diff --git a/boa_engine/src/value/serde_json.rs b/boa_engine/src/value/serde_json.rs index 645e7ae3619..bcefa994006 100644 --- a/boa_engine/src/value/serde_json.rs +++ b/boa_engine/src/value/serde_json.rs @@ -3,6 +3,7 @@ use super::JsValue; use crate::{ builtins::Array, + error::JsNativeError, property::{PropertyDescriptor, PropertyKey}, Context, JsResult, }; @@ -49,9 +50,9 @@ impl JsValue { .map(|i| Self::Integer(i as i32)) .or_else(|| num.as_f64().map(Self::Rational)) .ok_or_else(|| { - context.construct_type_error(format!( - "could not convert JSON number {num} to JsValue" - )) + JsNativeError::typ() + .with_message(format!("could not convert JSON number {num} to JsValue")) + .into() }), Value::String(string) => Ok(Self::from(string.as_str())), Value::Array(vec) => { @@ -111,7 +112,9 @@ impl JsValue { Self::String(string) => Ok(string.as_str().into()), &Self::Rational(rat) => Ok(rat.into()), &Self::Integer(int) => Ok(int.into()), - Self::BigInt(_bigint) => context.throw_type_error("cannot convert bigint to JSON"), + Self::BigInt(_bigint) => Err(JsNativeError::typ() + .with_message("cannot convert bigint to JSON") + .into()), Self::Object(obj) => { if obj.is_array() { let len = obj.length_of_array_like(context)?; @@ -134,7 +137,9 @@ impl JsValue { PropertyKey::String(string) => string.as_str().to_owned(), PropertyKey::Index(i) => i.to_string(), PropertyKey::Symbol(_sym) => { - return context.throw_type_error("cannot convert Symbol to JSON") + return Err(JsNativeError::typ() + .with_message("cannot convert Symbol to JSON") + .into()) } }; @@ -149,7 +154,9 @@ impl JsValue { Ok(Value::Object(map)) } } - Self::Symbol(_sym) => context.throw_type_error("cannot convert Symbol to JSON"), + Self::Symbol(_sym) => Err(JsNativeError::typ() + .with_message("cannot convert Symbol to JSON") + .into()), } } } @@ -198,7 +205,7 @@ mod tests { let phones = obj.get("phones", &mut context).unwrap(); let phones = phones.as_object().unwrap(); - let arr = JsArray::from_object(phones.clone(), &mut context).unwrap(); + let arr = JsArray::from_object(phones.clone()).unwrap(); assert_eq!(arr.at(0, &mut context).unwrap(), "+44 1234567".into()); assert_eq!(arr.at(1, &mut context).unwrap(), JsValue::from(-45_i32)); assert!(arr.at(2, &mut context).unwrap().is_object()); diff --git a/boa_engine/src/value/tests.rs b/boa_engine/src/value/tests.rs index dce5bb5a0b4..b8ee18d3bfd 100644 --- a/boa_engine/src/value/tests.rs +++ b/boa_engine/src/value/tests.rs @@ -539,46 +539,64 @@ fn to_integer_or_infinity() { let mut context = Context::default(); assert_eq!( - JsValue::undefined().to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(0)) + JsValue::undefined() + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(0) ); assert_eq!( - JsValue::nan().to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(0)) + JsValue::nan().to_integer_or_infinity(&mut context).unwrap(), + IntegerOrInfinity::Integer(0) ); assert_eq!( - JsValue::new(0.0).to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(0)) + JsValue::new(0.0) + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(0) ); assert_eq!( - JsValue::new(-0.0).to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(0)) + JsValue::new(-0.0) + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(0) ); assert_eq!( - JsValue::new(f64::INFINITY).to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::PositiveInfinity) + JsValue::new(f64::INFINITY) + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::PositiveInfinity ); assert_eq!( - JsValue::new(f64::NEG_INFINITY).to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::NegativeInfinity) + JsValue::new(f64::NEG_INFINITY) + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::NegativeInfinity ); assert_eq!( - JsValue::new(10).to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(10)) + JsValue::new(10) + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(10) ); assert_eq!( - JsValue::new(11.0).to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(11)) + JsValue::new(11.0) + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(11) ); assert_eq!( - JsValue::new("12").to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(12)) + JsValue::new("12") + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(12) ); assert_eq!( - JsValue::new(true).to_integer_or_infinity(&mut context), - Ok(IntegerOrInfinity::Integer(1)) + JsValue::new(true) + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(1) ); } @@ -679,7 +697,7 @@ mod cyclic_conversions { assert_eq!( forward(&mut context, src), - r#"Uncaught "TypeError": "cyclic object value""#, + "Uncaught TypeError: cyclic object value", ); } diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index efe70b5fbaf..868c070a4a9 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -13,6 +13,7 @@ use crate::{ }, context::intrinsics::StandardConstructors, environments::{BindingLocator, CompileTimeEnvironment}, + error::JsNativeError, object::{ internal_methods::get_prototype_from_constructor, JsObject, ObjectData, PrivateElement, }, @@ -643,7 +644,9 @@ impl JsObject { let this_function_object = self.clone(); if !self.is_callable() { - return context.throw_type_error("not a callable function"); + return Err(JsNativeError::typ() + .with_message("not a callable function") + .into()); } let object = self.borrow(); @@ -681,8 +684,9 @@ impl JsObject { drop(object); if code.is_class_constructor { - return context - .throw_type_error("Class constructor cannot be invoked without 'new'"); + return Err(JsNativeError::typ() + .with_message("Class constructor cannot be invoked without 'new'") + .into()); } std::mem::swap(&mut environments, &mut context.realm.environments); @@ -1222,7 +1226,9 @@ impl JsObject { }; if !self.is_constructor() { - return context.throw_type_error("not a constructor function"); + return Err(JsNativeError::typ() + .with_message("not a constructor function") + .into()); } let object = self.borrow(); @@ -1244,9 +1250,11 @@ impl JsObject { if constructor.expect("hmm").is_base() || val.is_undefined() { create_this(context) } else { - context.throw_type_error( - "Derived constructor can only return an Object or undefined", - ) + Err(JsNativeError::typ() + .with_message( + "Derived constructor can only return an Object or undefined", + ) + .into()) } } } @@ -1268,9 +1276,11 @@ impl JsObject { if constructor.expect("hmma").is_base() || val.is_undefined() { create_this(context) } else { - context.throw_type_error( - "Derived constructor can only return an Object or undefined", - ) + Err(JsNativeError::typ() + .with_message( + "Derived constructor can only return an Object or undefined", + ) + .into()) } } } @@ -1420,7 +1430,9 @@ impl JsObject { } else if let Some(this) = this { Ok(this) } else if !result.is_undefined() { - context.throw_type_error("Function constructor must not return non-object") + Err(JsNativeError::typ() + .with_message("Function constructor must not return non-object") + .into()) } else { let function_env = environment .slots() @@ -1433,8 +1445,8 @@ impl JsObject { .expect("this binding must be object") .clone()) } else { - //context.throw_type_error("Function constructor must not return non-object") - context.throw_reference_error("Must call super constructor in derived class before accessing 'this' or returning from derived constructor") + //Err(JsNativeError::typ().with_message("Function constructor must not return non-object").into()) + Err(JsNativeError::reference().with_message("Must call super constructor in derived class before accessing 'this' or returning from derived constructor").into()) } } } diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 3685570651e..34f256cc6ca 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -10,6 +10,7 @@ use crate::{ Array, ForInIterator, JsArgs, Number, Promise, }, environments::EnvironmentSlots, + error::JsNativeError, object::{FunctionBuilder, JsFunction, JsObject, ObjectData, PrivateElement}, property::{DescriptorKind, PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey}, value::Numeric, @@ -205,8 +206,9 @@ impl Context { if let Some(superclass) = superclass.as_constructor() { let proto = superclass.get("prototype", self)?; if !proto.is_object() && !proto.is_null() { - return self - .throw_type_error("superclass prototype must be an object or null"); + return Err(JsNativeError::typ() + .with_message("superclass prototype must be an object or null") + .into()); } let class = self.vm.pop(); @@ -231,7 +233,9 @@ impl Context { } else if superclass.is_null() { self.vm.push(JsValue::Null); } else { - return self.throw_type_error("superclass must be a constructor"); + return Err(JsNativeError::typ() + .with_message("superclass must be a constructor") + .into()); } } Opcode::SetClassPrototype => { @@ -386,10 +390,12 @@ impl Context { let lhs = self.vm.pop(); if !rhs.is_object() { - return self.throw_type_error(format!( - "right-hand side of 'in' should be an object, got {}", - rhs.type_of() - )); + return Err(JsNativeError::typ() + .with_message(format!( + "right-hand side of 'in' should be an object, got {}", + rhs.type_of() + )) + .into()); } let key = lhs.to_property_key(self)?; let value = self.has_property(&rhs, &key)?; @@ -568,12 +574,15 @@ impl Context { self.call(&get, &self.global_object().clone().into(), &[])? } _ => { - return self - .throw_reference_error(format!("{key} is not defined")) + return Err(JsNativeError::reference() + .with_message(format!("{key} is not defined")) + .into()) } }, _ => { - return self.throw_reference_error(format!("{key} is not defined")) + return Err(JsNativeError::reference() + .with_message(format!("{key} is not defined")) + .into()) } } } @@ -586,7 +595,9 @@ impl Context { } else { let name = JsString::from(self.interner().resolve_expect(binding_locator.name())); - return self.throw_reference_error(format!("{name} is not initialized")); + return Err(JsNativeError::reference() + .with_message(format!("{name} is not initialized")) + .into()); }; self.vm.push(value); @@ -654,9 +665,9 @@ impl Context { let exists = self.global_bindings_mut().contains_key(&key); if !exists && self.vm.frame().code.strict { - return self.throw_reference_error(format!( - "assignment to undeclared variable {key}" - )); + return Err(JsNativeError::reference() + .with_message(format!("assignment to undeclared variable {key}")) + .into()); } let success = @@ -667,9 +678,9 @@ impl Context { )?; if !success && self.vm.frame().code.strict { - return self.throw_type_error(format!( - "cannot set non-writable property: {key}", - )); + return Err(JsNativeError::typ() + .with_message(format!("cannot set non-writable property: {key}",)) + .into()); } } } else if !self.realm.environments.put_value_if_initialized( @@ -678,10 +689,12 @@ impl Context { binding_locator.name(), value, ) { - self.throw_reference_error(format!( - "cannot access '{}' before initialization", - self.interner().resolve_expect(binding_locator.name()) - ))?; + return Err(JsNativeError::reference() + .with_message(format!( + "cannot access '{}' before initialization", + self.interner().resolve_expect(binding_locator.name()) + )) + .into()); } } Opcode::Jump => { @@ -1125,7 +1138,9 @@ impl Context { .set_private_element(name, PrivateElement::Field(value)); } Some(PrivateElement::Method(_)) => { - return self.throw_type_error("private method is not writable"); + return Err(JsNativeError::typ() + .with_message("private method is not writable") + .into()); } Some(PrivateElement::Accessor { setter: Some(setter), @@ -1136,14 +1151,20 @@ impl Context { setter.call(&object.clone().into(), &[value], self)?; } None => { - return self.throw_type_error("private field not defined"); + return Err(JsNativeError::typ() + .with_message("private field not defined") + .into()); } _ => { - return self.throw_type_error("private field defined without a setter"); + return Err(JsNativeError::typ() + .with_message("private field defined without a setter") + .into()); } } } else { - return self.throw_type_error("cannot set private property on non-object"); + return Err(JsNativeError::typ() + .with_message("cannot set private property on non-object") + .into()); } } Opcode::SetPrivateField => { @@ -1165,7 +1186,9 @@ impl Context { object_borrow_mut.set_private_element(name, PrivateElement::Field(value)); } } else { - return self.throw_type_error("cannot set private property on non-object"); + return Err(JsNativeError::typ() + .with_message("cannot set private property on non-object") + .into()); } } Opcode::SetPrivateMethod => { @@ -1179,7 +1202,9 @@ impl Context { object_borrow_mut .set_private_element(name, PrivateElement::Method(value.clone())); } else { - return self.throw_type_error("cannot set private setter on non-object"); + return Err(JsNativeError::typ() + .with_message("cannot set private setter on non-object") + .into()); } } Opcode::SetPrivateSetter => { @@ -1192,7 +1217,9 @@ impl Context { let mut object_borrow_mut = object.borrow_mut(); object_borrow_mut.set_private_element_setter(name, value.clone()); } else { - return self.throw_type_error("cannot set private setter on non-object"); + return Err(JsNativeError::typ() + .with_message("cannot set private setter on non-object") + .into()); } } Opcode::SetPrivateGetter => { @@ -1205,7 +1232,9 @@ impl Context { let mut object_borrow_mut = object.borrow_mut(); object_borrow_mut.set_private_element_getter(name, value.clone()); } else { - return self.throw_type_error("cannot set private getter on non-object"); + return Err(JsNativeError::typ() + .with_message("cannot set private getter on non-object") + .into()); } } Opcode::GetPrivateField => { @@ -1226,16 +1255,20 @@ impl Context { self.vm.push(value); } PrivateElement::Accessor { .. } => { - return self.throw_type_error( - "private property was defined without a getter", - ); + return Err(JsNativeError::typ() + .with_message("private property was defined without a getter") + .into()); } } } else { - return self.throw_type_error("private property does not exist"); + return Err(JsNativeError::typ() + .with_message("private property does not exist") + .into()); } } else { - return self.throw_type_error("cannot read private property from non-object"); + return Err(JsNativeError::typ() + .with_message("cannot read private property from non-object") + .into()); } } Opcode::PushClassField => { @@ -1351,7 +1384,9 @@ impl Context { let object = self.vm.pop(); let result = object.to_object(self)?.__delete__(&key, self)?; if !result && self.vm.frame().code.strict { - return Err(self.construct_type_error("Cannot delete property")); + return Err(JsNativeError::typ() + .with_message("Cannot delete property") + .into()); } self.vm.push(result); } @@ -1362,7 +1397,9 @@ impl Context { .to_object(self)? .__delete__(&key.to_property_key(self)?, self)?; if !result && self.vm.frame().code.strict { - return Err(self.construct_type_error("Cannot delete property")); + return Err(JsNativeError::typ() + .with_message("Cannot delete property") + .into()); } self.vm.push(result); } @@ -1393,7 +1430,7 @@ impl Context { } Opcode::Throw => { let value = self.vm.pop(); - return Err(value); + return Err(value.into()); } Opcode::TryStart => { let next = self.vm.read::(); @@ -1476,7 +1513,7 @@ impl Context { return Ok(ShouldExit::True); } FinallyReturn::Err => { - return Err(self.vm.pop()); + return Err(self.vm.pop().into()); } } } @@ -1498,7 +1535,7 @@ impl Context { self.vm.push(this); } else { drop(env_b); - return self.throw_reference_error("Must call super constructor in derived class before accessing 'this' or returning from derived constructor"); + return Err(JsNativeError::reference().with_message("Must call super constructor in derived class before accessing 'this' or returning from derived constructor").into()); } } EnvironmentSlots::Global => { @@ -1533,7 +1570,7 @@ impl Context { home_object } else { - return self.throw_range_error("Must call super constructor in derived class before accessing 'this' or returning from derived constructor"); + return Err(JsNativeError::range().with_message("Must call super constructor in derived class before accessing 'this' or returning from derived constructor").into()); }; if let Some(home) = home { @@ -1575,7 +1612,9 @@ impl Context { .expect("function object must have prototype"); if !super_constructor.is_constructor() { - return self.throw_type_error("super constructor object must be constructor"); + return Err(JsNativeError::typ() + .with_message("super constructor object must be constructor") + .into()); } let result = super_constructor.__construct__(&arguments, &new_target, self)?; @@ -1590,7 +1629,9 @@ impl Context { .expect("super call must be in function environment"); if !this_env.borrow_mut().bind_this_value(&result) { - return self.throw_reference_error("this already initialized"); + return Err(JsNativeError::reference() + .with_message("this already initialized") + .into()); } self.vm.push(result); } @@ -1628,7 +1669,9 @@ impl Context { .expect("function object must have prototype"); if !super_constructor.is_constructor() { - return self.throw_type_error("super constructor object must be constructor"); + return Err(JsNativeError::typ() + .with_message("super constructor object must be constructor") + .into()); } let result = super_constructor.__construct__(&arguments, &new_target, self)?; @@ -1643,7 +1686,9 @@ impl Context { .expect("super call must be in function environment"); if !this_env.borrow_mut().bind_this_value(&result) { - return self.throw_reference_error("this already initialized"); + return Err(JsNativeError::reference() + .with_message("this already initialized") + .into()); } self.vm.push(result); } @@ -1676,7 +1721,9 @@ impl Context { .expect("function object must have prototype"); if !super_constructor.is_constructor() { - return self.throw_type_error("super constructor object must be constructor"); + return Err(JsNativeError::typ() + .with_message("super constructor object must be constructor") + .into()); } let result = super_constructor.__construct__(&arguments, &new_target, self)?; @@ -1690,7 +1737,9 @@ impl Context { .as_function_slots() .expect("super call must be in function environment"); if !this_env.borrow_mut().bind_this_value(&result) { - return self.throw_reference_error("this already initialized"); + return Err(JsNativeError::reference() + .with_message("this already initialized") + .into()); } self.vm.push(result); @@ -1737,7 +1786,9 @@ impl Context { } Opcode::CallEval => { if self.vm.stack_size_limit <= self.vm.stack.len() { - return self.throw_range_error("Maximum call stack size exceeded"); + return Err(JsNativeError::range() + .with_message("Maximum call stack size exceeded") + .into()); } let argument_count = self.vm.read::(); let mut arguments = Vec::with_capacity(argument_count as usize); @@ -1751,7 +1802,11 @@ impl Context { let object = match func { JsValue::Object(ref object) if object.is_callable() => object.clone(), - _ => return self.throw_type_error("not a callable function"), + _ => { + return Err(JsNativeError::typ() + .with_message("not a callable function") + .into()) + } }; // A native function with the name "eval" implies, that is this the built-in eval function. @@ -1774,7 +1829,9 @@ impl Context { } Opcode::CallEvalSpread => { if self.vm.stack_size_limit <= self.vm.stack.len() { - return self.throw_range_error("Maximum call stack size exceeded"); + return Err(JsNativeError::range() + .with_message("Maximum call stack size exceeded") + .into()); } // Get the arguments that are stored as an array object on the stack. @@ -1794,7 +1851,11 @@ impl Context { let object = match func { JsValue::Object(ref object) if object.is_callable() => object.clone(), - _ => return self.throw_type_error("not a callable function"), + _ => { + return Err(JsNativeError::typ() + .with_message("not a callable function") + .into()) + } }; // A native function with the name "eval" implies, that is this the built-in eval function. @@ -1817,7 +1878,9 @@ impl Context { } Opcode::Call => { if self.vm.stack_size_limit <= self.vm.stack.len() { - return self.throw_range_error("Maximum call stack size exceeded"); + return Err(JsNativeError::range() + .with_message("Maximum call stack size exceeded") + .into()); } let argument_count = self.vm.read::(); let mut arguments = Vec::with_capacity(argument_count as usize); @@ -1831,7 +1894,11 @@ impl Context { let object = match func { JsValue::Object(ref object) if object.is_callable() => object.clone(), - _ => return self.throw_type_error("not a callable function"), + _ => { + return Err(JsNativeError::typ() + .with_message("not a callable function") + .into()) + } }; let result = object.__call__(&this, &arguments, self)?; @@ -1840,7 +1907,9 @@ impl Context { } Opcode::CallSpread => { if self.vm.stack_size_limit <= self.vm.stack.len() { - return self.throw_range_error("Maximum call stack size exceeded"); + return Err(JsNativeError::range() + .with_message("Maximum call stack size exceeded") + .into()); } // Get the arguments that are stored as an array object on the stack. @@ -1860,7 +1929,11 @@ impl Context { let object = match func { JsValue::Object(ref object) if object.is_callable() => object.clone(), - _ => return self.throw_type_error("not a callable function"), + _ => { + return Err(JsNativeError::typ() + .with_message("not a callable function") + .into()) + } }; let result = object.__call__(&this, &arguments, self)?; @@ -1869,7 +1942,9 @@ impl Context { } Opcode::New => { if self.vm.stack_size_limit <= self.vm.stack.len() { - return self.throw_range_error("Maximum call stack size exceeded"); + return Err(JsNativeError::range() + .with_message("Maximum call stack size exceeded") + .into()); } let argument_count = self.vm.read::(); let mut arguments = Vec::with_capacity(argument_count as usize); @@ -1881,14 +1956,20 @@ impl Context { let result = func .as_constructor() - .ok_or_else(|| self.construct_type_error("not a constructor")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("not a constructor") + .into() + }) .and_then(|cons| cons.__construct__(&arguments, cons, self))?; self.vm.push(result); } Opcode::NewSpread => { if self.vm.stack_size_limit <= self.vm.stack.len() { - return self.throw_range_error("Maximum call stack size exceeded"); + return Err(JsNativeError::range() + .with_message("Maximum call stack size exceeded") + .into()); } // Get the arguments that are stored as an array object on the stack. let arguments_array = self.vm.pop(); @@ -1906,9 +1987,12 @@ impl Context { let result = func .as_constructor() - .ok_or_else(|| self.construct_type_error("not a constructor")) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("not a constructor") + .into() + }) .and_then(|cons| cons.__construct__(&arguments, cons, self))?; - self.vm.push(result); } Opcode::Return => { @@ -2015,7 +2099,9 @@ impl Context { .as_ref() .map(PropertyDescriptor::expect_value) .cloned() - .ok_or_else(|| self.construct_type_error("Could not find property `next`"))?; + .ok_or_else(|| { + JsNativeError::typ().with_message("Could not find property `next`") + })?; self.vm.push(iterator); self.vm.push(next_method); @@ -2136,16 +2222,20 @@ impl Context { } Opcode::RequireObjectCoercible => { let value = self.vm.pop(); - let value = value.require_object_coercible(self)?; + let value = value.require_object_coercible()?; self.vm.push(value); } Opcode::ValueNotNullOrUndefined => { let value = self.vm.pop(); if value.is_null() { - return self.throw_type_error("Cannot destructure 'null' value"); + return Err(JsNativeError::typ() + .with_message("Cannot destructure 'null' value") + .into()); } if value.is_undefined() { - return self.throw_type_error("Cannot destructure 'undefined' value"); + return Err(JsNativeError::typ() + .with_message("Cannot destructure 'undefined' value") + .into()); } self.vm.push(value); } @@ -2189,7 +2279,7 @@ impl Context { GeneratorResumeKind::Normal => return Ok(ShouldExit::False), GeneratorResumeKind::Throw => { let received = self.vm.pop(); - return Err(received); + return Err(received.into()); } GeneratorResumeKind::Return => { let mut finally_left = false; @@ -2216,7 +2306,7 @@ impl Context { let value = self.vm.pop(); if self.vm.frame().generator_resume_kind == GeneratorResumeKind::Throw { - return Err(value); + return Err(value.into()); } let completion = Ok(value); @@ -2244,9 +2334,11 @@ impl Context { if let Some(next) = gen.queue.front() { let (completion, r#return) = &next.completion; if *r#return { - match completion { - Ok(value) | Err(value) => self.vm.push(value), - } + let value = match completion { + Ok(value) => value.clone(), + Err(e) => e.clone().to_value(self), + }; + self.vm.push(value); self.vm.push(true); } else { self.vm.push(completion.clone()?); @@ -2277,7 +2369,8 @@ impl Context { let result = self.call(&next_method, &iterator.clone().into(), &[received])?; let result_object = result.as_object().ok_or_else(|| { - self.construct_type_error("generator next method returned non-object") + JsNativeError::typ() + .with_message("generator next method returned non-object") })?; let done = result_object.get("done", self)?.to_boolean(); if done { @@ -2298,9 +2391,8 @@ impl Context { if let Some(throw) = throw { let result = throw.call(&iterator.clone().into(), &[received], self)?; let result_object = result.as_object().ok_or_else(|| { - self.construct_type_error( - "generator throw method returned non-object", - ) + JsNativeError::typ() + .with_message("generator throw method returned non-object") })?; let done = result_object.get("done", self)?.to_boolean(); if done { @@ -2320,9 +2412,9 @@ impl Context { let iterator_record = IteratorRecord::new(iterator.clone(), next_method, done); iterator_record.close(Ok(JsValue::Undefined), self)?; - let error = - self.construct_type_error("iterator does not have a throw method"); - return Err(error); + return Err(JsNativeError::typ() + .with_message("iterator does not have a throw method") + .into()); } GeneratorResumeKind::Return => { let r#return = iterator.get_method("return", self)?; @@ -2330,9 +2422,8 @@ impl Context { let result = r#return.call(&iterator.clone().into(), &[received], self)?; let result_object = result.as_object().ok_or_else(|| { - self.construct_type_error( - "generator return method returned non-object", - ) + JsNativeError::typ() + .with_message("generator return method returned non-object") })?; let done = result_object.get("done", self)?.to_boolean(); if done { @@ -2634,12 +2725,14 @@ impl Context { self.vm.frame_mut().catch.pop(); self.vm.frame_mut().finally_return = FinallyReturn::Err; self.vm.frame_mut().thrown = true; + let e = e.to_value(self); self.vm.push(e); } else { self.vm.stack.truncate(start_stack_size); // Step 3.f in [AsyncBlockStart](https://tc39.es/ecma262/#sec-asyncblockstart). if let Some(promise_capability) = promise_capability { + let e = e.to_value(self); promise_capability .reject() .call(&JsValue::undefined(), &[e.clone()], self) diff --git a/boa_engine/src/vm/tests.rs b/boa_engine/src/vm/tests.rs index cd1b1108c07..26cb087f304 100644 --- a/boa_engine/src/vm/tests.rs +++ b/boa_engine/src/vm/tests.rs @@ -43,7 +43,14 @@ fn try_catch_finally_from_init() { } "#; - assert_eq!(Context::default().eval(source.as_bytes()), Err("h".into())); + assert_eq!( + Context::default() + .eval(source.as_bytes()) + .unwrap_err() + .as_opaque() + .unwrap(), + &"h".into() + ); } #[test] @@ -61,8 +68,8 @@ fn multiple_catches() { "#; assert_eq!( - Context::default().eval(source.as_bytes()), - Ok(JsValue::Undefined) + Context::default().eval(source.as_bytes()).unwrap(), + JsValue::Undefined ); } @@ -80,8 +87,8 @@ fn use_last_expr_try_block() { "#; assert_eq!( - Context::default().eval(source.as_bytes()), - Ok(JsValue::from("Hello!")) + Context::default().eval(source.as_bytes()).unwrap(), + JsValue::from("Hello!") ); } #[test] @@ -98,8 +105,8 @@ fn use_last_expr_catch_block() { "#; assert_eq!( - Context::default().eval(source.as_bytes()), - Ok(JsValue::from("Hello!")) + Context::default().eval(source.as_bytes()).unwrap(), + JsValue::from("Hello!") ); } @@ -114,8 +121,8 @@ fn no_use_last_expr_finally_block() { "#; assert_eq!( - Context::default().eval(source.as_bytes()), - Ok(JsValue::undefined()) + Context::default().eval(source.as_bytes()).unwrap(), + JsValue::undefined() ); } @@ -133,8 +140,8 @@ fn finally_block_binding_env() { "#; assert_eq!( - Context::default().eval(source.as_bytes()), - Ok(JsValue::from("Hey hey people")) + Context::default().eval(source.as_bytes()).unwrap(), + JsValue::from("Hey hey people") ); } @@ -152,8 +159,8 @@ fn run_super_method_in_object() { "#; assert_eq!( - Context::default().eval(source.as_bytes()), - Ok(JsValue::from("super")) + Context::default().eval(source.as_bytes()).unwrap(), + JsValue::from("super") ) } @@ -178,7 +185,7 @@ fn get_reference_by_super() { "#; assert_eq!( - Context::default().eval(source.as_bytes()), - Ok(JsValue::from("ab")) + Context::default().eval(source.as_bytes()).unwrap(), + JsValue::from("ab") ) } diff --git a/boa_examples/src/bin/classes.rs b/boa_examples/src/bin/classes.rs index 951284ce813..da9c9692e6f 100644 --- a/boa_examples/src/bin/classes.rs +++ b/boa_examples/src/bin/classes.rs @@ -1,6 +1,7 @@ // NOTE: this example requires the `console` feature to run correctly. use boa_engine::{ class::{Class, ClassBuilder}, + error::JsNativeError, property::Attribute, Context, JsResult, JsValue, }; @@ -28,7 +29,7 @@ struct Person { // or any function that matches the required signature. impl Person { /// Says hello if `this` is a `Person` - fn say_hello(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + fn say_hello(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // We check if this is an object. if let Some(object) = this.as_object() { // If it is we downcast the type to type `Person`. @@ -44,7 +45,9 @@ impl Person { } // If `this` was not an object or the type of `this` was not a native object `Person`, // we throw a `TypeError`. - context.throw_type_error("'this' is not a Person object") + Err(JsNativeError::typ() + .with_message("'this' is not a Person object") + .into()) } } diff --git a/boa_examples/src/bin/closures.rs b/boa_examples/src/bin/closures.rs index 2c453e40702..2cc73895e31 100644 --- a/boa_examples/src/bin/closures.rs +++ b/boa_examples/src/bin/closures.rs @@ -4,11 +4,11 @@ use boa_engine::{ object::{FunctionBuilder, JsObject}, property::{Attribute, PropertyDescriptor}, - Context, JsString, JsValue, + Context, JsError, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; -fn main() -> Result<(), JsValue> { +fn main() -> Result<(), JsError> { // We create a new `Context` to create a new Javascript executor. let mut context = Context::default(); diff --git a/boa_examples/src/bin/jsset.rs b/boa_examples/src/bin/jsset.rs index 1300320dc25..252227962c3 100644 --- a/boa_examples/src/bin/jsset.rs +++ b/boa_examples/src/bin/jsset.rs @@ -1,21 +1,21 @@ // This example shows how to manipulate a Javascript Set using Rust code. #![allow(clippy::bool_assert_comparison)] -use boa_engine::{object::JsSet, Context, JsValue}; +use boa_engine::{object::JsSet, Context, JsError, JsValue}; -fn main() -> Result<(), JsValue> { +fn main() -> Result<(), JsError> { // New `Context` for a new Javascript executor. let context = &mut Context::default(); // Create an empty set. let set = JsSet::new(context); - assert_eq!(set.size(context)?, 0); + assert_eq!(set.size()?, 0); set.add(5, context)?; - assert_eq!(set.size(context)?, 1); + assert_eq!(set.size()?, 1); set.add(10, context)?; - assert_eq!(set.size(context)?, 2); + assert_eq!(set.size()?, 2); set.clear(context)?; - assert_eq!(set.size(context)?, 0); + assert_eq!(set.size()?, 0); set.add("one", context)?; set.add("two", context)?; @@ -32,7 +32,7 @@ fn main() -> Result<(), JsValue> { assert_eq!(set.has("one", context)?, false); assert_eq!(set.has("three", context)?, false); - assert_eq!(set.size(context)?, 0); + assert_eq!(set.size()?, 0); // Add a slice into a set; set.add_items( @@ -40,12 +40,12 @@ fn main() -> Result<(), JsValue> { context, )?; // Will return 1, as one slice was added. - assert_eq!(set.size(context)?, 1); + assert_eq!(set.size()?, 1); // Make a new set from a slice let slice_set = JsSet::from_iter([JsValue::new(1), JsValue::new(2), JsValue::new(3)], context); // Will return 3, as each element of slice was added into the set. - assert_eq!(slice_set.size(context)?, 3); + assert_eq!(slice_set.size()?, 3); set.clear(context)?; diff --git a/boa_examples/src/bin/loadfile.rs b/boa_examples/src/bin/loadfile.rs index 1692aecf92c..da2feee5134 100644 --- a/boa_examples/src/bin/loadfile.rs +++ b/boa_examples/src/bin/loadfile.rs @@ -19,7 +19,7 @@ fn main() { } Err(e) => { // Pretty print the error - eprintln!("Uncaught {}", e.display()); + eprintln!("Uncaught {e}"); } }; } diff --git a/boa_examples/src/bin/loadstring.rs b/boa_examples/src/bin/loadstring.rs index f3c50098e61..d63b465c722 100644 --- a/boa_examples/src/bin/loadstring.rs +++ b/boa_examples/src/bin/loadstring.rs @@ -15,7 +15,7 @@ fn main() { } Err(e) => { // Pretty print the error - eprintln!("Uncaught {}", e.display()); + eprintln!("Uncaught {e}"); } }; } diff --git a/boa_tester/src/exec/js262.rs b/boa_tester/src/exec/js262.rs index f6ad73dd210..3a98f9feee7 100644 --- a/boa_tester/src/exec/js262.rs +++ b/boa_tester/src/exec/js262.rs @@ -2,7 +2,7 @@ use boa_engine::{ builtins::JsArgs, object::{JsObject, ObjectInitializer}, property::Attribute, - Context, JsResult, JsValue, + Context, JsNativeError, JsResult, JsValue, }; /// Initializes the object in the context. @@ -40,24 +40,18 @@ fn create_realm(_this: &JsValue, _: &[JsValue], _context: &mut Context) -> JsRes /// The `$262.detachArrayBuffer()` function. /// /// Implements the `DetachArrayBuffer` abstract operation. -fn detach_array_buffer( - _this: &JsValue, - args: &[JsValue], - context: &mut Context, -) -> JsResult { +fn detach_array_buffer(_this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { #[inline] - fn type_err(context: &mut Context) -> JsValue { - context.construct_type_error("The provided object was not an ArrayBuffer") + fn type_err() -> JsNativeError { + JsNativeError::typ().with_message("The provided object was not an ArrayBuffer") } let array_buffer = args .get(0) .and_then(JsValue::as_object) - .ok_or_else(|| type_err(context))?; + .ok_or_else(type_err)?; let mut array_buffer = array_buffer.borrow_mut(); - let array_buffer = array_buffer - .as_array_buffer_mut() - .ok_or_else(|| type_err(context))?; + let array_buffer = array_buffer.as_array_buffer_mut().ok_or_else(type_err)?; // 1. Assert: IsSharedArrayBuffer(arrayBuffer) is false. TODO // 2. If key is not present, set key to undefined. @@ -65,7 +59,9 @@ fn detach_array_buffer( // 3. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception. if !JsValue::same_value(&array_buffer.array_buffer_detach_key, key) { - return context.throw_type_error("Cannot detach array buffer with different key"); + return Err(JsNativeError::typ() + .with_message("Cannot detach array buffer with different key") + .into()); } // 4. Set arrayBuffer.[[ArrayBufferData]] to null. @@ -85,7 +81,9 @@ fn eval_script(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsRe if let Some(source_text) = args.get(0).and_then(JsValue::as_string) { match context.parse(source_text.as_str()) { // TODO: check strict - Err(e) => context.throw_type_error(format!("Uncaught Syntax Error: {e}")), + Err(e) => Err(JsNativeError::typ() + .with_message(format!("Uncaught Syntax Error: {e}")) + .into()), // Calling eval here parses the code a second time. // TODO: We can fix this after we have have defined the public api for the vm executer. Ok(_) => context.eval(source_text.as_str()), diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 3c519645211..6c843d3e2ad 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -2,13 +2,15 @@ mod js262; +use crate::read::ErrorType; + use super::{ Harness, Outcome, Phase, SuiteResult, Test, TestFlags, TestOutcomeResult, TestResult, TestSuite, IGNORED, }; use boa_engine::{ builtins::JsArgs, object::FunctionBuilder, property::Attribute, syntax::Parser, Context, - JsResult, JsValue, + JsNativeErrorKind, JsResult, JsValue, }; use boa_gc::{Cell, Finalize, Gc, Trace}; use colored::Colorize; @@ -181,7 +183,7 @@ impl Test { && matches!(*callback_obj.result.borrow(), Some(true) | None); let text = match res { Ok(val) => val.display().to_string(), - Err(e) => format!("Uncaught {}", e.display()), + Err(e) => format!("Uncaught {e}",), }; (passed, text) @@ -191,11 +193,11 @@ impl Test { } Outcome::Negative { phase: Phase::Parse | Phase::Early, - ref error_type, + error_type, } => { assert_eq!( - error_type.as_ref(), - "SyntaxError", + error_type, + ErrorType::SyntaxError, "non-SyntaxError parsing/early error found in {}", self.name ); @@ -215,7 +217,7 @@ impl Test { } => todo!("check module resolution errors"), Outcome::Negative { phase: Phase::Runtime, - ref error_type, + error_type, } => { let mut context = Context::default(); if let Err(e) = Parser::new(test_content.as_bytes()).parse_all(&mut context) { @@ -226,13 +228,35 @@ impl Test { Ok(_) => match context.eval(&test_content) { Ok(res) => (false, res.display().to_string()), Err(e) => { - let passed = e - .display() - .internals(true) - .to_string() - .contains(error_type.as_ref()); - - (passed, format!("Uncaught {}", e.display())) + let passed = if let Ok(e) = e.try_native(&mut context) { + match &e.kind { + JsNativeErrorKind::Syntax + if error_type == ErrorType::SyntaxError => + { + true + } + JsNativeErrorKind::Reference + if error_type == ErrorType::ReferenceError => + { + true + } + JsNativeErrorKind::Range + if error_type == ErrorType::RangeError => + { + true + } + JsNativeErrorKind::Type + if error_type == ErrorType::TypeError => + { + true + } + _ => false, + } + } else { + e.to_string().starts_with(&error_type.to_string()) + }; + + (passed, format!("Uncaught {e}")) } }, Err(e) => (false, e), @@ -331,15 +355,15 @@ impl Test { context .eval(harness.assert.as_ref()) - .map_err(|e| format!("could not run assert.js:\n{}", e.display()))?; + .map_err(|e| format!("could not run assert.js:\n{e}"))?; context .eval(harness.sta.as_ref()) - .map_err(|e| format!("could not run sta.js:\n{}", e.display()))?; + .map_err(|e| format!("could not run sta.js:\n{e}"))?; if self.flags.contains(TestFlags::ASYNC) { context .eval(harness.doneprint_handle.as_ref()) - .map_err(|e| format!("could not run doneprintHandle.js:\n{}", e.display()))?; + .map_err(|e| format!("could not run doneprintHandle.js:\n{e}"))?; } for include in self.includes.iter() { @@ -351,12 +375,7 @@ impl Test { .ok_or_else(|| format!("could not find the {include} include file."))? .as_ref(), ) - .map_err(|e| { - format!( - "could not run the {include} include file:\nUncaught {}", - e.display() - ) - })?; + .map_err(|e| format!("could not run the {include} include file:\nUncaught {e}"))?; } Ok(()) diff --git a/boa_tester/src/main.rs b/boa_tester/src/main.rs index d590c5a14bc..30a8a88fccd 100644 --- a/boa_tester/src/main.rs +++ b/boa_tester/src/main.rs @@ -77,6 +77,7 @@ use clap::Parser; use colored::Colorize; use fxhash::{FxHashMap, FxHashSet}; use once_cell::sync::Lazy; +use read::ErrorType; use serde::{Deserialize, Serialize}; use std::{ fs, @@ -455,7 +456,7 @@ impl Test { #[derive(Debug, Clone)] enum Outcome { Positive, - Negative { phase: Phase, error_type: Box }, + Negative { phase: Phase, error_type: ErrorType }, } impl Default for Outcome { diff --git a/boa_tester/src/read.rs b/boa_tester/src/read.rs index 86cadb7221a..266eec9e8df 100644 --- a/boa_tester/src/read.rs +++ b/boa_tester/src/read.rs @@ -4,7 +4,7 @@ use super::{Harness, Locale, Phase, Test, TestSuite, IGNORED}; use anyhow::Context; use fxhash::FxHashMap; use serde::Deserialize; -use std::{fs, io, path::Path, str::FromStr}; +use std::{fmt::Display, fs, io, path::Path, str::FromStr}; /// Representation of the YAML metadata in Test262 tests. #[derive(Debug, Clone, Deserialize)] @@ -33,7 +33,31 @@ pub(super) struct MetaData { pub(super) struct Negative { pub(super) phase: Phase, #[serde(rename = "type")] - pub(super) error_type: Box, + pub(super) error_type: ErrorType, +} + +/// All possible error types +#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Eq)] +#[allow(clippy::enum_variant_names)] // Better than appending `rename` to all variants +pub(super) enum ErrorType { + Test262Error, + SyntaxError, + ReferenceError, + RangeError, + TypeError, +} + +impl Display for ErrorType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ErrorType::Test262Error => "Test262Error", + ErrorType::SyntaxError => "SyntaxError", + ErrorType::ReferenceError => "ReferenceError", + ErrorType::RangeError => "RangeError", + ErrorType::TypeError => "TypeError", + } + .fmt(f) + } } /// Individual test flag. diff --git a/boa_wasm/src/lib.rs b/boa_wasm/src/lib.rs index fe37e482953..bfb70791e4a 100644 --- a/boa_wasm/src/lib.rs +++ b/boa_wasm/src/lib.rs @@ -67,6 +67,6 @@ pub fn evaluate(src: &str) -> Result { // Setup executor Context::default() .eval(src) - .map_err(|e| JsValue::from(format!("Uncaught {}", e.display()))) + .map_err(|e| JsValue::from(format!("Uncaught {e}"))) .map(|v| v.display().to_string()) } From faad44a551d01182fd63e8d91907ce2bfd763acd Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Thu, 15 Sep 2022 00:19:32 -0500 Subject: [PATCH 2/5] Fix detection of Test262Error in boa_tester --- boa_tester/src/exec/mod.rs | 12 +++++++++++- boa_tester/src/read.rs | 7 +++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 6c843d3e2ad..3728612aac6 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -253,7 +253,17 @@ impl Test { _ => false, } } else { - e.to_string().starts_with(&error_type.to_string()) + e.as_opaque() + .expect("try_native cannot fail if e is not opaque") + .as_object() + .and_then(|o| o.get("constructor", &mut context).ok()) + .as_ref() + .and_then(JsValue::as_object) + .and_then(|o| o.get("name", &mut context).ok()) + .as_ref() + .and_then(JsValue::as_string) + .map(|s| *s == error_type.as_str()) + .unwrap_or_default() }; (passed, format!("Uncaught {e}")) diff --git a/boa_tester/src/read.rs b/boa_tester/src/read.rs index 266eec9e8df..e198b64220a 100644 --- a/boa_tester/src/read.rs +++ b/boa_tester/src/read.rs @@ -4,7 +4,7 @@ use super::{Harness, Locale, Phase, Test, TestSuite, IGNORED}; use anyhow::Context; use fxhash::FxHashMap; use serde::Deserialize; -use std::{fmt::Display, fs, io, path::Path, str::FromStr}; +use std::{fs, io, path::Path, str::FromStr}; /// Representation of the YAML metadata in Test262 tests. #[derive(Debug, Clone, Deserialize)] @@ -47,8 +47,8 @@ pub(super) enum ErrorType { TypeError, } -impl Display for ErrorType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl ErrorType { + pub(super) fn as_str(self) -> &'static str { match self { ErrorType::Test262Error => "Test262Error", ErrorType::SyntaxError => "SyntaxError", @@ -56,7 +56,6 @@ impl Display for ErrorType { ErrorType::RangeError => "RangeError", ErrorType::TypeError => "TypeError", } - .fmt(f) } } From 6a84f78cdc4914bbd5ecafc8a9cc6d1cdf5e439e Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Thu, 15 Sep 2022 14:00:27 -0500 Subject: [PATCH 3/5] cargo update --- Cargo.lock | 228 ++++++++++++++++++++++++++++------------------------- 1 file changed, 119 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc882f59c41..06f8bc9b7e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "byteorder" @@ -285,18 +285,18 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] [[package]] name = "clipboard-win" -version = "4.4.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db" +checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219" dependencies = [ "error-code", "str-buf", @@ -358,9 +358,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", @@ -368,9 +368,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -379,9 +379,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" dependencies = [ "autocfg", "cfg-if", @@ -393,9 +393,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ "cfg-if", "once_cell", @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "cstr_core" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644828c273c063ab0d39486ba42a5d1f3a499d35529c759e763a9c6cb8a0fb08" +checksum = "dd98742e4fdca832d40cab219dc2e3048de17d873248f83f17df47c1bea70956" dependencies = [ "cty", "memchr", @@ -457,9 +457,9 @@ checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "endian-type" @@ -506,9 +506,9 @@ checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" [[package]] name = "fd-lock" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca" +checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517" dependencies = [ "cfg-if", "rustix", @@ -593,9 +593,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -614,9 +614,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.47" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7" +checksum = "237a0714f28b1ee39ccec0770ccb544eb02c9ef2c82bb096230eefcffa6468b0" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -637,7 +637,7 @@ dependencies = [ "icu_provider", "serde", "tinystr", - "zerovec", + "zerovec 0.7.0", ] [[package]] @@ -657,7 +657,7 @@ dependencies = [ "smallvec", "tinystr", "writeable", - "zerovec", + "zerovec 0.7.0", ] [[package]] @@ -671,7 +671,7 @@ dependencies = [ "litemap", "serde", "tinystr", - "zerovec", + "zerovec 0.7.0", ] [[package]] @@ -685,7 +685,7 @@ dependencies = [ "serde", "tinystr", "writeable", - "zerovec", + "zerovec 0.7.0", ] [[package]] @@ -699,7 +699,7 @@ dependencies = [ "icu_locid", "icu_provider", "serde", - "zerovec", + "zerovec 0.7.0", ] [[package]] @@ -717,7 +717,7 @@ dependencies = [ "writeable", "yoke", "zerofrom", - "zerovec", + "zerovec 0.7.0", ] [[package]] @@ -731,7 +731,7 @@ dependencies = [ "serde", "writeable", "yoke", - "zerovec", + "zerovec 0.7.0", ] [[package]] @@ -776,30 +776,30 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "0.6.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504" +checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "jemalloc-sys" -version = "0.5.0+5.3.0" +version = "0.5.1+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f655c3ecfa6b0d03634595b4b54551d4bd5ac208b9e0124873949a7ab168f70b" +checksum = "b7c2b313609b95939cb0c5a5c6917fb9b7c9394562aa3ef44eb66ffa51736432" dependencies = [ "cc", "fs_extra", @@ -818,9 +818,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -833,9 +833,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "linux-raw-sys" @@ -855,9 +855,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" dependencies = [ "autocfg", "scopeguard", @@ -985,9 +985,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "parking_lot" @@ -1035,9 +1035,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b450720b6f75cfbfabc195814bd3765f337a4f9a83186f8537297cac12f6705" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" dependencies = [ "phf_shared", "rand", @@ -1067,9 +1067,9 @@ dependencies = [ [[package]] name = "plotters" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" dependencies = [ "num-traits", "plotters-backend", @@ -1080,15 +1080,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" [[package]] name = "plotters-svg" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" dependencies = [ "plotters-backend", ] @@ -1141,18 +1141,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -1190,9 +1190,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -1223,9 +1223,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -1275,16 +1275,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.34.8" +version = "0.35.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c267b8394eb529872c3cf92e181c378b41fea36e68130357b52493701d2e" +checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "winapi", + "windows-sys", ] [[package]] @@ -1323,9 +1323,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "ryu-js" @@ -1400,9 +1400,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "smallvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" dependencies = [ "serde", ] @@ -1433,9 +1433,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -1492,18 +1492,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" dependencies = [ "proc-macro2", "quote", @@ -1523,13 +1523,14 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a34eb32d8589368c99f8d6c0b9be3d09480d992da08c5990702b3e6b9260af8" +checksum = "4dfb77d2490072fb5616d67686f55481b3d97701e383e208a7225843eba1aae6" dependencies = [ "displaydoc", "serde", - "zerovec", + "zerovec 0.7.0", + "zerovec 0.8.1", ] [[package]] @@ -1565,9 +1566,9 @@ checksum = "1218098468b8085b19a2824104c70d976491d247ce194bbd9dc77181150cdfd6" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-normalization" @@ -1580,27 +1581,27 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-libyaml" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931179334a56395bcf64ba5e0ff56781381c1a5832178280c7d7f91d1679aeb0" +checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" [[package]] name = "utf8parse" @@ -1693,9 +1694,9 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -1734,9 +1735,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.30.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -1747,39 +1748,39 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.30.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_i686_gnu" -version = "0.30.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_msvc" -version = "0.30.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_x86_64_gnu" -version = "0.30.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_msvc" -version = "0.30.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "writeable" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae065fb2b24446a45c2bea0fe256b4ca54032661ee72d2500aaadeeec41b8e4e" +checksum = "d8ab608ef0f68f7b5e1f17a38342cbc2725bf212f6ba9f103b0e05f675c41d83" [[package]] name = "yoke" @@ -1807,9 +1808,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aed578cc7fa1c85290bdaca18fa5ac8a9365ddd9ed54af4380a6c5e13d9fc484" +checksum = "79e9355fccf72b04b7deaa99ce7a0f6630530acf34045391b74460fcd714de54" dependencies = [ "zerofrom-derive", ] @@ -1838,6 +1839,15 @@ dependencies = [ "zerovec-derive", ] +[[package]] +name = "zerovec" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3412f49402c32fffcc98fa861dc496eaa777442c5a5fc1e8d33d0fbb53cb0d2" +dependencies = [ + "zerofrom", +] + [[package]] name = "zerovec-derive" version = "0.6.0" From 74baea6327f4c5eb0cd7b0a0084e733880eaf496 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Thu, 15 Sep 2022 19:04:40 -0500 Subject: [PATCH 4/5] Add documentation --- Cargo.lock | 1 + boa_engine/Cargo.toml | 1 + .../src/builtins/async_generator/mod.rs | 13 +- boa_engine/src/builtins/generator/mod.rs | 10 +- boa_engine/src/builtins/promise/mod.rs | 16 +- .../src/builtins/promise/promise_job.rs | 4 +- boa_engine/src/error/mod.rs | 333 ++++++++++++++---- boa_engine/src/error/vos.rs | 2 +- boa_engine/src/vm/mod.rs | 16 +- 9 files changed, 295 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06f8bc9b7e9..fe1e7436798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,7 @@ dependencies = [ "serde_json", "sys-locale", "tap", + "thiserror", "unicode-normalization", ] diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 492b9e610fa..2f541bb2733 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -50,6 +50,7 @@ unicode-normalization = "0.1.21" dyn-clone = "1.0.9" once_cell = "1.14.0" tap = "1.0.1" +thiserror = "1.0.35" icu_locale_canonicalizer = { version = "0.6.0", features = ["serde"], optional = true } icu_locid = { version = "0.6.0", features = ["serde"], optional = true } icu_datetime = { version = "0.6.0", features = ["serde"], optional = true } diff --git a/boa_engine/src/builtins/async_generator/mod.rs b/boa_engine/src/builtins/async_generator/mod.rs index a42c2a5912a..e6ea7180641 100644 --- a/boa_engine/src/builtins/async_generator/mod.rs +++ b/boa_engine/src/builtins/async_generator/mod.rs @@ -16,7 +16,7 @@ use crate::{ symbol::WellKnownSymbols, value::JsValue, vm::GeneratorResumeKind, - Context, JsResult, + Context, JsError, JsResult, }; use boa_gc::{Cell, Finalize, Gc, Trace}; use boa_profiler::Profiler; @@ -399,7 +399,10 @@ impl AsyncGenerator { } // 8. Let completion be ThrowCompletion(exception). - let completion = (Err(args.get_or_undefined(0).clone().into()), false); + let completion = ( + Err(JsError::from_opaque(args.get_or_undefined(0).clone())), + false, + ); // 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). generator.enqueue(completion.clone(), promise_capability.clone()); @@ -473,7 +476,7 @@ impl AsyncGenerator { // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »). promise_capability .reject() - .call(&JsValue::undefined(), &[e.to_value(context)], context) + .call(&JsValue::undefined(), &[e.to_opaque(context)], context) .expect("cannot fail per spec"); } // 8. Else, @@ -552,7 +555,7 @@ impl AsyncGenerator { } } (Err(value), _) => { - let value = value.to_value(context); + let value = value.to_opaque(context); context.vm.push(value); context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; } @@ -669,7 +672,7 @@ impl AsyncGenerator { gen.state = AsyncGeneratorState::Completed; // b. Let result be ThrowCompletion(reason). - let result = Err(args.get_or_undefined(0).clone().into()); + let result = Err(JsError::from_opaque(args.get_or_undefined(0).clone())); // c. Perform AsyncGeneratorCompleteStep(generator, result, true). let next = gen.queue.pop_front().expect("must have one entry"); diff --git a/boa_engine/src/builtins/generator/mod.rs b/boa_engine/src/builtins/generator/mod.rs index 0a737a7ea74..74eea06fc13 100644 --- a/boa_engine/src/builtins/generator/mod.rs +++ b/boa_engine/src/builtins/generator/mod.rs @@ -18,7 +18,7 @@ use crate::{ symbol::WellKnownSymbols, value::JsValue, vm::{CallFrame, GeneratorResumeKind, ReturnType}, - Context, JsResult, + Context, JsError, JsResult, }; use boa_gc::{Cell, Finalize, Gc, Trace}; use boa_profiler::Profiler; @@ -198,7 +198,11 @@ impl Generator { // 1. Let g be the this value. // 2. Let C be ThrowCompletion(exception). // 3. Return ? GeneratorResumeAbrupt(g, C, empty). - Self::generator_resume_abrupt(this, Err(args.get_or_undefined(0).clone().into()), context) + Self::generator_resume_abrupt( + this, + Err(JsError::from_opaque(args.get_or_undefined(0).clone())), + context, + ) } /// `27.5.3.3 GeneratorResume ( generator, value, generatorBrand )` @@ -386,7 +390,7 @@ impl Generator { context.run() } Err(value) => { - let value = value.to_value(context); + let value = value.to_opaque(context); context.vm.push(value); context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; context.run() diff --git a/boa_engine/src/builtins/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index 02c5470eb66..ff0ee49acd2 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -19,7 +19,7 @@ use crate::{ property::{Attribute, PropertyDescriptorBuilder}, symbol::WellKnownSymbols, value::JsValue, - Context, JsResult, + Context, JsError, JsResult, }; use boa_gc::{Cell as GcCell, Finalize, Gc, Trace}; use boa_profiler::Profiler; @@ -39,7 +39,7 @@ macro_rules! if_abrupt_reject_promise { let $value = match $value { // 1. If value is an abrupt completion, then Err(err) => { - let err = err.to_value($context); + let err = err.to_opaque($context); // a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). $context.call( &$capability.reject().clone().into(), @@ -338,7 +338,7 @@ impl Promise { // 10. If completion is an abrupt completion, then if let Err(e) = completion { - let e = e.to_value(context); + let e = e.to_opaque(context); // a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »). context.call(&resolving_functions.reject, &JsValue::Undefined, &[e])?; } @@ -1064,7 +1064,7 @@ impl Promise { .expect("cannot fail per spec"); // 3. Return ThrowCompletion(error). - return Err(error.into()); + return Err(JsError::from_opaque(error.into())); } // iv. Return resultCapability.[[Promise]]. @@ -1255,14 +1255,14 @@ impl Promise { // a. Let selfResolutionError be a newly created TypeError object. let self_resolution_error = JsNativeError::typ() .with_message("SameValue(resolution, promise) is true") - .to_value(context); + .to_opaque(context); // b. Perform RejectPromise(promise, selfResolutionError). promise .borrow_mut() .as_promise_mut() .expect("Expected promise to be a Promise") - .reject_promise(&self_resolution_error, context); + .reject_promise(&self_resolution_error.into(), context); // c. Return undefined. return Ok(JsValue::Undefined); @@ -1292,7 +1292,7 @@ impl Promise { .borrow_mut() .as_promise_mut() .expect("Expected promise to be a Promise") - .reject_promise(&e.to_value(context), context); + .reject_promise(&e.to_opaque(context), context); // b. Return undefined. return Ok(JsValue::Undefined); @@ -1834,7 +1834,7 @@ impl Promise { context, |_this, _args, captures, _context| { // 1. Return ThrowCompletion(reason). - Err(captures.reason.clone().into()) + Err(JsError::from_opaque(captures.reason.clone())) }, ThrowReasonCaptures { reason: reason.clone(), diff --git a/boa_engine/src/builtins/promise/promise_job.rs b/boa_engine/src/builtins/promise/promise_job.rs index 8361e947aa3..af97c6887b6 100644 --- a/boa_engine/src/builtins/promise/promise_job.rs +++ b/boa_engine/src/builtins/promise/promise_job.rs @@ -56,7 +56,7 @@ impl PromiseJob { // e. Else, let handlerResult be Completion(HostCallJobCallback(handler, undefined, « argument »)). Some(handler) => handler .call_job_callback(&JsValue::Undefined, &[argument.clone()], context) - .map_err(|e| e.to_value(context)), + .map_err(|e| e.to_opaque(context)), }; match promise_capability { @@ -146,7 +146,7 @@ impl PromiseJob { // c. If thenCallResult is an abrupt completion, then if let Err(value) = then_call_result { - let value = value.to_value(context); + let value = value.to_opaque(context); // i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »). return context.call( &resolving_functions.reject, diff --git a/boa_engine/src/error/mod.rs b/boa_engine/src/error/mod.rs index ea24664dd43..b801cbd6766 100644 --- a/boa_engine/src/error/mod.rs +++ b/boa_engine/src/error/mod.rs @@ -1,9 +1,5 @@ mod vos; -use std::borrow::Cow; - -use boa_gc::{Finalize, Trace}; - use crate::{ builtins::{error::ErrorKind, Array}, object::JsObject, @@ -12,28 +8,109 @@ use crate::{ syntax::parser, Context, JsResult, JsValue, }; - +use boa_gc::{Finalize, Trace}; +use std::borrow::Cow; +use thiserror::Error; use vos::ViewStr; +/// The error type returned by all operations related +/// to the execution of Javascript code. +/// +/// This is essentially an enum that can store either [`JsNativeError`]s (for ideal +/// native errors) or opaque [`JsValue`]s, since Javascript allows throwing any valid +/// `JsValue`. +/// +/// The implementation doesn't provide a [`From`] conversion +/// for `JsValue`. This is with the intent of encouraging the usage of proper +/// `JsNativeError`s instead of plain `JsValue`s. However, if you +/// do need a proper opaque error, you can construct one using the +/// [`JsError::from_opaque`] method. +/// +/// # Examples +/// +/// ```rust +/// # use boa_engine::{JsError, JsNativeError, JsNativeErrorKind, JsValue}; +/// let cause = JsError::from_opaque(JsValue::from("error!")); +/// let native_error: JsError = JsNativeError::typ() +/// .with_message("invalid type!") +/// .with_cause(cause) +/// .into(); +/// +/// assert!(native_error.as_native().is_some()); +/// assert!(matches!( +/// native_error.as_native().unwrap().kind, +/// JsNativeErrorKind::Type, +/// )); +/// ``` #[derive(Debug, Clone, Trace, Finalize)] pub struct JsError { inner: Repr, } +// hiding the enum makes it a bit more difficult +// to treat `JsError` as a proper enum, which should +// make it a bit harder to try to match against a native error +// without calling `try_native` first. #[derive(Debug, Clone, Trace, Finalize)] enum Repr { Native(JsNativeError), Opaque(JsValue), } +impl std::error::Error for JsError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self.inner { + Repr::Native(err) => err.source(), + Repr::Opaque(_) => None, + } + } +} + impl JsError { - pub fn to_value(&self, context: &mut Context) -> JsValue { + /// Creates a new `JsError` from a native error `err`. + pub fn from_native(err: JsNativeError) -> Self { + Self { + inner: Repr::Native(err), + } + } + + /// Creates a new `JsError` from an opaque error `value`. + pub fn from_opaque(value: JsValue) -> Self { + Self { + inner: Repr::Opaque(value), + } + } + + /// Converts the error to an opaque `JsValue` error + /// + /// Unwraps the inner `JsValue` if the error is already an opaque error. + pub fn to_opaque(&self, context: &mut Context) -> JsValue { match &self.inner { - Repr::Native(e) => e.to_value(context), + Repr::Native(e) => e.to_opaque(context).into(), Repr::Opaque(v) => v.clone(), } } + /// Unwraps the inner error if this contains a native error. + /// Otherwise, it will inspect the opaque error and try to extract the + /// necessary information to construct a native error similar to the provided + /// opaque error. + /// + /// # Note 1 + /// + /// This method won't try to make any conversions between JS types. + /// In other words, for this conversion to succeed: + /// - `message` **MUST** be a `JsString` value. + /// - `errors` (in the case of `AggregateError`s) **MUST** be an `Array` object. + /// + /// # Note 2 + /// + /// This operation should be considered a lossy conversion, since it + /// won't store any additional properties of the opaque + /// error, other than `message`, `cause` and `errors` (in the case of + /// `AggregateError`s). If you cannot affort a lossy conversion, clone + /// the object before calling [`from_opaque`][JsError::from_opaque] + /// to preserve its original properties. // TODO: Should probably change this to return a custom error instead pub fn try_native(&self, context: &mut Context) -> JsResult { match &self.inner { @@ -54,11 +131,10 @@ impl JsError { "".into() }; - let cause = if obj.has_property("cause", context)? { - Some(obj.get("cause", context)?) - } else { - None - }; + let cause = obj + .has_property("cause", context)? + .then(|| obj.get("cause", context)) + .transpose()?; let kind = match error { ErrorKind::Error => JsNativeErrorKind::Error, @@ -75,7 +151,9 @@ impl JsError { Some(errors) if errors.is_array() => { let length = errors.length_of_array_like(context)?; for i in 0..length { - error_list.push(errors.get(i, context)?.into()); + error_list.push(JsError::from_opaque( + errors.get(i, context)?, + )); } } _ => { @@ -94,18 +172,19 @@ impl JsError { return Ok(JsNativeError { kind, message, - cause, + cause: cause.map(|v| Box::new(JsError::from_opaque(v))), }); } } Err(JsNativeError::typ() .with_message("failed to convert value to native error") - .with_cause(val) .into()) } } } + /// Gets the inner [`JsValue`] if the error is an opaque error, + /// or `None` otherwise. pub fn as_opaque(&self) -> Option<&JsValue> { match self.inner { Repr::Native(_) => None, @@ -113,6 +192,8 @@ impl JsError { } } + /// Gets the inner [`JsNativeError`] if the error is a native + /// error, or `None` otherwise. pub fn as_native(&self) -> Option<&JsNativeError> { match self.inner { Repr::Native(ref e) => Some(e), @@ -135,22 +216,6 @@ impl From for JsError { } } -impl From for JsError { - fn from(value: JsValue) -> Self { - Self { - inner: Repr::Opaque(value), - } - } -} - -impl From for JsError { - fn from(object: JsObject) -> Self { - Self { - inner: Repr::Opaque(object.into()), - } - } -} - impl std::fmt::Display for JsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.inner { @@ -160,16 +225,24 @@ impl std::fmt::Display for JsError { } } -#[derive(Debug, Clone, Trace, Finalize)] +/// Native representation of an ideal `Error` object from Javascript. +/// +/// +#[derive(Debug, Clone, Trace, Finalize, Error)] +#[error("{kind}: {message}")] pub struct JsNativeError { + /// The kind of native error (e.g. `TypeError`, `SyntaxError`, etc.) pub kind: JsNativeErrorKind, #[unsafe_ignore_trace] message: ViewStr, - cause: Option, + #[source] + cause: Option>, } impl JsNativeError { - fn new(kind: JsNativeErrorKind, message: ViewStr, cause: Option) -> Self { + /// Creates a new `JsNativeError` from its `kind`, `message` and (optionally) + /// its `cause`. + fn new(kind: JsNativeErrorKind, message: ViewStr, cause: Option>) -> Self { Self { kind, message, @@ -177,31 +250,62 @@ impl JsNativeError { } } + /// Creates a new `JsNativeError` of kind `AggregateError` from + /// a list of [`JsError`]s, with empty `message` and undefined `cause`. pub fn aggregate(errors: Vec) -> Self { Self::new(JsNativeErrorKind::Aggregate(errors), "".into(), None) } + + /// Creates a new `JsNativeError` of kind `Error`, with empty `message` + /// and undefined `cause`. pub fn error() -> Self { Self::new(JsNativeErrorKind::Error, "".into(), None) } + + /// Creates a new `JsNativeError` of kind `EvalError`, with empty `message` + /// and undefined `cause`. pub fn eval() -> Self { Self::new(JsNativeErrorKind::Eval, "".into(), None) } + + /// Creates a new `JsNativeError` of kind `RangeError`, with empty `message` + /// and undefined `cause`. pub fn range() -> Self { Self::new(JsNativeErrorKind::Range, "".into(), None) } + + /// Creates a new `JsNativeError` of kind `ReferenceError`, with empty `message` + /// and undefined `cause`. pub fn reference() -> Self { Self::new(JsNativeErrorKind::Reference, "".into(), None) } + + /// Creates a new `JsNativeError` of kind `SyntaxError`, with empty `message` + /// and undefined `cause`. pub fn syntax() -> Self { Self::new(JsNativeErrorKind::Syntax, "".into(), None) } + + /// Creates a new `JsNativeError` of kind `TypeError`, with empty `message` + /// and undefined `cause`. pub fn typ() -> Self { Self::new(JsNativeErrorKind::Type, "".into(), None) } + + /// Creates a new `JsNativeError` of kind `UriError`, with empty `message` + /// and undefined `cause`. pub fn uri() -> Self { Self::new(JsNativeErrorKind::Uri, "".into(), None) } + /// Appends a message to this error. + /// + /// # Note + /// + /// A static [`str`] will be stored as a simple pointer, + /// while a [`String`] will be converted to a [`Rc`][std::rc::Rc] in order + /// to make clones more efficient. Prefer static `str`s if you want + /// efficiency, and [`String`] s if you prefer descriptive error messages. #[must_use] pub fn with_message(mut self, message: S) -> Self where @@ -211,53 +315,80 @@ impl JsNativeError { self } + /// Appends a cause to this error. #[must_use] pub fn with_cause(mut self, cause: V) -> Self where - V: Into, + V: Into, { - self.cause = Some(cause.into()); + self.cause = Some(Box::new(cause.into())); self } + /// Gets the `message` of this error. + /// + /// This is equivalent to the [`NativeError.prototype.message`][spec] + /// property. + /// + /// [spec]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message pub fn message(&self) -> &str { &self.message } - pub fn cause(&self) -> Option<&JsValue> { - self.cause.as_ref() + /// Gets the `cause` of this error. + /// + /// This is equivalent to the [`NativeError.prototype.cause`][spec] + /// property. + /// + /// [spec]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause + pub fn cause(&self) -> Option<&JsError> { + self.cause.as_deref() } - pub fn to_value(&self, context: &mut Context) -> JsValue { + /// Converts this native error to its opaque representation as a + /// [`JsObject`]. + pub fn to_opaque(&self, context: &mut Context) -> JsObject { let Self { kind, message, cause, } = self; let constructors = context.intrinsics().constructors(); - let prototype = match kind { - JsNativeErrorKind::Aggregate(_) => constructors.aggregate_error().prototype(), - JsNativeErrorKind::Error => constructors.error().prototype(), - JsNativeErrorKind::Eval => constructors.eval_error().prototype(), - JsNativeErrorKind::Range => constructors.range_error().prototype(), - JsNativeErrorKind::Reference => constructors.reference_error().prototype(), - JsNativeErrorKind::Syntax => constructors.syntax_error().prototype(), - JsNativeErrorKind::Type => constructors.type_error().prototype(), - JsNativeErrorKind::Uri => constructors.uri_error().prototype(), + let (prototype, tag) = match kind { + JsNativeErrorKind::Aggregate(_) => ( + constructors.aggregate_error().prototype(), + ErrorKind::Aggregate, + ), + JsNativeErrorKind::Error => (constructors.error().prototype(), ErrorKind::Error), + JsNativeErrorKind::Eval => (constructors.eval_error().prototype(), ErrorKind::Eval), + JsNativeErrorKind::Range => (constructors.range_error().prototype(), ErrorKind::Range), + JsNativeErrorKind::Reference => ( + constructors.reference_error().prototype(), + ErrorKind::Reference, + ), + JsNativeErrorKind::Syntax => { + (constructors.syntax_error().prototype(), ErrorKind::Syntax) + } + JsNativeErrorKind::Type => (constructors.type_error().prototype(), ErrorKind::Type), + JsNativeErrorKind::Uri => (constructors.uri_error().prototype(), ErrorKind::Uri), }; - let o = JsObject::from_proto_and_data(prototype, ObjectData::error(kind.as_error_kind())); + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(tag)); o.create_non_enumerable_data_property_or_throw("message", &**message, context); if let Some(cause) = cause { - o.create_non_enumerable_data_property_or_throw("cause", cause.clone(), context); + o.create_non_enumerable_data_property_or_throw( + "cause", + cause.to_opaque(context), + context, + ); } if let JsNativeErrorKind::Aggregate(errors) = kind { let errors = errors .iter() - .map(|e| e.to_value(context)) + .map(|e| e.to_opaque(context)) .collect::>(); let errors = Array::create_array_from_list(errors, context); o.define_property_or_throw( @@ -271,14 +402,7 @@ impl JsNativeError { ) .expect("The spec guarantees this succeeds for a newly created object "); } - o.into() - } -} - -impl std::fmt::Display for JsNativeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self { kind, message, .. } = self; - write!(f, "{kind}: {message}") + o } } @@ -288,34 +412,95 @@ impl From for JsNativeError { } } +/// The list of possible error types a [`JsNativeError`] can be. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-error-objects +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error #[derive(Debug, Clone, Trace, Finalize)] #[non_exhaustive] pub enum JsNativeErrorKind { + /// A collection of errors wrapped in a single error. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-objects + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError Aggregate(Vec), + /// A generic error. Commonly used as the base for custom exceptions. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-error-constructor + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error Error, + /// An error related to the global function [`eval()`][eval]. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError + /// [eval]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval Eval, + /// An error thrown when a value is outside its valid range. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError Range, + /// An error representing an invalid de-reference of a variable. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError Reference, + /// An error representing an invalid syntax in the Javascript language. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError Syntax, + /// An error thrown when a variable or argument is not of a valid type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError Type, + /// An error thrown when the [`encodeURI()`][e_uri] and [`decodeURI()`][d_uri] + /// functions receive invalid parameters. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError + /// [e_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI + /// [d_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI Uri, } -impl JsNativeErrorKind { - fn as_error_kind(&self) -> ErrorKind { - match self { - JsNativeErrorKind::Aggregate(_) => ErrorKind::Aggregate, - JsNativeErrorKind::Error => ErrorKind::Error, - JsNativeErrorKind::Eval => ErrorKind::Eval, - JsNativeErrorKind::Range => ErrorKind::Range, - JsNativeErrorKind::Reference => ErrorKind::Reference, - JsNativeErrorKind::Syntax => ErrorKind::Syntax, - JsNativeErrorKind::Type => ErrorKind::Type, - JsNativeErrorKind::Uri => ErrorKind::Uri, - } - } -} - impl std::fmt::Display for JsNativeErrorKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/boa_engine/src/error/vos.rs b/boa_engine/src/error/vos.rs index cb528f290cf..8b0a827de21 100644 --- a/boa_engine/src/error/vos.rs +++ b/boa_engine/src/error/vos.rs @@ -6,7 +6,7 @@ use std::{ }; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) enum ViewStr { +pub(super) enum ViewStr { Static(&'static str), Shared(Rc>), } diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 34f256cc6ca..76e7cf380d2 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -18,7 +18,7 @@ use crate::{ call_frame::CatchAddresses, code_block::{initialize_instance_elements, Readable}, }, - Context, JsBigInt, JsResult, JsString, JsValue, + Context, JsBigInt, JsError, JsResult, JsString, JsValue, }; use boa_interner::ToInternedString; use boa_profiler::Profiler; @@ -1430,7 +1430,7 @@ impl Context { } Opcode::Throw => { let value = self.vm.pop(); - return Err(value.into()); + return Err(JsError::from_opaque(value)); } Opcode::TryStart => { let next = self.vm.read::(); @@ -1513,7 +1513,7 @@ impl Context { return Ok(ShouldExit::True); } FinallyReturn::Err => { - return Err(self.vm.pop().into()); + return Err(JsError::from_opaque(self.vm.pop())); } } } @@ -2279,7 +2279,7 @@ impl Context { GeneratorResumeKind::Normal => return Ok(ShouldExit::False), GeneratorResumeKind::Throw => { let received = self.vm.pop(); - return Err(received.into()); + return Err(JsError::from_opaque(received)); } GeneratorResumeKind::Return => { let mut finally_left = false; @@ -2306,7 +2306,7 @@ impl Context { let value = self.vm.pop(); if self.vm.frame().generator_resume_kind == GeneratorResumeKind::Throw { - return Err(value.into()); + return Err(JsError::from_opaque(value)); } let completion = Ok(value); @@ -2336,7 +2336,7 @@ impl Context { if *r#return { let value = match completion { Ok(value) => value.clone(), - Err(e) => e.clone().to_value(self), + Err(e) => e.clone().to_opaque(self), }; self.vm.push(value); self.vm.push(true); @@ -2725,14 +2725,14 @@ impl Context { self.vm.frame_mut().catch.pop(); self.vm.frame_mut().finally_return = FinallyReturn::Err; self.vm.frame_mut().thrown = true; - let e = e.to_value(self); + let e = e.to_opaque(self); self.vm.push(e); } else { self.vm.stack.truncate(start_stack_size); // Step 3.f in [AsyncBlockStart](https://tc39.es/ecma262/#sec-asyncblockstart). if let Some(promise_capability) = promise_capability { - let e = e.to_value(self); + let e = e.to_opaque(self); promise_capability .reject() .call(&JsValue::undefined(), &[e.clone()], self) From f34a2907209420c4f1a4db22413ccc417746bc93 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Fri, 16 Sep 2022 01:56:03 -0500 Subject: [PATCH 5/5] Add doc examples --- boa_cli/src/main.rs | 1 - boa_engine/src/builtins/number/mod.rs | 2 - boa_engine/src/class.rs | 16 +- boa_engine/src/context/mod.rs | 43 +--- boa_engine/src/error/mod.rs | 236 +++++++++++++++++- boa_engine/src/object/jsmap.rs | 35 ++- boa_engine/src/object/mod.rs | 12 +- boa_engine/src/symbol.rs | 2 +- boa_engine/src/syntax/ast/node/parameters.rs | 8 +- boa_engine/src/syntax/ast/node/switch/mod.rs | 1 - .../syntax/parser/expression/identifiers.rs | 1 - 11 files changed, 271 insertions(+), 86 deletions(-) diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index 9084d1d7164..00dd5230083 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -112,7 +112,6 @@ impl Opt { #[derive(Debug, Clone, ArgEnum)] enum DumpFormat { /// The different types of format available for dumping. - /// // NOTE: This can easily support other formats just by // adding a field to this enum and adding the necessary // implementation. Example: Toml, Html, etc. diff --git a/boa_engine/src/builtins/number/mod.rs b/boa_engine/src/builtins/number/mod.rs index d6ecd7545b3..29e6be34c5a 100644 --- a/boa_engine/src/builtins/number/mod.rs +++ b/boa_engine/src/builtins/number/mod.rs @@ -325,7 +325,6 @@ impl Number { /// /// This function traverses a string representing a number, /// returning the floored log10 of this number. - /// fn flt_str_to_exp(flt: &str) -> i32 { let mut non_zero_encountered = false; let mut dot_encountered = false; @@ -359,7 +358,6 @@ impl Number { /// the exponent. The string is kept at an exact length of `precision`. /// /// When this procedure returns, `digits` is exactly `precision` long. - /// fn round_to_precision(digits: &mut String, precision: usize) -> bool { if digits.len() > precision { let to_round = digits.split_off(precision); diff --git a/boa_engine/src/class.rs b/boa_engine/src/class.rs index 860e5ae147d..c0619cc4237 100644 --- a/boa_engine/src/class.rs +++ b/boa_engine/src/class.rs @@ -2,14 +2,14 @@ //! //! Native classes are implemented through the [`Class`][class-trait] trait. //! ``` -//!# use boa_engine::{ -//!# property::Attribute, -//!# class::{Class, ClassBuilder}, -//!# Context, JsResult, JsValue, -//!# builtins::JsArgs, -//!# }; -//!# use boa_gc::{Finalize, Trace}; -//!# +//! # use boa_engine::{ +//! # property::Attribute, +//! # class::{Class, ClassBuilder}, +//! # Context, JsResult, JsValue, +//! # builtins::JsArgs, +//! # }; +//! # use boa_gc::{Finalize, Trace}; +//! # //! // This does not have to be an enum it can also be a struct. //! #[derive(Debug, Trace, Finalize)] //! enum Animal { diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 6899e711b74..6dbdfe80b5f 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -48,9 +48,9 @@ pub use icu::BoaProvider; /// /// ```rust /// use boa_engine::{ -/// Context, /// object::ObjectInitializer, -/// property::{Attribute, PropertyDescriptor} +/// property::{Attribute, PropertyDescriptor}, +/// Context, /// }; /// /// let script = r#" @@ -71,11 +71,7 @@ pub use icu::BoaProvider; /// let arg = ObjectInitializer::new(&mut context) /// .property("x", 12, Attribute::READONLY) /// .build(); -/// context.register_global_property( -/// "arg", -/// arg, -/// Attribute::all() -/// ); +/// context.register_global_property("arg", arg, Attribute::all()); /// /// let value = context.eval("test(arg)").unwrap(); /// @@ -422,36 +418,20 @@ impl Context { /// # Example /// ``` /// use boa_engine::{ - /// Context, + /// object::ObjectInitializer, /// property::{Attribute, PropertyDescriptor}, - /// object::ObjectInitializer + /// Context, /// }; /// /// let mut context = Context::default(); /// - /// context.register_global_property( - /// "myPrimitiveProperty", - /// 10, - /// Attribute::all() - /// ); + /// context.register_global_property("myPrimitiveProperty", 10, Attribute::all()); /// /// let object = ObjectInitializer::new(&mut context) - /// .property( - /// "x", - /// 0, - /// Attribute::all() - /// ) - /// .property( - /// "y", - /// 1, - /// Attribute::all() - /// ) - /// .build(); - /// context.register_global_property( - /// "myObjectProperty", - /// object, - /// Attribute::all() - /// ); + /// .property("x", 0, Attribute::all()) + /// .property("y", 1, Attribute::all()) + /// .build(); + /// context.register_global_property("myObjectProperty", object, Attribute::all()); /// ``` #[inline] pub fn register_global_property(&mut self, key: K, value: V, attribute: Attribute) @@ -474,7 +454,7 @@ impl Context { /// /// # Examples /// ``` - ///# use boa_engine::Context; + /// # use boa_engine::Context; /// let mut context = Context::default(); /// /// let value = context.eval("1 + 3").unwrap(); @@ -610,7 +590,6 @@ impl Context { /// Additionally, if the `intl` feature is enabled, [`ContextBuilder`] becomes /// the only way to create a new [`Context`], since now it requires a /// valid data provider for the `Intl` functionality. -/// #[cfg_attr( feature = "intl", doc = "The required data in a valid provider is specified in [`BoaProvider`]" diff --git a/boa_engine/src/error/mod.rs b/boa_engine/src/error/mod.rs index b801cbd6766..65f285a15c4 100644 --- a/boa_engine/src/error/mod.rs +++ b/boa_engine/src/error/mod.rs @@ -30,17 +30,20 @@ use vos::ViewStr; /// /// ```rust /// # use boa_engine::{JsError, JsNativeError, JsNativeErrorKind, JsValue}; -/// let cause = JsError::from_opaque(JsValue::from("error!")); +/// let cause = JsError::from_opaque("error!".into()); +/// +/// assert!(cause.as_opaque().is_some()); +/// assert_eq!(cause.as_opaque().unwrap(), &JsValue::from("error!")); +/// /// let native_error: JsError = JsNativeError::typ() /// .with_message("invalid type!") /// .with_cause(cause) /// .into(); /// /// assert!(native_error.as_native().is_some()); -/// assert!(matches!( -/// native_error.as_native().unwrap().kind, -/// JsNativeErrorKind::Type, -/// )); +/// +/// let kind = &native_error.as_native().unwrap().kind; +/// assert!(matches!(kind, JsNativeErrorKind::Type)); /// ``` #[derive(Debug, Clone, Trace, Finalize)] pub struct JsError { @@ -68,6 +71,15 @@ impl std::error::Error for JsError { impl JsError { /// Creates a new `JsError` from a native error `err`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsError, JsNativeError}; + /// let error = JsError::from_native(JsNativeError::syntax()); + /// + /// assert!(error.as_native().is_some()); + /// ``` pub fn from_native(err: JsNativeError) -> Self { Self { inner: Repr::Native(err), @@ -75,6 +87,15 @@ impl JsError { } /// Creates a new `JsError` from an opaque error `value`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::JsError; + /// let error = JsError::from_opaque(5.0f64.into()); + /// + /// assert!(error.as_opaque().is_some()); + /// ``` pub fn from_opaque(value: JsValue) -> Self { Self { inner: Repr::Opaque(value), @@ -84,6 +105,17 @@ impl JsError { /// Converts the error to an opaque `JsValue` error /// /// Unwraps the inner `JsValue` if the error is already an opaque error. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{Context, JsError, JsNativeError}; + /// let context = &mut Context::default(); + /// let error: JsError = JsNativeError::eval().with_message("invalid script").into(); + /// let error_val = error.to_opaque(context); + /// + /// assert!(error_val.as_object().unwrap().borrow().is_error()); + /// ``` pub fn to_opaque(&self, context: &mut Context) -> JsValue { match &self.inner { Repr::Native(e) => e.to_opaque(context).into(), @@ -111,6 +143,23 @@ impl JsError { /// `AggregateError`s). If you cannot affort a lossy conversion, clone /// the object before calling [`from_opaque`][JsError::from_opaque] /// to preserve its original properties. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{Context, JsError, JsNativeError, JsNativeErrorKind}; + /// let context = &mut Context::default(); + /// + /// // create a new, opaque Error object + /// let error: JsError = JsNativeError::typ().with_message("type error!").into(); + /// let error_val = error.to_opaque(context); + /// + /// // then, try to recover the original + /// let error = JsError::from_opaque(error_val).try_native(context).unwrap(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Type)); + /// assert_eq!(error.message(), "type error!"); + /// ``` // TODO: Should probably change this to return a custom error instead pub fn try_native(&self, context: &mut Context) -> JsResult { match &self.inner { @@ -185,6 +234,21 @@ impl JsError { /// Gets the inner [`JsValue`] if the error is an opaque error, /// or `None` otherwise. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsError, JsNativeError}; + /// let error: JsError = JsNativeError::reference() + /// .with_message("variable not found!") + /// .into(); + /// + /// assert!(error.as_opaque().is_none()); + /// + /// let error = JsError::from_opaque(256u32.into()); + /// + /// assert!(error.as_opaque().is_some()); + /// ``` pub fn as_opaque(&self) -> Option<&JsValue> { match self.inner { Repr::Native(_) => None, @@ -194,6 +258,19 @@ impl JsError { /// Gets the inner [`JsNativeError`] if the error is a native /// error, or `None` otherwise. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsError, JsNativeError, JsValue}; + /// let error: JsError = JsNativeError::error().with_message("Unknown error").into(); + /// + /// assert!(error.as_native().is_some()); + /// + /// let error = JsError::from_opaque(JsValue::undefined().into()); + /// + /// assert!(error.as_native().is_none()); + /// ``` pub fn as_native(&self) -> Option<&JsNativeError> { match self.inner { Repr::Native(ref e) => Some(e), @@ -227,7 +304,26 @@ impl std::fmt::Display for JsError { /// Native representation of an ideal `Error` object from Javascript. /// +/// This representation is more space efficient than its [`JsObject`] equivalent, +/// since it doesn't need to create a whole new `JsObject` to be instantiated. +/// Prefer using this over [`JsError`] when you don't need to throw +/// plain [`JsValue`]s as errors, or when you need to inspect the error type +/// of a `JsError`. +/// +/// # Examples +/// +/// ```rust +/// # use boa_engine::{JsNativeError, JsNativeErrorKind}; +/// +/// let native_error = JsNativeError::uri().with_message("cannot decode uri"); +/// +/// match native_error.kind { +/// JsNativeErrorKind::Uri => { /* handle URI error*/ } +/// _ => unreachable!(), +/// } /// +/// assert_eq!(native_error.message(), "cannot decode uri"); +/// ``` #[derive(Debug, Clone, Trace, Finalize, Error)] #[error("{kind}: {message}")] pub struct JsNativeError { @@ -252,48 +348,127 @@ impl JsNativeError { /// Creates a new `JsNativeError` of kind `AggregateError` from /// a list of [`JsError`]s, with empty `message` and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let inner_errors = vec![ + /// JsNativeError::typ().into(), + /// JsNativeError::syntax().into() + /// ]; + /// let error = JsNativeError::aggregate(inner_errors); + /// + /// assert!(matches!( + /// error.kind, + /// JsNativeErrorKind::Aggregate(ref errors) if errors.len() == 2 + /// )); + /// ``` pub fn aggregate(errors: Vec) -> Self { Self::new(JsNativeErrorKind::Aggregate(errors), "".into(), None) } /// Creates a new `JsNativeError` of kind `Error`, with empty `message` /// and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let error = JsNativeError::error(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Error)); + /// ``` pub fn error() -> Self { Self::new(JsNativeErrorKind::Error, "".into(), None) } /// Creates a new `JsNativeError` of kind `EvalError`, with empty `message` /// and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let error = JsNativeError::eval(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Eval)); + /// ``` pub fn eval() -> Self { Self::new(JsNativeErrorKind::Eval, "".into(), None) } /// Creates a new `JsNativeError` of kind `RangeError`, with empty `message` /// and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let error = JsNativeError::range(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Range)); + /// ``` pub fn range() -> Self { Self::new(JsNativeErrorKind::Range, "".into(), None) } /// Creates a new `JsNativeError` of kind `ReferenceError`, with empty `message` /// and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let error = JsNativeError::reference(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Reference)); + /// ``` pub fn reference() -> Self { Self::new(JsNativeErrorKind::Reference, "".into(), None) } /// Creates a new `JsNativeError` of kind `SyntaxError`, with empty `message` /// and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let error = JsNativeError::syntax(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Syntax)); + /// ``` pub fn syntax() -> Self { Self::new(JsNativeErrorKind::Syntax, "".into(), None) } /// Creates a new `JsNativeError` of kind `TypeError`, with empty `message` /// and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let error = JsNativeError::typ(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Type)); + /// ``` pub fn typ() -> Self { Self::new(JsNativeErrorKind::Type, "".into(), None) } /// Creates a new `JsNativeError` of kind `UriError`, with empty `message` /// and undefined `cause`. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{JsNativeError, JsNativeErrorKind}; + /// let error = JsNativeError::uri(); + /// + /// assert!(matches!(error.kind, JsNativeErrorKind::Uri)); + /// ``` pub fn uri() -> Self { Self::new(JsNativeErrorKind::Uri, "".into(), None) } @@ -306,6 +481,15 @@ impl JsNativeError { /// while a [`String`] will be converted to a [`Rc`][std::rc::Rc] in order /// to make clones more efficient. Prefer static `str`s if you want /// efficiency, and [`String`] s if you prefer descriptive error messages. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::JsNativeError; + /// let error = JsNativeError::range().with_message("number too large"); + /// + /// assert_eq!(error.message(), "number too large"); + /// ``` #[must_use] pub fn with_message(mut self, message: S) -> Self where @@ -316,6 +500,16 @@ impl JsNativeError { } /// Appends a cause to this error. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::JsNativeError; + /// let cause = JsNativeError::syntax(); + /// let error = JsNativeError::error().with_cause(cause); + /// + /// assert!(error.cause().unwrap().as_native().is_some()); + /// ``` #[must_use] pub fn with_cause(mut self, cause: V) -> Self where @@ -331,6 +525,15 @@ impl JsNativeError { /// property. /// /// [spec]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::JsNativeError; + /// let error = JsNativeError::range().with_message("number too large"); + /// + /// assert_eq!(error.message(), "number too large"); + /// ``` pub fn message(&self) -> &str { &self.message } @@ -341,12 +544,35 @@ impl JsNativeError { /// property. /// /// [spec]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::JsNativeError; + /// let cause = JsNativeError::syntax(); + /// let error = JsNativeError::error().with_cause(cause); + /// + /// assert!(error.cause().unwrap().as_native().is_some()); + /// ``` pub fn cause(&self) -> Option<&JsError> { self.cause.as_deref() } /// Converts this native error to its opaque representation as a /// [`JsObject`]. + /// + /// # Examples + /// + /// ```rust + /// # use boa_engine::{Context, JsError, JsNativeError}; + /// let context = &mut Context::default(); + /// + /// let error = JsNativeError::error().with_message("error!"); + /// let error_obj = error.to_opaque(context); + /// + /// assert!(error_obj.borrow().is_error()); + /// assert_eq!(error_obj.get("message", context).unwrap(), "error!".into()) + /// ``` pub fn to_opaque(&self, context: &mut Context) -> JsObject { let Self { kind, diff --git a/boa_engine/src/object/jsmap.rs b/boa_engine/src/object/jsmap.rs index 71969e29e23..4fa3feedd05 100644 --- a/boa_engine/src/object/jsmap.rs +++ b/boa_engine/src/object/jsmap.rs @@ -32,7 +32,6 @@ use std::ops::Deref; /// map.set("Key-2", 10, context).unwrap(); /// /// assert_eq!(map.get_size(context).unwrap(), 2.into()); -/// /// ``` /// /// Create a `JsMap` from a `JsArray` @@ -47,20 +46,23 @@ use std::ops::Deref; /// /// // Create an array of two `[key, value]` pairs /// let js_array = JsArray::new(context); -/// +/// /// // Create a `[key, value]` pair of JsValues /// let vec_one: Vec = vec![JsValue::new("first-key"), JsValue::new("first-value")]; /// /// // We create an push our `[key, value]` pair onto our array as a `JsArray` -/// js_array.push(JsArray::from_iter(vec_one, context), context).unwrap(); +/// js_array +/// .push(JsArray::from_iter(vec_one, context), context) +/// .unwrap(); /// /// // Create a `JsMap` from the `JsArray` using it's iterable property. /// let js_iterable_map = JsMap::from_js_iterable(&js_array.into(), context).unwrap(); /// -/// assert_eq!(js_iterable_map.get("first-key", context).unwrap(), "first-value".into()); -/// +/// assert_eq!( +/// js_iterable_map.get("first-key", context).unwrap(), +/// "first-value".into() +/// ); /// ``` -/// #[derive(Debug, Clone, Trace, Finalize)] pub struct JsMap { inner: JsObject, @@ -82,7 +84,6 @@ impl JsMap { /// /// // Create a new empty `JsMap`. /// let map = JsMap::new(context); - /// /// ``` #[inline] pub fn new(context: &mut Context) -> Self { @@ -104,16 +105,16 @@ impl JsMap { /// /// // Create an array of two `[key, value]` pairs /// let js_array = JsArray::new(context); - /// + /// /// // Create a `[key, value]` pair of JsValues and add it to the `JsArray` as a `JsArray` /// let vec_one: Vec = vec![JsValue::new("first-key"), JsValue::new("first-value")]; - /// js_array.push(JsArray::from_iter(vec_one, context), context).unwrap(); + /// js_array + /// .push(JsArray::from_iter(vec_one, context), context) + /// .unwrap(); /// /// // Create a `JsMap` from the `JsArray` using it's iterable property. /// let js_iterable_map = JsMap::from_js_iterable(&js_array.into(), context).unwrap(); - /// /// ``` - /// #[inline] pub fn from_js_iterable(iterable: &JsValue, context: &mut Context) -> JsResult { // Create a new map object. @@ -146,12 +147,11 @@ impl JsMap { /// // `some_object` can be any JavaScript `Map` object. /// let some_object = JsObject::from_proto_and_data( /// context.intrinsics().constructors().map().prototype(), - /// ObjectData::map(OrderedMap::new()) + /// ObjectData::map(OrderedMap::new()), /// ); - /// + /// /// // Create `JsMap` object with incoming object. /// let js_map = JsMap::from_object(some_object).unwrap(); - /// /// ``` /// /// Invalid Example - returns a `TypeError` with the message "object is not a Map" @@ -167,7 +167,6 @@ impl JsMap { /// /// // Some object is an Array object, not a map object /// assert!(JsMap::from_object(some_object.into()).is_err()); - /// /// ``` #[inline] pub fn from_object(object: JsObject) -> JsResult { @@ -226,7 +225,6 @@ impl JsMap { /// /// assert_eq!(js_map.get("foo", context).unwrap(), "bar".into()); /// assert_eq!(js_map.get(2, context).unwrap(), 4.into()) - /// /// ``` #[inline] pub fn set(&self, key: K, value: V, context: &mut Context) -> JsResult @@ -260,7 +258,6 @@ impl JsMap { /// let map_size = js_map.get_size(context).unwrap(); /// /// assert_eq!(map_size, 1.into()); - /// /// ``` #[inline] pub fn get_size(&self, context: &mut Context) -> JsResult { @@ -287,7 +284,6 @@ impl JsMap { /// /// assert_eq!(js_map.get_size(context).unwrap(), 1.into()); /// assert_eq!(js_map.get("foo", context).unwrap(), JsValue::undefined()); - /// /// ``` #[inline] pub fn delete(&self, key: T, context: &mut Context) -> JsResult @@ -314,7 +310,6 @@ impl JsMap { /// let retrieved_value = js_map.get("foo", context).unwrap(); /// /// assert_eq!(retrieved_value, "bar".into()); - /// /// ``` #[inline] pub fn get(&self, key: T, context: &mut Context) -> JsResult @@ -343,7 +338,6 @@ impl JsMap { /// js_map.clear(context).unwrap(); /// /// assert_eq!(js_map.get_size(context).unwrap(), 0.into()); - /// /// ``` #[inline] pub fn clear(&self, context: &mut Context) -> JsResult { @@ -368,7 +362,6 @@ impl JsMap { /// let has_key = js_map.has("foo", context).unwrap(); /// /// assert_eq!(has_key, true.into()); - /// /// ``` #[inline] pub fn has(&self, key: T, context: &mut Context) -> JsResult diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 9c8cf790301..e194c8f35ff 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -1774,16 +1774,8 @@ impl<'context> FunctionBuilder<'context> { /// # use boa_engine::{Context, JsValue, object::ObjectInitializer, property::Attribute}; /// let mut context = Context::default(); /// let object = ObjectInitializer::new(&mut context) -/// .property( -/// "hello", -/// "world", -/// Attribute::all() -/// ) -/// .property( -/// 1, -/// 1, -/// Attribute::all() -/// ) +/// .property("hello", "world", Attribute::all()) +/// .property(1, 1, Attribute::all()) /// .function(|_, _, _| Ok(JsValue::undefined()), "func", 0) /// .build(); /// ``` diff --git a/boa_engine/src/symbol.rs b/boa_engine/src/symbol.rs index bbd013978f3..38f04558f2d 100644 --- a/boa_engine/src/symbol.rs +++ b/boa_engine/src/symbol.rs @@ -28,7 +28,7 @@ use std::{ /// /// # Examples /// ``` -///# use boa_engine::symbol::WellKnownSymbols; +/// # use boa_engine::symbol::WellKnownSymbols; /// /// let iterator = WellKnownSymbols::iterator(); /// assert_eq!(iterator.description().as_deref(), Some("Symbol.iterator")); diff --git a/boa_engine/src/syntax/ast/node/parameters.rs b/boa_engine/src/syntax/ast/node/parameters.rs index 164aa83a1f0..56743bd1f87 100644 --- a/boa_engine/src/syntax/ast/node/parameters.rs +++ b/boa_engine/src/syntax/ast/node/parameters.rs @@ -226,10 +226,10 @@ impl Default for FormalParameterListFlags { /// /// In the declaration of a function, the parameters must be identifiers, /// not any value like numbers, strings, or objects. -///```text -///function foo(formalParameter1, formalParameter2) { -///} -///``` +/// ```text +/// function foo(formalParameter1, formalParameter2) { +/// } +/// ``` /// /// More information: /// - [ECMAScript reference][spec] diff --git a/boa_engine/src/syntax/ast/node/switch/mod.rs b/boa_engine/src/syntax/ast/node/switch/mod.rs index 9102a653f2e..c1ec75586ac 100644 --- a/boa_engine/src/syntax/ast/node/switch/mod.rs +++ b/boa_engine/src/syntax/ast/node/switch/mod.rs @@ -1,5 +1,4 @@ //! Switch node. -//! use crate::syntax::ast::node::Node; use boa_interner::{Interner, ToInternedString}; diff --git a/boa_engine/src/syntax/parser/expression/identifiers.rs b/boa_engine/src/syntax/parser/expression/identifiers.rs index d4981d89c1d..33e702166ef 100644 --- a/boa_engine/src/syntax/parser/expression/identifiers.rs +++ b/boa_engine/src/syntax/parser/expression/identifiers.rs @@ -4,7 +4,6 @@ //! - [ECMAScript specification][spec] //! //! [spec]: https://tc39.es/ecma262/#sec-identifiers -//! use crate::syntax::{ ast::{node::Identifier, Keyword},