Change ProxyObject.[[Get]] not to use custom accessor
[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     if (flags.isUndefined())
214         return NoFlags;
215     JSString* flagsString = flags.toString(exec);
216     if (!flagsString) {
217         ASSERT(exec->hadException());
218         return InvalidFlags;
219     }
220
221     RegExpFlags result = regExpFlags(flagsString->value(exec));
222     if (exec->hadException())
223         return InvalidFlags;
224     if (result == InvalidFlags)
225         throwSyntaxError(exec, 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     String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toString(exec)->value(exec);
233     if (exec->hadException())
234         return nullptr;
235
236     RegExpFlags flags = toFlags(exec, flagsArg);
237     if (flags == InvalidFlags)
238         return nullptr;
239
240     RegExp* regExp = RegExp::create(vm, pattern, flags);
241     if (!regExp->isValid())
242         return vm.throwException(exec, createSyntaxError(exec, regExp->errorMessage()));
243
244     Structure* structure = getRegExpStructure(exec, globalObject, newTarget);
245     if (vm.exception())
246         return nullptr;
247     return RegExpObject::create(vm, structure, regExp);
248 }
249
250 JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args,  JSObject* callee, JSValue newTarget)
251 {
252     VM& vm = exec->vm();
253     JSValue patternArg = args.at(0);
254     JSValue flagsArg = args.at(1);
255
256     bool isPatternRegExp = patternArg.inherits(RegExpObject::info());
257     bool constructAsRegexp = isRegExp(vm, exec, patternArg);
258
259     if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) {
260         JSValue constructor = patternArg.get(exec, vm.propertyNames->constructor);
261         if (vm.exception())
262             return 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         if (exec->hadException())
273             return nullptr;
274
275         if (!flagsArg.isUndefined()) {
276             RegExpFlags flags = toFlags(exec, flagsArg);
277             if (flags == InvalidFlags)
278                 return nullptr;
279             regExp = RegExp::create(vm, regExp->pattern(), flags);
280         }
281
282         return RegExpObject::create(exec->vm(), structure, regExp);
283     }
284
285     if (constructAsRegexp) {
286         JSValue pattern = patternArg.get(exec, vm.propertyNames->source);
287         if (flagsArg.isUndefined())
288             flagsArg = patternArg.get(exec, vm.propertyNames->flags);
289         patternArg = pattern;
290     }
291
292     return regExpCreate(exec, globalObject, newTarget, patternArg, flagsArg);
293 }
294
295 EncodedJSValue JSC_HOST_CALL esSpecRegExpCreate(ExecState* exec)
296 {
297     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
298     JSValue patternArg = exec->argument(0);
299     JSValue flagsArg = exec->argument(1);
300     return JSValue::encode(regExpCreate(exec, globalObject, jsUndefined(), patternArg, flagsArg));
301 }
302
303 static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec)
304 {
305     ArgList args(exec);
306     return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, exec->callee(), exec->newTarget()));
307 }
308
309 ConstructType RegExpConstructor::getConstructData(JSCell*, ConstructData& constructData)
310 {
311     constructData.native.function = constructWithRegExpConstructor;
312     return ConstructType::Host;
313 }
314
315 static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec)
316 {
317     ArgList args(exec);
318     return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, exec->callee()));
319 }
320
321 CallType RegExpConstructor::getCallData(JSCell*, CallData& callData)
322 {
323     callData.native.function = callRegExpConstructor;
324     return CallType::Host;
325 }
326
327 } // namespace JSC