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"
24 #include "ButterflyInlines.h"
25 #include "CopiedSpaceInlines.h"
27 #include "ExceptionHelpers.h"
29 #include "JSGlobalObject.h"
33 #include "Operations.h"
34 #include "RegExpConstructor.h"
35 #include "RegExpMatchesArray.h"
36 #include "RegExpPrototype.h"
37 #include <wtf/PassOwnPtr.h>
38 #include <wtf/text/StringBuilder.h>
41 #include <wtf/PassOwnPtr.h>
46 static JSValue
regExpObjectGlobal(ExecState
*, JSValue
, PropertyName
);
47 static JSValue
regExpObjectIgnoreCase(ExecState
*, JSValue
, PropertyName
);
48 static JSValue
regExpObjectMultiline(ExecState
*, JSValue
, PropertyName
);
49 static JSValue
regExpObjectSource(ExecState
*, JSValue
, PropertyName
);
53 #include "RegExpObject.lut.h"
57 ASSERT_HAS_TRIVIAL_DESTRUCTOR(RegExpObject
);
59 const ClassInfo
RegExpObject::s_info
= { "RegExp", &Base::s_info
, 0, ExecState::regExpTable
, CREATE_METHOD_TABLE(RegExpObject
) };
61 /* Source for RegExpObject.lut.h
63 global regExpObjectGlobal DontDelete|ReadOnly|DontEnum
64 ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum
65 multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum
66 source regExpObjectSource DontDelete|ReadOnly|DontEnum
70 RegExpObject::RegExpObject(JSGlobalObject
* globalObject
, Structure
* structure
, RegExp
* regExp
)
71 : JSNonFinalObject(globalObject
->vm(), structure
)
72 , m_regExp(globalObject
->vm(), this, regExp
)
73 , m_lastIndexIsWritable(true)
75 m_lastIndex
.setWithoutWriteBarrier(jsNumber(0));
78 void RegExpObject::finishCreation(JSGlobalObject
* globalObject
)
80 Base::finishCreation(globalObject
->vm());
81 ASSERT(inherits(&s_info
));
84 void RegExpObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
86 RegExpObject
* thisObject
= jsCast
<RegExpObject
*>(cell
);
87 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
88 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
89 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
91 Base::visitChildren(thisObject
, visitor
);
92 visitor
.append(&thisObject
->m_regExp
);
93 visitor
.append(&thisObject
->m_lastIndex
);
96 bool RegExpObject::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
98 if (propertyName
== exec
->propertyNames().lastIndex
) {
99 RegExpObject
* regExp
= asRegExpObject(cell
);
100 slot
.setValue(regExp
, regExp
->getLastIndex());
103 return getStaticValueSlot
<RegExpObject
, JSObject
>(exec
, ExecState::regExpTable(exec
), jsCast
<RegExpObject
*>(cell
), propertyName
, slot
);
106 bool RegExpObject::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
108 if (propertyName
== exec
->propertyNames().lastIndex
) {
109 RegExpObject
* regExp
= asRegExpObject(object
);
110 descriptor
.setDescriptor(regExp
->getLastIndex(), regExp
->m_lastIndexIsWritable
? DontDelete
| DontEnum
: DontDelete
| DontEnum
| ReadOnly
);
113 return getStaticValueDescriptor
<RegExpObject
, JSObject
>(exec
, ExecState::regExpTable(exec
), jsCast
<RegExpObject
*>(object
), propertyName
, descriptor
);
116 bool RegExpObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
118 if (propertyName
== exec
->propertyNames().lastIndex
)
120 return Base::deleteProperty(cell
, exec
, propertyName
);
123 void RegExpObject::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
125 if (mode
== IncludeDontEnumProperties
)
126 propertyNames
.add(exec
->propertyNames().lastIndex
);
127 Base::getOwnNonIndexPropertyNames(object
, exec
, propertyNames
, mode
);
130 void RegExpObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
132 if (mode
== IncludeDontEnumProperties
)
133 propertyNames
.add(exec
->propertyNames().lastIndex
);
134 Base::getPropertyNames(object
, exec
, propertyNames
, mode
);
137 static bool reject(ExecState
* exec
, bool throwException
, const char* message
)
140 throwTypeError(exec
, ASCIILiteral(message
));
144 bool RegExpObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
, bool shouldThrow
)
146 if (propertyName
== exec
->propertyNames().lastIndex
) {
147 RegExpObject
* regExp
= asRegExpObject(object
);
148 if (descriptor
.configurablePresent() && descriptor
.configurable())
149 return reject(exec
, shouldThrow
, "Attempting to change configurable attribute of unconfigurable property.");
150 if (descriptor
.enumerablePresent() && descriptor
.enumerable())
151 return reject(exec
, shouldThrow
, "Attempting to change enumerable attribute of unconfigurable property.");
152 if (descriptor
.isAccessorDescriptor())
153 return reject(exec
, shouldThrow
, "Attempting to change access mechanism for an unconfigurable property.");
154 if (!regExp
->m_lastIndexIsWritable
) {
155 if (descriptor
.writablePresent() && descriptor
.writable())
156 return reject(exec
, shouldThrow
, "Attempting to change writable attribute of unconfigurable property.");
157 if (!sameValue(exec
, regExp
->getLastIndex(), descriptor
.value()))
158 return reject(exec
, shouldThrow
, "Attempting to change value of a readonly property.");
161 if (descriptor
.writablePresent() && !descriptor
.writable())
162 regExp
->m_lastIndexIsWritable
= false;
163 if (descriptor
.value())
164 regExp
->setLastIndex(exec
, descriptor
.value(), false);
168 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
);
171 JSValue
regExpObjectGlobal(ExecState
*, JSValue slotBase
, PropertyName
)
173 return jsBoolean(asRegExpObject(slotBase
)->regExp()->global());
176 JSValue
regExpObjectIgnoreCase(ExecState
*, JSValue slotBase
, PropertyName
)
178 return jsBoolean(asRegExpObject(slotBase
)->regExp()->ignoreCase());
181 JSValue
regExpObjectMultiline(ExecState
*, JSValue slotBase
, PropertyName
)
183 return jsBoolean(asRegExpObject(slotBase
)->regExp()->multiline());
186 template <typename CharacterType
>
187 static inline void appendLineTerminatorEscape(StringBuilder
&, CharacterType
);
190 inline void appendLineTerminatorEscape
<LChar
>(StringBuilder
& builder
, LChar lineTerminator
)
192 if (lineTerminator
== '\n')
199 inline void appendLineTerminatorEscape
<UChar
>(StringBuilder
& builder
, UChar lineTerminator
)
201 if (lineTerminator
== '\n')
203 else if (lineTerminator
== '\r')
205 else if (lineTerminator
== 0x2028)
206 builder
.appendLiteral("u2028");
208 builder
.appendLiteral("u2029");
211 template <typename CharacterType
>
212 static inline JSValue
regExpObjectSourceInternal(ExecState
* exec
, String pattern
, const CharacterType
* characters
, unsigned length
)
214 bool previousCharacterWasBackslash
= false;
215 bool inBrackets
= false;
216 bool shouldEscape
= false;
218 // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/',
219 // and also states that the result must be a valid RegularExpressionLiteral. '//' is
220 // not a valid RegularExpressionLiteral (since it is a single line comment), and hence
221 // source cannot ever validly be "". If the source is empty, return a different Pattern
222 // that would match the same thing.
224 return jsNontrivialString(exec
, ASCIILiteral("(?:)"));
226 // early return for strings that don't contain a forwards slash and LineTerminator
227 for (unsigned i
= 0; i
< length
; ++i
) {
228 CharacterType ch
= characters
[i
];
229 if (!previousCharacterWasBackslash
) {
243 if (Lexer
<CharacterType
>::isLineTerminator(ch
)) {
248 if (previousCharacterWasBackslash
)
249 previousCharacterWasBackslash
= false;
251 previousCharacterWasBackslash
= ch
== '\\';
255 return jsString(exec
, pattern
);
257 previousCharacterWasBackslash
= false;
259 StringBuilder result
;
260 for (unsigned i
= 0; i
< length
; ++i
) {
261 CharacterType ch
= characters
[i
];
262 if (!previousCharacterWasBackslash
) {
274 // escape LineTerminator
275 if (Lexer
<CharacterType
>::isLineTerminator(ch
)) {
276 if (!previousCharacterWasBackslash
)
279 appendLineTerminatorEscape
<CharacterType
>(result
, ch
);
283 if (previousCharacterWasBackslash
)
284 previousCharacterWasBackslash
= false;
286 previousCharacterWasBackslash
= ch
== '\\';
289 return jsString(exec
, result
.toString());
292 JSValue
regExpObjectSource(ExecState
* exec
, JSValue slotBase
, PropertyName
)
294 String pattern
= asRegExpObject(slotBase
)->regExp()->pattern();
295 if (pattern
.is8Bit())
296 return regExpObjectSourceInternal(exec
, pattern
, pattern
.characters8(), pattern
.length());
297 return regExpObjectSourceInternal(exec
, pattern
, pattern
.characters16(), pattern
.length());
300 void RegExpObject::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
302 if (propertyName
== exec
->propertyNames().lastIndex
) {
303 asRegExpObject(cell
)->setLastIndex(exec
, value
, slot
.isStrictMode());
306 lookupPut
<RegExpObject
, JSObject
>(exec
, propertyName
, value
, ExecState::regExpTable(exec
), jsCast
<RegExpObject
*>(cell
), slot
);
309 JSValue
RegExpObject::exec(ExecState
* exec
, JSString
* string
)
311 if (MatchResult result
= match(exec
, string
))
312 return RegExpMatchesArray::create(exec
, string
, regExp(), result
);
316 // Shared implementation used by test and exec.
317 MatchResult
RegExpObject::match(ExecState
* exec
, JSString
* string
)
319 RegExp
* regExp
= this->regExp();
320 RegExpConstructor
* regExpConstructor
= exec
->lexicalGlobalObject()->regExpConstructor();
321 String input
= string
->value(exec
);
323 if (!regExp
->global())
324 return regExpConstructor
->performMatch(vm
, regExp
, string
, input
, 0);
326 JSValue jsLastIndex
= getLastIndex();
328 if (LIKELY(jsLastIndex
.isUInt32())) {
329 lastIndex
= jsLastIndex
.asUInt32();
330 if (lastIndex
> input
.length()) {
331 setLastIndex(exec
, 0);
332 return MatchResult::failed();
335 double doubleLastIndex
= jsLastIndex
.toInteger(exec
);
336 if (doubleLastIndex
< 0 || doubleLastIndex
> input
.length()) {
337 setLastIndex(exec
, 0);
338 return MatchResult::failed();
340 lastIndex
= static_cast<unsigned>(doubleLastIndex
);
343 MatchResult result
= regExpConstructor
->performMatch(vm
, regExp
, string
, input
, lastIndex
);
344 setLastIndex(exec
, result
.end
);