1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten ([email protected]) |
3 | * Copyright (C) 2003-2019 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 "YarrFlags.h" |
31 | |
32 | namespace JSC { |
33 | |
34 | static EncodedJSValue regExpConstructorInput(JSGlobalObject*, EncodedJSValue, PropertyName); |
35 | static EncodedJSValue regExpConstructorMultiline(JSGlobalObject*, EncodedJSValue, PropertyName); |
36 | static EncodedJSValue regExpConstructorLastMatch(JSGlobalObject*, EncodedJSValue, PropertyName); |
37 | static EncodedJSValue regExpConstructorLastParen(JSGlobalObject*, EncodedJSValue, PropertyName); |
38 | static EncodedJSValue regExpConstructorLeftContext(JSGlobalObject*, EncodedJSValue, PropertyName); |
39 | static EncodedJSValue regExpConstructorRightContext(JSGlobalObject*, EncodedJSValue, PropertyName); |
40 | template<int N> |
41 | static EncodedJSValue regExpConstructorDollar(JSGlobalObject*, EncodedJSValue, PropertyName); |
42 | |
43 | static bool setRegExpConstructorInput(JSGlobalObject*, EncodedJSValue, EncodedJSValue); |
44 | static bool setRegExpConstructorMultiline(JSGlobalObject*, 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, ®ExpConstructorTable, 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(JSGlobalObject*, CallFrame*); |
82 | static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(JSGlobalObject*, CallFrame*); |
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, vm.propertyNames->RegExp.string(), NameAdditionMode::WithoutStructureTransition); |
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 | putDirectNonIndexAccessorWithoutTransition(vm, vm.propertyNames->speciesSymbol, speciesSymbol, PropertyAttribute::Accessor | PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum); |
98 | } |
99 | |
100 | template<int N> |
101 | EncodedJSValue regExpConstructorDollar(JSGlobalObject*, EncodedJSValue thisValue, PropertyName) |
102 | { |
103 | JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(); |
104 | return JSValue::encode(globalObject->regExpGlobalData().getBackref(globalObject, N)); |
105 | } |
106 | |
107 | EncodedJSValue regExpConstructorInput(JSGlobalObject*, EncodedJSValue thisValue, PropertyName) |
108 | { |
109 | JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(); |
110 | return JSValue::encode(globalObject->regExpGlobalData().input()); |
111 | } |
112 | |
113 | EncodedJSValue regExpConstructorMultiline(JSGlobalObject*, EncodedJSValue thisValue, PropertyName) |
114 | { |
115 | JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(); |
116 | return JSValue::encode(jsBoolean(globalObject->regExpGlobalData().multiline())); |
117 | } |
118 | |
119 | EncodedJSValue regExpConstructorLastMatch(JSGlobalObject*, EncodedJSValue thisValue, PropertyName) |
120 | { |
121 | JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(); |
122 | return JSValue::encode(globalObject->regExpGlobalData().getBackref(globalObject, 0)); |
123 | } |
124 | |
125 | EncodedJSValue regExpConstructorLastParen(JSGlobalObject*, EncodedJSValue thisValue, PropertyName) |
126 | { |
127 | JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(); |
128 | return JSValue::encode(globalObject->regExpGlobalData().getLastParen(globalObject)); |
129 | } |
130 | |
131 | EncodedJSValue regExpConstructorLeftContext(JSGlobalObject*, EncodedJSValue thisValue, PropertyName) |
132 | { |
133 | JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(); |
134 | return JSValue::encode(globalObject->regExpGlobalData().getLeftContext(globalObject)); |
135 | } |
136 | |
137 | EncodedJSValue regExpConstructorRightContext(JSGlobalObject*, EncodedJSValue thisValue, PropertyName) |
138 | { |
139 | JSGlobalObject* globalObject = jsCast<RegExpConstructor*>(JSValue::decode(thisValue))->globalObject(); |
140 | return JSValue::encode(globalObject->regExpGlobalData().getRightContext(globalObject)); |
141 | } |
142 | |
143 | bool setRegExpConstructorInput(JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue value) |
144 | { |
145 | VM& vm = globalObject->vm(); |
146 | auto scope = DECLARE_THROW_SCOPE(vm); |
147 | if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) { |
148 | auto* string = JSValue::decode(value).toString(globalObject); |
149 | RETURN_IF_EXCEPTION(scope, { }); |
150 | scope.release(); |
151 | JSGlobalObject* globalObject = constructor->globalObject(); |
152 | globalObject->regExpGlobalData().setInput(globalObject, string); |
153 | return true; |
154 | } |
155 | return false; |
156 | } |
157 | |
158 | bool setRegExpConstructorMultiline(JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue value) |
159 | { |
160 | VM& vm = globalObject->vm(); |
161 | auto scope = DECLARE_THROW_SCOPE(vm); |
162 | if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) { |
163 | bool multiline = JSValue::decode(value).toBoolean(globalObject); |
164 | RETURN_IF_EXCEPTION(scope, { }); |
165 | scope.release(); |
166 | JSGlobalObject* globalObject = constructor->globalObject(); |
167 | globalObject->regExpGlobalData().setMultiline(multiline); |
168 | return true; |
169 | } |
170 | return false; |
171 | } |
172 | |
173 | inline Structure* getRegExpStructure(JSGlobalObject* globalObject, JSValue newTarget) |
174 | { |
175 | Structure* structure = globalObject->regExpStructure(); |
176 | if (newTarget != jsUndefined()) |
177 | structure = InternalFunction::createSubclassStructure(globalObject, globalObject->regExpConstructor(), newTarget, structure); |
178 | return structure; |
179 | } |
180 | |
181 | inline OptionSet<Yarr::Flags> toFlags(JSGlobalObject* globalObject, JSValue flags) |
182 | { |
183 | VM& vm = globalObject->vm(); |
184 | auto scope = DECLARE_THROW_SCOPE(vm); |
185 | |
186 | if (flags.isUndefined()) |
187 | return { }; |
188 | |
189 | auto result = Yarr::parseFlags(flags.toWTFString(globalObject)); |
190 | RETURN_IF_EXCEPTION(scope, { }); |
191 | if (!result) { |
192 | throwSyntaxError(globalObject, scope, "Invalid flags supplied to RegExp constructor."_s ); |
193 | return { }; |
194 | } |
195 | |
196 | return result.value(); |
197 | } |
198 | |
199 | static JSObject* regExpCreate(JSGlobalObject* globalObject, JSValue newTarget, JSValue patternArg, JSValue flagsArg) |
200 | { |
201 | VM& vm = globalObject->vm(); |
202 | auto scope = DECLARE_THROW_SCOPE(vm); |
203 | |
204 | String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toWTFString(globalObject); |
205 | RETURN_IF_EXCEPTION(scope, nullptr); |
206 | |
207 | auto flags = toFlags(globalObject, flagsArg); |
208 | RETURN_IF_EXCEPTION(scope, nullptr); |
209 | |
210 | RegExp* regExp = RegExp::create(vm, pattern, flags); |
211 | if (UNLIKELY(!regExp->isValid())) { |
212 | throwException(globalObject, scope, regExp->errorToThrow(globalObject)); |
213 | return nullptr; |
214 | } |
215 | |
216 | Structure* structure = getRegExpStructure(globalObject, newTarget); |
217 | RETURN_IF_EXCEPTION(scope, nullptr); |
218 | return RegExpObject::create(vm, structure, regExp); |
219 | } |
220 | |
221 | JSObject* constructRegExp(JSGlobalObject* globalObject, const ArgList& args, JSObject* callee, JSValue newTarget) |
222 | { |
223 | VM& vm = globalObject->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, globalObject, patternArg); |
230 | RETURN_IF_EXCEPTION(scope, nullptr); |
231 | |
232 | if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) { |
233 | JSValue constructor = patternArg.get(globalObject, 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(globalObject, newTarget); |
244 | RETURN_IF_EXCEPTION(scope, nullptr); |
245 | |
246 | if (!flagsArg.isUndefined()) { |
247 | auto flags = toFlags(globalObject, flagsArg); |
248 | RETURN_IF_EXCEPTION(scope, nullptr); |
249 | |
250 | regExp = RegExp::create(vm, regExp->pattern(), flags); |
251 | if (UNLIKELY(!regExp->isValid())) { |
252 | throwException(globalObject, scope, regExp->errorToThrow(globalObject)); |
253 | return nullptr; |
254 | } |
255 | } |
256 | |
257 | return RegExpObject::create(vm, structure, regExp); |
258 | } |
259 | |
260 | if (constructAsRegexp) { |
261 | JSValue pattern = patternArg.get(globalObject, vm.propertyNames->source); |
262 | RETURN_IF_EXCEPTION(scope, nullptr); |
263 | if (flagsArg.isUndefined()) { |
264 | flagsArg = patternArg.get(globalObject, vm.propertyNames->flags); |
265 | RETURN_IF_EXCEPTION(scope, nullptr); |
266 | } |
267 | patternArg = pattern; |
268 | } |
269 | |
270 | RELEASE_AND_RETURN(scope, regExpCreate(globalObject, newTarget, patternArg, flagsArg)); |
271 | } |
272 | |
273 | EncodedJSValue JSC_HOST_CALL esSpecRegExpCreate(JSGlobalObject* globalObject, CallFrame* callFrame) |
274 | { |
275 | JSValue patternArg = callFrame->argument(0); |
276 | JSValue flagsArg = callFrame->argument(1); |
277 | return JSValue::encode(regExpCreate(globalObject, jsUndefined(), patternArg, flagsArg)); |
278 | } |
279 | |
280 | EncodedJSValue JSC_HOST_CALL esSpecIsRegExp(JSGlobalObject* globalObject, CallFrame* callFrame) |
281 | { |
282 | VM& vm = globalObject->vm(); |
283 | return JSValue::encode(jsBoolean(isRegExp(vm, globalObject, callFrame->argument(0)))); |
284 | } |
285 | |
286 | static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(JSGlobalObject* globalObject, CallFrame* callFrame) |
287 | { |
288 | ArgList args(callFrame); |
289 | return JSValue::encode(constructRegExp(globalObject, args, callFrame->jsCallee(), callFrame->newTarget())); |
290 | } |
291 | |
292 | static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(JSGlobalObject* globalObject, CallFrame* callFrame) |
293 | { |
294 | ArgList args(callFrame); |
295 | return JSValue::encode(constructRegExp(globalObject, args, callFrame->jsCallee())); |
296 | } |
297 | |
298 | } // namespace JSC |
299 | |