]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
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 | |
93 | 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 | ||
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 | */ | |
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 | ||
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 | ||
157 | RegExpMatchesArray::~RegExpMatchesArray() | |
158 | { | |
159 | delete static_cast<RegExpConstructorPrivate*>(lazyCreationData()); | |
160 | } | |
161 | ||
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 | ||
183 | JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const | |
184 | { | |
185 | return new (exec) RegExpMatchesArray(exec, d.get()); | |
186 | } | |
187 | ||
188 | 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 | ||
198 | 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 | ||
210 | 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 | ||
217 | 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 | ||
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 | ||
229 | JSValuePtr regExpConstructorDollar1(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
230 | { | |
231 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 1); | |
232 | } | |
233 | ||
234 | JSValuePtr regExpConstructorDollar2(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
235 | { | |
236 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 2); | |
237 | } | |
238 | ||
239 | JSValuePtr regExpConstructorDollar3(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
240 | { | |
241 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 3); | |
242 | } | |
243 | ||
244 | JSValuePtr regExpConstructorDollar4(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
245 | { | |
246 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 4); | |
247 | } | |
248 | ||
249 | JSValuePtr regExpConstructorDollar5(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
250 | { | |
251 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 5); | |
252 | } | |
253 | ||
254 | JSValuePtr regExpConstructorDollar6(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
255 | { | |
256 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 6); | |
257 | } | |
258 | ||
259 | JSValuePtr regExpConstructorDollar7(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
260 | { | |
261 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 7); | |
262 | } | |
263 | ||
264 | JSValuePtr regExpConstructorDollar8(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
265 | { | |
266 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 8); | |
267 | } | |
268 | ||
269 | JSValuePtr regExpConstructorDollar9(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
270 | { | |
271 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 9); | |
272 | } | |
273 | ||
274 | JSValuePtr regExpConstructorInput(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
275 | { | |
276 | return jsString(exec, asRegExpConstructor(slot.slotBase())->input()); | |
277 | } | |
278 | ||
279 | JSValuePtr regExpConstructorMultiline(ExecState*, const Identifier&, const PropertySlot& slot) | |
280 | { | |
281 | return jsBoolean(asRegExpConstructor(slot.slotBase())->multiline()); | |
282 | } | |
283 | ||
284 | JSValuePtr regExpConstructorLastMatch(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
285 | { | |
286 | return asRegExpConstructor(slot.slotBase())->getBackref(exec, 0); | |
287 | } | |
288 | ||
289 | JSValuePtr regExpConstructorLastParen(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
290 | { | |
291 | return asRegExpConstructor(slot.slotBase())->getLastParen(exec); | |
292 | } | |
293 | ||
294 | JSValuePtr regExpConstructorLeftContext(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
295 | { | |
296 | return asRegExpConstructor(slot.slotBase())->getLeftContext(exec); | |
297 | } | |
298 | ||
299 | JSValuePtr regExpConstructorRightContext(ExecState* exec, const Identifier&, const PropertySlot& slot) | |
300 | { | |
301 | return asRegExpConstructor(slot.slotBase())->getRightContext(exec); | |
302 | } | |
303 | ||
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 | ||
309 | void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, JSValuePtr value) | |
310 | { | |
311 | asRegExpConstructor(baseObject)->setInput(value.toString(exec)); | |
312 | } | |
313 | ||
314 | void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, JSValuePtr value) | |
315 | { | |
316 | asRegExpConstructor(baseObject)->setMultiline(value.toBoolean(exec)); | |
317 | } | |
318 | ||
319 | // ECMA 15.10.4 | |
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 | ||
340 | static JSObject* constructWithRegExpConstructor(ExecState* exec, JSObject*, const ArgList& args) | |
341 | { | |
342 | return constructRegExp(exec, args); | |
343 | } | |
344 | ||
345 | ConstructType RegExpConstructor::getConstructData(ConstructData& constructData) | |
346 | { | |
347 | constructData.native.function = constructWithRegExpConstructor; | |
348 | return ConstructTypeHost; | |
349 | } | |
350 | ||
351 | // ECMA 15.10.3 | |
352 | static JSValuePtr callRegExpConstructor(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args) | |
353 | { | |
354 | return constructRegExp(exec, args); | |
355 | } | |
356 | ||
357 | CallType RegExpConstructor::getCallData(CallData& callData) | |
358 | { | |
359 | callData.native.function = callRegExpConstructor; | |
360 | return CallTypeHost; | |
361 | } | |
362 | ||
363 | void RegExpConstructor::setInput(const UString& input) | |
364 | { | |
365 | d->input = input; | |
366 | } | |
367 | ||
368 | 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 | ||
375 | void RegExpConstructor::setMultiline(bool multiline) | |
376 | { | |
377 | d->multiline = multiline; | |
378 | } | |
379 | ||
380 | bool RegExpConstructor::multiline() const | |
381 | { | |
382 | return d->multiline; | |
383 | } | |
384 | ||
385 | } // namespace JSC |