Browse Source

Unwrap removal (#1842)

This removes all the calls to  `unwrap()` in the codebase, which made me found a couple of places where it wasn't needed, and could be improved. I also noticed we don't have dependabot updates for the test262 submodule and the interner dependencies, so I added those.

I added lints so that no new unwraps are added.
pull/1843/head
Iban Eguia 3 years ago
parent
commit
748465d17b
  1. 50
      .github/dependabot.yml
  2. 25
      boa/src/builtins/array/mod.rs
  3. 4
      boa/src/builtins/iterable/mod.rs
  4. 11
      boa/src/builtins/json/mod.rs
  5. 31
      boa/src/builtins/number/mod.rs
  6. 6
      boa/src/builtins/object/mod.rs
  7. 34
      boa/src/builtins/regexp/mod.rs
  8. 23
      boa/src/builtins/string/mod.rs
  9. 24
      boa/src/bytecompiler.rs
  10. 1
      boa/src/lib.rs
  11. 323
      boa/src/object/internal_methods/array.rs
  12. 9
      boa/src/object/jsobject.rs
  13. 43
      boa/src/object/mod.rs
  14. 12
      boa/src/string.rs
  15. 5
      boa/src/syntax/lexer/cursor.rs
  16. 7
      boa/src/syntax/lexer/identifier.rs
  17. 25
      boa/src/syntax/lexer/template.rs
  18. 5
      boa/src/syntax/parser/error.rs
  19. 4
      boa/src/syntax/parser/expression/mod.rs
  20. 10
      boa/src/syntax/parser/statement/iteration/for_statement.rs
  21. 24
      boa/src/syntax/parser/statement/mod.rs
  22. 6
      boa/src/value/operations.rs
  23. 16
      boa/src/vm/code_block.rs
  24. 33
      boa/src/vm/mod.rs
  25. 2
      boa_cli/src/helper.rs
  26. 16
      boa_cli/src/main.rs
  27. 1
      boa_interner/src/lib.rs
  28. 5
      boa_tester/src/main.rs
  29. 1
      boa_unicode/src/lib.rs
  30. 1
      boa_wasm/src/lib.rs

50
.github/dependabot.yml

@ -1,30 +1,38 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: "npm" - package-ecosystem: npm
directory: "/" directory: /
schedule: schedule:
interval: "daily" interval: daily
- package-ecosystem: "github-actions" - package-ecosystem: github-actions
directory: "/" directory: /
schedule: schedule:
interval: "daily" interval: daily
- package-ecosystem: "cargo" - package-ecosystem: cargo
directory: "/" directory: /
schedule: schedule:
interval: "daily" interval: daily
- package-ecosystem: "cargo" - package-ecosystem: cargo
directory: "/boa/" directory: /boa/
schedule: schedule:
interval: "daily" interval: daily
- package-ecosystem: "cargo" - package-ecosystem: cargo
directory: "/boa_cli/" directory: /boa_cli/
schedule: schedule:
interval: "daily" interval: daily
- package-ecosystem: "cargo" - package-ecosystem: cargo
directory: "/boa_wasm/" directory: /boa_wasm/
schedule: schedule:
interval: "daily" interval: daily
- package-ecosystem: "cargo" - package-ecosystem: cargo
directory: "/boa_tester/" directory: /boa_tester/
schedule: schedule:
interval: "daily" interval: daily
- package-ecosystem: cargo
directory: /boa_interner/
schedule:
interval: daily
- package-ecosystem: gitsubmodule
directory: /test262/
schedule:
interval: weekly

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

@ -143,27 +143,30 @@ impl Array {
if number_of_args == 0 { if number_of_args == 0 {
// 4.a. Return ! ArrayCreate(0, proto). // 4.a. Return ! ArrayCreate(0, proto).
Ok(Self::array_create(0, Some(prototype), context) Ok(Self::array_create(0, Some(prototype), context)
.unwrap() .expect("this ArrayCreate call must not fail")
.into()) .into())
// 5. Else if numberOfArgs = 1, then // 5. Else if numberOfArgs = 1, then
} else if number_of_args == 1 { } else if number_of_args == 1 {
// a. Let len be values[0]. // a. Let len be values[0].
let len = &args[0]; let len = &args[0];
// b. Let array be ! ArrayCreate(0, proto). // b. Let array be ! ArrayCreate(0, proto).
let array = Self::array_create(0, Some(prototype), context).unwrap(); let array = Self::array_create(0, Some(prototype), context)
.expect("this ArrayCreate call must not fail");
// c. If Type(len) is not Number, then // c. If Type(len) is not Number, then
#[allow(clippy::if_not_else)] #[allow(clippy::if_not_else)]
let int_len = if !len.is_number() { let int_len = if !len.is_number() {
// i. Perform ! CreateDataPropertyOrThrow(array, "0", len). // i. Perform ! CreateDataPropertyOrThrow(array, "0", len).
array array
.create_data_property_or_throw(0, len, context) .create_data_property_or_throw(0, len, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// ii. Let intLen be 1𝔽. // ii. Let intLen be 1𝔽.
1 1
// d. Else, // d. Else,
} else { } else {
// i. Let intLen be ! ToUint32(len). // i. Let intLen be ! ToUint32(len).
let int_len = len.to_u32(context).unwrap(); let int_len = len
.to_u32(context)
.expect("this ToUint32 call must not fail");
// 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 context.throw_range_error("invalid array length"); return context.throw_range_error("invalid array length");
@ -171,7 +174,9 @@ impl Array {
int_len int_len
}; };
// e. Perform ! Set(array, "length", intLen, true). // e. Perform ! Set(array, "length", intLen, true).
array.set("length", int_len, true, context).unwrap(); array
.set("length", int_len, true, context)
.expect("this Set call must not fail");
// f. Return array. // f. Return array.
Ok(array.into()) Ok(array.into())
// 6. Else, // 6. Else,
@ -189,7 +194,7 @@ impl Array {
// iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK). // iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK).
array array
.create_data_property_or_throw(i, item, context) .create_data_property_or_throw(i, item, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow must not fail");
// iv. Set k to k + 1. // iv. Set k to k + 1.
} }
// e. Assert: The mathematical value of array's "length" property is numberOfArgs. // e. Assert: The mathematical value of array's "length" property is numberOfArgs.
@ -360,8 +365,8 @@ impl Array {
Ok( Ok(
c.construct(&[JsValue::new(length)], &c.clone().into(), context)? c.construct(&[JsValue::new(length)], &c.clone().into(), context)?
.as_object() .as_object()
.cloned() .expect("constructing an object should always return an object")
.unwrap(), .clone(),
) )
} else { } else {
context.throw_type_error("Symbol.species must be a constructor") context.throw_type_error("Symbol.species must be a constructor")
@ -537,7 +542,7 @@ impl Array {
if spreadable { if spreadable {
// item is guaranteed to be an object since is_concat_spreadable checks it, // item is guaranteed to be an object since is_concat_spreadable checks it,
// so we can call `.unwrap()` // so we can call `.unwrap()`
let item = item.as_object().unwrap(); let item = item.as_object().expect("guaranteed to be an object");
// i. Let k be 0. // i. Let k be 0.
// ii. Let len be ? LengthOfArrayLike(E). // ii. Let len be ? LengthOfArrayLike(E).
let len = item.length_of_array_like(context)?; let len = item.length_of_array_like(context)?;
@ -1639,7 +1644,7 @@ impl Array {
// v. If shouldFlatten is true // v. If shouldFlatten is true
if should_flatten { if should_flatten {
// For `should_flatten` to be true, element must be an object. // For `should_flatten` to be true, element must be an object.
let element = element.as_object().unwrap(); let element = element.as_object().expect("must be an object");
// 1. If depth is +Infinity let newDepth be +Infinity // 1. If depth is +Infinity let newDepth be +Infinity
let new_depth = if depth == u64::MAX { let new_depth = if depth == u64::MAX {

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

@ -83,10 +83,10 @@ pub fn create_iter_result_object(value: JsValue, done: bool, context: &mut Conte
// 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value). // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value).
obj.create_data_property_or_throw("value", value, context) obj.create_data_property_or_throw("value", value, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done). // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done).
obj.create_data_property_or_throw("done", done, context) obj.create_data_property_or_throw("done", done, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 5. Return obj. // 5. Return obj.
obj.into() obj.into()
} }

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

@ -170,7 +170,9 @@ impl Json {
// ii. For each String P of keys, do // ii. For each String P of keys, do
for p in keys { for p in keys {
// This is safe, because EnumerableOwnPropertyNames with 'key' type only returns strings. // This is safe, because EnumerableOwnPropertyNames with 'key' type only returns strings.
let p = p.as_string().unwrap(); let p = p
.as_string()
.expect("EnumerableOwnPropertyNames only returns strings");
// 1. Let newElement be ? InternalizeJSONProperty(val, P, reviver). // 1. Let newElement be ? InternalizeJSONProperty(val, P, reviver).
let new_element = let new_element =
@ -553,7 +555,12 @@ impl Json {
// a. Let K be ? EnumerableOwnPropertyNames(value, key). // a. Let K be ? EnumerableOwnPropertyNames(value, key).
let keys = value.enumerable_own_property_names(PropertyNameKind::Key, context)?; let keys = value.enumerable_own_property_names(PropertyNameKind::Key, context)?;
// Unwrap is safe, because EnumerableOwnPropertyNames with kind "key" only returns string values. // Unwrap is safe, because EnumerableOwnPropertyNames with kind "key" only returns string values.
keys.iter().map(|v| v.to_string(context).unwrap()).collect() keys.iter()
.map(|v| {
v.to_string(context)
.expect("EnumerableOwnPropertyNames only returns strings")
})
.collect()
}; };
// 7. Let partial be a new empty List. // 7. Let partial be a new empty List.

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

@ -343,7 +343,10 @@ impl Number {
fn round_to_precision(digits: &mut String, precision: usize) -> bool { fn round_to_precision(digits: &mut String, precision: usize) -> bool {
if digits.len() > precision { if digits.len() > precision {
let to_round = digits.split_off(precision); let to_round = digits.split_off(precision);
let mut digit = digits.pop().unwrap() as u8; let mut digit = digits
.pop()
.expect("already checked that lenght is bigger than precision")
as u8;
if let Some(first) = to_round.chars().next() { if let Some(first) = to_round.chars().next() {
if first > '4' { if first > '4' {
digit += 1; digit += 1;
@ -563,8 +566,9 @@ impl Number {
delta *= f64::from(radix); delta *= f64::from(radix);
// Write digit. // Write digit.
let digit = fraction as u32; let digit = fraction as u32;
frac_buf[fraction_cursor] = frac_buf[fraction_cursor] = std::char::from_digit(digit, u32::from(radix))
std::char::from_digit(digit, u32::from(radix)).unwrap() as u8; .expect("radix already checked")
as u8;
fraction_cursor += 1; fraction_cursor += 1;
// Calculate remainder. // Calculate remainder.
fraction -= f64::from(digit); fraction -= f64::from(digit);
@ -582,12 +586,16 @@ impl Number {
} else { } else {
let c: u8 = frac_buf[fraction_cursor]; let c: u8 = frac_buf[fraction_cursor];
// Reconstruct digit. // Reconstruct digit.
let digit_0 = (c as char).to_digit(10).unwrap(); let digit_0 = (c as char)
.to_digit(10)
.expect("charactre was not a valid digit");
if digit_0 + 1 >= u32::from(radix) { if digit_0 + 1 >= u32::from(radix) {
continue; continue;
} }
frac_buf[fraction_cursor] = frac_buf[fraction_cursor] =
std::char::from_digit(digit_0 + 1, u32::from(radix)).unwrap() as u8; std::char::from_digit(digit_0 + 1, u32::from(radix))
.expect("digit was not a valid number in the given radix")
as u8;
fraction_cursor += 1; fraction_cursor += 1;
} }
break; break;
@ -601,16 +609,17 @@ impl Number {
} }
// Compute integer digits. Fill unrepresented digits with zero. // Compute integer digits. Fill unrepresented digits with zero.
let mut int_iter = int_buf.iter_mut().enumerate().rev(); //.rev(); let mut int_iter = int_buf.iter_mut().enumerate().rev();
while FloatCore::integer_decode(integer / f64::from(radix)).1 > 0 { while FloatCore::integer_decode(integer / f64::from(radix)).1 > 0 {
integer /= f64::from(radix); integer /= f64::from(radix);
*int_iter.next().unwrap().1 = b'0'; *int_iter.next().expect("integer buffer exhausted").1 = b'0';
} }
loop { loop {
let remainder = integer % f64::from(radix); let remainder = integer % f64::from(radix);
*int_iter.next().unwrap().1 = *int_iter.next().expect("integer buffer exhausted").1 =
std::char::from_digit(remainder as u32, u32::from(radix)).unwrap() as u8; std::char::from_digit(remainder as u32, u32::from(radix))
.expect("remainder not a digit in the given number") as u8;
integer = (integer - remainder) / f64::from(radix); integer = (integer - remainder) / f64::from(radix);
if integer <= 0f64 { if integer <= 0f64 {
break; break;
@ -618,11 +627,11 @@ impl Number {
} }
// Add sign and terminate string. // Add sign and terminate string.
if negative { if negative {
*int_iter.next().unwrap().1 = b'-'; *int_iter.next().expect("integer buffer exhausted").1 = b'-';
} }
assert!(fraction_cursor < BUF_SIZE); assert!(fraction_cursor < BUF_SIZE);
let integer_cursor = int_iter.next().unwrap().0 + 1; let integer_cursor = int_iter.next().expect("integer buffer exhausted").0 + 1;
let fraction_cursor = fraction_cursor + BUF_SIZE / 2; let fraction_cursor = fraction_cursor + BUF_SIZE / 2;
String::from_utf8_lossy(&buffer[integer_cursor..fraction_cursor]).into() String::from_utf8_lossy(&buffer[integer_cursor..fraction_cursor]).into()
} }

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

@ -605,7 +605,9 @@ impl Object {
// 3.a. If nextSource is neither undefined nor null, then // 3.a. If nextSource is neither undefined nor null, then
if !source.is_null_or_undefined() { if !source.is_null_or_undefined() {
// 3.a.i. Let from be ! ToObject(nextSource). // 3.a.i. Let from be ! ToObject(nextSource).
let from = source.to_object(context).unwrap(); let from = source
.to_object(context)
.expect("this ToObject call must not fail");
// 3.a.ii. Let keys be ? from.[[OwnPropertyKeys]](). // 3.a.ii. Let keys be ? from.[[OwnPropertyKeys]]().
let keys = from.__own_property_keys__(context)?; let keys = from.__own_property_keys__(context)?;
// 3.a.iii. For each element nextKey of keys, do // 3.a.iii. For each element nextKey of keys, do
@ -925,7 +927,7 @@ impl Object {
// 4. Let closure be a new Abstract Closure with parameters (key, value) that captures // 4. Let closure be a new Abstract Closure with parameters (key, value) that captures
// obj and performs the following steps when called: // obj and performs the following steps when called:
let mut closure = FunctionBuilder::closure_with_captures( let closure = FunctionBuilder::closure_with_captures(
context, context,
|_, args, obj, context| { |_, args, obj, context| {
let key = args.get_or_undefined(0); let key = args.get_or_undefined(0);

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

@ -202,7 +202,9 @@ impl RegExp {
// 6. Else, // 6. Else,
let (p, f) = if let Some(pattern) = pattern_is_regexp { let (p, f) = if let Some(pattern) = pattern_is_regexp {
let obj = pattern.borrow(); let obj = pattern.borrow();
let regexp = obj.as_regexp().unwrap(); let regexp = obj
.as_regexp()
.expect("already checked that IsRegExp returns true");
// a. Let P be pattern.[[OriginalSource]]. // a. Let P be pattern.[[OriginalSource]].
// b. If flags is undefined, let F be pattern.[[OriginalFlags]]. // b. If flags is undefined, let F be pattern.[[OriginalFlags]].
@ -885,11 +887,11 @@ impl RegExp {
// 20. Perform ! CreateDataPropertyOrThrow(A, "index", 𝔽(lastIndex)). // 20. Perform ! CreateDataPropertyOrThrow(A, "index", 𝔽(lastIndex)).
a.create_data_property_or_throw("index", match_value.start(), context) a.create_data_property_or_throw("index", match_value.start(), context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 21. Perform ! CreateDataPropertyOrThrow(A, "input", S). // 21. Perform ! CreateDataPropertyOrThrow(A, "input", S).
a.create_data_property_or_throw("input", input.clone(), context) a.create_data_property_or_throw("input", input.clone(), context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 22. Let matchedSubstr be the substring of S from lastIndex to e. // 22. Let matchedSubstr be the substring of S from lastIndex to e.
let matched_substr = if let Some(s) = input.get(match_value.range()) { let matched_substr = if let Some(s) = input.get(match_value.range()) {
@ -900,7 +902,7 @@ impl RegExp {
// 23. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr). // 23. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr).
a.create_data_property_or_throw(0, matched_substr, context) a.create_data_property_or_throw(0, matched_substr, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 24. If R contains any GroupName, then // 24. If R contains any GroupName, then
// 25. Else, // 25. Else,
@ -924,7 +926,7 @@ impl RegExp {
groups groups
.to_object(context)? .to_object(context)?
.create_data_property_or_throw(name, value, context) .create_data_property_or_throw(name, value, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
} }
} }
groups groups
@ -935,7 +937,7 @@ impl RegExp {
// 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups). // 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
a.create_data_property_or_throw("groups", groups, context) a.create_data_property_or_throw("groups", groups, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do // 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do
for i in 1..=n { for i in 1..=n {
@ -958,7 +960,7 @@ impl RegExp {
// e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue). // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue).
a.create_data_property_or_throw(i, captured_value, context) a.create_data_property_or_throw(i, captured_value, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
} }
// 28. Return A. // 28. Return A.
@ -1019,7 +1021,8 @@ impl RegExp {
rx.set("lastIndex", 0, true, context)?; rx.set("lastIndex", 0, true, context)?;
// d. Let A be ! ArrayCreate(0). // d. Let A be ! ArrayCreate(0).
let a = Array::array_create(0, None, context).unwrap(); let a =
Array::array_create(0, None, context).expect("this ArrayCreate call must not fail");
// e. Let n be 0. // e. Let n be 0.
let mut n = 0; let mut n = 0;
@ -1037,7 +1040,7 @@ impl RegExp {
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr). // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr).
a.create_data_property_or_throw(n, match_str.clone(), context) a.create_data_property_or_throw(n, match_str.clone(), context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 3. If matchStr is the empty String, then // 3. If matchStr is the empty String, then
if match_str.is_empty() { if match_str.is_empty() {
@ -1530,11 +1533,12 @@ impl RegExp {
)?; )?;
let splitter = splitter let splitter = splitter
.as_object() .as_object()
// todo: remove when we handle realms on `get_prototype_from_constructor` and make `construct` always return a `JsObject` // TODO: remove when we handle realms on `get_prototype_from_constructor` and make
// `construct` always return a `JsObject`
.ok_or_else(|| context.construct_type_error("constructor did not return an object"))?; .ok_or_else(|| context.construct_type_error("constructor did not return an object"))?;
// 11. Let A be ! ArrayCreate(0). // 11. Let A be ! ArrayCreate(0).
let a = Array::array_create(0, None, context).unwrap(); let a = Array::array_create(0, None, context).expect("this ArrayCreate call must not fail");
// 12. Let lengthA be 0. // 12. Let lengthA be 0.
let mut length_a = 0; let mut length_a = 0;
@ -1567,7 +1571,7 @@ impl RegExp {
// c. Perform ! CreateDataPropertyOrThrow(A, "0", S). // c. Perform ! CreateDataPropertyOrThrow(A, "0", S).
a.create_data_property_or_throw(0, arg_str, context) a.create_data_property_or_throw(0, arg_str, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// d. Return A. // d. Return A.
return Ok(a.into()); return Ok(a.into());
@ -1611,7 +1615,7 @@ impl RegExp {
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
a.create_data_property_or_throw(length_a, arg_str_substring, context) a.create_data_property_or_throw(length_a, arg_str_substring, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 3. Set lengthA to lengthA + 1. // 3. Set lengthA to lengthA + 1.
length_a += 1; length_a += 1;
@ -1642,7 +1646,7 @@ impl RegExp {
// b. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), nextCapture). // b. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), nextCapture).
a.create_data_property_or_throw(length_a, next_capture, context) a.create_data_property_or_throw(length_a, next_capture, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// d. Set lengthA to lengthA + 1. // d. Set lengthA to lengthA + 1.
length_a += 1; length_a += 1;
@ -1672,7 +1676,7 @@ impl RegExp {
// 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). // 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
a.create_data_property_or_throw(length_a, arg_str_substring, context) a.create_data_property_or_throw(length_a, arg_str_substring, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 22. Return A. // 22. Return A.
Ok(a.into()) Ok(a.into())

23
boa/src/builtins/string/mod.rs

@ -1678,7 +1678,7 @@ impl String {
if separator.is_undefined() { if separator.is_undefined() {
// a. Perform ! CreateDataPropertyOrThrow(A, "0", S). // a. Perform ! CreateDataPropertyOrThrow(A, "0", S).
a.create_data_property_or_throw(0, this_str, context) a.create_data_property_or_throw(0, this_str, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// b. Return A. // b. Return A.
return Ok(a.into()); return Ok(a.into());
@ -1693,7 +1693,7 @@ impl String {
if !separator_str.is_empty() { if !separator_str.is_empty() {
// i. Perform ! CreateDataPropertyOrThrow(A, "0", S). // i. Perform ! CreateDataPropertyOrThrow(A, "0", S).
a.create_data_property_or_throw(0, this_str, context) a.create_data_property_or_throw(0, this_str, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
} }
// b. Return A. // b. Return A.
@ -1732,7 +1732,7 @@ impl String {
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
a.create_data_property_or_throw(length_a, this_str_substring, context) a.create_data_property_or_throw(length_a, this_str_substring, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 3. Set lengthA to lengthA + 1. // 3. Set lengthA to lengthA + 1.
length_a += 1; length_a += 1;
@ -1763,7 +1763,7 @@ impl String {
// 16. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). // 16. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
a.create_data_property_or_throw(length_a, this_str_substring, context) a.create_data_property_or_throw(length_a, this_str_substring, context)
.unwrap(); .expect("this CreateDataPropertyOrThrow call must not fail");
// 17. Return A. // 17. Return A.
Ok(a.into()) Ok(a.into())
@ -2013,8 +2013,14 @@ pub(crate) fn get_substitution(
// $nn // $nn
(Some(second), Some(third)) if second_is_digit && third_is_digit => { (Some(second), Some(third)) if second_is_digit && third_is_digit => {
// The nnth element of captures, where nn is a two-digit decimal number in the range 01 to 99. // The nnth element of captures, where nn is a two-digit decimal number in the range 01 to 99.
let tens = second.to_digit(10).unwrap() as usize; let tens = second
let units = third.to_digit(10).unwrap() as usize; .to_digit(10)
.expect("could not convert character to digit after checking it")
as usize;
let units = third
.to_digit(10)
.expect("could not convert character to digit after checking it")
as usize;
let nn = 10 * tens + units; let nn = 10 * tens + units;
// If nn ≤ m and the nnth element of captures is undefined, use the empty String instead. // If nn ≤ m and the nnth element of captures is undefined, use the empty String instead.
@ -2034,7 +2040,10 @@ pub(crate) fn get_substitution(
// $n // $n
(Some(second), _) if second_is_digit => { (Some(second), _) if second_is_digit => {
// The nth element of captures, where n is a single digit in the range 1 to 9. // The nth element of captures, where n is a single digit in the range 1 to 9.
let n = second.to_digit(10).unwrap() as usize; let n = second
.to_digit(10)
.expect("could not convert character to digit after checking it")
as usize;
// If n ≤ m and the nth element of captures is undefined, use the empty String instead. // If n ≤ m and the nth element of captures is undefined, use the empty String instead.
// If n > m, no replacement is done. // If n > m, no replacement is done.

24
boa/src/bytecompiler.rs

@ -261,7 +261,7 @@ impl<'b> ByteCompiler<'b> {
#[inline] #[inline]
fn pop_loop_control_info(&mut self) { fn pop_loop_control_info(&mut self) {
let loop_info = self.jump_info.pop().unwrap(); let loop_info = self.jump_info.pop().expect("no jump informatiojn found");
assert!(loop_info.kind == JumpControlInfoKind::Loop); assert!(loop_info.kind == JumpControlInfoKind::Loop);
@ -284,7 +284,7 @@ impl<'b> ByteCompiler<'b> {
#[inline] #[inline]
fn pop_switch_control_info(&mut self) { fn pop_switch_control_info(&mut self) {
let info = self.jump_info.pop().unwrap(); let info = self.jump_info.pop().expect("no jump information found");
assert!(info.kind == JumpControlInfoKind::Switch); assert!(info.kind == JumpControlInfoKind::Switch);
@ -296,7 +296,11 @@ impl<'b> ByteCompiler<'b> {
#[inline] #[inline]
fn push_try_control_info(&mut self) { fn push_try_control_info(&mut self) {
if !self.jump_info.is_empty() { if !self.jump_info.is_empty() {
let start_address = self.jump_info.last().unwrap().start_address; let start_address = self
.jump_info
.last()
.expect("no jump information found")
.start_address;
self.jump_info.push(JumpControlInfo { self.jump_info.push(JumpControlInfo {
label: None, label: None,
@ -312,7 +316,7 @@ impl<'b> ByteCompiler<'b> {
#[inline] #[inline]
fn pop_try_control_info(&mut self, finally_start_address: Option<u32>) { fn pop_try_control_info(&mut self, finally_start_address: Option<u32>) {
if !self.jump_info.is_empty() { if !self.jump_info.is_empty() {
let mut info = self.jump_info.pop().unwrap(); let mut info = self.jump_info.pop().expect("no jump information found");
assert!(info.kind == JumpControlInfoKind::Try); assert!(info.kind == JumpControlInfoKind::Try);
@ -1219,7 +1223,11 @@ impl<'b> ByteCompiler<'b> {
self.emit_opcode(Opcode::TryEnd); self.emit_opcode(Opcode::TryEnd);
self.emit(Opcode::FinallySetJump, &[start_address]); self.emit(Opcode::FinallySetJump, &[start_address]);
let label = self.jump(); let label = self.jump();
self.jump_info.last_mut().unwrap().try_continues.push(label); self.jump_info
.last_mut()
.expect("no jump information found")
.try_continues
.push(label);
} else { } else {
let mut items = self let mut items = self
.jump_info .jump_info
@ -1264,7 +1272,11 @@ impl<'b> ByteCompiler<'b> {
} }
let label = self.jump(); let label = self.jump();
if node.label().is_none() { if node.label().is_none() {
self.jump_info.last_mut().unwrap().breaks.push(label); self.jump_info
.last_mut()
.expect("no jump information found")
.breaks
.push(label);
} else { } else {
for info in self.jump_info.iter_mut().rev() { for info in self.jump_info.iter_mut().rev() {
if info.label == node.label() { if info.label == node.label() {

1
boa/src/lib.rs

@ -10,6 +10,7 @@
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
)] )]
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
#![warn( #![warn(
clippy::perf, clippy::perf,
clippy::single_match_else, clippy::single_match_else,

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

@ -35,165 +35,13 @@ pub(crate) fn array_exotic_define_own_property(
PropertyKey::String(ref s) if s == "length" => { PropertyKey::String(ref s) if s == "length" => {
// a. Return ? ArraySetLength(A, Desc). // a. Return ? ArraySetLength(A, Desc).
// Abstract operation `ArraySetLength ( A, Desc )` array_set_length(obj, desc, context)
//
// https://tc39.es/ecma262/#sec-arraysetlength
// 1. If Desc.[[Value]] is absent, then
let new_len_val = match desc.value() {
Some(value) => value,
_ => {
// a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
return super::ordinary_define_own_property(
obj,
"length".into(),
desc,
context,
);
}
};
// 3. Let newLen be ? ToUint32(Desc.[[Value]]).
let new_len = new_len_val.to_u32(context)?;
// 4. Let numberLen be ? ToNumber(Desc.[[Value]]).
let number_len = new_len_val.to_number(context)?;
// 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");
}
// 2. Let newLenDesc be a copy of Desc.
// 6. Set newLenDesc.[[Value]] to newLen.
let mut new_len_desc = PropertyDescriptor::builder()
.value(new_len)
.maybe_writable(desc.writable())
.maybe_enumerable(desc.enumerable())
.maybe_configurable(desc.configurable());
// 7. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
let old_len_desc =
super::ordinary_get_own_property(obj, &"length".into(), context)?.unwrap();
// 8. Assert: ! IsDataDescriptor(oldLenDesc) is true.
debug_assert!(old_len_desc.is_data_descriptor());
// 9. Assert: oldLenDesc.[[Configurable]] is false.
debug_assert!(!old_len_desc.expect_configurable());
// 10. Let oldLen be oldLenDesc.[[Value]].
let old_len = old_len_desc.expect_value();
// 11. If newLen ≥ oldLen, then
if new_len >= old_len.to_u32(context)? {
// a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
return super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.build(),
context,
);
}
// 12. If oldLenDesc.[[Writable]] is false, return false.
if !old_len_desc.expect_writable() {
return Ok(false);
}
// 13. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
let new_writable = if new_len_desc.inner().writable().unwrap_or(true) {
true
}
// 14. Else,
else {
// a. NOTE: Setting the [[Writable]] attribute to false is deferred in case any
// elements cannot be deleted.
// c. Set newLenDesc.[[Writable]] to true.
new_len_desc = new_len_desc.writable(true);
// b. Let newWritable be false.
false
};
// 15. Let succeeded be ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
// 16. If succeeded is false, return false.
if !super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.clone().build(),
context,
)
.unwrap()
{
return Ok(false);
}
// 17. For each own property key P of A that is an array index, whose numeric value is
// greater than or equal to newLen, in descending numeric index order, do
let ordered_keys = {
let mut keys: Vec<_> = obj
.borrow()
.properties
.index_property_keys()
.filter(|idx| new_len <= **idx && **idx < u32::MAX)
.copied()
.collect();
keys.sort_unstable_by(|x, y| y.cmp(x));
keys
};
for index in ordered_keys {
// a. Let deleteSucceeded be ! A.[[Delete]](P).
// b. If deleteSucceeded is false, then
if !obj.__delete__(&index.into(), context)? {
// i. Set newLenDesc.[[Value]] to ! ToUint32(P) + 1𝔽.
new_len_desc = new_len_desc.value(index + 1);
// ii. If newWritable is false, set newLenDesc.[[Writable]] to false.
if !new_writable {
new_len_desc = new_len_desc.writable(false);
}
// iii. Perform ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.build(),
context,
)
.unwrap();
// iv. Return false.
return Ok(false);
}
}
// 18. If newWritable is false, then
if !new_writable {
// a. Set succeeded to ! OrdinaryDefineOwnProperty(A, "length",
// PropertyDescriptor { [[Writable]]: false }).
let succeeded = super::ordinary_define_own_property(
obj,
"length".into(),
PropertyDescriptor::builder().writable(false).build(),
context,
)?;
// b. Assert: succeeded is true.
debug_assert!(succeeded);
}
// 19. Return true.
Ok(true)
} }
// 3. Else if P is an array index, then // 3. Else if P is an array index, then
PropertyKey::Index(index) if index < u32::MAX => { PropertyKey::Index(index) if index < u32::MAX => {
// a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). // a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
let old_len_desc = let old_len_desc = super::ordinary_get_own_property(obj, &"length".into(), context)?
super::ordinary_get_own_property(obj, &"length".into(), context)?.unwrap(); .expect("the property descriptor must exist");
// b. Assert: ! IsDataDescriptor(oldLenDesc) is true. // b. Assert: ! IsDataDescriptor(oldLenDesc) is true.
debug_assert!(old_len_desc.is_data_descriptor()); debug_assert!(old_len_desc.is_data_descriptor());
@ -204,7 +52,10 @@ pub(crate) fn array_exotic_define_own_property(
// d. Let oldLen be oldLenDesc.[[Value]]. // d. Let oldLen be oldLenDesc.[[Value]].
// e. Assert: oldLen is a non-negative integral Number. // e. Assert: oldLen is a non-negative integral Number.
// f. Let index be ! ToUint32(P). // f. Let index be ! ToUint32(P).
let old_len = old_len_desc.expect_value().to_u32(context).unwrap(); let old_len = old_len_desc
.expect_value()
.to_u32(context)
.expect("this ToUint32 call must not fail");
// g. If index ≥ oldLen and oldLenDesc.[[Writable]] is false, return false. // g. If index ≥ oldLen and oldLenDesc.[[Writable]] is false, return false.
if index >= old_len && !old_len_desc.expect_writable() { if index >= old_len && !old_len_desc.expect_writable() {
@ -228,8 +79,7 @@ pub(crate) fn array_exotic_define_own_property(
"length".into(), "length".into(),
old_len_desc.into(), old_len_desc.into(),
context, context,
) )?;
.unwrap();
// iii. Assert: succeeded is true. // iii. Assert: succeeded is true.
debug_assert!(succeeded); debug_assert!(succeeded);
@ -246,3 +96,160 @@ pub(crate) fn array_exotic_define_own_property(
_ => super::ordinary_define_own_property(obj, key, desc, context), _ => super::ordinary_define_own_property(obj, key, desc, context),
} }
} }
/// Abstract operation `ArraySetLength ( A, Desc )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arraysetlength
fn array_set_length(
obj: &JsObject,
desc: PropertyDescriptor,
context: &mut Context,
) -> JsResult<bool> {
// 1. If Desc.[[Value]] is absent, then
let new_len_val = match desc.value() {
Some(value) => value,
_ => {
// a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
return super::ordinary_define_own_property(obj, "length".into(), desc, context);
}
};
// 3. Let newLen be ? ToUint32(Desc.[[Value]]).
let new_len = new_len_val.to_u32(context)?;
// 4. Let numberLen be ? ToNumber(Desc.[[Value]]).
let number_len = new_len_val.to_number(context)?;
// 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");
}
// 2. Let newLenDesc be a copy of Desc.
// 6. Set newLenDesc.[[Value]] to newLen.
let mut new_len_desc = PropertyDescriptor::builder()
.value(new_len)
.maybe_writable(desc.writable())
.maybe_enumerable(desc.enumerable())
.maybe_configurable(desc.configurable());
// 7. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
let old_len_desc = super::ordinary_get_own_property(obj, &"length".into(), context)?
.expect("the property descriptor must exist");
// 8. Assert: ! IsDataDescriptor(oldLenDesc) is true.
debug_assert!(old_len_desc.is_data_descriptor());
// 9. Assert: oldLenDesc.[[Configurable]] is false.
debug_assert!(!old_len_desc.expect_configurable());
// 10. Let oldLen be oldLenDesc.[[Value]].
let old_len = old_len_desc.expect_value();
// 11. If newLen ≥ oldLen, then
if new_len >= old_len.to_u32(context)? {
// a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
return super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.build(),
context,
);
}
// 12. If oldLenDesc.[[Writable]] is false, return false.
if !old_len_desc.expect_writable() {
return Ok(false);
}
// 13. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
let new_writable = if new_len_desc.inner().writable().unwrap_or(true) {
true
}
// 14. Else,
else {
// a. NOTE: Setting the [[Writable]] attribute to false is deferred in case any
// elements cannot be deleted.
// c. Set newLenDesc.[[Writable]] to true.
new_len_desc = new_len_desc.writable(true);
// b. Let newWritable be false.
false
};
// 15. Let succeeded be ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
// 16. If succeeded is false, return false.
if !super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.clone().build(),
context,
)
.expect("this OrdinaryDefineOwnProperty call must not fail")
{
return Ok(false);
}
// 17. For each own property key P of A that is an array index, whose numeric value is
// greater than or equal to newLen, in descending numeric index order, do
let ordered_keys = {
let mut keys: Vec<_> = obj
.borrow()
.properties
.index_property_keys()
.filter(|idx| new_len <= **idx && **idx < u32::MAX)
.copied()
.collect();
keys.sort_unstable_by(|x, y| y.cmp(x));
keys
};
for index in ordered_keys {
// a. Let deleteSucceeded be ! A.[[Delete]](P).
// b. If deleteSucceeded is false, then
if !obj.__delete__(&index.into(), context)? {
// i. Set newLenDesc.[[Value]] to ! ToUint32(P) + 1𝔽.
new_len_desc = new_len_desc.value(index + 1);
// ii. If newWritable is false, set newLenDesc.[[Writable]] to false.
if !new_writable {
new_len_desc = new_len_desc.writable(false);
}
// iii. Perform ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.build(),
context,
)
.expect("this OrdinaryDefineOwnProperty call must not fail");
// iv. Return false.
return Ok(false);
}
}
// 18. If newWritable is false, then
if !new_writable {
// a. Set succeeded to ! OrdinaryDefineOwnProperty(A, "length",
// PropertyDescriptor { [[Writable]]: false }).
let succeeded = super::ordinary_define_own_property(
obj,
"length".into(),
PropertyDescriptor::builder().writable(false).build(),
context,
)
.expect("this OrdinaryDefineOwnProperty call must not fail");
// b. Assert: succeeded is true.
debug_assert!(succeeded);
}
// 19. Return true.
Ok(true)
}

9
boa/src/object/jsobject.rs

@ -211,7 +211,9 @@ impl JsObject {
{ {
let object = self.borrow(); let object = self.borrow();
if object.is::<T>() { if object.is::<T>() {
Some(Ref::map(object, |x| x.downcast_ref::<T>().unwrap())) Some(Ref::map(object, |x| {
x.downcast_ref::<T>().expect("downcasting reference failed")
}))
} else { } else {
None None
} }
@ -231,7 +233,10 @@ impl JsObject {
{ {
let object = self.borrow_mut(); let object = self.borrow_mut();
if object.is::<T>() { if object.is::<T>() {
Some(RefMut::map(object, |x| x.downcast_mut::<T>().unwrap())) Some(RefMut::map(object, |x| {
x.downcast_mut::<T>()
.expect("downcasting mutable reference failed")
}))
} else { } else {
None None
} }

43
boa/src/object/mod.rs

@ -1238,7 +1238,7 @@ where
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionBuilder<'context> { pub struct FunctionBuilder<'context> {
context: &'context mut Context, context: &'context mut Context,
function: Option<Function>, function: Function,
name: JsString, name: JsString,
length: usize, length: usize,
} }
@ -1249,10 +1249,10 @@ impl<'context> FunctionBuilder<'context> {
pub fn native(context: &'context mut Context, function: NativeFunctionSignature) -> Self { pub fn native(context: &'context mut Context, function: NativeFunctionSignature) -> Self {
Self { Self {
context, context,
function: Some(Function::Native { function: Function::Native {
function, function,
constructor: false, constructor: false,
}), },
name: JsString::default(), name: JsString::default(),
length: 0, length: 0,
} }
@ -1266,11 +1266,11 @@ impl<'context> FunctionBuilder<'context> {
{ {
Self { Self {
context, context,
function: Some(Function::Closure { function: Function::Closure {
function: Box::new(move |this, args, _, context| function(this, args, context)), function: Box::new(move |this, args, _, context| function(this, args, context)),
constructor: false, constructor: false,
captures: Captures::new(()), captures: Captures::new(()),
}), },
name: JsString::default(), name: JsString::default(),
length: 0, length: 0,
} }
@ -1294,7 +1294,7 @@ impl<'context> FunctionBuilder<'context> {
{ {
Self { Self {
context, context,
function: Some(Function::Closure { function: Function::Closure {
function: Box::new(move |this, args, captures: Captures, context| { function: Box::new(move |this, args, captures: Captures, context| {
let mut captures = captures.as_mut_any(); let mut captures = captures.as_mut_any();
let captures = captures.downcast_mut::<C>().ok_or_else(|| { let captures = captures.downcast_mut::<C>().ok_or_else(|| {
@ -1304,7 +1304,7 @@ impl<'context> FunctionBuilder<'context> {
}), }),
constructor: false, constructor: false,
captures: Captures::new(captures), captures: Captures::new(captures),
}), },
name: JsString::default(), name: JsString::default(),
length: 0, length: 0,
} }
@ -1314,7 +1314,7 @@ impl<'context> FunctionBuilder<'context> {
/// ///
/// The default is `""` (empty string). /// The default is `""` (empty string).
#[inline] #[inline]
pub fn name<N>(&mut self, name: N) -> &mut Self pub fn name<N>(mut self, name: N) -> Self
where where
N: AsRef<str>, N: AsRef<str>,
{ {
@ -1328,7 +1328,7 @@ impl<'context> FunctionBuilder<'context> {
/// ///
/// The default is `0`. /// The default is `0`.
#[inline] #[inline]
pub fn length(&mut self, length: usize) -> &mut Self { pub fn length(mut self, length: usize) -> Self {
self.length = length; self.length = length;
self self
} }
@ -1337,40 +1337,47 @@ impl<'context> FunctionBuilder<'context> {
/// ///
/// The default is `false`. /// The default is `false`.
#[inline] #[inline]
pub fn constructor(&mut self, yes: bool) -> &mut Self { pub fn constructor(mut self, yes: bool) -> Self {
match self.function.as_mut() { match self.function {
Some(Function::Native { constructor, .. } | Function::Closure { constructor, .. }) => { Function::Native {
ref mut constructor,
..
}
| Function::Closure {
ref mut constructor,
..
} => {
*constructor = yes; *constructor = yes;
} }
_ => unreachable!(), Function::VmOrdinary { .. } => unreachable!("function must be native or closure"),
} }
self self
} }
/// Build the function object. /// Build the function object.
#[inline] #[inline]
pub fn build(&mut self) -> JsObject { pub fn build(self) -> JsObject {
let function = JsObject::from_proto_and_data( let function = JsObject::from_proto_and_data(
self.context self.context
.standard_objects() .standard_objects()
.function_object() .function_object()
.prototype(), .prototype(),
ObjectData::function(self.function.take().unwrap()), ObjectData::function(self.function),
); );
let property = PropertyDescriptor::builder() let property = PropertyDescriptor::builder()
.writable(false) .writable(false)
.enumerable(false) .enumerable(false)
.configurable(true); .configurable(true);
function.insert_property("length", property.clone().value(self.length)); function.insert_property("length", property.clone().value(self.length));
function.insert_property("name", property.value(self.name.clone())); function.insert_property("name", property.value(self.name));
function function
} }
/// Initializes the `Function.prototype` function object. /// Initializes the `Function.prototype` function object.
pub(crate) fn build_function_prototype(&mut self, object: &JsObject) { pub(crate) fn build_function_prototype(self, object: &JsObject) {
let mut object = object.borrow_mut(); let mut object = object.borrow_mut();
object.data = ObjectData::function(self.function.take().unwrap()); object.data = ObjectData::function(self.function);
object.set_prototype(self.context.standard_objects().object_object().prototype()); object.set_prototype(self.context.standard_objects().object_object().prototype());
let property = PropertyDescriptor::builder() let property = PropertyDescriptor::builder()

12
boa/src/string.rs

@ -213,8 +213,8 @@ impl Inner {
// of the string array. // of the string array.
let inner_layout = Layout::new::<Self>(); let inner_layout = Layout::new::<Self>();
let (layout, offset) = inner_layout let (layout, offset) = inner_layout
.extend(Layout::array::<u8>(s.len()).unwrap()) .extend(Layout::array::<u8>(s.len()).expect("failed to create memory layout"))
.unwrap(); .expect("failed to extend memory layout");
let inner = unsafe { let inner = unsafe {
let inner = alloc(layout).cast::<Self>(); let inner = alloc(layout).cast::<Self>();
@ -253,8 +253,8 @@ impl Inner {
// of the string array. // of the string array.
let inner_layout = Layout::new::<Self>(); let inner_layout = Layout::new::<Self>();
let (layout, offset) = inner_layout let (layout, offset) = inner_layout
.extend(Layout::array::<u8>(total_string_size).unwrap()) .extend(Layout::array::<u8>(total_string_size).expect("failed to create memory layout"))
.unwrap(); .expect("failed to extend memory layout");
let inner = unsafe { let inner = unsafe {
let inner = alloc(layout).cast::<Self>(); let inner = alloc(layout).cast::<Self>();
@ -292,8 +292,8 @@ impl Inner {
let inner_layout = Layout::new::<Self>(); let inner_layout = Layout::new::<Self>();
let (layout, _offset) = inner_layout let (layout, _offset) = inner_layout
.extend(Layout::array::<u8>(len).unwrap()) .extend(Layout::array::<u8>(len).expect("failed to create memory layout"))
.unwrap(); .expect("failed to extend memory layout");
dealloc(x.as_ptr().cast::<_>(), layout); dealloc(x.as_ptr().cast::<_>(), layout);
} }

5
boa/src/syntax/lexer/cursor.rs

@ -204,7 +204,10 @@ where
return Ok(()); return Ok(());
} else if let Some(ch) = self.peek_char()? { } else if let Some(ch) = self.peek_char()? {
for _ in 0..utf8_len(ch) { for _ in 0..utf8_len(ch) {
buf.push(self.next_byte()?.unwrap()); buf.push(
self.next_byte()?
.expect("already checked that the next character exists"),
);
} }
} else { } else {
// next_is_pred will return false if the next value is None so the None case should already be handled. // next_is_pred will return false if the next value is None so the None case should already be handled.

7
boa/src/syntax/lexer/identifier.rs

@ -154,7 +154,10 @@ impl Identifier {
if Self::is_identifier_start(ch) { if Self::is_identifier_start(ch) {
contains_escaped_chars = true; contains_escaped_chars = true;
String::from(char::try_from(ch).unwrap()) String::from(
char::try_from(ch)
.expect("all identifier starts must be convertible to strings"),
)
} else { } else {
return Err(Error::Syntax("invalid identifier start".into(), start_pos)); return Err(Error::Syntax("invalid identifier start".into(), start_pos));
} }
@ -185,7 +188,7 @@ impl Identifier {
_ => break, _ => break,
}; };
identifier_name.push(char::try_from(ch).unwrap()); identifier_name.push(char::try_from(ch).expect("checked character value"));
} }
Ok((identifier_name, contains_escaped_chars)) Ok((identifier_name, contains_escaped_chars))

25
boa/src/syntax/lexer/template.rs

@ -20,7 +20,8 @@ use serde::{Deserialize, Serialize};
pub struct TemplateString { pub struct TemplateString {
/// The template string of template literal with argument `raw` true. /// The template string of template literal with argument `raw` true.
raw: Sym, raw: Sym,
/// The start position of the template string. Used to make lexer error if `to_owned_cooked` failed. /// The start position of the template string. Used to make lexer error if `to_owned_cooked`
/// failed.
start_pos: Position, start_pos: Position,
} }
@ -40,7 +41,8 @@ impl TemplateString {
self.raw self.raw
} }
/// Creats a new cooked template string. Returns a lexer error if it fails to cook the template string. /// Creats a new cooked template string. Returns a lexer error if it fails to cook the
/// template string.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
@ -70,8 +72,8 @@ impl TemplateString {
} }
Some(ch) => { Some(ch) => {
// The caller guarantees that sequences '`' and '${' never appear // The caller guarantees that sequences '`' and '${' never appear
// LineTerminatorSequence <CR> <LF> is consumed by `cursor.next_char()` and returns <LF>, // LineTerminatorSequence <CR> <LF> is consumed by `cursor.next_char()` and
// which matches the TV of <CR> <LF> // returns <LF>, which matches the TV of <CR> <LF>
buf.push_code_point(ch); buf.push_code_point(ch);
} }
None => break, None => break,
@ -119,7 +121,8 @@ impl<R> Tokenizer<R> for TemplateLiteral {
})?; })?;
match ch { match ch {
0x0060 /* ` */ => { // `
0x0060 => {
let raw = buf.to_string_lossy(); let raw = buf.to_string_lossy();
let raw_sym = interner.get_or_intern(raw); let raw_sym = interner.get_or_intern(raw);
let template_string = TemplateString::new(raw_sym, start_pos); let template_string = TemplateString::new(raw_sym, start_pos);
@ -129,7 +132,8 @@ impl<R> Tokenizer<R> for TemplateLiteral {
Span::new(start_pos, cursor.pos()), Span::new(start_pos, cursor.pos()),
)); ));
} }
0x0024 /* $ */ if cursor.next_is(b'{')? => { // $
0x0024 if cursor.next_is(b'{')? => {
let raw = buf.to_string_lossy(); let raw = buf.to_string_lossy();
let raw_sym = interner.get_or_intern(raw); let raw_sym = interner.get_or_intern(raw);
let template_string = TemplateString::new(raw_sym, start_pos); let template_string = TemplateString::new(raw_sym, start_pos);
@ -139,7 +143,8 @@ impl<R> Tokenizer<R> for TemplateLiteral {
Span::new(start_pos, cursor.pos()), Span::new(start_pos, cursor.pos()),
)); ));
} }
0x005C /* \ */ => { // \
0x005C => {
let escape_ch = cursor.peek()?.ok_or_else(|| { let escape_ch = cursor.peek()?.ok_or_else(|| {
Error::from(io::Error::new( Error::from(io::Error::new(
ErrorKind::UnexpectedEof, ErrorKind::UnexpectedEof,
@ -149,7 +154,11 @@ impl<R> Tokenizer<R> for TemplateLiteral {
buf.push(u16::from(b'\\')); buf.push(u16::from(b'\\'));
match escape_ch { match escape_ch {
b'`' | b'$' | b'\\' => buf.push(u16::from(cursor.next_byte()?.unwrap())), b'`' | b'$' | b'\\' => {
let next_byte =
cursor.next_byte()?.expect("already checked next character");
buf.push(u16::from(next_byte));
}
_ => continue, _ => continue,
} }
} }

5
boa/src/syntax/parser/error.rs

@ -134,7 +134,10 @@ impl fmt::Display for ParseError {
f, f,
"expected {}, got '{found}' in {context} at line {}, col {}", "expected {}, got '{found}' in {context} at line {}, col {}",
if expected.len() == 1 { if expected.len() == 1 {
format!("token '{}'", expected.first().unwrap()) format!(
"token '{}'",
expected.first().expect("already checked that length is 1")
)
} else { } else {
format!( format!(
"one of {}", "one of {}",

4
boa/src/syntax/parser/expression/mod.rs

@ -489,10 +489,6 @@ where
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult { fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult {
let _timer = BoaProfiler::global().start_event("Relation Expression", "Parsing"); let _timer = BoaProfiler::global().start_event("Relation Expression", "Parsing");
if None::<InputElement>.is_some() {
cursor.set_goal(None::<InputElement>.unwrap());
}
let mut lhs = let mut lhs =
ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
while let Some(tok) = cursor.peek(0, interner)? { while let Some(tok) = cursor.peek(0, interner)? {

10
boa/src/syntax/parser/statement/iteration/for_statement.rs

@ -102,9 +102,9 @@ where
), ),
}; };
match cursor.peek(0, interner)? { match (init.as_ref(), cursor.peek(0, interner)?) {
Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::In) && init.is_some() => { (Some(init), Some(tok)) if tok.kind() == &TokenKind::Keyword(Keyword::In) => {
let init = node_to_iterable_loop_initializer(&init.unwrap(), init_position)?; let init = node_to_iterable_loop_initializer(init, init_position)?;
let _next = cursor.next(interner)?; let _next = cursor.next(interner)?;
let expr = Expression::new(None, true, self.allow_yield, self.allow_await) let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
@ -125,8 +125,8 @@ where
return Ok(ForInLoop::new(init, expr, body).into()); return Ok(ForInLoop::new(init, expr, body).into());
} }
Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::Of) && init.is_some() => { (Some(init), Some(tok)) if tok.kind() == &TokenKind::Keyword(Keyword::Of) => {
let init = node_to_iterable_loop_initializer(&init.unwrap(), init_position)?; let init = node_to_iterable_loop_initializer(init, init_position)?;
let _next = cursor.next(interner)?; let _next = cursor.next(interner)?;
let iterable = Expression::new(None, true, self.allow_yield, self.allow_await) let iterable = Expression::new(None, true, self.allow_yield, self.allow_await)

24
boa/src/syntax/parser/statement/mod.rs

@ -202,19 +202,17 @@ where
// Labelled Statement check // Labelled Statement check
cursor.set_goal(InputElement::Div); cursor.set_goal(InputElement::Div);
let tok = cursor.peek(1, interner)?; let tok = cursor.peek(1, interner)?;
if tok.is_some()
&& matches!( if let Some(tok) = tok {
tok.unwrap().kind(), if matches!(tok.kind(), TokenKind::Punctuator(Punctuator::Colon)) {
TokenKind::Punctuator(Punctuator::Colon) return LabelledStatement::new(
) self.allow_yield,
{ self.allow_await,
return LabelledStatement::new( self.allow_return,
self.allow_yield, )
self.allow_await, .parse(cursor, interner)
self.allow_return, .map(Node::from);
) }
.parse(cursor, interner)
.map(Node::from);
} }
ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor, interner) ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor, interner)

6
boa/src/value/operations.rs

@ -541,7 +541,8 @@ impl JsValue {
} else { } else {
y.ceil() y.ceil()
}; };
(*x < JsBigInt::try_from(n).unwrap()).into() (*x < JsBigInt::try_from(n).expect("invalid conversion to BigInt"))
.into()
} }
(Numeric::Number(x), Numeric::BigInt(ref y)) => { (Numeric::Number(x), Numeric::BigInt(ref y)) => {
if x.is_nan() { if x.is_nan() {
@ -555,7 +556,8 @@ impl JsValue {
} else { } else {
x.ceil() x.ceil()
}; };
(JsBigInt::try_from(n).unwrap() < *y).into() (JsBigInt::try_from(n).expect("invalid conversion to BigInt") < *y)
.into()
} }
}, },
} }

16
boa/src/vm/code_block.rs

@ -136,7 +136,7 @@ impl CodeBlock {
/// ///
/// Returns an empty `String` if no operands are present. /// Returns an empty `String` if no operands are present.
pub(crate) fn instruction_operands(&self, pc: &mut usize) -> String { pub(crate) fn instruction_operands(&self, pc: &mut usize) -> String {
let opcode: Opcode = self.code[*pc].try_into().unwrap(); let opcode: Opcode = self.code[*pc].try_into().expect("invalid opcode");
*pc += size_of::<Opcode>(); *pc += size_of::<Opcode>();
match opcode { match opcode {
Opcode::PushInt8 => { Opcode::PushInt8 => {
@ -313,7 +313,7 @@ impl ToInternedString for CodeBlock {
let mut pc = 0; let mut pc = 0;
let mut count = 0; let mut count = 0;
while pc < self.code.len() { while pc < self.code.len() {
let opcode: Opcode = self.code[pc].try_into().unwrap(); let opcode: Opcode = self.code[pc].try_into().expect("invalid opcode");
let operands = self.instruction_operands(&mut pc); let operands = self.instruction_operands(&mut pc);
f.push_str(&format!( f.push_str(&format!(
" {pc:06} {count:04} {:<27}\n{operands}", " {pc:06} {count:04} {:<27}\n{operands}",
@ -406,7 +406,7 @@ impl JsVmFunction {
prototype prototype
.define_property_or_throw("constructor", constructor_property, context) .define_property_or_throw("constructor", constructor_property, context)
.unwrap(); .expect("failed to define the constructor property of the function");
let prototype_property = PropertyDescriptor::builder() let prototype_property = PropertyDescriptor::builder()
.value(prototype) .value(prototype)
@ -417,13 +417,13 @@ impl JsVmFunction {
constructor constructor
.define_property_or_throw("prototype", prototype_property, context) .define_property_or_throw("prototype", prototype_property, context)
.unwrap(); .expect("failed to define the prototype property of the function");
constructor constructor
.define_property_or_throw("name", name_property, context) .define_property_or_throw("name", name_property, context)
.unwrap(); .expect("failed to define the name property of the function");
constructor constructor
.define_property_or_throw("length", length_property, context) .define_property_or_throw("length", length_property, context)
.unwrap(); .expect("failed to define the length property of the function");
constructor constructor
} }
@ -462,7 +462,7 @@ impl JsObject {
let body = { let body = {
let object = self.borrow(); let object = self.borrow();
let function = object.as_function().unwrap(); let function = object.as_function().expect("not a function");
match function { match function {
Function::Native { Function::Native {
@ -633,7 +633,7 @@ impl JsObject {
let body = { let body = {
let object = self.borrow(); let object = self.borrow();
let function = object.as_function().unwrap(); let function = object.as_function().expect("not a function");
match function { match function {
Function::Native { function, .. } => FunctionBody::Native { Function::Native { function, .. } => FunctionBody::Native {

33
boa/src/vm/mod.rs

@ -55,7 +55,7 @@ impl Vm {
#[inline] #[inline]
#[track_caller] #[track_caller]
pub(crate) fn pop(&mut self) -> JsValue { pub(crate) fn pop(&mut self) -> JsValue {
self.stack.pop().unwrap() self.stack.pop().expect("stack was empty")
} }
#[track_caller] #[track_caller]
@ -66,14 +66,24 @@ impl Vm {
value value
} }
/// Retrieves the VM frame
///
/// # Panics
///
/// If there is no frame, then this will panic.
#[inline] #[inline]
pub(crate) fn frame(&self) -> &CallFrame { pub(crate) fn frame(&self) -> &CallFrame {
self.frame.as_ref().unwrap() self.frame.as_ref().expect("no frame found")
} }
/// Retrieves the VM frame mutably
///
/// # Panics
///
/// If there is no frame, then this will panic.
#[inline] #[inline]
pub(crate) fn frame_mut(&mut self) -> &mut CallFrame { pub(crate) fn frame_mut(&mut self) -> &mut CallFrame {
self.frame.as_mut().unwrap() self.frame.as_mut().expect("no frame found")
} }
#[inline] #[inline]
@ -661,10 +671,10 @@ impl Context {
let excluded_key_count = self.vm.read::<u32>(); let excluded_key_count = self.vm.read::<u32>();
let mut excluded_keys = Vec::with_capacity(excluded_key_count as usize); let mut excluded_keys = Vec::with_capacity(excluded_key_count as usize);
for _ in 0..excluded_key_count { for _ in 0..excluded_key_count {
excluded_keys.push(self.vm.pop().as_string().unwrap().clone()); excluded_keys.push(self.vm.pop().as_string().expect("not a string").clone());
} }
let value = self.vm.pop(); let value = self.vm.pop();
let object = value.as_object().unwrap(); let object = value.as_object().expect("not an object");
let source = self.vm.pop(); let source = self.vm.pop();
object.copy_data_properties(&source, excluded_keys, self)?; object.copy_data_properties(&source, excluded_keys, self)?;
self.vm.push(value); self.vm.push(value);
@ -989,7 +999,7 @@ impl Context {
let done = self.vm.pop(); let done = self.vm.pop();
let next_function = self.vm.pop(); let next_function = self.vm.pop();
let iterator = self.vm.pop(); let iterator = self.vm.pop();
if !done.as_boolean().unwrap() { if !done.as_boolean().expect("not a boolean") {
let iterator_record = IteratorRecord::new(iterator, next_function); let iterator_record = IteratorRecord::new(iterator, next_function);
iterator_record.close(Ok(JsValue::Null), self)?; iterator_record.close(Ok(JsValue::Null), self)?;
} }
@ -1075,7 +1085,8 @@ impl Context {
args.push(self.vm.pop()); args.push(self.vm.pop());
} }
let array = Array::new_array(self); let array = Array::new_array(self);
Array::add_to_array_object(&array, &args, self).unwrap(); Array::add_to_array_object(&array, &args, self)
.expect("could not add to array object");
self.vm.push(array); self.vm.push(array);
} else { } else {
self.vm.pop(); self.vm.pop();
@ -1139,7 +1150,13 @@ impl Context {
while self.vm.frame().pc < self.vm.frame().code.code.len() { while self.vm.frame().pc < self.vm.frame().code.code.len() {
let result = if self.vm.trace { let result = if self.vm.trace {
let mut pc = self.vm.frame().pc; let mut pc = self.vm.frame().pc;
let opcode: Opcode = self.vm.frame().code.read::<u8>(pc).try_into().unwrap(); let opcode: Opcode = self
.vm
.frame()
.code
.read::<u8>(pc)
.try_into()
.expect("invalid opcode");
let operands = self.vm.frame().code.instruction_operands(&mut pc); let operands = self.vm.frame().code.instruction_operands(&mut pc);
let instant = Instant::now(); let instant = Instant::now();

2
boa_cli/src/helper.rs

@ -137,7 +137,7 @@ impl Highlighter for LineHighlighter {
(?P<op>[+\-/*%~^!&|=<>;:]) | (?P<op>[+\-/*%~^!&|=<>;:]) |
(?P<number>0[bB][01](_?[01])*n?|0[oO][0-7](_?[0-7])*n?|0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?|(([0-9](_?[0-9])*\.([0-9](_?[0-9])*)?)|(([0-9](_?[0-9])*)?\.[0-9](_?[0-9])*)|([0-9](_?[0-9])*))([eE][+-]?[0-9](_?[0-9])*)?n?)"#, (?P<number>0[bB][01](_?[01])*n?|0[oO][0-7](_?[0-7])*n?|0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?|(([0-9](_?[0-9])*\.([0-9](_?[0-9])*)?)|(([0-9](_?[0-9])*)?\.[0-9](_?[0-9])*)|([0-9](_?[0-9])*))([eE][+-]?[0-9](_?[0-9])*)?n?)"#,
) )
.unwrap(); .expect("could not compile regular expression");
coloured = reg coloured = reg
.replace_all(&coloured, |caps: &Captures<'_>| { .replace_all(&coloured, |caps: &Captures<'_>| {

16
boa_cli/src/main.rs

@ -2,6 +2,7 @@
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
)] )]
#![cfg_attr(not(test), deny(clippy::unwrap_used))]
#![warn( #![warn(
clippy::perf, clippy::perf,
clippy::single_match_else, clippy::single_match_else,
@ -168,9 +169,16 @@ where
match arg { match arg {
Some(format) => match format { Some(format) => match format {
DumpFormat::Debug => println!("{ast:#?}"), DumpFormat::Debug => println!("{ast:#?}"),
DumpFormat::Json => println!("{}", serde_json::to_string(&ast).unwrap()), DumpFormat::Json => println!(
"{}",
serde_json::to_string(&ast).expect("could not convert AST to a JSON string")
),
DumpFormat::JsonPretty => { DumpFormat::JsonPretty => {
println!("{}", serde_json::to_string_pretty(&ast).unwrap()); println!(
"{}",
serde_json::to_string_pretty(&ast)
.expect("could not convert AST to a pretty JSON string")
);
} }
}, },
// Default ast dumping format. // Default ast dumping format.
@ -256,7 +264,9 @@ pub fn main() -> Result<(), std::io::Error> {
} }
} }
editor.save_history(CLI_HISTORY).unwrap(); editor
.save_history(CLI_HISTORY)
.expect("could not save CLI history");
} }
Ok(()) Ok(())

1
boa_interner/src/lib.rs

@ -12,6 +12,7 @@
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
)] )]
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
#![warn( #![warn(
clippy::perf, clippy::perf,
clippy::single_match_else, clippy::single_match_else,

5
boa_tester/src/main.rs

@ -2,6 +2,11 @@
//! //!
//! This crate will run the full ECMAScript test suite (Test262) and report compliance of the //! This crate will run the full ECMAScript test suite (Test262) and report compliance of the
//! `boa` context. //! `boa` context.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
)]
#![cfg_attr(not(test), deny(clippy::unwrap_used))]
#![warn( #![warn(
clippy::perf, clippy::perf,
clippy::single_match_else, clippy::single_match_else,

1
boa_unicode/src/lib.rs

@ -10,6 +10,7 @@
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
)] )]
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
#![warn( #![warn(
clippy::perf, clippy::perf,
clippy::single_match_else, clippy::single_match_else,

1
boa_wasm/src/lib.rs

@ -2,6 +2,7 @@
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
)] )]
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
#![warn( #![warn(
clippy::perf, clippy::perf,
clippy::single_match_else, clippy::single_match_else,

Loading…
Cancel
Save