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"
32 #include "JSCInlines.h"
33 #include "RegExpConstructor.h"
34 #include "RegExpMatchesArray.h"
35 #include "RegExpPrototype.h"
36 #include <wtf/text/StringBuilder.h>
40 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject
);
42 const ClassInfo
RegExpObject::s_info
= { "RegExp", &Base::s_info
, nullptr, CREATE_METHOD_TABLE(RegExpObject
) };
44 RegExpObject::RegExpObject(VM
& vm
, Structure
* structure
, RegExp
* regExp
)
45 : JSNonFinalObject(vm
, structure
)
46 , m_regExp(vm
, this, regExp
)
47 , m_lastIndexIsWritable(true)
49 m_lastIndex
.setWithoutWriteBarrier(jsNumber(0));
52 void RegExpObject::finishCreation(VM
& vm
)
54 Base::finishCreation(vm
);
55 ASSERT(inherits(info()));
58 void RegExpObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
60 RegExpObject
* thisObject
= jsCast
<RegExpObject
*>(cell
);
61 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
62 Base::visitChildren(thisObject
, visitor
);
63 visitor
.append(&thisObject
->m_regExp
);
64 visitor
.append(&thisObject
->m_lastIndex
);
67 bool RegExpObject::getOwnPropertySlot(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
69 if (propertyName
== exec
->propertyNames().lastIndex
) {
70 RegExpObject
* regExp
= asRegExpObject(object
);
71 unsigned attributes
= regExp
->m_lastIndexIsWritable
? DontDelete
| DontEnum
: DontDelete
| DontEnum
| ReadOnly
;
72 slot
.setValue(regExp
, attributes
, regExp
->getLastIndex());
75 return Base::getOwnPropertySlot(object
, exec
, propertyName
, slot
);
78 bool RegExpObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
80 if (propertyName
== exec
->propertyNames().lastIndex
)
82 return Base::deleteProperty(cell
, exec
, propertyName
);
85 void RegExpObject::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
87 if (mode
.includeDontEnumProperties())
88 propertyNames
.add(exec
->propertyNames().lastIndex
);
89 Base::getOwnNonIndexPropertyNames(object
, exec
, propertyNames
, mode
);
92 void RegExpObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
94 if (mode
.includeDontEnumProperties())
95 propertyNames
.add(exec
->propertyNames().lastIndex
);
96 Base::getPropertyNames(object
, exec
, propertyNames
, mode
);
99 void RegExpObject::getGenericPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
101 if (mode
.includeDontEnumProperties())
102 propertyNames
.add(exec
->propertyNames().lastIndex
);
103 Base::getGenericPropertyNames(object
, exec
, propertyNames
, mode
);
106 static bool reject(ExecState
* exec
, bool throwException
, const char* message
)
109 throwTypeError(exec
, ASCIILiteral(message
));
113 bool RegExpObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool shouldThrow
)
115 if (propertyName
== exec
->propertyNames().lastIndex
) {
116 RegExpObject
* regExp
= asRegExpObject(object
);
117 if (descriptor
.configurablePresent() && descriptor
.configurable())
118 return reject(exec
, shouldThrow
, "Attempting to change configurable attribute of unconfigurable property.");
119 if (descriptor
.enumerablePresent() && descriptor
.enumerable())
120 return reject(exec
, shouldThrow
, "Attempting to change enumerable attribute of unconfigurable property.");
121 if (descriptor
.isAccessorDescriptor())
122 return reject(exec
, shouldThrow
, "Attempting to change access mechanism for an unconfigurable property.");
123 if (!regExp
->m_lastIndexIsWritable
) {
124 if (descriptor
.writablePresent() && descriptor
.writable())
125 return reject(exec
, shouldThrow
, "Attempting to change writable attribute of unconfigurable property.");
126 if (!sameValue(exec
, regExp
->getLastIndex(), descriptor
.value()))
127 return reject(exec
, shouldThrow
, "Attempting to change value of a readonly property.");
130 if (descriptor
.writablePresent() && !descriptor
.writable())
131 regExp
->m_lastIndexIsWritable
= false;
132 if (descriptor
.value())
133 regExp
->setLastIndex(exec
, descriptor
.value(), false);
137 return Base::defineOwnProperty(object
, exec
, propertyName
, descriptor
, shouldThrow
);
140 static void regExpObjectSetLastIndexStrict(ExecState
* exec
, JSObject
* slotBase
, EncodedJSValue
, EncodedJSValue value
)
142 asRegExpObject(slotBase
)->setLastIndex(exec
, JSValue::decode(value
), true);
145 static void regExpObjectSetLastIndexNonStrict(ExecState
* exec
, JSObject
* slotBase
, EncodedJSValue
, EncodedJSValue value
)
147 asRegExpObject(slotBase
)->setLastIndex(exec
, JSValue::decode(value
), false);
150 void RegExpObject::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
152 if (propertyName
== exec
->propertyNames().lastIndex
) {
153 asRegExpObject(cell
)->setLastIndex(exec
, value
, slot
.isStrictMode());
154 slot
.setCustomProperty(asRegExpObject(cell
), slot
.isStrictMode()
155 ? regExpObjectSetLastIndexStrict
156 : regExpObjectSetLastIndexNonStrict
);
159 Base::put(cell
, exec
, propertyName
, value
, slot
);
162 JSValue
RegExpObject::exec(ExecState
* exec
, JSString
* string
)
164 if (MatchResult result
= match(exec
, string
))
165 return createRegExpMatchesArray(exec
, string
, regExp(), result
);
169 // Shared implementation used by test and exec.
170 MatchResult
RegExpObject::match(ExecState
* exec
, JSString
* string
)
172 RegExp
* regExp
= this->regExp();
173 RegExpConstructor
* regExpConstructor
= exec
->lexicalGlobalObject()->regExpConstructor();
174 String input
= string
->value(exec
);
176 if (!regExp
->global())
177 return regExpConstructor
->performMatch(vm
, regExp
, string
, input
, 0);
179 JSValue jsLastIndex
= getLastIndex();
181 if (LIKELY(jsLastIndex
.isUInt32())) {
182 lastIndex
= jsLastIndex
.asUInt32();
183 if (lastIndex
> input
.length()) {
184 setLastIndex(exec
, 0);
185 return MatchResult::failed();
188 double doubleLastIndex
= jsLastIndex
.toInteger(exec
);
189 if (doubleLastIndex
< 0 || doubleLastIndex
> input
.length()) {
190 setLastIndex(exec
, 0);
191 return MatchResult::failed();
193 lastIndex
= static_cast<unsigned>(doubleLastIndex
);
196 MatchResult result
= regExpConstructor
->performMatch(vm
, regExp
, string
, input
, lastIndex
);
197 setLastIndex(exec
, result
.end
);