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