|
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 "Error.h" |
|
27 #include "ExceptionHelpers.h" |
|
28 #include "JSArray.h" |
|
29 #include "JSFunction.h" |
|
30 #include "JSString.h" |
|
31 #include "Lookup.h" |
|
32 #include "ObjectPrototype.h" |
|
33 #include "RegExpMatchesArray.h" |
|
34 #include "RegExpObject.h" |
|
35 #include "RegExpPrototype.h" |
|
36 #include "RegExp.h" |
|
37 #include "RegExpCache.h" |
|
38 #include <wtf/PassOwnPtr.h> |
|
39 |
|
40 namespace JSC { |
|
41 |
|
42 static JSValue regExpConstructorInput(ExecState*, JSValue, const Identifier&); |
|
43 static JSValue regExpConstructorMultiline(ExecState*, JSValue, const Identifier&); |
|
44 static JSValue regExpConstructorLastMatch(ExecState*, JSValue, const Identifier&); |
|
45 static JSValue regExpConstructorLastParen(ExecState*, JSValue, const Identifier&); |
|
46 static JSValue regExpConstructorLeftContext(ExecState*, JSValue, const Identifier&); |
|
47 static JSValue regExpConstructorRightContext(ExecState*, JSValue, const Identifier&); |
|
48 static JSValue regExpConstructorDollar1(ExecState*, JSValue, const Identifier&); |
|
49 static JSValue regExpConstructorDollar2(ExecState*, JSValue, const Identifier&); |
|
50 static JSValue regExpConstructorDollar3(ExecState*, JSValue, const Identifier&); |
|
51 static JSValue regExpConstructorDollar4(ExecState*, JSValue, const Identifier&); |
|
52 static JSValue regExpConstructorDollar5(ExecState*, JSValue, const Identifier&); |
|
53 static JSValue regExpConstructorDollar6(ExecState*, JSValue, const Identifier&); |
|
54 static JSValue regExpConstructorDollar7(ExecState*, JSValue, const Identifier&); |
|
55 static JSValue regExpConstructorDollar8(ExecState*, JSValue, const Identifier&); |
|
56 static JSValue regExpConstructorDollar9(ExecState*, JSValue, const Identifier&); |
|
57 |
|
58 static void setRegExpConstructorInput(ExecState*, JSObject*, JSValue); |
|
59 static void setRegExpConstructorMultiline(ExecState*, JSObject*, JSValue); |
|
60 |
|
61 } // namespace JSC |
|
62 |
|
63 #include "RegExpConstructor.lut.h" |
|
64 |
|
65 namespace JSC { |
|
66 |
|
67 ASSERT_CLASS_FITS_IN_CELL(RegExpConstructor); |
|
68 |
|
69 const ClassInfo RegExpConstructor::info = { "Function", &InternalFunction::info, 0, ExecState::regExpConstructorTable }; |
|
70 |
|
71 /* Source for RegExpConstructor.lut.h |
|
72 @begin regExpConstructorTable |
|
73 input regExpConstructorInput None |
|
74 $_ regExpConstructorInput DontEnum |
|
75 multiline regExpConstructorMultiline None |
|
76 $* regExpConstructorMultiline DontEnum |
|
77 lastMatch regExpConstructorLastMatch DontDelete|ReadOnly |
|
78 $& regExpConstructorLastMatch DontDelete|ReadOnly|DontEnum |
|
79 lastParen regExpConstructorLastParen DontDelete|ReadOnly |
|
80 $+ regExpConstructorLastParen DontDelete|ReadOnly|DontEnum |
|
81 leftContext regExpConstructorLeftContext DontDelete|ReadOnly |
|
82 $` regExpConstructorLeftContext DontDelete|ReadOnly|DontEnum |
|
83 rightContext regExpConstructorRightContext DontDelete|ReadOnly |
|
84 $' regExpConstructorRightContext DontDelete|ReadOnly|DontEnum |
|
85 $1 regExpConstructorDollar1 DontDelete|ReadOnly |
|
86 $2 regExpConstructorDollar2 DontDelete|ReadOnly |
|
87 $3 regExpConstructorDollar3 DontDelete|ReadOnly |
|
88 $4 regExpConstructorDollar4 DontDelete|ReadOnly |
|
89 $5 regExpConstructorDollar5 DontDelete|ReadOnly |
|
90 $6 regExpConstructorDollar6 DontDelete|ReadOnly |
|
91 $7 regExpConstructorDollar7 DontDelete|ReadOnly |
|
92 $8 regExpConstructorDollar8 DontDelete|ReadOnly |
|
93 $9 regExpConstructorDollar9 DontDelete|ReadOnly |
|
94 @end |
|
95 */ |
|
96 |
|
97 RegExpConstructor::RegExpConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) |
|
98 : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "RegExp")) |
|
99 , d(adoptPtr(new RegExpConstructorPrivate)) |
|
100 { |
|
101 // ECMA 15.10.5.1 RegExp.prototype |
|
102 putDirectWithoutTransition(exec->propertyNames().prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly); |
|
103 |
|
104 // no. of arguments for constructor |
|
105 putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 2), ReadOnly | DontDelete | DontEnum); |
|
106 } |
|
107 |
|
108 RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate* data) |
|
109 : JSArray(exec->lexicalGlobalObject()->regExpMatchesArrayStructure(), data->lastNumSubPatterns + 1, CreateInitialized) |
|
110 { |
|
111 RegExpConstructorPrivate* d = new RegExpConstructorPrivate; |
|
112 d->input = data->lastInput; |
|
113 d->lastInput = data->lastInput; |
|
114 d->lastNumSubPatterns = data->lastNumSubPatterns; |
|
115 unsigned offsetVectorSize = (data->lastNumSubPatterns + 1) * 2; // only copying the result part of the vector |
|
116 d->lastOvector().resize(offsetVectorSize); |
|
117 memcpy(d->lastOvector().data(), data->lastOvector().data(), offsetVectorSize * sizeof(int)); |
|
118 // d->multiline is not needed, and remains uninitialized |
|
119 |
|
120 setSubclassData(d); |
|
121 } |
|
122 |
|
123 RegExpMatchesArray::~RegExpMatchesArray() |
|
124 { |
|
125 delete static_cast<RegExpConstructorPrivate*>(subclassData()); |
|
126 } |
|
127 |
|
128 void RegExpMatchesArray::fillArrayInstance(ExecState* exec) |
|
129 { |
|
130 RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(subclassData()); |
|
131 ASSERT(d); |
|
132 |
|
133 unsigned lastNumSubpatterns = d->lastNumSubPatterns; |
|
134 |
|
135 for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { |
|
136 int start = d->lastOvector()[2 * i]; |
|
137 if (start >= 0) |
|
138 JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start)); |
|
139 else |
|
140 JSArray::put(exec, i, jsUndefined()); |
|
141 } |
|
142 |
|
143 PutPropertySlot slot; |
|
144 JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector()[0]), slot); |
|
145 JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->input), slot); |
|
146 |
|
147 delete d; |
|
148 setSubclassData(0); |
|
149 } |
|
150 |
|
151 JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const |
|
152 { |
|
153 return new (exec) RegExpMatchesArray(exec, d.get()); |
|
154 } |
|
155 |
|
156 JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) const |
|
157 { |
|
158 if (!d->lastOvector().isEmpty() && i <= d->lastNumSubPatterns) { |
|
159 int start = d->lastOvector()[2 * i]; |
|
160 if (start >= 0) |
|
161 return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); |
|
162 } |
|
163 return jsEmptyString(exec); |
|
164 } |
|
165 |
|
166 JSValue RegExpConstructor::getLastParen(ExecState* exec) const |
|
167 { |
|
168 unsigned i = d->lastNumSubPatterns; |
|
169 if (i > 0) { |
|
170 ASSERT(!d->lastOvector().isEmpty()); |
|
171 int start = d->lastOvector()[2 * i]; |
|
172 if (start >= 0) |
|
173 return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); |
|
174 } |
|
175 return jsEmptyString(exec); |
|
176 } |
|
177 |
|
178 JSValue RegExpConstructor::getLeftContext(ExecState* exec) const |
|
179 { |
|
180 if (!d->lastOvector().isEmpty()) |
|
181 return jsSubstring(exec, d->lastInput, 0, d->lastOvector()[0]); |
|
182 return jsEmptyString(exec); |
|
183 } |
|
184 |
|
185 JSValue RegExpConstructor::getRightContext(ExecState* exec) const |
|
186 { |
|
187 if (!d->lastOvector().isEmpty()) |
|
188 return jsSubstring(exec, d->lastInput, d->lastOvector()[1], d->lastInput.size() - d->lastOvector()[1]); |
|
189 return jsEmptyString(exec); |
|
190 } |
|
191 |
|
192 bool RegExpConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
|
193 { |
|
194 return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, slot); |
|
195 } |
|
196 |
|
197 bool RegExpConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) |
|
198 { |
|
199 return getStaticValueDescriptor<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, descriptor); |
|
200 } |
|
201 |
|
202 JSValue regExpConstructorDollar1(ExecState* exec, JSValue slotBase, const Identifier&) |
|
203 { |
|
204 return asRegExpConstructor(slotBase)->getBackref(exec, 1); |
|
205 } |
|
206 |
|
207 JSValue regExpConstructorDollar2(ExecState* exec, JSValue slotBase, const Identifier&) |
|
208 { |
|
209 return asRegExpConstructor(slotBase)->getBackref(exec, 2); |
|
210 } |
|
211 |
|
212 JSValue regExpConstructorDollar3(ExecState* exec, JSValue slotBase, const Identifier&) |
|
213 { |
|
214 return asRegExpConstructor(slotBase)->getBackref(exec, 3); |
|
215 } |
|
216 |
|
217 JSValue regExpConstructorDollar4(ExecState* exec, JSValue slotBase, const Identifier&) |
|
218 { |
|
219 return asRegExpConstructor(slotBase)->getBackref(exec, 4); |
|
220 } |
|
221 |
|
222 JSValue regExpConstructorDollar5(ExecState* exec, JSValue slotBase, const Identifier&) |
|
223 { |
|
224 return asRegExpConstructor(slotBase)->getBackref(exec, 5); |
|
225 } |
|
226 |
|
227 JSValue regExpConstructorDollar6(ExecState* exec, JSValue slotBase, const Identifier&) |
|
228 { |
|
229 return asRegExpConstructor(slotBase)->getBackref(exec, 6); |
|
230 } |
|
231 |
|
232 JSValue regExpConstructorDollar7(ExecState* exec, JSValue slotBase, const Identifier&) |
|
233 { |
|
234 return asRegExpConstructor(slotBase)->getBackref(exec, 7); |
|
235 } |
|
236 |
|
237 JSValue regExpConstructorDollar8(ExecState* exec, JSValue slotBase, const Identifier&) |
|
238 { |
|
239 return asRegExpConstructor(slotBase)->getBackref(exec, 8); |
|
240 } |
|
241 |
|
242 JSValue regExpConstructorDollar9(ExecState* exec, JSValue slotBase, const Identifier&) |
|
243 { |
|
244 return asRegExpConstructor(slotBase)->getBackref(exec, 9); |
|
245 } |
|
246 |
|
247 JSValue regExpConstructorInput(ExecState* exec, JSValue slotBase, const Identifier&) |
|
248 { |
|
249 return jsString(exec, asRegExpConstructor(slotBase)->input()); |
|
250 } |
|
251 |
|
252 JSValue regExpConstructorMultiline(ExecState*, JSValue slotBase, const Identifier&) |
|
253 { |
|
254 return jsBoolean(asRegExpConstructor(slotBase)->multiline()); |
|
255 } |
|
256 |
|
257 JSValue regExpConstructorLastMatch(ExecState* exec, JSValue slotBase, const Identifier&) |
|
258 { |
|
259 return asRegExpConstructor(slotBase)->getBackref(exec, 0); |
|
260 } |
|
261 |
|
262 JSValue regExpConstructorLastParen(ExecState* exec, JSValue slotBase, const Identifier&) |
|
263 { |
|
264 return asRegExpConstructor(slotBase)->getLastParen(exec); |
|
265 } |
|
266 |
|
267 JSValue regExpConstructorLeftContext(ExecState* exec, JSValue slotBase, const Identifier&) |
|
268 { |
|
269 return asRegExpConstructor(slotBase)->getLeftContext(exec); |
|
270 } |
|
271 |
|
272 JSValue regExpConstructorRightContext(ExecState* exec, JSValue slotBase, const Identifier&) |
|
273 { |
|
274 return asRegExpConstructor(slotBase)->getRightContext(exec); |
|
275 } |
|
276 |
|
277 void RegExpConstructor::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) |
|
278 { |
|
279 lookupPut<RegExpConstructor, InternalFunction>(exec, propertyName, value, ExecState::regExpConstructorTable(exec), this, slot); |
|
280 } |
|
281 |
|
282 void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, JSValue value) |
|
283 { |
|
284 asRegExpConstructor(baseObject)->setInput(value.toString(exec)); |
|
285 } |
|
286 |
|
287 void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, JSValue value) |
|
288 { |
|
289 asRegExpConstructor(baseObject)->setMultiline(value.toBoolean(exec)); |
|
290 } |
|
291 |
|
292 // ECMA 15.10.4 |
|
293 JSObject* constructRegExp(ExecState* exec, const ArgList& args) |
|
294 { |
|
295 JSValue arg0 = args.at(0); |
|
296 JSValue arg1 = args.at(1); |
|
297 |
|
298 if (arg0.inherits(&RegExpObject::info)) { |
|
299 if (!arg1.isUndefined()) |
|
300 return throwError(exec, createTypeError(exec, "Cannot supply flags when constructing one RegExp from another.")); |
|
301 return asObject(arg0); |
|
302 } |
|
303 |
|
304 UString pattern = arg0.isUndefined() ? UString("") : arg0.toString(exec); |
|
305 UString flags = arg1.isUndefined() ? UString("") : arg1.toString(exec); |
|
306 |
|
307 RefPtr<RegExp> regExp = exec->globalData().regExpCache()->lookupOrCreate(pattern, flags); |
|
308 if (!regExp->isValid()) |
|
309 return throwError(exec, createSyntaxError(exec, makeString("Invalid regular expression: ", regExp->errorMessage()))); |
|
310 return new (exec) RegExpObject(exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp.release()); |
|
311 } |
|
312 |
|
313 static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec) |
|
314 { |
|
315 ArgList args(exec); |
|
316 return JSValue::encode(constructRegExp(exec, args)); |
|
317 } |
|
318 |
|
319 ConstructType RegExpConstructor::getConstructData(ConstructData& constructData) |
|
320 { |
|
321 constructData.native.function = constructWithRegExpConstructor; |
|
322 return ConstructTypeHost; |
|
323 } |
|
324 |
|
325 // ECMA 15.10.3 |
|
326 static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec) |
|
327 { |
|
328 ArgList args(exec); |
|
329 return JSValue::encode(constructRegExp(exec, args)); |
|
330 } |
|
331 |
|
332 CallType RegExpConstructor::getCallData(CallData& callData) |
|
333 { |
|
334 callData.native.function = callRegExpConstructor; |
|
335 return CallTypeHost; |
|
336 } |
|
337 |
|
338 void RegExpConstructor::setInput(const UString& input) |
|
339 { |
|
340 d->input = input; |
|
341 } |
|
342 |
|
343 const UString& RegExpConstructor::input() const |
|
344 { |
|
345 // Can detect a distinct initial state that is invisible to JavaScript, by checking for null |
|
346 // state (since jsString turns null strings to empty strings). |
|
347 return d->input; |
|
348 } |
|
349 |
|
350 void RegExpConstructor::setMultiline(bool multiline) |
|
351 { |
|
352 d->multiline = multiline; |
|
353 } |
|
354 |
|
355 bool RegExpConstructor::multiline() const |
|
356 { |
|
357 return d->multiline; |
|
358 } |
|
359 |
|
360 } // namespace JSC |