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 "JSArrayBufferView.h"
29#include "ThrowScope.h"
30#include "ToNativeFromValue.h"
31
32namespace JSC {
33
34JS_EXPORT_PRIVATE const ClassInfo* getInt8ArrayClassInfo();
35JS_EXPORT_PRIVATE const ClassInfo* getInt16ArrayClassInfo();
36JS_EXPORT_PRIVATE const ClassInfo* getInt32ArrayClassInfo();
37JS_EXPORT_PRIVATE const ClassInfo* getUint8ArrayClassInfo();
38JS_EXPORT_PRIVATE const ClassInfo* getUint8ClampedArrayClassInfo();
39JS_EXPORT_PRIVATE const ClassInfo* getUint16ArrayClassInfo();
40JS_EXPORT_PRIVATE const ClassInfo* getUint32ArrayClassInfo();
41JS_EXPORT_PRIVATE const ClassInfo* getFloat32ArrayClassInfo();
42JS_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
86enum class CopyType {
87 LeftToRight,
88 Unobservable,
89};
90
91static const ASCIILiteral typedArrayBufferHasBeenDetachedErrorMessage { "Underlying ArrayBuffer has been detached from the view"_s };
92
93template<typename Adaptor>
94class JSGenericTypedArrayView final : public JSArrayBufferView {
95public:
96 typedef JSArrayBufferView Base;
97 typedef typename Adaptor::Type ElementType;
98
99 static const unsigned StructureFlags = Base::StructureFlags | OverridesGetPropertyNames | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero;
100
101 static const unsigned elementSize = sizeof(typename Adaptor::Type);
102
103protected:
104 JSGenericTypedArrayView(VM&, ConstructionContext&);
105
106public:
107 static JSGenericTypedArrayView* create(ExecState*, Structure*, unsigned length);
108 static JSGenericTypedArrayView* createWithFastVector(ExecState*, Structure*, unsigned length, void* vector);
109 static JSGenericTypedArrayView* createUninitialized(ExecState*, Structure*, unsigned length);
110 static JSGenericTypedArrayView* create(ExecState*, Structure*, RefPtr<ArrayBuffer>&&, unsigned byteOffset, unsigned length);
111 static JSGenericTypedArrayView* create(VM&, Structure*, RefPtr<typename Adaptor::ViewType>&& impl);
112 static JSGenericTypedArrayView* create(Structure*, JSGlobalObject*, RefPtr<typename Adaptor::ViewType>&& impl);
113
114 unsigned byteLength() const { return m_length * sizeof(typename Adaptor::Type); }
115 size_t byteSize() const { return sizeOf(m_length, sizeof(typename Adaptor::Type)); }
116
117 const typename Adaptor::Type* typedVector() const
118 {
119 return bitwise_cast<const typename Adaptor::Type*>(vector());
120 }
121 typename Adaptor::Type* typedVector()
122 {
123 return bitwise_cast<typename Adaptor::Type*>(vector());
124 }
125
126 // These methods are meant to match indexed access methods that JSObject
127 // supports - hence the slight redundancy.
128 bool canGetIndexQuickly(unsigned i)
129 {
130 return i < m_length;
131 }
132 bool canSetIndexQuickly(unsigned i)
133 {
134 return i < m_length;
135 }
136
137 typename Adaptor::Type getIndexQuicklyAsNativeValue(unsigned i)
138 {
139 ASSERT(i < m_length);
140 return typedVector()[i];
141 }
142
143 double getIndexQuicklyAsDouble(unsigned i)
144 {
145 return Adaptor::toDouble(getIndexQuicklyAsNativeValue(i));
146 }
147
148 JSValue getIndexQuickly(unsigned i)
149 {
150 return Adaptor::toJSValue(getIndexQuicklyAsNativeValue(i));
151 }
152
153 void setIndexQuicklyToNativeValue(unsigned i, typename Adaptor::Type value)
154 {
155 ASSERT(i < m_length);
156 typedVector()[i] = value;
157 }
158
159 void setIndexQuicklyToDouble(unsigned i, double value)
160 {
161 setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(jsNumber(value)));
162 }
163
164 void setIndexQuickly(unsigned i, JSValue value)
165 {
166 ASSERT(!value.isObject());
167 setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value));
168 }
169
170 bool setIndex(ExecState* exec, unsigned i, JSValue jsValue)
171 {
172 VM& vm = exec->vm();
173 auto scope = DECLARE_THROW_SCOPE(vm);
174
175 typename Adaptor::Type value = toNativeFromValue<Adaptor>(exec, jsValue);
176 RETURN_IF_EXCEPTION(scope, false);
177
178 if (isNeutered()) {
179 throwTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
180 return false;
181 }
182
183 if (i >= m_length)
184 return false;
185
186 setIndexQuicklyToNativeValue(i, value);
187 return true;
188 }
189
190 static ElementType toAdaptorNativeFromValue(ExecState* exec, JSValue jsValue) { return toNativeFromValue<Adaptor>(exec, jsValue); }
191
192 static Optional<ElementType> toAdaptorNativeFromValueWithoutCoercion(JSValue jsValue) { return toNativeFromValueWithoutCoercion<Adaptor>(jsValue); }
193
194 void sort()
195 {
196 RELEASE_ASSERT(!isNeutered());
197 switch (Adaptor::typeValue) {
198 case TypeFloat32:
199 sortFloat<int32_t>();
200 break;
201 case TypeFloat64:
202 sortFloat<int64_t>();
203 break;
204 default: {
205 ElementType* array = typedVector();
206 std::sort(array, array + m_length);
207 break;
208 }
209 }
210 }
211
212 bool canAccessRangeQuickly(unsigned offset, unsigned length)
213 {
214 return offset <= m_length
215 && offset + length <= m_length
216 // check overflow
217 && offset + length >= offset;
218 }
219
220 // Like canSetQuickly, except: if it returns false, it will throw the
221 // appropriate exception.
222 bool validateRange(ExecState*, unsigned offset, unsigned length);
223
224 // Returns true if successful, and false on error; if it returns false
225 // then it will have thrown an exception.
226 bool set(ExecState*, unsigned offset, JSObject*, unsigned objectOffset, unsigned length, CopyType type = CopyType::Unobservable);
227
228 RefPtr<typename Adaptor::ViewType> possiblySharedTypedImpl();
229 RefPtr<typename Adaptor::ViewType> unsharedTypedImpl();
230
231 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
232 {
233 return Structure::create(vm, globalObject, prototype, TypeInfo(typeForTypedArrayType(Adaptor::typeValue), StructureFlags), info(), NonArray);
234 }
235
236 static const ClassInfo s_info; // This is never accessed directly, since that would break linkage on some compilers.
237
238 static const ClassInfo* info()
239 {
240 switch (Adaptor::typeValue) {
241 case TypeInt8:
242 return getInt8ArrayClassInfo();
243 case TypeInt16:
244 return getInt16ArrayClassInfo();
245 case TypeInt32:
246 return getInt32ArrayClassInfo();
247 case TypeUint8:
248 return getUint8ArrayClassInfo();
249 case TypeUint8Clamped:
250 return getUint8ClampedArrayClassInfo();
251 case TypeUint16:
252 return getUint16ArrayClassInfo();
253 case TypeUint32:
254 return getUint32ArrayClassInfo();
255 case TypeFloat32:
256 return getFloat32ArrayClassInfo();
257 case TypeFloat64:
258 return getFloat64ArrayClassInfo();
259 default:
260 RELEASE_ASSERT_NOT_REACHED();
261 return 0;
262 }
263 }
264
265 ArrayBuffer* existingBuffer();
266
267 static const TypedArrayType TypedArrayStorageType = Adaptor::typeValue;
268
269 // This is the default DOM unwrapping. It calls toUnsharedNativeTypedView().
270 static RefPtr<typename Adaptor::ViewType> toWrapped(VM&, JSValue);
271
272protected:
273 friend struct TypedArrayClassInfos;
274
275 static EncodedJSValue throwNeuteredTypedArrayTypeError(ExecState*, EncodedJSValue, PropertyName);
276
277 static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
278 static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
279 static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
280 static bool deleteProperty(JSCell*, ExecState*, PropertyName);
281
282 static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
283 static bool putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
284 static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
285
286 static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
287
288 static size_t estimatedSize(JSCell*, VM&);
289 static void visitChildren(JSCell*, SlotVisitor&);
290
291private:
292 // Returns true if successful, and false on error; it will throw on error.
293 template<typename OtherAdaptor>
294 bool setWithSpecificType(
295 ExecState*, unsigned offset, JSGenericTypedArrayView<OtherAdaptor>*,
296 unsigned objectOffset, unsigned length, CopyType);
297
298 // The ECMA 6 spec states that floating point Typed Arrays should have the following ordering:
299 //
300 // -Inifinity < negative finite numbers < -0.0 < 0.0 < positive finite numbers < Infinity < NaN
301 // Note: regardless of the sign or exact representation of a NaN it is greater than all other values.
302 //
303 // An interesting fact about IEEE 754 floating point numbers is that have an adjacent representation
304 // i.e. for any finite floating point x there does not exist a finite floating point y such that
305 // ((float) ((int) x + 1)) > y > x (where int represents a signed bit integer with the same number
306 // of bits as float). Thus, if we have an array of floating points if we view it as an
307 // array of signed bit integers it will sort in the format we desire. Note, denormal
308 // numbers fit this property as they are floating point numbers with a exponent field of all
309 // zeros so they will be closer to the signed zeros than any normalized number.
310 //
311 // All the processors we support, however, use twos complement. Fortunately, if you compare a signed
312 // bit number as if it were twos complement the result will be correct assuming both numbers are not
313 // negative. e.g.
314 //
315 // - <=> - = reversed (-30 > -20 = true)
316 // + <=> + = ordered (30 > 20 = true)
317 // - <=> + = ordered (-30 > 20 = false)
318 // + <=> - = ordered (30 > -20 = true)
319 //
320 // For NaN, we normalize the NaN to a peticular representation; the sign bit is 0, all exponential bits
321 // are 1 and only the MSB of the mantissa is 1. So, NaN is recognized as the largest integral numbers.
322
323 void purifyArray()
324 {
325 ElementType* array = typedVector();
326 for (unsigned i = 0; i < m_length; i++)
327 array[i] = purifyNaN(array[i]);
328 }
329
330 template<typename IntegralType>
331 void sortFloat()
332 {
333 ASSERT(sizeof(IntegralType) == sizeof(ElementType));
334
335 // Since there might be another view that sets the bits of
336 // our floats to NaNs with negative sign bits we need to
337 // purify the array.
338 // We use a separate function here to avoid the strict aliasing rule.
339 // We could use a union but ASAN seems to frown upon that.
340 purifyArray();
341
342 IntegralType* array = reinterpret_cast_ptr<IntegralType*>(typedVector());
343 std::sort(array, array + m_length, [] (IntegralType a, IntegralType b) {
344 if (a >= 0 || b >= 0)
345 return a < b;
346 return a > b;
347 });
348
349 }
350
351};
352
353template<typename Adaptor>
354inline RefPtr<typename Adaptor::ViewType> toPossiblySharedNativeTypedView(VM& vm, JSValue value)
355{
356 typename Adaptor::JSViewType* wrapper = jsDynamicCast<typename Adaptor::JSViewType*>(vm, value);
357 if (!wrapper)
358 return nullptr;
359 return wrapper->possiblySharedTypedImpl();
360}
361
362template<typename Adaptor>
363inline RefPtr<typename Adaptor::ViewType> toUnsharedNativeTypedView(VM& vm, JSValue value)
364{
365 RefPtr<typename Adaptor::ViewType> result = toPossiblySharedNativeTypedView<Adaptor>(vm, value);
366 if (!result || result->isShared())
367 return nullptr;
368 return result;
369}
370
371template<typename Adaptor>
372RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::toWrapped(VM& vm, JSValue value)
373{
374 return JSC::toUnsharedNativeTypedView<Adaptor>(vm, value);
375}
376
377} // namespace JSC
378