Add CatchScope and force all exception checks to be via ThrowScope or CatchScope.
[WebKit-https.git] / Source / JavaScriptCore / runtime / RegExpConstructor.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003, 2007-2008, 2016 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 "RegExpMatchesArray.h"
29 #include "RegExpPrototype.h"
30 #include "StructureInlines.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, 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 RegExpConstructor::RegExpConstructor(VM& vm, Structure* structure, RegExpPrototype* regExpPrototype)
81     : InternalFunction(vm, structure)
82     , m_cachedResult(vm, this, regExpPrototype->emptyRegExp())
83     , m_multiline(false)
84 {
85 }
86
87 void RegExpConstructor::finishCreation(VM& vm, RegExpPrototype* regExpPrototype, GetterSetter* speciesSymbol)
88 {
89     Base::finishCreation(vm, ASCIILiteral("RegExp"));
90     ASSERT(inherits(info()));
91
92     putDirectWithoutTransition(vm, vm.propertyNames->prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly);
93
94     // no. of arguments for constructor
95     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(2), ReadOnly | DontDelete | DontEnum);
96
97     putDirectNonIndexAccessor(vm, vm.propertyNames->speciesSymbol, speciesSymbol, Accessor | ReadOnly | DontEnum);
98 }
99
100 void RegExpConstructor::destroy(JSCell* cell)
101 {
102     static_cast<RegExpConstructor*>(cell)->RegExpConstructor::~RegExpConstructor();
103 }
104
105 void RegExpConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor)
106 {
107     RegExpConstructor* thisObject = jsCast<RegExpConstructor*>(cell);
108     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
109     Base::visitChildren(thisObject, visitor);
110     thisObject->m_cachedResult.visitChildren(visitor);
111 }
112
113 JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i)
114 {
115     JSArray* array = m_cachedResult.lastResult(exec, this);
116
117     if (i < array->length()) {
118         JSValue result = JSValue(array).get(exec, i);
119         ASSERT(result.isString() || result.isUndefined());
120         if (!result.isUndefined())
121             return result;
122     }
123     return jsEmptyString(exec);
124 }
125
126 JSValue RegExpConstructor::getLastParen(ExecState* exec)
127 {
128     JSArray* array = m_cachedResult.lastResult(exec, this);
129     unsigned length = array->length();
130     if (length > 1) {
131         JSValue result = JSValue(array).get(exec, length - 1);
132         ASSERT(result.isString() || result.isUndefined());
133         if (!result.isUndefined())
134             return result;
135     }
136     return jsEmptyString(exec);
137 }
138
139 JSValue RegExpConstructor::getLeftContext(ExecState* exec)
140 {
141     return m_cachedResult.leftContext(exec, this);
142 }
143
144 JSValue RegExpConstructor::getRightContext(ExecState* exec)
145 {
146     return m_cachedResult.rightContext(exec, this);
147 }
148
149 template<int N>
150 EncodedJSValue regExpConstructorDollar(ExecState* exec, EncodedJSValue thisValue, PropertyName)
151 {
152     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getBackref(exec, N));
153 }
154
155 EncodedJSValue regExpConstructorInput(ExecState*, EncodedJSValue thisValue, PropertyName)
156 {
157     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->input());
158 }
159
160 EncodedJSValue regExpConstructorMultiline(ExecState*, EncodedJSValue thisValue, PropertyName)
161 {
162     return JSValue::encode(jsBoolean(asRegExpConstructor(JSValue::decode(thisValue))->multiline()));
163 }
164
165 EncodedJSValue regExpConstructorLastMatch(ExecState* exec, EncodedJSValue thisValue, PropertyName)
166 {
167     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getBackref(exec, 0));
168 }
169
170 EncodedJSValue regExpConstructorLastParen(ExecState* exec, EncodedJSValue thisValue, PropertyName)
171 {
172     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getLastParen(exec));
173 }
174
175 EncodedJSValue regExpConstructorLeftContext(ExecState* exec, EncodedJSValue thisValue, PropertyName)
176 {
177     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getLeftContext(exec));
178 }
179
180 EncodedJSValue regExpConstructorRightContext(ExecState* exec, EncodedJSValue thisValue, PropertyName)
181 {
182     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getRightContext(exec));
183 }
184
185 bool setRegExpConstructorInput(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
186 {
187     if (auto constructor = jsDynamicCast<RegExpConstructor*>(JSValue::decode(thisValue))) {
188         constructor->setInput(exec, JSValue::decode(value).toString(exec));
189         return true;
190     }
191     return false;
192 }
193
194 bool setRegExpConstructorMultiline(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
195 {
196     if (auto constructor = jsDynamicCast<RegExpConstructor*>(JSValue::decode(thisValue))) {
197         constructor->setMultiline(JSValue::decode(value).toBoolean(exec));
198         return true;
199     }
200     return false;
201 }
202
203 inline Structure* getRegExpStructure(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget)
204 {
205     Structure* structure = globalObject->regExpStructure();
206     if (newTarget != jsUndefined())
207         structure = InternalFunction::createSubclassStructure(exec, newTarget, structure);
208     return structure;
209 }
210
211 inline RegExpFlags toFlags(ExecState* exec, JSValue flags)
212 {
213     VM& vm = exec->vm();
214     auto scope = DECLARE_THROW_SCOPE(vm);
215
216     if (flags.isUndefined())
217         return NoFlags;
218     JSString* flagsString = flags.toString(exec);
219     ASSERT(scope.exception() || flagsString);
220     if (!flagsString) {
221         return InvalidFlags;
222     }
223
224     RegExpFlags result = regExpFlags(flagsString->value(exec));
225     if (UNLIKELY(scope.exception()))
226         return InvalidFlags;
227     if (result == InvalidFlags)
228         throwSyntaxError(exec, scope, ASCIILiteral("Invalid flags supplied to RegExp constructor."));
229     return result;
230 }
231
232 static JSObject* regExpCreate(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget, JSValue patternArg, JSValue flagsArg)
233 {
234     VM& vm = exec->vm();
235     auto scope = DECLARE_THROW_SCOPE(vm);
236
237     String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toString(exec)->value(exec);
238     if (UNLIKELY(scope.exception()))
239         return nullptr;
240
241     RegExpFlags flags = toFlags(exec, flagsArg);
242     if (flags == InvalidFlags)
243         return nullptr;
244
245     RegExp* regExp = RegExp::create(vm, pattern, flags);
246     if (!regExp->isValid())
247         return throwException(exec, scope, createSyntaxError(exec, regExp->errorMessage()));
248
249     Structure* structure = getRegExpStructure(exec, globalObject, newTarget);
250     if (UNLIKELY(scope.exception()))
251         return nullptr;
252     return RegExpObject::create(vm, structure, regExp);
253 }
254
255 JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args,  JSObject* callee, JSValue newTarget)
256 {
257     VM& vm = exec->vm();
258     auto scope = DECLARE_THROW_SCOPE(vm);
259     JSValue patternArg = args.at(0);
260     JSValue flagsArg = args.at(1);
261
262     bool isPatternRegExp = patternArg.inherits(RegExpObject::info());
263     bool constructAsRegexp = isRegExp(vm, exec, patternArg);
264
265     if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) {
266         JSValue constructor = patternArg.get(exec, vm.propertyNames->constructor);
267         if (UNLIKELY(scope.exception()))
268             return nullptr;
269         if (callee == constructor) {
270             // We know that patternArg is a object otherwise constructAsRegexp would be false.
271             return patternArg.getObject();
272         }
273     }
274
275     if (isPatternRegExp) {
276         RegExp* regExp = jsCast<RegExpObject*>(patternArg)->regExp();
277         Structure* structure = getRegExpStructure(exec, globalObject, newTarget);
278         if (UNLIKELY(scope.exception()))
279             return nullptr;
280
281         if (!flagsArg.isUndefined()) {
282             RegExpFlags flags = toFlags(exec, flagsArg);
283             if (flags == InvalidFlags)
284                 return nullptr;
285             regExp = RegExp::create(vm, regExp->pattern(), flags);
286         }
287
288         return RegExpObject::create(exec->vm(), structure, regExp);
289     }
290
291     if (constructAsRegexp) {
292         JSValue pattern = patternArg.get(exec, vm.propertyNames->source);
293         if (flagsArg.isUndefined())
294             flagsArg = patternArg.get(exec, vm.propertyNames->flags);
295         patternArg = pattern;
296     }
297
298     return regExpCreate(exec, globalObject, newTarget, patternArg, flagsArg);
299 }
300
301 EncodedJSValue JSC_HOST_CALL esSpecRegExpCreate(ExecState* exec)
302 {
303     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
304     JSValue patternArg = exec->argument(0);
305     JSValue flagsArg = exec->argument(1);
306     return JSValue::encode(regExpCreate(exec, globalObject, jsUndefined(), patternArg, flagsArg));
307 }
308
309 static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec)
310 {
311     ArgList args(exec);
312     return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, exec->callee(), exec->newTarget()));
313 }
314
315 ConstructType RegExpConstructor::getConstructData(JSCell*, ConstructData& constructData)
316 {
317     constructData.native.function = constructWithRegExpConstructor;
318     return ConstructType::Host;
319 }
320
321 static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec)
322 {
323     ArgList args(exec);
324     return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, exec->callee()));
325 }
326
327 CallType RegExpConstructor::getCallData(JSCell*, CallData& callData)
328 {
329     callData.native.function = callRegExpConstructor;
330     return CallType::Host;
331 }
332
333 } // namespace JSC