-fix remaining regressions in background images painting.
[kdelibs.git] / kjs / regexp_object.cpp
blobaff69c30aaf76692e5467582d1634b659315fafd
1 // -*- c-basic-offset: 2 -*-
2 /*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
5 * Copyright (C) 2003 Apple Computer, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "config.h"
24 #include "regexp_object.h"
26 #include "regexp_object.lut.h"
28 #include <stdio.h>
29 #include "value.h"
30 #include "object.h"
31 #include "types.h"
32 #include "nodes.h"
33 #include "interpreter.h"
34 #include "operations.h"
35 #include "internal.h"
36 #include "regexp.h"
37 #include "error_object.h"
38 #include "lookup.h"
40 using namespace KJS;
42 // ------------------------------ RegExpPrototype ---------------------------
44 // ECMA 15.10.5
46 const ClassInfo RegExpPrototype::info = {"RegExp", 0, 0, 0};
48 RegExpPrototype::RegExpPrototype(ExecState *exec,
49 ObjectPrototype *objProto,
50 FunctionPrototype *funcProto)
51 : JSObject(objProto)
53 // The constructor will be added later in RegExpObject's constructor (?)
55 static const Identifier execPropertyName("exec");
56 static const Identifier testPropertyName("test");
57 static const Identifier compilePropertyName("compile");
58 putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::Exec, 0, execPropertyName), DontEnum);
59 putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::Test, 0, testPropertyName), DontEnum);
60 putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::ToString, 0, toStringPropertyName), DontEnum);
61 putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::Compile, 1, compilePropertyName), DontEnum);
64 // ------------------------------ RegExpProtoFunc ---------------------------
66 RegExpProtoFunc::RegExpProtoFunc(ExecState*, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
67 : InternalFunctionImp(funcProto, name), id(i)
69 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
72 JSValue *RegExpProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
74 if (!thisObj->inherits(&RegExpImp::info)) {
75 if (thisObj->inherits(&RegExpPrototype::info)) {
76 switch (id) {
77 case ToString: return jsString("//");
81 return throwError(exec, TypeError);
84 switch (id) {
85 case Test: // 15.10.6.2
86 case Exec:
88 RegExp *regExp = static_cast<RegExpImp*>(thisObj)->regExp();
89 RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp());
91 UString input;
92 if (args.isEmpty())
93 input = regExpObj->get(exec, "input")->toString(exec);
94 else
95 input = args[0]->toString(exec);
97 double lastIndex = thisObj->get(exec, "lastIndex")->toInteger(exec);
99 bool globalFlag = thisObj->get(exec, "global")->toBoolean(exec);
100 if (!globalFlag)
101 lastIndex = 0;
102 if (lastIndex < 0 || lastIndex > input.size()) {
103 thisObj->put(exec, "lastIndex", jsNumber(0), DontDelete | DontEnum);
104 return jsNull();
107 int foundIndex;
108 regExp->prepareMatch(input);
109 UString match = regExpObj->performMatch(regExp, input, static_cast<int>(lastIndex), &foundIndex);
110 regExp->doneMatch();
111 bool didMatch = !match.isNull();
113 // Test
114 if (id == Test)
115 return jsBoolean(didMatch);
117 // Exec
118 if (didMatch) {
119 if (globalFlag)
120 thisObj->put(exec, "lastIndex", jsNumber(foundIndex + match.size()), DontDelete | DontEnum);
121 return regExpObj->arrayOfMatches(exec, match);
122 } else {
123 if (globalFlag)
124 thisObj->put(exec, "lastIndex", jsNumber(0), DontDelete | DontEnum);
125 return jsNull();
128 break;
129 case ToString: {
130 UString result = "/" + thisObj->get(exec, "source")->toString(exec) + "/";
131 if (thisObj->get(exec, "global")->toBoolean(exec)) {
132 result += "g";
134 if (thisObj->get(exec, "ignoreCase")->toBoolean(exec)) {
135 result += "i";
137 if (thisObj->get(exec, "multiline")->toBoolean(exec)) {
138 result += "m";
140 return jsString(result);
142 case Compile: { // JS1.2 legacy, but still in use in the wild somewhat
143 RegExpImp* instance = static_cast<RegExpImp*>(thisObj);
144 RegExp* newEngine = RegExpObjectImp::makeEngine(exec, args[0]->toString(exec), args[1]);
145 if (!newEngine)
146 return exec->exception();
147 instance->setRegExp(newEngine);
148 return instance;
152 return jsUndefined();
155 // ------------------------------ RegExpImp ------------------------------------
157 const ClassInfo RegExpImp::info = {"RegExp", 0, 0, 0};
159 RegExpImp::RegExpImp(RegExpPrototype *regexpProto)
160 : JSObject(regexpProto), reg(0L)
164 RegExpImp::~RegExpImp()
166 delete reg;
169 void RegExpImp::setRegExp(RegExp *r)
171 delete reg;
172 reg = r;
174 putDirect("global", jsBoolean(r->flags() & RegExp::Global), DontDelete | ReadOnly | DontEnum);
175 putDirect("ignoreCase", jsBoolean(r->flags() & RegExp::IgnoreCase), DontDelete | ReadOnly | DontEnum);
176 putDirect("multiline", jsBoolean(r->flags() & RegExp::Multiline), DontDelete | ReadOnly | DontEnum);
177 putDirect("source", jsString (r->pattern()), DontDelete | ReadOnly | DontEnum);
178 putDirect("lastIndex", jsNumber(0), DontDelete | DontEnum);
181 // ------------------------------ RegExpObjectImp ------------------------------
183 const ClassInfo RegExpObjectImp::info = {"Function", &InternalFunctionImp::info, &RegExpTable, 0};
185 /* Source for regexp_object.lut.h
186 @begin RegExpTable 20
187 input RegExpObjectImp::Input None
188 $_ RegExpObjectImp::Input DontEnum
189 multiline RegExpObjectImp::Multiline None
190 $* RegExpObjectImp::Multiline DontEnum
191 lastMatch RegExpObjectImp::LastMatch DontDelete|ReadOnly
192 $& RegExpObjectImp::LastMatch DontDelete|ReadOnly|DontEnum
193 lastParen RegExpObjectImp::LastParen DontDelete|ReadOnly
194 $+ RegExpObjectImp::LastParen DontDelete|ReadOnly|DontEnum
195 leftContext RegExpObjectImp::LeftContext DontDelete|ReadOnly
196 $` RegExpObjectImp::LeftContext DontDelete|ReadOnly|DontEnum
197 rightContext RegExpObjectImp::RightContext DontDelete|ReadOnly
198 $' RegExpObjectImp::RightContext DontDelete|ReadOnly|DontEnum
199 $1 RegExpObjectImp::Dollar1 DontDelete|ReadOnly
200 $2 RegExpObjectImp::Dollar2 DontDelete|ReadOnly
201 $3 RegExpObjectImp::Dollar3 DontDelete|ReadOnly
202 $4 RegExpObjectImp::Dollar4 DontDelete|ReadOnly
203 $5 RegExpObjectImp::Dollar5 DontDelete|ReadOnly
204 $6 RegExpObjectImp::Dollar6 DontDelete|ReadOnly
205 $7 RegExpObjectImp::Dollar7 DontDelete|ReadOnly
206 $8 RegExpObjectImp::Dollar8 DontDelete|ReadOnly
207 $9 RegExpObjectImp::Dollar9 DontDelete|ReadOnly
208 @end
211 RegExpObjectImp::RegExpObjectImp(ExecState *exec,
212 FunctionPrototype *funcProto,
213 RegExpPrototype *regProto)
215 : InternalFunctionImp(funcProto), lastInput(""), lastNumSubPatterns(0), multiline(false)
217 // ECMA 15.10.5.1 RegExp.prototype
218 putDirect(prototypePropertyName, regProto, DontEnum|DontDelete|ReadOnly);
220 // no. of arguments for constructor
221 putDirect(lengthPropertyName, jsNumber(2), ReadOnly|DontDelete|DontEnum);
225 To facilitate result caching, exec(), test(), match(), search(), and replace() dipatch regular
226 expression matching through the performMatch function. We use cached results to calculate,
227 e.g., RegExp.lastMatch and RegExp.leftParen.
229 UString RegExpObjectImp::performMatch(RegExp* r, const UString& s, int startOffset, int *endOffset, int **ovector)
231 int tmpOffset;
232 int *tmpOvector;
233 UString match = r->match(s, startOffset, &tmpOffset, &tmpOvector);
235 if (endOffset)
236 *endOffset = tmpOffset;
237 if (ovector)
238 *ovector = tmpOvector;
240 if (!match.isNull()) {
241 ASSERT(tmpOvector);
243 lastInput = s;
244 lastOvector.set(tmpOvector);
245 lastNumSubPatterns = r->subPatterns();
248 return match;
251 JSObject *RegExpObjectImp::arrayOfMatches(ExecState *exec, const UString &result) const
253 List list;
254 // The returned array contains 'result' as first item, followed by the list of matches
255 list.append(jsString(result));
256 if ( lastOvector )
257 for ( unsigned i = 1 ; i < lastNumSubPatterns + 1 ; ++i )
259 int start = lastOvector[2*i];
260 if (start == -1)
261 list.append(jsUndefined());
262 else {
263 UString substring = lastInput.substr( start, lastOvector[2*i+1] - start );
264 list.append(jsString(substring));
267 JSObject *arr = exec->lexicalInterpreter()->builtinArray()->construct(exec, list);
268 arr->put(exec, "index", jsNumber(lastOvector[0]));
269 arr->put(exec, "input", jsString(lastInput));
270 return arr;
273 JSValue *RegExpObjectImp::getBackref(unsigned i) const
275 if (lastOvector && i < lastNumSubPatterns + 1) {
276 UString substring = lastInput.substr(lastOvector[2*i], lastOvector[2*i+1] - lastOvector[2*i] );
277 return jsString(substring);
280 return jsString("");
283 JSValue *RegExpObjectImp::getLastMatch() const
285 if (lastOvector) {
286 UString substring = lastInput.substr(lastOvector[0], lastOvector[1] - lastOvector[0]);
287 return jsString(substring);
290 return jsString("");
293 JSValue *RegExpObjectImp::getLastParen() const
295 int i = lastNumSubPatterns;
296 if (i > 0) {
297 ASSERT(lastOvector);
298 UString substring = lastInput.substr(lastOvector[2*i], lastOvector[2*i+1] - lastOvector[2*i]);
299 return jsString(substring);
302 return jsString("");
305 JSValue *RegExpObjectImp::getLeftContext() const
307 if (lastOvector) {
308 UString substring = lastInput.substr(0, lastOvector[0]);
309 return jsString(substring);
312 return jsString("");
315 JSValue *RegExpObjectImp::getRightContext() const
317 if (lastOvector) {
318 UString s = lastInput;
319 UString substring = s.substr(lastOvector[1], s.size() - lastOvector[1]);
320 return jsString(substring);
323 return jsString("");
326 bool RegExpObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
328 return getStaticValueSlot<RegExpObjectImp, InternalFunctionImp>(exec, &RegExpTable, this, propertyName, slot);
331 JSValue *RegExpObjectImp::getValueProperty(ExecState *exec, int token) const
333 switch (token) {
334 case Dollar1:
335 return getBackref(1);
336 case Dollar2:
337 return getBackref(2);
338 case Dollar3:
339 return getBackref(3);
340 case Dollar4:
341 return getBackref(4);
342 case Dollar5:
343 return getBackref(5);
344 case Dollar6:
345 return getBackref(6);
346 case Dollar7:
347 return getBackref(7);
348 case Dollar8:
349 return getBackref(8);
350 case Dollar9:
351 return getBackref(9);
352 case Input:
353 return jsString(lastInput);
354 case Multiline:
355 return jsBoolean(multiline);
356 case LastMatch:
357 return getLastMatch();
358 case LastParen:
359 return getLastParen();
360 case LeftContext:
361 return getLeftContext();
362 case RightContext:
363 return getRightContext();
364 default:
365 ASSERT(0);
368 return jsString("");
371 void RegExpObjectImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
373 lookupPut<RegExpObjectImp, InternalFunctionImp>(exec, propertyName, value, attr, &RegExpTable, this);
376 void RegExpObjectImp::putValueProperty(ExecState *exec, int token, JSValue *value, int attr)
378 switch (token) {
379 case Input:
380 lastInput = value->toString(exec);
381 break;
382 case Multiline:
383 multiline = value->toBoolean(exec);
384 break;
385 default:
386 ASSERT(0);
390 bool RegExpObjectImp::implementsConstruct() const
392 return true;
395 RegExp* RegExpObjectImp::makeEngine(ExecState *exec, const UString &p, JSValue *flagsInput)
397 UString flags = flagsInput->isUndefined() ? UString("") : flagsInput->toString(exec);
399 // Check for validity of flags
400 for (int pos = 0; pos < flags.size(); ++pos) {
401 switch (flags[pos].unicode()) {
402 case 'g':
403 case 'i':
404 case 'm':
405 break;
406 default: {
407 throwError(exec, SyntaxError,
408 "Invalid regular expression flags");
409 return 0;
414 bool global = (flags.find("g") >= 0);
415 bool ignoreCase = (flags.find("i") >= 0);
416 bool multiline = (flags.find("m") >= 0);
418 int reflags = RegExp::None;
419 if (global)
420 reflags |= RegExp::Global;
421 if (ignoreCase)
422 reflags |= RegExp::IgnoreCase;
423 if (multiline)
424 reflags |= RegExp::Multiline;
426 RegExp *re = new RegExp(p, reflags);
427 if (!re->isValid()) {
428 throwError(exec, SyntaxError,
429 "Invalid regular expression");
430 delete re;
431 return 0;
433 return re;
437 // ECMA 15.10.4
438 JSObject *RegExpObjectImp::construct(ExecState *exec, const List &args)
440 JSObject *o = args[0]->getObject();
441 if (o && o->inherits(&RegExpImp::info)) {
442 if (!args[1]->isUndefined())
443 return throwError(exec, TypeError);
444 return o;
447 UString p = args[0]->isUndefined() ? UString("") : args[0]->toString(exec);
449 RegExp* re = makeEngine(exec, p, args[1]);
450 if (!re)
451 return exec->exception()->toObject(exec);
454 RegExpPrototype *proto = static_cast<RegExpPrototype*>(exec->lexicalInterpreter()->builtinRegExpPrototype());
455 RegExpImp *dat = new RegExpImp(proto);
457 dat->setRegExp(re);
459 return dat;
462 // ECMA 15.10.3
463 JSValue *RegExpObjectImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
465 // The RegExp argument case is handled by construct()
467 return construct(exec, args);