|
|
@ -21,13 +21,14 @@ use crate::{ |
|
|
|
exec::Interpreter, |
|
|
|
exec::Interpreter, |
|
|
|
BoaProfiler, |
|
|
|
BoaProfiler, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
use gc::{unsafe_empty_trace, Finalize, Trace}; |
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
#[cfg(test)] |
|
|
|
mod tests; |
|
|
|
mod tests; |
|
|
|
|
|
|
|
|
|
|
|
/// The internal representation on a `RegExp` object.
|
|
|
|
/// The internal representation on a `RegExp` object.
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Debug, Clone, Finalize)] |
|
|
|
pub(crate) struct RegExp { |
|
|
|
pub struct RegExp { |
|
|
|
/// Regex matcher.
|
|
|
|
/// Regex matcher.
|
|
|
|
matcher: Regex, |
|
|
|
matcher: Regex, |
|
|
|
|
|
|
|
|
|
|
@ -54,6 +55,13 @@ pub(crate) struct RegExp { |
|
|
|
|
|
|
|
|
|
|
|
/// Flag 'u' - Unicode.
|
|
|
|
/// Flag 'u' - Unicode.
|
|
|
|
unicode: bool, |
|
|
|
unicode: bool, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) original_source: String, |
|
|
|
|
|
|
|
original_flags: String, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsafe impl Trace for RegExp { |
|
|
|
|
|
|
|
unsafe_empty_trace!(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl InternalState for RegExp {} |
|
|
|
impl InternalState for RegExp {} |
|
|
@ -66,7 +74,7 @@ impl RegExp { |
|
|
|
pub(crate) const LENGTH: usize = 2; |
|
|
|
pub(crate) const LENGTH: usize = 2; |
|
|
|
|
|
|
|
|
|
|
|
/// Create a new `RegExp`
|
|
|
|
/// Create a new `RegExp`
|
|
|
|
pub(crate) fn make_regexp(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { |
|
|
|
pub(crate) fn make_regexp(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
let arg = args.get(0).ok_or_else(Value::undefined)?; |
|
|
|
let arg = args.get(0).ok_or_else(Value::undefined)?; |
|
|
|
let mut regex_body = String::new(); |
|
|
|
let mut regex_body = String::new(); |
|
|
|
let mut regex_flags = String::new(); |
|
|
|
let mut regex_flags = String::new(); |
|
|
@ -77,15 +85,10 @@ impl RegExp { |
|
|
|
} |
|
|
|
} |
|
|
|
Value::Object(ref obj) => { |
|
|
|
Value::Object(ref obj) => { |
|
|
|
let obj = obj.borrow(); |
|
|
|
let obj = obj.borrow(); |
|
|
|
let slots = obj.internal_slots(); |
|
|
|
if let Some(regex) = obj.as_regexp() { |
|
|
|
if slots.get("RegExpMatcher").is_some() { |
|
|
|
|
|
|
|
// first argument is another `RegExp` object, so copy its pattern and flags
|
|
|
|
// first argument is another `RegExp` object, so copy its pattern and flags
|
|
|
|
if let Some(body) = slots.get("OriginalSource") { |
|
|
|
regex_body = regex.original_source.clone(); |
|
|
|
regex_body = ctx.to_string(body)?.to_string(); |
|
|
|
regex_flags = regex.original_flags.clone(); |
|
|
|
} |
|
|
|
|
|
|
|
if let Some(flags) = slots.get("OriginalFlags") { |
|
|
|
|
|
|
|
regex_flags = ctx.to_string(flags)?.to_string(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
_ => return Err(Value::undefined()), |
|
|
|
_ => return Err(Value::undefined()), |
|
|
@ -149,133 +152,129 @@ impl RegExp { |
|
|
|
multiline, |
|
|
|
multiline, |
|
|
|
sticky, |
|
|
|
sticky, |
|
|
|
unicode, |
|
|
|
unicode, |
|
|
|
|
|
|
|
original_source: regex_body, |
|
|
|
|
|
|
|
original_flags: regex_flags, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// This value is used by console.log and other routines to match Object type
|
|
|
|
this.set_data(ObjectData::RegExp(regexp)); |
|
|
|
// to its Javascript Identifier (global constructor method name)
|
|
|
|
|
|
|
|
this.set_data(ObjectData::Ordinary); |
|
|
|
|
|
|
|
this.set_internal_slot("RegExpMatcher", Value::undefined()); |
|
|
|
|
|
|
|
this.set_internal_slot("OriginalSource", Value::from(regex_body)); |
|
|
|
|
|
|
|
this.set_internal_slot("OriginalFlags", Value::from(regex_flags)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.set_internal_state(regexp); |
|
|
|
|
|
|
|
Ok(this.clone()) |
|
|
|
Ok(this.clone()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.dotAll`
|
|
|
|
// /// `RegExp.prototype.dotAll`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The `dotAll` property indicates whether or not the "`s`" flag is used with the regular expression.
|
|
|
|
// /// The `dotAll` property indicates whether or not the "`s`" flag is used with the regular expression.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll
|
|
|
|
fn get_dot_all(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_dot_all(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all))) |
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all)))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.flags`
|
|
|
|
// /// `RegExp.prototype.flags`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
|
|
|
|
// /// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags
|
|
|
|
/// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2
|
|
|
|
// /// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2
|
|
|
|
fn get_flags(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_flags(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone()))) |
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone())))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.global`
|
|
|
|
// /// `RegExp.prototype.global`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The `global` property indicates whether or not the "`g`" flag is used with the regular expression.
|
|
|
|
// /// The `global` property indicates whether or not the "`g`" flag is used with the regular expression.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global
|
|
|
|
fn get_global(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_global(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global))) |
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global)))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.ignoreCase`
|
|
|
|
// /// `RegExp.prototype.ignoreCase`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The `ignoreCase` property indicates whether or not the "`i`" flag is used with the regular expression.
|
|
|
|
// /// The `ignoreCase` property indicates whether or not the "`i`" flag is used with the regular expression.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase
|
|
|
|
fn get_ignore_case(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_ignore_case(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case))) |
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case)))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.multiline`
|
|
|
|
// /// `RegExp.prototype.multiline`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The multiline property indicates whether or not the "m" flag is used with the regular expression.
|
|
|
|
// /// The multiline property indicates whether or not the "m" flag is used with the regular expression.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline
|
|
|
|
fn get_multiline(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_multiline(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline))) |
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline)))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.source`
|
|
|
|
// /// `RegExp.prototype.source`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The `source` property returns a `String` containing the source text of the regexp object,
|
|
|
|
// /// The `source` property returns a `String` containing the source text of the regexp object,
|
|
|
|
/// and it doesn't contain the two forward slashes on both sides and any flags.
|
|
|
|
// /// and it doesn't contain the two forward slashes on both sides and any flags.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
|
|
|
|
fn get_source(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_source(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
Ok(this.get_internal_slot("OriginalSource")) |
|
|
|
// Ok(this.get_internal_slot("OriginalSource"))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.sticky`
|
|
|
|
// /// `RegExp.prototype.sticky`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
|
|
|
|
// /// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky
|
|
|
|
fn get_sticky(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_sticky(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky))) |
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky)))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.unicode`
|
|
|
|
// /// `RegExp.prototype.unicode`
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// The unicode property indicates whether or not the "`u`" flag is used with a regular expression.
|
|
|
|
// /// The unicode property indicates whether or not the "`u`" flag is used with a regular expression.
|
|
|
|
/// unicode is a read-only property of an individual regular expression instance.
|
|
|
|
// /// unicode is a read-only property of an individual regular expression instance.
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// More information:
|
|
|
|
// /// More information:
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
///
|
|
|
|
// ///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode
|
|
|
|
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode
|
|
|
|
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode
|
|
|
|
fn get_unicode(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
// fn get_unicode(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode))) |
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode)))
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.test( string )`
|
|
|
|
/// `RegExp.prototype.test( string )`
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -292,7 +291,8 @@ impl RegExp { |
|
|
|
pub(crate) fn test(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { |
|
|
|
pub(crate) fn test(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { |
|
|
|
let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; |
|
|
|
let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; |
|
|
|
let mut last_index = usize::from(&this.get_field("lastIndex")); |
|
|
|
let mut last_index = usize::from(&this.get_field("lastIndex")); |
|
|
|
let result = this.with_internal_state_ref(|regex: &RegExp| { |
|
|
|
let result = if let Some(object) = this.as_object() { |
|
|
|
|
|
|
|
let regex = object.as_regexp().unwrap(); |
|
|
|
let result = if let Some(m) = regex.matcher.find_at(arg_str.as_str(), last_index) { |
|
|
|
let result = if let Some(m) = regex.matcher.find_at(arg_str.as_str(), last_index) { |
|
|
|
if regex.use_last_index { |
|
|
|
if regex.use_last_index { |
|
|
|
last_index = m.end(); |
|
|
|
last_index = m.end(); |
|
|
@ -305,7 +305,9 @@ impl RegExp { |
|
|
|
false |
|
|
|
false |
|
|
|
}; |
|
|
|
}; |
|
|
|
Ok(Value::boolean(result)) |
|
|
|
Ok(Value::boolean(result)) |
|
|
|
}); |
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("object is not a regexp") |
|
|
|
|
|
|
|
}; |
|
|
|
this.set_field("lastIndex", Value::from(last_index)); |
|
|
|
this.set_field("lastIndex", Value::from(last_index)); |
|
|
|
result |
|
|
|
result |
|
|
|
} |
|
|
|
} |
|
|
@ -325,7 +327,8 @@ impl RegExp { |
|
|
|
pub(crate) fn exec(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { |
|
|
|
pub(crate) fn exec(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { |
|
|
|
let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; |
|
|
|
let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; |
|
|
|
let mut last_index = usize::from(&this.get_field("lastIndex")); |
|
|
|
let mut last_index = usize::from(&this.get_field("lastIndex")); |
|
|
|
let result = this.with_internal_state_ref(|regex: &RegExp| { |
|
|
|
let result = if let Some(object) = this.as_object() { |
|
|
|
|
|
|
|
let regex = object.as_regexp().unwrap(); |
|
|
|
let mut locations = regex.matcher.capture_locations(); |
|
|
|
let mut locations = regex.matcher.capture_locations(); |
|
|
|
let result = if let Some(m) = |
|
|
|
let result = if let Some(m) = |
|
|
|
regex |
|
|
|
regex |
|
|
@ -357,7 +360,9 @@ impl RegExp { |
|
|
|
Value::null() |
|
|
|
Value::null() |
|
|
|
}; |
|
|
|
}; |
|
|
|
Ok(result) |
|
|
|
Ok(result) |
|
|
|
}); |
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("object is not a regexp") |
|
|
|
|
|
|
|
}; |
|
|
|
this.set_field("lastIndex", Value::from(last_index)); |
|
|
|
this.set_field("lastIndex", Value::from(last_index)); |
|
|
|
result |
|
|
|
result |
|
|
|
} |
|
|
|
} |
|
|
@ -373,8 +378,12 @@ impl RegExp { |
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match
|
|
|
|
pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Interpreter) -> ResultValue { |
|
|
|
pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Interpreter) -> ResultValue { |
|
|
|
let (matcher, flags) = this |
|
|
|
let (matcher, flags) = if let Some(object) = this.as_object() { |
|
|
|
.with_internal_state_ref(|regex: &RegExp| (regex.matcher.clone(), regex.flags.clone())); |
|
|
|
let regex = object.as_regexp().unwrap(); |
|
|
|
|
|
|
|
(regex.matcher.clone(), regex.flags.clone()) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("object is not a regexp") |
|
|
|
|
|
|
|
}; |
|
|
|
if flags.contains('g') { |
|
|
|
if flags.contains('g') { |
|
|
|
let mut matches = Vec::new(); |
|
|
|
let mut matches = Vec::new(); |
|
|
|
for mat in matcher.find_iter(&arg) { |
|
|
|
for mat in matcher.find_iter(&arg) { |
|
|
@ -400,9 +409,13 @@ impl RegExp { |
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
|
|
|
|
#[allow(clippy::wrong_self_convention)] |
|
|
|
#[allow(clippy::wrong_self_convention)] |
|
|
|
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { |
|
|
|
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
let body = ctx.to_string(&this.get_internal_slot("OriginalSource"))?; |
|
|
|
let (body, flags) = if let Some(object) = this.as_object() { |
|
|
|
let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone()); |
|
|
|
let regex = object.as_regexp().unwrap(); |
|
|
|
|
|
|
|
(regex.original_source.clone(), regex.flags.clone()) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("object is not an object") |
|
|
|
|
|
|
|
}; |
|
|
|
Ok(Value::from(format!("/{}/{}", body, flags))) |
|
|
|
Ok(Value::from(format!("/{}/{}", body, flags))) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -418,7 +431,8 @@ impl RegExp { |
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll
|
|
|
|
// TODO: it's returning an array, it should return an iterator
|
|
|
|
// TODO: it's returning an array, it should return an iterator
|
|
|
|
pub(crate) fn match_all(this: &Value, arg_str: String) -> ResultValue { |
|
|
|
pub(crate) fn match_all(this: &Value, arg_str: String) -> ResultValue { |
|
|
|
let matches: Vec<Value> = this.with_internal_state_ref(|regex: &RegExp| { |
|
|
|
let matches = if let Some(object) = this.as_object() { |
|
|
|
|
|
|
|
let regex = object.as_regexp().unwrap(); |
|
|
|
let mut matches = Vec::new(); |
|
|
|
let mut matches = Vec::new(); |
|
|
|
|
|
|
|
|
|
|
|
for m in regex.matcher.find_iter(&arg_str) { |
|
|
|
for m in regex.matcher.find_iter(&arg_str) { |
|
|
@ -448,7 +462,9 @@ impl RegExp { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
matches |
|
|
|
matches |
|
|
|
}); |
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("object is not a regexp") |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let length = matches.len(); |
|
|
|
let length = matches.len(); |
|
|
|
let result = Value::from(matches); |
|
|
|
let result = Value::from(matches); |
|
|
@ -458,8 +474,12 @@ impl RegExp { |
|
|
|
Ok(result) |
|
|
|
Ok(result) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Create a new `RegExp` object.
|
|
|
|
/// Initialise the `RegExp` object on the global object.
|
|
|
|
pub(crate) fn create(global: &Value) -> Value { |
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { |
|
|
|
|
|
|
|
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); |
|
|
|
|
|
|
|
let global = interpreter.global(); |
|
|
|
|
|
|
|
|
|
|
|
// Create prototype
|
|
|
|
// Create prototype
|
|
|
|
let prototype = Value::new_object(Some(global)); |
|
|
|
let prototype = Value::new_object(Some(global)); |
|
|
|
prototype |
|
|
|
prototype |
|
|
@ -470,16 +490,18 @@ impl RegExp { |
|
|
|
make_builtin_fn(Self::test, "test", &prototype, 1); |
|
|
|
make_builtin_fn(Self::test, "test", &prototype, 1); |
|
|
|
make_builtin_fn(Self::exec, "exec", &prototype, 1); |
|
|
|
make_builtin_fn(Self::exec, "exec", &prototype, 1); |
|
|
|
make_builtin_fn(Self::to_string, "toString", &prototype, 0); |
|
|
|
make_builtin_fn(Self::to_string, "toString", &prototype, 0); |
|
|
|
make_builtin_fn(Self::get_dot_all, "dotAll", &prototype, 0); |
|
|
|
|
|
|
|
make_builtin_fn(Self::get_flags, "flags", &prototype, 0); |
|
|
|
// TODO: make them accessor properties, not methods.
|
|
|
|
make_builtin_fn(Self::get_global, "global", &prototype, 0); |
|
|
|
// make_builtin_fn(Self::get_dot_all, "dotAll", &prototype, 0);
|
|
|
|
make_builtin_fn(Self::get_ignore_case, "ignoreCase", &prototype, 0); |
|
|
|
// make_builtin_fn(Self::get_flags, "flags", &prototype, 0);
|
|
|
|
make_builtin_fn(Self::get_multiline, "multiline", &prototype, 0); |
|
|
|
// make_builtin_fn(Self::get_global, "global", &prototype, 0);
|
|
|
|
make_builtin_fn(Self::get_source, "source", &prototype, 0); |
|
|
|
// make_builtin_fn(Self::get_ignore_case, "ignoreCase", &prototype, 0);
|
|
|
|
make_builtin_fn(Self::get_sticky, "sticky", &prototype, 0); |
|
|
|
// make_builtin_fn(Self::get_multiline, "multiline", &prototype, 0);
|
|
|
|
make_builtin_fn(Self::get_unicode, "unicode", &prototype, 0); |
|
|
|
// make_builtin_fn(Self::get_source, "source", &prototype, 0);
|
|
|
|
|
|
|
|
// make_builtin_fn(Self::get_sticky, "sticky", &prototype, 0);
|
|
|
|
make_constructor_fn( |
|
|
|
// make_builtin_fn(Self::get_unicode, "unicode", &prototype, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let regexp = make_constructor_fn( |
|
|
|
Self::NAME, |
|
|
|
Self::NAME, |
|
|
|
Self::LENGTH, |
|
|
|
Self::LENGTH, |
|
|
|
Self::make_regexp, |
|
|
|
Self::make_regexp, |
|
|
@ -487,15 +509,8 @@ impl RegExp { |
|
|
|
prototype, |
|
|
|
prototype, |
|
|
|
true, |
|
|
|
true, |
|
|
|
true, |
|
|
|
true, |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Initialise the `RegExp` object on the global object.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { |
|
|
|
|
|
|
|
let global = interpreter.global(); |
|
|
|
|
|
|
|
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(Self::NAME, Self::create(global)) |
|
|
|
(Self::NAME, regexp) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|