• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 : FastAllocBase {
93     // Global search cache / settings
RegExpConstructorPrivateJSC::RegExpConstructorPrivate94     RegExpConstructorPrivate()
95         : lastNumSubPatterns(0)
96         , multiline(false)
97         , lastOvectorIndex(0)
98     {
99     }
100 
lastOvectorJSC::RegExpConstructorPrivate101     const Vector<int, 32>& lastOvector() const { return ovector[lastOvectorIndex]; }
lastOvectorJSC::RegExpConstructorPrivate102     Vector<int, 32>& lastOvector() { return ovector[lastOvectorIndex]; }
tempOvectorJSC::RegExpConstructorPrivate103     Vector<int, 32>& tempOvector() { return ovector[lastOvectorIndex ? 0 : 1]; }
changeLastOvectorJSC::RegExpConstructorPrivate104     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 
RegExpConstructor(ExecState * exec,PassRefPtr<Structure> structure,RegExpPrototype * regExpPrototype)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 */
performMatch(RegExp * r,const UString & s,int startOffset,int & position,int & length,int ** ovector)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 
RegExpMatchesArray(ExecState * exec,RegExpConstructorPrivate * data)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 
~RegExpMatchesArray()164 RegExpMatchesArray::~RegExpMatchesArray()
165 {
166     delete static_cast<RegExpConstructorPrivate*>(lazyCreationData());
167 }
168 
fillArrayInstance(ExecState * exec)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 
arrayOfMatches(ExecState * exec) const190 JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const
191 {
192     return new (exec) RegExpMatchesArray(exec, d.get());
193 }
194 
getBackref(ExecState * exec,unsigned i) const195 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 
getLastParen(ExecState * exec) const205 JSValue RegExpConstructor::getLastParen(ExecState* exec) const
206 {
207     unsigned i = d->lastNumSubPatterns;
208     if (i > 0) {
209         ASSERT(!d->lastOvector().isEmpty());
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 
getLeftContext(ExecState * exec) const217 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 
getRightContext(ExecState * exec) const224 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 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)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 
regExpConstructorDollar1(ExecState * exec,const Identifier &,const PropertySlot & slot)236 JSValue regExpConstructorDollar1(ExecState* exec, const Identifier&, const PropertySlot& slot)
237 {
238     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 1);
239 }
240 
regExpConstructorDollar2(ExecState * exec,const Identifier &,const PropertySlot & slot)241 JSValue regExpConstructorDollar2(ExecState* exec, const Identifier&, const PropertySlot& slot)
242 {
243     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 2);
244 }
245 
regExpConstructorDollar3(ExecState * exec,const Identifier &,const PropertySlot & slot)246 JSValue regExpConstructorDollar3(ExecState* exec, const Identifier&, const PropertySlot& slot)
247 {
248     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 3);
249 }
250 
regExpConstructorDollar4(ExecState * exec,const Identifier &,const PropertySlot & slot)251 JSValue regExpConstructorDollar4(ExecState* exec, const Identifier&, const PropertySlot& slot)
252 {
253     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 4);
254 }
255 
regExpConstructorDollar5(ExecState * exec,const Identifier &,const PropertySlot & slot)256 JSValue regExpConstructorDollar5(ExecState* exec, const Identifier&, const PropertySlot& slot)
257 {
258     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 5);
259 }
260 
regExpConstructorDollar6(ExecState * exec,const Identifier &,const PropertySlot & slot)261 JSValue regExpConstructorDollar6(ExecState* exec, const Identifier&, const PropertySlot& slot)
262 {
263     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 6);
264 }
265 
regExpConstructorDollar7(ExecState * exec,const Identifier &,const PropertySlot & slot)266 JSValue regExpConstructorDollar7(ExecState* exec, const Identifier&, const PropertySlot& slot)
267 {
268     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 7);
269 }
270 
regExpConstructorDollar8(ExecState * exec,const Identifier &,const PropertySlot & slot)271 JSValue regExpConstructorDollar8(ExecState* exec, const Identifier&, const PropertySlot& slot)
272 {
273     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 8);
274 }
275 
regExpConstructorDollar9(ExecState * exec,const Identifier &,const PropertySlot & slot)276 JSValue regExpConstructorDollar9(ExecState* exec, const Identifier&, const PropertySlot& slot)
277 {
278     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 9);
279 }
280 
regExpConstructorInput(ExecState * exec,const Identifier &,const PropertySlot & slot)281 JSValue regExpConstructorInput(ExecState* exec, const Identifier&, const PropertySlot& slot)
282 {
283     return jsString(exec, asRegExpConstructor(slot.slotBase())->input());
284 }
285 
regExpConstructorMultiline(ExecState *,const Identifier &,const PropertySlot & slot)286 JSValue regExpConstructorMultiline(ExecState*, const Identifier&, const PropertySlot& slot)
287 {
288     return jsBoolean(asRegExpConstructor(slot.slotBase())->multiline());
289 }
290 
regExpConstructorLastMatch(ExecState * exec,const Identifier &,const PropertySlot & slot)291 JSValue regExpConstructorLastMatch(ExecState* exec, const Identifier&, const PropertySlot& slot)
292 {
293     return asRegExpConstructor(slot.slotBase())->getBackref(exec, 0);
294 }
295 
regExpConstructorLastParen(ExecState * exec,const Identifier &,const PropertySlot & slot)296 JSValue regExpConstructorLastParen(ExecState* exec, const Identifier&, const PropertySlot& slot)
297 {
298     return asRegExpConstructor(slot.slotBase())->getLastParen(exec);
299 }
300 
regExpConstructorLeftContext(ExecState * exec,const Identifier &,const PropertySlot & slot)301 JSValue regExpConstructorLeftContext(ExecState* exec, const Identifier&, const PropertySlot& slot)
302 {
303     return asRegExpConstructor(slot.slotBase())->getLeftContext(exec);
304 }
305 
regExpConstructorRightContext(ExecState * exec,const Identifier &,const PropertySlot & slot)306 JSValue regExpConstructorRightContext(ExecState* exec, const Identifier&, const PropertySlot& slot)
307 {
308     return asRegExpConstructor(slot.slotBase())->getRightContext(exec);
309 }
310 
put(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)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 
setRegExpConstructorInput(ExecState * exec,JSObject * baseObject,JSValue value)316 void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, JSValue value)
317 {
318     asRegExpConstructor(baseObject)->setInput(value.toString(exec));
319 }
320 
setRegExpConstructorMultiline(ExecState * exec,JSObject * baseObject,JSValue value)321 void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, JSValue value)
322 {
323     asRegExpConstructor(baseObject)->setMultiline(value.toBoolean(exec));
324 }
325 
326 // ECMA 15.10.4
constructRegExp(ExecState * exec,const ArgList & args)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 
constructWithRegExpConstructor(ExecState * exec,JSObject *,const ArgList & args)347 static JSObject* constructWithRegExpConstructor(ExecState* exec, JSObject*, const ArgList& args)
348 {
349     return constructRegExp(exec, args);
350 }
351 
getConstructData(ConstructData & constructData)352 ConstructType RegExpConstructor::getConstructData(ConstructData& constructData)
353 {
354     constructData.native.function = constructWithRegExpConstructor;
355     return ConstructTypeHost;
356 }
357 
358 // ECMA 15.10.3
callRegExpConstructor(ExecState * exec,JSObject *,JSValue,const ArgList & args)359 static JSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args)
360 {
361     return constructRegExp(exec, args);
362 }
363 
getCallData(CallData & callData)364 CallType RegExpConstructor::getCallData(CallData& callData)
365 {
366     callData.native.function = callRegExpConstructor;
367     return CallTypeHost;
368 }
369 
setInput(const UString & input)370 void RegExpConstructor::setInput(const UString& input)
371 {
372     d->input = input;
373 }
374 
input() const375 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 
setMultiline(bool multiline)382 void RegExpConstructor::setMultiline(bool multiline)
383 {
384     d->multiline = multiline;
385 }
386 
multiline() const387 bool RegExpConstructor::multiline() const
388 {
389     return d->multiline;
390 }
391 
392 } // namespace JSC
393