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