Browse Source

Made `String.prototype.indexOf` spec compliant. (#597)

- Added tests
pull/599/head
HalidOdat 4 years ago committed by GitHub
parent
commit
356ce1a827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 42
      boa/src/builtins/string/mod.rs
  2. 82
      boa/src/builtins/string/tests.rs

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

@ -543,39 +543,25 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
pub(crate) fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. let this = ctx.require_object_coercible(this)?;
// Then we convert it into a Rust String by wrapping it in from_value let string = ctx.to_string(this)?;
let primitive_val = ctx.to_string(this)?;
// TODO: Should throw TypeError if search_string is regular expression
let search_string = ctx.to_string(
args.get(0)
.expect("failed to get argument for String method"),
)?;
let length = primitive_val.chars().count() as i32; let search_string =
ctx.to_string(&args.get(0).cloned().unwrap_or_else(Value::undefined))?;
// If less than 2 args specified, position is 'undefined', defaults to 0 let length = string.chars().count();
let position = if args.len() < 2 { let start = args
0 .get(1)
} else { .map(|position| ctx.to_integer(position))
i32::from(args.get(1).expect("Could not get argument")) .transpose()?
}; .map_or(0, |position| position.max(0.0).min(length as f64) as usize);
let start = min(max(position, 0), length);
// Here cannot use the &str method "find", because this returns the byte if start < length {
// index: we need to return the char index in the JS String if let Some(position) = string.find(search_string.as_str()) {
// Instead, iterate over the part we're checking until the slice we're return Ok(string[..position].chars().count().into());
// checking "starts with" the search string
for index in start..length {
let this_string: StdString = primitive_val.chars().skip(index as usize).collect();
if this_string.starts_with(search_string.as_str()) {
// Explicitly return early with the index value
return Ok(Value::from(index));
} }
} }
// Didn't find a match, so return -1
Ok(Value::from(-1)) Ok(Value::from(-1))
} }

82
boa/src/builtins/string/tests.rs

@ -415,3 +415,85 @@ fn trim_end() {
assert_eq!(forward(&mut engine, "'Hello \n'.trimEnd()"), "\"Hello\""); assert_eq!(forward(&mut engine, "'Hello \n'.trimEnd()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' Hello '.trimEnd()"), "\" Hello\""); assert_eq!(forward(&mut engine, "' Hello '.trimEnd()"), "\" Hello\"");
} }
#[test]
fn index_of_with_no_arguments() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf()"), "-1");
assert_eq!(forward(&mut engine, "'undefined'.indexOf()"), "0");
assert_eq!(forward(&mut engine, "'a1undefined'.indexOf()"), "2");
assert_eq!(forward(&mut engine, "'a1undefined1a'.indexOf()"), "2");
assert_eq!(forward(&mut engine, "'µµµundefined'.indexOf()"), "3");
assert_eq!(forward(&mut engine, "'µµµundefinedµµµ'.indexOf()"), "3");
}
#[test]
fn index_of_with_string_search_string_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf('hello')"), "-1");
assert_eq!(
forward(&mut engine, "'undefined'.indexOf('undefined')"),
"0"
);
assert_eq!(
forward(&mut engine, "'a1undefined'.indexOf('undefined')"),
"2"
);
assert_eq!(
forward(&mut engine, "'a1undefined1a'.indexOf('undefined')"),
"2"
);
assert_eq!(
forward(&mut engine, "'µµµundefined'.indexOf('undefined')"),
"3"
);
assert_eq!(
forward(&mut engine, "'µµµundefinedµµµ'.indexOf('undefined')"),
"3"
);
}
#[test]
fn index_of_with_non_string_search_string_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf(1)"), "-1");
assert_eq!(forward(&mut engine, "'1'.indexOf(1)"), "0");
assert_eq!(forward(&mut engine, "'true'.indexOf(true)"), "0");
assert_eq!(forward(&mut engine, "'ab100ba'.indexOf(100)"), "2");
assert_eq!(forward(&mut engine, "'µµµfalse'.indexOf(true)"), "-1");
assert_eq!(forward(&mut engine, "'µµµ5µµµ'.indexOf(5)"), "3");
}
#[test]
fn index_of_with_from_index_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'x'.indexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'abcx'.indexOf('x', 2)"), "3");
assert_eq!(forward(&mut engine, "'x'.indexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'µµµxµµµ'.indexOf('x', 2)"), "3");
assert_eq!(
forward(&mut engine, "'µµµxµµµ'.indexOf('x', 10000000)"),
"-1"
);
}
#[test]
fn generic_index_of() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
forward_val(
&mut engine,
"Number.prototype.indexOf = String.prototype.indexOf",
)
.unwrap();
assert_eq!(forward(&mut engine, "(10).indexOf(9)"), "-1");
assert_eq!(forward(&mut engine, "(10).indexOf(0)"), "1");
assert_eq!(forward(&mut engine, "(10).indexOf('0')"), "1");
}

Loading…
Cancel
Save