Browse Source

Fix `String.prototype.replace()` order of `ToString` execution (#2799)

Fix order of `ToString` execution, there are 3 failing tests on `.replace()` but they seem to be regex related
pull/2797/head
Haled Odat 2 years ago
parent
commit
7a4d6526b0
  1. 58
      boa_engine/src/builtins/string/mod.rs

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

@ -930,11 +930,16 @@ impl String {
args: &[JsValue], args: &[JsValue],
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// Helper enum.
enum CallableOrString<'a> {
FunctionalReplace(&'a JsObject),
ReplaceValue(JsString),
}
// 1. Let O be ? RequireObjectCoercible(this value). // 1. Let O be ? RequireObjectCoercible(this value).
this.require_object_coercible()?; let o = this.require_object_coercible()?;
let search_value = args.get_or_undefined(0); let search_value = args.get_or_undefined(0);
let replace_value = args.get_or_undefined(1); let replace_value = args.get_or_undefined(1);
// 2. If searchValue is neither undefined nor null, then // 2. If searchValue is neither undefined nor null, then
@ -945,73 +950,72 @@ impl String {
// b. If replacer is not undefined, then // b. If replacer is not undefined, then
if let Some(replacer) = replacer { if let Some(replacer) = replacer {
// i. Return ? Call(replacer, searchValue, « O, replaceValue »). // i. Return ? Call(replacer, searchValue, « O, replaceValue »).
return replacer.call( return replacer.call(search_value, &[o.clone(), replace_value.clone()], context);
search_value,
&[this.clone(), replace_value.clone()],
context,
);
} }
} }
// 3. Let string be ? ToString(O). // 3. Let string be ? ToString(O).
let this_str = this.to_string(context)?; let string = o.to_string(context)?;
// 4. Let searchString be ? ToString(searchValue). // 4. Let searchString be ? ToString(searchValue).
let search_str = search_value.to_string(context)?; let search_string = search_value.to_string(context)?;
// 5. Let functionalReplace be IsCallable(replaceValue). // 5. Let functionalReplace be IsCallable(replaceValue).
let replace_obj = replace_value.as_callable(); let functional_replace = replace_value.as_callable();
// 6. If functionalReplace is false, then // 6. If functionalReplace is false, then
let replace_value = if let Some(callable) = functional_replace {
CallableOrString::FunctionalReplace(callable)
} else {
// a. Set replaceValue to ? ToString(replaceValue). // a. Set replaceValue to ? ToString(replaceValue).
CallableOrString::ReplaceValue(replace_value.to_string(context)?)
};
// 7. Let searchLength be the length of searchString. // 7. Let searchLength be the length of searchString.
let search_length = search_str.len(); let search_length = search_string.len();
// 8. Let position be ! StringIndexOf(string, searchString, 0). // 8. Let position be ! StringIndexOf(string, searchString, 0).
// 9. If position is -1, return string. // 9. If position is -1, return string.
let Some(position) = this_str.index_of(&search_str, 0) else { let Some(position) = string.index_of(&search_string, 0) else {
return Ok(this_str.into()); return Ok(string.into());
}; };
// 10. Let preserved be the substring of string from 0 to position. // 10. Let preserved be the substring of string from 0 to position.
let preserved = &this_str[..position]; let preserved = &string[..position];
let replacement = match replace_value {
// 11. If functionalReplace is true, then // 11. If functionalReplace is true, then
// 12. Else, CallableOrString::FunctionalReplace(replace_fn) => {
let replacement = if let Some(replace_fn) = replace_obj {
// a. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)). // a. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)).
replace_fn replace_fn
.call( .call(
&JsValue::undefined(), &JsValue::undefined(),
&[search_str.into(), position.into(), this_str.clone().into()], &[search_string.into(), position.into(), string.clone().into()],
context, context,
)? )?
.to_string(context)? .to_string(context)?
} else { }
// 12. Else,
CallableOrString::ReplaceValue(replace_value) => {
// a. Assert: Type(replaceValue) is String. // a. Assert: Type(replaceValue) is String.
// b. Let captures be a new empty List. // b. Let captures be a new empty List.
let captures = Vec::new(); let captures = Vec::new();
// c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue). // c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue).
get_substitution( get_substitution(
&search_str, &search_string,
&this_str, &string,
position, position,
&captures, &captures,
&JsValue::undefined(), &JsValue::undefined(),
&replace_value.to_string(context)?, &replace_value,
context, context,
)? )?
}
}; };
// 13. Return the string-concatenation of preserved, replacement, and the substring of string from position + searchLength. // 13. Return the string-concatenation of preserved, replacement, and the substring of string from position + searchLength.
Ok(js_string!( Ok(js_string!(preserved, &replacement, &string[position + search_length..]).into())
preserved,
&replacement,
&this_str[position + search_length..]
)
.into())
} }
/// `22.1.3.18 String.prototype.replaceAll ( searchValue, replaceValue )` /// `22.1.3.18 String.prototype.replaceAll ( searchValue, replaceValue )`

Loading…
Cancel
Save