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