Speculatively change iteration protocall to use the same next function
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSGenericTypedArrayViewConstructorInlines.h
1 /*
2  * Copyright (C) 2013-2016 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 "BuiltinNames.h"
29 #include "Error.h"
30 #include "IteratorOperations.h"
31 #include "JSArrayBuffer.h"
32 #include "JSCJSValueInlines.h"
33 #include "JSDataView.h"
34 #include "JSGenericTypedArrayViewConstructor.h"
35 #include "JSGlobalObject.h"
36 #include "StructureInlines.h"
37
38 namespace JSC {
39
40 template<typename ViewClass>
41 JSGenericTypedArrayViewConstructor<ViewClass>::JSGenericTypedArrayViewConstructor(VM& vm, Structure* structure)
42     : Base(vm, structure)
43 {
44 }
45
46 template<typename ViewClass>
47 void JSGenericTypedArrayViewConstructor<ViewClass>::finishCreation(VM& vm, JSGlobalObject* globalObject, JSObject* prototype, const String& name, FunctionExecutable* privateAllocator)
48 {
49     Base::finishCreation(vm, name);
50     putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly);
51     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(3), DontEnum | ReadOnly);
52     putDirectWithoutTransition(vm, vm.propertyNames->BYTES_PER_ELEMENT, jsNumber(ViewClass::elementSize), DontEnum | ReadOnly | DontDelete);
53
54     if (privateAllocator)
55         putDirectBuiltinFunction(vm, globalObject, vm.propertyNames->builtinNames().allocateTypedArrayPrivateName(), privateAllocator, DontEnum | DontDelete | ReadOnly);
56 }
57
58 template<typename ViewClass>
59 JSGenericTypedArrayViewConstructor<ViewClass>*
60 JSGenericTypedArrayViewConstructor<ViewClass>::create(
61     VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype,
62     const String& name, FunctionExecutable* privateAllocator)
63 {
64     JSGenericTypedArrayViewConstructor* result =
65         new (NotNull, allocateCell<JSGenericTypedArrayViewConstructor>(vm.heap))
66         JSGenericTypedArrayViewConstructor(vm, structure);
67     result->finishCreation(vm, globalObject, prototype, name, privateAllocator);
68     return result;
69 }
70
71 template<typename ViewClass>
72 Structure* JSGenericTypedArrayViewConstructor<ViewClass>::createStructure(
73     VM& vm, JSGlobalObject* globalObject, JSValue prototype)
74 {
75     return Structure::create(
76         vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
77 }
78
79 template<typename ViewClass>
80 inline JSObject* constructGenericTypedArrayViewFromIterator(ExecState* exec, Structure* structure, JSObject* iterable, JSValue iteratorMethod)
81 {
82     VM& vm = exec->vm();
83     auto scope = DECLARE_THROW_SCOPE(vm);
84
85     MarkedArgumentBuffer storage;
86     forEachInIterable(*exec, iterable, iteratorMethod, [&] (VM&, ExecState&, JSValue value) {
87         storage.append(value);
88     });
89     RETURN_IF_EXCEPTION(scope, nullptr);
90
91     ViewClass* result = ViewClass::createUninitialized(exec, structure, storage.size());
92     EXCEPTION_ASSERT(!!scope.exception() == !result);
93     if (UNLIKELY(!result))
94         return nullptr;
95
96     for (unsigned i = 0; i < storage.size(); ++i) {
97         bool success = result->setIndex(exec, i, storage.at(i));
98         EXCEPTION_ASSERT(scope.exception() || success);
99         if (!success)
100             return nullptr;
101     }
102
103     return result;
104 }
105
106 template<typename ViewClass>
107 inline JSObject* constructGenericTypedArrayViewWithArguments(ExecState* exec, Structure* structure, EncodedJSValue firstArgument, unsigned offset, std::optional<unsigned> lengthOpt)
108 {
109     VM& vm = exec->vm();
110     auto scope = DECLARE_THROW_SCOPE(vm);
111
112     JSValue firstValue = JSValue::decode(firstArgument);
113
114     if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, firstValue)) {
115         RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
116         unsigned length = 0;
117
118         if (lengthOpt)
119             length = lengthOpt.value();
120         else {
121             if ((buffer->byteLength() - offset) % ViewClass::elementSize)
122                 return throwRangeError(exec, scope, ASCIILiteral("ArrayBuffer length minus the byteOffset is not a multiple of the element size"));
123             length = (buffer->byteLength() - offset) / ViewClass::elementSize;
124         }
125
126         scope.release();
127         return ViewClass::create(exec, structure, WTFMove(buffer), offset, length);
128     }
129     ASSERT(!offset && !lengthOpt);
130     
131     if (ViewClass::TypedArrayStorageType == TypeDataView)
132         return throwTypeError(exec, scope, ASCIILiteral("Expected ArrayBuffer for the first argument."));
133     
134     // For everything but DataView, we allow construction with any of:
135     // - Another array. This creates a copy of the of that array.
136     // - A primitive. This creates a new typed array of that length and zero-initializes it.
137
138     if (JSObject* object = jsDynamicCast<JSObject*>(vm, firstValue)) {
139         unsigned length;
140
141         if (isTypedView(object->classInfo(vm)->typedArrayStorageType))
142             length = jsCast<JSArrayBufferView*>(object)->length();
143         else {
144             // This getPropertySlot operation should not be observed by the Proxy.
145             // So we use VMInquiry. And purge the opaque object cases (proxy and namespace object) by isTaintedByOpaqueObject() guard.
146             PropertySlot lengthSlot(object, PropertySlot::InternalMethodType::VMInquiry);
147             object->getPropertySlot(exec, vm.propertyNames->length, lengthSlot);
148             RETURN_IF_EXCEPTION(scope, nullptr);
149
150             JSValue iteratorFunc = object->get(exec, vm.propertyNames->iteratorSymbol);
151             RETURN_IF_EXCEPTION(scope, nullptr);
152
153             // We would like not use the iterator as it is painfully slow. Fortunately, unless
154             // 1) The iterator is not a known iterator.
155             // 2) The base object does not have a length getter.
156             // 3) The base object might have indexed getters.
157             // it should not be observable that we do not use the iterator.
158
159             if (!iteratorFunc.isUndefined()
160                 && (iteratorFunc != object->globalObject()->arrayProtoValuesFunction()
161                     || lengthSlot.isAccessor() || lengthSlot.isCustom() || lengthSlot.isTaintedByOpaqueObject()
162                     || hasAnyArrayStorage(object->indexingType()))) {
163
164                     return constructGenericTypedArrayViewFromIterator<ViewClass>(exec, structure, object, iteratorFunc);
165             }
166
167             if (lengthSlot.isUnset())
168                 length = 0;
169             else {
170                 JSValue value = lengthSlot.getValue(exec, vm.propertyNames->length);
171                 RETURN_IF_EXCEPTION(scope, nullptr);
172                 length = value.toUInt32(exec);
173                 RETURN_IF_EXCEPTION(scope, nullptr);
174             }
175         }
176
177         
178         ViewClass* result = ViewClass::createUninitialized(exec, structure, length);
179         EXCEPTION_ASSERT(!!scope.exception() == !result);
180         if (UNLIKELY(!result))
181             return nullptr;
182         
183         scope.release();
184         if (!result->set(exec, 0, object, 0, length))
185             return nullptr;
186         
187         return result;
188     }
189
190     unsigned length = firstValue.toIndex(exec, "length");
191     RETURN_IF_EXCEPTION(scope, nullptr);
192     scope.release();
193     return ViewClass::create(exec, structure, length);
194 }
195
196 template<typename ViewClass>
197 EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState*);
198
199 template<typename ViewClass>
200 EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec)
201 {
202     VM& vm = exec->vm();
203     auto scope = DECLARE_THROW_SCOPE(vm);
204
205     InternalFunction* function = asInternalFunction(exec->jsCallee());
206     Structure* parentStructure = function->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
207     Structure* structure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), parentStructure);
208     RETURN_IF_EXCEPTION(scope, encodedJSValue());
209
210     size_t argCount = exec->argumentCount();
211
212     if (!argCount) {
213         if (ViewClass::TypedArrayStorageType == TypeDataView)
214             return throwVMTypeError(exec, scope, ASCIILiteral("DataView constructor requires at least one argument."));
215
216         scope.release();
217         return JSValue::encode(ViewClass::create(exec, structure, 0));
218     }
219
220     JSValue firstValue = exec->uncheckedArgument(0);
221     unsigned offset = 0;
222     std::optional<unsigned> length = std::nullopt;
223     if (jsDynamicCast<JSArrayBuffer*>(vm, firstValue) && argCount > 1) {
224         offset = exec->uncheckedArgument(1).toIndex(exec, "byteOffset");
225         RETURN_IF_EXCEPTION(scope, encodedJSValue());
226
227         if (argCount > 2) {
228             if (ViewClass::TypedArrayStorageType == TypeDataView) {
229                 // If the DataView byteLength is present but undefined, treat it as missing.
230                 JSValue byteLengthValue = exec->uncheckedArgument(2);
231                 if (!byteLengthValue.isUndefined()) {
232                     length = byteLengthValue.toIndex(exec, "byteLength");
233                     RETURN_IF_EXCEPTION(scope, encodedJSValue());
234                 }
235             } else {
236                 length = exec->uncheckedArgument(2).toIndex(exec, "length");
237                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
238             }
239         }
240     }
241
242     scope.release();
243     return JSValue::encode(constructGenericTypedArrayViewWithArguments<ViewClass>(exec, structure, JSValue::encode(firstValue), offset, length));
244 }
245
246 template<typename ViewClass>
247 ConstructType JSGenericTypedArrayViewConstructor<ViewClass>::getConstructData(JSCell*, ConstructData& constructData)
248 {
249     constructData.native.function = constructGenericTypedArrayView<ViewClass>;
250     return ConstructType::Host;
251 }
252
253 template<typename ViewClass>
254 static EncodedJSValue JSC_HOST_CALL callGenericTypedArrayView(ExecState* exec)
255 {
256     VM& vm = exec->vm();
257     auto scope = DECLARE_THROW_SCOPE(vm);
258     return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(exec, scope, ViewClass::info()->className));
259 }
260
261 template<typename ViewClass>
262 CallType JSGenericTypedArrayViewConstructor<ViewClass>::getCallData(JSCell*, CallData& callData)
263 {
264     callData.native.function = callGenericTypedArrayView<ViewClass>;
265     return CallType::Host;
266 }
267
268 } // namespace JSC