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