1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2003-2018 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, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
63{
64 VM& vm = exec->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, exec, propertyName, slot);
72}
73
74bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
75{
76 VM& vm = exec->vm();
77 if (propertyName == vm.propertyNames->lastIndex)
78 return false;
79 return Base::deleteProperty(cell, exec, propertyName);
80}
81
82void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
83{
84 VM& vm = exec->vm();
85 if (mode.includeDontEnumProperties())
86 propertyNames.add(vm.propertyNames->lastIndex);
87 Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
88}
89
90void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
91{
92 VM& vm = exec->vm();
93 if (mode.includeDontEnumProperties())
94 propertyNames.add(vm.propertyNames->lastIndex);
95 Base::getPropertyNames(object, exec, propertyNames, mode);
96}
97
98void RegExpObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
99{
100 VM& vm = exec->vm();
101 if (mode.includeDontEnumProperties())
102 propertyNames.add(vm.propertyNames->lastIndex);
103 Base::getGenericPropertyNames(object, exec, propertyNames, mode);
104}
105
106bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
107{
108 VM& vm = exec->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(exec, scope, shouldThrow, UnconfigurablePropertyChangeConfigurabilityError);
115 if (descriptor.enumerablePresent() && descriptor.enumerable())
116 return typeError(exec, scope, shouldThrow, UnconfigurablePropertyChangeEnumerabilityError);
117 if (descriptor.isAccessorDescriptor())
118 return typeError(exec, scope, shouldThrow, UnconfigurablePropertyChangeAccessMechanismError);
119 if (!regExp->lastIndexIsWritable()) {
120 if (descriptor.writablePresent() && descriptor.writable())
121 return typeError(exec, scope, shouldThrow, UnconfigurablePropertyChangeWritabilityError);
122 if (descriptor.value() && !sameValue(exec, regExp->getLastIndex(), descriptor.value()))
123 return typeError(exec, scope, shouldThrow, ReadonlyPropertyChangeError);
124 return true;
125 }
126 if (descriptor.value()) {
127 regExp->setLastIndex(exec, descriptor.value(), false);
128 RETURN_IF_EXCEPTION(scope, false);
129 }
130 if (descriptor.writablePresent() && !descriptor.writable())
131 regExp->setLastIndexIsNotWritable();
132 return true;
133 }
134
135 RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow));
136}
137
138static bool regExpObjectSetLastIndexStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
139{
140 return jsCast<RegExpObject*>(JSValue::decode(thisValue))->setLastIndex(exec, JSValue::decode(value), true);
141}
142
143static bool regExpObjectSetLastIndexNonStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
144{
145 return jsCast<RegExpObject*>(JSValue::decode(thisValue))->setLastIndex(exec, JSValue::decode(value), false);
146}
147
148bool RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
149{
150 VM& vm = exec->vm();
151 RegExpObject* thisObject = jsCast<RegExpObject*>(cell);
152
153 if (UNLIKELY(isThisValueAltered(slot, thisObject)))
154 return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
155
156 if (propertyName == vm.propertyNames->lastIndex) {
157 bool result = thisObject->setLastIndex(exec, value, slot.isStrictMode());
158 slot.setCustomValue(thisObject, slot.isStrictMode()
159 ? regExpObjectSetLastIndexStrict
160 : regExpObjectSetLastIndexNonStrict);
161 return result;
162 }
163 return Base::put(cell, exec, propertyName, value, slot);
164}
165
166JSValue RegExpObject::exec(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
167{
168 return execInline(exec, globalObject, string);
169}
170
171// Shared implementation used by test and exec.
172MatchResult RegExpObject::match(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
173{
174 return matchInline(exec, globalObject, string);
175}
176
177JSValue RegExpObject::matchGlobal(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
178{
179 VM& vm = globalObject->vm();
180 auto scope = DECLARE_THROW_SCOPE(vm);
181 RegExp* regExp = this->regExp();
182
183 ASSERT(regExp->global());
184
185 setLastIndex(exec, 0);
186 RETURN_IF_EXCEPTION(scope, { });
187
188 String s = string->value(exec);
189 RETURN_IF_EXCEPTION(scope, { });
190
191 ASSERT(!s.isNull());
192 if (regExp->unicode()) {
193 unsigned stringLength = s.length();
194 RELEASE_AND_RETURN(scope, collectMatches(
195 vm, exec, string, s, globalObject, regExp,
196 [&] (size_t end) -> size_t {
197 return advanceStringUnicode(s, stringLength, end);
198 }));
199 }
200
201 RELEASE_AND_RETURN(scope, collectMatches(
202 vm, exec, string, s, globalObject, regExp,
203 [&] (size_t end) -> size_t {
204 return end + 1;
205 }));
206}
207
208} // namespace JSC
209