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 | |
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 | static const ASCIILiteral typedArrayBufferHasBeenDetachedErrorMessage { "Underlying ArrayBuffer has been detached from the view"_s }; |
92 | |
93 | template<typename Adaptor> |
94 | class JSGenericTypedArrayView final : public JSArrayBufferView { |
95 | public: |
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 | |
103 | protected: |
104 | JSGenericTypedArrayView(VM&, ConstructionContext&); |
105 | |
106 | public: |
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 | |
272 | protected: |
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 | |
291 | private: |
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 | |
353 | template<typename Adaptor> |
354 | inline 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 | |
362 | template<typename Adaptor> |
363 | inline 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 | |
371 | template<typename Adaptor> |
372 | RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::toWrapped(VM& vm, JSValue value) |
373 | { |
374 | return JSC::toUnsharedNativeTypedView<Adaptor>(vm, value); |
375 | } |
376 | |
377 | } // namespace JSC |
378 | |