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(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, ®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(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, vm.propertyNames->RegExp.string(), NameVisibility::Visible, 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(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 | auto scope = DECLARE_THROW_SCOPE(vm); |
154 | if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) { |
155 | auto* string = JSValue::decode(value).toString(exec); |
156 | RETURN_IF_EXCEPTION(scope, { }); |
157 | scope.release(); |
158 | JSGlobalObject* globalObject = constructor->globalObject(vm); |
159 | globalObject->regExpGlobalData().setInput(exec, globalObject, string); |
160 | return true; |
161 | } |
162 | return false; |
163 | } |
164 | |
165 | bool setRegExpConstructorMultiline(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value) |
166 | { |
167 | VM& vm = exec->vm(); |
168 | auto scope = DECLARE_THROW_SCOPE(vm); |
169 | if (auto constructor = jsDynamicCast<RegExpConstructor*>(vm, JSValue::decode(thisValue))) { |
170 | bool multiline = JSValue::decode(value).toBoolean(exec); |
171 | RETURN_IF_EXCEPTION(scope, { }); |
172 | scope.release(); |
173 | JSGlobalObject* globalObject = constructor->globalObject(vm); |
174 | globalObject->regExpGlobalData().setMultiline(multiline); |
175 | return true; |
176 | } |
177 | return false; |
178 | } |
179 | |
180 | inline Structure* getRegExpStructure(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget) |
181 | { |
182 | Structure* structure = globalObject->regExpStructure(); |
183 | if (newTarget != jsUndefined()) |
184 | structure = InternalFunction::createSubclassStructure(exec, newTarget, structure); |
185 | return structure; |
186 | } |
187 | |
188 | inline OptionSet<Yarr::Flags> toFlags(ExecState* exec, JSValue flags) |
189 | { |
190 | VM& vm = exec->vm(); |
191 | auto scope = DECLARE_THROW_SCOPE(vm); |
192 | |
193 | if (flags.isUndefined()) |
194 | return { }; |
195 | |
196 | auto result = Yarr::parseFlags(flags.toWTFString(exec)); |
197 | RETURN_IF_EXCEPTION(scope, { }); |
198 | if (!result) { |
199 | throwSyntaxError(exec, scope, "Invalid flags supplied to RegExp constructor."_s ); |
200 | return { }; |
201 | } |
202 | |
203 | return result.value(); |
204 | } |
205 | |
206 | static JSObject* regExpCreate(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget, JSValue patternArg, JSValue flagsArg) |
207 | { |
208 | VM& vm = exec->vm(); |
209 | auto scope = DECLARE_THROW_SCOPE(vm); |
210 | |
211 | String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toWTFString(exec); |
212 | RETURN_IF_EXCEPTION(scope, nullptr); |
213 | |
214 | auto flags = toFlags(exec, flagsArg); |
215 | RETURN_IF_EXCEPTION(scope, nullptr); |
216 | |
217 | RegExp* regExp = RegExp::create(vm, pattern, flags); |
218 | if (UNLIKELY(!regExp->isValid())) { |
219 | throwException(exec, scope, regExp->errorToThrow(exec)); |
220 | return nullptr; |
221 | } |
222 | |
223 | Structure* structure = getRegExpStructure(exec, globalObject, newTarget); |
224 | RETURN_IF_EXCEPTION(scope, nullptr); |
225 | return RegExpObject::create(vm, structure, regExp); |
226 | } |
227 | |
228 | JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, JSObject* callee, JSValue newTarget) |
229 | { |
230 | VM& vm = exec->vm(); |
231 | auto scope = DECLARE_THROW_SCOPE(vm); |
232 | JSValue patternArg = args.at(0); |
233 | JSValue flagsArg = args.at(1); |
234 | |
235 | bool isPatternRegExp = patternArg.inherits<RegExpObject>(vm); |
236 | bool constructAsRegexp = isRegExp(vm, exec, patternArg); |
237 | RETURN_IF_EXCEPTION(scope, nullptr); |
238 | |
239 | if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) { |
240 | JSValue constructor = patternArg.get(exec, vm.propertyNames->constructor); |
241 | RETURN_IF_EXCEPTION(scope, nullptr); |
242 | if (callee == constructor) { |
243 | // We know that patternArg is a object otherwise constructAsRegexp would be false. |
244 | return patternArg.getObject(); |
245 | } |
246 | } |
247 | |
248 | if (isPatternRegExp) { |
249 | RegExp* regExp = jsCast<RegExpObject*>(patternArg)->regExp(); |
250 | Structure* structure = getRegExpStructure(exec, globalObject, newTarget); |
251 | RETURN_IF_EXCEPTION(scope, nullptr); |
252 | |
253 | if (!flagsArg.isUndefined()) { |
254 | auto flags = toFlags(exec, flagsArg); |
255 | RETURN_IF_EXCEPTION(scope, nullptr); |
256 | |
257 | regExp = RegExp::create(vm, regExp->pattern(), flags); |
258 | if (UNLIKELY(!regExp->isValid())) { |
259 | throwException(exec, scope, regExp->errorToThrow(exec)); |
260 | return nullptr; |
261 | } |
262 | } |
263 | |
264 | return RegExpObject::create(vm, structure, regExp); |
265 | } |
266 | |
267 | if (constructAsRegexp) { |
268 | JSValue pattern = patternArg.get(exec, vm.propertyNames->source); |
269 | RETURN_IF_EXCEPTION(scope, nullptr); |
270 | if (flagsArg.isUndefined()) { |
271 | flagsArg = patternArg.get(exec, vm.propertyNames->flags); |
272 | RETURN_IF_EXCEPTION(scope, nullptr); |
273 | } |
274 | patternArg = pattern; |
275 | } |
276 | |
277 | RELEASE_AND_RETURN(scope, regExpCreate(exec, globalObject, newTarget, patternArg, flagsArg)); |
278 | } |
279 | |
280 | EncodedJSValue JSC_HOST_CALL esSpecRegExpCreate(ExecState* exec) |
281 | { |
282 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
283 | JSValue patternArg = exec->argument(0); |
284 | JSValue flagsArg = exec->argument(1); |
285 | return JSValue::encode(regExpCreate(exec, globalObject, jsUndefined(), patternArg, flagsArg)); |
286 | } |
287 | |
288 | static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec) |
289 | { |
290 | ArgList args(exec); |
291 | return JSValue::encode(constructRegExp(exec, jsCast<InternalFunction*>(exec->jsCallee())->globalObject(exec->vm()), args, exec->jsCallee(), exec->newTarget())); |
292 | } |
293 | |
294 | static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec) |
295 | { |
296 | ArgList args(exec); |
297 | return JSValue::encode(constructRegExp(exec, jsCast<InternalFunction*>(exec->jsCallee())->globalObject(exec->vm()), args, exec->jsCallee())); |
298 | } |
299 | |
300 | } // namespace JSC |
301 | |