Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef vm_TypedArrayObject_h
8 : #define vm_TypedArrayObject_h
9 :
10 : #include "mozilla/Attributes.h"
11 :
12 : #include "jsobj.h"
13 :
14 : #include "gc/Barrier.h"
15 : #include "gc/Zone.h"
16 : #include "js/Class.h"
17 : #include "vm/ArrayBufferObject.h"
18 : #include "vm/SharedArrayObject.h"
19 :
20 : #define JS_FOR_EACH_TYPED_ARRAY(macro) \
21 : macro(int8_t, Int8) \
22 : macro(uint8_t, Uint8) \
23 : macro(int16_t, Int16) \
24 : macro(uint16_t, Uint16) \
25 : macro(int32_t, Int32) \
26 : macro(uint32_t, Uint32) \
27 : macro(float, Float32) \
28 : macro(double, Float64) \
29 : macro(uint8_clamped, Uint8Clamped)
30 :
31 : typedef struct JSProperty JSProperty;
32 :
33 : namespace js {
34 :
35 : enum class TypedArrayLength { Fixed, Dynamic };
36 :
37 : /*
38 : * TypedArrayObject
39 : *
40 : * The non-templated base class for the specific typed implementations.
41 : * This class holds all the member variables that are used by
42 : * the subclasses.
43 : */
44 :
45 : class TypedArrayObject : public NativeObject
46 : {
47 : public:
48 : // Underlying (Shared)ArrayBufferObject.
49 : static const size_t BUFFER_SLOT = 0;
50 : static_assert(BUFFER_SLOT == JS_TYPEDARRAYLAYOUT_BUFFER_SLOT,
51 : "self-hosted code with burned-in constants must get the "
52 : "right buffer slot");
53 :
54 : // Slot containing length of the view in number of typed elements.
55 : static const size_t LENGTH_SLOT = 1;
56 : static_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT,
57 : "self-hosted code with burned-in constants must get the "
58 : "right length slot");
59 :
60 : // Offset of view within underlying (Shared)ArrayBufferObject.
61 : static const size_t BYTEOFFSET_SLOT = 2;
62 : static_assert(BYTEOFFSET_SLOT == JS_TYPEDARRAYLAYOUT_BYTEOFFSET_SLOT,
63 : "self-hosted code with burned-in constants must get the "
64 : "right byteOffset slot");
65 :
66 : static const size_t RESERVED_SLOTS = 3;
67 :
68 : #ifdef DEBUG
69 : static const uint8_t ZeroLengthArrayData = 0x4A;
70 : #endif
71 :
72 : static int lengthOffset();
73 : static int dataOffset();
74 :
75 : // The raw pointer to the buffer memory, the "private" value.
76 : //
77 : // This offset is exposed for performance reasons - so that it
78 : // need not be looked up on accesses.
79 : static const size_t DATA_SLOT = 3;
80 :
81 : static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
82 : "bad inlined constant in jsfriendapi.h");
83 :
84 0 : static bool sameBuffer(Handle<TypedArrayObject*> a, Handle<TypedArrayObject*> b) {
85 : // Inline buffers.
86 0 : if (!a->hasBuffer() || !b->hasBuffer())
87 0 : return a.get() == b.get();
88 :
89 : // Shared buffers.
90 0 : if (a->isSharedMemory() && b->isSharedMemory()) {
91 0 : return (a->bufferObject()->as<SharedArrayBufferObject>().globalID() ==
92 0 : b->bufferObject()->as<SharedArrayBufferObject>().globalID());
93 : }
94 :
95 0 : return a->bufferObject() == b->bufferObject();
96 : }
97 :
98 : static const Class classes[Scalar::MaxTypedArrayViewType];
99 : static const Class protoClasses[Scalar::MaxTypedArrayViewType];
100 : static const Class sharedTypedArrayPrototypeClass;
101 :
102 0 : static const Class* classForType(Scalar::Type type) {
103 0 : MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
104 0 : return &classes[type];
105 : }
106 :
107 60 : static const Class* protoClassForType(Scalar::Type type) {
108 60 : MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
109 60 : return &protoClasses[type];
110 : }
111 :
112 : static const size_t FIXED_DATA_START = DATA_SLOT + 1;
113 :
114 : // For typed arrays which can store their data inline, the array buffer
115 : // object is created lazily.
116 : static const uint32_t INLINE_BUFFER_LIMIT =
117 : (NativeObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value);
118 :
119 : static gc::AllocKind
120 0 : AllocKindForLazyBuffer(size_t nbytes)
121 : {
122 0 : MOZ_ASSERT(nbytes <= INLINE_BUFFER_LIMIT);
123 0 : if (nbytes == 0)
124 0 : nbytes += sizeof(uint8_t);
125 0 : size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
126 0 : MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
127 0 : return gc::GetGCObjectKind(FIXED_DATA_START + dataSlots);
128 : }
129 :
130 : inline Scalar::Type type() const;
131 : inline size_t bytesPerElement() const;
132 :
133 0 : static Value bufferValue(TypedArrayObject* tarr) {
134 0 : return tarr->getFixedSlot(BUFFER_SLOT);
135 : }
136 0 : static Value byteOffsetValue(TypedArrayObject* tarr) {
137 0 : Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT);
138 0 : MOZ_ASSERT(v.toInt32() >= 0);
139 0 : return v;
140 : }
141 0 : static Value byteLengthValue(TypedArrayObject* tarr) {
142 0 : return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement());
143 : }
144 0 : static Value lengthValue(TypedArrayObject* tarr) {
145 0 : return tarr->getFixedSlot(LENGTH_SLOT);
146 : }
147 :
148 : static bool
149 : ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> tarray);
150 :
151 0 : bool hasBuffer() const {
152 0 : return bufferValue(const_cast<TypedArrayObject*>(this)).isObject();
153 : }
154 0 : JSObject* bufferObject() const {
155 0 : return bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
156 : }
157 0 : uint32_t byteOffset() const {
158 0 : return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
159 : }
160 0 : uint32_t byteLength() const {
161 0 : return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
162 : }
163 0 : uint32_t length() const {
164 0 : return lengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
165 : }
166 :
167 : bool hasInlineElements() const;
168 : void setInlineElements();
169 0 : uint8_t* elementsRaw() const {
170 0 : return *(uint8_t **)((((char *)this) + this->dataOffset()));
171 : }
172 0 : uint8_t* elements() const {
173 0 : assertZeroLengthArrayData();
174 0 : return elementsRaw();
175 : }
176 :
177 : #ifdef DEBUG
178 : void assertZeroLengthArrayData() const;
179 : #else
180 : void assertZeroLengthArrayData() const {};
181 : #endif
182 :
183 : Value getElement(uint32_t index);
184 : static void setElement(TypedArrayObject& obj, uint32_t index, double d);
185 :
186 : void notifyBufferDetached(JSContext* cx, void* newData);
187 :
188 : static bool
189 : GetTemplateObjectForNative(JSContext* cx, Native native, uint32_t len,
190 : MutableHandleObject res);
191 :
192 : /*
193 : * Byte length above which created typed arrays and data views will have
194 : * singleton types regardless of the context in which they are created.
195 : */
196 : static const uint32_t SINGLETON_BYTE_LENGTH = 1024 * 1024 * 10;
197 :
198 : static bool isOriginalLengthGetter(Native native);
199 :
200 0 : ArrayBufferObject* bufferUnshared() const {
201 0 : MOZ_ASSERT(!isSharedMemory());
202 0 : JSObject* obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
203 0 : if (!obj)
204 0 : return nullptr;
205 0 : return &obj->as<ArrayBufferObject>();
206 : }
207 0 : SharedArrayBufferObject* bufferShared() const {
208 0 : MOZ_ASSERT(isSharedMemory());
209 0 : JSObject* obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
210 0 : if (!obj)
211 0 : return nullptr;
212 0 : return &obj->as<SharedArrayBufferObject>();
213 : }
214 0 : ArrayBufferObjectMaybeShared* bufferEither() const {
215 0 : JSObject* obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
216 0 : if (!obj)
217 0 : return nullptr;
218 0 : if (isSharedMemory())
219 0 : return &obj->as<SharedArrayBufferObject>();
220 0 : return &obj->as<ArrayBufferObject>();
221 : }
222 :
223 0 : SharedMem<void*> viewDataShared() const {
224 0 : return SharedMem<void*>::shared(viewDataEither_());
225 : }
226 0 : SharedMem<void*> viewDataEither() const {
227 0 : if (isSharedMemory())
228 0 : return SharedMem<void*>::shared(viewDataEither_());
229 0 : return SharedMem<void*>::unshared(viewDataEither_());
230 : }
231 0 : void initViewData(SharedMem<uint8_t*> viewData) {
232 : // Install a pointer to the buffer location that corresponds
233 : // to offset zero within the typed array.
234 : //
235 : // The following unwrap is safe because the DATA_SLOT is
236 : // accessed only from jitted code and from the
237 : // viewDataEither_() accessor below; in neither case does the
238 : // raw pointer escape untagged into C++ code.
239 0 : initPrivate(viewData.unwrap(/*safe - see above*/));
240 0 : }
241 0 : void* viewDataUnshared() const {
242 0 : MOZ_ASSERT(!isSharedMemory());
243 0 : return viewDataEither_();
244 : }
245 :
246 0 : bool hasDetachedBuffer() const {
247 : // Shared buffers can't be detached.
248 0 : if (isSharedMemory())
249 0 : return false;
250 :
251 : // A typed array with a null buffer has never had its buffer exposed to
252 : // become detached.
253 0 : ArrayBufferObject* buffer = bufferUnshared();
254 0 : if (!buffer)
255 0 : return false;
256 :
257 0 : return buffer->isDetached();
258 : }
259 :
260 : private:
261 0 : void* viewDataEither_() const {
262 : // Note, do not check whether shared or not
263 : // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
264 0 : return static_cast<void*>(getPrivate(DATA_SLOT));
265 : }
266 :
267 : public:
268 : static void trace(JSTracer* trc, JSObject* obj);
269 : static void finalize(FreeOp* fop, JSObject* obj);
270 : static void objectMoved(JSObject* obj, const JSObject* old);
271 : static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* obj, const JSObject* old,
272 : gc::AllocKind allocKind);
273 :
274 : /* Initialization bits */
275 :
276 : template<Value ValueGetter(TypedArrayObject* tarr)>
277 : static bool
278 0 : GetterImpl(JSContext* cx, const CallArgs& args)
279 : {
280 0 : MOZ_ASSERT(is(args.thisv()));
281 0 : args.rval().set(ValueGetter(&args.thisv().toObject().as<TypedArrayObject>()));
282 0 : return true;
283 : }
284 :
285 : // ValueGetter is a function that takes an unwrapped typed array object and
286 : // returns a Value. Given such a function, Getter<> is a native that
287 : // retrieves a given Value, probably from a slot on the object.
288 : template<Value ValueGetter(TypedArrayObject* tarr)>
289 : static bool
290 0 : Getter(JSContext* cx, unsigned argc, Value* vp)
291 : {
292 0 : CallArgs args = CallArgsFromVp(argc, vp);
293 0 : return CallNonGenericMethod<is, GetterImpl<ValueGetter>>(cx, args);
294 : }
295 :
296 : static const JSFunctionSpec protoFunctions[];
297 : static const JSPropertySpec protoAccessors[];
298 : static const JSFunctionSpec staticFunctions[];
299 : static const JSPropertySpec staticProperties[];
300 :
301 : /* Accessors and functions */
302 :
303 : static bool is(HandleValue v);
304 :
305 : static bool set(JSContext* cx, unsigned argc, Value* vp);
306 :
307 : private:
308 : static bool set_impl(JSContext* cx, const CallArgs& args);
309 : };
310 :
311 : MOZ_MUST_USE bool TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp);
312 :
313 : extern TypedArrayObject*
314 : TypedArrayCreateWithTemplate(JSContext* cx, HandleObject templateObj, int32_t len);
315 :
316 : inline bool
317 796148 : IsTypedArrayClass(const Class* clasp)
318 : {
319 796148 : return &TypedArrayObject::classes[0] <= clasp &&
320 796148 : clasp < &TypedArrayObject::classes[Scalar::MaxTypedArrayViewType];
321 : }
322 :
323 : bool
324 : IsTypedArrayConstructor(HandleValue v, uint32_t type);
325 :
326 : inline Scalar::Type
327 0 : TypedArrayObject::type() const
328 : {
329 0 : MOZ_ASSERT(IsTypedArrayClass(getClass()));
330 0 : return static_cast<Scalar::Type>(getClass() - &classes[0]);
331 : }
332 :
333 : inline size_t
334 0 : TypedArrayObject::bytesPerElement() const
335 : {
336 0 : return Scalar::byteSize(type());
337 : }
338 :
339 : // Return value is whether the string is some integer. If the string is an
340 : // integer which is not representable as a uint64_t, the return value is true
341 : // and the resulting index is UINT64_MAX.
342 : template <typename CharT>
343 : bool
344 : StringIsTypedArrayIndex(const CharT* s, size_t length, uint64_t* indexp);
345 :
346 : inline bool
347 0 : IsTypedArrayIndex(jsid id, uint64_t* indexp)
348 : {
349 0 : if (JSID_IS_INT(id)) {
350 0 : int32_t i = JSID_TO_INT(id);
351 0 : MOZ_ASSERT(i >= 0);
352 0 : *indexp = (double)i;
353 0 : return true;
354 : }
355 :
356 0 : if (MOZ_UNLIKELY(!JSID_IS_STRING(id)))
357 0 : return false;
358 :
359 0 : JS::AutoCheckCannotGC nogc;
360 0 : JSAtom* atom = JSID_TO_ATOM(id);
361 0 : size_t length = atom->length();
362 :
363 0 : if (atom->hasLatin1Chars()) {
364 0 : const Latin1Char* s = atom->latin1Chars(nogc);
365 0 : if (!JS7_ISDEC(*s) && *s != '-')
366 0 : return false;
367 0 : return StringIsTypedArrayIndex(s, length, indexp);
368 : }
369 :
370 0 : const char16_t* s = atom->twoByteChars(nogc);
371 0 : if (!JS7_ISDEC(*s) && *s != '-')
372 0 : return false;
373 0 : return StringIsTypedArrayIndex(s, length, indexp);
374 : }
375 :
376 : /*
377 : * Implements [[DefineOwnProperty]] for TypedArrays when the property
378 : * key is a TypedArray index.
379 : */
380 : bool
381 : DefineTypedArrayElement(JSContext* cx, HandleObject arr, uint64_t index,
382 : Handle<PropertyDescriptor> desc, ObjectOpResult& result);
383 :
384 : static inline unsigned
385 0 : TypedArrayShift(Scalar::Type viewType)
386 : {
387 0 : switch (viewType) {
388 : case Scalar::Int8:
389 : case Scalar::Uint8:
390 : case Scalar::Uint8Clamped:
391 0 : return 0;
392 : case Scalar::Int16:
393 : case Scalar::Uint16:
394 0 : return 1;
395 : case Scalar::Int32:
396 : case Scalar::Uint32:
397 : case Scalar::Float32:
398 0 : return 2;
399 : case Scalar::Int64:
400 : case Scalar::Float64:
401 0 : return 3;
402 : case Scalar::Float32x4:
403 : case Scalar::Int8x16:
404 : case Scalar::Int16x8:
405 : case Scalar::Int32x4:
406 0 : return 4;
407 : default:;
408 : }
409 0 : MOZ_CRASH("Unexpected array type");
410 : }
411 :
412 : static inline unsigned
413 0 : TypedArrayElemSize(Scalar::Type viewType)
414 : {
415 0 : return 1u << TypedArrayShift(viewType);
416 : }
417 :
418 : // Assign
419 : //
420 : // target[targetOffset] = unsafeSrcCrossCompartment[0]
421 : // ...
422 : // target[targetOffset + unsafeSrcCrossCompartment.length - 1] =
423 : // unsafeSrcCrossCompartment[unsafeSrcCrossCompartment.length - 1]
424 : //
425 : // where the source element range doesn't overlap the target element range in
426 : // memory.
427 : extern void
428 : SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset,
429 : TypedArrayObject* unsafeSrcCrossCompartment);
430 :
431 : } // namespace js
432 :
433 : template <>
434 : inline bool
435 540104 : JSObject::is<js::TypedArrayObject>() const
436 : {
437 540104 : return js::IsTypedArrayClass(getClass());
438 : }
439 :
440 : #endif /* vm_TypedArrayObject_h */
|