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 : #include "builtin/DataViewObject.h"
8 :
9 : #include "mozilla/Alignment.h"
10 : #include "mozilla/Casting.h"
11 :
12 : #include <string.h>
13 :
14 : #include "jsapi.h"
15 : #include "jsarray.h"
16 : #include "jscntxt.h"
17 : #include "jsnum.h"
18 : #include "jsobj.h"
19 : #ifdef XP_WIN
20 : # include "jswin.h"
21 : #endif
22 : #include "jswrapper.h"
23 :
24 : #include "jit/AtomicOperations.h"
25 : #include "js/Conversions.h"
26 : #include "vm/ArrayBufferObject.h"
27 : #include "vm/GlobalObject.h"
28 : #include "vm/Interpreter.h"
29 : #include "vm/SharedMem.h"
30 : #include "vm/WrapperObject.h"
31 :
32 : #include "gc/Nursery-inl.h"
33 : #include "gc/StoreBuffer-inl.h"
34 : #include "vm/ArrayBufferObject-inl.h"
35 : #include "vm/NativeObject-inl.h"
36 :
37 : using namespace js;
38 : using namespace js::gc;
39 :
40 : using mozilla::AssertedCast;
41 : using JS::CanonicalizeNaN;
42 : using JS::ToInt32;
43 : using JS::ToUint32;
44 :
45 : static NewObjectKind
46 0 : DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto)
47 : {
48 0 : if (!proto && byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH)
49 0 : return SingletonObject;
50 : jsbytecode* pc;
51 0 : JSScript* script = cx->currentScript(&pc);
52 0 : if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &DataViewObject::class_))
53 0 : return SingletonObject;
54 0 : return GenericObject;
55 : }
56 :
57 : DataViewObject*
58 0 : DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
59 : Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, JSObject* protoArg)
60 : {
61 0 : if (arrayBuffer->isDetached()) {
62 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
63 0 : return nullptr;
64 : }
65 :
66 0 : MOZ_ASSERT(byteOffset <= INT32_MAX);
67 0 : MOZ_ASSERT(byteLength <= INT32_MAX);
68 0 : MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
69 :
70 0 : RootedObject proto(cx, protoArg);
71 0 : RootedObject obj(cx);
72 :
73 0 : NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
74 0 : obj = NewObjectWithClassProto(cx, &class_, proto, newKind);
75 0 : if (!obj)
76 0 : return nullptr;
77 :
78 0 : if (!proto) {
79 0 : if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
80 0 : MOZ_ASSERT(obj->isSingleton());
81 : } else {
82 : jsbytecode* pc;
83 0 : RootedScript script(cx, cx->currentScript(&pc));
84 0 : if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj,
85 0 : newKind == SingletonObject))
86 : {
87 0 : return nullptr;
88 : }
89 : }
90 : }
91 :
92 : // Caller should have established these preconditions, and no
93 : // (non-self-hosted) JS code has had an opportunity to run so nothing can
94 : // have invalidated them.
95 0 : MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
96 0 : MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
97 :
98 0 : DataViewObject& dvobj = obj->as<DataViewObject>();
99 :
100 : // The isSharedMemory property is invariant. Self-hosting code that sets
101 : // BUFFER_SLOT or the private slot (if it does) must maintain it by always
102 : // setting those to reference shared memory.
103 0 : bool isSharedMemory = IsSharedArrayBuffer(arrayBuffer.get());
104 0 : if (isSharedMemory)
105 0 : dvobj.setIsSharedMemory();
106 :
107 0 : dvobj.setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
108 0 : dvobj.setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(byteLength));
109 0 : dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
110 :
111 0 : SharedMem<uint8_t*> ptr = arrayBuffer->dataPointerEither();
112 : // A pointer to raw shared memory is exposed through the private slot. This
113 : // is safe so long as getPrivate() is not used willy-nilly. It is wrapped in
114 : // other accessors in TypedArrayObject.h.
115 0 : dvobj.initPrivate(ptr.unwrap(/*safe - see above*/) + byteOffset);
116 :
117 : // Include a barrier if the data view's data pointer is in the nursery, as
118 : // is done for typed arrays.
119 0 : if (!IsInsideNursery(obj) && cx->nursery().isInside(ptr)) {
120 : // Shared buffer data should never be nursery-allocated, so we
121 : // need to fail here if isSharedMemory. However, mmap() can
122 : // place a SharedArrayRawBuffer up against the bottom end of a
123 : // nursery chunk, and a zero-length buffer will erroneously be
124 : // perceived as being inside the nursery; sidestep that.
125 0 : if (isSharedMemory) {
126 0 : MOZ_ASSERT(arrayBuffer->byteLength() == 0 &&
127 : (uintptr_t(ptr.unwrapValue()) & gc::ChunkMask) == 0);
128 : } else {
129 0 : cx->zone()->group()->storeBuffer().putWholeCell(obj);
130 : }
131 : }
132 :
133 : // Verify that the private slot is at the expected place
134 0 : MOZ_ASSERT(dvobj.numFixedSlots() == TypedArrayObject::DATA_SLOT);
135 :
136 0 : if (arrayBuffer->is<ArrayBufferObject>()) {
137 0 : if (!arrayBuffer->as<ArrayBufferObject>().addView(cx, &dvobj))
138 0 : return nullptr;
139 : }
140 :
141 0 : return &dvobj;
142 : }
143 :
144 : // ES2017 draft rev 931261ecef9b047b14daacf82884134da48dfe0f
145 : // 24.3.2.1 DataView (extracted part of the main algorithm)
146 : bool
147 0 : DataViewObject::getAndCheckConstructorArgs(JSContext* cx, HandleObject bufobj, const CallArgs& args,
148 : uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr)
149 : {
150 : // Step 3.
151 0 : if (!IsArrayBufferMaybeShared(bufobj)) {
152 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
153 0 : "DataView", "ArrayBuffer", bufobj->getClass()->name);
154 0 : return false;
155 : }
156 0 : Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &AsArrayBufferMaybeShared(bufobj));
157 :
158 : // Step 4.
159 : uint64_t offset;
160 0 : if (!ToIndex(cx, args.get(1), &offset))
161 0 : return false;
162 :
163 : // Step 5.
164 0 : if (buffer->isDetached()) {
165 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
166 0 : return false;
167 : }
168 :
169 : // Step 6.
170 0 : uint32_t bufferByteLength = buffer->byteLength();
171 :
172 : // Step 7.
173 0 : if (offset > bufferByteLength) {
174 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
175 0 : return false;
176 : }
177 0 : MOZ_ASSERT(offset <= INT32_MAX);
178 :
179 : // Step 8.a
180 0 : uint64_t viewByteLength = bufferByteLength - offset;
181 0 : if (args.hasDefined(2)) {
182 : // Step 9.a.
183 0 : if (!ToIndex(cx, args.get(2), &viewByteLength))
184 0 : return false;
185 :
186 :
187 0 : MOZ_ASSERT(offset + viewByteLength >= offset,
188 : "can't overflow: both numbers are less than DOUBLE_INTEGRAL_PRECISION_LIMIT");
189 :
190 : // Step 9.b.
191 0 : if (offset + viewByteLength > bufferByteLength) {
192 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
193 0 : JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
194 0 : return false;
195 : }
196 : }
197 0 : MOZ_ASSERT(viewByteLength <= INT32_MAX);
198 :
199 0 : *byteOffsetPtr = AssertedCast<uint32_t>(offset);
200 0 : *byteLengthPtr = AssertedCast<uint32_t>(viewByteLength);
201 0 : return true;
202 : }
203 :
204 : bool
205 0 : DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args)
206 : {
207 0 : MOZ_ASSERT(args.isConstructing());
208 0 : assertSameCompartment(cx, bufobj);
209 :
210 : uint32_t byteOffset, byteLength;
211 0 : if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength))
212 0 : return false;
213 :
214 0 : RootedObject proto(cx);
215 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
216 0 : return false;
217 :
218 0 : Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &AsArrayBufferMaybeShared(bufobj));
219 0 : JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto);
220 0 : if (!obj)
221 0 : return false;
222 0 : args.rval().setObject(*obj);
223 0 : return true;
224 : }
225 :
226 : // Create a DataView object in another compartment.
227 : //
228 : // ES6 supports creating a DataView in global A (using global A's DataView
229 : // constructor) backed by an ArrayBuffer created in global B.
230 : //
231 : // Our DataViewObject implementation doesn't support a DataView in
232 : // compartment A backed by an ArrayBuffer in compartment B. So in this case,
233 : // we create the DataView in B (!) and return a cross-compartment wrapper.
234 : //
235 : // Extra twist: the spec says the new DataView's [[Prototype]] must be
236 : // A's DataView.prototype. So even though we're creating the DataView in B,
237 : // its [[Prototype]] must be (a cross-compartment wrapper for) the
238 : // DataView.prototype in A.
239 : bool
240 0 : DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args)
241 : {
242 0 : MOZ_ASSERT(args.isConstructing());
243 0 : MOZ_ASSERT(bufobj->is<WrapperObject>());
244 :
245 0 : RootedObject unwrapped(cx, CheckedUnwrap(bufobj));
246 0 : if (!unwrapped) {
247 0 : ReportAccessDenied(cx);
248 0 : return false;
249 : }
250 :
251 : // NB: This entails the IsArrayBuffer check
252 : uint32_t byteOffset, byteLength;
253 0 : if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength))
254 0 : return false;
255 :
256 : // Make sure to get the [[Prototype]] for the created view from this
257 : // compartment.
258 0 : RootedObject proto(cx);
259 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
260 0 : return false;
261 :
262 0 : Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
263 0 : if (!proto) {
264 0 : proto = GlobalObject::getOrCreateDataViewPrototype(cx, global);
265 0 : if (!proto)
266 0 : return false;
267 : }
268 :
269 0 : RootedObject dv(cx);
270 : {
271 0 : JSAutoCompartment ac(cx, unwrapped);
272 :
273 0 : Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
274 0 : buffer = &unwrapped->as<ArrayBufferObjectMaybeShared>();
275 :
276 0 : RootedObject wrappedProto(cx, proto);
277 0 : if (!cx->compartment()->wrap(cx, &wrappedProto))
278 0 : return false;
279 :
280 0 : dv = DataViewObject::create(cx, byteOffset, byteLength, buffer, wrappedProto);
281 0 : if (!dv)
282 0 : return false;
283 : }
284 :
285 0 : if (!cx->compartment()->wrap(cx, &dv))
286 0 : return false;
287 :
288 0 : args.rval().setObject(*dv);
289 0 : return true;
290 : }
291 :
292 : bool
293 0 : DataViewObject::construct(JSContext* cx, unsigned argc, Value* vp)
294 : {
295 0 : CallArgs args = CallArgsFromVp(argc, vp);
296 :
297 0 : if (!ThrowIfNotConstructing(cx, args, "DataView"))
298 0 : return false;
299 :
300 0 : RootedObject bufobj(cx);
301 0 : if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj))
302 0 : return false;
303 :
304 0 : if (bufobj->is<WrapperObject>())
305 0 : return constructWrapped(cx, bufobj, args);
306 0 : return constructSameCompartment(cx, bufobj, args);
307 : }
308 :
309 : template <typename NativeType>
310 : /* static */ SharedMem<uint8_t*>
311 0 : DataViewObject::getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, uint64_t offset,
312 : bool* isSharedMemory)
313 : {
314 0 : const size_t TypeSize = sizeof(NativeType);
315 0 : if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) {
316 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
317 : "1");
318 0 : return SharedMem<uint8_t*>::unshared(nullptr);
319 : }
320 :
321 0 : MOZ_ASSERT(offset < UINT32_MAX);
322 0 : *isSharedMemory = obj->isSharedMemory();
323 0 : return obj->dataPointerEither().cast<uint8_t*>() + uint32_t(offset);
324 : }
325 :
326 : static inline bool
327 0 : needToSwapBytes(bool littleEndian)
328 : {
329 : #if MOZ_LITTLE_ENDIAN
330 0 : return !littleEndian;
331 : #else
332 : return littleEndian;
333 : #endif
334 : }
335 :
336 : static inline uint8_t
337 0 : swapBytes(uint8_t x)
338 : {
339 0 : return x;
340 : }
341 :
342 : static inline uint16_t
343 0 : swapBytes(uint16_t x)
344 : {
345 0 : return ((x & 0xff) << 8) | (x >> 8);
346 : }
347 :
348 : static inline uint32_t
349 0 : swapBytes(uint32_t x)
350 : {
351 0 : return ((x & 0xff) << 24) |
352 0 : ((x & 0xff00) << 8) |
353 0 : ((x & 0xff0000) >> 8) |
354 0 : ((x & 0xff000000) >> 24);
355 : }
356 :
357 : static inline uint64_t
358 0 : swapBytes(uint64_t x)
359 : {
360 0 : uint32_t a = x & UINT32_MAX;
361 0 : uint32_t b = x >> 32;
362 0 : return (uint64_t(swapBytes(a)) << 32) | swapBytes(b);
363 : }
364 :
365 : template <typename DataType> struct DataToRepType { typedef DataType result; };
366 : template <> struct DataToRepType<int8_t> { typedef uint8_t result; };
367 : template <> struct DataToRepType<uint8_t> { typedef uint8_t result; };
368 : template <> struct DataToRepType<int16_t> { typedef uint16_t result; };
369 : template <> struct DataToRepType<uint16_t> { typedef uint16_t result; };
370 : template <> struct DataToRepType<int32_t> { typedef uint32_t result; };
371 : template <> struct DataToRepType<uint32_t> { typedef uint32_t result; };
372 : template <> struct DataToRepType<float> { typedef uint32_t result; };
373 : template <> struct DataToRepType<double> { typedef uint64_t result; };
374 :
375 : static inline void
376 0 : Memcpy(uint8_t* dest, uint8_t* src, size_t nbytes)
377 : {
378 0 : memcpy(dest, src, nbytes);
379 0 : }
380 :
381 : static inline void
382 0 : Memcpy(uint8_t* dest, SharedMem<uint8_t*> src, size_t nbytes)
383 : {
384 0 : jit::AtomicOperations::memcpySafeWhenRacy(dest, src, nbytes);
385 0 : }
386 :
387 : static inline void
388 0 : Memcpy(SharedMem<uint8_t*> dest, uint8_t* src, size_t nbytes)
389 : {
390 0 : jit::AtomicOperations::memcpySafeWhenRacy(dest, src, nbytes);
391 0 : }
392 :
393 : template <typename DataType, typename BufferPtrType>
394 : struct DataViewIO
395 : {
396 : typedef typename DataToRepType<DataType>::result ReadWriteType;
397 :
398 0 : static void fromBuffer(DataType* dest, BufferPtrType unalignedBuffer, bool wantSwap)
399 : {
400 0 : MOZ_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
401 0 : Memcpy((uint8_t*) dest, unalignedBuffer, sizeof(ReadWriteType));
402 0 : if (wantSwap) {
403 0 : ReadWriteType* rwDest = reinterpret_cast<ReadWriteType*>(dest);
404 0 : *rwDest = swapBytes(*rwDest);
405 : }
406 0 : }
407 :
408 0 : static void toBuffer(BufferPtrType unalignedBuffer, const DataType* src, bool wantSwap)
409 : {
410 0 : MOZ_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
411 0 : ReadWriteType temp = *reinterpret_cast<const ReadWriteType*>(src);
412 0 : if (wantSwap)
413 0 : temp = swapBytes(temp);
414 0 : Memcpy(unalignedBuffer, (uint8_t*) &temp, sizeof(ReadWriteType));
415 0 : }
416 : };
417 :
418 : template<typename NativeType>
419 : /* static */ bool
420 0 : DataViewObject::read(JSContext* cx, Handle<DataViewObject*> obj, const CallArgs& args,
421 : NativeType* val)
422 : {
423 : // Steps 1-2. done by the caller
424 : // Step 3. unnecessary assert
425 :
426 : // Step 4.
427 : uint64_t getIndex;
428 0 : if (!ToIndex(cx, args.get(0), &getIndex))
429 0 : return false;
430 :
431 : // Step 5.
432 0 : bool isLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
433 :
434 : // Steps 6-7.
435 0 : if (obj->arrayBufferEither().isDetached()) {
436 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
437 0 : return false;
438 : }
439 :
440 : // Steps 8-12.
441 : bool isSharedMemory;
442 0 : SharedMem<uint8_t*> data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex,
443 0 : &isSharedMemory);
444 0 : if (!data)
445 0 : return false;
446 :
447 : // Step 13.
448 0 : if (isSharedMemory) {
449 0 : DataViewIO<NativeType, SharedMem<uint8_t*>>::fromBuffer(val, data,
450 0 : needToSwapBytes(isLittleEndian));
451 : } else {
452 0 : DataViewIO<NativeType, uint8_t*>::fromBuffer(val, data.unwrapUnshared(),
453 0 : needToSwapBytes(isLittleEndian));
454 : }
455 0 : return true;
456 : }
457 :
458 : template <typename NativeType>
459 : static inline bool
460 0 : WebIDLCast(JSContext* cx, HandleValue value, NativeType* out)
461 : {
462 : int32_t temp;
463 0 : if (!ToInt32(cx, value, &temp))
464 0 : return false;
465 : // Technically, the behavior of assigning an out of range value to a signed
466 : // variable is undefined. In practice, compilers seem to do what we want
467 : // without issuing any warnings.
468 0 : *out = static_cast<NativeType>(temp);
469 0 : return true;
470 : }
471 :
472 : template <>
473 : inline bool
474 0 : WebIDLCast<float>(JSContext* cx, HandleValue value, float* out)
475 : {
476 : double temp;
477 0 : if (!ToNumber(cx, value, &temp))
478 0 : return false;
479 0 : *out = static_cast<float>(temp);
480 0 : return true;
481 : }
482 :
483 : template <>
484 : inline bool
485 0 : WebIDLCast<double>(JSContext* cx, HandleValue value, double* out)
486 : {
487 0 : return ToNumber(cx, value, out);
488 : }
489 :
490 : template<typename NativeType>
491 : /* static */ bool
492 0 : DataViewObject::write(JSContext* cx, Handle<DataViewObject*> obj, const CallArgs& args)
493 : {
494 : // Steps 1-2. done by the caller
495 : // Step 3. unnecessary assert
496 :
497 : // Step 4.
498 : uint64_t getIndex;
499 0 : if (!ToIndex(cx, args.get(0), &getIndex))
500 0 : return false;
501 :
502 : // Step 5. Should just call ToNumber (unobservable)
503 : NativeType value;
504 0 : if (!WebIDLCast(cx, args.get(1), &value))
505 0 : return false;
506 :
507 : #ifdef JS_MORE_DETERMINISTIC
508 : // See the comment in ElementSpecific::doubleToNative.
509 : if (TypeIsFloatingPoint<NativeType>())
510 : value = JS::CanonicalizeNaN(value);
511 : #endif
512 :
513 : // Step 6.
514 0 : bool isLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
515 :
516 : // Steps 7-8.
517 0 : if (obj->arrayBufferEither().isDetached()) {
518 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
519 0 : return false;
520 : }
521 :
522 : // Steps 9-13.
523 : bool isSharedMemory;
524 0 : SharedMem<uint8_t*> data = DataViewObject::getDataPointer<NativeType>(cx, obj, getIndex,
525 0 : &isSharedMemory);
526 0 : if (!data)
527 0 : return false;
528 :
529 : // Step 14.
530 0 : if (isSharedMemory) {
531 0 : DataViewIO<NativeType, SharedMem<uint8_t*>>::toBuffer(data, &value,
532 0 : needToSwapBytes(isLittleEndian));
533 : } else {
534 0 : DataViewIO<NativeType, uint8_t*>::toBuffer(data.unwrapUnshared(), &value,
535 0 : needToSwapBytes(isLittleEndian));
536 : }
537 0 : return true;
538 : }
539 :
540 : bool
541 0 : DataViewObject::getInt8Impl(JSContext* cx, const CallArgs& args)
542 : {
543 0 : MOZ_ASSERT(is(args.thisv()));
544 :
545 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
546 :
547 : int8_t val;
548 0 : if (!read(cx, thisView, args, &val))
549 0 : return false;
550 0 : args.rval().setInt32(val);
551 0 : return true;
552 : }
553 :
554 : bool
555 0 : DataViewObject::fun_getInt8(JSContext* cx, unsigned argc, Value* vp)
556 : {
557 0 : CallArgs args = CallArgsFromVp(argc, vp);
558 0 : return CallNonGenericMethod<is, getInt8Impl>(cx, args);
559 : }
560 :
561 : bool
562 0 : DataViewObject::getUint8Impl(JSContext* cx, const CallArgs& args)
563 : {
564 0 : MOZ_ASSERT(is(args.thisv()));
565 :
566 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
567 :
568 : uint8_t val;
569 0 : if (!read(cx, thisView, args, &val))
570 0 : return false;
571 0 : args.rval().setInt32(val);
572 0 : return true;
573 : }
574 :
575 : bool
576 0 : DataViewObject::fun_getUint8(JSContext* cx, unsigned argc, Value* vp)
577 : {
578 0 : CallArgs args = CallArgsFromVp(argc, vp);
579 0 : return CallNonGenericMethod<is, getUint8Impl>(cx, args);
580 : }
581 :
582 : bool
583 0 : DataViewObject::getInt16Impl(JSContext* cx, const CallArgs& args)
584 : {
585 0 : MOZ_ASSERT(is(args.thisv()));
586 :
587 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
588 :
589 : int16_t val;
590 0 : if (!read(cx, thisView, args, &val))
591 0 : return false;
592 0 : args.rval().setInt32(val);
593 0 : return true;
594 : }
595 :
596 : bool
597 0 : DataViewObject::fun_getInt16(JSContext* cx, unsigned argc, Value* vp)
598 : {
599 0 : CallArgs args = CallArgsFromVp(argc, vp);
600 0 : return CallNonGenericMethod<is, getInt16Impl>(cx, args);
601 : }
602 :
603 : bool
604 0 : DataViewObject::getUint16Impl(JSContext* cx, const CallArgs& args)
605 : {
606 0 : MOZ_ASSERT(is(args.thisv()));
607 :
608 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
609 :
610 : uint16_t val;
611 0 : if (!read(cx, thisView, args, &val))
612 0 : return false;
613 0 : args.rval().setInt32(val);
614 0 : return true;
615 : }
616 :
617 : bool
618 0 : DataViewObject::fun_getUint16(JSContext* cx, unsigned argc, Value* vp)
619 : {
620 0 : CallArgs args = CallArgsFromVp(argc, vp);
621 0 : return CallNonGenericMethod<is, getUint16Impl>(cx, args);
622 : }
623 :
624 : bool
625 0 : DataViewObject::getInt32Impl(JSContext* cx, const CallArgs& args)
626 : {
627 0 : MOZ_ASSERT(is(args.thisv()));
628 :
629 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
630 :
631 : int32_t val;
632 0 : if (!read(cx, thisView, args, &val))
633 0 : return false;
634 0 : args.rval().setInt32(val);
635 0 : return true;
636 : }
637 :
638 : bool
639 0 : DataViewObject::fun_getInt32(JSContext* cx, unsigned argc, Value* vp)
640 : {
641 0 : CallArgs args = CallArgsFromVp(argc, vp);
642 0 : return CallNonGenericMethod<is, getInt32Impl>(cx, args);
643 : }
644 :
645 : bool
646 0 : DataViewObject::getUint32Impl(JSContext* cx, const CallArgs& args)
647 : {
648 0 : MOZ_ASSERT(is(args.thisv()));
649 :
650 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
651 :
652 : uint32_t val;
653 0 : if (!read(cx, thisView, args, &val))
654 0 : return false;
655 0 : args.rval().setNumber(val);
656 0 : return true;
657 : }
658 :
659 : bool
660 0 : DataViewObject::fun_getUint32(JSContext* cx, unsigned argc, Value* vp)
661 : {
662 0 : CallArgs args = CallArgsFromVp(argc, vp);
663 0 : return CallNonGenericMethod<is, getUint32Impl>(cx, args);
664 : }
665 :
666 : bool
667 0 : DataViewObject::getFloat32Impl(JSContext* cx, const CallArgs& args)
668 : {
669 0 : MOZ_ASSERT(is(args.thisv()));
670 :
671 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
672 :
673 : float val;
674 0 : if (!read(cx, thisView, args, &val))
675 0 : return false;
676 :
677 0 : args.rval().setDouble(CanonicalizeNaN(val));
678 0 : return true;
679 : }
680 :
681 : bool
682 0 : DataViewObject::fun_getFloat32(JSContext* cx, unsigned argc, Value* vp)
683 : {
684 0 : CallArgs args = CallArgsFromVp(argc, vp);
685 0 : return CallNonGenericMethod<is, getFloat32Impl>(cx, args);
686 : }
687 :
688 : bool
689 0 : DataViewObject::getFloat64Impl(JSContext* cx, const CallArgs& args)
690 : {
691 0 : MOZ_ASSERT(is(args.thisv()));
692 :
693 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
694 :
695 : double val;
696 0 : if (!read(cx, thisView, args, &val))
697 0 : return false;
698 :
699 0 : args.rval().setDouble(CanonicalizeNaN(val));
700 0 : return true;
701 : }
702 :
703 : bool
704 0 : DataViewObject::fun_getFloat64(JSContext* cx, unsigned argc, Value* vp)
705 : {
706 0 : CallArgs args = CallArgsFromVp(argc, vp);
707 0 : return CallNonGenericMethod<is, getFloat64Impl>(cx, args);
708 : }
709 :
710 : bool
711 0 : DataViewObject::setInt8Impl(JSContext* cx, const CallArgs& args)
712 : {
713 0 : MOZ_ASSERT(is(args.thisv()));
714 :
715 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
716 :
717 0 : if (!write<int8_t>(cx, thisView, args))
718 0 : return false;
719 0 : args.rval().setUndefined();
720 0 : return true;
721 : }
722 :
723 : bool
724 0 : DataViewObject::fun_setInt8(JSContext* cx, unsigned argc, Value* vp)
725 : {
726 0 : CallArgs args = CallArgsFromVp(argc, vp);
727 0 : return CallNonGenericMethod<is, setInt8Impl>(cx, args);
728 : }
729 :
730 : bool
731 0 : DataViewObject::setUint8Impl(JSContext* cx, const CallArgs& args)
732 : {
733 0 : MOZ_ASSERT(is(args.thisv()));
734 :
735 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
736 :
737 0 : if (!write<uint8_t>(cx, thisView, args))
738 0 : return false;
739 0 : args.rval().setUndefined();
740 0 : return true;
741 : }
742 :
743 : bool
744 0 : DataViewObject::fun_setUint8(JSContext* cx, unsigned argc, Value* vp)
745 : {
746 0 : CallArgs args = CallArgsFromVp(argc, vp);
747 0 : return CallNonGenericMethod<is, setUint8Impl>(cx, args);
748 : }
749 :
750 : bool
751 0 : DataViewObject::setInt16Impl(JSContext* cx, const CallArgs& args)
752 : {
753 0 : MOZ_ASSERT(is(args.thisv()));
754 :
755 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
756 :
757 0 : if (!write<int16_t>(cx, thisView, args))
758 0 : return false;
759 0 : args.rval().setUndefined();
760 0 : return true;
761 : }
762 :
763 : bool
764 0 : DataViewObject::fun_setInt16(JSContext* cx, unsigned argc, Value* vp)
765 : {
766 0 : CallArgs args = CallArgsFromVp(argc, vp);
767 0 : return CallNonGenericMethod<is, setInt16Impl>(cx, args);
768 : }
769 :
770 : bool
771 0 : DataViewObject::setUint16Impl(JSContext* cx, const CallArgs& args)
772 : {
773 0 : MOZ_ASSERT(is(args.thisv()));
774 :
775 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
776 :
777 0 : if (!write<uint16_t>(cx, thisView, args))
778 0 : return false;
779 0 : args.rval().setUndefined();
780 0 : return true;
781 : }
782 :
783 : bool
784 0 : DataViewObject::fun_setUint16(JSContext* cx, unsigned argc, Value* vp)
785 : {
786 0 : CallArgs args = CallArgsFromVp(argc, vp);
787 0 : return CallNonGenericMethod<is, setUint16Impl>(cx, args);
788 : }
789 :
790 : bool
791 0 : DataViewObject::setInt32Impl(JSContext* cx, const CallArgs& args)
792 : {
793 0 : MOZ_ASSERT(is(args.thisv()));
794 :
795 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
796 :
797 0 : if (!write<int32_t>(cx, thisView, args))
798 0 : return false;
799 0 : args.rval().setUndefined();
800 0 : return true;
801 : }
802 :
803 : bool
804 0 : DataViewObject::fun_setInt32(JSContext* cx, unsigned argc, Value* vp)
805 : {
806 0 : CallArgs args = CallArgsFromVp(argc, vp);
807 0 : return CallNonGenericMethod<is, setInt32Impl>(cx, args);
808 : }
809 :
810 : bool
811 0 : DataViewObject::setUint32Impl(JSContext* cx, const CallArgs& args)
812 : {
813 0 : MOZ_ASSERT(is(args.thisv()));
814 :
815 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
816 :
817 0 : if (!write<uint32_t>(cx, thisView, args))
818 0 : return false;
819 0 : args.rval().setUndefined();
820 0 : return true;
821 : }
822 :
823 : bool
824 0 : DataViewObject::fun_setUint32(JSContext* cx, unsigned argc, Value* vp)
825 : {
826 0 : CallArgs args = CallArgsFromVp(argc, vp);
827 0 : return CallNonGenericMethod<is, setUint32Impl>(cx, args);
828 : }
829 :
830 : bool
831 0 : DataViewObject::setFloat32Impl(JSContext* cx, const CallArgs& args)
832 : {
833 0 : MOZ_ASSERT(is(args.thisv()));
834 :
835 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
836 :
837 0 : if (!write<float>(cx, thisView, args))
838 0 : return false;
839 0 : args.rval().setUndefined();
840 0 : return true;
841 : }
842 :
843 : bool
844 0 : DataViewObject::fun_setFloat32(JSContext* cx, unsigned argc, Value* vp)
845 : {
846 0 : CallArgs args = CallArgsFromVp(argc, vp);
847 0 : return CallNonGenericMethod<is, setFloat32Impl>(cx, args);
848 : }
849 :
850 : bool
851 0 : DataViewObject::setFloat64Impl(JSContext* cx, const CallArgs& args)
852 : {
853 0 : MOZ_ASSERT(is(args.thisv()));
854 :
855 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
856 :
857 0 : if (!write<double>(cx, thisView, args))
858 0 : return false;
859 0 : args.rval().setUndefined();
860 0 : return true;
861 : }
862 :
863 : bool
864 0 : DataViewObject::fun_setFloat64(JSContext* cx, unsigned argc, Value* vp)
865 : {
866 0 : CallArgs args = CallArgsFromVp(argc, vp);
867 0 : return CallNonGenericMethod<is, setFloat64Impl>(cx, args);
868 : }
869 :
870 :
871 : bool
872 0 : DataViewObject::bufferGetterImpl(JSContext* cx, const CallArgs& args)
873 : {
874 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
875 0 : args.rval().set(DataViewObject::bufferValue(thisView));
876 0 : return true;
877 : }
878 :
879 : bool
880 0 : DataViewObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp)
881 : {
882 0 : CallArgs args = CallArgsFromVp(argc, vp);
883 0 : return CallNonGenericMethod<is, bufferGetterImpl>(cx, args);
884 : }
885 :
886 : bool
887 0 : DataViewObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
888 : {
889 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
890 0 : args.rval().set(DataViewObject::byteLengthValue(thisView));
891 0 : return true;
892 : }
893 :
894 : bool
895 0 : DataViewObject::byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
896 : {
897 0 : CallArgs args = CallArgsFromVp(argc, vp);
898 0 : return CallNonGenericMethod<is, byteLengthGetterImpl>(cx, args);
899 : }
900 :
901 : bool
902 0 : DataViewObject::byteOffsetGetterImpl(JSContext* cx, const CallArgs& args)
903 : {
904 0 : Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>());
905 0 : args.rval().set(DataViewObject::byteOffsetValue(thisView));
906 0 : return true;
907 : }
908 :
909 : bool
910 0 : DataViewObject::byteOffsetGetter(JSContext* cx, unsigned argc, Value* vp)
911 : {
912 0 : CallArgs args = CallArgsFromVp(argc, vp);
913 0 : return CallNonGenericMethod<is, byteOffsetGetterImpl>(cx, args);
914 : }
915 :
916 : const Class DataViewObject::protoClass_ = {
917 : js_Object_str,
918 : JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
919 : JS_NULL_CLASS_OPS,
920 : &DataViewObject::classSpec_
921 : };
922 :
923 : JSObject*
924 6 : DataViewObject::CreatePrototype(JSContext* cx, JSProtoKey key)
925 : {
926 6 : return GlobalObject::createBlankPrototype(cx, cx->global(), &DataViewObject::protoClass_);
927 : }
928 :
929 : static const ClassOps DataViewObjectClassOps = {
930 : nullptr, /* addProperty */
931 : nullptr, /* delProperty */
932 : nullptr, /* getProperty */
933 : nullptr, /* setProperty */
934 : nullptr, /* enumerate */
935 : nullptr, /* newEnumerate */
936 : nullptr, /* resolve */
937 : nullptr, /* mayResolve */
938 : nullptr, /* finalize */
939 : nullptr, /* call */
940 : nullptr, /* hasInstance */
941 : nullptr, /* construct */
942 : ArrayBufferViewObject::trace
943 : };
944 :
945 : const ClassSpec DataViewObject::classSpec_ = {
946 : GenericCreateConstructor<DataViewObject::construct, 3, gc::AllocKind::FUNCTION>,
947 : DataViewObject::CreatePrototype,
948 : nullptr,
949 : nullptr,
950 : DataViewObject::methods,
951 : DataViewObject::properties,
952 : };
953 :
954 : const Class DataViewObject::class_ = {
955 : "DataView",
956 : JSCLASS_HAS_PRIVATE |
957 : JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |
958 : JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
959 : &DataViewObjectClassOps,
960 : &DataViewObject::classSpec_
961 : };
962 :
963 : const JSFunctionSpec DataViewObject::methods[] = {
964 : JS_FN("getInt8", DataViewObject::fun_getInt8, 1,0),
965 : JS_FN("getUint8", DataViewObject::fun_getUint8, 1,0),
966 : JS_FN("getInt16", DataViewObject::fun_getInt16, 1,0),
967 : JS_FN("getUint16", DataViewObject::fun_getUint16, 1,0),
968 : JS_FN("getInt32", DataViewObject::fun_getInt32, 1,0),
969 : JS_FN("getUint32", DataViewObject::fun_getUint32, 1,0),
970 : JS_FN("getFloat32", DataViewObject::fun_getFloat32, 1,0),
971 : JS_FN("getFloat64", DataViewObject::fun_getFloat64, 1,0),
972 : JS_FN("setInt8", DataViewObject::fun_setInt8, 2,0),
973 : JS_FN("setUint8", DataViewObject::fun_setUint8, 2,0),
974 : JS_FN("setInt16", DataViewObject::fun_setInt16, 2,0),
975 : JS_FN("setUint16", DataViewObject::fun_setUint16, 2,0),
976 : JS_FN("setInt32", DataViewObject::fun_setInt32, 2,0),
977 : JS_FN("setUint32", DataViewObject::fun_setUint32, 2,0),
978 : JS_FN("setFloat32", DataViewObject::fun_setFloat32, 2,0),
979 : JS_FN("setFloat64", DataViewObject::fun_setFloat64, 2,0),
980 : JS_FS_END
981 : };
982 :
983 : const JSPropertySpec DataViewObject::properties[] = {
984 : JS_PSG("buffer", DataViewObject::bufferGetter, 0),
985 : JS_PSG("byteLength", DataViewObject::byteLengthGetter, 0),
986 : JS_PSG("byteOffset", DataViewObject::byteOffsetGetter, 0),
987 : JS_STRING_SYM_PS(toStringTag, "DataView", JSPROP_READONLY),
988 : JS_PS_END
989 : };
990 :
991 : void
992 0 : DataViewObject::notifyBufferDetached(void* newData)
993 : {
994 0 : setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(0));
995 0 : setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
996 0 : setPrivate(newData);
997 0 : }
998 :
999 : JS_FRIEND_API(bool)
1000 138 : JS_IsDataViewObject(JSObject* obj)
1001 : {
1002 138 : obj = CheckedUnwrap(obj);
1003 138 : return obj ? obj->is<DataViewObject>() : false;
1004 : }
1005 :
1006 : JS_FRIEND_API(uint32_t)
1007 0 : JS_GetDataViewByteOffset(JSObject* obj)
1008 : {
1009 0 : obj = CheckedUnwrap(obj);
1010 0 : if (!obj)
1011 0 : return 0;
1012 0 : return obj->as<DataViewObject>().byteOffset();
1013 : }
1014 :
1015 : JS_FRIEND_API(void*)
1016 0 : JS_GetDataViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
1017 : {
1018 0 : obj = CheckedUnwrap(obj);
1019 0 : if (!obj)
1020 0 : return nullptr;
1021 0 : DataViewObject& dv = obj->as<DataViewObject>();
1022 0 : *isSharedMemory = dv.isSharedMemory();
1023 0 : return dv.dataPointerEither().unwrap(/*safe - caller sees isSharedMemory*/);
1024 : }
1025 :
1026 : JS_FRIEND_API(uint32_t)
1027 0 : JS_GetDataViewByteLength(JSObject* obj)
1028 : {
1029 0 : obj = CheckedUnwrap(obj);
1030 0 : if (!obj)
1031 0 : return 0;
1032 0 : return obj->as<DataViewObject>().byteLength();
1033 : }
1034 :
1035 : JS_FRIEND_API(JSObject*)
1036 0 : JS_NewDataView(JSContext* cx, HandleObject buffer, uint32_t byteOffset, int32_t byteLength)
1037 : {
1038 0 : RootedObject constructor(cx);
1039 0 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewObject::class_);
1040 0 : if (!GetBuiltinConstructor(cx, key, &constructor))
1041 0 : return nullptr;
1042 :
1043 0 : FixedConstructArgs<3> cargs(cx);
1044 :
1045 0 : cargs[0].setObject(*buffer);
1046 0 : cargs[1].setNumber(byteOffset);
1047 0 : cargs[2].setInt32(byteLength);
1048 :
1049 0 : RootedValue fun(cx, ObjectValue(*constructor));
1050 0 : RootedObject obj(cx);
1051 0 : if (!Construct(cx, fun, cargs, fun, &obj))
1052 0 : return nullptr;
1053 0 : return obj;
1054 : }
|