[JSC] Add missing exception checks revealed by newly added exception checks, follow...
[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 "RegExpGlobalDataInlines.h"
28 #include "RegExpPrototype.h"
29 #include "StructureInlines.h"
30 #include "YarrFlags.h"
31
32 namespace JSC {
33
34 static EncodedJSValue regExpConstructorInput(ExecState*, EncodedJSValue, PropertyName);
35 static EncodedJSValue regExpConstructorMultiline(ExecState*, EncodedJSValue, PropertyName);
36 static EncodedJSValue regExpConstructorLastMatch(ExecState*, EncodedJSValue, PropertyName);
37 static EncodedJSValue regExpConstructorLastParen(ExecState*, EncodedJSValue, PropertyName);
38 static EncodedJSValue regExpConstructorLeftContext(ExecState*, EncodedJSValue, PropertyName);
39 static EncodedJSValue regExpConstructorRightContext(ExecState*, EncodedJSValue, PropertyName);
40 template<int N>
41 static EncodedJSValue regExpConstructorDollar(ExecState*, EncodedJSValue, PropertyName);
42
43 static bool setRegExpConstructorInput(ExecState*, EncodedJSValue, EncodedJSValue);
44 static bool setRegExpConstructorMultiline(ExecState*, 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(ExecState*);
82 static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState*);
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(), 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
100 template<int N>
101 EncodedJSValue 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
108 EncodedJSValue 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
115 EncodedJSValue 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
122 EncodedJSValue 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
129 EncodedJSValue 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
136 EncodedJSValue 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
143 EncodedJSValue 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
150 bool 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
165 bool 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
180 inline 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
188 inline 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
206 static 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
228 JSObject* 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
280 EncodedJSValue 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
288 static 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
294 static 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