1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2003-2019 Apple Inc. All Rights Reserved.
4 * Copyright (C) 2009 Torch Mobile, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22#include "config.h"
23#include "RegExpConstructor.h"
24
25#include "Error.h"
26#include "GetterSetter.h"
27#include "JSCInlines.h"
28#include "RegExpGlobalDataInlines.h"
29#include "RegExpPrototype.h"
30#include "YarrFlags.h"
31
32namespace JSC {
33
34static EncodedJSValue regExpConstructorInput(JSGlobalObject*, EncodedJSValue, PropertyName);
35static EncodedJSValue regExpConstructorMultiline(JSGlobalObject*, EncodedJSValue, PropertyName);
36static EncodedJSValue regExpConstructorLastMatch(JSGlobalObject*, EncodedJSValue, PropertyName);
37static EncodedJSValue regExpConstructorLastParen(JSGlobalObject*, EncodedJSValue, PropertyName);
38static EncodedJSValue regExpConstructorLeftContext(JSGlobalObject*, EncodedJSValue, PropertyName);
39static EncodedJSValue regExpConstructorRightContext(JSGlobalObject*, EncodedJSValue, PropertyName);
40template<int N>
41static EncodedJSValue regExpConstructorDollar(JSGlobalObject*, EncodedJSValue, PropertyName);
42
43static bool setRegExpConstructorInput(JSGlobalObject*, EncodedJSValue, EncodedJSValue);
44static bool setRegExpConstructorMultiline(JSGlobalObject*, EncodedJSValue, EncodedJSValue);
45
46} // namespace JSC
47
48#include "RegExpConstructor.lut.h"
49
50namespace JSC {
51
52const ClassInfo RegExpConstructor::s_info = { "Function", &InternalFunction::s_info, &regExpConstructorTable, nullptr, CREATE_METHOD_TABLE(RegExpConstructor) };
53
54/* Source for RegExpConstructor.lut.h
55@begin regExpConstructorTable
56 input regExpConstructorInput None
57 $_ regExpConstructorInput DontEnum
58 multiline regExpConstructorMultiline None
59 $* regExpConstructorMultiline DontEnum
60 lastMatch regExpConstructorLastMatch DontDelete|ReadOnly
61 $& regExpConstructorLastMatch DontDelete|ReadOnly|DontEnum
62 lastParen regExpConstructorLastParen DontDelete|ReadOnly
63 $+ regExpConstructorLastParen DontDelete|ReadOnly|DontEnum
64 leftContext regExpConstructorLeftContext DontDelete|ReadOnly
65 $` regExpConstructorLeftContext DontDelete|ReadOnly|DontEnum
66 rightContext regExpConstructorRightContext DontDelete|ReadOnly
67 $' regExpConstructorRightContext DontDelete|ReadOnly|DontEnum
68 $1 regExpConstructorDollar<1> DontDelete|ReadOnly
69 $2 regExpConstructorDollar<2> DontDelete|ReadOnly
70 $3 regExpConstructorDollar<3> DontDelete|ReadOnly
71 $4 regExpConstructorDollar<4> DontDelete|ReadOnly
72 $5 regExpConstructorDollar<5> DontDelete|ReadOnly
73 $6 regExpConstructorDollar<6> DontDelete|ReadOnly
74 $7 regExpConstructorDollar<7> DontDelete|ReadOnly
75 $8 regExpConstructorDollar<8> DontDelete|ReadOnly
76 $9 regExpConstructorDollar<9> DontDelete|ReadOnly
77@end
78*/
79
80
81static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(JSGlobalObject*, CallFrame*);
82static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(JSGlobalObject*, CallFrame*);
83
84RegExpConstructor::RegExpConstructor(VM& vm, Structure* structure)
85 : InternalFunction(vm, structure, callRegExpConstructor, constructWithRegExpConstructor)
86{
87}
88
89void RegExpConstructor::finishCreation(VM& vm, RegExpPrototype* regExpPrototype, GetterSetter* speciesSymbol)
90{
91 Base::finishCreation(vm, vm.propertyNames->RegExp.string(), NameAdditionMode::WithoutStructureTransition);
92 ASSERT(inherits(vm, info()));
93
94 putDirectWithoutTransition(vm, vm.propertyNames->prototype, regExpPrototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
95 putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(2), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
96
97 putDirectNonIndexAccessorWithoutTransition(vm, vm.propertyNames->speciesSymbol, speciesSymbol, PropertyAttribute::Accessor | PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
98}
99
100template<int N>
101EncodedJSValue regExpConstructorDollar(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
102{
103 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
104 return JSValue::encode(globalObject->regExpGlobalData().getBackref(globalObject, N));
105}
106
107EncodedJSValue regExpConstructorInput(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
108{
109 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
110 return JSValue::encode(globalObject->regExpGlobalData().input());
111}
112
113EncodedJSValue regExpConstructorMultiline(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
114{
115 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
116 return JSValue::encode(jsBoolean(globalObject->regExpGlobalData().multiline()));
117}
118
119EncodedJSValue regExpConstructorLastMatch(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
120{
121 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
122 return JSValue::encode(globalObject->regExpGlobalData().getBackref(globalObject, 0));
123}
124
125EncodedJSValue regExpConstructorLastParen(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
126{
127 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
128 return JSValue::encode(globalObject->regExpGlobalData().getLastParen(globalObject));
129}
130
131EncodedJSValue regExpConstructorLeftContext(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
132{
133 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
134 return JSValue::encode(globalObject->regExpGlobalData().getLeftContext(globalObject));
135}
136
137EncodedJSValue regExpConstructorRightContext(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
138{
139 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
140 return JSValue::encode(globalObject->regExpGlobalData().getRightContext(globalObject));
141}
142
143bool setRegExpConstructorInput(JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue value)
144{
145 VM& vm = globalObject->vm();
146 auto scope = DECLARE_THROW_SCOPE(vm);
147 if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) {
148 auto* string = JSValue::decode(value).toString(globalObject);
149 RETURN_IF_EXCEPTION(scope, { });
150 scope.release();
151 JSGlobalObject* globalObject = constructor->globalObject();
152 globalObject->regExpGlobalData().setInput(globalObject, string);
153 return true;
154 }
155 return false;
156}
157
158bool setRegExpConstructorMultiline(JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue value)
159{
160 VM& vm = globalObject->vm();
161 auto scope = DECLARE_THROW_SCOPE(vm);
162 if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) {
163 bool multiline = JSValue::decode(value).toBoolean(globalObject);
164 RETURN_IF_EXCEPTION(scope, { });
165 scope.release();
166 JSGlobalObject* globalObject = constructor->globalObject();
167 globalObject->regExpGlobalData().setMultiline(multiline);
168 return true;
169 }
170 return false;
171}
172
173inline Structure* getRegExpStructure(JSGlobalObject* globalObject, JSValue newTarget)
174{
175 Structure* structure = globalObject->regExpStructure();
176 if (newTarget != jsUndefined())
177 structure = InternalFunction::createSubclassStructure(globalObject, globalObject->regExpConstructor(), newTarget, structure);
178 return structure;
179}
180
181inline OptionSet<Yarr::Flags> toFlags(JSGlobalObject* globalObject, JSValue flags)
182{
183 VM& vm = globalObject->vm();
184 auto scope = DECLARE_THROW_SCOPE(vm);
185
186 if (flags.isUndefined())
187 return { };
188
189 auto result = Yarr::parseFlags(flags.toWTFString(globalObject));
190 RETURN_IF_EXCEPTION(scope, { });
191 if (!result) {
192 throwSyntaxError(globalObject, scope, "Invalid flags supplied to RegExp constructor."_s);
193 return { };
194 }
195
196 return result.value();
197}
198
199static JSObject* regExpCreate(JSGlobalObject* globalObject, JSValue newTarget, JSValue patternArg, JSValue flagsArg)
200{
201 VM& vm = globalObject->vm();
202 auto scope = DECLARE_THROW_SCOPE(vm);
203
204 String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toWTFString(globalObject);
205 RETURN_IF_EXCEPTION(scope, nullptr);
206
207 auto flags = toFlags(globalObject, flagsArg);
208 RETURN_IF_EXCEPTION(scope, nullptr);
209
210 RegExp* regExp = RegExp::create(vm, pattern, flags);
211 if (UNLIKELY(!regExp->isValid())) {
212 throwException(globalObject, scope, regExp->errorToThrow(globalObject));
213 return nullptr;
214 }
215
216 Structure* structure = getRegExpStructure(globalObject, newTarget);
217 RETURN_IF_EXCEPTION(scope, nullptr);
218 return RegExpObject::create(vm, structure, regExp);
219}
220
221JSObject* constructRegExp(JSGlobalObject* globalObject, const ArgList& args, JSObject* callee, JSValue newTarget)
222{
223 VM& vm = globalObject->vm();
224 auto scope = DECLARE_THROW_SCOPE(vm);
225 JSValue patternArg = args.at(0);
226 JSValue flagsArg = args.at(1);
227
228 bool isPatternRegExp = patternArg.inherits<RegExpObject>(vm);
229 bool constructAsRegexp = isRegExp(vm, globalObject, patternArg);
230 RETURN_IF_EXCEPTION(scope, nullptr);
231
232 if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) {
233 JSValue constructor = patternArg.get(globalObject, vm.propertyNames->constructor);
234 RETURN_IF_EXCEPTION(scope, nullptr);
235 if (callee == constructor) {
236 // We know that patternArg is a object otherwise constructAsRegexp would be false.
237 return patternArg.getObject();
238 }
239 }
240
241 if (isPatternRegExp) {
242 RegExp* regExp = jsCast<RegExpObject*>(patternArg)->regExp();
243 Structure* structure = getRegExpStructure(globalObject, newTarget);
244 RETURN_IF_EXCEPTION(scope, nullptr);
245
246 if (!flagsArg.isUndefined()) {
247 auto flags = toFlags(globalObject, flagsArg);
248 RETURN_IF_EXCEPTION(scope, nullptr);
249
250 regExp = RegExp::create(vm, regExp->pattern(), flags);
251 if (UNLIKELY(!regExp->isValid())) {
252 throwException(globalObject, scope, regExp->errorToThrow(globalObject));
253 return nullptr;
254 }
255 }
256
257 return RegExpObject::create(vm, structure, regExp);
258 }
259
260 if (constructAsRegexp) {
261 JSValue pattern = patternArg.get(globalObject, vm.propertyNames->source);
262 RETURN_IF_EXCEPTION(scope, nullptr);
263 if (flagsArg.isUndefined()) {
264 flagsArg = patternArg.get(globalObject, vm.propertyNames->flags);
265 RETURN_IF_EXCEPTION(scope, nullptr);
266 }
267 patternArg = pattern;
268 }
269
270 RELEASE_AND_RETURN(scope, regExpCreate(globalObject, newTarget, patternArg, flagsArg));
271}
272
273EncodedJSValue JSC_HOST_CALL esSpecRegExpCreate(JSGlobalObject* globalObject, CallFrame* callFrame)
274{
275 JSValue patternArg = callFrame->argument(0);
276 JSValue flagsArg = callFrame->argument(1);
277 return JSValue::encode(regExpCreate(globalObject, jsUndefined(), patternArg, flagsArg));
278}
279
280EncodedJSValue JSC_HOST_CALL esSpecIsRegExp(JSGlobalObject* globalObject, CallFrame* callFrame)
281{
282 VM& vm = globalObject->vm();
283 return JSValue::encode(jsBoolean(isRegExp(vm, globalObject, callFrame->argument(0))));
284}
285
286static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(JSGlobalObject* globalObject, CallFrame* callFrame)
287{
288 ArgList args(callFrame);
289 return JSValue::encode(constructRegExp(globalObject, args, callFrame->jsCallee(), callFrame->newTarget()));
290}
291
292static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(JSGlobalObject* globalObject, CallFrame* callFrame)
293{
294 ArgList args(callFrame);
295 return JSValue::encode(constructRegExp(globalObject, args, callFrame->jsCallee()));
296}
297
298} // namespace JSC
299