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