1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2003-2019 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 "Error.h"
25#include "ExceptionHelpers.h"
26#include "JSArray.h"
27#include "JSGlobalObject.h"
28#include "JSString.h"
29#include "Lookup.h"
30#include "JSCInlines.h"
31#include "RegExpObjectInlines.h"
32
33namespace JSC {
34
35STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject);
36
37const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(RegExpObject) };
38
39RegExpObject::RegExpObject(VM& vm, Structure* structure, RegExp* regExp)
40 : JSNonFinalObject(vm, structure)
41 , m_regExpAndLastIndexIsNotWritableFlag(bitwise_cast<uintptr_t>(regExp)) // lastIndexIsNotWritableFlag is not set.
42{
43 m_lastIndex.setWithoutWriteBarrier(jsNumber(0));
44}
45
46void RegExpObject::finishCreation(VM& vm)
47{
48 Base::finishCreation(vm);
49 ASSERT(inherits(vm, info()));
50 ASSERT(type() == RegExpObjectType);
51}
52
53void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
54{
55 RegExpObject* thisObject = jsCast<RegExpObject*>(cell);
56 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
57 Base::visitChildren(thisObject, visitor);
58 visitor.appendUnbarriered(thisObject->regExp());
59 visitor.append(thisObject->m_lastIndex);
60}
61
62bool RegExpObject::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
63{
64 VM& vm = globalObject->vm();
65 if (propertyName == vm.propertyNames->lastIndex) {
66 RegExpObject* regExp = jsCast<RegExpObject*>(object);
67 unsigned attributes = regExp->lastIndexIsWritable() ? PropertyAttribute::DontDelete | PropertyAttribute::DontEnum : PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
68 slot.setValue(regExp, attributes, regExp->getLastIndex());
69 return true;
70 }
71 return Base::getOwnPropertySlot(object, globalObject, propertyName, slot);
72}
73
74bool RegExpObject::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName)
75{
76 VM& vm = globalObject->vm();
77 if (propertyName == vm.propertyNames->lastIndex)
78 return false;
79 return Base::deleteProperty(cell, globalObject, propertyName);
80}
81
82void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, EnumerationMode mode)
83{
84 VM& vm = globalObject->vm();
85 if (mode.includeDontEnumProperties())
86 propertyNames.add(vm.propertyNames->lastIndex);
87 Base::getOwnNonIndexPropertyNames(object, globalObject, propertyNames, mode);
88}
89
90void RegExpObject::getPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, EnumerationMode mode)
91{
92 VM& vm = globalObject->vm();
93 if (mode.includeDontEnumProperties())
94 propertyNames.add(vm.propertyNames->lastIndex);
95 Base::getPropertyNames(object, globalObject, propertyNames, mode);
96}
97
98void RegExpObject::getGenericPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, EnumerationMode mode)
99{
100 VM& vm = globalObject->vm();
101 if (mode.includeDontEnumProperties())
102 propertyNames.add(vm.propertyNames->lastIndex);
103 Base::getGenericPropertyNames(object, globalObject, propertyNames, mode);
104}
105
106bool RegExpObject::defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
107{
108 VM& vm = globalObject->vm();
109 auto scope = DECLARE_THROW_SCOPE(vm);
110
111 if (propertyName == vm.propertyNames->lastIndex) {
112 RegExpObject* regExp = jsCast<RegExpObject*>(object);
113 if (descriptor.configurablePresent() && descriptor.configurable())
114 return typeError(globalObject, scope, shouldThrow, UnconfigurablePropertyChangeConfigurabilityError);
115 if (descriptor.enumerablePresent() && descriptor.enumerable())
116 return typeError(globalObject, scope, shouldThrow, UnconfigurablePropertyChangeEnumerabilityError);
117 if (descriptor.isAccessorDescriptor())
118 return typeError(globalObject, scope, shouldThrow, UnconfigurablePropertyChangeAccessMechanismError);
119 if (!regExp->lastIndexIsWritable()) {
120 if (descriptor.writablePresent() && descriptor.writable())
121 return typeError(globalObject, scope, shouldThrow, UnconfigurablePropertyChangeWritabilityError);
122 if (descriptor.value()) {
123 bool isSame = sameValue(globalObject, regExp->getLastIndex(), descriptor.value());
124 RETURN_IF_EXCEPTION(scope, false);
125 if (!isSame)
126 return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyChangeError);
127 }
128 return true;
129 }
130 if (descriptor.value()) {
131 regExp->setLastIndex(globalObject, descriptor.value(), false);
132 RETURN_IF_EXCEPTION(scope, false);
133 }
134 if (descriptor.writablePresent() && !descriptor.writable())
135 regExp->setLastIndexIsNotWritable();
136 return true;
137 }
138
139 RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, shouldThrow));
140}
141
142static bool regExpObjectSetLastIndexStrict(JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue value)
143{
144 return jsCast<RegExpObject*>(JSValue::decode(thisValue))->setLastIndex(globalObject, JSValue::decode(value), true);
145}
146
147static bool regExpObjectSetLastIndexNonStrict(JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue value)
148{
149 return jsCast<RegExpObject*>(JSValue::decode(thisValue))->setLastIndex(globalObject, JSValue::decode(value), false);
150}
151
152bool RegExpObject::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
153{
154 VM& vm = globalObject->vm();
155 RegExpObject* thisObject = jsCast<RegExpObject*>(cell);
156
157 if (UNLIKELY(isThisValueAltered(slot, thisObject)))
158 return ordinarySetSlow(globalObject, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
159
160 if (propertyName == vm.propertyNames->lastIndex) {
161 bool result = thisObject->setLastIndex(globalObject, value, slot.isStrictMode());
162 slot.setCustomValue(thisObject, slot.isStrictMode()
163 ? regExpObjectSetLastIndexStrict
164 : regExpObjectSetLastIndexNonStrict);
165 return result;
166 }
167 return Base::put(cell, globalObject, propertyName, value, slot);
168}
169
170JSValue RegExpObject::exec(JSGlobalObject* globalObject, JSString* string)
171{
172 return execInline(globalObject, string);
173}
174
175// Shared implementation used by test and exec.
176MatchResult RegExpObject::match(JSGlobalObject* globalObject, JSString* string)
177{
178 return matchInline(globalObject, string);
179}
180
181JSValue RegExpObject::matchGlobal(JSGlobalObject* globalObject, JSString* string)
182{
183 VM& vm = globalObject->vm();
184 auto scope = DECLARE_THROW_SCOPE(vm);
185 RegExp* regExp = this->regExp();
186
187 ASSERT(regExp->global());
188
189 setLastIndex(globalObject, 0);
190 RETURN_IF_EXCEPTION(scope, { });
191
192 String s = string->value(globalObject);
193 RETURN_IF_EXCEPTION(scope, { });
194
195 ASSERT(!s.isNull());
196 if (regExp->unicode()) {
197 unsigned stringLength = s.length();
198 RELEASE_AND_RETURN(scope, collectMatches(
199 vm, globalObject, string, s, regExp,
200 [&] (size_t end) -> size_t {
201 return advanceStringUnicode(s, stringLength, end);
202 }));
203 }
204
205 RELEASE_AND_RETURN(scope, collectMatches(
206 vm, globalObject, string, s, regExp,
207 [&] (size_t end) -> size_t {
208 return end + 1;
209 }));
210}
211
212} // namespace JSC
213