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