Serenity Operating System
at master 286 lines 13 kB view raw
1/* 2 * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibJS/Runtime/Error.h> 8#include <LibJS/Runtime/GlobalObject.h> 9#include <LibJS/Runtime/RegExpConstructor.h> 10#include <LibJS/Runtime/RegExpObject.h> 11#include <LibJS/Runtime/Value.h> 12 13namespace JS { 14 15RegExpConstructor::RegExpConstructor(Realm& realm) 16 : NativeFunction(realm.vm().names.RegExp.as_string(), *realm.intrinsics().function_prototype()) 17{ 18} 19 20ThrowCompletionOr<void> RegExpConstructor::initialize(Realm& realm) 21{ 22 auto& vm = this->vm(); 23 MUST_OR_THROW_OOM(NativeFunction::initialize(realm)); 24 25 // 22.2.4.1 RegExp.prototype, https://tc39.es/ecma262/#sec-regexp.prototype 26 define_direct_property(vm.names.prototype, realm.intrinsics().regexp_prototype(), 0); 27 28 define_native_accessor(realm, *vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable); 29 30 define_direct_property(vm.names.length, Value(2), Attribute::Configurable); 31 32 // Additional properties of the RegExp constructor, https://github.com/tc39/proposal-regexp-legacy-features#additional-properties-of-the-regexp-constructor 33 define_native_accessor(realm, vm.names.input, input_getter, input_setter, Attribute::Configurable); 34 define_native_accessor(realm, vm.names.inputAlias, input_alias_getter, input_alias_setter, Attribute::Configurable); 35 define_native_accessor(realm, vm.names.lastMatch, last_match_getter, {}, Attribute::Configurable); 36 define_native_accessor(realm, vm.names.lastMatchAlias, last_match_alias_getter, {}, Attribute::Configurable); 37 define_native_accessor(realm, vm.names.lastParen, last_paren_getter, {}, Attribute::Configurable); 38 define_native_accessor(realm, vm.names.lastParenAlias, last_paren_alias_getter, {}, Attribute::Configurable); 39 define_native_accessor(realm, vm.names.leftContext, left_context_getter, {}, Attribute::Configurable); 40 define_native_accessor(realm, vm.names.leftContextAlias, left_context_alias_getter, {}, Attribute::Configurable); 41 define_native_accessor(realm, vm.names.rightContext, right_context_getter, {}, Attribute::Configurable); 42 define_native_accessor(realm, vm.names.rightContextAlias, right_context_alias_getter, {}, Attribute::Configurable); 43 define_native_accessor(realm, vm.names.$1, group_1_getter, {}, Attribute::Configurable); 44 define_native_accessor(realm, vm.names.$2, group_2_getter, {}, Attribute::Configurable); 45 define_native_accessor(realm, vm.names.$3, group_3_getter, {}, Attribute::Configurable); 46 define_native_accessor(realm, vm.names.$4, group_4_getter, {}, Attribute::Configurable); 47 define_native_accessor(realm, vm.names.$5, group_5_getter, {}, Attribute::Configurable); 48 define_native_accessor(realm, vm.names.$6, group_6_getter, {}, Attribute::Configurable); 49 define_native_accessor(realm, vm.names.$7, group_7_getter, {}, Attribute::Configurable); 50 define_native_accessor(realm, vm.names.$8, group_8_getter, {}, Attribute::Configurable); 51 define_native_accessor(realm, vm.names.$9, group_9_getter, {}, Attribute::Configurable); 52 53 return {}; 54} 55 56// 22.2.3.1 RegExp ( pattern, flags ), https://tc39.es/ecma262/#sec-regexp-pattern-flags 57ThrowCompletionOr<Value> RegExpConstructor::call() 58{ 59 auto& vm = this->vm(); 60 61 auto pattern = vm.argument(0); 62 auto flags = vm.argument(1); 63 64 // 1. Let patternIsRegExp be ? IsRegExp(pattern). 65 bool pattern_is_regexp = TRY(pattern.is_regexp(vm)); 66 67 // 2. If NewTarget is undefined, then 68 // a. Let newTarget be the active function object. 69 auto& new_target = *this; 70 71 // b. If patternIsRegExp is true and flags is undefined, then 72 if (pattern_is_regexp && flags.is_undefined()) { 73 // i. Let patternConstructor be ? Get(pattern, "constructor"). 74 auto pattern_constructor = TRY(pattern.as_object().get(vm.names.constructor)); 75 76 // ii. If SameValue(newTarget, patternConstructor) is true, return pattern. 77 if (same_value(&new_target, pattern_constructor)) 78 return pattern; 79 } 80 81 return TRY(construct(new_target)); 82} 83 84// 22.2.3.1 RegExp ( pattern, flags ), https://tc39.es/ecma262/#sec-regexp-pattern-flags 85ThrowCompletionOr<NonnullGCPtr<Object>> RegExpConstructor::construct(FunctionObject& new_target) 86{ 87 auto& vm = this->vm(); 88 89 auto pattern = vm.argument(0); 90 auto flags = vm.argument(1); 91 92 // 1. Let patternIsRegExp be ? IsRegExp(pattern). 93 bool pattern_is_regexp = TRY(pattern.is_regexp(vm)); 94 95 // NOTE: Step 2 is handled in call() above. 96 // 3. Else, let newTarget be NewTarget. 97 98 Value pattern_value; 99 Value flags_value; 100 101 // 4. If pattern is an Object and pattern has a [[RegExpMatcher]] internal slot, then 102 if (pattern.is_object() && is<RegExpObject>(pattern.as_object())) { 103 // a. Let P be pattern.[[OriginalSource]]. 104 auto& regexp_pattern = static_cast<RegExpObject&>(pattern.as_object()); 105 pattern_value = PrimitiveString::create(vm, regexp_pattern.pattern()); 106 107 // b. If flags is undefined, let F be pattern.[[OriginalFlags]]. 108 if (flags.is_undefined()) 109 flags_value = PrimitiveString::create(vm, regexp_pattern.flags()); 110 // c. Else, let F be flags. 111 else 112 flags_value = flags; 113 } 114 // 5. Else if patternIsRegExp is true, then 115 else if (pattern_is_regexp) { 116 // a. Let P be ? Get(pattern, "source"). 117 pattern_value = TRY(pattern.as_object().get(vm.names.source)); 118 119 // b. If flags is undefined, then 120 if (flags.is_undefined()) { 121 // i. Let F be ? Get(pattern, "flags"). 122 flags_value = TRY(pattern.as_object().get(vm.names.flags)); 123 } 124 // c. Else, let F be flags. 125 else { 126 flags_value = flags; 127 } 128 } 129 // 6. Else, 130 else { 131 // a. Let P be pattern. 132 pattern_value = pattern; 133 134 // b. Let F be flags. 135 flags_value = flags; 136 } 137 138 // 7. Let O be ? RegExpAlloc(newTarget). 139 auto regexp_object = TRY(regexp_alloc(vm, new_target)); 140 141 // 8. Return ? RegExpInitialize(O, P, F). 142 return TRY(regexp_object->regexp_initialize(vm, pattern_value, flags_value)); 143} 144 145// 22.2.4.2 get RegExp [ @@species ], https://tc39.es/ecma262/#sec-get-regexp-@@species 146JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::symbol_species_getter) 147{ 148 // 1. Return the this value. 149 return vm.this_value(); 150} 151 152// get RegExp.input, https://github.com/tc39/proposal-regexp-legacy-features#get-regexpinput 153JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_getter) 154{ 155 auto* regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); 156 157 // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]]). 158 auto property_getter = &RegExpLegacyStaticProperties::input; 159 return TRY(get_legacy_regexp_static_property(vm, *regexp_constructor, vm.this_value(), property_getter)); 160} 161 162// get RegExp.$_, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp_ 163JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_alias_getter) 164{ 165 // Keep the same implementation with `get RegExp.input` 166 return input_getter(vm); 167} 168 169// set RegExp.input, https://github.com/tc39/proposal-regexp-legacy-features#set-regexpinput--val 170JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_setter) 171{ 172 auto* regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); 173 174 // 1. Perform ? SetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]], val). 175 auto property_setter = &RegExpLegacyStaticProperties::set_input; 176 TRY(set_legacy_regexp_static_property(vm, *regexp_constructor, vm.this_value(), property_setter, vm.argument(0))); 177 return js_undefined(); 178} 179 180// set RegExp.$_, https://github.com/tc39/proposal-regexp-legacy-features#set-regexp_---val 181JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_alias_setter) 182{ 183 // Keep the same implementation with `set RegExp.input` 184 return input_setter(vm); 185} 186 187// get RegExp.lastMatch, https://github.com/tc39/proposal-regexp-legacy-features#get-regexplastmatch 188JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_match_getter) 189{ 190 auto* regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); 191 192 // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastMatch]]). 193 auto property_getter = &RegExpLegacyStaticProperties::last_match; 194 return TRY(get_legacy_regexp_static_property(vm, *regexp_constructor, vm.this_value(), property_getter)); 195} 196 197// get RegExp.$&, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp 198JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_match_alias_getter) 199{ 200 // Keep the same implementation with `get RegExp.lastMatch` 201 return last_match_getter(vm); 202} 203 204// get RegExp.lastParen, https://github.com/tc39/proposal-regexp-legacy-features#get-regexplastparen 205JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_paren_getter) 206{ 207 auto* regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); 208 209 // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastParen]]). 210 auto property_getter = &RegExpLegacyStaticProperties::last_paren; 211 return TRY(get_legacy_regexp_static_property(vm, *regexp_constructor, vm.this_value(), property_getter)); 212} 213 214// get RegExp.$+, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp-1 215JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_paren_alias_getter) 216{ 217 // Keep the same implementation with `get RegExp.lastParen` 218 return last_paren_getter(vm); 219} 220 221// get RegExp.leftContext, https://github.com/tc39/proposal-regexp-legacy-features#get-regexpleftcontext 222JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::left_context_getter) 223{ 224 auto* regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); 225 226 // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLeftContext]]). 227 auto property_getter = &RegExpLegacyStaticProperties::left_context; 228 return TRY(get_legacy_regexp_static_property(vm, *regexp_constructor, vm.this_value(), property_getter)); 229} 230 231// get RegExp.$`, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp-2 232JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::left_context_alias_getter) 233{ 234 // Keep the same implementation with `get RegExp.leftContext` 235 return left_context_getter(vm); 236} 237 238// get RegExp.rightContext, https://github.com/tc39/proposal-regexp-legacy-features#get-regexprightcontext 239JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::right_context_getter) 240{ 241 auto* regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); 242 243 // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpRightContext]]). 244 auto property_getter = &RegExpLegacyStaticProperties::right_context; 245 return TRY(get_legacy_regexp_static_property(vm, *regexp_constructor, vm.this_value(), property_getter)); 246} 247 248// get RegExp.$', https://github.com/tc39/proposal-regexp-legacy-features#get-regexp-3 249JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::right_context_alias_getter) 250{ 251 // Keep the same implementation with `get RegExp.rightContext` 252 return right_context_getter(vm); 253} 254 255#define DEFINE_REGEXP_GROUP_GETTER(n) \ 256 JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::group_##n##_getter) \ 257 { \ 258 auto* regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); \ 259 \ 260 /* 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen##n##]]).*/ \ 261 auto property_getter = &RegExpLegacyStaticProperties::$##n; \ 262 return TRY(get_legacy_regexp_static_property(vm, *regexp_constructor, vm.this_value(), property_getter)); \ 263 } 264 265// get RegExp.$1, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp1 266DEFINE_REGEXP_GROUP_GETTER(1); 267// get RegExp.$2, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp2 268DEFINE_REGEXP_GROUP_GETTER(2); 269// get RegExp.$3, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp3 270DEFINE_REGEXP_GROUP_GETTER(3); 271// get RegExp.$4, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp4 272DEFINE_REGEXP_GROUP_GETTER(4); 273// get RegExp.$5, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp5 274DEFINE_REGEXP_GROUP_GETTER(5); 275// get RegExp.$6, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp6 276DEFINE_REGEXP_GROUP_GETTER(6); 277// get RegExp.$7, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp7 278DEFINE_REGEXP_GROUP_GETTER(7); 279// get RegExp.$8, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp8 280DEFINE_REGEXP_GROUP_GETTER(8); 281// get RegExp.$9, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp9 282DEFINE_REGEXP_GROUP_GETTER(9); 283 284#undef DEFINE_REGEXP_GROUP_GETTER 285 286}