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