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