We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / runtime / GenericArgumentsInlines.h
1 /*
2  * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #pragma once
27
28 #include "GenericArguments.h"
29 #include "JSCInlines.h"
30
31 namespace JSC {
32
33 template<typename Type>
34 void GenericArguments<Type>::visitChildren(JSCell* thisCell, SlotVisitor& visitor)
35 {
36     Type* thisObject = static_cast<Type*>(thisCell);
37     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
38     Base::visitChildren(thisCell, visitor);
39     
40     if (thisObject->m_modifiedArgumentsDescriptor)
41         visitor.markAuxiliary(thisObject->m_modifiedArgumentsDescriptor.get());
42 }
43
44 template<typename Type>
45 bool GenericArguments<Type>::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName ident, PropertySlot& slot)
46 {
47     Type* thisObject = jsCast<Type*>(object);
48     VM& vm = exec->vm();
49     
50     if (!thisObject->overrodeThings()) {
51         if (ident == vm.propertyNames->length) {
52             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), jsNumber(thisObject->internalLength()));
53             return true;
54         }
55         if (ident == vm.propertyNames->callee) {
56             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), thisObject->callee());
57             return true;
58         }
59         if (ident == vm.propertyNames->iteratorSymbol) {
60             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), thisObject->globalObject(vm)->arrayProtoValuesFunction());
61             return true;
62         }
63     }
64     
65     if (std::optional<uint32_t> index = parseIndex(ident))
66         return GenericArguments<Type>::getOwnPropertySlotByIndex(thisObject, exec, *index, slot);
67
68     return Base::getOwnPropertySlot(thisObject, exec, ident, slot);
69 }
70
71 template<typename Type>
72 bool GenericArguments<Type>::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot)
73 {
74     Type* thisObject = jsCast<Type*>(object);
75     
76     if (!thisObject->isModifiedArgumentDescriptor(index) && thisObject->isMappedArgument(index)) {
77         slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), thisObject->getIndexQuickly(index));
78         return true;
79     }
80     
81     bool result = Base::getOwnPropertySlotByIndex(object, exec, index, slot);
82     
83     if (thisObject->isMappedArgument(index)) {
84         ASSERT(result);
85         slot.setValue(thisObject, slot.attributes(), thisObject->getIndexQuickly(index));
86         return true;
87     }
88     
89     return result;
90 }
91
92 template<typename Type>
93 void GenericArguments<Type>::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
94 {
95     Type* thisObject = jsCast<Type*>(object);
96
97     if (array.includeStringProperties()) {
98         for (unsigned i = 0; i < thisObject->internalLength(); ++i) {
99             if (!thisObject->isMappedArgument(i))
100                 continue;
101             array.add(Identifier::from(exec, i));
102         }
103     }
104
105     if (mode.includeDontEnumProperties() && !thisObject->overrodeThings()) {
106         VM& vm = exec->vm();
107         array.add(vm.propertyNames->length);
108         array.add(vm.propertyNames->callee);
109         if (array.includeSymbolProperties())
110             array.add(vm.propertyNames->iteratorSymbol);
111     }
112     Base::getOwnPropertyNames(thisObject, exec, array, mode);
113 }
114
115 template<typename Type>
116 bool GenericArguments<Type>::put(JSCell* cell, ExecState* exec, PropertyName ident, JSValue value, PutPropertySlot& slot)
117 {
118     Type* thisObject = jsCast<Type*>(cell);
119     VM& vm = exec->vm();
120     
121     if (!thisObject->overrodeThings()
122         && (ident == vm.propertyNames->length
123             || ident == vm.propertyNames->callee
124             || ident == vm.propertyNames->iteratorSymbol)) {
125         thisObject->overrideThings(vm);
126         PutPropertySlot dummy = slot; // This put is not cacheable, so we shadow the slot that was given to us.
127         return Base::put(thisObject, exec, ident, value, dummy);
128     }
129
130     // https://tc39.github.io/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver
131     // Fall back to the OrdinarySet when the receiver is altered from the thisObject.
132     if (UNLIKELY(isThisValueAltered(slot, thisObject)))
133         return ordinarySetSlow(exec, thisObject, ident, value, slot.thisValue(), slot.isStrictMode());
134     
135     std::optional<uint32_t> index = parseIndex(ident);
136     if (index && thisObject->isMappedArgument(index.value())) {
137         thisObject->setIndexQuickly(vm, index.value(), value);
138         return true;
139     }
140     
141     return Base::put(thisObject, exec, ident, value, slot);
142 }
143
144 template<typename Type>
145 bool GenericArguments<Type>::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow)
146 {
147     Type* thisObject = jsCast<Type*>(cell);
148     VM& vm = exec->vm();
149
150     if (thisObject->isMappedArgument(index)) {
151         thisObject->setIndexQuickly(vm, index, value);
152         return true;
153     }
154     
155     return Base::putByIndex(cell, exec, index, value, shouldThrow);
156 }
157
158 template<typename Type>
159 bool GenericArguments<Type>::deleteProperty(JSCell* cell, ExecState* exec, PropertyName ident)
160 {
161     Type* thisObject = jsCast<Type*>(cell);
162     VM& vm = exec->vm();
163     
164     if (!thisObject->overrodeThings()
165         && (ident == vm.propertyNames->length
166             || ident == vm.propertyNames->callee
167             || ident == vm.propertyNames->iteratorSymbol))
168         thisObject->overrideThings(vm);
169     
170     if (std::optional<uint32_t> index = parseIndex(ident))
171         return GenericArguments<Type>::deletePropertyByIndex(thisObject, exec, *index);
172
173     return Base::deleteProperty(thisObject, exec, ident);
174 }
175
176 template<typename Type>
177 bool GenericArguments<Type>::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned index)
178 {
179     Type* thisObject = jsCast<Type*>(cell);
180     VM& vm = exec->vm();
181
182     bool propertyMightBeInJSObjectStorage = thisObject->isModifiedArgumentDescriptor(index) || !thisObject->isMappedArgument(index);
183     bool deletedProperty = true;
184     if (propertyMightBeInJSObjectStorage)
185         deletedProperty = Base::deletePropertyByIndex(cell, exec, index);
186
187     if (deletedProperty) {
188         // Deleting an indexed property unconditionally unmaps it.
189         if (thisObject->isMappedArgument(index)) {
190             // We need to check that the property was mapped so we don't write to random memory.
191             thisObject->unmapArgument(vm, index);
192         }
193         thisObject->setModifiedArgumentDescriptor(vm, index);
194     }
195
196     return deletedProperty;
197 }
198
199 template<typename Type>
200 bool GenericArguments<Type>::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow)
201 {
202     Type* thisObject = jsCast<Type*>(object);
203     VM& vm = exec->vm();
204     auto scope = DECLARE_THROW_SCOPE(vm);
205     
206     if (ident == vm.propertyNames->length
207         || ident == vm.propertyNames->callee
208         || ident == vm.propertyNames->iteratorSymbol)
209         thisObject->overrideThingsIfNecessary(vm);
210     else {
211         std::optional<uint32_t> optionalIndex = parseIndex(ident);
212         if (optionalIndex) {
213             uint32_t index = optionalIndex.value();
214             if (!descriptor.isAccessorDescriptor() && thisObject->isMappedArgument(optionalIndex.value())) {
215                 // If the property is not deleted and we are using a non-accessor descriptor, then
216                 // make sure that the aliased argument sees the value.
217                 if (descriptor.value())
218                     thisObject->setIndexQuickly(vm, index, descriptor.value());
219             
220                 // If the property is not deleted and we are using a non-accessor, writable,
221                 // configurable and enumerable descriptor and isn't modified, then we are done.
222                 // The argument continues to be aliased.
223                 if (descriptor.writable() && descriptor.configurable() && descriptor.enumerable() && !thisObject->isModifiedArgumentDescriptor(index))
224                     return true;
225                 
226                 if (!thisObject->isModifiedArgumentDescriptor(index)) {
227                     // If it is a new entry, we need to put direct to initialize argument[i] descriptor properly
228                     JSValue value = thisObject->getIndexQuickly(index);
229                     ASSERT(value);
230                     object->putDirectMayBeIndex(exec, ident, value);
231                     scope.assertNoException();
232
233                     thisObject->setModifiedArgumentDescriptor(vm, index);
234                 }
235             }
236             
237             if (thisObject->isMappedArgument(index)) {
238                 // Just unmap arguments if its descriptor contains {writable: false}.
239                 // Check https://tc39.github.io/ecma262/#sec-createunmappedargumentsobject
240                 // and https://tc39.github.io/ecma262/#sec-createmappedargumentsobject to verify that all data
241                 // property from arguments object are {writable: true, configurable: true, enumerable: true} by default
242                 if ((descriptor.writablePresent() && !descriptor.writable()) || descriptor.isAccessorDescriptor()) {
243                     if (!descriptor.isAccessorDescriptor()) {
244                         JSValue value = thisObject->getIndexQuickly(index);
245                         ASSERT(value);
246                         object->putDirectMayBeIndex(exec, ident, value);
247                         scope.assertNoException();
248                     }
249                     thisObject->unmapArgument(vm, index);
250                     thisObject->setModifiedArgumentDescriptor(vm, index);
251                 }
252             }
253         }
254     }
255
256     // Now just let the normal object machinery do its thing.
257     scope.release();
258     return Base::defineOwnProperty(object, exec, ident, descriptor, shouldThrow);
259 }
260
261 template<typename Type>
262 void GenericArguments<Type>::initModifiedArgumentsDescriptor(VM& vm, unsigned argsLength)
263 {
264     RELEASE_ASSERT(!m_modifiedArgumentsDescriptor);
265
266     if (argsLength) {
267         void* backingStore = vm.gigacageAuxiliarySpace(m_modifiedArgumentsDescriptor.kind).allocateNonVirtual(vm, WTF::roundUpToMultipleOf<8>(argsLength), nullptr, AllocationFailureMode::Assert);
268         bool* modifiedArguments = static_cast<bool*>(backingStore);
269         m_modifiedArgumentsDescriptor.set(vm, this, modifiedArguments);
270         for (unsigned i = argsLength; i--;)
271             modifiedArguments[i] = false;
272     }
273 }
274
275 template<typename Type>
276 void GenericArguments<Type>::initModifiedArgumentsDescriptorIfNecessary(VM& vm, unsigned argsLength)
277 {
278     if (!m_modifiedArgumentsDescriptor)
279         initModifiedArgumentsDescriptor(vm, argsLength);
280 }
281
282 template<typename Type>
283 void GenericArguments<Type>::setModifiedArgumentDescriptor(VM& vm, unsigned index, unsigned length)
284 {
285     initModifiedArgumentsDescriptorIfNecessary(vm, length);
286     if (index < length)
287         m_modifiedArgumentsDescriptor[index] = true;
288 }
289
290 template<typename Type>
291 bool GenericArguments<Type>::isModifiedArgumentDescriptor(unsigned index, unsigned length)
292 {
293     if (!m_modifiedArgumentsDescriptor)
294         return false;
295     if (index < length)
296         return m_modifiedArgumentsDescriptor[index];
297     return false;
298 }
299
300 template<typename Type>
301 void GenericArguments<Type>::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
302 {
303     VM& vm = exec->vm();
304     auto scope = DECLARE_THROW_SCOPE(vm);
305
306     Type* thisObject = static_cast<Type*>(this);
307     for (unsigned i = 0; i < length; ++i) {
308         if (thisObject->isMappedArgument(i + offset))
309             exec->r(firstElementDest + i) = thisObject->getIndexQuickly(i + offset);
310         else {
311             exec->r(firstElementDest + i) = get(exec, i + offset);
312             RETURN_IF_EXCEPTION(scope, void());
313         }
314     }
315 }
316
317 } // namespace JSC