source: webkit/trunk/JavaScriptCore/kjs/RegExpObject.cpp@ 34857

Last change on this file since 34857 was 34857, checked in by [email protected], 17 years ago

2008-06-28 Sam Weinig <[email protected]>

Try and fix the Windows build again.

  • kjs/RegExpObject.cpp:
  • kjs/date_object.cpp:
  • kjs/error_object.cpp:
  • Property svn:eol-style set to native
File size: 19.0 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
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 "RegExpObject.h"
23#include "RegExpObject.lut.h"
24
25#include "ArrayPrototype.h"
26#include "JSArray.h"
27#include "JSObject.h"
28#include "JSString.h"
29#include "JSValue.h"
30#include "ObjectPrototype.h"
31#include "UnusedParam.h"
32#include "error_object.h"
33#include "operations.h"
34#include "regexp.h"
35
36#include <stdio.h>
37
38namespace KJS {
39
40// ------------------------------ RegExpPrototype ---------------------------
41
42static JSValue* regExpProtoFuncTest(ExecState*, JSObject*, JSValue*, const ArgList&);
43static JSValue* regExpProtoFuncExec(ExecState*, JSObject*, JSValue*, const ArgList&);
44static JSValue* regExpProtoFuncCompile(ExecState*, JSObject*, JSValue*, const ArgList&);
45static JSValue* regExpProtoFuncToString(ExecState*, JSObject*, JSValue*, const ArgList&);
46
47// ECMA 15.10.5
48
49const ClassInfo RegExpPrototype::info = { "RegExpPrototype", 0, 0, 0 };
50
51RegExpPrototype::RegExpPrototype(ExecState* exec, ObjectPrototype* objectPrototype, FunctionPrototype* functionPrototype)
52 : JSObject(objectPrototype)
53{
54 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().compile, regExpProtoFuncCompile), DontEnum);
55 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().exec, regExpProtoFuncExec), DontEnum);
56 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().test, regExpProtoFuncTest), DontEnum);
57 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toString, regExpProtoFuncToString), DontEnum);
58}
59
60// ------------------------------ Functions ---------------------------
61
62JSValue* regExpProtoFuncTest(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
63{
64 if (!thisValue->isObject(&RegExpObject::info))
65 return throwError(exec, TypeError);
66 return static_cast<RegExpObject*>(thisValue)->test(exec, args);
67}
68
69JSValue* regExpProtoFuncExec(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
70{
71 if (!thisValue->isObject(&RegExpObject::info))
72 return throwError(exec, TypeError);
73 return static_cast<RegExpObject*>(thisValue)->exec(exec, args);
74}
75
76JSValue* regExpProtoFuncCompile(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
77{
78 if (!thisValue->isObject(&RegExpObject::info))
79 return throwError(exec, TypeError);
80
81 RefPtr<RegExp> regExp;
82 JSValue* arg0 = args[0];
83 JSValue* arg1 = args[1];
84
85 if (arg0->isObject(&RegExpObject::info)) {
86 if (!arg1->isUndefined())
87 return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another.");
88 regExp = static_cast<RegExpObject*>(arg0)->regExp();
89 } else {
90 UString pattern = args.isEmpty() ? UString("") : arg0->toString(exec);
91 UString flags = arg1->isUndefined() ? UString("") : arg1->toString(exec);
92 regExp = RegExp::create(pattern, flags);
93 }
94
95 if (!regExp->isValid())
96 return throwError(exec, SyntaxError, UString("Invalid regular expression: ").append(regExp->errorMessage()));
97
98 static_cast<RegExpObject*>(thisValue)->setRegExp(regExp.release());
99 static_cast<RegExpObject*>(thisValue)->setLastIndex(0);
100 return jsUndefined();
101}
102
103JSValue* regExpProtoFuncToString(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
104{
105 if (!thisValue->isObject(&RegExpObject::info)) {
106 if (thisValue->isObject(&RegExpPrototype::info))
107 return jsString(exec, "//");
108 return throwError(exec, TypeError);
109 }
110
111 UString result = "/" + static_cast<RegExpObject*>(thisValue)->get(exec, exec->propertyNames().source)->toString(exec) + "/";
112 if (static_cast<RegExpObject*>(thisValue)->get(exec, exec->propertyNames().global)->toBoolean(exec))
113 result += "g";
114 if (static_cast<RegExpObject*>(thisValue)->get(exec, exec->propertyNames().ignoreCase)->toBoolean(exec))
115 result += "i";
116 if (static_cast<RegExpObject*>(thisValue)->get(exec, exec->propertyNames().multiline)->toBoolean(exec))
117 result += "m";
118 return jsString(exec, result);
119}
120
121// ------------------------------ RegExpObject ------------------------------------
122
123const ClassInfo RegExpObject::info = { "RegExp", 0, 0, ExecState::regExpTable };
124
125/* Source for RegExpObject.lut.h
126@begin regExpTable
127 global RegExpObject::Global DontDelete|ReadOnly|DontEnum
128 ignoreCase RegExpObject::IgnoreCase DontDelete|ReadOnly|DontEnum
129 multiline RegExpObject::Multiline DontDelete|ReadOnly|DontEnum
130 source RegExpObject::Source DontDelete|ReadOnly|DontEnum
131 lastIndex RegExpObject::LastIndex DontDelete|DontEnum
132@end
133*/
134
135RegExpObject::RegExpObject(RegExpPrototype* regexpProto, PassRefPtr<RegExp> regExp)
136 : JSObject(regexpProto)
137 , m_regExp(regExp)
138 , m_lastIndex(0)
139{
140}
141
142RegExpObject::~RegExpObject()
143{
144}
145
146bool RegExpObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
147{
148 return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), this, propertyName, slot);
149}
150
151JSValue* RegExpObject::getValueProperty(ExecState* exec, int token) const
152{
153 switch (token) {
154 case Global:
155 return jsBoolean(m_regExp->global());
156 case IgnoreCase:
157 return jsBoolean(m_regExp->ignoreCase());
158 case Multiline:
159 return jsBoolean(m_regExp->multiline());
160 case Source:
161 return jsString(exec, m_regExp->pattern());
162 case LastIndex:
163 return jsNumber(exec, m_lastIndex);
164 }
165
166 ASSERT_NOT_REACHED();
167 return 0;
168}
169
170void RegExpObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
171{
172 lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), this);
173}
174
175void RegExpObject::putValueProperty(ExecState* exec, int token, JSValue* value)
176{
177 UNUSED_PARAM(token);
178 ASSERT(token == LastIndex);
179 m_lastIndex = value->toInteger(exec);
180}
181
182bool RegExpObject::match(ExecState* exec, const ArgList& args)
183{
184 RegExpConstructor* regExpObj = exec->lexicalGlobalObject()->regExpConstructor();
185
186 UString input;
187 if (!args.isEmpty())
188 input = args[0]->toString(exec);
189 else {
190 input = regExpObj->input();
191 if (input.isNull()) {
192 throwError(exec, GeneralError, "No input.");
193 return false;
194 }
195 }
196
197 bool global = get(exec, exec->propertyNames().global)->toBoolean(exec);
198 int lastIndex = 0;
199 if (global) {
200 if (m_lastIndex < 0 || m_lastIndex > input.size()) {
201 m_lastIndex = 0;
202 return false;
203 }
204 lastIndex = static_cast<int>(m_lastIndex);
205 }
206
207 int foundIndex;
208 int foundLength;
209 regExpObj->performMatch(m_regExp.get(), input, lastIndex, foundIndex, foundLength);
210
211 if (global) {
212 lastIndex = foundIndex < 0 ? 0 : foundIndex + foundLength;
213 m_lastIndex = lastIndex;
214 }
215
216 return foundIndex >= 0;
217}
218
219JSValue* RegExpObject::test(ExecState* exec, const ArgList& args)
220{
221 return jsBoolean(match(exec, args));
222}
223
224JSValue* RegExpObject::exec(ExecState* exec, const ArgList& args)
225{
226 return match(exec, args)
227 ? exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec)
228 : jsNull();
229}
230
231static JSValue* callRegExpObject(ExecState* exec, JSObject* function, JSValue*, const ArgList& args)
232{
233 return static_cast<RegExpObject*>(function)->exec(exec, args);
234}
235
236CallType RegExpObject::getCallData(CallData& callData)
237{
238 callData.native.function = callRegExpObject;
239 return CallTypeNative;
240}
241
242// ------------------------------ RegExpConstructor ------------------------------
243
244const ClassInfo RegExpConstructor::info = { "Function", &InternalFunction::info, 0, ExecState::regExpConstructorTable };
245
246/* Source for RegExpObject.lut.h
247@begin regExpConstructorTable
248 input RegExpConstructor::Input None
249 $_ RegExpConstructor::Input DontEnum
250 multiline RegExpConstructor::Multiline None
251 $* RegExpConstructor::Multiline DontEnum
252 lastMatch RegExpConstructor::LastMatch DontDelete|ReadOnly
253 $& RegExpConstructor::LastMatch DontDelete|ReadOnly|DontEnum
254 lastParen RegExpConstructor::LastParen DontDelete|ReadOnly
255 $+ RegExpConstructor::LastParen DontDelete|ReadOnly|DontEnum
256 leftContext RegExpConstructor::LeftContext DontDelete|ReadOnly
257 $` RegExpConstructor::LeftContext DontDelete|ReadOnly|DontEnum
258 rightContext RegExpConstructor::RightContext DontDelete|ReadOnly
259 $' RegExpConstructor::RightContext DontDelete|ReadOnly|DontEnum
260 $1 RegExpConstructor::Dollar1 DontDelete|ReadOnly
261 $2 RegExpConstructor::Dollar2 DontDelete|ReadOnly
262 $3 RegExpConstructor::Dollar3 DontDelete|ReadOnly
263 $4 RegExpConstructor::Dollar4 DontDelete|ReadOnly
264 $5 RegExpConstructor::Dollar5 DontDelete|ReadOnly
265 $6 RegExpConstructor::Dollar6 DontDelete|ReadOnly
266 $7 RegExpConstructor::Dollar7 DontDelete|ReadOnly
267 $8 RegExpConstructor::Dollar8 DontDelete|ReadOnly
268 $9 RegExpConstructor::Dollar9 DontDelete|ReadOnly
269@end
270*/
271
272struct RegExpConstructorPrivate {
273 // Global search cache / settings
274 RegExpConstructorPrivate() : lastNumSubPatterns(0), multiline(false) { }
275 UString lastInput;
276 OwnArrayPtr<int> lastOvector;
277 unsigned lastNumSubPatterns : 31;
278 bool multiline : 1;
279};
280
281RegExpConstructor::RegExpConstructor(ExecState* exec, FunctionPrototype* funcProto, RegExpPrototype* regProto)
282 : InternalFunction(funcProto, Identifier(exec, "RegExp"))
283 , d(new RegExpConstructorPrivate)
284{
285 // ECMA 15.10.5.1 RegExp.prototype
286 putDirect(exec->propertyNames().prototype, regProto, DontEnum | DontDelete | ReadOnly);
287
288 // no. of arguments for constructor
289 putDirect(exec->propertyNames().length, jsNumber(exec, 2), ReadOnly | DontDelete | DontEnum);
290}
291
292/*
293 To facilitate result caching, exec(), test(), match(), search(), and replace() dipatch regular
294 expression matching through the performMatch function. We use cached results to calculate,
295 e.g., RegExp.lastMatch and RegExp.leftParen.
296*/
297void RegExpConstructor::performMatch(RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector)
298{
299 OwnArrayPtr<int> tmpOvector;
300 position = r->match(s, startOffset, &tmpOvector);
301
302 if (ovector)
303 *ovector = tmpOvector.get();
304
305 if (position != -1) {
306 ASSERT(tmpOvector);
307
308 length = tmpOvector[1] - tmpOvector[0];
309
310 d->lastInput = s;
311 d->lastOvector.set(tmpOvector.release());
312 d->lastNumSubPatterns = r->numSubpatterns();
313 }
314}
315
316class RegExpMatchesArray : public JSArray {
317public:
318 RegExpMatchesArray(ExecState*, RegExpConstructorPrivate*);
319 virtual ~RegExpMatchesArray();
320
321private:
322 virtual bool getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::getOwnPropertySlot(exec, propertyName, slot); }
323 virtual bool getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::getOwnPropertySlot(exec, propertyName, slot); }
324 virtual void put(ExecState* exec, const Identifier& propertyName, JSValue* v) { if (lazyCreationData()) fillArrayInstance(exec); JSArray::put(exec, propertyName, v); }
325 virtual void put(ExecState* exec, unsigned propertyName, JSValue* v) { if (lazyCreationData()) fillArrayInstance(exec); JSArray::put(exec, propertyName, v); }
326 virtual bool deleteProperty(ExecState* exec, const Identifier& propertyName) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::deleteProperty(exec, propertyName); }
327 virtual bool deleteProperty(ExecState* exec, unsigned propertyName) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::deleteProperty(exec, propertyName); }
328 virtual void getPropertyNames(ExecState* exec, PropertyNameArray& arr) { if (lazyCreationData()) fillArrayInstance(exec); JSArray::getPropertyNames(exec, arr); }
329
330 void fillArrayInstance(ExecState*);
331};
332
333RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate* data)
334 : JSArray(exec->lexicalGlobalObject()->arrayPrototype(), data->lastNumSubPatterns + 1)
335{
336 RegExpConstructorPrivate* d = new RegExpConstructorPrivate;
337 d->lastInput = data->lastInput;
338 d->lastNumSubPatterns = data->lastNumSubPatterns;
339 unsigned offsetVectorSize = (data->lastNumSubPatterns + 1) * 2; // only copying the result part of the vector
340 d->lastOvector.set(new int[offsetVectorSize]);
341 memcpy(d->lastOvector.get(), data->lastOvector.get(), offsetVectorSize * sizeof(int));
342 // d->multiline is not needed, and remains uninitialized
343
344 setLazyCreationData(d);
345}
346
347RegExpMatchesArray::~RegExpMatchesArray()
348{
349 delete static_cast<RegExpConstructorPrivate*>(lazyCreationData());
350}
351
352void RegExpMatchesArray::fillArrayInstance(ExecState* exec)
353{
354 RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(lazyCreationData());
355 ASSERT(d);
356
357 unsigned lastNumSubpatterns = d->lastNumSubPatterns;
358
359 for (unsigned i = 0; i <= lastNumSubpatterns; ++i) {
360 int start = d->lastOvector[2 * i];
361 if (start >= 0)
362 JSArray::put(exec, i, jsString(exec, d->lastInput.substr(start, d->lastOvector[2 * i + 1] - start)));
363 }
364 JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector[0]));
365 JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->lastInput));
366
367 delete d;
368 setLazyCreationData(0);
369}
370
371JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const
372{
373 return new (exec) RegExpMatchesArray(exec, d.get());
374}
375
376JSValue* RegExpConstructor::getBackref(ExecState* exec, unsigned i) const
377{
378 if (d->lastOvector && i <= d->lastNumSubPatterns)
379 return jsString(exec, d->lastInput.substr(d->lastOvector[2 * i], d->lastOvector[2 * i + 1] - d->lastOvector[2 * i]));
380 return jsString(exec, "");
381}
382
383JSValue* RegExpConstructor::getLastParen(ExecState* exec) const
384{
385 unsigned i = d->lastNumSubPatterns;
386 if (i > 0) {
387 ASSERT(d->lastOvector);
388 return jsString(exec, d->lastInput.substr(d->lastOvector[2 * i], d->lastOvector[2 * i + 1] - d->lastOvector[2 * i]));
389 }
390 return jsString(exec, "");
391}
392
393JSValue* RegExpConstructor::getLeftContext(ExecState* exec) const
394{
395 if (d->lastOvector)
396 return jsString(exec, d->lastInput.substr(0, d->lastOvector[0]));
397 return jsString(exec, "");
398}
399
400JSValue* RegExpConstructor::getRightContext(ExecState* exec) const
401{
402 if (d->lastOvector) {
403 UString s = d->lastInput;
404 return jsString(exec, s.substr(d->lastOvector[1], s.size() - d->lastOvector[1]));
405 }
406 return jsString(exec, "");
407}
408
409bool RegExpConstructor::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
410{
411 return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, slot);
412}
413
414JSValue *RegExpConstructor::getValueProperty(ExecState* exec, int token) const
415{
416 switch (token) {
417 case Dollar1:
418 return getBackref(exec, 1);
419 case Dollar2:
420 return getBackref(exec, 2);
421 case Dollar3:
422 return getBackref(exec, 3);
423 case Dollar4:
424 return getBackref(exec, 4);
425 case Dollar5:
426 return getBackref(exec, 5);
427 case Dollar6:
428 return getBackref(exec, 6);
429 case Dollar7:
430 return getBackref(exec, 7);
431 case Dollar8:
432 return getBackref(exec, 8);
433 case Dollar9:
434 return getBackref(exec, 9);
435 case Input:
436 return jsString(exec, d->lastInput);
437 case Multiline:
438 return jsBoolean(d->multiline);
439 case LastMatch:
440 return getBackref(exec, 0);
441 case LastParen:
442 return getLastParen(exec);
443 case LeftContext:
444 return getLeftContext(exec);
445 case RightContext:
446 return getRightContext(exec);
447 default:
448 ASSERT_NOT_REACHED();
449 }
450
451 return jsString(exec, "");
452}
453
454void RegExpConstructor::put(ExecState *exec, const Identifier &propertyName, JSValue *value)
455{
456 lookupPut<RegExpConstructor, InternalFunction>(exec, propertyName, value, ExecState::regExpConstructorTable(exec), this);
457}
458
459void RegExpConstructor::putValueProperty(ExecState *exec, int token, JSValue *value)
460{
461 switch (token) {
462 case Input:
463 d->lastInput = value->toString(exec);
464 break;
465 case Multiline:
466 d->multiline = value->toBoolean(exec);
467 break;
468 default:
469 ASSERT(0);
470 }
471}
472
473// ECMA 15.10.4
474static JSObject* constructRegExp(ExecState* exec, const ArgList& args)
475{
476 JSValue* arg0 = args[0];
477 JSValue* arg1 = args[1];
478
479 if (arg0->isObject(&RegExpObject::info)) {
480 if (!arg1->isUndefined())
481 return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another.");
482 return static_cast<JSObject*>(arg0);
483 }
484
485 UString pattern = arg0->isUndefined() ? UString("") : arg0->toString(exec);
486 UString flags = arg1->isUndefined() ? UString("") : arg1->toString(exec);
487
488 RefPtr<RegExp> regExp = RegExp::create(pattern, flags);
489 return regExp->isValid()
490 ? new (exec) RegExpObject(exec->lexicalGlobalObject()->regExpPrototype(), regExp.release())
491 : throwError(exec, SyntaxError, UString("Invalid regular expression: ").append(regExp->errorMessage()));
492}
493
494static JSObject* constructWithRegExpConstructor(ExecState* exec, JSObject*, const ArgList& args)
495{
496 return constructRegExp(exec, args);
497}
498
499ConstructType RegExpConstructor::getConstructData(ConstructData& constructData)
500{
501 constructData.native.function = constructWithRegExpConstructor;
502 return ConstructTypeNative;
503}
504
505// ECMA 15.10.3
506static JSValue* callRegExpConstructor(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
507{
508 return constructRegExp(exec, args);
509}
510
511CallType RegExpConstructor::getCallData(CallData& callData)
512{
513 callData.native.function = callRegExpConstructor;
514 return CallTypeNative;
515}
516
517const UString& RegExpConstructor::input() const
518{
519 // Can detect a distinct initial state that is invisible to JavaScript, by checking for null
520 // state (since jsString turns null strings to empty strings).
521 return d->lastInput;
522}
523
524}
Note: See TracBrowser for help on using the repository browser.