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.getUnsafe()); |
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 (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 | 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 (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 | 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 | RELEASE_AND_RETURN(scope, 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, argsLength); |
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.at(index, length) = 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.at(index, length); |
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 |
317 | |