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