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 "JSCInlines.h"
34 #include "RegExpConstructor.h"
35 #include "RegExpMatchesArray.h"
36 #include "RegExpPrototype.h"
37 #include <wtf/PassOwnPtr.h>
38 #include <wtf/text/StringBuilder.h>
42 static EncodedJSValue
regExpObjectGlobal(ExecState
*, JSObject
*, EncodedJSValue
, PropertyName
);
43 static EncodedJSValue
regExpObjectIgnoreCase(ExecState
*, JSObject
*, EncodedJSValue
, PropertyName
);
44 static EncodedJSValue
regExpObjectMultiline(ExecState
*, JSObject
*, EncodedJSValue
, PropertyName
);
45 static EncodedJSValue
regExpObjectSource(ExecState
*, JSObject
*, EncodedJSValue
, PropertyName
);
49 #include "RegExpObject.lut.h"
53 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject
);
55 const ClassInfo
RegExpObject::s_info
= { "RegExp", &Base::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(VM
& vm
, Structure
* structure
, RegExp
* regExp
)
67 : JSNonFinalObject(vm
, structure
)
68 , m_regExp(vm
, this, regExp
)
69 , m_lastIndexIsWritable(true)
71 m_lastIndex
.setWithoutWriteBarrier(jsNumber(0));
74 void RegExpObject::finishCreation(VM
& vm
)
76 Base::finishCreation(vm
);
77 ASSERT(inherits(info()));
80 void RegExpObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
82 RegExpObject
* thisObject
= jsCast
<RegExpObject
*>(cell
);
83 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
84 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
85 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
87 Base::visitChildren(thisObject
, visitor
);
88 visitor
.append(&thisObject
->m_regExp
);
89 visitor
.append(&thisObject
->m_lastIndex
);
92 bool RegExpObject::getOwnPropertySlot(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
94 if (propertyName
== exec
->propertyNames().lastIndex
) {
95 RegExpObject
* regExp
= asRegExpObject(object
);
96 unsigned attributes
= regExp
->m_lastIndexIsWritable
? DontDelete
| DontEnum
: DontDelete
| DontEnum
| ReadOnly
;
97 slot
.setValue(regExp
, attributes
, regExp
->getLastIndex());
100 return getStaticValueSlot
<RegExpObject
, JSObject
>(exec
, ExecState::regExpTable(exec
->vm()), jsCast
<RegExpObject
*>(object
), propertyName
, slot
);
103 bool RegExpObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
105 if (propertyName
== exec
->propertyNames().lastIndex
)
107 return Base::deleteProperty(cell
, exec
, propertyName
);
110 void RegExpObject::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
112 if (mode
== IncludeDontEnumProperties
)
113 propertyNames
.add(exec
->propertyNames().lastIndex
);
114 Base::getOwnNonIndexPropertyNames(object
, exec
, propertyNames
, mode
);
117 void RegExpObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
119 if (mode
== IncludeDontEnumProperties
)
120 propertyNames
.add(exec
->propertyNames().lastIndex
);
121 Base::getPropertyNames(object
, exec
, propertyNames
, mode
);
124 static bool reject(ExecState
* exec
, bool throwException
, const char* message
)
127 throwTypeError(exec
, ASCIILiteral(message
));
131 bool RegExpObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool shouldThrow
)
133 if (propertyName
== exec
->propertyNames().lastIndex
) {
134 RegExpObject
* regExp
= asRegExpObject(object
);
135 if (descriptor
.configurablePresent() && descriptor
.configurable())
136 return reject(exec
, shouldThrow
, "Attempting to change configurable attribute of unconfigurable property.");
137 if (descriptor
.enumerablePresent() && descriptor
.enumerable())
138 return reject(exec
, shouldThrow
, "Attempting to change enumerable attribute of unconfigurable property.");
139 if (descriptor
.isAccessorDescriptor())
140 return reject(exec
, shouldThrow
, "Attempting to change access mechanism for an unconfigurable property.");
141 if (!regExp
->m_lastIndexIsWritable
) {
142 if (descriptor
.writablePresent() && descriptor
.writable())
143 return reject(exec
, shouldThrow
, "Attempting to change writable attribute of unconfigurable property.");
144 if (!sameValue(exec
, regExp
->getLastIndex(), descriptor
.value()))
145 return reject(exec
, shouldThrow
, "Attempting to change value of a readonly property.");
148 if (descriptor
.writablePresent() && !descriptor
.writable())
149 regExp
->m_lastIndexIsWritable
= false;
150 if (descriptor
.value())
151 regExp
->setLastIndex(exec
, descriptor
.value(), false);
155 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
);
158 EncodedJSValue
regExpObjectGlobal(ExecState
*, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
160 return JSValue::encode(jsBoolean(asRegExpObject(slotBase
)->regExp()->global()));
163 EncodedJSValue
regExpObjectIgnoreCase(ExecState
*, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
165 return JSValue::encode(jsBoolean(asRegExpObject(slotBase
)->regExp()->ignoreCase()));
168 EncodedJSValue
regExpObjectMultiline(ExecState
*, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
170 return JSValue::encode(jsBoolean(asRegExpObject(slotBase
)->regExp()->multiline()));
173 template <typename CharacterType
>
174 static inline void appendLineTerminatorEscape(StringBuilder
&, CharacterType
);
177 inline void appendLineTerminatorEscape
<LChar
>(StringBuilder
& builder
, LChar lineTerminator
)
179 if (lineTerminator
== '\n')
186 inline void appendLineTerminatorEscape
<UChar
>(StringBuilder
& builder
, UChar lineTerminator
)
188 if (lineTerminator
== '\n')
190 else if (lineTerminator
== '\r')
192 else if (lineTerminator
== 0x2028)
193 builder
.appendLiteral("u2028");
195 builder
.appendLiteral("u2029");
198 template <typename CharacterType
>
199 static inline JSValue
regExpObjectSourceInternal(ExecState
* exec
, String pattern
, const CharacterType
* characters
, unsigned length
)
201 bool previousCharacterWasBackslash
= false;
202 bool inBrackets
= false;
203 bool shouldEscape
= false;
205 // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/',
206 // and also states that the result must be a valid RegularExpressionLiteral. '//' is
207 // not a valid RegularExpressionLiteral (since it is a single line comment), and hence
208 // source cannot ever validly be "". If the source is empty, return a different Pattern
209 // that would match the same thing.
211 return jsNontrivialString(exec
, ASCIILiteral("(?:)"));
213 // early return for strings that don't contain a forwards slash and LineTerminator
214 for (unsigned i
= 0; i
< length
; ++i
) {
215 CharacterType ch
= characters
[i
];
216 if (!previousCharacterWasBackslash
) {
230 if (Lexer
<CharacterType
>::isLineTerminator(ch
)) {
235 if (previousCharacterWasBackslash
)
236 previousCharacterWasBackslash
= false;
238 previousCharacterWasBackslash
= ch
== '\\';
242 return jsString(exec
, pattern
);
244 previousCharacterWasBackslash
= false;
246 StringBuilder result
;
247 for (unsigned i
= 0; i
< length
; ++i
) {
248 CharacterType ch
= characters
[i
];
249 if (!previousCharacterWasBackslash
) {
261 // escape LineTerminator
262 if (Lexer
<CharacterType
>::isLineTerminator(ch
)) {
263 if (!previousCharacterWasBackslash
)
266 appendLineTerminatorEscape
<CharacterType
>(result
, ch
);
270 if (previousCharacterWasBackslash
)
271 previousCharacterWasBackslash
= false;
273 previousCharacterWasBackslash
= ch
== '\\';
276 return jsString(exec
, result
.toString());
281 EncodedJSValue
regExpObjectSource(ExecState
* exec
, JSObject
* slotBase
, EncodedJSValue
, PropertyName
)
283 String pattern
= asRegExpObject(slotBase
)->regExp()->pattern();
284 if (pattern
.is8Bit())
285 return JSValue::encode(regExpObjectSourceInternal(exec
, pattern
, pattern
.characters8(), pattern
.length()));
286 return JSValue::encode(regExpObjectSourceInternal(exec
, pattern
, pattern
.characters16(), pattern
.length()));
289 void RegExpObject::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
291 if (propertyName
== exec
->propertyNames().lastIndex
) {
292 asRegExpObject(cell
)->setLastIndex(exec
, value
, slot
.isStrictMode());
295 Base::put(cell
, exec
, propertyName
, value
, slot
);
298 JSValue
RegExpObject::exec(ExecState
* exec
, JSString
* string
)
300 if (MatchResult result
= match(exec
, string
))
301 return RegExpMatchesArray::create(exec
, string
, regExp(), result
);
305 // Shared implementation used by test and exec.
306 MatchResult
RegExpObject::match(ExecState
* exec
, JSString
* string
)
308 RegExp
* regExp
= this->regExp();
309 RegExpConstructor
* regExpConstructor
= exec
->lexicalGlobalObject()->regExpConstructor();
310 String input
= string
->value(exec
);
312 if (!regExp
->global())
313 return regExpConstructor
->performMatch(vm
, regExp
, string
, input
, 0);
315 JSValue jsLastIndex
= getLastIndex();
317 if (LIKELY(jsLastIndex
.isUInt32())) {
318 lastIndex
= jsLastIndex
.asUInt32();
319 if (lastIndex
> input
.length()) {
320 setLastIndex(exec
, 0);
321 return MatchResult::failed();
324 double doubleLastIndex
= jsLastIndex
.toInteger(exec
);
325 if (doubleLastIndex
< 0 || doubleLastIndex
> input
.length()) {
326 setLastIndex(exec
, 0);
327 return MatchResult::failed();
329 lastIndex
= static_cast<unsigned>(doubleLastIndex
);
332 MatchResult result
= regExpConstructor
->performMatch(vm
, regExp
, string
, input
, lastIndex
);
333 setLastIndex(exec
, result
.end
);