]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
6fe7ccc8 | 3 | * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved. |
9dae56ea A |
4 | * |
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. | |
9 | * | |
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. | |
14 | * | |
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 | |
18 | * | |
19 | */ | |
20 | ||
21 | #include "config.h" | |
22 | #include "RegExpObject.h" | |
23 | ||
93a37866 A |
24 | #include "ButterflyInlines.h" |
25 | #include "CopiedSpaceInlines.h" | |
ba379fdc | 26 | #include "Error.h" |
14957cd0 | 27 | #include "ExceptionHelpers.h" |
9dae56ea A |
28 | #include "JSArray.h" |
29 | #include "JSGlobalObject.h" | |
30 | #include "JSString.h" | |
4e4e5a6f | 31 | #include "Lookup.h" |
81345200 | 32 | #include "JSCInlines.h" |
9dae56ea | 33 | #include "RegExpConstructor.h" |
6fe7ccc8 | 34 | #include "RegExpMatchesArray.h" |
9dae56ea | 35 | #include "RegExpPrototype.h" |
93a37866 | 36 | #include <wtf/text/StringBuilder.h> |
14957cd0 | 37 | |
9dae56ea A |
38 | namespace JSC { |
39 | ||
81345200 | 40 | STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject); |
9dae56ea | 41 | |
ed1e77d3 | 42 | const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, nullptr, CREATE_METHOD_TABLE(RegExpObject) }; |
9dae56ea | 43 | |
81345200 A |
44 | RegExpObject::RegExpObject(VM& vm, Structure* structure, RegExp* regExp) |
45 | : JSNonFinalObject(vm, structure) | |
46 | , m_regExp(vm, this, regExp) | |
6fe7ccc8 | 47 | , m_lastIndexIsWritable(true) |
9dae56ea | 48 | { |
6fe7ccc8 | 49 | m_lastIndex.setWithoutWriteBarrier(jsNumber(0)); |
9dae56ea A |
50 | } |
51 | ||
81345200 | 52 | void RegExpObject::finishCreation(VM& vm) |
9dae56ea | 53 | { |
81345200 A |
54 | Base::finishCreation(vm); |
55 | ASSERT(inherits(info())); | |
9dae56ea A |
56 | } |
57 | ||
6fe7ccc8 | 58 | void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) |
14957cd0 | 59 | { |
6fe7ccc8 | 60 | RegExpObject* thisObject = jsCast<RegExpObject*>(cell); |
81345200 | 61 | ASSERT_GC_OBJECT_INHERITS(thisObject, info()); |
6fe7ccc8 | 62 | Base::visitChildren(thisObject, visitor); |
93a37866 A |
63 | visitor.append(&thisObject->m_regExp); |
64 | visitor.append(&thisObject->m_lastIndex); | |
6fe7ccc8 A |
65 | } |
66 | ||
81345200 | 67 | bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
9dae56ea | 68 | { |
6fe7ccc8 A |
69 | if (propertyName == exec->propertyNames().lastIndex) { |
70 | RegExpObject* regExp = asRegExpObject(object); | |
81345200 A |
71 | unsigned attributes = regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly; |
72 | slot.setValue(regExp, attributes, regExp->getLastIndex()); | |
6fe7ccc8 A |
73 | return true; |
74 | } | |
ed1e77d3 | 75 | return Base::getOwnPropertySlot(object, exec, propertyName, slot); |
6fe7ccc8 A |
76 | } |
77 | ||
93a37866 | 78 | bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) |
6fe7ccc8 A |
79 | { |
80 | if (propertyName == exec->propertyNames().lastIndex) | |
81 | return false; | |
82 | return Base::deleteProperty(cell, exec, propertyName); | |
9dae56ea A |
83 | } |
84 | ||
93a37866 | 85 | void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
f9bf01c6 | 86 | { |
ed1e77d3 | 87 | if (mode.includeDontEnumProperties()) |
6fe7ccc8 | 88 | propertyNames.add(exec->propertyNames().lastIndex); |
93a37866 | 89 | Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); |
6fe7ccc8 A |
90 | } |
91 | ||
92 | void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) | |
93 | { | |
ed1e77d3 | 94 | if (mode.includeDontEnumProperties()) |
6fe7ccc8 A |
95 | propertyNames.add(exec->propertyNames().lastIndex); |
96 | Base::getPropertyNames(object, exec, propertyNames, mode); | |
97 | } | |
98 | ||
ed1e77d3 A |
99 | void RegExpObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
100 | { | |
101 | if (mode.includeDontEnumProperties()) | |
102 | propertyNames.add(exec->propertyNames().lastIndex); | |
103 | Base::getGenericPropertyNames(object, exec, propertyNames, mode); | |
104 | } | |
105 | ||
6fe7ccc8 A |
106 | static bool reject(ExecState* exec, bool throwException, const char* message) |
107 | { | |
108 | if (throwException) | |
93a37866 | 109 | throwTypeError(exec, ASCIILiteral(message)); |
6fe7ccc8 A |
110 | return false; |
111 | } | |
112 | ||
81345200 | 113 | bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) |
6fe7ccc8 A |
114 | { |
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."); | |
128 | return true; | |
129 | } | |
130 | if (descriptor.writablePresent() && !descriptor.writable()) | |
131 | regExp->m_lastIndexIsWritable = false; | |
132 | if (descriptor.value()) | |
133 | regExp->setLastIndex(exec, descriptor.value(), false); | |
134 | return true; | |
135 | } | |
136 | ||
137 | return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); | |
f9bf01c6 A |
138 | } |
139 | ||
ed1e77d3 | 140 | static void regExpObjectSetLastIndexStrict(ExecState* exec, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) |
9dae56ea | 141 | { |
ed1e77d3 | 142 | asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), true); |
93a37866 A |
143 | } |
144 | ||
ed1e77d3 | 145 | static void regExpObjectSetLastIndexNonStrict(ExecState* exec, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) |
93a37866 | 146 | { |
ed1e77d3 | 147 | asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), false); |
9dae56ea A |
148 | } |
149 | ||
93a37866 | 150 | void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
9dae56ea | 151 | { |
6fe7ccc8 A |
152 | if (propertyName == exec->propertyNames().lastIndex) { |
153 | asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode()); | |
ed1e77d3 A |
154 | slot.setCustomProperty(asRegExpObject(cell), slot.isStrictMode() |
155 | ? regExpObjectSetLastIndexStrict | |
156 | : regExpObjectSetLastIndexNonStrict); | |
6fe7ccc8 A |
157 | return; |
158 | } | |
81345200 | 159 | Base::put(cell, exec, propertyName, value, slot); |
9dae56ea A |
160 | } |
161 | ||
6fe7ccc8 | 162 | JSValue RegExpObject::exec(ExecState* exec, JSString* string) |
9dae56ea | 163 | { |
6fe7ccc8 | 164 | if (MatchResult result = match(exec, string)) |
ed1e77d3 | 165 | return createRegExpMatchesArray(exec, string, regExp(), result); |
9dae56ea A |
166 | return jsNull(); |
167 | } | |
168 | ||
9dae56ea | 169 | // Shared implementation used by test and exec. |
6fe7ccc8 | 170 | MatchResult RegExpObject::match(ExecState* exec, JSString* string) |
9dae56ea | 171 | { |
6fe7ccc8 | 172 | RegExp* regExp = this->regExp(); |
9dae56ea | 173 | RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); |
93a37866 A |
174 | String input = string->value(exec); |
175 | VM& vm = exec->vm(); | |
6fe7ccc8 | 176 | if (!regExp->global()) |
93a37866 | 177 | return regExpConstructor->performMatch(vm, regExp, string, input, 0); |
9dae56ea | 178 | |
14957cd0 A |
179 | JSValue jsLastIndex = getLastIndex(); |
180 | unsigned lastIndex; | |
181 | if (LIKELY(jsLastIndex.isUInt32())) { | |
182 | lastIndex = jsLastIndex.asUInt32(); | |
183 | if (lastIndex > input.length()) { | |
6fe7ccc8 A |
184 | setLastIndex(exec, 0); |
185 | return MatchResult::failed(); | |
14957cd0 A |
186 | } |
187 | } else { | |
188 | double doubleLastIndex = jsLastIndex.toInteger(exec); | |
189 | if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { | |
6fe7ccc8 A |
190 | setLastIndex(exec, 0); |
191 | return MatchResult::failed(); | |
14957cd0 A |
192 | } |
193 | lastIndex = static_cast<unsigned>(doubleLastIndex); | |
9dae56ea A |
194 | } |
195 | ||
93a37866 | 196 | MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex); |
6fe7ccc8 A |
197 | setLastIndex(exec, result.end); |
198 | return result; | |
9dae56ea A |
199 | } |
200 | ||
201 | } // namespace JSC |