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