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(ExecState*, EncodedJSValue, PropertyName);
35static EncodedJSValue regExpConstructorMultiline(ExecState*, EncodedJSValue, PropertyName);
36static EncodedJSValue regExpConstructorLastMatch(ExecState*, EncodedJSValue, PropertyName);
37static EncodedJSValue regExpConstructorLastParen(ExecState*, EncodedJSValue, PropertyName);
38static EncodedJSValue regExpConstructorLeftContext(ExecState*, EncodedJSValue, PropertyName);
39static EncodedJSValue regExpConstructorRightContext(ExecState*, EncodedJSValue, PropertyName);
40template<int N>
41static EncodedJSValue regExpConstructorDollar(ExecState*, EncodedJSValue, PropertyName);
42
43static bool setRegExpConstructorInput(ExecState*, EncodedJSValue, EncodedJSValue);
44static bool setRegExpConstructorMultiline(ExecState*, 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(ExecState*);
82static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState*);
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(), NameVisibility::Visible, 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(ExecState* exec, EncodedJSValue thisValue, PropertyName)
102{
103 VM& vm = exec->vm();
104 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(vm);
105 return JSValue::encode(globalObject->regExpGlobalData().getBackref(exec, globalObject, N));
106}
107
108EncodedJSValue regExpConstructorInput(ExecState* exec, EncodedJSValue thisValue, PropertyName)
109{
110 VM& vm = exec->vm();
111 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(vm);
112 return JSValue::encode(globalObject->regExpGlobalData().input());
113}
114
115EncodedJSValue regExpConstructorMultiline(ExecState* exec, EncodedJSValue thisValue, PropertyName)
116{
117 VM& vm = exec->vm();
118 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(vm);
119 return JSValue::encode(jsBoolean(globalObject->regExpGlobalData().multiline()));
120}
121
122EncodedJSValue regExpConstructorLastMatch(ExecState* exec, EncodedJSValue thisValue, PropertyName)
123{
124 VM& vm = exec->vm();
125 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(vm);
126 return JSValue::encode(globalObject->regExpGlobalData().getBackref(exec, globalObject, 0));
127}
128
129EncodedJSValue regExpConstructorLastParen(ExecState* exec, EncodedJSValue thisValue, PropertyName)
130{
131 VM& vm = exec->vm();
132 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(vm);
133 return JSValue::encode(globalObject->regExpGlobalData().getLastParen(exec, globalObject));
134}
135
136EncodedJSValue regExpConstructorLeftContext(ExecState* exec, EncodedJSValue thisValue, PropertyName)
137{
138 VM& vm = exec->vm();
139 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(vm);
140 return JSValue::encode(globalObject->regExpGlobalData().getLeftContext(exec, globalObject));
141}
142
143EncodedJSValue regExpConstructorRightContext(ExecState* exec, EncodedJSValue thisValue, PropertyName)
144{
145 VM& vm = exec->vm();
146 JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(vm);
147 return JSValue::encode(globalObject->regExpGlobalData().getRightContext(exec, globalObject));
148}
149
150bool setRegExpConstructorInput(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
151{
152 VM& vm = exec->vm();
153 auto scope = DECLARE_THROW_SCOPE(vm);
154 if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) {
155 auto* string = JSValue::decode(value).toString(exec);
156 RETURN_IF_EXCEPTION(scope, { });
157 scope.release();
158 JSGlobalObject* globalObject = constructor->globalObject(vm);
159 globalObject->regExpGlobalData().setInput(exec, globalObject, string);
160 return true;
161 }
162 return false;
163}
164
165bool setRegExpConstructorMultiline(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
166{
167 VM& vm = exec->vm();
168 auto scope = DECLARE_THROW_SCOPE(vm);
169 if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) {
170 bool multiline = JSValue::decode(value).toBoolean(exec);
171 RETURN_IF_EXCEPTION(scope, { });
172 scope.release();
173 JSGlobalObject* globalObject = constructor->globalObject(vm);
174 globalObject->regExpGlobalData().setMultiline(multiline);
175 return true;
176 }
177 return false;
178}
179
180inline Structure* getRegExpStructure(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget)
181{
182 Structure* structure = globalObject->regExpStructure();
183 if (newTarget != jsUndefined())
184 structure = InternalFunction::createSubclassStructure(exec, newTarget, structure);
185 return structure;
186}
187
188inline OptionSet<Yarr::Flags> toFlags(ExecState* exec, JSValue flags)
189{
190 VM& vm = exec->vm();
191 auto scope = DECLARE_THROW_SCOPE(vm);
192
193 if (flags.isUndefined())
194 return { };
195
196 auto result = Yarr::parseFlags(flags.toWTFString(exec));
197 RETURN_IF_EXCEPTION(scope, { });
198 if (!result) {
199 throwSyntaxError(exec, scope, "Invalid flags supplied to RegExp constructor."_s);
200 return { };
201 }
202
203 return result.value();
204}
205
206static JSObject* regExpCreate(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget, JSValue patternArg, JSValue flagsArg)
207{
208 VM& vm = exec->vm();
209 auto scope = DECLARE_THROW_SCOPE(vm);
210
211 String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toWTFString(exec);
212 RETURN_IF_EXCEPTION(scope, nullptr);
213
214 auto flags = toFlags(exec, flagsArg);
215 RETURN_IF_EXCEPTION(scope, nullptr);
216
217 RegExp* regExp = RegExp::create(vm, pattern, flags);
218 if (UNLIKELY(!regExp->isValid())) {
219 throwException(exec, scope, regExp->errorToThrow(exec));
220 return nullptr;
221 }
222
223 Structure* structure = getRegExpStructure(exec, globalObject, newTarget);
224 RETURN_IF_EXCEPTION(scope, nullptr);
225 return RegExpObject::create(vm, structure, regExp);
226}
227
228JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, JSObject* callee, JSValue newTarget)
229{
230 VM& vm = exec->vm();
231 auto scope = DECLARE_THROW_SCOPE(vm);
232 JSValue patternArg = args.at(0);
233 JSValue flagsArg = args.at(1);
234
235 bool isPatternRegExp = patternArg.inherits<RegExpObject>(vm);
236 bool constructAsRegexp = isRegExp(vm, exec, patternArg);
237 RETURN_IF_EXCEPTION(scope, nullptr);
238
239 if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) {
240 JSValue constructor = patternArg.get(exec, vm.propertyNames->constructor);
241 RETURN_IF_EXCEPTION(scope, nullptr);
242 if (callee == constructor) {
243 // We know that patternArg is a object otherwise constructAsRegexp would be false.
244 return patternArg.getObject();
245 }
246 }
247
248 if (isPatternRegExp) {
249 RegExp* regExp = jsCast<RegExpObject*>(patternArg)->regExp();
250 Structure* structure = getRegExpStructure(exec, globalObject, newTarget);
251 RETURN_IF_EXCEPTION(scope, nullptr);
252
253 if (!flagsArg.isUndefined()) {
254 auto flags = toFlags(exec, flagsArg);
255 RETURN_IF_EXCEPTION(scope, nullptr);
256
257 regExp = RegExp::create(vm, regExp->pattern(), flags);
258 if (UNLIKELY(!regExp->isValid())) {
259 throwException(exec, scope, regExp->errorToThrow(exec));
260 return nullptr;
261 }
262 }
263
264 return RegExpObject::create(vm, structure, regExp);
265 }
266
267 if (constructAsRegexp) {
268 JSValue pattern = patternArg.get(exec, vm.propertyNames->source);
269 RETURN_IF_EXCEPTION(scope, nullptr);
270 if (flagsArg.isUndefined()) {
271 flagsArg = patternArg.get(exec, vm.propertyNames->flags);
272 RETURN_IF_EXCEPTION(scope, nullptr);
273 }
274 patternArg = pattern;
275 }
276
277 RELEASE_AND_RETURN(scope, regExpCreate(exec, globalObject, newTarget, patternArg, flagsArg));
278}
279
280EncodedJSValue JSC_HOST_CALL esSpecRegExpCreate(ExecState* exec)
281{
282 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
283 JSValue patternArg = exec->argument(0);
284 JSValue flagsArg = exec->argument(1);
285 return JSValue::encode(regExpCreate(exec, globalObject, jsUndefined(), patternArg, flagsArg));
286}
287
288static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec)
289{
290 ArgList args(exec);
291 return JSValue::encode(constructRegExp(exec, jsCast<InternalFunction*>(exec->jsCallee())->globalObject(exec->vm()), args, exec->jsCallee(), exec->newTarget()));
292}
293
294static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec)
295{
296 ArgList args(exec);
297 return JSValue::encode(constructRegExp(exec, jsCast<InternalFunction*>(exec->jsCallee())->globalObject(exec->vm()), args, exec->jsCallee()));
298}
299
300} // namespace JSC
301