Skip to content

Commit

Permalink
Fix RegExp.prototype.search to accept all objects
Browse files Browse the repository at this point in the history
* Add some more rust tests from the 262 suite
  • Loading branch information
raskad committed Jun 14, 2021
1 parent 60a5a53 commit 9ac467a
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 40 deletions.
75 changes: 35 additions & 40 deletions boa/src/builtins/regexp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,53 +734,48 @@ impl RegExp {
pub(crate) fn search(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// 1. Let rx be the this value.
// 2. If Type(rx) is not Object, throw a TypeError exception.
if let Some(object) = this.as_object() {
if object.is_regexp() {
// 3. Let S be ? ToString(string).
let arg_str = args
.get(0)
.cloned()
.unwrap_or_default()
.to_string(context)?;
if !this.is_object() {
return context.throw_type_error(
"RegExp.prototype[Symbol.search] method called on incompatible value",
);
}

// 4. Let previousLastIndex be ? Get(rx, "lastIndex").
let previous_last_index =
this.get_field("lastIndex", context)?.to_length(context)?;
// 3. Let S be ? ToString(string).
let arg_str = args
.get(0)
.cloned()
.unwrap_or_default()
.to_string(context)?;

// 5. If SameValue(previousLastIndex, +0𝔽) is false, then
if previous_last_index != 0 {
// a. Perform ? Set(rx, "lastIndex", +0𝔽, true).
this.set_field("lastIndex", 0, context)?;
}
// 4. Let previousLastIndex be ? Get(rx, "lastIndex").
let previous_last_index = this.get_field("lastIndex", context)?.to_length(context)?;

// 6. Let result be ? RegExpExec(rx, S).
let result = Self::exec(this, &[Value::from(arg_str)], context)?;
// 5. If SameValue(previousLastIndex, +0𝔽) is false, then
if previous_last_index != 0 {
// a. Perform ? Set(rx, "lastIndex", +0𝔽, true).
this.set_field("lastIndex", 0, context)?;
}

// 7. Let currentLastIndex be ? Get(rx, "lastIndex").
let current_last_index =
this.get_field("lastIndex", context)?.to_length(context)?;
// 6. Let result be ? RegExpExec(rx, S).
let result = Self::exec(this, &[Value::from(arg_str)], context)?;

// 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
if current_last_index != previous_last_index {
// a. Perform ? Set(rx, "lastIndex", previousLastIndex, true).
this.set_field("lastIndex", previous_last_index, context)?;
}
// 7. Let currentLastIndex be ? Get(rx, "lastIndex").
let current_last_index = this.get_field("lastIndex", context)?.to_length(context)?;

// 9. If result is null, return -1𝔽.
// 10. Return ? Get(result, "index").
if result.is_null() {
Ok(Value::from(-1))
} else {
result.get_field("index", context).map_err(|_| {
context.construct_type_error("Could not find property `index`")
})
}
} else {
context
.throw_type_error("RegExp.prototype.search method called on incompatible value")
}
// 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
if current_last_index != previous_last_index {
// a. Perform ? Set(rx, "lastIndex", previousLastIndex, true).
this.set_field("lastIndex", previous_last_index, context)?;
}

// 9. If result is null, return -1𝔽.
// 10. Return ? Get(result, "index").
if result.is_null() {
Ok(Value::from(-1))
} else {
context.throw_type_error("RegExp.prototype.search method called on incompatible value")
result
.get_field("index", context)
.map_err(|_| context.construct_type_error("Could not find property `index`"))
}
}
}
75 changes: 75 additions & 0 deletions boa/src/builtins/regexp/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,81 @@ fn no_panic_on_invalid_character_escape() {
fn search() {
let mut context = Context::new();

// coerce-string
assert_eq!(
forward(
&mut context,
r#"
var obj = {
toString: function() {
return 'toString value';
}
};
/ring/[Symbol.search](obj)
"#
),
"4"
);

// failure-return-val
assert_eq!(forward(&mut context, "/z/[Symbol.search]('a')"), "-1");

// length
assert_eq!(
forward(&mut context, "RegExp.prototype[Symbol.search].length"),
"1"
);

let init =
"var obj = Object.getOwnPropertyDescriptor(RegExp.prototype[Symbol.search], \"length\")";
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut context, "obj.enumerable"), "false");
assert_eq!(forward(&mut context, "obj.writable"), "false");
assert_eq!(forward(&mut context, "obj.configurable"), "true");

// name
assert_eq!(
forward(&mut context, "RegExp.prototype[Symbol.search].name"),
"\"[Symbol.search]\""
);

let init =
"var obj = Object.getOwnPropertyDescriptor(RegExp.prototype[Symbol.search], \"name\")";
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut context, "obj.enumerable"), "false");
assert_eq!(forward(&mut context, "obj.writable"), "false");
assert_eq!(forward(&mut context, "obj.configurable"), "true");

// prop-desc
let init = "var obj = Object.getOwnPropertyDescriptor(RegExp.prototype, Symbol.search)";
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut context, "obj.enumerable"), "false");
assert_eq!(forward(&mut context, "obj.writable"), "true");
assert_eq!(forward(&mut context, "obj.configurable"), "true");

// success-return-val
assert_eq!(forward(&mut context, "/a/[Symbol.search]('abc')"), "0");
assert_eq!(forward(&mut context, "/b/[Symbol.search]('abc')"), "1");
assert_eq!(forward(&mut context, "/c/[Symbol.search]('abc')"), "2");

// this-val-non-obj
let error = "Uncaught \"TypeError\": \"RegExp.prototype[Symbol.search] method called on incompatible value\"";
let init = "var search = RegExp.prototype[Symbol.search]";
eprintln!("{}", forward(&mut context, init));
assert_eq!(forward(&mut context, "search.call()"), error);
assert_eq!(forward(&mut context, "search.call(undefined)"), error);
assert_eq!(forward(&mut context, "search.call(null)"), error);
assert_eq!(forward(&mut context, "search.call(true)"), error);
assert_eq!(forward(&mut context, "search.call('string')"), error);
assert_eq!(forward(&mut context, "search.call(Symbol.search)"), error);
assert_eq!(forward(&mut context, "search.call(86)"), error);

// u-lastindex-advance
assert_eq!(
forward(&mut context, "/\\udf06/u[Symbol.search]('\\ud834\\udf06')"),
"-1"
);

assert_eq!(forward(&mut context, "/a/[Symbol.search](\"a\")"), "0");
assert_eq!(forward(&mut context, "/a/[Symbol.search](\"ba\")"), "1");
assert_eq!(forward(&mut context, "/a/[Symbol.search](\"bb\")"), "-1");
Expand Down

0 comments on commit 9ac467a

Please sign in to comment.