REGRESSION: (r256619) [ Mac wk1 Release ] legacy-animation-engine/fast/animation...
[WebKit-https.git] / Source / JavaScriptCore / runtime / RegExpConstructor.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
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
32 namespace JSC {
33
34 static EncodedJSValue regExpConstructorInput(JSGlobalObject*, EncodedJSValue, PropertyName);
35 static EncodedJSValue regExpConstructorMultiline(JSGlobalObject*, EncodedJSValue, PropertyName);
36 static EncodedJSValue regExpConstructorLastMatch(JSGlobalObject*, EncodedJSValue, PropertyName);
37 static EncodedJSValue regExpConstructorLastParen(JSGlobalObject*, EncodedJSValue, PropertyName);
38 static EncodedJSValue regExpConstructorLeftContext(JSGlobalObject*, EncodedJSValue, PropertyName);
39 static EncodedJSValue regExpConstructorRightContext(JSGlobalObject*, EncodedJSValue, PropertyName);
40 template<int N>
41 static EncodedJSValue regExpConstructorDollar(JSGlobalObject*, EncodedJSValue, PropertyName);
42
43 static bool setRegExpConstructorInput(JSGlobalObject*, EncodedJSValue, EncodedJSValue);
44 static bool setRegExpConstructorMultiline(JSGlobalObject*, EncodedJSValue, EncodedJSValue);
45
46 } // namespace JSC
47
48 #include "RegExpConstructor.lut.h"
49
50 namespace JSC {
51
52 const 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
81 static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(JSGlobalObject*, CallFrame*);
82 static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(JSGlobalObject*, CallFrame*);
83
84 RegExpConstructor::RegExpConstructor(VM& vm, Structure* structure)
85     : InternalFunction(vm, structure, callRegExpConstructor, constructWithRegExpConstructor)
86 {
87 }
88
89 void 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
100 template<int N>
101 EncodedJSValue 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
107 EncodedJSValue regExpConstructorInput(JSGlobalObject*, EncodedJSValue thisValue, PropertyName)
108 {
109     JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject();
110     return JSValue::encode(globalObject->regExpGlobalData().input());
111 }
112
113 EncodedJSValue 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
119 EncodedJSValue 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
125 EncodedJSValue 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
131 EncodedJSValue 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
137 EncodedJSValue 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
143 bool 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
158 bool 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
173 inline 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
181 inline 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
199 static 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
221 JSObject* 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
273 EncodedJSValue 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
280 EncodedJSValue 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
286 static 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
292 static 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