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