Browse Source

Fix `String.prototype.substr()` (#2805)

Fixes the remaining failing tests on `String.prototype.substr()`

It changes the following:

- Fixes the remaining failing tests on `String.prototype.substr()
- Move `substr` under the `"annex-b"` feature flag.
pull/2806/head
Haled Odat 2 years ago
parent
commit
333b94a9ec
  1. 133
      boa_engine/src/builtins/string/mod.rs

133
boa_engine/src/builtins/string/mod.rs

@ -99,7 +99,6 @@ impl IntrinsicObject for String {
.method(Self::to_lowercase, "toLowerCase", 0) .method(Self::to_lowercase, "toLowerCase", 0)
.method(Self::to_uppercase, "toUpperCase", 0) .method(Self::to_uppercase, "toUpperCase", 0)
.method(Self::substring, "substring", 2) .method(Self::substring, "substring", 2)
.method(Self::substr, "substr", 2)
.method(Self::split, "split", 2) .method(Self::split, "split", 2)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, "valueOf", 0)
.method(Self::match_all, "matchAll", 1) .method(Self::match_all, "matchAll", 1)
@ -112,6 +111,7 @@ impl IntrinsicObject for String {
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
{ {
builder builder
.method(Self::substr, "substr", 2)
.method(Self::anchor, "anchor", 1) .method(Self::anchor, "anchor", 1)
.method(Self::big, "big", 0) .method(Self::big, "big", 0)
.method(Self::blink, "blink", 0) .method(Self::blink, "blink", 0)
@ -1776,69 +1776,6 @@ impl String {
Ok(js_string!(&string[from..to]).into()) Ok(js_string!(&string[from..to]).into())
} }
/// `String.prototype.substr( start[, length] )`
///
/// The `substr()` method returns a portion of the string, starting at the specified index and extending for a given number of characters afterward.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
/// <https://tc39.es/ecma262/#sec-string.prototype.substr>
pub(crate) fn substr(
this: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
// 1. Let O be ? RequireObjectCoercible(this value).
let this = this.require_object_coercible()?;
// 2. Let S be ? ToString(O).
let string = this.to_string(context)?;
// 3. Let size be the length of S.
let size = string.len() as i64;
// 4. Let intStart be ? ToIntegerOrInfinity(start).
let int_start = args.get_or_undefined(0).to_integer_or_infinity(context)?;
// 7. If length is undefined, let intLength be size; otherwise let intLength be ? ToIntegerOrInfinity(length).
// Moved it before to ensure an error throws before returning the empty string on `match int_start`
let int_length = match args.get_or_undefined(1) {
&JsValue::Undefined => IntegerOrInfinity::Integer(size),
val => val.to_integer_or_infinity(context)?,
};
let int_start = match int_start {
// 6. Else if intStart < 0, set intStart to max(size + intStart, 0).
IntegerOrInfinity::Integer(i) if i < 0 => max(size + i, 0),
IntegerOrInfinity::Integer(i) => i,
// 8. If intStart is +∞, ... return the empty String
IntegerOrInfinity::PositiveInfinity => return Ok(js_string!().into()),
// 5. If intStart is -∞, set intStart to 0.
IntegerOrInfinity::NegativeInfinity => 0,
} as usize;
// 8. If ... intLength ≤ 0, or intLength is +∞, return the empty String.
let int_length = match int_length {
IntegerOrInfinity::Integer(i) if i > 0 => i,
_ => return Ok(js_string!().into()),
} as usize;
// 9. Let intEnd be min(intStart + intLength, size).
let int_end = min(int_start + int_length, size as usize);
if let Some(substr) = string.get(int_start..int_end) {
// 11. Return the substring of S from intStart to intEnd.
Ok(js_string!(substr).into())
} else {
// 10. If intStart ≥ intEnd, return the empty String.
Ok(js_string!().into())
}
}
/// `String.prototype.split ( separator, limit )` /// `String.prototype.split ( separator, limit )`
/// ///
/// The split() method divides a String into an ordered list of substrings, puts these substrings into an array, and returns the array. /// The split() method divides a String into an ordered list of substrings, puts these substrings into an array, and returns the array.
@ -2198,6 +2135,74 @@ impl String {
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
impl String { impl String {
/// `String.prototype.substr( start[, length] )`
///
/// The `substr()` method returns a portion of the string, starting at the specified index and extending for a given number of characters afterward.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
/// <https://tc39.es/ecma262/#sec-string.prototype.substr>
pub(crate) fn substr(
this: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
// 1. Let O be ? RequireObjectCoercible(this value).
let o = this.require_object_coercible()?;
// 2. Let S be ? ToString(O).
let s = o.to_string(context)?;
// 3. Let size be the length of S.
let size = s.len() as i64;
// 4. Let intStart be ? ToIntegerOrInfinity(start).
let start = args.get_or_undefined(0);
let int_start = start.to_integer_or_infinity(context)?;
let int_start = match int_start {
// 5. If intStart is -∞, set intStart to 0.
IntegerOrInfinity::NegativeInfinity => 0,
// 6. Else if intStart < 0, set intStart to max(size + intStart, 0).
IntegerOrInfinity::Integer(int_start) if int_start < 0 => max(size + int_start, 0),
IntegerOrInfinity::Integer(int_start) => int_start,
// 7. Else, set intStart to min(intStart, size).
//
// NOTE: size will always we smaller than +∞
IntegerOrInfinity::PositiveInfinity => size,
} as usize;
// 8. If length is undefined, let intLength be size;
// otherwise let intLength be ? ToIntegerOrInfinity(length).
let length = args.get_or_undefined(1);
let int_length = if length.is_undefined() {
IntegerOrInfinity::Integer(size)
} else {
length.to_integer_or_infinity(context)?
};
// 9. Set intLength to the result of clamping intLength between 0 and size.
let int_length = match int_length {
IntegerOrInfinity::NegativeInfinity => 0,
IntegerOrInfinity::PositiveInfinity => size,
IntegerOrInfinity::Integer(i) => i.clamp(0, size),
} as usize;
// 10. Let intEnd be min(intStart + intLength, size).
let int_end = min(int_start + int_length, size as usize);
// 11. Return the substring of S from intStart to intEnd.
if let Some(substr) = s.get(int_start..int_end) {
Ok(js_string!(substr).into())
} else {
Ok(js_string!().into())
}
}
/// `CreateHTML(string, tag, attribute, value)` /// `CreateHTML(string, tag, attribute, value)`
/// ///
/// More information: /// More information:

Loading…
Cancel
Save