2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2012 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 "RegExpObject.h"
25 #include "ExceptionHelpers.h"
27 #include "JSGlobalObject.h"
31 #include "RegExpConstructor.h"
32 #include "RegExpMatchesArray.h"
33 #include "RegExpPrototype.h"
34 #include "UStringBuilder.h"
35 #include "UStringConcatenate.h"
36 #include <wtf/PassOwnPtr.h>
38 #include <wtf/PassOwnPtr.h>
42 static JSValue
regExpObjectGlobal(ExecState
*, JSValue
, const Identifier
&);
43 static JSValue
regExpObjectIgnoreCase(ExecState
*, JSValue
, const Identifier
&);
44 static JSValue
regExpObjectMultiline(ExecState
*, JSValue
, const Identifier
&);
45 static JSValue
regExpObjectSource(ExecState
*, JSValue
, const Identifier
&);
49 #include "RegExpObject.lut.h"
53 ASSERT_CLASS_FITS_IN_CELL(RegExpObject
);
55 const ClassInfo
RegExpObject::s_info
= { "RegExp", &JSNonFinalObject::s_info
, 0, ExecState::regExpTable
, CREATE_METHOD_TABLE(RegExpObject
) };
57 /* Source for RegExpObject.lut.h
59 global regExpObjectGlobal DontDelete|ReadOnly|DontEnum
60 ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum
61 multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum
62 source regExpObjectSource DontDelete|ReadOnly|DontEnum
66 RegExpObject::RegExpObject(JSGlobalObject
* globalObject
, Structure
* structure
, RegExp
* regExp
)
67 : JSNonFinalObject(globalObject
->globalData(), structure
)
68 , m_regExp(globalObject
->globalData(), this, regExp
)
69 , m_lastIndexIsWritable(true)
71 m_lastIndex
.setWithoutWriteBarrier(jsNumber(0));
74 void RegExpObject::finishCreation(JSGlobalObject
* globalObject
)
76 Base::finishCreation(globalObject
->globalData());
77 ASSERT(inherits(&s_info
));
80 void RegExpObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
82 RegExpObject
* thisObject
= jsCast
<RegExpObject
*>(cell
);
83 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
84 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
85 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
86 Base::visitChildren(thisObject
, visitor
);
87 if (thisObject
->m_regExp
)
88 visitor
.append(&thisObject
->m_regExp
);
89 if (UNLIKELY(!thisObject
->m_lastIndex
.get().isInt32()))
90 visitor
.append(&thisObject
->m_lastIndex
);
93 bool RegExpObject::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
95 if (propertyName
== exec
->propertyNames().lastIndex
) {
96 RegExpObject
* regExp
= asRegExpObject(cell
);
97 slot
.setValue(regExp
, regExp
->getLastIndex());
100 return getStaticValueSlot
<RegExpObject
, JSObject
>(exec
, ExecState::regExpTable(exec
), jsCast
<RegExpObject
*>(cell
), propertyName
, slot
);
103 bool RegExpObject::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
105 if (propertyName
== exec
->propertyNames().lastIndex
) {
106 RegExpObject
* regExp
= asRegExpObject(object
);
107 descriptor
.setDescriptor(regExp
->getLastIndex(), regExp
->m_lastIndexIsWritable
? DontDelete
| DontEnum
: DontDelete
| DontEnum
| ReadOnly
);
110 return getStaticValueDescriptor
<RegExpObject
, JSObject
>(exec
, ExecState::regExpTable(exec
), jsCast
<RegExpObject
*>(object
), propertyName
, descriptor
);
113 bool RegExpObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
)
115 if (propertyName
== exec
->propertyNames().lastIndex
)
117 return Base::deleteProperty(cell
, exec
, propertyName
);
120 void RegExpObject::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
122 if (mode
== IncludeDontEnumProperties
)
123 propertyNames
.add(exec
->propertyNames().lastIndex
);
124 Base::getOwnPropertyNames(object
, exec
, propertyNames
, mode
);
127 void RegExpObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
129 if (mode
== IncludeDontEnumProperties
)
130 propertyNames
.add(exec
->propertyNames().lastIndex
);
131 Base::getPropertyNames(object
, exec
, propertyNames
, mode
);
134 static bool reject(ExecState
* exec
, bool throwException
, const char* message
)
137 throwTypeError(exec
, message
);
141 bool RegExpObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, bool shouldThrow
)
143 if (propertyName
== exec
->propertyNames().lastIndex
) {
144 RegExpObject
* regExp
= asRegExpObject(object
);
145 if (descriptor
.configurablePresent() && descriptor
.configurable())
146 return reject(exec
, shouldThrow
, "Attempting to change configurable attribute of unconfigurable property.");
147 if (descriptor
.enumerablePresent() && descriptor
.enumerable())
148 return reject(exec
, shouldThrow
, "Attempting to change enumerable attribute of unconfigurable property.");
149 if (descriptor
.isAccessorDescriptor())
150 return reject(exec
, shouldThrow
, "Attempting to change access mechanism for an unconfigurable property.");
151 if (!regExp
->m_lastIndexIsWritable
) {
152 if (descriptor
.writablePresent() && descriptor
.writable())
153 return reject(exec
, shouldThrow
, "Attempting to change writable attribute of unconfigurable property.");
154 if (!sameValue(exec
, regExp
->getLastIndex(), descriptor
.value()))
155 return reject(exec
, shouldThrow
, "Attempting to change value of a readonly property.");
158 if (descriptor
.writablePresent() && !descriptor
.writable())
159 regExp
->m_lastIndexIsWritable
= false;
160 if (descriptor
.value())
161 regExp
->setLastIndex(exec
, descriptor
.value(), false);
165 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
);
168 JSValue
regExpObjectGlobal(ExecState
*, JSValue slotBase
, const Identifier
&)
170 return jsBoolean(asRegExpObject(slotBase
)->regExp()->global());
173 JSValue
regExpObjectIgnoreCase(ExecState
*, JSValue slotBase
, const Identifier
&)
175 return jsBoolean(asRegExpObject(slotBase
)->regExp()->ignoreCase());
178 JSValue
regExpObjectMultiline(ExecState
*, JSValue slotBase
, const Identifier
&)
180 return jsBoolean(asRegExpObject(slotBase
)->regExp()->multiline());
183 JSValue
regExpObjectSource(ExecState
* exec
, JSValue slotBase
, const Identifier
&)
185 UString pattern
= asRegExpObject(slotBase
)->regExp()->pattern();
186 unsigned length
= pattern
.length();
187 const UChar
* characters
= pattern
.characters();
188 bool previousCharacterWasBackslash
= false;
189 bool inBrackets
= false;
190 bool shouldEscape
= false;
192 // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/',
193 // and also states that the result must be a valid RegularExpressionLiteral. '//' is
194 // not a valid RegularExpressionLiteral (since it is a single line comment), and hence
195 // source cannot ever validly be "". If the source is empty, return a different Pattern
196 // that would match the same thing.
198 return jsString(exec
, "(?:)");
200 // early return for strings that don't contain a forwards slash and LineTerminator
201 for (unsigned i
= 0; i
< length
; ++i
) {
202 UChar ch
= characters
[i
];
203 if (!previousCharacterWasBackslash
) {
217 if (Lexer
<UChar
>::isLineTerminator(ch
)) {
222 if (previousCharacterWasBackslash
)
223 previousCharacterWasBackslash
= false;
225 previousCharacterWasBackslash
= ch
== '\\';
229 return jsString(exec
, pattern
);
231 previousCharacterWasBackslash
= false;
233 UStringBuilder result
;
234 for (unsigned i
= 0; i
< length
; ++i
) {
235 UChar ch
= characters
[i
];
236 if (!previousCharacterWasBackslash
) {
248 // escape LineTerminator
249 if (Lexer
<UChar
>::isLineTerminator(ch
)) {
250 if (!previousCharacterWasBackslash
)
257 else if (ch
== 0x2028)
258 result
.append("u2028");
260 result
.append("u2029");
264 if (previousCharacterWasBackslash
)
265 previousCharacterWasBackslash
= false;
267 previousCharacterWasBackslash
= ch
== '\\';
270 return jsString(exec
, result
.toUString());
273 void RegExpObject::put(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
275 if (propertyName
== exec
->propertyNames().lastIndex
) {
276 asRegExpObject(cell
)->setLastIndex(exec
, value
, slot
.isStrictMode());
279 lookupPut
<RegExpObject
, JSObject
>(exec
, propertyName
, value
, ExecState::regExpTable(exec
), jsCast
<RegExpObject
*>(cell
), slot
);
282 JSValue
RegExpObject::exec(ExecState
* exec
, JSString
* string
)
284 if (MatchResult result
= match(exec
, string
))
285 return RegExpMatchesArray::create(exec
, string
, regExp(), result
);
289 // Shared implementation used by test and exec.
290 MatchResult
RegExpObject::match(ExecState
* exec
, JSString
* string
)
292 RegExp
* regExp
= this->regExp();
293 RegExpConstructor
* regExpConstructor
= exec
->lexicalGlobalObject()->regExpConstructor();
294 UString input
= string
->value(exec
);
295 JSGlobalData
& globalData
= exec
->globalData();
296 if (!regExp
->global())
297 return regExpConstructor
->performMatch(globalData
, regExp
, string
, input
, 0);
299 JSValue jsLastIndex
= getLastIndex();
301 if (LIKELY(jsLastIndex
.isUInt32())) {
302 lastIndex
= jsLastIndex
.asUInt32();
303 if (lastIndex
> input
.length()) {
304 setLastIndex(exec
, 0);
305 return MatchResult::failed();
308 double doubleLastIndex
= jsLastIndex
.toInteger(exec
);
309 if (doubleLastIndex
< 0 || doubleLastIndex
> input
.length()) {
310 setLastIndex(exec
, 0);
311 return MatchResult::failed();
313 lastIndex
= static_cast<unsigned>(doubleLastIndex
);
316 MatchResult result
= regExpConstructor
->performMatch(globalData
, regExp
, string
, input
, lastIndex
);
317 setLastIndex(exec
, result
.end
);