1 // -*- c-basic-offset: 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
24 #include "regexp_object.h"
26 #include "regexp_object.lut.h"
33 #include "interpreter.h"
34 #include "operations.h"
37 #include "error_object.h"
42 // ------------------------------ RegExpPrototype ---------------------------
46 const ClassInfo
RegExpPrototype::info
= {"RegExp", 0, 0, 0};
48 RegExpPrototype::RegExpPrototype(ExecState
*exec
,
49 ObjectPrototype
*objProto
,
50 FunctionPrototype
*funcProto
)
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
)) {
77 case ToString
: return jsString("//");
81 return throwError(exec
, TypeError
);
85 case Test
: // 15.10.6.2
88 RegExp
*regExp
= static_cast<RegExpImp
*>(thisObj
)->regExp();
89 RegExpObjectImp
* regExpObj
= static_cast<RegExpObjectImp
*>(exec
->lexicalInterpreter()->builtinRegExp());
93 input
= regExpObj
->get(exec
, "input")->toString(exec
);
95 input
= args
[0]->toString(exec
);
97 double lastIndex
= thisObj
->get(exec
, "lastIndex")->toInteger(exec
);
99 bool globalFlag
= thisObj
->get(exec
, "global")->toBoolean(exec
);
102 if (lastIndex
< 0 || lastIndex
> input
.size()) {
103 thisObj
->put(exec
, "lastIndex", jsNumber(0), DontDelete
| DontEnum
);
108 regExp
->prepareMatch(input
);
109 UString match
= regExpObj
->performMatch(regExp
, input
, static_cast<int>(lastIndex
), &foundIndex
);
111 bool didMatch
= !match
.isNull();
115 return jsBoolean(didMatch
);
120 thisObj
->put(exec
, "lastIndex", jsNumber(foundIndex
+ match
.size()), DontDelete
| DontEnum
);
121 return regExpObj
->arrayOfMatches(exec
, match
);
124 thisObj
->put(exec
, "lastIndex", jsNumber(0), DontDelete
| DontEnum
);
130 UString result
= "/" + thisObj
->get(exec
, "source")->toString(exec
) + "/";
131 if (thisObj
->get(exec
, "global")->toBoolean(exec
)) {
134 if (thisObj
->get(exec
, "ignoreCase")->toBoolean(exec
)) {
137 if (thisObj
->get(exec
, "multiline")->toBoolean(exec
)) {
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]);
146 return exec
->exception();
147 instance
->setRegExp(newEngine
);
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()
169 void RegExpImp::setRegExp(RegExp
*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
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
)
233 UString match
= r
->match(s
, startOffset
, &tmpOffset
, &tmpOvector
);
236 *endOffset
= tmpOffset
;
238 *ovector
= tmpOvector
;
240 if (!match
.isNull()) {
244 lastOvector
.set(tmpOvector
);
245 lastNumSubPatterns
= r
->subPatterns();
251 JSObject
*RegExpObjectImp::arrayOfMatches(ExecState
*exec
, const UString
&result
) const
254 // The returned array contains 'result' as first item, followed by the list of matches
255 list
.append(jsString(result
));
257 for ( unsigned i
= 1 ; i
< lastNumSubPatterns
+ 1 ; ++i
)
259 int start
= lastOvector
[2*i
];
261 list
.append(jsUndefined());
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
));
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
);
283 JSValue
*RegExpObjectImp::getLastMatch() const
286 UString substring
= lastInput
.substr(lastOvector
[0], lastOvector
[1] - lastOvector
[0]);
287 return jsString(substring
);
293 JSValue
*RegExpObjectImp::getLastParen() const
295 int i
= lastNumSubPatterns
;
298 UString substring
= lastInput
.substr(lastOvector
[2*i
], lastOvector
[2*i
+1] - lastOvector
[2*i
]);
299 return jsString(substring
);
305 JSValue
*RegExpObjectImp::getLeftContext() const
308 UString substring
= lastInput
.substr(0, lastOvector
[0]);
309 return jsString(substring
);
315 JSValue
*RegExpObjectImp::getRightContext() const
318 UString s
= lastInput
;
319 UString substring
= s
.substr(lastOvector
[1], s
.size() - lastOvector
[1]);
320 return jsString(substring
);
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
335 return getBackref(1);
337 return getBackref(2);
339 return getBackref(3);
341 return getBackref(4);
343 return getBackref(5);
345 return getBackref(6);
347 return getBackref(7);
349 return getBackref(8);
351 return getBackref(9);
353 return jsString(lastInput
);
355 return jsBoolean(multiline
);
357 return getLastMatch();
359 return getLastParen();
361 return getLeftContext();
363 return getRightContext();
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
)
380 lastInput
= value
->toString(exec
);
383 multiline
= value
->toBoolean(exec
);
390 bool RegExpObjectImp::implementsConstruct() const
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()) {
407 throwError(exec
, SyntaxError
,
408 "Invalid regular expression flags");
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
;
420 reflags
|= RegExp::Global
;
422 reflags
|= RegExp::IgnoreCase
;
424 reflags
|= RegExp::Multiline
;
426 RegExp
*re
= new RegExp(p
, reflags
);
427 if (!re
->isValid()) {
428 throwError(exec
, SyntaxError
,
429 "Invalid regular expression");
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
);
447 UString p
= args
[0]->isUndefined() ? UString("") : args
[0]->toString(exec
);
449 RegExp
* re
= makeEngine(exec
, p
, args
[1]);
451 return exec
->exception()->toObject(exec
);
454 RegExpPrototype
*proto
= static_cast<RegExpPrototype
*>(exec
->lexicalInterpreter()->builtinRegExpPrototype());
455 RegExpImp
*dat
= new RegExpImp(proto
);
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
);