2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved.
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.
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.
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
22 #include "RegExpConstructor.h"
23 #include "RegExpConstructor.lut.h"
25 #include "ArrayPrototype.h"
27 #include "JSFunction.h"
29 #include "ObjectPrototype.h"
30 #include "RegExpObject.h"
31 #include "RegExpPrototype.h"
36 const ClassInfo RegExpConstructor::info = { "Function", &InternalFunction::info, 0, ExecState::regExpConstructorTable };
38 /* Source for RegExpConstructor.lut.h
39 @begin regExpConstructorTable
40 input RegExpConstructor::Input None
41 $_ RegExpConstructor::Input DontEnum
42 multiline RegExpConstructor::Multiline None
43 $* RegExpConstructor::Multiline DontEnum
44 lastMatch RegExpConstructor::LastMatch DontDelete|ReadOnly
45 $& RegExpConstructor::LastMatch DontDelete|ReadOnly|DontEnum
46 lastParen RegExpConstructor::LastParen DontDelete|ReadOnly
47 $+ RegExpConstructor::LastParen DontDelete|ReadOnly|DontEnum
48 leftContext RegExpConstructor::LeftContext DontDelete|ReadOnly
49 $` RegExpConstructor::LeftContext DontDelete|ReadOnly|DontEnum
50 rightContext RegExpConstructor::RightContext DontDelete|ReadOnly
51 $' RegExpConstructor::RightContext DontDelete|ReadOnly|DontEnum
52 $1 RegExpConstructor::Dollar1 DontDelete|ReadOnly
53 $2 RegExpConstructor::Dollar2 DontDelete|ReadOnly
54 $3 RegExpConstructor::Dollar3 DontDelete|ReadOnly
55 $4 RegExpConstructor::Dollar4 DontDelete|ReadOnly
56 $5 RegExpConstructor::Dollar5 DontDelete|ReadOnly
57 $6 RegExpConstructor::Dollar6 DontDelete|ReadOnly
58 $7 RegExpConstructor::Dollar7 DontDelete|ReadOnly
59 $8 RegExpConstructor::Dollar8 DontDelete|ReadOnly
60 $9 RegExpConstructor::Dollar9 DontDelete|ReadOnly
64 struct RegExpConstructorPrivate {
65 // Global search cache / settings
66 RegExpConstructorPrivate() : lastNumSubPatterns(0), multiline(false) { }
68 OwnArrayPtr<int> lastOvector;
69 unsigned lastNumSubPatterns : 31;
73 RegExpConstructor::RegExpConstructor(ExecState* exec, FunctionPrototype* funcProto, RegExpPrototype* regProto)
74 : InternalFunction(funcProto, Identifier(exec, "RegExp"))
75 , d(new RegExpConstructorPrivate)
77 // ECMA 15.10.5.1 RegExp.prototype
78 putDirect(exec->propertyNames().prototype, regProto, DontEnum | DontDelete | ReadOnly);
80 // no. of arguments for constructor
81 putDirect(exec->propertyNames().length, jsNumber(exec, 2), ReadOnly | DontDelete | DontEnum);
85 To facilitate result caching, exec(), test(), match(), search(), and replace() dipatch regular
86 expression matching through the performMatch function. We use cached results to calculate,
87 e.g., RegExp.lastMatch and RegExp.leftParen.
89 void RegExpConstructor::performMatch(RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector)
91 OwnArrayPtr<int> tmpOvector;
92 position = r->match(s, startOffset, &tmpOvector);
95 *ovector = tmpOvector.get();
100 length = tmpOvector[1] - tmpOvector[0];
103 d->lastOvector.set(tmpOvector.release());
104 d->lastNumSubPatterns = r->numSubpatterns();
108 class RegExpMatchesArray : public JSArray {
110 RegExpMatchesArray(ExecState*, RegExpConstructorPrivate*);
111 virtual ~RegExpMatchesArray();
114 virtual bool getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::getOwnPropertySlot(exec, propertyName, slot); }
115 virtual bool getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::getOwnPropertySlot(exec, propertyName, slot); }
116 virtual void put(ExecState* exec, const Identifier& propertyName, JSValue* v) { if (lazyCreationData()) fillArrayInstance(exec); JSArray::put(exec, propertyName, v); }
117 virtual void put(ExecState* exec, unsigned propertyName, JSValue* v) { if (lazyCreationData()) fillArrayInstance(exec); JSArray::put(exec, propertyName, v); }
118 virtual bool deleteProperty(ExecState* exec, const Identifier& propertyName) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::deleteProperty(exec, propertyName); }
119 virtual bool deleteProperty(ExecState* exec, unsigned propertyName) { if (lazyCreationData()) fillArrayInstance(exec); return JSArray::deleteProperty(exec, propertyName); }
120 virtual void getPropertyNames(ExecState* exec, PropertyNameArray& arr) { if (lazyCreationData()) fillArrayInstance(exec); JSArray::getPropertyNames(exec, arr); }
122 void fillArrayInstance(ExecState*);
125 RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate* data)
126 : JSArray(exec->lexicalGlobalObject()->arrayPrototype(), data->lastNumSubPatterns + 1)
128 RegExpConstructorPrivate* d = new RegExpConstructorPrivate;
129 d->lastInput = data->lastInput;
130 d->lastNumSubPatterns = data->lastNumSubPatterns;
131 unsigned offsetVectorSize = (data->lastNumSubPatterns + 1) * 2; // only copying the result part of the vector
132 d->lastOvector.set(new int[offsetVectorSize]);
133 memcpy(d->lastOvector.get(), data->lastOvector.get(), offsetVectorSize * sizeof(int));
134 // d->multiline is not needed, and remains uninitialized
136 setLazyCreationData(d);
139 RegExpMatchesArray::~RegExpMatchesArray()
141 delete static_cast<RegExpConstructorPrivate*>(lazyCreationData());
144 void RegExpMatchesArray::fillArrayInstance(ExecState* exec)
146 RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(lazyCreationData());
149 unsigned lastNumSubpatterns = d->lastNumSubPatterns;
151 for (unsigned i = 0; i <= lastNumSubpatterns; ++i) {
152 int start = d->lastOvector[2 * i];
154 JSArray::put(exec, i, jsString(exec, d->lastInput.substr(start, d->lastOvector[2 * i + 1] - start)));
156 JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector[0]));
157 JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->lastInput));
160 setLazyCreationData(0);
163 JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const
165 return new (exec) RegExpMatchesArray(exec, d.get());
168 JSValue* RegExpConstructor::getBackref(ExecState* exec, unsigned i) const
170 if (d->lastOvector && i <= d->lastNumSubPatterns)
171 return jsString(exec, d->lastInput.substr(d->lastOvector[2 * i], d->lastOvector[2 * i + 1] - d->lastOvector[2 * i]));
172 return jsString(exec, "");
175 JSValue* RegExpConstructor::getLastParen(ExecState* exec) const
177 unsigned i = d->lastNumSubPatterns;
179 ASSERT(d->lastOvector);
180 return jsString(exec, d->lastInput.substr(d->lastOvector[2 * i], d->lastOvector[2 * i + 1] - d->lastOvector[2 * i]));
182 return jsString(exec, "");
185 JSValue* RegExpConstructor::getLeftContext(ExecState* exec) const
188 return jsString(exec, d->lastInput.substr(0, d->lastOvector[0]));
189 return jsString(exec, "");
192 JSValue* RegExpConstructor::getRightContext(ExecState* exec) const
194 if (d->lastOvector) {
195 UString s = d->lastInput;
196 return jsString(exec, s.substr(d->lastOvector[1], s.size() - d->lastOvector[1]));
198 return jsString(exec, "");
201 bool RegExpConstructor::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
203 return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, slot);
206 JSValue *RegExpConstructor::getValueProperty(ExecState* exec, int token) const
210 return getBackref(exec, 1);
212 return getBackref(exec, 2);
214 return getBackref(exec, 3);
216 return getBackref(exec, 4);
218 return getBackref(exec, 5);
220 return getBackref(exec, 6);
222 return getBackref(exec, 7);
224 return getBackref(exec, 8);
226 return getBackref(exec, 9);
228 return jsString(exec, d->lastInput);
230 return jsBoolean(d->multiline);
232 return getBackref(exec, 0);
234 return getLastParen(exec);
236 return getLeftContext(exec);
238 return getRightContext(exec);
240 ASSERT_NOT_REACHED();
243 return jsString(exec, "");
246 void RegExpConstructor::put(ExecState *exec, const Identifier &propertyName, JSValue *value)
248 lookupPut<RegExpConstructor, InternalFunction>(exec, propertyName, value, ExecState::regExpConstructorTable(exec), this);
251 void RegExpConstructor::putValueProperty(ExecState *exec, int token, JSValue *value)
255 d->lastInput = value->toString(exec);
258 d->multiline = value->toBoolean(exec);
266 static JSObject* constructRegExp(ExecState* exec, const ArgList& args)
268 JSValue* arg0 = args[0];
269 JSValue* arg1 = args[1];
271 if (arg0->isObject(&RegExpObject::info)) {
272 if (!arg1->isUndefined())
273 return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another.");
274 return static_cast<JSObject*>(arg0);
277 UString pattern = arg0->isUndefined() ? UString("") : arg0->toString(exec);
278 UString flags = arg1->isUndefined() ? UString("") : arg1->toString(exec);
280 RefPtr<RegExp> regExp = RegExp::create(pattern, flags);
281 return regExp->isValid()
282 ? new (exec) RegExpObject(exec->lexicalGlobalObject()->regExpPrototype(), regExp.release())
283 : throwError(exec, SyntaxError, UString("Invalid regular expression: ").append(regExp->errorMessage()));
286 static JSObject* constructWithRegExpConstructor(ExecState* exec, JSObject*, const ArgList& args)
288 return constructRegExp(exec, args);
291 ConstructType RegExpConstructor::getConstructData(ConstructData& constructData)
293 constructData.native.function = constructWithRegExpConstructor;
294 return ConstructTypeNative;
298 static JSValue* callRegExpConstructor(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
300 return constructRegExp(exec, args);
303 CallType RegExpConstructor::getCallData(CallData& callData)
305 callData.native.function = callRegExpConstructor;
306 return CallTypeNative;
309 const UString& RegExpConstructor::input() const
311 // Can detect a distinct initial state that is invisible to JavaScript, by checking for null
312 // state (since jsString turns null strings to empty strings).