432283f0ad9cbfec650e44f33bd02efeb650e66e
[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 "RegExpPrototype.h"
29 #include "StructureInlines.h"
30
31 namespace JSC {
32
33 static EncodedJSValue regExpConstructorInput(ExecState*, EncodedJSValue, PropertyName);
34 static EncodedJSValue regExpConstructorMultiline(ExecState*, EncodedJSValue, PropertyName);
35 static EncodedJSValue regExpConstructorLastMatch(ExecState*, EncodedJSValue, PropertyName);
36 static EncodedJSValue regExpConstructorLastParen(ExecState*, EncodedJSValue, PropertyName);
37 static EncodedJSValue regExpConstructorLeftContext(ExecState*, EncodedJSValue, PropertyName);
38 static EncodedJSValue regExpConstructorRightContext(ExecState*, EncodedJSValue, PropertyName);
39 template<int N>
40 static EncodedJSValue regExpConstructorDollar(ExecState*, EncodedJSValue, PropertyName);
41
42 static bool setRegExpConstructorInput(ExecState*, EncodedJSValue, EncodedJSValue);
43 static bool setRegExpConstructorMultiline(ExecState*, EncodedJSValue, EncodedJSValue);
44
45 } // namespace JSC
46
47 #include "RegExpConstructor.lut.h"
48
49 namespace JSC {
50
51 const ClassInfo RegExpConstructor::s_info = { "Function", &InternalFunction::s_info, &regExpConstructorTable, CREATE_METHOD_TABLE(RegExpConstructor) };
52
53 /* Source for RegExpConstructor.lut.h
54 @begin regExpConstructorTable
55     input           regExpConstructorInput          None
56     $_              regExpConstructorInput          DontEnum
57     multiline       regExpConstructorMultiline      None
58     $*              regExpConstructorMultiline      DontEnum
59     lastMatch       regExpConstructorLastMatch      DontDelete|ReadOnly
60     $&              regExpConstructorLastMatch      DontDelete|ReadOnly|DontEnum
61     lastParen       regExpConstructorLastParen      DontDelete|ReadOnly
62     $+              regExpConstructorLastParen      DontDelete|ReadOnly|DontEnum
63     leftContext     regExpConstructorLeftContext    DontDelete|ReadOnly
64     $`              regExpConstructorLeftContext    DontDelete|ReadOnly|DontEnum
65     rightContext    regExpConstructorRightContext   DontDelete|ReadOnly
66     $'              regExpConstructorRightContext   DontDelete|ReadOnly|DontEnum
67     $1              regExpConstructorDollar<1>      DontDelete|ReadOnly
68     $2              regExpConstructorDollar<2>      DontDelete|ReadOnly
69     $3              regExpConstructorDollar<3>      DontDelete|ReadOnly
70     $4              regExpConstructorDollar<4>      DontDelete|ReadOnly
71     $5              regExpConstructorDollar<5>      DontDelete|ReadOnly
72     $6              regExpConstructorDollar<6>      DontDelete|ReadOnly
73     $7              regExpConstructorDollar<7>      DontDelete|ReadOnly
74     $8              regExpConstructorDollar<8>      DontDelete|ReadOnly
75     $9              regExpConstructorDollar<9>      DontDelete|ReadOnly
76 @end
77 */
78
79 RegExpConstructor::RegExpConstructor(VM& vm, Structure* structure, RegExpPrototype* regExpPrototype)
80     : InternalFunction(vm, structure)
81     , m_cachedResult(vm, this, regExpPrototype->emptyRegExp())
82     , m_multiline(false)
83 {
84 }
85
86 void RegExpConstructor::finishCreation(VM& vm, RegExpPrototype* regExpPrototype, GetterSetter* speciesSymbol)
87 {
88     Base::finishCreation(vm, ASCIILiteral("RegExp"));
89     ASSERT(inherits(vm, info()));
90
91     putDirectWithoutTransition(vm, vm.propertyNames->prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly);
92
93     // no. of arguments for constructor
94     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(2), ReadOnly | DontDelete | DontEnum);
95
96     putDirectNonIndexAccessor(vm, vm.propertyNames->speciesSymbol, speciesSymbol, Accessor | ReadOnly | DontEnum);
97 }
98
99 void RegExpConstructor::destroy(JSCell* cell)
100 {
101     static_cast<RegExpConstructor*>(cell)->RegExpConstructor::~RegExpConstructor();
102 }
103
104 void RegExpConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor)
105 {
106     RegExpConstructor* thisObject = jsCast<RegExpConstructor*>(cell);
107     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
108     Base::visitChildren(thisObject, visitor);
109     thisObject->m_cachedResult.visitChildren(visitor);
110 }
111
112 JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i)
113 {
114     JSArray* array = m_cachedResult.lastResult(exec, this);
115
116     if (i < array->length()) {
117         JSValue result = JSValue(array).get(exec, i);
118         ASSERT(result.isString() || result.isUndefined());
119         if (!result.isUndefined())
120             return result;
121     }
122     return jsEmptyString(exec);
123 }
124
125 JSValue RegExpConstructor::getLastParen(ExecState* exec)
126 {
127     JSArray* array = m_cachedResult.lastResult(exec, this);
128     unsigned length = array->length();
129     if (length > 1) {
130         JSValue result = JSValue(array).get(exec, length - 1);
131         ASSERT(result.isString() || result.isUndefined());
132         if (!result.isUndefined())
133             return result;
134     }
135     return jsEmptyString(exec);
136 }
137
138 JSValue RegExpConstructor::getLeftContext(ExecState* exec)
139 {
140     return m_cachedResult.leftContext(exec, this);
141 }
142
143 JSValue RegExpConstructor::getRightContext(ExecState* exec)
144 {
145     return m_cachedResult.rightContext(exec, this);
146 }
147
148 template<int N>
149 EncodedJSValue regExpConstructorDollar(ExecState* exec, EncodedJSValue thisValue, PropertyName)
150 {
151     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getBackref(exec, N));
152 }
153
154 EncodedJSValue regExpConstructorInput(ExecState*, EncodedJSValue thisValue, PropertyName)
155 {
156     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->input());
157 }
158
159 EncodedJSValue regExpConstructorMultiline(ExecState*, EncodedJSValue thisValue, PropertyName)
160 {
161     return JSValue::encode(jsBoolean(asRegExpConstructor(JSValue::decode(thisValue))->multiline()));
162 }
163
164 EncodedJSValue regExpConstructorLastMatch(ExecState* exec, EncodedJSValue thisValue, PropertyName)
165 {
166     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getBackref(exec, 0));
167 }
168
169 EncodedJSValue regExpConstructorLastParen(ExecState* exec, EncodedJSValue thisValue, PropertyName)
170 {
171     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getLastParen(exec));
172 }
173
174 EncodedJSValue regExpConstructorLeftContext(ExecState* exec, EncodedJSValue thisValue, PropertyName)
175 {
176     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getLeftContext(exec));
177 }
178
179 EncodedJSValue regExpConstructorRightContext(ExecState* exec, EncodedJSValue thisValue, PropertyName)
180 {
181     return JSValue::encode(asRegExpConstructor(JSValue::decode(thisValue))->getRightContext(exec));
182 }
183
184 bool setRegExpConstructorInput(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
185 {
186     if (auto constructor = jsDynamicCast<RegExpConstructor*>(exec->vm(), JSValue::decode(thisValue))) {
187         constructor->setInput(exec, JSValue::decode(value).toString(exec));
188         return true;
189     }
190     return false;
191 }
192
193 bool setRegExpConstructorMultiline(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
194 {
195     if (auto constructor = jsDynamicCast<RegExpConstructor*>(exec->vm(), JSValue::decode(thisValue))) {
196         constructor->setMultiline(JSValue::decode(value).toBoolean(exec));
197         return true;
198     }
199     return false;
200 }
201
202 inline Structure* getRegExpStructure(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget)
203 {
204     Structure* structure = globalObject->regExpStructure();
205     if (newTarget != jsUndefined())
206         structure = InternalFunction::createSubclassStructure(exec, newTarget, structure);
207     return structure;
208 }
209
210 inline RegExpFlags toFlags(ExecState* exec, JSValue flags)
211 {
212     VM& vm = exec->vm();
213     auto scope = DECLARE_THROW_SCOPE(vm);
214
215     if (flags.isUndefined())
216         return NoFlags;
217     JSString* flagsString = flags.toStringOrNull(exec);
218     ASSERT(!!scope.exception() == !flagsString);
219     if (UNLIKELY(!flagsString))
220         return InvalidFlags;
221
222     RegExpFlags result = regExpFlags(flagsString->value(exec));
223     RETURN_IF_EXCEPTION(scope, InvalidFlags);
224     if (result == InvalidFlags)
225         throwSyntaxError(exec, scope, ASCIILiteral("Invalid flags supplied to RegExp constructor."));
226     return result;
227 }
228
229 static JSObject* regExpCreate(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget, JSValue patternArg, JSValue flagsArg)
230 {
231     VM& vm = exec->vm();
232     auto scope = DECLARE_THROW_SCOPE(vm);
233
234     String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toWTFString(exec);
235     RETURN_IF_EXCEPTION(scope, nullptr);
236
237     RegExpFlags flags = toFlags(exec, flagsArg);
238     ASSERT(!!scope.exception() == (flags == InvalidFlags));
239     if (UNLIKELY(flags == InvalidFlags))
240         return nullptr;
241
242     RegExp* regExp = RegExp::create(vm, pattern, flags);
243     if (!regExp->isValid())
244         return throwException(exec, scope, createSyntaxError(exec, regExp->errorMessage()));
245
246     Structure* structure = getRegExpStructure(exec, globalObject, newTarget);
247     RETURN_IF_EXCEPTION(scope, nullptr);
248     return RegExpObject::create(vm, structure, regExp);
249 }
250
251 JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args,  JSObject* callee, JSValue newTarget)
252 {
253     VM& vm = exec->vm();
254     auto scope = DECLARE_THROW_SCOPE(vm);
255     JSValue patternArg = args.at(0);
256     JSValue flagsArg = args.at(1);
257
258     bool isPatternRegExp = patternArg.inherits(vm, RegExpObject::info());
259     bool constructAsRegexp = isRegExp(vm, exec, patternArg);
260     RETURN_IF_EXCEPTION(scope, nullptr);
261
262     if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) {
263         JSValue constructor = patternArg.get(exec, vm.propertyNames->constructor);
264         RETURN_IF_EXCEPTION(scope, nullptr);
265         if (callee == constructor) {
266             // We know that patternArg is a object otherwise constructAsRegexp would be false.
267             return patternArg.getObject();
268         }
269     }
270
271     if (isPatternRegExp) {
272         RegExp* regExp = jsCast<RegExpObject*>(patternArg)->regExp();
273         Structure* structure = getRegExpStructure(exec, globalObject, newTarget);
274         RETURN_IF_EXCEPTION(scope, nullptr);
275
276         if (!flagsArg.isUndefined()) {
277             RegExpFlags flags = toFlags(exec, flagsArg);
278             ASSERT(!!scope.exception() == (flags == InvalidFlags));
279             if (flags == InvalidFlags)
280                 return nullptr;
281             regExp = RegExp::create(vm, regExp->pattern(), flags);
282         }
283
284         return RegExpObject::create(vm, structure, regExp);
285     }
286
287     if (constructAsRegexp) {
288         JSValue pattern = patternArg.get(exec, vm.propertyNames->source);
289         RETURN_IF_EXCEPTION(scope, nullptr);
290         if (flagsArg.isUndefined()) {
291             flagsArg = patternArg.get(exec, vm.propertyNames->flags);
292             RETURN_IF_EXCEPTION(scope, nullptr);
293         }
294         patternArg = pattern;
295     }
296
297     scope.release();
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->jsCallee())->globalObject(), args, exec->jsCallee(), 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->jsCallee())->globalObject(), args, exec->jsCallee()));
325 }
326
327 CallType RegExpConstructor::getCallData(JSCell*, CallData& callData)
328 {
329     callData.native.function = callRegExpConstructor;
330     return CallType::Host;
331 }
332
333 } // namespace JSC