Browse Source

Generic `JsResult<R>` in `context.throw_` methods (#1734)

Previously when we had the `context.throw_` methods (like `context.thtrow_type_error()`) they were limited as to where we could call them, e.i. a function that returned `JsResult<JsValue>`. So we had to call the `context.construct_` methods with an explicit `Err()` enum wrap to throw in functions that returned non-jsvalues (which happens a lot).
Now, with this PR the throw methods have a generic `JsResult<R>` return that can return in any `JsResult<T>` returning function. Which cleans the API and makes the user experience a bit better.

```rust
return Err(context.construct_type_error("..."));
// to
return context.throw_type_error("...");
```
pull/1735/head
Halid Odat 3 years ago
parent
commit
3269c1b901
  1. 8
      boa/src/bigint.rs
  2. 10
      boa/src/builtins/array/mod.rs
  3. 2
      boa/src/builtins/array_buffer/mod.rs
  4. 4
      boa/src/builtins/intl/mod.rs
  5. 2
      boa/src/builtins/iterable/mod.rs
  6. 6
      boa/src/builtins/json/mod.rs
  7. 9
      boa/src/builtins/number/mod.rs
  8. 33
      boa/src/builtins/regexp/mod.rs
  9. 113
      boa/src/builtins/typed_array/mod.rs
  10. 12
      boa/src/context.rs
  11. 10
      boa/src/environment/declarative_environment_record.rs
  12. 8
      boa/src/environment/global_environment_record.rs
  13. 2
      boa/src/environment/object_environment_record.rs
  14. 11
      boa/src/lib.rs
  15. 2
      boa/src/object/internal_methods/array.rs
  16. 101
      boa/src/object/internal_methods/proxy.rs
  17. 12
      boa/src/object/jsobject.rs
  18. 25
      boa/src/object/operations.rs
  19. 8
      boa/src/syntax/ast/node/declaration/mod.rs
  20. 42
      boa/src/value/mod.rs
  21. 6
      boa/src/value/operations.rs
  22. 20
      boa/src/vm/mod.rs
  23. 2
      boa_tester/src/exec/js262.rs
  24. 5
      boa_wasm/src/lib.rs

8
boa/src/bigint.rs

@ -166,7 +166,7 @@ impl JsBigInt {
let y = if let Some(y) = y.inner.to_biguint() { let y = if let Some(y) = y.inner.to_biguint() {
y y
} else { } else {
return Err(context.construct_range_error("BigInt negative exponent")); return context.throw_range_error("BigInt negative exponent");
}; };
let num_bits = (x.inner.bits() as f64 let num_bits = (x.inner.bits() as f64
@ -175,7 +175,7 @@ impl JsBigInt {
+ 1f64; + 1f64;
if num_bits > 1_000_000_000f64 { if num_bits > 1_000_000_000f64 {
return Err(context.construct_range_error("Maximum BigInt size exceeded")); return context.throw_range_error("Maximum BigInt size exceeded");
} }
Ok(Self::new(x.inner.as_ref().clone().pow(y))) Ok(Self::new(x.inner.as_ref().clone().pow(y)))
@ -192,7 +192,7 @@ impl JsBigInt {
Ok(Self::new(inner)) Ok(Self::new(inner))
} else { } else {
Err(context.construct_range_error("Maximum BigInt size exceeded")) context.throw_range_error("Maximum BigInt size exceeded")
} }
} }
@ -207,7 +207,7 @@ impl JsBigInt {
Ok(Self::new(inner)) Ok(Self::new(inner))
} else { } else {
Err(context.construct_range_error("Maximum BigInt size exceeded")) context.throw_range_error("Maximum BigInt size exceeded")
} }
} }

10
boa/src/builtins/array/mod.rs

@ -165,7 +165,7 @@ impl Array {
let int_len = len.to_u32(context).unwrap(); let int_len = len.to_u32(context).unwrap();
// ii. If SameValueZero(intLen, len) is false, throw a RangeError exception. // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception.
if !JsValue::same_value_zero(&int_len.into(), len) { if !JsValue::same_value_zero(&int_len.into(), len) {
return Err(context.construct_range_error("invalid array length")); return context.throw_range_error("invalid array length");
} }
int_len int_len
}; };
@ -210,7 +210,7 @@ impl Array {
) -> JsResult<JsObject> { ) -> JsResult<JsObject> {
// 1. If length > 2^32 - 1, throw a RangeError exception. // 1. If length > 2^32 - 1, throw a RangeError exception.
if length > 2usize.pow(32) - 1 { if length > 2usize.pow(32) - 1 {
return Err(context.construct_range_error("array exceeded max size")); return context.throw_range_error("array exceeded max size");
} }
// 7. Return A. // 7. Return A.
// 2. If proto is not present, set proto to %Array.prototype%. // 2. If proto is not present, set proto to %Array.prototype%.
@ -365,7 +365,7 @@ impl Array {
.unwrap(), .unwrap(),
) )
} else { } else {
Err(context.construct_type_error("Symbol.species must be a constructor")) context.throw_type_error("Symbol.species must be a constructor")
} }
} }
@ -1669,8 +1669,8 @@ impl Array {
} else { } else {
// 1. If targetIndex >= 2^53 - 1, throw a TypeError exception // 1. If targetIndex >= 2^53 - 1, throw a TypeError exception
if target_index >= Number::MAX_SAFE_INTEGER as u64 { if target_index >= Number::MAX_SAFE_INTEGER as u64 {
return Err(context return context
.construct_type_error("Target index exceeded max safe integer value")); .throw_type_error("Target index exceeded max safe integer value");
} }
// 2. Perform ? CreateDataPropertyOrThrow(target, targetIndex, element) // 2. Perform ? CreateDataPropertyOrThrow(target, targetIndex, element)

2
boa/src/builtins/array_buffer/mod.rs

@ -363,7 +363,7 @@ impl ArrayBuffer {
let src_block = if let Some(b) = &self.array_buffer_data { let src_block = if let Some(b) = &self.array_buffer_data {
b b
} else { } else {
return Err(context.construct_syntax_error("Cannot clone detached array buffer")); return context.throw_syntax_error("Cannot clone detached array buffer");
}; };
{ {

4
boa/src/builtins/intl/mod.rs

@ -93,9 +93,7 @@ impl Intl {
let k_value = o.get(k, context)?; let k_value = o.get(k, context)?;
// ii. If Type(kValue) is not String or Object, throw a TypeError exception. // ii. If Type(kValue) is not String or Object, throw a TypeError exception.
if !(k_value.is_object() || k_value.is_string()) { if !(k_value.is_object() || k_value.is_string()) {
return Err(context return context.throw_type_error("locale should be a String or Object");
.throw_type_error("locale should be a String or Object")
.unwrap_err());
} }
// iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then // 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) // TODO: handle checks for InitializedLocale internal slot (there should be an if statement here)

2
boa/src/builtins/iterable/mod.rs

@ -149,7 +149,7 @@ impl JsValue {
// 4. If Type(iterator) is not Object, throw a TypeError exception. // 4. If Type(iterator) is not Object, throw a TypeError exception.
if !iterator.is_object() { if !iterator.is_object() {
return Err(context.construct_type_error("the iterator is not an object")); return context.throw_type_error("the iterator is not an object");
} }
// 5. Let nextMethod be ? GetV(iterator, "next"). // 5. Let nextMethod be ? GetV(iterator, "next").

6
boa/src/builtins/json/mod.rs

@ -448,7 +448,7 @@ impl Json {
// 10. If Type(value) is BigInt, throw a TypeError exception. // 10. If Type(value) is BigInt, throw a TypeError exception.
if value.is_bigint() { if value.is_bigint() {
return Err(context.construct_type_error("cannot serialize bigint to JSON")); return context.throw_type_error("cannot serialize bigint to JSON");
} }
// 11. If Type(value) is Object and IsCallable(value) is false, then // 11. If Type(value) is Object and IsCallable(value) is false, then
@ -531,7 +531,7 @@ impl Json {
// 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical. // 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.
let limiter = RecursionLimiter::new(value); let limiter = RecursionLimiter::new(value);
if limiter.live { if limiter.live {
return Err(context.construct_type_error("cyclic object value")); return context.throw_type_error("cyclic object value");
} }
// 2. Append value to state.[[Stack]]. // 2. Append value to state.[[Stack]].
@ -644,7 +644,7 @@ impl Json {
// 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical. // 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.
let limiter = RecursionLimiter::new(value); let limiter = RecursionLimiter::new(value);
if limiter.live { if limiter.live {
return Err(context.construct_type_error("cyclic object value")); return context.throw_type_error("cyclic object value");
} }
// 2. Append value to state.[[Stack]]. // 2. Append value to state.[[Stack]].

9
boa/src/builtins/number/mod.rs

@ -222,8 +222,8 @@ impl Number {
let this_str_num = if let Some(precision) = precision { let this_str_num = if let Some(precision) = precision {
// 5. If f < 0 or f > 100, throw a RangeError exception. // 5. If f < 0 or f > 100, throw a RangeError exception.
if !(0..=100).contains(&precision) { if !(0..=100).contains(&precision) {
return Err(context return context
.construct_range_error("toExponential() argument must be between 0 and 100")); .throw_range_error("toExponential() argument must be between 0 and 100");
} }
f64_to_exponential_with_precision(this_num, precision as usize) f64_to_exponential_with_precision(this_num, precision as usize)
} else { } else {
@ -256,9 +256,8 @@ impl Number {
0..=100 => n.to_integer(context)? as usize, 0..=100 => n.to_integer(context)? as usize,
// 4, 5. If f < 0 or f > 100, throw a RangeError exception. // 4, 5. If f < 0 or f > 100, throw a RangeError exception.
_ => { _ => {
return Err(context.construct_range_error( return context
"toFixed() digits argument must be between 0 and 100", .throw_range_error("toFixed() digits argument must be between 0 and 100")
))
} }
}, },
// 3. If fractionDigits is undefined, then f is 0. // 3. If fractionDigits is undefined, then f is 0.

33
boa/src/builtins/regexp/mod.rs

@ -332,8 +332,8 @@ impl RegExp {
// 14. Set obj.[[RegExpMatcher]] to the Abstract Closure that evaluates parseResult by applying the semantics provided in 22.2.2 using patternCharacters as the pattern's List of SourceCharacter values and F as the flag parameters. // 14. Set obj.[[RegExpMatcher]] to the Abstract Closure that evaluates parseResult by applying the semantics provided in 22.2.2 using patternCharacters as the pattern's List of SourceCharacter values and F as the flag parameters.
let matcher = match Regex::with_flags(&p, f.as_ref()) { let matcher = match Regex::with_flags(&p, f.as_ref()) {
Err(error) => { Err(error) => {
return Err(context return context
.construct_syntax_error(format!("failed to create matcher: {}", error.text))); .throw_syntax_error(format!("failed to create matcher: {}", error.text));
} }
Ok(val) => val, Ok(val) => val,
}; };
@ -779,9 +779,7 @@ impl RegExp {
// b. If Type(result) is neither Object nor Null, throw a TypeError exception. // b. If Type(result) is neither Object nor Null, throw a TypeError exception.
if !result.is_object() && !result.is_null() { if !result.is_object() && !result.is_null() {
return Err( return context.throw_type_error("regexp exec returned neither object nor null");
context.construct_type_error("regexp exec returned neither object nor null")
);
} }
// c. Return result. // c. Return result.
@ -790,7 +788,7 @@ impl RegExp {
// 5. Perform ? RequireInternalSlot(R, [[RegExpMatcher]]). // 5. Perform ? RequireInternalSlot(R, [[RegExpMatcher]]).
if !this.is_regexp() { if !this.is_regexp() {
return Err(context.construct_type_error("RegExpExec called with invalid value")); return context.throw_type_error("RegExpExec called with invalid value");
} }
// 6. Return ? RegExpBuiltinExec(R, S). // 6. Return ? RegExpBuiltinExec(R, S).
@ -814,9 +812,7 @@ impl RegExp {
if let Some(rx) = obj.as_regexp() { if let Some(rx) = obj.as_regexp() {
rx.clone() rx.clone()
} else { } else {
return Err( return context.throw_type_error("RegExpBuiltinExec called with invalid value");
context.construct_type_error("RegExpBuiltinExec called with invalid value")
);
} }
}; };
@ -870,9 +866,8 @@ impl RegExp {
) { ) {
Ok(s) => s.len(), Ok(s) => s.len(),
Err(_) => { Err(_) => {
return Err(context.construct_type_error( return context
"Failed to get byte index from utf16 encoded string", .throw_type_error("Failed to get byte index from utf16 encoded string")
))
} }
}; };
let r = matcher.find_from(&input, last_byte_index).next(); let r = matcher.find_from(&input, last_byte_index).next();
@ -1046,9 +1041,8 @@ impl RegExp {
let rx = if let Some(rx) = this.as_object() { let rx = if let Some(rx) = this.as_object() {
rx rx
} else { } else {
return Err(context.construct_type_error( return context
"RegExp.prototype.match method called on incompatible value", .throw_type_error("RegExp.prototype.match method called on incompatible value");
));
}; };
// 3. Let S be ? ToString(string). // 3. Let S be ? ToString(string).
@ -1495,9 +1489,9 @@ impl RegExp {
let rx = if let Some(rx) = this.as_object() { let rx = if let Some(rx) = this.as_object() {
rx rx
} else { } else {
return Err(context.construct_type_error( return context.throw_type_error(
"RegExp.prototype[Symbol.search] method called on incompatible value", "RegExp.prototype[Symbol.search] method called on incompatible value",
)); );
}; };
// 3. Let S be ? ToString(string). // 3. Let S be ? ToString(string).
@ -1557,9 +1551,8 @@ impl RegExp {
let rx = if let Some(rx) = this.as_object() { let rx = if let Some(rx) = this.as_object() {
rx rx
} else { } else {
return Err(context.construct_type_error( return context
"RegExp.prototype.split method called on incompatible value", .throw_type_error("RegExp.prototype.split method called on incompatible value");
));
}; };
// 3. Let S be ? ToString(string). // 3. Let S be ? ToString(string).

113
boa/src/builtins/typed_array/mod.rs

@ -547,7 +547,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -682,7 +682,7 @@ impl TypedArray {
// 2. Perform ? ValidateTypedArray(O). // 2. Perform ? ValidateTypedArray(O).
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -747,7 +747,7 @@ impl TypedArray {
// b. Let buffer be O.[[ViewedArrayBuffer]]. // b. Let buffer be O.[[ViewedArrayBuffer]].
// c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// d. Let typedArrayName be the String value of O.[[TypedArrayName]]. // d. Let typedArrayName be the String value of O.[[TypedArrayName]].
@ -848,7 +848,7 @@ impl TypedArray {
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))? .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?
.is_detached() .is_detached()
{ {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Return CreateArrayIterator(O, key+value). // 3. Return CreateArrayIterator(O, key+value).
@ -876,7 +876,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -935,7 +935,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -980,7 +980,7 @@ impl TypedArray {
// 14. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception. // 14. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 15. Repeat, while k < final, // 15. Repeat, while k < final,
@ -1015,7 +1015,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1096,7 +1096,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1154,7 +1154,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1211,7 +1211,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1263,7 +1263,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1336,7 +1336,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1418,7 +1418,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1475,7 +1475,7 @@ impl TypedArray {
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))? .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?
.is_detached() .is_detached()
{ {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Return CreateArrayIterator(O, key). // 3. Return CreateArrayIterator(O, key).
@ -1503,7 +1503,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1606,7 +1606,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1664,7 +1664,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1741,7 +1741,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1821,7 +1821,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -1933,7 +1933,7 @@ impl TypedArray {
// 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. // 1. Let targetBuffer be target.[[ViewedArrayBuffer]].
// 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
if target_array.is_detached() { if target_array.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
let target_buffer_obj = target_array let target_buffer_obj = target_array
.viewed_array_buffer() .viewed_array_buffer()
@ -1945,7 +1945,7 @@ impl TypedArray {
// 4. Let srcBuffer be source.[[ViewedArrayBuffer]]. // 4. Let srcBuffer be source.[[ViewedArrayBuffer]].
// 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. // 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
if source_array.is_detached() { if source_array.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
let mut src_buffer_obj = source_array let mut src_buffer_obj = source_array
.viewed_array_buffer() .viewed_array_buffer()
@ -1979,23 +1979,23 @@ impl TypedArray {
let target_offset = match target_offset { let target_offset = match target_offset {
IntegerOrInfinity::Integer(i) if i >= 0 => i as usize, IntegerOrInfinity::Integer(i) if i >= 0 => i as usize,
IntegerOrInfinity::PositiveInfinity => { IntegerOrInfinity::PositiveInfinity => {
return Err(context.construct_range_error("Target offset cannot be Infinity")) return context.throw_range_error("Target offset cannot be Infinity");
} }
_ => unreachable!(), _ => unreachable!(),
}; };
// 16. If srcLength + targetOffset > targetLength, throw a RangeError exception. // 16. If srcLength + targetOffset > targetLength, throw a RangeError exception.
if src_length + target_offset > target_length { if src_length + target_offset > target_length {
return Err(context.construct_range_error( return context.throw_range_error(
"Source typed array and target offset longer than target typed array", "Source typed array and target offset longer than target typed array",
)); );
} }
// 17. If target.[[ContentType]] ≠ source.[[ContentType]], throw a TypeError exception. // 17. If target.[[ContentType]] ≠ source.[[ContentType]], throw a TypeError exception.
if target_name.content_type() != src_name.content_type() { if target_name.content_type() != src_name.content_type() {
return Err(context.construct_type_error( return context.throw_type_error(
"Source typed array and target typed array have different content types", "Source typed array and target typed array have different content types",
)); );
} }
// TODO: Shared Array Buffer // TODO: Shared Array Buffer
@ -2142,7 +2142,7 @@ impl TypedArray {
// 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. // 1. Let targetBuffer be target.[[ViewedArrayBuffer]].
// 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
if target_array.is_detached() { if target_array.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let targetLength be target.[[ArrayLength]]. // 3. Let targetLength be target.[[ArrayLength]].
@ -2167,7 +2167,7 @@ impl TypedArray {
let target_offset = match target_offset { let target_offset = match target_offset {
// 10. If targetOffset is +∞, throw a RangeError exception. // 10. If targetOffset is +∞, throw a RangeError exception.
IntegerOrInfinity::PositiveInfinity => { IntegerOrInfinity::PositiveInfinity => {
return Err(context.construct_range_error("Target offset cannot be Infinity")) return context.throw_range_error("Target offset cannot be Infinity")
} }
IntegerOrInfinity::Integer(i) if i >= 0 => i as usize, IntegerOrInfinity::Integer(i) if i >= 0 => i as usize,
_ => unreachable!(), _ => unreachable!(),
@ -2175,9 +2175,9 @@ impl TypedArray {
// 11. If srcLength + targetOffset > targetLength, throw a RangeError exception. // 11. If srcLength + targetOffset > targetLength, throw a RangeError exception.
if src_length + target_offset > target_length { if src_length + target_offset > target_length {
return Err(context.construct_range_error( return context.throw_range_error(
"Source object and target offset longer than target typed array", "Source object and target offset longer than target typed array",
)); );
} }
// 12. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. // 12. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
@ -2213,9 +2213,7 @@ impl TypedArray {
// e. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. // e. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
if target_buffer.is_detached_buffer() { if target_buffer.is_detached_buffer() {
return Err( return context.throw_type_error("Cannot set value on detached array buffer");
context.construct_type_error("Cannot set value on detached array buffer")
);
} }
// f. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered). // f. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered).
@ -2255,7 +2253,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -2304,7 +2302,7 @@ impl TypedArray {
if count > 0 { if count > 0 {
// a. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception. // a. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// b. Let srcName be the String value of O.[[TypedArrayName]]. // b. Let srcName be the String value of O.[[TypedArrayName]].
@ -2419,7 +2417,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Let len be O.[[ArrayLength]]. // 3. Let len be O.[[ArrayLength]].
@ -2545,8 +2543,8 @@ impl TypedArray {
.expect("Must be array buffer") .expect("Must be array buffer")
.is_detached_buffer() .is_detached_buffer()
{ {
return Err(context return context
.construct_type_error("Cannot sort typed array with detached buffer")); .throw_type_error("Cannot sort typed array with detached buffer");
} }
// c. If v is NaN, return +0𝔽. // c. If v is NaN, return +0𝔽.
@ -2770,7 +2768,7 @@ impl TypedArray {
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))? .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?
.is_detached() .is_detached()
{ {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. Return CreateArrayIterator(O, value). // 3. Return CreateArrayIterator(O, value).
@ -2848,8 +2846,8 @@ impl TypedArray {
.content_type() .content_type()
!= typed_array_name.content_type() != typed_array_name.content_type()
{ {
return Err(context return context
.construct_type_error("New typed array has different context type than exemplar")); .throw_type_error("New typed array has different context type than exemplar");
} }
// 6. Return result. // 6. Return result.
@ -2879,7 +2877,7 @@ impl TypedArray {
.as_typed_array() .as_typed_array()
.ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?; .ok_or_else(|| context.construct_type_error("Value is not a typed array object"))?;
if o.is_detached() { if o.is_detached() {
return Err(context.construct_type_error("Buffer of the typed array is detached")); return context.throw_type_error("Buffer of the typed array is detached");
} }
// 3. If argumentList is a List of a single Number, then // 3. If argumentList is a List of a single Number, then
@ -2887,8 +2885,8 @@ impl TypedArray {
if let Some(number) = args[0].as_number() { if let Some(number) = args[0].as_number() {
// a. If newTypedArray.[[ArrayLength]] < ℝ(argumentList[0]), throw a TypeError exception. // a. If newTypedArray.[[ArrayLength]] < ℝ(argumentList[0]), throw a TypeError exception.
if (o.array_length() as f64) < number { if (o.array_length() as f64) < number {
return Err(context return context
.construct_type_error("New typed array length is smaller than expected")); .throw_type_error("New typed array length is smaller than expected");
} }
} }
} }
@ -3035,9 +3033,7 @@ impl TypedArray {
// 1. Let srcData be srcArray.[[ViewedArrayBuffer]]. // 1. Let srcData be srcArray.[[ViewedArrayBuffer]].
// 2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. // 2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
if src_array.is_detached() { if src_array.is_detached() {
return Err( return context.throw_type_error("Cannot initialize typed array from detached buffer");
context.construct_type_error("Cannot initialize typed array from detached buffer")
);
} }
let src_data_obj = src_array let src_data_obj = src_array
.viewed_array_buffer() .viewed_array_buffer()
@ -3095,15 +3091,14 @@ impl TypedArray {
// b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. // b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
if src_data.is_detached_buffer() { if src_data.is_detached_buffer() {
return Err(context return context
.construct_type_error("Cannot initialize typed array from detached buffer")); .throw_type_error("Cannot initialize typed array from detached buffer");
} }
// c. If srcArray.[[ContentType]] ≠ O.[[ContentType]], throw a TypeError exception. // c. If srcArray.[[ContentType]] ≠ O.[[ContentType]], throw a TypeError exception.
if src_name.content_type() != constructor_name.content_type() { if src_name.content_type() != constructor_name.content_type() {
return Err(context.construct_type_error( return context
"Cannot initialize typed array from different content type", .throw_type_error("Cannot initialize typed array from different content type");
));
} }
// d. Let srcByteIndex be srcByteOffset. // d. Let srcByteIndex be srcByteOffset.
@ -3188,7 +3183,7 @@ impl TypedArray {
// 4. If offset modulo elementSize ≠ 0, throw a RangeError exception. // 4. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if offset % constructor_name.element_size() != 0 { if offset % constructor_name.element_size() != 0 {
return Err(context.construct_range_error("Invalid length for typed array")); return context.throw_range_error("Invalid length for typed array");
} }
let buffer_byte_length = { let buffer_byte_length = {
@ -3199,8 +3194,8 @@ impl TypedArray {
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if buffer_array.is_detached_buffer() { if buffer_array.is_detached_buffer() {
return Err(context return context
.construct_type_error("Cannot construct typed array from detached buffer")); .throw_type_error("Cannot construct typed array from detached buffer");
} }
// 7. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. // 7. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
@ -3211,7 +3206,7 @@ impl TypedArray {
let new_byte_length = if length.is_undefined() { let new_byte_length = if length.is_undefined() {
// a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception. // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.
if buffer_byte_length % constructor_name.element_size() != 0 { if buffer_byte_length % constructor_name.element_size() != 0 {
return Err(context.construct_range_error("Invalid length for typed array")); return context.throw_range_error("Invalid length for typed array");
} }
// b. Let newByteLength be bufferByteLength - offset. // b. Let newByteLength be bufferByteLength - offset.
@ -3219,7 +3214,7 @@ impl TypedArray {
// c. If newByteLength < 0, throw a RangeError exception. // c. If newByteLength < 0, throw a RangeError exception.
if new_byte_length < 0 { if new_byte_length < 0 {
return Err(context.construct_range_error("Invalid length for typed array")); return context.throw_range_error("Invalid length for typed array");
} }
new_byte_length as usize new_byte_length as usize
@ -3233,7 +3228,7 @@ impl TypedArray {
// b. If offset + newByteLength > bufferByteLength, throw a RangeError exception. // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
if offset + new_byte_length > buffer_byte_length { if offset + new_byte_length > buffer_byte_length {
return Err(context.construct_range_error("Invalid length for typed array")); return context.throw_range_error("Invalid length for typed array");
} }
new_byte_length new_byte_length

12
boa/src/context.rs

@ -555,7 +555,7 @@ impl Context {
/// Throws a `Error` with the specified message. /// Throws a `Error` with the specified message.
#[inline] #[inline]
pub fn throw_error<M>(&mut self, message: M) -> JsResult<JsValue> pub fn throw_error<M, R>(&mut self, message: M) -> JsResult<R>
where where
M: Into<Box<str>>, M: Into<Box<str>>,
{ {
@ -582,7 +582,7 @@ impl Context {
/// Throws a `RangeError` with the specified message. /// Throws a `RangeError` with the specified message.
#[inline] #[inline]
pub fn throw_range_error<M>(&mut self, message: M) -> JsResult<JsValue> pub fn throw_range_error<M, R>(&mut self, message: M) -> JsResult<R>
where where
M: Into<Box<str>>, M: Into<Box<str>>,
{ {
@ -609,7 +609,7 @@ impl Context {
/// Throws a `TypeError` with the specified message. /// Throws a `TypeError` with the specified message.
#[inline] #[inline]
pub fn throw_type_error<M>(&mut self, message: M) -> JsResult<JsValue> pub fn throw_type_error<M, R>(&mut self, message: M) -> JsResult<R>
where where
M: Into<Box<str>>, M: Into<Box<str>>,
{ {
@ -636,7 +636,7 @@ impl Context {
/// Throws a `ReferenceError` with the specified message. /// Throws a `ReferenceError` with the specified message.
#[inline] #[inline]
pub fn throw_reference_error<M>(&mut self, message: M) -> JsResult<JsValue> pub fn throw_reference_error<M, R>(&mut self, message: M) -> JsResult<R>
where where
M: Into<Box<str>>, M: Into<Box<str>>,
{ {
@ -663,7 +663,7 @@ impl Context {
/// Throws a `SyntaxError` with the specified message. /// Throws a `SyntaxError` with the specified message.
#[inline] #[inline]
pub fn throw_syntax_error<M>(&mut self, message: M) -> JsResult<JsValue> pub fn throw_syntax_error<M, R>(&mut self, message: M) -> JsResult<R>
where where
M: Into<Box<str>>, M: Into<Box<str>>,
{ {
@ -705,7 +705,7 @@ impl Context {
} }
/// Throws a `EvalError` with the specified message. /// Throws a `EvalError` with the specified message.
pub fn throw_eval_error<M>(&mut self, message: M) -> JsResult<JsValue> pub fn throw_eval_error<M, R>(&mut self, message: M) -> JsResult<R>
where where
M: Into<Box<str>>, M: Into<Box<str>>,
{ {

10
boa/src/environment/declarative_environment_record.rs

@ -179,7 +179,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
if self.env_rec.borrow().get(name).is_none() { if self.env_rec.borrow().get(name).is_none() {
// a. If S is true, throw a ReferenceError exception. // a. If S is true, throw a ReferenceError exception.
if strict { if strict {
return Err(context.construct_reference_error(format!("{} not found", name))); return context.throw_reference_error(format!("{} not found", name));
} }
// b. Perform envRec.CreateMutableBinding(N, true). // b. Perform envRec.CreateMutableBinding(N, true).
@ -204,9 +204,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
// 3. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception. // 3. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception.
if binding_value_is_none { if binding_value_is_none {
return Err( return context.throw_reference_error(format!("{} has not been initialized", name));
context.construct_reference_error(format!("{} has not been initialized", name))
);
// 4. Else if the binding for N in envRec is a mutable binding, change its bound value to V. // 4. Else if the binding for N in envRec is a mutable binding, change its bound value to V.
} else if binding_mutable { } else if binding_mutable {
let mut env_rec = self.env_rec.borrow_mut(); let mut env_rec = self.env_rec.borrow_mut();
@ -216,8 +214,8 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
// a. Assert: This is an attempt to change the value of an immutable binding. // a. Assert: This is an attempt to change the value of an immutable binding.
// b. If S is true, throw a TypeError exception. // b. If S is true, throw a TypeError exception.
} else if strict { } else if strict {
return Err(context return context
.construct_type_error(format!("Cannot mutate an immutable binding {}", name))); .throw_type_error(format!("Cannot mutate an immutable binding {}", name));
} }
// 6. Return NormalCompletion(empty). // 6. Return NormalCompletion(empty).

8
boa/src/environment/global_environment_record.rs

@ -300,9 +300,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
// 1. Let DclRec be envRec.[[DeclarativeRecord]]. // 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, throw a TypeError exception. // 2. If DclRec.HasBinding(N) is true, throw a TypeError exception.
if !allow_name_reuse && self.declarative_record.has_binding(name, context)? { if !allow_name_reuse && self.declarative_record.has_binding(name, context)? {
return Err( return context.throw_type_error(format!("Binding already exists for {}", name));
context.construct_type_error(format!("Binding already exists for {}", name))
);
} }
// 3. Return DclRec.CreateMutableBinding(N, D). // 3. Return DclRec.CreateMutableBinding(N, D).
@ -325,9 +323,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
// 1. Let DclRec be envRec.[[DeclarativeRecord]]. // 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, throw a TypeError exception. // 2. If DclRec.HasBinding(N) is true, throw a TypeError exception.
if self.declarative_record.has_binding(name, context)? { if self.declarative_record.has_binding(name, context)? {
return Err( return context.throw_type_error(format!("Binding already exists for {}", name));
context.construct_type_error(format!("Binding already exists for {}", name))
);
} }
// 3. Return DclRec.CreateImmutableBinding(N, S). // 3. Return DclRec.CreateImmutableBinding(N, S).

2
boa/src/environment/object_environment_record.rs

@ -157,7 +157,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
// 3. If stillExists is false and S is true, throw a ReferenceError exception. // 3. If stillExists is false and S is true, throw a ReferenceError exception.
if !still_exists && strict { if !still_exists && strict {
return Err(context.construct_reference_error("Binding already exists")); return context.throw_reference_error("Binding already exists");
} }
// 4. Return ? Set(bindingObject, N, V, S). // 4. Return ? Set(bindingObject, N, V, S).

11
boa/src/lib.rs

@ -108,10 +108,7 @@ pub(crate) fn forward<T: AsRef<[u8]>>(context: &mut Context, src: T) -> String {
Err(e) => { Err(e) => {
return format!( return format!(
"Uncaught {}", "Uncaught {}",
context context.construct_syntax_error(e.to_string()).display()
.throw_syntax_error(e.to_string())
.expect_err("interpreter.throw_syntax_error() did not return an error")
.display()
); );
} }
}; };
@ -133,11 +130,7 @@ pub(crate) fn forward_val<T: AsRef<[u8]>>(context: &mut Context, src: T) -> JsRe
let src_bytes: &[u8] = src.as_ref(); let src_bytes: &[u8] = src.as_ref();
// Setup executor // Setup executor
let result = parse(src_bytes, false) let result = parse(src_bytes, false)
.map_err(|e| { .map_err(|e| context.construct_syntax_error(e.to_string()))
context
.throw_syntax_error(e.to_string())
.expect_err("interpreter.throw_syntax_error() did not return an error")
})
.and_then(|expr| expr.run(context)); .and_then(|expr| expr.run(context));
// The main_timer needs to be dropped before the BoaProfiler is. // The main_timer needs to be dropped before the BoaProfiler is.

2
boa/src/object/internal_methods/array.rs

@ -62,7 +62,7 @@ pub(crate) fn array_exotic_define_own_property(
// 5. If SameValueZero(newLen, numberLen) is false, throw a RangeError exception. // 5. If SameValueZero(newLen, numberLen) is false, throw a RangeError exception.
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
if new_len as f64 != number_len { if new_len as f64 != number_len {
return Err(context.construct_range_error("bad length for array")); return context.throw_range_error("bad length for array");
} }
// 2. Let newLenDesc be a copy of Desc. // 2. Let newLenDesc be a copy of Desc.

101
boa/src/object/internal_methods/proxy.rs

@ -100,9 +100,7 @@ pub(crate) fn proxy_exotic_get_prototype_of(
let handler_proto = match &handler_proto { let handler_proto = match &handler_proto {
JsValue::Object(obj) => Some(obj.clone()), JsValue::Object(obj) => Some(obj.clone()),
JsValue::Null => None, JsValue::Null => None,
_ => { _ => return context.throw_type_error("Proxy trap result is neither object nor null"),
return Err(context.construct_type_error("Proxy trap result is neither object nor null"))
}
}; };
// 9. Let extensibleTarget be ? IsExtensible(target). // 9. Let extensibleTarget be ? IsExtensible(target).
@ -116,7 +114,7 @@ pub(crate) fn proxy_exotic_get_prototype_of(
// 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception. // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.
if handler_proto != target_proto { if handler_proto != target_proto {
return Err(context.construct_type_error("Proxy trap returned unexpected prototype")); return context.throw_type_error("Proxy trap returned unexpected prototype");
} }
// 13. Return handlerProto. // 13. Return handlerProto.
@ -181,7 +179,7 @@ pub(crate) fn proxy_exotic_set_prototype_of(
// 12. If SameValue(V, targetProto) is false, throw a TypeError exception. // 12. If SameValue(V, targetProto) is false, throw a TypeError exception.
if val != target_proto { if val != target_proto {
return Err(context.construct_type_error("Proxy trap failed to set prototype")); return context.throw_type_error("Proxy trap failed to set prototype");
} }
// 13. Return true. // 13. Return true.
@ -225,7 +223,7 @@ pub(crate) fn proxy_exotic_is_extensible(obj: &JsObject, context: &mut Context)
// 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.
if boolean_trap_result != target_result { if boolean_trap_result != target_result {
return Err(context.construct_type_error("Proxy trap returned unexpected extensible value")); return context.throw_type_error("Proxy trap returned unexpected extensible value");
} }
// 10. Return booleanTrapResult. // 10. Return booleanTrapResult.
@ -271,7 +269,7 @@ pub(crate) fn proxy_exotic_prevent_extensions(
if boolean_trap_result && target.is_extensible(context)? { if boolean_trap_result && target.is_extensible(context)? {
// a. Let extensibleTarget be ? IsExtensible(target). // a. Let extensibleTarget be ? IsExtensible(target).
// b. If extensibleTarget is true, throw a TypeError exception. // b. If extensibleTarget is true, throw a TypeError exception.
return Err(context.construct_type_error("Proxy trap failed to set extensible")); return context.throw_type_error("Proxy trap failed to set extensible");
} }
// 9. Return booleanTrapResult. // 9. Return booleanTrapResult.
@ -318,9 +316,7 @@ pub(crate) fn proxy_exotic_get_own_property(
// 8. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception. // 8. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
if !trap_result_obj.is_object() && !trap_result_obj.is_undefined() { if !trap_result_obj.is_object() && !trap_result_obj.is_undefined() {
return Err( return context.throw_type_error("Proxy trap result is neither object nor undefined");
context.construct_type_error("Proxy trap result is neither object nor undefined")
);
} }
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P). // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
@ -331,17 +327,17 @@ pub(crate) fn proxy_exotic_get_own_property(
if let Some(desc) = target_desc { if let Some(desc) = target_desc {
// b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
if !desc.expect_configurable() { if !desc.expect_configurable() {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap result is undefined adn target result is not configurable", "Proxy trap result is undefined adn target result is not configurable",
)); );
} }
// c. Let extensibleTarget be ? IsExtensible(target). // c. Let extensibleTarget be ? IsExtensible(target).
// d. If extensibleTarget is false, throw a TypeError exception. // d. If extensibleTarget is false, throw a TypeError exception.
if !target.is_extensible(context)? { if !target.is_extensible(context)? {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap result is undefined and target is not extensible", "Proxy trap result is undefined and target is not extensible",
)); );
// e. Return undefined. // e. Return undefined.
} else { } else {
return Ok(None); return Ok(None);
@ -368,7 +364,7 @@ pub(crate) fn proxy_exotic_get_own_property(
result_desc.clone(), result_desc.clone(),
target_desc.clone(), target_desc.clone(),
) { ) {
return Err(context.construct_type_error("Proxy trap returned unexpected property")); return context.throw_type_error("Proxy trap returned unexpected property");
} }
// 16. If resultDesc.[[Configurable]] is false, then // 16. If resultDesc.[[Configurable]] is false, then
@ -380,17 +376,17 @@ pub(crate) fn proxy_exotic_get_own_property(
if let Some(false) = result_desc.writable() { if let Some(false) = result_desc.writable() {
// i. If targetDesc.[[Writable]] is true, throw a TypeError exception. // i. If targetDesc.[[Writable]] is true, throw a TypeError exception.
if desc.expect_writable() { if desc.expect_writable() {
return Err( return
context.construct_type_error("Proxy trap result is writable and not configurable while target result is not configurable") context.throw_type_error("Proxy trap result is writable and not configurable while target result is not configurable")
); ;
} }
} }
} }
// i. Throw a TypeError exception. // i. Throw a TypeError exception.
_ => { _ => {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap result is not configurable and target result is undefined", "Proxy trap result is not configurable and target result is undefined",
)) )
} }
} }
} }
@ -461,12 +457,12 @@ pub(crate) fn proxy_exotic_define_own_property(
None => { None => {
// a. If extensibleTarget is false, throw a TypeError exception. // a. If extensibleTarget is false, throw a TypeError exception.
if !extensible_target { if !extensible_target {
return Err(context.construct_type_error("Proxy trap failed to set property")); return context.throw_type_error("Proxy trap failed to set property");
} }
// b. If settingConfigFalse is true, throw a TypeError exception. // b. If settingConfigFalse is true, throw a TypeError exception.
if setting_config_false { if setting_config_false {
return Err(context.construct_type_error("Proxy trap failed to set property")); return context.throw_type_error("Proxy trap failed to set property");
} }
} }
// 15. Else, // 15. Else,
@ -477,16 +473,14 @@ pub(crate) fn proxy_exotic_define_own_property(
desc.clone(), desc.clone(),
Some(target_desc.clone()), Some(target_desc.clone()),
) { ) {
return Err( return context.throw_type_error("Proxy trap set property to unexpected value");
context.construct_type_error("Proxy trap set property to unexpected value")
);
} }
// b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception. // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
if setting_config_false && target_desc.expect_configurable() { if setting_config_false && target_desc.expect_configurable() {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap set property with unexpected configurable field", "Proxy trap set property with unexpected configurable field",
)); );
} }
// c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then
@ -497,9 +491,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. // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
if let Some(writable) = desc.writable() { if let Some(writable) = desc.writable() {
if !writable { if !writable {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap set property with unexpected writable field", "Proxy trap set property with unexpected writable field",
)); );
} }
} }
} }
@ -559,13 +553,13 @@ pub(crate) fn proxy_exotic_has_property(
if let Some(target_desc) = target_desc { if let Some(target_desc) = target_desc {
// i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
if !target_desc.expect_configurable() { if !target_desc.expect_configurable() {
return Err(context.construct_type_error("Proxy trap returned unexpected property")); return context.throw_type_error("Proxy trap returned unexpected property");
} }
// ii. Let extensibleTarget be ? IsExtensible(target). // ii. Let extensibleTarget be ? IsExtensible(target).
// iii. If extensibleTarget is false, throw a TypeError exception. // iii. If extensibleTarget is false, throw a TypeError exception.
if !target.is_extensible(context)? { if !target.is_extensible(context)? {
return Err(context.construct_type_error("Proxy trap returned unexpected property")); return context.throw_type_error("Proxy trap returned unexpected property");
} }
} }
} }
@ -623,8 +617,8 @@ pub(crate) fn proxy_exotic_get(
if target_desc.is_data_descriptor() && !target_desc.expect_writable() { if target_desc.is_data_descriptor() && !target_desc.expect_writable() {
// i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
if !JsValue::same_value(&trap_result, target_desc.expect_value()) { if !JsValue::same_value(&trap_result, target_desc.expect_value()) {
return Err(context return context
.construct_type_error("Proxy trap returned unexpected data descriptor")); .throw_type_error("Proxy trap returned unexpected data descriptor");
} }
} }
@ -632,9 +626,8 @@ pub(crate) fn proxy_exotic_get(
if target_desc.is_accessor_descriptor() && target_desc.expect_get().is_undefined() { if target_desc.is_accessor_descriptor() && target_desc.expect_get().is_undefined() {
// i. If trapResult is not undefined, throw a TypeError exception. // i. If trapResult is not undefined, throw a TypeError exception.
if !trap_result.is_undefined() { if !trap_result.is_undefined() {
return Err(context.construct_type_error( return context
"Proxy trap returned unexpected accessor descriptor", .throw_type_error("Proxy trap returned unexpected accessor descriptor");
));
} }
} }
} }
@ -700,9 +693,7 @@ pub(crate) fn proxy_exotic_set(
if target_desc.is_data_descriptor() && !target_desc.expect_writable() { if target_desc.is_data_descriptor() && !target_desc.expect_writable() {
// i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception. // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
if !JsValue::same_value(&value, target_desc.expect_value()) { if !JsValue::same_value(&value, target_desc.expect_value()) {
return Err( return context.throw_type_error("Proxy trap set unexpected data descriptor");
context.construct_type_error("Proxy trap set unexpected data descriptor")
);
} }
} }
@ -711,9 +702,8 @@ pub(crate) fn proxy_exotic_set(
// i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
match target_desc.set() { match target_desc.set() {
None | Some(&JsValue::Undefined) => { None | Some(&JsValue::Undefined) => {
return Err(context.construct_type_error( return context
"Proxy trap set unexpected accessor descriptor", .throw_type_error("Proxy trap set unexpected accessor descriptor");
));
} }
_ => {} _ => {}
} }
@ -776,7 +766,7 @@ pub(crate) fn proxy_exotic_delete(
// 11. If targetDesc.[[Configurable]] is false, throw a TypeError exception. // 11. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
Some(target_desc) => { Some(target_desc) => {
if !target_desc.expect_configurable() { if !target_desc.expect_configurable() {
return Err(context.construct_type_error("Proxy trap failed to delete property")); return context.throw_type_error("Proxy trap failed to delete property");
} }
} }
} }
@ -784,7 +774,7 @@ pub(crate) fn proxy_exotic_delete(
// 12. Let extensibleTarget be ? IsExtensible(target). // 12. Let extensibleTarget be ? IsExtensible(target).
// 13. If extensibleTarget is false, throw a TypeError exception. // 13. If extensibleTarget is false, throw a TypeError exception.
if !target.is_extensible(context)? { if !target.is_extensible(context)? {
return Err(context.construct_type_error("Proxy trap failed to delete property")); return context.throw_type_error("Proxy trap failed to delete property");
} }
// 14. Return true. // 14. Return true.
@ -835,17 +825,17 @@ pub(crate) fn proxy_exotic_own_property_keys(
match value { match value {
JsValue::String(s) => { JsValue::String(s) => {
if !unchecked_result_keys.insert(s.clone().into()) { if !unchecked_result_keys.insert(s.clone().into()) {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap result contains duplicate string property keys", "Proxy trap result contains duplicate string property keys",
)); );
} }
trap_result.push(s.clone().into()) trap_result.push(s.clone().into())
} }
JsValue::Symbol(s) => { JsValue::Symbol(s) => {
if !unchecked_result_keys.insert(s.clone().into()) { if !unchecked_result_keys.insert(s.clone().into()) {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap result contains duplicate symbol property keys", "Proxy trap result contains duplicate symbol property keys",
)); );
} }
trap_result.push(s.clone().into()) trap_result.push(s.clone().into())
} }
@ -895,9 +885,9 @@ pub(crate) fn proxy_exotic_own_property_keys(
// a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
// b. Remove key from uncheckedResultKeys. // b. Remove key from uncheckedResultKeys.
if !unchecked_result_keys.remove(&key) { if !unchecked_result_keys.remove(&key) {
return Err(context.construct_type_error( return context.throw_type_error(
"Proxy trap failed to return all non-configurable property keys", "Proxy trap failed to return all non-configurable property keys",
)); );
} }
} }
@ -911,15 +901,14 @@ pub(crate) fn proxy_exotic_own_property_keys(
// a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
// b. Remove key from uncheckedResultKeys. // b. Remove key from uncheckedResultKeys.
if !unchecked_result_keys.remove(&key) { if !unchecked_result_keys.remove(&key) {
return Err(context.construct_type_error( return context
"Proxy trap failed to return all configurable property keys", .throw_type_error("Proxy trap failed to return all configurable property keys");
));
} }
} }
// 22. If uncheckedResultKeys is not empty, throw a TypeError exception. // 22. If uncheckedResultKeys is not empty, throw a TypeError exception.
if !unchecked_result_keys.is_empty() { if !unchecked_result_keys.is_empty() {
return Err(context.construct_type_error("Proxy trap failed to return all property keys")); return context.throw_type_error("Proxy trap failed to return all property keys");
} }
// 23. Return trapResult. // 23. Return trapResult.
@ -1014,9 +1003,7 @@ fn proxy_exotic_construct(
// 10. If Type(newObj) is not Object, throw a TypeError exception. // 10. If Type(newObj) is not Object, throw a TypeError exception.
if !new_obj.is_object() { if !new_obj.is_object() {
return Err( return context.throw_type_error("Proxy trap constructor returned non-object value");
context.construct_type_error("Proxy trap constructor returned non-object value")
);
} }
// 11. Return newObj. // 11. Return newObj.

12
boa/src/object/jsobject.rs

@ -480,9 +480,7 @@ impl JsObject {
// b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception. // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception.
// todo: extract IsCallable to be callable from Value // todo: extract IsCallable to be callable from Value
if !getter.is_undefined() && getter.as_object().map_or(true, |o| !o.is_callable()) { if !getter.is_undefined() && getter.as_object().map_or(true, |o| !o.is_callable()) {
return Err( return context.throw_type_error("Property descriptor getter must be callable");
context.construct_type_error("Property descriptor getter must be callable")
);
} }
// c. Set desc.[[Get]] to getter. // c. Set desc.[[Get]] to getter.
Some(getter) Some(getter)
@ -498,9 +496,7 @@ impl JsObject {
// 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception. // 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception.
// todo: extract IsCallable to be callable from Value // todo: extract IsCallable to be callable from Value
if !setter.is_undefined() && setter.as_object().map_or(true, |o| !o.is_callable()) { if !setter.is_undefined() && setter.as_object().map_or(true, |o| !o.is_callable()) {
return Err( return context.throw_type_error("Property descriptor setter must be callable");
context.construct_type_error("Property descriptor setter must be callable")
);
} }
// 14.c. Set desc.[[Set]] to setter. // 14.c. Set desc.[[Set]] to setter.
Some(setter) Some(setter)
@ -511,10 +507,10 @@ impl JsObject {
// 15. If desc.[[Get]] is present or desc.[[Set]] is present, then ... // 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. // a. If desc.[[Value]] is present or desc.[[Writable]] is present, throw a TypeError exception.
if get.as_ref().or_else(|| set.as_ref()).is_some() && desc.inner().is_data_descriptor() { if get.as_ref().or_else(|| set.as_ref()).is_some() && desc.inner().is_data_descriptor() {
return Err(context.construct_type_error( return context.throw_type_error(
"Invalid property descriptor.\ "Invalid property descriptor.\
Cannot both specify accessors and a value or writable attribute", Cannot both specify accessors and a value or writable attribute",
)); );
} }
desc = desc.maybe_get(get).maybe_set(set); desc = desc.maybe_get(get).maybe_set(set);

25
boa/src/object/operations.rs

@ -91,9 +91,7 @@ impl JsObject {
let success = self.__set__(key.clone(), value.into(), self.clone().into(), context)?; 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. // 5. If success is false and Throw is true, throw a TypeError exception.
if !success && throw { if !success && throw {
return Err( return context.throw_type_error(format!("cannot set non-writable property: {}", key));
context.construct_type_error(format!("cannot set non-writable property: {}", key))
);
} }
// 6. Return success. // 6. Return success.
Ok(success) Ok(success)
@ -152,7 +150,7 @@ impl JsObject {
let success = self.create_data_property(key.clone(), value, context)?; let success = self.create_data_property(key.clone(), value, context)?;
// 4. If success is false, throw a TypeError exception. // 4. If success is false, throw a TypeError exception.
if !success { if !success {
return Err(context.construct_type_error(format!("cannot redefine property: {}", key))); return context.throw_type_error(format!("cannot redefine property: {}", key));
} }
// 5. Return success. // 5. Return success.
Ok(success) Ok(success)
@ -182,7 +180,7 @@ impl JsObject {
let success = self.__define_own_property__(key.clone(), desc.into(), context)?; let success = self.__define_own_property__(key.clone(), desc.into(), context)?;
// 4. If success is false, throw a TypeError exception. // 4. If success is false, throw a TypeError exception.
if !success { if !success {
return Err(context.construct_type_error(format!("cannot redefine property: {}", key))); return context.throw_type_error(format!("cannot redefine property: {}", key));
} }
// 5. Return success. // 5. Return success.
Ok(success) Ok(success)
@ -206,7 +204,7 @@ impl JsObject {
let success = self.__delete__(&key, context)?; let success = self.__delete__(&key, context)?;
// 4. If success is false, throw a TypeError exception. // 4. If success is false, throw a TypeError exception.
if !success { if !success {
return Err(context.construct_type_error(format!("cannot delete property: {}", key))); return context.throw_type_error(format!("cannot delete property: {}", key));
} }
// 5. Return success. // 5. Return success.
Ok(success) Ok(success)
@ -451,7 +449,7 @@ impl JsObject {
// 4. If Type(C) is not Object, throw a TypeError exception. // 4. If Type(C) is not Object, throw a TypeError exception.
if !c.is_object() { if !c.is_object() {
return Err(context.construct_type_error("property 'constructor' is not an object")); return context.throw_type_error("property 'constructor' is not an object");
} }
// 5. Let S be ? Get(C, @@species). // 5. Let S be ? Get(C, @@species).
@ -466,7 +464,7 @@ impl JsObject {
// 8. Throw a TypeError exception. // 8. Throw a TypeError exception.
match s.as_object() { match s.as_object() {
Some(obj) if obj.is_constructor() => Ok(obj.clone()), Some(obj) if obj.is_constructor() => Ok(obj.clone()),
_ => Err(context.construct_type_error("property 'constructor' is not a constructor")), _ => context.throw_type_error("property 'constructor' is not a constructor"),
} }
} }
@ -555,8 +553,9 @@ impl JsObject {
// 5. Return func. // 5. Return func.
JsValue::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())), JsValue::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())),
// 4. If IsCallable(func) is false, throw a TypeError exception. // 4. If IsCallable(func) is false, throw a TypeError exception.
_ => Err(context _ => {
.construct_type_error("value returned for property of object is not a function")), context.throw_type_error("value returned for property of object is not a function")
}
} }
} }
@ -667,7 +666,7 @@ impl JsValue {
let next = obj.get(index, context)?; let next = obj.get(index, context)?;
// c. If Type(next) is not an element of elementTypes, throw a TypeError exception. // c. If Type(next) is not an element of elementTypes, throw a TypeError exception.
if !types.contains(&next.get_type()) { if !types.contains(&next.get_type()) {
return Err(context.construct_type_error("bad type")); return context.throw_type_error("bad type");
} }
// d. Append next as the last element of list. // d. Append next as the last element of list.
list.push(next.clone()); list.push(next.clone());
@ -746,8 +745,8 @@ impl JsValue {
obj obj
} else { } else {
// 5. If Type(P) is not Object, throw a TypeError exception. // 5. If Type(P) is not Object, throw a TypeError exception.
return Err(context return context
.construct_type_error("function has non-object prototype in instanceof check")); .throw_type_error("function has non-object prototype in instanceof check");
}; };
// 6. Repeat, // 6. Repeat,

8
boa/src/syntax/ast/node/declaration/mod.rs

@ -472,10 +472,10 @@ impl DeclarationPatternObject {
}; };
if value.is_null() { if value.is_null() {
return Err(context.construct_type_error("Cannot destructure 'null' value")); return context.throw_type_error("Cannot destructure 'null' value");
} }
if value.is_undefined() { if value.is_undefined() {
return Err(context.construct_type_error("Cannot destructure 'undefined' value")); return context.throw_type_error("Cannot destructure 'undefined' value");
} }
// 1. Perform ? RequireObjectCoercible(value). // 1. Perform ? RequireObjectCoercible(value).
@ -685,10 +685,10 @@ impl DeclarationPatternArray {
}; };
if value.is_null() { if value.is_null() {
return Err(context.construct_type_error("Cannot destructure 'null' value")); return context.throw_type_error("Cannot destructure 'null' value");
} }
if value.is_undefined() { if value.is_undefined() {
return Err(context.construct_type_error("Cannot destructure 'undefined' value")); return context.throw_type_error("Cannot destructure 'undefined' value");
} }
// 1. Let iteratorRecord be ? GetIterator(value). // 1. Let iteratorRecord be ? GetIterator(value).

42
boa/src/value/mod.rs

@ -374,7 +374,7 @@ impl JsValue {
// 5. If success is false and Throw is true, throw a TypeError exception. // 5. If success is false and Throw is true, throw a TypeError exception.
// 6. Return success. // 6. Return success.
if !success && throw { if !success && throw {
return Err(context.construct_type_error("Cannot assign value to property")); return context.throw_type_error("Cannot assign value to property");
} else { } else {
return Ok(value); return Ok(value);
} }
@ -435,7 +435,7 @@ impl JsValue {
// v. If Type(result) is not Object, return result. // v. If Type(result) is not Object, return result.
// vi. Throw a TypeError exception. // vi. Throw a TypeError exception.
return if result.is_object() { return if result.is_object() {
Err(context.construct_type_error("Symbol.toPrimitive cannot return an object")) context.throw_type_error("Symbol.toPrimitive cannot return an object")
} else { } else {
Ok(result) Ok(result)
}; };
@ -465,33 +465,29 @@ impl JsValue {
/// [spec]: https://tc39.es/ecma262/#sec-tobigint /// [spec]: https://tc39.es/ecma262/#sec-tobigint
pub fn to_bigint(&self, context: &mut Context) -> JsResult<JsBigInt> { pub fn to_bigint(&self, context: &mut Context) -> JsResult<JsBigInt> {
match self { match self {
JsValue::Null => Err(context.construct_type_error("cannot convert null to a BigInt")), JsValue::Null => context.throw_type_error("cannot convert null to a BigInt"),
JsValue::Undefined => { JsValue::Undefined => context.throw_type_error("cannot convert undefined to a BigInt"),
Err(context.construct_type_error("cannot convert undefined to a BigInt"))
}
JsValue::String(ref string) => { JsValue::String(ref string) => {
if let Some(value) = JsBigInt::from_string(string) { if let Some(value) = JsBigInt::from_string(string) {
Ok(value) Ok(value)
} else { } else {
Err(context.construct_syntax_error(format!( context.throw_syntax_error(format!(
"cannot convert string '{}' to bigint primitive", "cannot convert string '{}' to bigint primitive",
string string
))) ))
} }
} }
JsValue::Boolean(true) => Ok(JsBigInt::one()), JsValue::Boolean(true) => Ok(JsBigInt::one()),
JsValue::Boolean(false) => Ok(JsBigInt::zero()), JsValue::Boolean(false) => Ok(JsBigInt::zero()),
JsValue::Integer(_) | JsValue::Rational(_) => { JsValue::Integer(_) | JsValue::Rational(_) => {
Err(context.construct_type_error("cannot convert Number to a BigInt")) context.throw_type_error("cannot convert Number to a BigInt")
} }
JsValue::BigInt(b) => Ok(b.clone()), JsValue::BigInt(b) => Ok(b.clone()),
JsValue::Object(_) => { JsValue::Object(_) => {
let primitive = self.to_primitive(context, PreferredType::Number)?; let primitive = self.to_primitive(context, PreferredType::Number)?;
primitive.to_bigint(context) primitive.to_bigint(context)
} }
JsValue::Symbol(_) => { JsValue::Symbol(_) => context.throw_type_error("cannot convert Symbol to a BigInt"),
Err(context.construct_type_error("cannot convert Symbol to a BigInt"))
}
} }
} }
@ -522,9 +518,7 @@ impl JsValue {
JsValue::Rational(rational) => Ok(Number::to_native_string(*rational).into()), JsValue::Rational(rational) => Ok(Number::to_native_string(*rational).into()),
JsValue::Integer(integer) => Ok(integer.to_string().into()), JsValue::Integer(integer) => Ok(integer.to_string().into()),
JsValue::String(string) => Ok(string.clone()), JsValue::String(string) => Ok(string.clone()),
JsValue::Symbol(_) => { JsValue::Symbol(_) => context.throw_type_error("can't convert symbol to string"),
Err(context.construct_type_error("can't convert symbol to string"))
}
JsValue::BigInt(ref bigint) => Ok(bigint.to_string().into()), JsValue::BigInt(ref bigint) => Ok(bigint.to_string().into()),
JsValue::Object(_) => { JsValue::Object(_) => {
let primitive = self.to_primitive(context, PreferredType::String)?; let primitive = self.to_primitive(context, PreferredType::String)?;
@ -541,7 +535,7 @@ impl JsValue {
pub fn to_object(&self, context: &mut Context) -> JsResult<JsObject> { pub fn to_object(&self, context: &mut Context) -> JsResult<JsObject> {
match self { match self {
JsValue::Undefined | JsValue::Null => { JsValue::Undefined | JsValue::Null => {
Err(context.construct_type_error("cannot convert 'null' or 'undefined' to object")) context.throw_type_error("cannot convert 'null' or 'undefined' to object")
} }
JsValue::Boolean(boolean) => { JsValue::Boolean(boolean) => {
let prototype = context.standard_objects().boolean_object().prototype(); let prototype = context.standard_objects().boolean_object().prototype();
@ -859,13 +853,11 @@ impl JsValue {
let integer_index = self.to_integer(context)?; let integer_index = self.to_integer(context)?;
if integer_index < 0.0 { if integer_index < 0.0 {
return Err(context.construct_range_error("Integer index must be >= 0")); return context.throw_range_error("Integer index must be >= 0");
} }
if integer_index > Number::MAX_SAFE_INTEGER { if integer_index > Number::MAX_SAFE_INTEGER {
return Err( return context.throw_range_error("Integer index must be less than 2**(53) - 1");
context.construct_range_error("Integer index must be less than 2**(53) - 1")
);
} }
Ok(integer_index as usize) Ok(integer_index as usize)
@ -922,12 +914,8 @@ impl JsValue {
JsValue::String(ref string) => Ok(string.string_to_number()), JsValue::String(ref string) => Ok(string.string_to_number()),
JsValue::Rational(number) => Ok(number), JsValue::Rational(number) => Ok(number),
JsValue::Integer(integer) => Ok(f64::from(integer)), JsValue::Integer(integer) => Ok(f64::from(integer)),
JsValue::Symbol(_) => { JsValue::Symbol(_) => context.throw_type_error("argument must not be a symbol"),
Err(context.construct_type_error("argument must not be a symbol")) JsValue::BigInt(_) => context.throw_type_error("argument must not be a bigint"),
}
JsValue::BigInt(_) => {
Err(context.construct_type_error("argument must not be a bigint"))
}
JsValue::Object(_) => { JsValue::Object(_) => {
let primitive = self.to_primitive(context, PreferredType::Number)?; let primitive = self.to_primitive(context, PreferredType::Number)?;
primitive.to_number(context) primitive.to_number(context)
@ -962,7 +950,7 @@ impl JsValue {
#[inline] #[inline]
pub fn require_object_coercible(&self, context: &mut Context) -> JsResult<&JsValue> { pub fn require_object_coercible(&self, context: &mut Context) -> JsResult<&JsValue> {
if self.is_null_or_undefined() { if self.is_null_or_undefined() {
Err(context.construct_type_error("cannot convert null or undefined to Object")) context.throw_type_error("cannot convert null or undefined to Object")
} else { } else {
Ok(self) Ok(self)
} }

6
boa/src/value/operations.rs

@ -398,10 +398,10 @@ impl JsValue {
pub fn instance_of(&self, target: &JsValue, context: &mut Context) -> JsResult<bool> { pub fn instance_of(&self, target: &JsValue, context: &mut Context) -> JsResult<bool> {
// 1. If Type(target) is not Object, throw a TypeError exception. // 1. If Type(target) is not Object, throw a TypeError exception.
if !target.is_object() { if !target.is_object() {
return Err(context.construct_type_error(format!( return context.throw_type_error(format!(
"right-hand side of 'instanceof' should be an object, got {}", "right-hand side of 'instanceof' should be an object, got {}",
target.type_of() target.type_of()
))); ));
} }
// 2. Let instOfHandler be ? GetMethod(target, @@hasInstance). // 2. Let instOfHandler be ? GetMethod(target, @@hasInstance).
@ -419,7 +419,7 @@ impl JsValue {
} }
None => { None => {
// 4. If IsCallable(target) is false, throw a TypeError exception. // 4. If IsCallable(target) is false, throw a TypeError exception.
Err(context.construct_type_error("right-hand side of 'instanceof' is not callable")) context.throw_type_error("right-hand side of 'instanceof' is not callable")
} }
} }
} }

20
boa/src/vm/mod.rs

@ -225,10 +225,10 @@ impl Context {
let lhs = self.vm.pop(); let lhs = self.vm.pop();
if !rhs.is_object() { if !rhs.is_object() {
return Err(self.construct_type_error(format!( return self.throw_type_error(format!(
"right-hand side of 'in' should be an object, got {}", "right-hand side of 'in' should be an object, got {}",
rhs.type_of() rhs.type_of()
))); ));
} }
let key = lhs.to_property_key(self)?; let key = lhs.to_property_key(self)?;
let value = self.has_property(&rhs, &key)?; let value = self.has_property(&rhs, &key)?;
@ -634,7 +634,7 @@ impl Context {
} }
Opcode::Call => { Opcode::Call => {
if self.vm.stack_size_limit <= self.vm.stack.len() { if self.vm.stack_size_limit <= self.vm.stack.len() {
return Err(self.construct_range_error("Maximum call stack size exceeded")); return self.throw_range_error("Maximum call stack size exceeded");
} }
let argc = self.vm.read::<u32>(); let argc = self.vm.read::<u32>();
let mut args = Vec::with_capacity(argc as usize); let mut args = Vec::with_capacity(argc as usize);
@ -647,7 +647,7 @@ impl Context {
let object = match func { let object = match func {
JsValue::Object(ref object) if object.is_callable() => object.clone(), JsValue::Object(ref object) if object.is_callable() => object.clone(),
_ => return Err(self.construct_type_error("not a callable function")), _ => return self.throw_type_error("not a callable function"),
}; };
let result = object.__call__(&this, &args, self)?; let result = object.__call__(&this, &args, self)?;
@ -656,7 +656,7 @@ impl Context {
} }
Opcode::CallWithRest => { Opcode::CallWithRest => {
if self.vm.stack_size_limit <= self.vm.stack.len() { if self.vm.stack_size_limit <= self.vm.stack.len() {
return Err(self.construct_range_error("Maximum call stack size exceeded")); return self.throw_range_error("Maximum call stack size exceeded");
} }
let argc = self.vm.read::<u32>(); let argc = self.vm.read::<u32>();
let mut args = Vec::with_capacity(argc as usize); let mut args = Vec::with_capacity(argc as usize);
@ -680,7 +680,7 @@ impl Context {
let object = match func { let object = match func {
JsValue::Object(ref object) if object.is_callable() => object.clone(), JsValue::Object(ref object) if object.is_callable() => object.clone(),
_ => return Err(self.construct_type_error("not a callable function")), _ => return self.throw_type_error("not a callable function"),
}; };
let result = object.__call__(&this, &args, self)?; let result = object.__call__(&this, &args, self)?;
@ -689,7 +689,7 @@ impl Context {
} }
Opcode::New => { Opcode::New => {
if self.vm.stack_size_limit <= self.vm.stack.len() { if self.vm.stack_size_limit <= self.vm.stack.len() {
return Err(self.construct_range_error("Maximum call stack size exceeded")); return self.throw_range_error("Maximum call stack size exceeded");
} }
let argc = self.vm.read::<u32>(); let argc = self.vm.read::<u32>();
let mut args = Vec::with_capacity(argc as usize); let mut args = Vec::with_capacity(argc as usize);
@ -707,7 +707,7 @@ impl Context {
} }
Opcode::NewWithRest => { Opcode::NewWithRest => {
if self.vm.stack_size_limit <= self.vm.stack.len() { if self.vm.stack_size_limit <= self.vm.stack.len() {
return Err(self.construct_range_error("Maximum call stack size exceeded")); return self.throw_range_error("Maximum call stack size exceeded");
} }
let argc = self.vm.read::<u32>(); let argc = self.vm.read::<u32>();
let mut args = Vec::with_capacity(argc as usize); let mut args = Vec::with_capacity(argc as usize);
@ -854,10 +854,10 @@ impl Context {
Opcode::ValueNotNullOrUndefined => { Opcode::ValueNotNullOrUndefined => {
let value = self.vm.pop(); let value = self.vm.pop();
if value.is_null() { if value.is_null() {
return Err(self.construct_type_error("Cannot destructure 'null' value")); return self.throw_type_error("Cannot destructure 'null' value");
} }
if value.is_undefined() { if value.is_undefined() {
return Err(self.construct_type_error("Cannot destructure 'undefined' value")); return self.throw_type_error("Cannot destructure 'undefined' value");
} }
self.vm.push(value); self.vm.push(value);
} }

2
boa_tester/src/exec/js262.rs

@ -67,7 +67,7 @@ fn detach_array_buffer(
// 3. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception. // 3. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception.
if !JsValue::same_value(&array_buffer.array_buffer_detach_key, key) { if !JsValue::same_value(&array_buffer.array_buffer_detach_key, key) {
return Err(context.construct_type_error("Cannot detach array buffer with different key")); return context.throw_type_error("Cannot detach array buffer with different key");
} }
// 4. Set arrayBuffer.[[ArrayBufferData]] to null. // 4. Set arrayBuffer.[[ArrayBufferData]] to null.

5
boa_wasm/src/lib.rs

@ -11,10 +11,7 @@ pub fn evaluate(src: &str) -> Result<String, JsValue> {
Err(e) => { Err(e) => {
return Err(format!( return Err(format!(
"Uncaught {}", "Uncaught {}",
context context.construct_syntax_error(e.to_string()).display()
.throw_syntax_error(e.to_string())
.expect_err("interpreter.throw_syntax_error() did not return an error")
.display()
) )
.into()); .into());
} }

Loading…
Cancel
Save