/ runtime / JSGenericTypedArrayViewConstructorInlines.h
JSGenericTypedArrayViewConstructorInlines.h
  1  /*
  2   * Copyright (C) 2013-2020 Apple Inc. All rights reserved.
  3   * Copyright (C) 2020 Sony Interactive Entertainment Inc.
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions
  7   * are met:
  8   * 1. Redistributions of source code must retain the above copyright
  9   *    notice, this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   *
 14   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 15   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 16   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 17   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 18   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 19   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 20   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 21   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 22   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 23   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 24   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 25   */
 26  
 27  #pragma once
 28  
 29  #include "BuiltinNames.h"
 30  #include "Error.h"
 31  #include "IteratorOperations.h"
 32  #include "JSArrayBuffer.h"
 33  #include "JSArrayBufferPrototypeInlines.h"
 34  #include "JSCJSValueInlines.h"
 35  #include "JSDataView.h"
 36  #include "JSGenericTypedArrayViewConstructor.h"
 37  #include "JSGlobalObject.h"
 38  #include "StructureInlines.h"
 39  
 40  namespace JSC {
 41  
 42  template<typename ViewClass>
 43  JSGenericTypedArrayViewConstructor<ViewClass>::JSGenericTypedArrayViewConstructor(VM& vm, Structure* structure)
 44      : Base(vm, structure, callConstructor(), constructConstructor())
 45  {
 46  }
 47  
 48  template<typename ViewClass>
 49  void JSGenericTypedArrayViewConstructor<ViewClass>::finishCreation(VM& vm, JSGlobalObject*, JSObject* prototype, const String& name)
 50  {
 51      Base::finishCreation(vm, ViewClass::TypedArrayStorageType == TypeDataView ? 1 : 3, name, PropertyAdditionMode::WithoutStructureTransition);
 52      putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
 53      putDirectWithoutTransition(vm, vm.propertyNames->BYTES_PER_ELEMENT, jsNumber(ViewClass::elementSize), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete);
 54  }
 55  
 56  template<typename ViewClass>
 57  JSGenericTypedArrayViewConstructor<ViewClass>*
 58  JSGenericTypedArrayViewConstructor<ViewClass>::create(
 59      VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype,
 60      const String& name)
 61  {
 62      JSGenericTypedArrayViewConstructor* result =
 63          new (NotNull, allocateCell<JSGenericTypedArrayViewConstructor>(vm.heap))
 64          JSGenericTypedArrayViewConstructor(vm, structure);
 65      result->finishCreation(vm, globalObject, prototype, name);
 66      return result;
 67  }
 68  
 69  template<typename ViewClass>
 70  Structure* JSGenericTypedArrayViewConstructor<ViewClass>::createStructure(
 71      VM& vm, JSGlobalObject* globalObject, JSValue prototype)
 72  {
 73      return Structure::create(
 74          vm, globalObject, prototype, TypeInfo(InternalFunctionType, StructureFlags), info());
 75  }
 76  
 77  template<typename ViewClass>
 78  inline JSObject* constructGenericTypedArrayViewFromIterator(JSGlobalObject* globalObject, Structure* structure, JSObject* iterable, JSValue iteratorMethod)
 79  {
 80      VM& vm = globalObject->vm();
 81      auto scope = DECLARE_THROW_SCOPE(vm);
 82  
 83      MarkedArgumentBuffer storage;
 84      forEachInIterable(*globalObject, iterable, iteratorMethod, [&] (VM&, JSGlobalObject&, JSValue value) {
 85          storage.append(value);
 86          if (UNLIKELY(storage.hasOverflowed())) {
 87              throwOutOfMemoryError(globalObject, scope);
 88              return;
 89          }
 90      });
 91      RETURN_IF_EXCEPTION(scope, nullptr);
 92  
 93      ViewClass* result = ViewClass::createUninitialized(globalObject, structure, storage.size());
 94      EXCEPTION_ASSERT(!!scope.exception() == !result);
 95      if (UNLIKELY(!result))
 96          return nullptr;
 97  
 98      for (unsigned i = 0; i < storage.size(); ++i) {
 99          bool success = result->setIndex(globalObject, i, storage.at(i));
100          EXCEPTION_ASSERT(scope.exception() || success);
101          if (!success)
102              return nullptr;
103      }
104  
105      return result;
106  }
107  
108  inline JSArrayBuffer* constructCustomArrayBufferIfNeeded(JSGlobalObject* globalObject, JSArrayBufferView* view)
109  {
110      VM& vm = globalObject->vm();
111      auto scope = DECLARE_THROW_SCOPE(vm);
112  
113      JSArrayBuffer* source = view->possiblySharedJSBuffer(globalObject);
114      RETURN_IF_EXCEPTION(scope, nullptr);
115      if (source->isShared())
116          return nullptr;
117  
118      Optional<JSValue> species = arrayBufferSpeciesConstructor(globalObject, source, ArrayBufferSharingMode::Default);
119      RETURN_IF_EXCEPTION(scope, nullptr);
120      if (!species)
121          return nullptr;
122  
123      if (!species->isConstructor(vm)) {
124          throwTypeError(globalObject, scope, "species is not a constructor"_s);
125          return nullptr;
126      }
127  
128      JSValue prototype = species->get(globalObject, vm.propertyNames->prototype);
129      RETURN_IF_EXCEPTION(scope, nullptr);
130  
131      auto buffer = ArrayBuffer::tryCreate(source->impl()->byteLength(), 1);
132      if (!buffer) {
133          throwOutOfMemoryError(globalObject, scope);
134          return nullptr;
135      }
136  
137      auto result = JSArrayBuffer::create(vm, getFunctionRealm(vm, asObject(species.value()))->arrayBufferStructure(ArrayBufferSharingMode::Default), WTFMove(buffer));
138      if (prototype.isObject())
139          result->setPrototypeDirect(vm, prototype);
140      return result;
141  }
142  
143  template<typename ViewClass>
144  inline JSObject* constructGenericTypedArrayViewWithArguments(JSGlobalObject* globalObject, Structure* structure, EncodedJSValue firstArgument, unsigned offset, Optional<unsigned> lengthOpt)
145  {
146      VM& vm = globalObject->vm();
147      auto scope = DECLARE_THROW_SCOPE(vm);
148  
149      JSValue firstValue = JSValue::decode(firstArgument);
150  
151      if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, firstValue)) {
152          RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
153          if (buffer->isDetached()) {
154              throwTypeError(globalObject, scope, "Buffer is already detached"_s);
155              return nullptr;
156          }
157  
158          unsigned length = 0;
159          if (lengthOpt)
160              length = lengthOpt.value();
161          else {
162              if (UNLIKELY((buffer->byteLength() - offset) % ViewClass::elementSize)) {
163                  throwRangeError(globalObject, scope, "ArrayBuffer length minus the byteOffset is not a multiple of the element size"_s);
164                  return nullptr;
165              }
166              length = (buffer->byteLength() - offset) / ViewClass::elementSize;
167          }
168  
169          RELEASE_AND_RETURN(scope, ViewClass::create(globalObject, structure, WTFMove(buffer), offset, length));
170      }
171      ASSERT(!offset && !lengthOpt);
172      
173      if (UNLIKELY(ViewClass::TypedArrayStorageType == TypeDataView)) {
174          throwTypeError(globalObject, scope, "Expected ArrayBuffer for the first argument."_s);
175          return nullptr;
176      }
177      
178      // For everything but DataView, we allow construction with any of:
179      // - Another array. This creates a copy of the of that array.
180      // - A primitive. This creates a new typed array of that length and zero-initializes it.
181  
182      if (JSObject* object = jsDynamicCast<JSObject*>(vm, firstValue)) {
183          unsigned length;
184          JSArrayBuffer* customBuffer = nullptr;
185  
186          if (isTypedView(object->classInfo(vm)->typedArrayStorageType)) {
187              auto* view = jsCast<JSArrayBufferView*>(object);
188  
189              customBuffer = constructCustomArrayBufferIfNeeded(globalObject, view);
190              RETURN_IF_EXCEPTION(scope, nullptr);
191              if (view->isDetached()) {
192                  throwTypeError(globalObject, scope, "Underlying ArrayBuffer has been detached from the view"_s);
193                  return nullptr;
194              }
195  
196              length = view->length();
197          } else {
198              // This getPropertySlot operation should not be observed by the Proxy.
199              // So we use VMInquiry. And purge the opaque object cases (proxy and namespace object) by isTaintedByOpaqueObject() guard.
200              PropertySlot lengthSlot(object, PropertySlot::InternalMethodType::VMInquiry, &vm);
201              object->getPropertySlot(globalObject, vm.propertyNames->length, lengthSlot);
202              RETURN_IF_EXCEPTION(scope, nullptr);
203              lengthSlot.disallowVMEntry.reset();
204  
205              JSValue iteratorFunc = object->get(globalObject, vm.propertyNames->iteratorSymbol);
206              RETURN_IF_EXCEPTION(scope, nullptr);
207  
208              // We would like not use the iterator as it is painfully slow. Fortunately, unless
209              // 1) The iterator is not a known iterator.
210              // 2) The base object does not have a length getter.
211              // 3) The base object might have indexed getters.
212              // it should not be observable that we do not use the iterator.
213  
214              if (!iteratorFunc.isUndefinedOrNull()
215                  && (iteratorFunc != object->globalObject(vm)->arrayProtoValuesFunction()
216                      || lengthSlot.isAccessor() || lengthSlot.isCustom() || lengthSlot.isTaintedByOpaqueObject()
217                      || hasAnyArrayStorage(object->indexingType()))) {
218  
219                      RELEASE_AND_RETURN(scope, constructGenericTypedArrayViewFromIterator<ViewClass>(globalObject, structure, object, iteratorFunc));
220              }
221  
222              if (lengthSlot.isUnset())
223                  length = 0;
224              else {
225                  JSValue value = lengthSlot.getValue(globalObject, vm.propertyNames->length);
226                  RETURN_IF_EXCEPTION(scope, nullptr);
227                  length = value.toLength(globalObject);
228                  RETURN_IF_EXCEPTION(scope, nullptr);
229              }
230          }
231  
232          ViewClass* result = customBuffer
233              ? ViewClass::create(globalObject, structure, customBuffer->impl(), 0, length)
234              : ViewClass::createUninitialized(globalObject, structure, length);
235          EXCEPTION_ASSERT(!!scope.exception() == !result);
236          if (UNLIKELY(!result))
237              return nullptr;
238  
239          scope.release();
240          if (!result->set(globalObject, 0, object, 0, length))
241              return nullptr;
242          
243          return result;
244      }
245  
246      unsigned length = firstValue.toIndex(globalObject, "length");
247      RETURN_IF_EXCEPTION(scope, nullptr);
248      RELEASE_AND_RETURN(scope, ViewClass::create(globalObject, structure, length));
249  }
250  
251  template<typename ViewClass>
252  ALWAYS_INLINE EncodedJSValue constructGenericTypedArrayViewImpl(JSGlobalObject* globalObject, CallFrame* callFrame)
253  {
254      VM& vm = globalObject->vm();
255      auto scope = DECLARE_THROW_SCOPE(vm);
256  
257      JSObject* newTarget = asObject(callFrame->newTarget());
258      Structure* structure = newTarget == callFrame->jsCallee()
259          ? globalObject->typedArrayStructure(ViewClass::TypedArrayStorageType)
260          : InternalFunction::createSubclassStructure(globalObject, newTarget, getFunctionRealm(vm, newTarget)->typedArrayStructure(ViewClass::TypedArrayStorageType));
261      RETURN_IF_EXCEPTION(scope, { });
262  
263      size_t argCount = callFrame->argumentCount();
264  
265      if (!argCount) {
266          if (ViewClass::TypedArrayStorageType == TypeDataView)
267              return throwVMTypeError(globalObject, scope, "DataView constructor requires at least one argument."_s);
268  
269          RELEASE_AND_RETURN(scope, JSValue::encode(ViewClass::create(globalObject, structure, 0)));
270      }
271  
272      JSValue firstValue = callFrame->uncheckedArgument(0);
273      unsigned offset = 0;
274      Optional<unsigned> length = WTF::nullopt;
275      if (jsDynamicCast<JSArrayBuffer*>(vm, firstValue) && argCount > 1) {
276          offset = callFrame->uncheckedArgument(1).toIndex(globalObject, "byteOffset");
277          RETURN_IF_EXCEPTION(scope, encodedJSValue());
278  
279          if (argCount > 2) {
280              // If the length value is present but undefined, treat it as missing.
281              JSValue lengthValue = callFrame->uncheckedArgument(2);
282              if (!lengthValue.isUndefined()) {
283                  length = lengthValue.toIndex(globalObject, ViewClass::TypedArrayStorageType == TypeDataView ? "byteLength" : "length");
284                  RETURN_IF_EXCEPTION(scope, encodedJSValue());
285              }
286          }
287      }
288  
289      RELEASE_AND_RETURN(scope, JSValue::encode(constructGenericTypedArrayViewWithArguments<ViewClass>(globalObject, structure, JSValue::encode(firstValue), offset, length)));
290  }
291  
292  template<typename ViewClass>
293  ALWAYS_INLINE EncodedJSValue callGenericTypedArrayViewImpl(JSGlobalObject* globalObject, CallFrame*)
294  {
295      VM& vm = globalObject->vm();
296      auto scope = DECLARE_THROW_SCOPE(vm);
297      return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(globalObject, scope, ViewClass::info()->className));
298  }
299  
300  } // namespace JSC