De-virtualize JSObject::getOwnPropertyDescriptor
[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 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 "ArrayPrototype.h"
26 #include "Error.h"
27 #include "ExceptionHelpers.h"
28 #include "JSArray.h"
29 #include "JSFunction.h"
30 #include "JSString.h"
31 #include "Lookup.h"
32 #include "ObjectPrototype.h"
33 #include "RegExpMatchesArray.h"
34 #include "RegExpObject.h"
35 #include "RegExpPrototype.h"
36 #include "RegExp.h"
37 #include "RegExpCache.h"
38 #include "UStringConcatenate.h"
39 #include <wtf/PassOwnPtr.h>
40
41 namespace JSC {
42
43 static JSValue regExpConstructorInput(ExecState*, JSValue, const Identifier&);
44 static JSValue regExpConstructorMultiline(ExecState*, JSValue, const Identifier&);
45 static JSValue regExpConstructorLastMatch(ExecState*, JSValue, const Identifier&);
46 static JSValue regExpConstructorLastParen(ExecState*, JSValue, const Identifier&);
47 static JSValue regExpConstructorLeftContext(ExecState*, JSValue, const Identifier&);
48 static JSValue regExpConstructorRightContext(ExecState*, JSValue, const Identifier&);
49 static JSValue regExpConstructorDollar1(ExecState*, JSValue, const Identifier&);
50 static JSValue regExpConstructorDollar2(ExecState*, JSValue, const Identifier&);
51 static JSValue regExpConstructorDollar3(ExecState*, JSValue, const Identifier&);
52 static JSValue regExpConstructorDollar4(ExecState*, JSValue, const Identifier&);
53 static JSValue regExpConstructorDollar5(ExecState*, JSValue, const Identifier&);
54 static JSValue regExpConstructorDollar6(ExecState*, JSValue, const Identifier&);
55 static JSValue regExpConstructorDollar7(ExecState*, JSValue, const Identifier&);
56 static JSValue regExpConstructorDollar8(ExecState*, JSValue, const Identifier&);
57 static JSValue regExpConstructorDollar9(ExecState*, JSValue, const Identifier&);
58
59 static void setRegExpConstructorInput(ExecState*, JSObject*, JSValue);
60 static void setRegExpConstructorMultiline(ExecState*, JSObject*, JSValue);
61
62 } // namespace JSC
63
64 #include "RegExpConstructor.lut.h"
65
66 namespace JSC {
67
68 ASSERT_CLASS_FITS_IN_CELL(RegExpConstructor);
69
70 const ClassInfo RegExpConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::regExpConstructorTable, CREATE_METHOD_TABLE(RegExpConstructor) };
71
72 const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)};
73
74 /* Source for RegExpConstructor.lut.h
75 @begin regExpConstructorTable
76     input           regExpConstructorInput          None
77     $_              regExpConstructorInput          DontEnum
78     multiline       regExpConstructorMultiline      None
79     $*              regExpConstructorMultiline      DontEnum
80     lastMatch       regExpConstructorLastMatch      DontDelete|ReadOnly
81     $&              regExpConstructorLastMatch      DontDelete|ReadOnly|DontEnum
82     lastParen       regExpConstructorLastParen      DontDelete|ReadOnly
83     $+              regExpConstructorLastParen      DontDelete|ReadOnly|DontEnum
84     leftContext     regExpConstructorLeftContext    DontDelete|ReadOnly
85     $`              regExpConstructorLeftContext    DontDelete|ReadOnly|DontEnum
86     rightContext    regExpConstructorRightContext   DontDelete|ReadOnly
87     $'              regExpConstructorRightContext   DontDelete|ReadOnly|DontEnum
88     $1              regExpConstructorDollar1        DontDelete|ReadOnly
89     $2              regExpConstructorDollar2        DontDelete|ReadOnly
90     $3              regExpConstructorDollar3        DontDelete|ReadOnly
91     $4              regExpConstructorDollar4        DontDelete|ReadOnly
92     $5              regExpConstructorDollar5        DontDelete|ReadOnly
93     $6              regExpConstructorDollar6        DontDelete|ReadOnly
94     $7              regExpConstructorDollar7        DontDelete|ReadOnly
95     $8              regExpConstructorDollar8        DontDelete|ReadOnly
96     $9              regExpConstructorDollar9        DontDelete|ReadOnly
97 @end
98 */
99
100 RegExpConstructor::RegExpConstructor(JSGlobalObject* globalObject, Structure* structure)
101     : InternalFunction(globalObject, structure)
102     , d(adoptPtr(new RegExpConstructorPrivate))
103 {
104 }
105
106 void RegExpConstructor::finishCreation(ExecState* exec, RegExpPrototype* regExpPrototype)
107 {
108     Base::finishCreation(exec->globalData(), Identifier(exec, "RegExp"));
109     ASSERT(inherits(&s_info));
110
111     // ECMA 15.10.5.1 RegExp.prototype
112     putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly);
113
114     // no. of arguments for constructor
115     putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(2), ReadOnly | DontDelete | DontEnum);
116 }
117
118 RegExpMatchesArray::RegExpMatchesArray(ExecState* exec)
119     : JSArray(exec->globalData(), exec->lexicalGlobalObject()->regExpMatchesArrayStructure())
120 {
121 }
122
123 void RegExpMatchesArray::finishCreation(JSGlobalData& globalData, RegExpConstructorPrivate* data)
124 {
125     Base::finishCreation(globalData, data->lastNumSubPatterns + 1, CreateInitialized);
126     RegExpConstructorPrivate* d = new RegExpConstructorPrivate;
127     d->input = data->lastInput;
128     d->lastInput = data->lastInput;
129     d->lastNumSubPatterns = data->lastNumSubPatterns;
130     unsigned offsetVectorSize = (data->lastNumSubPatterns + 1) * 2; // only copying the result part of the vector
131     d->lastOvector().resize(offsetVectorSize);
132     memcpy(d->lastOvector().data(), data->lastOvector().data(), offsetVectorSize * sizeof(int));
133     // d->multiline is not needed, and remains uninitialized
134
135     setSubclassData(d);
136 }
137
138 RegExpMatchesArray::~RegExpMatchesArray()
139 {
140     delete static_cast<RegExpConstructorPrivate*>(subclassData());
141 }
142
143 void RegExpMatchesArray::fillArrayInstance(ExecState* exec)
144 {
145     RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(subclassData());
146     ASSERT(d);
147
148     unsigned lastNumSubpatterns = d->lastNumSubPatterns;
149
150     for (unsigned i = 0; i <= lastNumSubpatterns; ++i) {
151         int start = d->lastOvector()[2 * i];
152         if (start >= 0)
153             JSArray::putByIndex(this, exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start));
154         else
155             JSArray::putByIndex(this, exec, i, jsUndefined());
156     }
157
158     PutPropertySlot slot;
159     JSArray::put(this, exec, exec->propertyNames().index, jsNumber(d->lastOvector()[0]), slot);
160     JSArray::put(this, exec, exec->propertyNames().input, jsString(exec, d->input), slot);
161
162     delete d;
163     setSubclassData(0);
164 }
165
166 JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const
167 {
168     return RegExpMatchesArray::create(exec, d.get());
169 }
170
171 JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) const
172 {
173     if (!d->lastOvector().isEmpty() && i <= d->lastNumSubPatterns) {
174         int start = d->lastOvector()[2 * i];
175         if (start >= 0)
176             return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start);
177     }
178     return jsEmptyString(exec);
179 }
180
181 JSValue RegExpConstructor::getLastParen(ExecState* exec) const
182 {
183     unsigned i = d->lastNumSubPatterns;
184     if (i > 0) {
185         ASSERT(!d->lastOvector().isEmpty());
186         int start = d->lastOvector()[2 * i];
187         if (start >= 0)
188             return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start);
189     }
190     return jsEmptyString(exec);
191 }
192
193 JSValue RegExpConstructor::getLeftContext(ExecState* exec) const
194 {
195     if (!d->lastOvector().isEmpty())
196         return jsSubstring(exec, d->lastInput, 0, d->lastOvector()[0]);
197     return jsEmptyString(exec);
198 }
199
200 JSValue RegExpConstructor::getRightContext(ExecState* exec) const
201 {
202     if (!d->lastOvector().isEmpty())
203         return jsSubstring(exec, d->lastInput, d->lastOvector()[1], d->lastInput.length() - d->lastOvector()[1]);
204     return jsEmptyString(exec);
205 }
206     
207 bool RegExpConstructor::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
208 {
209     return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), static_cast<RegExpConstructor*>(cell), propertyName, slot);
210 }
211
212 bool RegExpConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
213 {
214     return getStaticValueDescriptor<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), static_cast<RegExpConstructor*>(object), propertyName, descriptor);
215 }
216
217 JSValue regExpConstructorDollar1(ExecState* exec, JSValue slotBase, const Identifier&)
218 {
219     return asRegExpConstructor(slotBase)->getBackref(exec, 1);
220 }
221
222 JSValue regExpConstructorDollar2(ExecState* exec, JSValue slotBase, const Identifier&)
223 {
224     return asRegExpConstructor(slotBase)->getBackref(exec, 2);
225 }
226
227 JSValue regExpConstructorDollar3(ExecState* exec, JSValue slotBase, const Identifier&)
228 {
229     return asRegExpConstructor(slotBase)->getBackref(exec, 3);
230 }
231
232 JSValue regExpConstructorDollar4(ExecState* exec, JSValue slotBase, const Identifier&)
233 {
234     return asRegExpConstructor(slotBase)->getBackref(exec, 4);
235 }
236
237 JSValue regExpConstructorDollar5(ExecState* exec, JSValue slotBase, const Identifier&)
238 {
239     return asRegExpConstructor(slotBase)->getBackref(exec, 5);
240 }
241
242 JSValue regExpConstructorDollar6(ExecState* exec, JSValue slotBase, const Identifier&)
243 {
244     return asRegExpConstructor(slotBase)->getBackref(exec, 6);
245 }
246
247 JSValue regExpConstructorDollar7(ExecState* exec, JSValue slotBase, const Identifier&)
248 {
249     return asRegExpConstructor(slotBase)->getBackref(exec, 7);
250 }
251
252 JSValue regExpConstructorDollar8(ExecState* exec, JSValue slotBase, const Identifier&)
253 {
254     return asRegExpConstructor(slotBase)->getBackref(exec, 8);
255 }
256
257 JSValue regExpConstructorDollar9(ExecState* exec, JSValue slotBase, const Identifier&)
258 {
259     return asRegExpConstructor(slotBase)->getBackref(exec, 9);
260 }
261
262 JSValue regExpConstructorInput(ExecState* exec, JSValue slotBase, const Identifier&)
263 {
264     return jsString(exec, asRegExpConstructor(slotBase)->input());
265 }
266
267 JSValue regExpConstructorMultiline(ExecState*, JSValue slotBase, const Identifier&)
268 {
269     return jsBoolean(asRegExpConstructor(slotBase)->multiline());
270 }
271
272 JSValue regExpConstructorLastMatch(ExecState* exec, JSValue slotBase, const Identifier&)
273 {
274     return asRegExpConstructor(slotBase)->getBackref(exec, 0);
275 }
276
277 JSValue regExpConstructorLastParen(ExecState* exec, JSValue slotBase, const Identifier&)
278 {
279     return asRegExpConstructor(slotBase)->getLastParen(exec);
280 }
281
282 JSValue regExpConstructorLeftContext(ExecState* exec, JSValue slotBase, const Identifier&)
283 {
284     return asRegExpConstructor(slotBase)->getLeftContext(exec);
285 }
286
287 JSValue regExpConstructorRightContext(ExecState* exec, JSValue slotBase, const Identifier&)
288 {
289     return asRegExpConstructor(slotBase)->getRightContext(exec);
290 }
291
292 void RegExpConstructor::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
293 {
294     lookupPut<RegExpConstructor, InternalFunction>(exec, propertyName, value, ExecState::regExpConstructorTable(exec), static_cast<RegExpConstructor*>(cell), slot);
295 }
296
297 void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, JSValue value)
298 {
299     asRegExpConstructor(baseObject)->setInput(value.toString(exec));
300 }
301
302 void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, JSValue value)
303 {
304     asRegExpConstructor(baseObject)->setMultiline(value.toBoolean(exec));
305 }
306
307 // ECMA 15.10.4
308 JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, bool callAsConstructor)
309 {
310     JSValue arg0 = args.at(0);
311     JSValue arg1 = args.at(1);
312
313     if (arg0.inherits(&RegExpObject::s_info)) {
314         if (!arg1.isUndefined())
315             return throwError(exec, createTypeError(exec, "Cannot supply flags when constructing one RegExp from another."));
316         // If called as a function, this just returns the first argument (see 15.10.3.1).
317         if (callAsConstructor) {
318             RegExp* regExp = static_cast<RegExpObject*>(asObject(arg0))->regExp();
319             return RegExpObject::create(exec, globalObject, globalObject->regExpStructure(), regExp);
320         }
321         return asObject(arg0);
322     }
323
324     UString pattern = arg0.isUndefined() ? UString("") : arg0.toString(exec);
325     if (exec->hadException())
326         return 0;
327
328     RegExpFlags flags = NoFlags;
329     if (!arg1.isUndefined()) {
330         flags = regExpFlags(arg1.toString(exec));
331         if (exec->hadException())
332             return 0;
333         if (flags == InvalidFlags)
334             return throwError(exec, createSyntaxError(exec, "Invalid flags supplied to RegExp constructor."));
335     }
336
337     RegExp* regExp = RegExp::create(exec->globalData(), pattern, flags);
338     if (!regExp->isValid())
339         return throwError(exec, createSyntaxError(exec, regExp->errorMessage()));
340     return RegExpObject::create(exec, exec->lexicalGlobalObject(), globalObject->regExpStructure(), regExp);
341 }
342
343 static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec)
344 {
345     ArgList args(exec);
346     return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, true));
347 }
348
349 ConstructType RegExpConstructor::getConstructData(JSCell*, ConstructData& constructData)
350 {
351     constructData.native.function = constructWithRegExpConstructor;
352     return ConstructTypeHost;
353 }
354
355 // ECMA 15.10.3
356 static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec)
357 {
358     ArgList args(exec);
359     return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args));
360 }
361
362 CallType RegExpConstructor::getCallData(JSCell*, CallData& callData)
363 {
364     callData.native.function = callRegExpConstructor;
365     return CallTypeHost;
366 }
367
368 void RegExpConstructor::setInput(const UString& input)
369 {
370     d->input = input;
371 }
372
373 const UString& RegExpConstructor::input() const
374 {
375     // Can detect a distinct initial state that is invisible to JavaScript, by checking for null
376     // state (since jsString turns null strings to empty strings).
377     return d->input;
378 }
379
380 void RegExpConstructor::setMultiline(bool multiline)
381 {
382     d->multiline = multiline;
383 }
384
385 bool RegExpConstructor::multiline() const
386 {
387     return d->multiline;
388 }
389
390 } // namespace JSC