[JSC] Pass VM& parameter as much as possible
[WebKit.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     
39     if (thisObject->m_modifiedArgumentsDescriptor)
40         visitor.markAuxiliary(thisObject->m_modifiedArgumentsDescriptor.get());
41 }
42
43 template<typename Type>
44 bool GenericArguments<Type>::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName ident, PropertySlot& slot)
45 {
46     Type* thisObject = jsCast<Type*>(object);
47     VM& vm = exec->vm();
48     
49     if (!thisObject->overrodeThings()) {
50         if (ident == vm.propertyNames->length) {
51             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), jsNumber(thisObject->internalLength()));
52             return true;
53         }
54         if (ident == vm.propertyNames->callee) {
55             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), thisObject->callee());
56             return true;
57         }
58         if (ident == vm.propertyNames->iteratorSymbol) {
59             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), thisObject->globalObject(vm)->arrayProtoValuesFunction());
60             return true;
61         }
62     }
63     
64     if (std::optional<uint32_t> index = parseIndex(ident))
65         return GenericArguments<Type>::getOwnPropertySlotByIndex(thisObject, exec, *index, slot);
66
67     return Base::getOwnPropertySlot(thisObject, exec, ident, slot);
68 }
69
70 template<typename Type>
71 bool GenericArguments<Type>::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot)
72 {
73     Type* thisObject = jsCast<Type*>(object);
74     
75     if (!thisObject->isModifiedArgumentDescriptor(index) && thisObject->isMappedArgument(index)) {
76         slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), thisObject->getIndexQuickly(index));
77         return true;
78     }
79     
80     bool result = Base::getOwnPropertySlotByIndex(object, exec, index, slot);
81     
82     if (thisObject->isMappedArgument(index)) {
83         ASSERT(result);
84         slot.setValue(thisObject, slot.attributes(), thisObject->getIndexQuickly(index));
85         return true;
86     }
87     
88     return result;
89 }
90
91 template<typename Type>
92 void GenericArguments<Type>::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
93 {
94     Type* thisObject = jsCast<Type*>(object);
95
96     if (array.includeStringProperties()) {
97         for (unsigned i = 0; i < thisObject->internalLength(); ++i) {
98             if (!thisObject->isMappedArgument(i))
99                 continue;
100             array.add(Identifier::from(exec, i));
101         }
102     }
103
104     if (mode.includeDontEnumProperties() && !thisObject->overrodeThings()) {
105         VM& vm = exec->vm();
106         array.add(vm.propertyNames->length);
107         array.add(vm.propertyNames->callee);
108         if (array.includeSymbolProperties())
109             array.add(vm.propertyNames->iteratorSymbol);
110     }
111     Base::getOwnPropertyNames(thisObject, exec, array, mode);
112 }
113
114 template<typename Type>
115 bool GenericArguments<Type>::put(JSCell* cell, ExecState* exec, PropertyName ident, JSValue value, PutPropertySlot& slot)
116 {
117     Type* thisObject = jsCast<Type*>(cell);
118     VM& vm = exec->vm();
119     
120     if (!thisObject->overrodeThings()
121         && (ident == vm.propertyNames->length
122             || ident == vm.propertyNames->callee
123             || ident == vm.propertyNames->iteratorSymbol)) {
124         thisObject->overrideThings(vm);
125         PutPropertySlot dummy = slot; // This put is not cacheable, so we shadow the slot that was given to us.
126         return Base::put(thisObject, exec, ident, value, dummy);
127     }
128
129     // https://tc39.github.io/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver
130     // Fall back to the OrdinarySet when the receiver is altered from the thisObject.
131     if (UNLIKELY(isThisValueAltered(slot, thisObject)))
132         return ordinarySetSlow(exec, thisObject, ident, value, slot.thisValue(), slot.isStrictMode());
133     
134     std::optional<uint32_t> index = parseIndex(ident);
135     if (index && thisObject->isMappedArgument(index.value())) {
136         thisObject->setIndexQuickly(vm, index.value(), value);
137         return true;
138     }
139     
140     return Base::put(thisObject, exec, ident, value, slot);
141 }
142
143 template<typename Type>
144 bool GenericArguments<Type>::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow)
145 {
146     Type* thisObject = jsCast<Type*>(cell);
147     VM& vm = exec->vm();
148
149     if (thisObject->isMappedArgument(index)) {
150         thisObject->setIndexQuickly(vm, index, value);
151         return true;
152     }
153     
154     return Base::putByIndex(cell, exec, index, value, shouldThrow);
155 }
156
157 template<typename Type>
158 bool GenericArguments<Type>::deleteProperty(JSCell* cell, ExecState* exec, PropertyName ident)
159 {
160     Type* thisObject = jsCast<Type*>(cell);
161     VM& vm = exec->vm();
162     
163     if (!thisObject->overrodeThings()
164         && (ident == vm.propertyNames->length
165             || ident == vm.propertyNames->callee
166             || ident == vm.propertyNames->iteratorSymbol))
167         thisObject->overrideThings(vm);
168     
169     if (std::optional<uint32_t> index = parseIndex(ident))
170         return GenericArguments<Type>::deletePropertyByIndex(thisObject, exec, *index);
171
172     return Base::deleteProperty(thisObject, exec, ident);
173 }
174
175 template<typename Type>
176 bool GenericArguments<Type>::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned index)
177 {
178     Type* thisObject = jsCast<Type*>(cell);
179     VM& vm = exec->vm();
180
181     bool propertyMightBeInJSObjectStorage = thisObject->isModifiedArgumentDescriptor(index) || !thisObject->isMappedArgument(index);
182     bool deletedProperty = true;
183     if (propertyMightBeInJSObjectStorage)
184         deletedProperty = Base::deletePropertyByIndex(cell, exec, index);
185
186     if (deletedProperty) {
187         // Deleting an indexed property unconditionally unmaps it.
188         if (thisObject->isMappedArgument(index)) {
189             // We need to check that the property was mapped so we don't write to random memory.
190             thisObject->unmapArgument(vm, index);
191         }
192         thisObject->setModifiedArgumentDescriptor(vm, index);
193     }
194
195     return deletedProperty;
196 }
197
198 template<typename Type>
199 bool GenericArguments<Type>::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow)
200 {
201     Type* thisObject = jsCast<Type*>(object);
202     VM& vm = exec->vm();
203     auto scope = DECLARE_THROW_SCOPE(vm);
204     
205     if (ident == vm.propertyNames->length
206         || ident == vm.propertyNames->callee
207         || ident == vm.propertyNames->iteratorSymbol)
208         thisObject->overrideThingsIfNecessary(vm);
209     else {
210         std::optional<uint32_t> optionalIndex = parseIndex(ident);
211         if (optionalIndex) {
212             uint32_t index = optionalIndex.value();
213             if (!descriptor.isAccessorDescriptor() && thisObject->isMappedArgument(optionalIndex.value())) {
214                 // If the property is not deleted and we are using a non-accessor descriptor, then
215                 // make sure that the aliased argument sees the value.
216                 if (descriptor.value())
217                     thisObject->setIndexQuickly(vm, index, descriptor.value());
218             
219                 // If the property is not deleted and we are using a non-accessor, writable,
220                 // configurable and enumerable descriptor and isn't modified, then we are done.
221                 // The argument continues to be aliased.
222                 if (descriptor.writable() && descriptor.configurable() && descriptor.enumerable() && !thisObject->isModifiedArgumentDescriptor(index))
223                     return true;
224                 
225                 if (!thisObject->isModifiedArgumentDescriptor(index)) {
226                     // If it is a new entry, we need to put direct to initialize argument[i] descriptor properly
227                     JSValue value = thisObject->getIndexQuickly(index);
228                     ASSERT(value);
229                     object->putDirectMayBeIndex(exec, ident, value);
230                     scope.assertNoException();
231
232                     thisObject->setModifiedArgumentDescriptor(vm, index);
233                 }
234             }
235             
236             if (thisObject->isMappedArgument(index)) {
237                 // Just unmap arguments if its descriptor contains {writable: false}.
238                 // Check https://tc39.github.io/ecma262/#sec-createunmappedargumentsobject
239                 // and https://tc39.github.io/ecma262/#sec-createmappedargumentsobject to verify that all data
240                 // property from arguments object are {writable: true, configurable: true, enumerable: true} by default
241                 if ((descriptor.writablePresent() && !descriptor.writable()) || descriptor.isAccessorDescriptor()) {
242                     if (!descriptor.isAccessorDescriptor()) {
243                         JSValue value = thisObject->getIndexQuickly(index);
244                         ASSERT(value);
245                         object->putDirectMayBeIndex(exec, ident, value);
246                         scope.assertNoException();
247                     }
248                     thisObject->unmapArgument(vm, index);
249                     thisObject->setModifiedArgumentDescriptor(vm, index);
250                 }
251             }
252         }
253     }
254
255     // Now just let the normal object machinery do its thing.
256     scope.release();
257     return Base::defineOwnProperty(object, exec, ident, descriptor, shouldThrow);
258 }
259
260 template<typename Type>
261 void GenericArguments<Type>::initModifiedArgumentsDescriptor(VM& vm, unsigned argsLength)
262 {
263     RELEASE_ASSERT(!m_modifiedArgumentsDescriptor);
264
265     if (argsLength) {
266         void* backingStore = vm.gigacageAuxiliarySpace(m_modifiedArgumentsDescriptor.kind).allocateNonVirtual(vm, WTF::roundUpToMultipleOf<8>(argsLength), nullptr, AllocationFailureMode::Assert);
267         bool* modifiedArguments = static_cast<bool*>(backingStore);
268         m_modifiedArgumentsDescriptor.set(vm, this, modifiedArguments);
269         for (unsigned i = argsLength; i--;)
270             modifiedArguments[i] = false;
271     }
272 }
273
274 template<typename Type>
275 void GenericArguments<Type>::initModifiedArgumentsDescriptorIfNecessary(VM& vm, unsigned argsLength)
276 {
277     if (!m_modifiedArgumentsDescriptor)
278         initModifiedArgumentsDescriptor(vm, argsLength);
279 }
280
281 template<typename Type>
282 void GenericArguments<Type>::setModifiedArgumentDescriptor(VM& vm, unsigned index, unsigned length)
283 {
284     initModifiedArgumentsDescriptorIfNecessary(vm, length);
285     if (index < length)
286         m_modifiedArgumentsDescriptor[index] = true;
287 }
288
289 template<typename Type>
290 bool GenericArguments<Type>::isModifiedArgumentDescriptor(unsigned index, unsigned length)
291 {
292     if (!m_modifiedArgumentsDescriptor)
293         return false;
294     if (index < length)
295         return m_modifiedArgumentsDescriptor[index];
296     return false;
297 }
298
299 template<typename Type>
300 void GenericArguments<Type>::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
301 {
302     VM& vm = exec->vm();
303     auto scope = DECLARE_THROW_SCOPE(vm);
304
305     Type* thisObject = static_cast<Type*>(this);
306     for (unsigned i = 0; i < length; ++i) {
307         if (thisObject->isMappedArgument(i + offset))
308             exec->r(firstElementDest + i) = thisObject->getIndexQuickly(i + offset);
309         else {
310             exec->r(firstElementDest + i) = get(exec, i + offset);
311             RETURN_IF_EXCEPTION(scope, void());
312         }
313     }
314 }
315
316 } // namespace JSC