From 748465d17b16287901d8540a8ae3fe6a0b69d365 Mon Sep 17 00:00:00 2001 From: Iban Eguia Date: Thu, 17 Feb 2022 17:44:21 +0000 Subject: [PATCH] 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. --- .github/dependabot.yml | 50 +-- boa/src/builtins/array/mod.rs | 25 +- boa/src/builtins/iterable/mod.rs | 4 +- boa/src/builtins/json/mod.rs | 11 +- boa/src/builtins/number/mod.rs | 31 +- boa/src/builtins/object/mod.rs | 6 +- boa/src/builtins/regexp/mod.rs | 34 +- boa/src/builtins/string/mod.rs | 23 +- boa/src/bytecompiler.rs | 24 +- boa/src/lib.rs | 1 + boa/src/object/internal_methods/array.rs | 323 +++++++++--------- boa/src/object/jsobject.rs | 9 +- boa/src/object/mod.rs | 43 ++- boa/src/string.rs | 12 +- boa/src/syntax/lexer/cursor.rs | 5 +- boa/src/syntax/lexer/identifier.rs | 7 +- boa/src/syntax/lexer/template.rs | 25 +- boa/src/syntax/parser/error.rs | 5 +- boa/src/syntax/parser/expression/mod.rs | 4 - .../statement/iteration/for_statement.rs | 10 +- boa/src/syntax/parser/statement/mod.rs | 24 +- boa/src/value/operations.rs | 6 +- boa/src/vm/code_block.rs | 16 +- boa/src/vm/mod.rs | 33 +- boa_cli/src/helper.rs | 2 +- boa_cli/src/main.rs | 16 +- boa_interner/src/lib.rs | 1 + boa_tester/src/main.rs | 5 + boa_unicode/src/lib.rs | 1 + boa_wasm/src/lib.rs | 1 + 30 files changed, 441 insertions(+), 316 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5f6f3a22d43..d5f45527e62 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,30 +1,38 @@ version: 2 updates: - - package-ecosystem: "npm" - directory: "/" + - package-ecosystem: npm + directory: / schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/" + interval: daily + - package-ecosystem: github-actions + directory: / schedule: - interval: "daily" - - package-ecosystem: "cargo" - directory: "/" + interval: daily + - package-ecosystem: cargo + directory: / schedule: - interval: "daily" - - package-ecosystem: "cargo" - directory: "/boa/" + interval: daily + - package-ecosystem: cargo + directory: /boa/ schedule: - interval: "daily" - - package-ecosystem: "cargo" - directory: "/boa_cli/" + interval: daily + - package-ecosystem: cargo + directory: /boa_cli/ schedule: - interval: "daily" - - package-ecosystem: "cargo" - directory: "/boa_wasm/" + interval: daily + - package-ecosystem: cargo + directory: /boa_wasm/ schedule: - interval: "daily" - - package-ecosystem: "cargo" - directory: "/boa_tester/" + interval: daily + - package-ecosystem: cargo + directory: /boa_tester/ schedule: - interval: "daily" + interval: daily + - package-ecosystem: cargo + directory: /boa_interner/ + schedule: + interval: daily + - package-ecosystem: gitsubmodule + directory: /test262/ + schedule: + interval: weekly diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index acec9aeea41..2bd45eefd91 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -143,27 +143,30 @@ impl Array { if number_of_args == 0 { // 4.a. Return ! ArrayCreate(0, proto). Ok(Self::array_create(0, Some(prototype), context) - .unwrap() + .expect("this ArrayCreate call must not fail") .into()) // 5. Else if numberOfArgs = 1, then } else if number_of_args == 1 { // a. Let len be values[0]. let len = &args[0]; // 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 #[allow(clippy::if_not_else)] let int_len = if !len.is_number() { // i. Perform ! CreateDataPropertyOrThrow(array, "0", len). array .create_data_property_or_throw(0, len, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); // ii. Let intLen be 1𝔽. 1 // d. Else, } else { // 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. if !JsValue::same_value_zero(&int_len.into(), len) { return context.throw_range_error("invalid array length"); @@ -171,7 +174,9 @@ impl Array { int_len }; // 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. Ok(array.into()) // 6. Else, @@ -189,7 +194,7 @@ impl Array { // iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK). array .create_data_property_or_throw(i, item, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow must not fail"); // iv. Set k to k + 1. } // e. Assert: The mathematical value of array's "length" property is numberOfArgs. @@ -360,8 +365,8 @@ impl Array { Ok( c.construct(&[JsValue::new(length)], &c.clone().into(), context)? .as_object() - .cloned() - .unwrap(), + .expect("constructing an object should always return an object") + .clone(), ) } else { context.throw_type_error("Symbol.species must be a constructor") @@ -537,7 +542,7 @@ impl Array { if spreadable { // item is guaranteed to be an object since is_concat_spreadable checks it, // 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. // ii. Let len be ? LengthOfArrayLike(E). let len = item.length_of_array_like(context)?; @@ -1639,7 +1644,7 @@ impl Array { // v. If shouldFlatten is true if should_flatten { // 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 let new_depth = if depth == u64::MAX { diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 9add566c6a2..fbf2dc47ba5 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/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). obj.create_data_property_or_throw("value", value, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done). obj.create_data_property_or_throw("done", done, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); // 5. Return obj. obj.into() } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index e1a60adf614..98cc4868210 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -170,7 +170,9 @@ impl Json { // ii. For each String P of keys, do for p in keys { // 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). let new_element = @@ -553,7 +555,12 @@ impl Json { // a. Let K be ? EnumerableOwnPropertyNames(value, key). let keys = value.enumerable_own_property_names(PropertyNameKind::Key, context)?; // 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. diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 54f9f16770f..5bf33f451a4 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -343,7 +343,10 @@ impl Number { fn round_to_precision(digits: &mut String, precision: usize) -> bool { if digits.len() > 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 first > '4' { digit += 1; @@ -563,8 +566,9 @@ impl Number { delta *= f64::from(radix); // Write digit. let digit = fraction as u32; - frac_buf[fraction_cursor] = - std::char::from_digit(digit, u32::from(radix)).unwrap() as u8; + frac_buf[fraction_cursor] = std::char::from_digit(digit, u32::from(radix)) + .expect("radix already checked") + as u8; fraction_cursor += 1; // Calculate remainder. fraction -= f64::from(digit); @@ -582,12 +586,16 @@ impl Number { } else { let c: u8 = frac_buf[fraction_cursor]; // 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) { continue; } 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; } break; @@ -601,16 +609,17 @@ impl Number { } // 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 { integer /= f64::from(radix); - *int_iter.next().unwrap().1 = b'0'; + *int_iter.next().expect("integer buffer exhausted").1 = b'0'; } loop { let remainder = integer % f64::from(radix); - *int_iter.next().unwrap().1 = - std::char::from_digit(remainder as u32, u32::from(radix)).unwrap() as u8; + *int_iter.next().expect("integer buffer exhausted").1 = + 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); if integer <= 0f64 { break; @@ -618,11 +627,11 @@ impl Number { } // Add sign and terminate string. if negative { - *int_iter.next().unwrap().1 = b'-'; + *int_iter.next().expect("integer buffer exhausted").1 = b'-'; } 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; String::from_utf8_lossy(&buffer[integer_cursor..fraction_cursor]).into() } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index fc9049c47c8..b56d52a7323 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -605,7 +605,9 @@ impl Object { // 3.a. If nextSource is neither undefined nor null, then if !source.is_null_or_undefined() { // 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]](). let keys = from.__own_property_keys__(context)?; // 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 // obj and performs the following steps when called: - let mut closure = FunctionBuilder::closure_with_captures( + let closure = FunctionBuilder::closure_with_captures( context, |_, args, obj, context| { let key = args.get_or_undefined(0); diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 4eafeb35208..cfd532b416a 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -202,7 +202,9 @@ impl RegExp { // 6. Else, let (p, f) = if let Some(pattern) = pattern_is_regexp { 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]]. // b. If flags is undefined, let F be pattern.[[OriginalFlags]]. @@ -885,11 +887,11 @@ impl RegExp { // 20. Perform ! CreateDataPropertyOrThrow(A, "index", 𝔽(lastIndex)). 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). 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. let matched_substr = if let Some(s) = input.get(match_value.range()) { @@ -900,7 +902,7 @@ impl RegExp { // 23. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr). 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 // 25. Else, @@ -924,7 +926,7 @@ impl RegExp { groups .to_object(context)? .create_data_property_or_throw(name, value, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); } } groups @@ -935,7 +937,7 @@ impl RegExp { // 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups). 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 for i in 1..=n { @@ -958,7 +960,7 @@ impl RegExp { // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue). a.create_data_property_or_throw(i, captured_value, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); } // 28. Return A. @@ -1019,7 +1021,8 @@ impl RegExp { rx.set("lastIndex", 0, true, context)?; // 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. let mut n = 0; @@ -1037,7 +1040,7 @@ impl RegExp { // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr). 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 if match_str.is_empty() { @@ -1530,11 +1533,12 @@ impl RegExp { )?; let splitter = splitter .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"))?; // 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. let mut length_a = 0; @@ -1567,7 +1571,7 @@ impl RegExp { // c. Perform ! CreateDataPropertyOrThrow(A, "0", S). a.create_data_property_or_throw(0, arg_str, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); // d. Return A. return Ok(a.into()); @@ -1611,7 +1615,7 @@ impl RegExp { // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). 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. length_a += 1; @@ -1642,7 +1646,7 @@ impl RegExp { // b. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), nextCapture). 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. length_a += 1; @@ -1672,7 +1676,7 @@ impl RegExp { // 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). a.create_data_property_or_throw(length_a, arg_str_substring, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); // 22. Return A. Ok(a.into()) diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index c0252ecf01b..437203ac511 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -1678,7 +1678,7 @@ impl String { if separator.is_undefined() { // a. Perform ! CreateDataPropertyOrThrow(A, "0", S). a.create_data_property_or_throw(0, this_str, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); // b. Return A. return Ok(a.into()); @@ -1693,7 +1693,7 @@ impl String { if !separator_str.is_empty() { // i. Perform ! CreateDataPropertyOrThrow(A, "0", S). a.create_data_property_or_throw(0, this_str, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); } // b. Return A. @@ -1732,7 +1732,7 @@ impl String { // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). 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. length_a += 1; @@ -1763,7 +1763,7 @@ impl String { // 16. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). a.create_data_property_or_throw(length_a, this_str_substring, context) - .unwrap(); + .expect("this CreateDataPropertyOrThrow call must not fail"); // 17. Return A. Ok(a.into()) @@ -2013,8 +2013,14 @@ pub(crate) fn get_substitution( // $nn (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. - let tens = second.to_digit(10).unwrap() as usize; - let units = third.to_digit(10).unwrap() as usize; + let tens = second + .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; // 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 (Some(second), _) if second_is_digit => { // 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, no replacement is done. diff --git a/boa/src/bytecompiler.rs b/boa/src/bytecompiler.rs index a3decd3204e..b98b977e862 100644 --- a/boa/src/bytecompiler.rs +++ b/boa/src/bytecompiler.rs @@ -261,7 +261,7 @@ impl<'b> ByteCompiler<'b> { #[inline] 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); @@ -284,7 +284,7 @@ impl<'b> ByteCompiler<'b> { #[inline] 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); @@ -296,7 +296,11 @@ impl<'b> ByteCompiler<'b> { #[inline] fn push_try_control_info(&mut self) { 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 { label: None, @@ -312,7 +316,7 @@ impl<'b> ByteCompiler<'b> { #[inline] fn pop_try_control_info(&mut self, finally_start_address: Option) { 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); @@ -1219,7 +1223,11 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::TryEnd); self.emit(Opcode::FinallySetJump, &[start_address]); 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 { let mut items = self .jump_info @@ -1264,7 +1272,11 @@ impl<'b> ByteCompiler<'b> { } let label = self.jump(); 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 { for info in self.jump_info.iter_mut().rev() { if info.label == node.label() { diff --git a/boa/src/lib.rs b/boa/src/lib.rs index b0612d26955..c0f8dd59afb 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -10,6 +10,7 @@ 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), forbid(clippy::unwrap_used))] #![warn( clippy::perf, clippy::single_match_else, diff --git a/boa/src/object/internal_methods/array.rs b/boa/src/object/internal_methods/array.rs index bc87c6e0133..007af7a0a75 100644 --- a/boa/src/object/internal_methods/array.rs +++ b/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" => { // a. Return ? ArraySetLength(A, Desc). - // Abstract operation `ArraySetLength ( A, Desc )` - // - // 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) + array_set_length(obj, desc, context) } - // 3. Else if P is an array index, then PropertyKey::Index(index) if index < u32::MAX => { // a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). - let old_len_desc = - super::ordinary_get_own_property(obj, &"length".into(), context)?.unwrap(); + let old_len_desc = super::ordinary_get_own_property(obj, &"length".into(), context)? + .expect("the property descriptor must exist"); // b. Assert: ! IsDataDescriptor(oldLenDesc) is true. 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]]. // e. Assert: oldLen is a non-negative integral Number. // 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. if index >= old_len && !old_len_desc.expect_writable() { @@ -228,8 +79,7 @@ pub(crate) fn array_exotic_define_own_property( "length".into(), old_len_desc.into(), context, - ) - .unwrap(); + )?; // iii. Assert: succeeded is true. debug_assert!(succeeded); @@ -246,3 +96,160 @@ pub(crate) fn array_exotic_define_own_property( _ => 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 { + // 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) +} diff --git a/boa/src/object/jsobject.rs b/boa/src/object/jsobject.rs index e88c2d97447..7451617a987 100644 --- a/boa/src/object/jsobject.rs +++ b/boa/src/object/jsobject.rs @@ -211,7 +211,9 @@ impl JsObject { { let object = self.borrow(); if object.is::() { - Some(Ref::map(object, |x| x.downcast_ref::().unwrap())) + Some(Ref::map(object, |x| { + x.downcast_ref::().expect("downcasting reference failed") + })) } else { None } @@ -231,7 +233,10 @@ impl JsObject { { let object = self.borrow_mut(); if object.is::() { - Some(RefMut::map(object, |x| x.downcast_mut::().unwrap())) + Some(RefMut::map(object, |x| { + x.downcast_mut::() + .expect("downcasting mutable reference failed") + })) } else { None } diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 0d9dcb9f332..d05482d22eb 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -1238,7 +1238,7 @@ where #[derive(Debug)] pub struct FunctionBuilder<'context> { context: &'context mut Context, - function: Option, + function: Function, name: JsString, length: usize, } @@ -1249,10 +1249,10 @@ impl<'context> FunctionBuilder<'context> { pub fn native(context: &'context mut Context, function: NativeFunctionSignature) -> Self { Self { context, - function: Some(Function::Native { + function: Function::Native { function, constructor: false, - }), + }, name: JsString::default(), length: 0, } @@ -1266,11 +1266,11 @@ impl<'context> FunctionBuilder<'context> { { Self { context, - function: Some(Function::Closure { + function: Function::Closure { function: Box::new(move |this, args, _, context| function(this, args, context)), constructor: false, captures: Captures::new(()), - }), + }, name: JsString::default(), length: 0, } @@ -1294,7 +1294,7 @@ impl<'context> FunctionBuilder<'context> { { Self { context, - function: Some(Function::Closure { + function: Function::Closure { function: Box::new(move |this, args, captures: Captures, context| { let mut captures = captures.as_mut_any(); let captures = captures.downcast_mut::().ok_or_else(|| { @@ -1304,7 +1304,7 @@ impl<'context> FunctionBuilder<'context> { }), constructor: false, captures: Captures::new(captures), - }), + }, name: JsString::default(), length: 0, } @@ -1314,7 +1314,7 @@ impl<'context> FunctionBuilder<'context> { /// /// The default is `""` (empty string). #[inline] - pub fn name(&mut self, name: N) -> &mut Self + pub fn name(mut self, name: N) -> Self where N: AsRef, { @@ -1328,7 +1328,7 @@ impl<'context> FunctionBuilder<'context> { /// /// The default is `0`. #[inline] - pub fn length(&mut self, length: usize) -> &mut Self { + pub fn length(mut self, length: usize) -> Self { self.length = length; self } @@ -1337,40 +1337,47 @@ impl<'context> FunctionBuilder<'context> { /// /// The default is `false`. #[inline] - pub fn constructor(&mut self, yes: bool) -> &mut Self { - match self.function.as_mut() { - Some(Function::Native { constructor, .. } | Function::Closure { constructor, .. }) => { + pub fn constructor(mut self, yes: bool) -> Self { + match self.function { + Function::Native { + ref mut constructor, + .. + } + | Function::Closure { + ref mut constructor, + .. + } => { *constructor = yes; } - _ => unreachable!(), + Function::VmOrdinary { .. } => unreachable!("function must be native or closure"), } self } /// Build the function object. #[inline] - pub fn build(&mut self) -> JsObject { + pub fn build(self) -> JsObject { let function = JsObject::from_proto_and_data( self.context .standard_objects() .function_object() .prototype(), - ObjectData::function(self.function.take().unwrap()), + ObjectData::function(self.function), ); let property = PropertyDescriptor::builder() .writable(false) .enumerable(false) .configurable(true); 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 } /// 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(); - object.data = ObjectData::function(self.function.take().unwrap()); + object.data = ObjectData::function(self.function); object.set_prototype(self.context.standard_objects().object_object().prototype()); let property = PropertyDescriptor::builder() diff --git a/boa/src/string.rs b/boa/src/string.rs index 7ba12bffb58..bcea59c2dd9 100644 --- a/boa/src/string.rs +++ b/boa/src/string.rs @@ -213,8 +213,8 @@ impl Inner { // of the string array. let inner_layout = Layout::new::(); let (layout, offset) = inner_layout - .extend(Layout::array::(s.len()).unwrap()) - .unwrap(); + .extend(Layout::array::(s.len()).expect("failed to create memory layout")) + .expect("failed to extend memory layout"); let inner = unsafe { let inner = alloc(layout).cast::(); @@ -253,8 +253,8 @@ impl Inner { // of the string array. let inner_layout = Layout::new::(); let (layout, offset) = inner_layout - .extend(Layout::array::(total_string_size).unwrap()) - .unwrap(); + .extend(Layout::array::(total_string_size).expect("failed to create memory layout")) + .expect("failed to extend memory layout"); let inner = unsafe { let inner = alloc(layout).cast::(); @@ -292,8 +292,8 @@ impl Inner { let inner_layout = Layout::new::(); let (layout, _offset) = inner_layout - .extend(Layout::array::(len).unwrap()) - .unwrap(); + .extend(Layout::array::(len).expect("failed to create memory layout")) + .expect("failed to extend memory layout"); dealloc(x.as_ptr().cast::<_>(), layout); } diff --git a/boa/src/syntax/lexer/cursor.rs b/boa/src/syntax/lexer/cursor.rs index a6f0b5629ad..3c3d04d799f 100644 --- a/boa/src/syntax/lexer/cursor.rs +++ b/boa/src/syntax/lexer/cursor.rs @@ -204,7 +204,10 @@ where return Ok(()); } else if let Some(ch) = self.peek_char()? { 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 { // next_is_pred will return false if the next value is None so the None case should already be handled. diff --git a/boa/src/syntax/lexer/identifier.rs b/boa/src/syntax/lexer/identifier.rs index 6ab70dc6350..38a54c26cbb 100644 --- a/boa/src/syntax/lexer/identifier.rs +++ b/boa/src/syntax/lexer/identifier.rs @@ -154,7 +154,10 @@ impl Identifier { if Self::is_identifier_start(ch) { 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 { return Err(Error::Syntax("invalid identifier start".into(), start_pos)); } @@ -185,7 +188,7 @@ impl Identifier { _ => 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)) diff --git a/boa/src/syntax/lexer/template.rs b/boa/src/syntax/lexer/template.rs index e7d689241c7..3613e1a1aea 100644 --- a/boa/src/syntax/lexer/template.rs +++ b/boa/src/syntax/lexer/template.rs @@ -20,7 +20,8 @@ use serde::{Deserialize, Serialize}; pub struct TemplateString { /// The template string of template literal with argument `raw` true. 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, } @@ -40,7 +41,8 @@ impl TemplateString { 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: /// - [ECMAScript reference][spec] @@ -70,8 +72,8 @@ impl TemplateString { } Some(ch) => { // The caller guarantees that sequences '`' and '${' never appear - // LineTerminatorSequence is consumed by `cursor.next_char()` and returns , - // which matches the TV of + // LineTerminatorSequence is consumed by `cursor.next_char()` and + // returns , which matches the TV of buf.push_code_point(ch); } None => break, @@ -119,7 +121,8 @@ impl Tokenizer for TemplateLiteral { })?; match ch { - 0x0060 /* ` */ => { + // ` + 0x0060 => { let raw = buf.to_string_lossy(); let raw_sym = interner.get_or_intern(raw); let template_string = TemplateString::new(raw_sym, start_pos); @@ -129,7 +132,8 @@ impl Tokenizer for TemplateLiteral { 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_sym = interner.get_or_intern(raw); let template_string = TemplateString::new(raw_sym, start_pos); @@ -139,7 +143,8 @@ impl Tokenizer for TemplateLiteral { Span::new(start_pos, cursor.pos()), )); } - 0x005C /* \ */ => { + // \ + 0x005C => { let escape_ch = cursor.peek()?.ok_or_else(|| { Error::from(io::Error::new( ErrorKind::UnexpectedEof, @@ -149,7 +154,11 @@ impl Tokenizer for TemplateLiteral { buf.push(u16::from(b'\\')); 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, } } diff --git a/boa/src/syntax/parser/error.rs b/boa/src/syntax/parser/error.rs index a40737322dd..8472326863a 100644 --- a/boa/src/syntax/parser/error.rs +++ b/boa/src/syntax/parser/error.rs @@ -134,7 +134,10 @@ impl fmt::Display for ParseError { f, "expected {}, got '{found}' in {context} at line {}, col {}", if expected.len() == 1 { - format!("token '{}'", expected.first().unwrap()) + format!( + "token '{}'", + expected.first().expect("already checked that length is 1") + ) } else { format!( "one of {}", diff --git a/boa/src/syntax/parser/expression/mod.rs b/boa/src/syntax/parser/expression/mod.rs index f3e7f9cec67..f806292a762 100644 --- a/boa/src/syntax/parser/expression/mod.rs +++ b/boa/src/syntax/parser/expression/mod.rs @@ -489,10 +489,6 @@ where fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = BoaProfiler::global().start_event("Relation Expression", "Parsing"); - if None::.is_some() { - cursor.set_goal(None::.unwrap()); - } - let mut lhs = ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; while let Some(tok) = cursor.peek(0, interner)? { diff --git a/boa/src/syntax/parser/statement/iteration/for_statement.rs b/boa/src/syntax/parser/statement/iteration/for_statement.rs index 46e07b3bdc8..8f90dc817e0 100644 --- a/boa/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa/src/syntax/parser/statement/iteration/for_statement.rs @@ -102,9 +102,9 @@ where ), }; - match cursor.peek(0, interner)? { - Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::In) && init.is_some() => { - let init = node_to_iterable_loop_initializer(&init.unwrap(), init_position)?; + match (init.as_ref(), cursor.peek(0, interner)?) { + (Some(init), Some(tok)) if tok.kind() == &TokenKind::Keyword(Keyword::In) => { + let init = node_to_iterable_loop_initializer(init, init_position)?; let _next = cursor.next(interner)?; 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()); } - Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::Of) && init.is_some() => { - let init = node_to_iterable_loop_initializer(&init.unwrap(), init_position)?; + (Some(init), Some(tok)) if tok.kind() == &TokenKind::Keyword(Keyword::Of) => { + let init = node_to_iterable_loop_initializer(init, init_position)?; let _next = cursor.next(interner)?; let iterable = Expression::new(None, true, self.allow_yield, self.allow_await) diff --git a/boa/src/syntax/parser/statement/mod.rs b/boa/src/syntax/parser/statement/mod.rs index 71b56543124..184c23046c6 100644 --- a/boa/src/syntax/parser/statement/mod.rs +++ b/boa/src/syntax/parser/statement/mod.rs @@ -202,19 +202,17 @@ where // Labelled Statement check cursor.set_goal(InputElement::Div); let tok = cursor.peek(1, interner)?; - if tok.is_some() - && matches!( - tok.unwrap().kind(), - TokenKind::Punctuator(Punctuator::Colon) - ) - { - return LabelledStatement::new( - self.allow_yield, - self.allow_await, - self.allow_return, - ) - .parse(cursor, interner) - .map(Node::from); + + if let Some(tok) = tok { + if matches!(tok.kind(), TokenKind::Punctuator(Punctuator::Colon)) { + return LabelledStatement::new( + self.allow_yield, + self.allow_await, + self.allow_return, + ) + .parse(cursor, interner) + .map(Node::from); + } } ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor, interner) diff --git a/boa/src/value/operations.rs b/boa/src/value/operations.rs index 5597c1686f2..19899e69508 100644 --- a/boa/src/value/operations.rs +++ b/boa/src/value/operations.rs @@ -541,7 +541,8 @@ impl JsValue { } else { 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)) => { if x.is_nan() { @@ -555,7 +556,8 @@ impl JsValue { } else { x.ceil() }; - (JsBigInt::try_from(n).unwrap() < *y).into() + (JsBigInt::try_from(n).expect("invalid conversion to BigInt") < *y) + .into() } }, } diff --git a/boa/src/vm/code_block.rs b/boa/src/vm/code_block.rs index 2692cf1a680..849151d203f 100644 --- a/boa/src/vm/code_block.rs +++ b/boa/src/vm/code_block.rs @@ -136,7 +136,7 @@ impl CodeBlock { /// /// Returns an empty `String` if no operands are present. 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::(); match opcode { Opcode::PushInt8 => { @@ -313,7 +313,7 @@ impl ToInternedString for CodeBlock { let mut pc = 0; let mut count = 0; 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); f.push_str(&format!( " {pc:06} {count:04} {:<27}\n{operands}", @@ -406,7 +406,7 @@ impl JsVmFunction { prototype .define_property_or_throw("constructor", constructor_property, context) - .unwrap(); + .expect("failed to define the constructor property of the function"); let prototype_property = PropertyDescriptor::builder() .value(prototype) @@ -417,13 +417,13 @@ impl JsVmFunction { constructor .define_property_or_throw("prototype", prototype_property, context) - .unwrap(); + .expect("failed to define the prototype property of the function"); constructor .define_property_or_throw("name", name_property, context) - .unwrap(); + .expect("failed to define the name property of the function"); constructor .define_property_or_throw("length", length_property, context) - .unwrap(); + .expect("failed to define the length property of the function"); constructor } @@ -462,7 +462,7 @@ impl JsObject { let body = { let object = self.borrow(); - let function = object.as_function().unwrap(); + let function = object.as_function().expect("not a function"); match function { Function::Native { @@ -633,7 +633,7 @@ impl JsObject { let body = { let object = self.borrow(); - let function = object.as_function().unwrap(); + let function = object.as_function().expect("not a function"); match function { Function::Native { function, .. } => FunctionBody::Native { diff --git a/boa/src/vm/mod.rs b/boa/src/vm/mod.rs index 0e621c219bf..35a7ce33457 100644 --- a/boa/src/vm/mod.rs +++ b/boa/src/vm/mod.rs @@ -55,7 +55,7 @@ impl Vm { #[inline] #[track_caller] pub(crate) fn pop(&mut self) -> JsValue { - self.stack.pop().unwrap() + self.stack.pop().expect("stack was empty") } #[track_caller] @@ -66,14 +66,24 @@ impl Vm { value } + /// Retrieves the VM frame + /// + /// # Panics + /// + /// If there is no frame, then this will panic. #[inline] 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] pub(crate) fn frame_mut(&mut self) -> &mut CallFrame { - self.frame.as_mut().unwrap() + self.frame.as_mut().expect("no frame found") } #[inline] @@ -661,10 +671,10 @@ impl Context { let excluded_key_count = self.vm.read::(); let mut excluded_keys = Vec::with_capacity(excluded_key_count as usize); 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 object = value.as_object().unwrap(); + let object = value.as_object().expect("not an object"); let source = self.vm.pop(); object.copy_data_properties(&source, excluded_keys, self)?; self.vm.push(value); @@ -989,7 +999,7 @@ impl Context { let done = self.vm.pop(); let next_function = 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); iterator_record.close(Ok(JsValue::Null), self)?; } @@ -1075,7 +1085,8 @@ impl Context { args.push(self.vm.pop()); } 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); } else { self.vm.pop(); @@ -1139,7 +1150,13 @@ impl Context { while self.vm.frame().pc < self.vm.frame().code.code.len() { let result = if self.vm.trace { let mut pc = self.vm.frame().pc; - let opcode: Opcode = self.vm.frame().code.read::(pc).try_into().unwrap(); + let opcode: Opcode = self + .vm + .frame() + .code + .read::(pc) + .try_into() + .expect("invalid opcode"); let operands = self.vm.frame().code.instruction_operands(&mut pc); let instant = Instant::now(); diff --git a/boa_cli/src/helper.rs b/boa_cli/src/helper.rs index 0db7d70dfcd..cd4f723e0c4 100644 --- a/boa_cli/src/helper.rs +++ b/boa_cli/src/helper.rs @@ -137,7 +137,7 @@ impl Highlighter for LineHighlighter { (?P[+\-/*%~^!&|=<>;:]) | (?P0[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 .replace_all(&coloured, |caps: &Captures<'_>| { diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index f5fb981bfd1..3524647766b 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -2,6 +2,7 @@ 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( clippy::perf, clippy::single_match_else, @@ -168,9 +169,16 @@ where match arg { Some(format) => match format { 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 => { - 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. @@ -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(()) diff --git a/boa_interner/src/lib.rs b/boa_interner/src/lib.rs index b2879bbbaf2..09af26fe3a5 100644 --- a/boa_interner/src/lib.rs +++ b/boa_interner/src/lib.rs @@ -12,6 +12,7 @@ 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), forbid(clippy::unwrap_used))] #![warn( clippy::perf, clippy::single_match_else, diff --git a/boa_tester/src/main.rs b/boa_tester/src/main.rs index b0bd6d78dca..1546b2f7585 100644 --- a/boa_tester/src/main.rs +++ b/boa_tester/src/main.rs @@ -2,6 +2,11 @@ //! //! This crate will run the full ECMAScript test suite (Test262) and report compliance of the //! `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( clippy::perf, clippy::single_match_else, diff --git a/boa_unicode/src/lib.rs b/boa_unicode/src/lib.rs index 01129b64448..997acd6110e 100644 --- a/boa_unicode/src/lib.rs +++ b/boa_unicode/src/lib.rs @@ -10,6 +10,7 @@ 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), forbid(clippy::unwrap_used))] #![warn( clippy::perf, clippy::single_match_else, diff --git a/boa_wasm/src/lib.rs b/boa_wasm/src/lib.rs index 81b53098778..7ec244637f3 100644 --- a/boa_wasm/src/lib.rs +++ b/boa_wasm/src/lib.rs @@ -2,6 +2,7 @@ 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), forbid(clippy::unwrap_used))] #![warn( clippy::perf, clippy::single_match_else,