diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 4466143cfc6..d77cc1a8ede 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -72,6 +72,12 @@ impl BuiltIn for RegExp { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + let get_species = FunctionBuilder::new(context, Self::get_species) + .name("get [Symbol.species]") + .constructable(false) + .callable(true) + .build(); + let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; let get_global = FunctionBuilder::new(context, Self::get_global) @@ -117,6 +123,12 @@ impl BuiltIn for RegExp { ) .name(Self::NAME) .length(Self::LENGTH) + .static_accessor( + WellKnownSymbols::species(), + Some(get_species), + None, + Attribute::CONFIGURABLE, + ) .property("lastIndex", 0, Attribute::all()) .method(Self::test, "test", 1) .method(Self::exec, "exec", 1) @@ -258,6 +270,21 @@ impl RegExp { Ok(this) } + /// `get RegExp[@@species]` + /// + /// The RegExp[@@species] accessor property returns the RegExp constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-get-regexp-@@species + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@species + fn get_species(this: &Value, _: &[Value], _: &mut Context) -> Result { + // 1. Return the this value. + Ok(this.clone()) + } + #[inline] fn regexp_has_flag(this: &Value, flag: char, context: &mut Context) -> Result { if let Some(object) = this.as_object() { diff --git a/boa/src/builtins/regexp/tests.rs b/boa/src/builtins/regexp/tests.rs index b4f2c341d4c..788b75e2f74 100644 --- a/boa/src/builtins/regexp/tests.rs +++ b/boa/src/builtins/regexp/tests.rs @@ -15,6 +15,51 @@ fn constructors() { assert_eq!(forward(&mut context, "ctor_literal.test('1.0')"), "true"); } +#[test] +fn species() { + let mut context = Context::new(); + + let init = r#" + var descriptor = Object.getOwnPropertyDescriptor(RegExp, Symbol.species); + var accessor = Object.getOwnPropertyDescriptor(RegExp, Symbol.species).get; + var name = Object.getOwnPropertyDescriptor(descriptor.get, "name"); + var length = Object.getOwnPropertyDescriptor(descriptor.get, "length"); + var thisVal = {}; + "#; + eprintln!("{}", forward(&mut context, init)); + + // length + assert_eq!(forward(&mut context, "descriptor.get.length"), "0"); + assert_eq!(forward(&mut context, "length.enumerable"), "false"); + assert_eq!(forward(&mut context, "length.writable"), "false"); + assert_eq!(forward(&mut context, "length.configurable"), "true"); + + // return-value + assert_eq!( + forward(&mut context, "Object.is(accessor.call(thisVal), thisVal)"), + "true" + ); + + // symbol-species-name + assert_eq!( + forward(&mut context, "descriptor.get.name"), + "\"get [Symbol.species]\"" + ); + assert_eq!(forward(&mut context, "name.enumerable"), "false"); + assert_eq!(forward(&mut context, "name.writable"), "false"); + assert_eq!(forward(&mut context, "name.configurable"), "true"); + + // symbol-species + assert_eq!(forward(&mut context, "descriptor.set"), "undefined"); + assert_eq!( + forward(&mut context, "typeof descriptor.get"), + "\"function\"" + ); + assert_eq!(forward(&mut context, "descriptor.enumerable"), "false"); + assert_eq!(forward(&mut context, "descriptor.writable"), "false"); + assert_eq!(forward(&mut context, "descriptor.configurable"), "true"); +} + // TODO: uncomment this test when property getters are supported // #[test]