]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/RegExpConstructor.cpp
JavaScriptCore-554.1.tar.gz
[apple/javascriptcore.git] / 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().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
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