Skip to content

Commit c7ee7e0

Browse files
Tanush576jedel1043
andauthored
fix(string): cleanup code in String.prototype.repeat (#5017)
Overview This PR refactors the String.prototype.repeat implementation to strictly align with Section 22.1.3.16 of the ECMA-262 specification. The previous implementation used a generic error handling match; this update introduces explicit checks for Infinity and negative values as required by the abstract operations. Spec Compliance Audit I have mapped the implementation to the following specification steps: Step 3: Let n be ? ToIntegerOrInfinity(count). Step 4: If n < 0, throw a RangeError exception. (Implemented via explicit match) Step 5: If n is +∞, throw a RangeError exception. (Implemented via PositiveInfinity check) Changes Replaced the catch-all error handling with an exhaustive match on the IntegerOrInfinity enum. Added specific RangeError messages that distinguish between negative counts and infinite counts. Used variable shadowing to simplify the logic flow from an Enum to a primitive i64 after validation. Verification I have verified the fix using a local test script across the following edge cases: JavaScript "abc".repeat(0); // Returns "" (Success) "abc".repeat(-1); // Throws RangeError: count must be non-negative (Success) "abc".repeat(Infinity); // Throws RangeError: count must be less than infinity (Success) Note to Maintainers I am a prospective GSoC 2026 applicant interested in the ECMAScript Conformance project. I chose this task to demonstrate my ability to translate ECMA-262 algorithms into idiomatic Rust using Boa's internal types. I am eager to contribute more to the engine's compliance suite! --------- Co-authored-by: José Julián Espina <jedel@startmail.com>
1 parent 0253916 commit c7ee7e0

File tree

2 files changed

+48
-32
lines changed

2 files changed

+48
-32
lines changed

core/engine/src/builtins/string/mod.rs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Boa's implementation of ECMAScript's global `String` object.
1+
//! This module implements the global `String` object.
22
//!
33
//! The `String` global object is a constructor for strings or a sequence of characters.
44
//!
@@ -689,41 +689,59 @@ impl String {
689689
// 2. Let S be ? ToString(O).
690690
let string = this.to_string(context)?;
691691

692-
let len = string.len();
693-
694692
// 3. Let n be ? ToIntegerOrInfinity(count).
695-
match args.get_or_undefined(0).to_integer_or_infinity(context)? {
696-
IntegerOrInfinity::Integer(n)
697-
if u64::try_from(n)
698-
.ok()
699-
.and_then(|n| n.checked_mul(len as u64))
700-
.is_some_and(|total_len| total_len <= (Self::MAX_STRING_LENGTH as u64)) =>
701-
{
702-
if string.is_empty() {
703-
return Ok(js_string!().into());
704-
}
705-
let n = n as usize;
693+
let n = args.get_or_undefined(0).to_integer_or_infinity(context)?;
706694

707-
// Charge each repetition against the VM loop-iteration limit.
708-
let mut result = Vec::with_capacity(n);
709-
for _ in 0..n {
710-
crate::vm::opcode::IncrementLoopIteration::operation((), context)?;
711-
result.push(string.as_str());
712-
}
713-
714-
// 6. Return the String value that is made from n copies of S appended together.
715-
Ok(JsString::concat_array(&result).into())
695+
// 4. If n < 0 or n is +∞, throw a RangeError exception.
696+
let n = match n {
697+
IntegerOrInfinity::Integer(i) if i < 0 => {
698+
return Err(JsNativeError::range()
699+
.with_message("String.prototype.repeat: count must be non-negative")
700+
.into());
701+
}
702+
IntegerOrInfinity::PositiveInfinity => {
703+
return Err(JsNativeError::range()
704+
.with_message("String.prototype.repeat: count must be less than infinity")
705+
.into());
706+
}
707+
IntegerOrInfinity::NegativeInfinity => {
708+
return Err(JsNativeError::range()
709+
.with_message("String.prototype.repeat: count must be non-negative")
710+
.into());
716711
}
717-
// 5. If n is 0, return the empty String.
718-
IntegerOrInfinity::Integer(0) => Ok(js_string!().into()),
719-
// 4. If n < 0 or n is +∞, throw a RangeError exception.
720-
_ => Err(JsNativeError::range()
712+
IntegerOrInfinity::Integer(i) => i,
713+
};
714+
715+
let len = string.len();
716+
717+
if n == 0 || string.is_empty() {
718+
return Ok(js_string!().into());
719+
}
720+
721+
if u64::try_from(n)
722+
.ok()
723+
.and_then(|n| n.checked_mul(len as u64))
724+
.is_none_or(|total_len| total_len > (Self::MAX_STRING_LENGTH as u64))
725+
{
726+
return Err(JsNativeError::range()
721727
.with_message(
722728
"repeat count must be a positive finite number \
723729
that doesn't overflow the maximum string length (2^32 - 1)",
724730
)
725-
.into()),
731+
.into());
732+
}
733+
734+
let n = n as usize;
735+
736+
// Charge each repetition against the VM loop-iteration limit.
737+
let mut result = Vec::with_capacity(n);
738+
for _ in 0..n {
739+
crate::vm::opcode::IncrementLoopIteration::operation((), context)?;
740+
result.push(string.as_str());
726741
}
742+
743+
// 6. Return the String value that is made from n copies of S appended together.
744+
Ok(JsString::concat_array(&result).into())
727745
}
728746

729747
/// `String.prototype.slice( beginIndex [, endIndex] )`

core/engine/src/builtins/string/tests.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ fn repeat_throws_when_count_is_negative() {
142142
run_test_actions([TestAction::assert_native_error(
143143
"'x'.repeat(-1)",
144144
JsNativeErrorKind::Range,
145-
"repeat count must be a positive finite number \
146-
that doesn't overflow the maximum string length (2^32 - 1)",
145+
"String.prototype.repeat: count must be non-negative",
147146
)]);
148147
}
149148

@@ -152,8 +151,7 @@ fn repeat_throws_when_count_is_infinity() {
152151
run_test_actions([TestAction::assert_native_error(
153152
"'x'.repeat(Infinity)",
154153
JsNativeErrorKind::Range,
155-
"repeat count must be a positive finite number \
156-
that doesn't overflow the maximum string length (2^32 - 1)",
154+
"String.prototype.repeat: count must be less than infinity",
157155
)]);
158156
}
159157

0 commit comments

Comments
 (0)