|
|
|
@ -12,7 +12,7 @@
|
|
|
|
|
use crate::{ |
|
|
|
|
builtins::BuiltIn, |
|
|
|
|
gc::{empty_trace, Finalize, Trace}, |
|
|
|
|
object::{ConstructorBuilder, ObjectData, PROTOTYPE}, |
|
|
|
|
object::{ConstructorBuilder, FunctionBuilder, GcObject, ObjectData, PROTOTYPE}, |
|
|
|
|
property::{Attribute, DataDescriptor}, |
|
|
|
|
value::{RcString, Value}, |
|
|
|
|
BoaProfiler, Context, Result, |
|
|
|
@ -71,6 +71,44 @@ impl BuiltIn for RegExp {
|
|
|
|
|
fn init(context: &mut Context) -> (&'static str, Value, Attribute) { |
|
|
|
|
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); |
|
|
|
|
|
|
|
|
|
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; |
|
|
|
|
|
|
|
|
|
let get_global = FunctionBuilder::new(context, Self::get_global) |
|
|
|
|
.name("get global") |
|
|
|
|
.constructable(false) |
|
|
|
|
.callable(true) |
|
|
|
|
.build(); |
|
|
|
|
let get_ignore_case = FunctionBuilder::new(context, Self::get_ignore_case) |
|
|
|
|
.name("get ignoreCase") |
|
|
|
|
.constructable(false) |
|
|
|
|
.callable(true) |
|
|
|
|
.build(); |
|
|
|
|
let get_multiline = FunctionBuilder::new(context, Self::get_multiline) |
|
|
|
|
.name("get multiline") |
|
|
|
|
.constructable(false) |
|
|
|
|
.callable(true) |
|
|
|
|
.build(); |
|
|
|
|
let get_dot_all = FunctionBuilder::new(context, Self::get_dot_all) |
|
|
|
|
.name("get dotAll") |
|
|
|
|
.constructable(false) |
|
|
|
|
.callable(true) |
|
|
|
|
.build(); |
|
|
|
|
let get_unicode = FunctionBuilder::new(context, Self::get_unicode) |
|
|
|
|
.name("get unicode") |
|
|
|
|
.constructable(false) |
|
|
|
|
.callable(true) |
|
|
|
|
.build(); |
|
|
|
|
let get_sticky = FunctionBuilder::new(context, Self::get_sticky) |
|
|
|
|
.name("get sticky") |
|
|
|
|
.constructable(false) |
|
|
|
|
.callable(true) |
|
|
|
|
.build(); |
|
|
|
|
let get_flags = FunctionBuilder::new(context, Self::get_flags) |
|
|
|
|
.name("get flags") |
|
|
|
|
.constructable(false) |
|
|
|
|
.callable(true) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
let regexp_object = ConstructorBuilder::with_standard_object( |
|
|
|
|
context, |
|
|
|
|
Self::constructor, |
|
|
|
@ -82,6 +120,13 @@ impl BuiltIn for RegExp {
|
|
|
|
|
.method(Self::test, "test", 1) |
|
|
|
|
.method(Self::exec, "exec", 1) |
|
|
|
|
.method(Self::to_string, "toString", 0) |
|
|
|
|
.accessor("global", Some(get_global), None, flag_attributes) |
|
|
|
|
.accessor("ignoreCase", Some(get_ignore_case), None, flag_attributes) |
|
|
|
|
.accessor("multiline", Some(get_multiline), None, flag_attributes) |
|
|
|
|
.accessor("dotAll", Some(get_dot_all), None, flag_attributes) |
|
|
|
|
.accessor("unicode", Some(get_unicode), None, flag_attributes) |
|
|
|
|
.accessor("sticky", Some(get_sticky), None, flag_attributes) |
|
|
|
|
.accessor("flags", Some(get_flags), None, flag_attributes) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
// TODO: add them RegExp accessor properties
|
|
|
|
@ -207,76 +252,191 @@ impl RegExp {
|
|
|
|
|
Ok(this) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.dotAll`
|
|
|
|
|
// ///
|
|
|
|
|
// /// The `dotAll` property indicates whether or not the "`s`" flag is used with the regular expression.
|
|
|
|
|
// ///
|
|
|
|
|
// /// More information:
|
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// fn get_dot_all(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all)))
|
|
|
|
|
// }
|
|
|
|
|
#[inline] |
|
|
|
|
fn regexp_has_flag(this: &Value, flag: char, context: &mut Context) -> Result<Value> { |
|
|
|
|
if let Some(object) = this.as_object() { |
|
|
|
|
if let Some(regexp) = object.borrow().as_regexp() { |
|
|
|
|
return Ok(Value::boolean(match flag { |
|
|
|
|
'g' => regexp.global, |
|
|
|
|
'm' => regexp.multiline, |
|
|
|
|
's' => regexp.dot_all, |
|
|
|
|
'i' => regexp.ignore_case, |
|
|
|
|
'u' => regexp.unicode, |
|
|
|
|
'y' => regexp.sticky, |
|
|
|
|
_ => unreachable!(), |
|
|
|
|
})); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.flags`
|
|
|
|
|
// ///
|
|
|
|
|
// /// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
|
|
|
|
|
// ///
|
|
|
|
|
// /// More information:
|
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// /// [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 Context) -> Result<Value> {
|
|
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone())))
|
|
|
|
|
// }
|
|
|
|
|
if GcObject::equals( |
|
|
|
|
&object, |
|
|
|
|
&context.standard_objects().regexp_object().prototype, |
|
|
|
|
) { |
|
|
|
|
return Ok(Value::undefined()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.global`
|
|
|
|
|
// ///
|
|
|
|
|
// /// The `global` property indicates whether or not the "`g`" flag is used with the regular expression.
|
|
|
|
|
// ///
|
|
|
|
|
// /// More information:
|
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// fn get_global(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global)))
|
|
|
|
|
// }
|
|
|
|
|
let name = match flag { |
|
|
|
|
'g' => "global", |
|
|
|
|
'm' => "multiline", |
|
|
|
|
's' => "dotAll", |
|
|
|
|
'i' => "ignoreCase", |
|
|
|
|
'u' => "unicode", |
|
|
|
|
'y' => "sticky", |
|
|
|
|
_ => unreachable!(), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.ignoreCase`
|
|
|
|
|
// ///
|
|
|
|
|
// /// The `ignoreCase` property indicates whether or not the "`i`" flag is used with the regular expression.
|
|
|
|
|
// ///
|
|
|
|
|
// /// More information:
|
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// fn get_ignore_case(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case)))
|
|
|
|
|
// }
|
|
|
|
|
context.throw_type_error(format!( |
|
|
|
|
"RegExp.prototype.{} getter called on non-RegExp object", |
|
|
|
|
name |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.multiline`
|
|
|
|
|
// ///
|
|
|
|
|
// /// The multiline property indicates whether or not the "m" flag is used with the regular expression.
|
|
|
|
|
// ///
|
|
|
|
|
// /// More information:
|
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// fn get_multiline(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline)))
|
|
|
|
|
// }
|
|
|
|
|
/// `get RegExp.prototype.global`
|
|
|
|
|
///
|
|
|
|
|
/// The `global` property indicates whether or not the "`g`" flag is used with the regular expression.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [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
|
|
|
|
|
pub(crate) fn get_global(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
Self::regexp_has_flag(this, 'g', context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `get RegExp.prototype.ignoreCase`
|
|
|
|
|
///
|
|
|
|
|
/// The `ignoreCase` property indicates whether or not the "`i`" flag is used with the regular expression.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [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
|
|
|
|
|
pub(crate) fn get_ignore_case( |
|
|
|
|
this: &Value, |
|
|
|
|
_: &[Value], |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<Value> { |
|
|
|
|
Self::regexp_has_flag(this, 'i', context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `get RegExp.prototype.multiline`
|
|
|
|
|
///
|
|
|
|
|
/// The multiline property indicates whether or not the "m" flag is used with the regular expression.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [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
|
|
|
|
|
pub(crate) fn get_multiline(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
Self::regexp_has_flag(this, 'm', context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `get RegExp.prototype.dotAll`
|
|
|
|
|
///
|
|
|
|
|
/// The `dotAll` property indicates whether or not the "`s`" flag is used with the regular expression.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [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
|
|
|
|
|
pub(crate) fn get_dot_all(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
Self::regexp_has_flag(this, 's', context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `get RegExp.prototype.unicode`
|
|
|
|
|
///
|
|
|
|
|
/// 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.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [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
|
|
|
|
|
pub(crate) fn get_unicode(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
Self::regexp_has_flag(this, 'u', context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `get RegExp.prototype.sticky`
|
|
|
|
|
///
|
|
|
|
|
/// This flag indicates that it matches only from the index indicated by the `lastIndex` property
|
|
|
|
|
/// of this regular expression in the target string (and does not attempt to match from any later indexes).
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [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
|
|
|
|
|
pub(crate) fn get_sticky(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
Self::regexp_has_flag(this, 'y', context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `get RegExp.prototype.flags`
|
|
|
|
|
///
|
|
|
|
|
/// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [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
|
|
|
|
|
/// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2
|
|
|
|
|
pub(crate) fn get_flags(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
if let Some(object) = this.as_object() { |
|
|
|
|
let mut result = String::new(); |
|
|
|
|
if object |
|
|
|
|
.get(&"global".into(), this.clone(), context)? |
|
|
|
|
.to_boolean() |
|
|
|
|
{ |
|
|
|
|
result.push('g'); |
|
|
|
|
} |
|
|
|
|
if object |
|
|
|
|
.get(&"ignoreCase".into(), this.clone(), context)? |
|
|
|
|
.to_boolean() |
|
|
|
|
{ |
|
|
|
|
result.push('i'); |
|
|
|
|
} |
|
|
|
|
if object |
|
|
|
|
.get(&"multiline".into(), this.clone(), context)? |
|
|
|
|
.to_boolean() |
|
|
|
|
{ |
|
|
|
|
result.push('m'); |
|
|
|
|
} |
|
|
|
|
if object |
|
|
|
|
.get(&"dotAll".into(), this.clone(), context)? |
|
|
|
|
.to_boolean() |
|
|
|
|
{ |
|
|
|
|
result.push('s'); |
|
|
|
|
} |
|
|
|
|
if object |
|
|
|
|
.get(&"unicode".into(), this.clone(), context)? |
|
|
|
|
.to_boolean() |
|
|
|
|
{ |
|
|
|
|
result.push('u'); |
|
|
|
|
} |
|
|
|
|
if object |
|
|
|
|
.get(&"sticky".into(), this.clone(), context)? |
|
|
|
|
.to_boolean() |
|
|
|
|
{ |
|
|
|
|
result.push('y'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Ok(result.into()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
context.throw_type_error("RegExp.prototype.flags getter called on non-object") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.source`
|
|
|
|
|
// ///
|
|
|
|
@ -289,39 +449,10 @@ impl RegExp {
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// fn get_source(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// pub(crate) fn get_source(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// Ok(this.get_internal_slot("OriginalSource"))
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.sticky`
|
|
|
|
|
// ///
|
|
|
|
|
// /// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.
|
|
|
|
|
// ///
|
|
|
|
|
// /// More information:
|
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// fn get_sticky(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky)))
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// /// `RegExp.prototype.unicode`
|
|
|
|
|
// ///
|
|
|
|
|
// /// 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.
|
|
|
|
|
// ///
|
|
|
|
|
// /// More information:
|
|
|
|
|
// /// - [ECMAScript reference][spec]
|
|
|
|
|
// /// - [MDN documentation][mdn]
|
|
|
|
|
// ///
|
|
|
|
|
// /// [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
|
|
|
|
|
// fn get_unicode(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
|
|
|
|
|
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode)))
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
/// `RegExp.prototype.test( string )`
|
|
|
|
|
///
|
|
|
|
|
/// The `test()` method executes a search for a match between a regular expression and a specified string.
|
|
|
|
|