JSGenericTypedArrayView.h
1 /* 2 * Copyright (C) 2013-2020 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 "JSArrayBufferView.h" 29 #include "ThrowScope.h" 30 #include "ToNativeFromValue.h" 31 32 namespace JSC { 33 34 JS_EXPORT_PRIVATE const ClassInfo* getInt8ArrayClassInfo(); 35 JS_EXPORT_PRIVATE const ClassInfo* getInt16ArrayClassInfo(); 36 JS_EXPORT_PRIVATE const ClassInfo* getInt32ArrayClassInfo(); 37 JS_EXPORT_PRIVATE const ClassInfo* getUint8ArrayClassInfo(); 38 JS_EXPORT_PRIVATE const ClassInfo* getUint8ClampedArrayClassInfo(); 39 JS_EXPORT_PRIVATE const ClassInfo* getUint16ArrayClassInfo(); 40 JS_EXPORT_PRIVATE const ClassInfo* getUint32ArrayClassInfo(); 41 JS_EXPORT_PRIVATE const ClassInfo* getFloat32ArrayClassInfo(); 42 JS_EXPORT_PRIVATE const ClassInfo* getFloat64ArrayClassInfo(); 43 44 // A typed array view is our representation of a typed array object as seen 45 // from JavaScript. For example: 46 // 47 // var o = new Int8Array(100); 48 // 49 // Here, 'o' points to a JSGenericTypedArrayView<int8_t>. 50 // 51 // Views contain five fields: 52 // 53 // Structure* S // from JSCell 54 // Butterfly* B // from JSObject 55 // ElementType* V 56 // uint32_t L 57 // TypedArrayMode M 58 // 59 // These fields take up a total of four pointer-width words. FIXME: Make 60 // it take less words! 61 // 62 // B is usually unused but may stored some additional "overflow" data for 63 // one of the modes. V always points to the base of the typed array's data, 64 // and may point to either GC-managed copied space, or data in the C heap; 65 // which of those things it points to is governed by the mode although for 66 // simple accesses to the view you can just read from the pointer either 67 // way. M specifies the mode of the view. L is the length, in units that 68 // depend on the view's type. 69 70 // The JSGenericTypedArrayView is templatized by an Adaptor that controls 71 // the element type and how it's converted; it should obey the following 72 // interface; I use int8_t as an example: 73 // 74 // struct Adaptor { 75 // typedef int8_t Type; 76 // typedef Int8Array ViewType; 77 // typedef JSInt8Array JSViewType; 78 // static int8_t toNativeFromInt32(int32_t); 79 // static int8_t toNativeFromUint32(uint32_t); 80 // static int8_t toNativeFromDouble(double); 81 // static JSValue toJSValue(int8_t); 82 // static double toDouble(int8_t); 83 // template<T> static T::Type convertTo(uint8_t); 84 // }; 85 86 enum class CopyType { 87 LeftToRight, 88 Unobservable, 89 }; 90 91 extern const ASCIILiteral typedArrayBufferHasBeenDetachedErrorMessage; 92 93 template<typename Adaptor> 94 class JSGenericTypedArrayView final : public JSArrayBufferView { 95 public: 96 using Base = JSArrayBufferView; 97 typedef typename Adaptor::Type ElementType; 98 99 static constexpr unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetOwnPropertyNames | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero; 100 101 static constexpr unsigned elementSize = sizeof(typename Adaptor::Type); 102 103 static JSGenericTypedArrayView* create(JSGlobalObject*, Structure*, unsigned length); 104 static JSGenericTypedArrayView* createWithFastVector(JSGlobalObject*, Structure*, unsigned length, void* vector); 105 static JSGenericTypedArrayView* createUninitialized(JSGlobalObject*, Structure*, unsigned length); 106 static JSGenericTypedArrayView* create(JSGlobalObject*, Structure*, RefPtr<ArrayBuffer>&&, unsigned byteOffset, unsigned length); 107 static JSGenericTypedArrayView* create(VM&, Structure*, RefPtr<typename Adaptor::ViewType>&& impl); 108 static JSGenericTypedArrayView* create(Structure*, JSGlobalObject*, RefPtr<typename Adaptor::ViewType>&& impl); 109 110 unsigned byteLength() const { return m_length * sizeof(typename Adaptor::Type); } 111 size_t byteSize() const { return sizeOf(m_length, sizeof(typename Adaptor::Type)); } 112 113 const typename Adaptor::Type* typedVector() const 114 { 115 return bitwise_cast<const typename Adaptor::Type*>(vector()); 116 } 117 typename Adaptor::Type* typedVector() 118 { 119 return bitwise_cast<typename Adaptor::Type*>(vector()); 120 } 121 122 // These methods are meant to match indexed access methods that JSObject 123 // supports - hence the slight redundancy. 124 bool canGetIndexQuickly(unsigned i) const 125 { 126 return i < m_length; 127 } 128 bool canSetIndexQuickly(unsigned i, JSValue value) const 129 { 130 return i < m_length && value.isNumber(); 131 } 132 133 typename Adaptor::Type getIndexQuicklyAsNativeValue(unsigned i) const 134 { 135 ASSERT(i < m_length); 136 return typedVector()[i]; 137 } 138 139 double getIndexQuicklyAsDouble(unsigned i) 140 { 141 return Adaptor::toDouble(getIndexQuicklyAsNativeValue(i)); 142 } 143 144 JSValue getIndexQuickly(unsigned i) const 145 { 146 return Adaptor::toJSValue(getIndexQuicklyAsNativeValue(i)); 147 } 148 149 void setIndexQuicklyToNativeValue(unsigned i, typename Adaptor::Type value) 150 { 151 ASSERT(i < m_length); 152 typedVector()[i] = value; 153 } 154 155 void setIndexQuicklyToDouble(unsigned i, double value) 156 { 157 setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(jsNumber(value))); 158 } 159 160 void setIndexQuickly(unsigned i, JSValue value) 161 { 162 ASSERT(!value.isObject()); 163 setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value)); 164 } 165 166 bool setIndex(JSGlobalObject* globalObject, unsigned i, JSValue jsValue) 167 { 168 VM& vm = getVM(globalObject); 169 auto scope = DECLARE_THROW_SCOPE(vm); 170 171 typename Adaptor::Type value = toNativeFromValue<Adaptor>(globalObject, jsValue); 172 RETURN_IF_EXCEPTION(scope, false); 173 174 if (isDetached() || i >= m_length) 175 return false; 176 177 setIndexQuicklyToNativeValue(i, value); 178 return true; 179 } 180 181 static ElementType toAdaptorNativeFromValue(JSGlobalObject* globalObject, JSValue jsValue) { return toNativeFromValue<Adaptor>(globalObject, jsValue); } 182 183 static Optional<ElementType> toAdaptorNativeFromValueWithoutCoercion(JSValue jsValue) { return toNativeFromValueWithoutCoercion<Adaptor>(jsValue); } 184 185 void sort() 186 { 187 RELEASE_ASSERT(!isDetached()); 188 switch (Adaptor::typeValue) { 189 case TypeFloat32: 190 sortFloat<int32_t>(); 191 break; 192 case TypeFloat64: 193 sortFloat<int64_t>(); 194 break; 195 default: { 196 ElementType* array = typedVector(); 197 std::sort(array, array + m_length); 198 break; 199 } 200 } 201 } 202 203 bool canAccessRangeQuickly(unsigned offset, unsigned length) 204 { 205 return offset <= m_length 206 && offset + length <= m_length 207 // check overflow 208 && offset + length >= offset; 209 } 210 211 // Like canSetQuickly, except: if it returns false, it will throw the 212 // appropriate exception. 213 bool validateRange(JSGlobalObject*, unsigned offset, unsigned length); 214 215 // Returns true if successful, and false on error; if it returns false 216 // then it will have thrown an exception. 217 bool set(JSGlobalObject*, unsigned offset, JSObject*, unsigned objectOffset, unsigned length, CopyType type = CopyType::Unobservable); 218 219 RefPtr<typename Adaptor::ViewType> possiblySharedTypedImpl(); 220 RefPtr<typename Adaptor::ViewType> unsharedTypedImpl(); 221 222 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 223 { 224 return Structure::create(vm, globalObject, prototype, TypeInfo(typeForTypedArrayType(Adaptor::typeValue), StructureFlags), info(), NonArray); 225 } 226 227 static const ClassInfo s_info; // This is never accessed directly, since that would break linkage on some compilers. 228 229 static const ClassInfo* info() 230 { 231 switch (Adaptor::typeValue) { 232 case TypeInt8: 233 return getInt8ArrayClassInfo(); 234 case TypeInt16: 235 return getInt16ArrayClassInfo(); 236 case TypeInt32: 237 return getInt32ArrayClassInfo(); 238 case TypeUint8: 239 return getUint8ArrayClassInfo(); 240 case TypeUint8Clamped: 241 return getUint8ClampedArrayClassInfo(); 242 case TypeUint16: 243 return getUint16ArrayClassInfo(); 244 case TypeUint32: 245 return getUint32ArrayClassInfo(); 246 case TypeFloat32: 247 return getFloat32ArrayClassInfo(); 248 case TypeFloat64: 249 return getFloat64ArrayClassInfo(); 250 default: 251 RELEASE_ASSERT_NOT_REACHED(); 252 return nullptr; 253 } 254 } 255 256 template<typename CellType, SubspaceAccess mode> 257 static IsoSubspace* subspaceFor(VM& vm) 258 { 259 switch (Adaptor::typeValue) { 260 case TypeInt8: 261 return vm.int8ArraySpace<mode>(); 262 case TypeInt16: 263 return vm.int16ArraySpace<mode>(); 264 case TypeInt32: 265 return vm.int32ArraySpace<mode>(); 266 case TypeUint8: 267 return vm.uint8ArraySpace<mode>(); 268 case TypeUint8Clamped: 269 return vm.uint8ClampedArraySpace<mode>(); 270 case TypeUint16: 271 return vm.uint16ArraySpace<mode>(); 272 case TypeUint32: 273 return vm.uint32ArraySpace<mode>(); 274 case TypeFloat32: 275 return vm.float32ArraySpace<mode>(); 276 case TypeFloat64: 277 return vm.float64ArraySpace<mode>(); 278 default: 279 RELEASE_ASSERT_NOT_REACHED(); 280 return nullptr; 281 } 282 } 283 284 ArrayBuffer* existingBuffer(); 285 286 static const TypedArrayType TypedArrayStorageType = Adaptor::typeValue; 287 288 // This is the default DOM unwrapping. It calls toUnsharedNativeTypedView(). 289 static RefPtr<typename Adaptor::ViewType> toWrapped(VM&, JSValue); 290 // [AllowShared] annotation allows accepting TypedArray originated from SharedArrayBuffer. 291 static RefPtr<typename Adaptor::ViewType> toWrappedAllowShared(VM&, JSValue); 292 293 private: 294 friend struct TypedArrayClassInfos; 295 296 JSGenericTypedArrayView(VM&, ConstructionContext&); 297 298 static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, PropertyName, PropertySlot&); 299 static bool put(JSCell*, JSGlobalObject*, PropertyName, JSValue, PutPropertySlot&); 300 static bool defineOwnProperty(JSObject*, JSGlobalObject*, PropertyName, const PropertyDescriptor&, bool shouldThrow); 301 static bool deleteProperty(JSCell*, JSGlobalObject*, PropertyName, DeletePropertySlot&); 302 303 static bool getOwnPropertySlotByIndex(JSObject*, JSGlobalObject*, unsigned propertyName, PropertySlot&); 304 static bool putByIndex(JSCell*, JSGlobalObject*, unsigned propertyName, JSValue, bool shouldThrow); 305 static bool deletePropertyByIndex(JSCell*, JSGlobalObject*, unsigned propertyName); 306 307 static void getOwnPropertyNames(JSObject*, JSGlobalObject*, PropertyNameArray&, DontEnumPropertiesMode); 308 309 static size_t estimatedSize(JSCell*, VM&); 310 static void visitChildren(JSCell*, SlotVisitor&); 311 312 // Returns true if successful, and false on error; it will throw on error. 313 template<typename OtherAdaptor> 314 bool setWithSpecificType( 315 JSGlobalObject*, unsigned offset, JSGenericTypedArrayView<OtherAdaptor>*, 316 unsigned objectOffset, unsigned length, CopyType); 317 318 // The ECMA 6 spec states that floating point Typed Arrays should have the following ordering: 319 // 320 // -Inifinity < negative finite numbers < -0.0 < 0.0 < positive finite numbers < Infinity < NaN 321 // Note: regardless of the sign or exact representation of a NaN it is greater than all other values. 322 // 323 // An interesting fact about IEEE 754 floating point numbers is that have an adjacent representation 324 // i.e. for any finite floating point x there does not exist a finite floating point y such that 325 // ((float) ((int) x + 1)) > y > x (where int represents a signed bit integer with the same number 326 // of bits as float). Thus, if we have an array of floating points if we view it as an 327 // array of signed bit integers it will sort in the format we desire. Note, denormal 328 // numbers fit this property as they are floating point numbers with a exponent field of all 329 // zeros so they will be closer to the signed zeros than any normalized number. 330 // 331 // All the processors we support, however, use twos complement. Fortunately, if you compare a signed 332 // bit number as if it were twos complement the result will be correct assuming both numbers are not 333 // negative. e.g. 334 // 335 // - <=> - = reversed (-30 > -20 = true) 336 // + <=> + = ordered (30 > 20 = true) 337 // - <=> + = ordered (-30 > 20 = false) 338 // + <=> - = ordered (30 > -20 = true) 339 // 340 // For NaN, we normalize the NaN to a peticular representation; the sign bit is 0, all exponential bits 341 // are 1 and only the MSB of the mantissa is 1. So, NaN is recognized as the largest integral numbers. 342 343 void purifyArray() 344 { 345 ElementType* array = typedVector(); 346 for (unsigned i = 0; i < m_length; i++) 347 array[i] = purifyNaN(array[i]); 348 } 349 350 template<typename IntegralType> 351 void sortFloat() 352 { 353 ASSERT(sizeof(IntegralType) == sizeof(ElementType)); 354 355 // Since there might be another view that sets the bits of 356 // our floats to NaNs with negative sign bits we need to 357 // purify the array. 358 // We use a separate function here to avoid the strict aliasing rule. 359 // We could use a union but ASAN seems to frown upon that. 360 purifyArray(); 361 362 IntegralType* array = reinterpret_cast_ptr<IntegralType*>(typedVector()); 363 std::sort(array, array + m_length, [] (IntegralType a, IntegralType b) { 364 if (a >= 0 || b >= 0) 365 return a < b; 366 return a > b; 367 }); 368 369 } 370 371 }; 372 373 template<typename Adaptor> 374 inline RefPtr<typename Adaptor::ViewType> toPossiblySharedNativeTypedView(VM& vm, JSValue value) 375 { 376 typename Adaptor::JSViewType* wrapper = jsDynamicCast<typename Adaptor::JSViewType*>(vm, value); 377 if (!wrapper) 378 return nullptr; 379 return wrapper->possiblySharedTypedImpl(); 380 } 381 382 template<typename Adaptor> 383 inline RefPtr<typename Adaptor::ViewType> toUnsharedNativeTypedView(VM& vm, JSValue value) 384 { 385 RefPtr<typename Adaptor::ViewType> result = toPossiblySharedNativeTypedView<Adaptor>(vm, value); 386 if (!result || result->isShared()) 387 return nullptr; 388 return result; 389 } 390 391 template<typename Adaptor> 392 RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::toWrapped(VM& vm, JSValue value) 393 { 394 return JSC::toUnsharedNativeTypedView<Adaptor>(vm, value); 395 } 396 397 template<typename Adaptor> 398 RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::toWrappedAllowShared(VM& vm, JSValue value) 399 { 400 return JSC::toPossiblySharedNativeTypedView<Adaptor>(vm, value); 401 } 402 403 404 } // namespace JSC