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 "vm/ArrayBufferObject-inl.h"
8 : #include "vm/ArrayBufferObject.h"
9 :
10 : #include "mozilla/Alignment.h"
11 : #include "mozilla/CheckedInt.h"
12 : #include "mozilla/FloatingPoint.h"
13 : #include "mozilla/Maybe.h"
14 : #include "mozilla/PodOperations.h"
15 : #include "mozilla/TaggedAnonymousMemory.h"
16 :
17 : #include <string.h>
18 : #ifndef XP_WIN
19 : # include <sys/mman.h>
20 : #endif
21 :
22 : #ifdef MOZ_VALGRIND
23 : # include <valgrind/memcheck.h>
24 : #endif
25 :
26 : #include "jsapi.h"
27 : #include "jsarray.h"
28 : #include "jscntxt.h"
29 : #include "jscpucfg.h"
30 : #include "jsfriendapi.h"
31 : #include "jsnum.h"
32 : #include "jsobj.h"
33 : #include "jstypes.h"
34 : #include "jsutil.h"
35 : #ifdef XP_WIN
36 : # include "jswin.h"
37 : #endif
38 : #include "jswrapper.h"
39 :
40 : #include "builtin/DataViewObject.h"
41 : #include "gc/Barrier.h"
42 : #include "gc/Marking.h"
43 : #include "gc/Memory.h"
44 : #include "js/Conversions.h"
45 : #include "js/MemoryMetrics.h"
46 : #include "vm/GlobalObject.h"
47 : #include "vm/Interpreter.h"
48 : #include "vm/SharedArrayObject.h"
49 : #include "vm/WrapperObject.h"
50 : #include "wasm/WasmSignalHandlers.h"
51 : #include "wasm/WasmTypes.h"
52 :
53 : #include "jsatominlines.h"
54 :
55 : #include "vm/NativeObject-inl.h"
56 : #include "vm/Shape-inl.h"
57 :
58 : using JS::ToInt32;
59 :
60 : using mozilla::DebugOnly;
61 : using mozilla::CheckedInt;
62 : using mozilla::Some;
63 : using mozilla::Maybe;
64 : using mozilla::Nothing;
65 :
66 : using namespace js;
67 : using namespace js::gc;
68 :
69 : /*
70 : * Convert |v| to an array index for an array of length |length| per
71 : * the Typed Array Specification section 7.0, |subarray|. If successful,
72 : * the output value is in the range [0, length].
73 : */
74 : bool
75 0 : js::ToClampedIndex(JSContext* cx, HandleValue v, uint32_t length, uint32_t* out)
76 : {
77 : int32_t result;
78 0 : if (!ToInt32(cx, v, &result))
79 0 : return false;
80 0 : if (result < 0) {
81 0 : result += length;
82 0 : if (result < 0)
83 0 : result = 0;
84 0 : } else if (uint32_t(result) > length) {
85 0 : result = length;
86 : }
87 0 : *out = uint32_t(result);
88 0 : return true;
89 : }
90 :
91 : /*
92 : * ArrayBufferObject
93 : *
94 : * This class holds the underlying raw buffer that the TypedArrayObject classes
95 : * access. It can be created explicitly and passed to a TypedArrayObject, or
96 : * can be created implicitly by constructing a TypedArrayObject with a size.
97 : */
98 :
99 : /*
100 : * ArrayBufferObject (base)
101 : */
102 :
103 : static JSObject*
104 6 : CreateArrayBufferPrototype(JSContext* cx, JSProtoKey key)
105 : {
106 6 : return GlobalObject::createBlankPrototype(cx, cx->global(), &ArrayBufferObject::protoClass_);
107 : }
108 :
109 : static const ClassOps ArrayBufferObjectClassOps = {
110 : nullptr, /* addProperty */
111 : nullptr, /* delProperty */
112 : nullptr, /* getProperty */
113 : nullptr, /* setProperty */
114 : nullptr, /* enumerate */
115 : nullptr, /* newEnumerate */
116 : nullptr, /* resolve */
117 : nullptr, /* mayResolve */
118 : ArrayBufferObject::finalize,
119 : nullptr, /* call */
120 : nullptr, /* hasInstance */
121 : nullptr, /* construct */
122 : ArrayBufferObject::trace,
123 : };
124 :
125 : static const JSFunctionSpec static_functions[] = {
126 : JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
127 : JS_FS_END
128 : };
129 :
130 : static const JSPropertySpec static_properties[] = {
131 : JS_SELF_HOSTED_SYM_GET(species, "ArrayBufferSpecies", 0),
132 : JS_PS_END
133 : };
134 :
135 :
136 : static const JSFunctionSpec prototype_functions[] = {
137 : JS_SELF_HOSTED_FN("slice", "ArrayBufferSlice", 2, 0),
138 : JS_FS_END
139 : };
140 :
141 : static const JSPropertySpec prototype_properties[] = {
142 : JS_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0),
143 : JS_STRING_SYM_PS(toStringTag, "ArrayBuffer", JSPROP_READONLY),
144 : JS_PS_END
145 : };
146 :
147 : static const ClassSpec ArrayBufferObjectClassSpec = {
148 : GenericCreateConstructor<ArrayBufferObject::class_constructor, 1, gc::AllocKind::FUNCTION>,
149 : CreateArrayBufferPrototype,
150 : static_functions,
151 : static_properties,
152 : prototype_functions,
153 : prototype_properties
154 : };
155 :
156 : static const ClassExtension ArrayBufferObjectClassExtension = {
157 : nullptr, /* weakmapKeyDelegateOp */
158 : ArrayBufferObject::objectMoved
159 : };
160 :
161 : const Class ArrayBufferObject::class_ = {
162 : "ArrayBuffer",
163 : JSCLASS_DELAY_METADATA_BUILDER |
164 : JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
165 : JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
166 : JSCLASS_BACKGROUND_FINALIZE,
167 : &ArrayBufferObjectClassOps,
168 : &ArrayBufferObjectClassSpec,
169 : &ArrayBufferObjectClassExtension
170 : };
171 :
172 : const Class ArrayBufferObject::protoClass_ = {
173 : "ArrayBufferPrototype",
174 : JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
175 : JS_NULL_CLASS_OPS,
176 : &ArrayBufferObjectClassSpec
177 : };
178 :
179 : bool
180 0 : js::IsArrayBuffer(HandleValue v)
181 : {
182 0 : return v.isObject() && v.toObject().is<ArrayBufferObject>();
183 : }
184 :
185 : bool
186 0 : js::IsArrayBuffer(HandleObject obj)
187 : {
188 0 : return obj->is<ArrayBufferObject>();
189 : }
190 :
191 : bool
192 0 : js::IsArrayBuffer(JSObject* obj)
193 : {
194 0 : return obj->is<ArrayBufferObject>();
195 : }
196 :
197 : ArrayBufferObject&
198 0 : js::AsArrayBuffer(HandleObject obj)
199 : {
200 0 : MOZ_ASSERT(IsArrayBuffer(obj));
201 0 : return obj->as<ArrayBufferObject>();
202 : }
203 :
204 : ArrayBufferObject&
205 0 : js::AsArrayBuffer(JSObject* obj)
206 : {
207 0 : MOZ_ASSERT(IsArrayBuffer(obj));
208 0 : return obj->as<ArrayBufferObject>();
209 : }
210 :
211 : bool
212 0 : js::IsArrayBufferMaybeShared(HandleValue v)
213 : {
214 0 : return v.isObject() && v.toObject().is<ArrayBufferObjectMaybeShared>();
215 : }
216 :
217 : bool
218 0 : js::IsArrayBufferMaybeShared(HandleObject obj)
219 : {
220 0 : return obj->is<ArrayBufferObjectMaybeShared>();
221 : }
222 :
223 : bool
224 0 : js::IsArrayBufferMaybeShared(JSObject* obj)
225 : {
226 0 : return obj->is<ArrayBufferObjectMaybeShared>();
227 : }
228 :
229 : ArrayBufferObjectMaybeShared&
230 0 : js::AsArrayBufferMaybeShared(HandleObject obj)
231 : {
232 0 : MOZ_ASSERT(IsArrayBufferMaybeShared(obj));
233 0 : return obj->as<ArrayBufferObjectMaybeShared>();
234 : }
235 :
236 : ArrayBufferObjectMaybeShared&
237 0 : js::AsArrayBufferMaybeShared(JSObject* obj)
238 : {
239 0 : MOZ_ASSERT(IsArrayBufferMaybeShared(obj));
240 0 : return obj->as<ArrayBufferObjectMaybeShared>();
241 : }
242 :
243 : MOZ_ALWAYS_INLINE bool
244 0 : ArrayBufferObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
245 : {
246 0 : MOZ_ASSERT(IsArrayBuffer(args.thisv()));
247 0 : args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
248 0 : return true;
249 : }
250 :
251 : bool
252 0 : ArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
253 : {
254 0 : CallArgs args = CallArgsFromVp(argc, vp);
255 0 : return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
256 : }
257 :
258 : /*
259 : * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
260 : */
261 : bool
262 0 : ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
263 : {
264 0 : CallArgs args = CallArgsFromVp(argc, vp);
265 0 : args.rval().setBoolean(args.get(0).isObject() &&
266 0 : JS_IsArrayBufferViewObject(&args.get(0).toObject()));
267 0 : return true;
268 : }
269 :
270 : // ES2017 draft 24.1.2.1
271 : bool
272 0 : ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
273 : {
274 0 : CallArgs args = CallArgsFromVp(argc, vp);
275 :
276 : // Step 1.
277 0 : if (!ThrowIfNotConstructing(cx, args, "ArrayBuffer"))
278 0 : return false;
279 :
280 : // Step 2.
281 : uint64_t byteLength;
282 0 : if (!ToIndex(cx, args.get(0), &byteLength))
283 0 : return false;
284 :
285 : // Step 3 (Inlined 24.1.1.1 AllocateArrayBuffer).
286 : // 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
287 0 : RootedObject proto(cx);
288 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
289 0 : return false;
290 :
291 : // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
292 : // Refuse to allocate too large buffers, currently limited to ~2 GiB.
293 0 : if (byteLength > INT32_MAX) {
294 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
295 0 : return false;
296 : }
297 :
298 : // 24.1.1.1, steps 1 and 4-6.
299 0 : JSObject* bufobj = create(cx, uint32_t(byteLength), proto);
300 0 : if (!bufobj)
301 0 : return false;
302 0 : args.rval().setObject(*bufobj);
303 0 : return true;
304 : }
305 :
306 : static ArrayBufferObject::BufferContents
307 0 : AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
308 : {
309 0 : uint8_t* p = cx->runtime()->pod_callocCanGC<uint8_t>(nbytes);
310 0 : if (!p)
311 0 : ReportOutOfMemory(cx);
312 :
313 0 : return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
314 : }
315 :
316 : static void
317 0 : NoteViewBufferWasDetached(ArrayBufferViewObject* view,
318 : ArrayBufferObject::BufferContents newContents,
319 : JSContext* cx)
320 : {
321 0 : view->notifyBufferDetached(cx, newContents.data());
322 :
323 : // Notify compiled jit code that the base pointer has moved.
324 0 : MarkObjectStateChange(cx, view);
325 0 : }
326 :
327 : /* static */ void
328 0 : ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer,
329 : BufferContents newContents)
330 : {
331 0 : assertSameCompartment(cx, buffer);
332 0 : MOZ_ASSERT(!buffer->isPreparedForAsmJS());
333 :
334 : // When detaching buffers where we don't know all views, the new data must
335 : // match the old data. All missing views are typed objects, which do not
336 : // expect their data to ever change.
337 0 : MOZ_ASSERT_IF(buffer->forInlineTypedObject(),
338 : newContents.data() == buffer->dataPointer());
339 :
340 : // When detaching a buffer with typed object views, any jitcode accessing
341 : // such views must be deoptimized so that detachment checks are performed.
342 : // This is done by setting a compartment-wide flag indicating that buffers
343 : // with typed object views have been detached.
344 0 : if (buffer->hasTypedObjectViews()) {
345 : // Make sure the global object's group has been instantiated, so the
346 : // flag change will be observed.
347 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
348 0 : if (!JSObject::getGroup(cx, cx->global()))
349 0 : oomUnsafe.crash("ArrayBufferObject::detach");
350 0 : MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER);
351 0 : cx->compartment()->detachedTypedObjects = 1;
352 : }
353 :
354 : // Update all views of the buffer to account for the buffer having been
355 : // detached, and clear the buffer's data and list of views.
356 :
357 0 : auto& innerViews = cx->compartment()->innerViews.get();
358 0 : if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(buffer)) {
359 0 : for (size_t i = 0; i < views->length(); i++)
360 0 : NoteViewBufferWasDetached((*views)[i], newContents, cx);
361 0 : innerViews.removeViews(buffer);
362 : }
363 0 : if (buffer->firstView()) {
364 0 : if (buffer->forInlineTypedObject()) {
365 : // The buffer points to inline data in its first view, so to keep
366 : // this pointer alive we don't clear out the first view.
367 0 : MOZ_ASSERT(buffer->firstView()->is<InlineTransparentTypedObject>());
368 : } else {
369 0 : NoteViewBufferWasDetached(buffer->firstView(), newContents, cx);
370 0 : buffer->setFirstView(nullptr);
371 : }
372 : }
373 :
374 0 : if (newContents.data() != buffer->dataPointer())
375 0 : buffer->setNewData(cx->runtime()->defaultFreeOp(), newContents, OwnsData);
376 :
377 0 : buffer->setByteLength(0);
378 0 : buffer->setIsDetached();
379 0 : }
380 :
381 : void
382 0 : ArrayBufferObject::setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState)
383 : {
384 0 : if (ownsData()) {
385 0 : MOZ_ASSERT(newContents.data() != dataPointer());
386 0 : releaseData(fop);
387 : }
388 :
389 0 : setDataPointer(newContents, ownsState);
390 0 : }
391 :
392 : // This is called *only* from changeContents(), below.
393 : // By construction, every view parameter will be mapping unshared memory (an ArrayBuffer).
394 : // Hence no reason to worry about shared memory here.
395 :
396 : void
397 0 : ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
398 : uint8_t* oldDataPointer, BufferContents newContents)
399 : {
400 0 : MOZ_ASSERT(!view->isSharedMemory());
401 :
402 : // Watch out for NULL data pointers in views. This means that the view
403 : // is not fully initialized (in which case it'll be initialized later
404 : // with the correct pointer).
405 0 : JS::AutoCheckCannotGC nogc;
406 0 : uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
407 0 : if (viewDataPointer) {
408 0 : MOZ_ASSERT(newContents);
409 0 : ptrdiff_t offset = viewDataPointer - oldDataPointer;
410 0 : viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset;
411 0 : view->setDataPointerUnshared(viewDataPointer);
412 : }
413 :
414 : // Notify compiled jit code that the base pointer has moved.
415 0 : MarkObjectStateChange(cx, view);
416 0 : }
417 :
418 : // BufferContents is specific to ArrayBuffer, hence it will not represent shared memory.
419 :
420 : void
421 0 : ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents,
422 : OwnsState ownsState)
423 : {
424 0 : MOZ_RELEASE_ASSERT(!isWasm());
425 0 : MOZ_ASSERT(!forInlineTypedObject());
426 :
427 : // Change buffer contents.
428 0 : uint8_t* oldDataPointer = dataPointer();
429 0 : setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
430 :
431 : // Update all views.
432 0 : auto& innerViews = cx->compartment()->innerViews.get();
433 0 : if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
434 0 : for (size_t i = 0; i < views->length(); i++)
435 0 : changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
436 : }
437 0 : if (firstView())
438 0 : changeViewContents(cx, firstView(), oldDataPointer, newContents);
439 0 : }
440 :
441 : /*
442 : * Wasm Raw Buf Linear Memory Structure
443 : *
444 : * The linear heap in Wasm is an mmaped array buffer. Several
445 : * constants manage its lifetime:
446 : *
447 : * - length - the wasm-visible current length of the buffer. Accesses in the
448 : * range [0, length] succeed. May only increase.
449 : *
450 : * - boundsCheckLimit - when !WASM_HUGE_MEMORY, the size against which we
451 : * perform bounds checks. It is always a constant offset smaller than
452 : * mappedSize. Currently that constant offset is 64k (wasm::GuardSize).
453 : *
454 : * - max - the optional declared limit on how much length can grow.
455 : *
456 : * - mappedSize - the actual mmaped size. Access in the range
457 : * [0, mappedSize] will either succeed, or be handled by the wasm signal
458 : * handlers.
459 : *
460 : * The below diagram shows the layout of the wasm heap. The wasm-visible
461 : * portion of the heap starts at 0. There is one extra page prior to the
462 : * start of the wasm heap which contains the WasmArrayRawBuffer struct at
463 : * its end (i.e. right before the start of the WASM heap).
464 : *
465 : * WasmArrayRawBuffer
466 : * \ ArrayBufferObject::dataPointer()
467 : * \ /
468 : * \ |
469 : * ______|_|____________________________________________________________
470 : * |______|_|______________|___________________|____________|____________|
471 : * 0 length maxSize boundsCheckLimit mappedSize
472 : *
473 : * \_______________________/
474 : * COMMITED
475 : * \____________________________________________/
476 : * SLOP
477 : * \_____________________________________________________________________/
478 : * MAPPED
479 : *
480 : * Invariants:
481 : * - length only increases
482 : * - 0 <= length <= maxSize (if present) <= boundsCheckLimit <= mappedSize
483 : * - on ARM boundsCheckLimit must be a valid ARM immediate.
484 : * - if maxSize is not specified, boundsCheckLimit/mappedSize may grow. They are
485 : * otherwise constant.
486 : *
487 : * NOTE: For asm.js on non-x64 we guarantee that
488 : *
489 : * length == maxSize == boundsCheckLimit == mappedSize
490 : *
491 : * That is, signal handlers will not be invoked, since they cannot emulate
492 : * asm.js accesses on non-x64 architectures.
493 : *
494 : * The region between length and mappedSize is the SLOP - an area where we use
495 : * signal handlers to catch things that slip by bounds checks. Logically it has
496 : * two parts:
497 : *
498 : * - from length to boundsCheckLimit - this part of the SLOP serves to catch
499 : * accesses to memory we have reserved but not yet grown into. This allows us
500 : * to grow memory up to max (when present) without having to patch/update the
501 : * bounds checks.
502 : *
503 : * - from boundsCheckLimit to mappedSize - (Note: In current patch 0) - this
504 : * part of the SLOP allows us to bounds check against base pointers and fold
505 : * some constant offsets inside loads. This enables better Bounds
506 : * Check Elimination.
507 : *
508 : */
509 :
510 : class js::WasmArrayRawBuffer
511 : {
512 : Maybe<uint32_t> maxSize_;
513 : size_t mappedSize_;
514 :
515 : protected:
516 0 : WasmArrayRawBuffer(uint8_t* buffer, uint32_t length, const Maybe<uint32_t>& maxSize,
517 : size_t mappedSize)
518 0 : : maxSize_(maxSize), mappedSize_(mappedSize)
519 : {
520 0 : MOZ_ASSERT(buffer == dataPointer());
521 0 : }
522 :
523 : public:
524 : static WasmArrayRawBuffer* Allocate(uint32_t numBytes, const Maybe<uint32_t>& maxSize);
525 : static void Release(void* mem);
526 :
527 0 : uint8_t* dataPointer() {
528 0 : uint8_t* ptr = reinterpret_cast<uint8_t*>(this);
529 0 : return ptr + sizeof(WasmArrayRawBuffer);
530 : }
531 :
532 0 : uint8_t* basePointer() {
533 0 : return dataPointer() - gc::SystemPageSize();
534 : }
535 :
536 0 : size_t mappedSize() const {
537 0 : return mappedSize_;
538 : }
539 :
540 0 : Maybe<uint32_t> maxSize() const {
541 0 : return maxSize_;
542 : }
543 :
544 0 : size_t allocatedBytes() const {
545 0 : return mappedSize_ + gc::SystemPageSize();
546 : }
547 :
548 : #ifndef WASM_HUGE_MEMORY
549 : uint32_t boundsCheckLimit() const {
550 : MOZ_ASSERT(mappedSize_ <= UINT32_MAX);
551 : MOZ_ASSERT(mappedSize_ >= wasm::GuardSize);
552 : MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize_ - wasm::GuardSize));
553 : return mappedSize_ - wasm::GuardSize;
554 : }
555 : #endif
556 :
557 0 : MOZ_MUST_USE bool growToSizeInPlace(uint32_t oldSize, uint32_t newSize) {
558 0 : MOZ_ASSERT(newSize >= oldSize);
559 0 : MOZ_ASSERT_IF(maxSize(), newSize <= maxSize().value());
560 0 : MOZ_ASSERT(newSize <= mappedSize());
561 :
562 0 : uint32_t delta = newSize - oldSize;
563 0 : MOZ_ASSERT(delta % wasm::PageSize == 0);
564 :
565 0 : uint8_t* dataEnd = dataPointer() + oldSize;
566 0 : MOZ_ASSERT(uintptr_t(dataEnd) % gc::SystemPageSize() == 0);
567 : # ifdef XP_WIN
568 : if (delta && !VirtualAlloc(dataEnd, delta, MEM_COMMIT, PAGE_READWRITE))
569 : return false;
570 : # else // XP_WIN
571 0 : if (delta && mprotect(dataEnd, delta, PROT_READ | PROT_WRITE))
572 0 : return false;
573 : # endif // !XP_WIN
574 :
575 : # if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
576 : VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)dataEnd, delta);
577 : # endif
578 :
579 0 : MemProfiler::SampleNative(dataEnd, delta);
580 0 : return true;
581 : }
582 :
583 : #ifndef WASM_HUGE_MEMORY
584 : bool extendMappedSize(uint32_t maxSize) {
585 : size_t newMappedSize = wasm::ComputeMappedSize(maxSize);
586 : MOZ_ASSERT(mappedSize_ <= newMappedSize);
587 : if (mappedSize_ == newMappedSize)
588 : return true;
589 :
590 : # ifdef XP_WIN
591 : uint8_t* mappedEnd = dataPointer() + mappedSize_;
592 : uint32_t delta = newMappedSize - mappedSize_;
593 : if (!VirtualAlloc(mappedEnd, delta, MEM_RESERVE, PAGE_NOACCESS))
594 : return false;
595 : # elif defined(XP_LINUX)
596 : // Note this will not move memory (no MREMAP_MAYMOVE specified)
597 : if (MAP_FAILED == mremap(dataPointer(), mappedSize_, newMappedSize, 0))
598 : return false;
599 : # else
600 : // No mechanism for remapping on MacOS and other Unices. Luckily
601 : // shouldn't need it here as most of these are 64-bit.
602 : return false;
603 : # endif
604 :
605 : mappedSize_ = newMappedSize;
606 : return true;
607 : }
608 :
609 : // Try and grow the mapped region of memory. Does not changes current size.
610 : // Does not move memory if no space to grow.
611 : void tryGrowMaxSizeInPlace(uint32_t deltaMaxSize) {
612 : CheckedInt<uint32_t> newMaxSize = maxSize_.value();
613 : newMaxSize += deltaMaxSize;
614 : MOZ_ASSERT(newMaxSize.isValid());
615 : MOZ_ASSERT(newMaxSize.value() % wasm::PageSize == 0);
616 :
617 : if (!extendMappedSize(newMaxSize.value()))
618 : return;
619 :
620 : maxSize_ = Some(newMaxSize.value());
621 : }
622 : #endif // WASM_HUGE_MEMORY
623 : };
624 :
625 : /* static */ WasmArrayRawBuffer*
626 0 : WasmArrayRawBuffer::Allocate(uint32_t numBytes, const Maybe<uint32_t>& maxSize)
627 : {
628 : size_t mappedSize;
629 : #ifdef WASM_HUGE_MEMORY
630 0 : mappedSize = wasm::HugeMappedSize;
631 : #else
632 : mappedSize = wasm::ComputeMappedSize(maxSize.valueOr(numBytes));
633 : #endif
634 :
635 0 : MOZ_RELEASE_ASSERT(mappedSize <= SIZE_MAX - gc::SystemPageSize());
636 0 : MOZ_RELEASE_ASSERT(numBytes <= maxSize.valueOr(UINT32_MAX));
637 0 : MOZ_ASSERT(numBytes % gc::SystemPageSize() == 0);
638 0 : MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0);
639 :
640 0 : uint64_t mappedSizeWithHeader = mappedSize + gc::SystemPageSize();
641 0 : uint64_t numBytesWithHeader = numBytes + gc::SystemPageSize();
642 :
643 : # ifdef XP_WIN
644 : void* data = VirtualAlloc(nullptr, (size_t) mappedSizeWithHeader, MEM_RESERVE, PAGE_NOACCESS);
645 : if (!data)
646 : return nullptr;
647 :
648 : if (!VirtualAlloc(data, numBytesWithHeader, MEM_COMMIT, PAGE_READWRITE)) {
649 : VirtualFree(data, 0, MEM_RELEASE);
650 : return nullptr;
651 : }
652 : # else // XP_WIN
653 : void* data = MozTaggedAnonymousMmap(nullptr, (size_t) mappedSizeWithHeader, PROT_NONE,
654 0 : MAP_PRIVATE | MAP_ANON, -1, 0, "wasm-reserved");
655 0 : if (data == MAP_FAILED)
656 0 : return nullptr;
657 :
658 : // Note we will waste a page on zero-sized memories here
659 0 : if (mprotect(data, numBytesWithHeader, PROT_READ | PROT_WRITE)) {
660 0 : munmap(data, mappedSizeWithHeader);
661 0 : return nullptr;
662 : }
663 : # endif // !XP_WIN
664 0 : MemProfiler::SampleNative(data, numBytesWithHeader);
665 :
666 : # if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
667 : VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + numBytesWithHeader,
668 : mappedSizeWithHeader - numBytesWithHeader);
669 : # endif
670 :
671 0 : uint8_t* base = reinterpret_cast<uint8_t*>(data) + gc::SystemPageSize();
672 0 : uint8_t* header = base - sizeof(WasmArrayRawBuffer);
673 :
674 0 : auto rawBuf = new (header) WasmArrayRawBuffer(base, numBytes, maxSize, mappedSize);
675 0 : return rawBuf;
676 : }
677 :
678 : /* static */ void
679 0 : WasmArrayRawBuffer::Release(void* mem)
680 : {
681 0 : WasmArrayRawBuffer* header = (WasmArrayRawBuffer*)((uint8_t*)mem - sizeof(WasmArrayRawBuffer));
682 0 : uint8_t* base = header->basePointer();
683 0 : MOZ_RELEASE_ASSERT(header->mappedSize() <= SIZE_MAX - gc::SystemPageSize());
684 0 : size_t mappedSizeWithHeader = header->mappedSize() + gc::SystemPageSize();
685 :
686 0 : MemProfiler::RemoveNative(base);
687 : # ifdef XP_WIN
688 : VirtualFree(base, 0, MEM_RELEASE);
689 : # else // XP_WIN
690 0 : munmap(base, mappedSizeWithHeader);
691 : # endif // !XP_WIN
692 :
693 : # if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
694 : VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, mappedSizeWithHeader);
695 : # endif
696 0 : }
697 :
698 : WasmArrayRawBuffer*
699 0 : ArrayBufferObject::BufferContents::wasmBuffer() const
700 : {
701 0 : MOZ_RELEASE_ASSERT(kind_ == WASM);
702 0 : return (WasmArrayRawBuffer*)(data_ - sizeof(WasmArrayRawBuffer));
703 : }
704 :
705 : #define ROUND_UP(v, a) ((v) % (a) == 0 ? (v) : v + a - ((v) % (a)))
706 :
707 : /* static */ ArrayBufferObject*
708 0 : ArrayBufferObject::createForWasm(JSContext* cx, uint32_t initialSize,
709 : const Maybe<uint32_t>& maybeMaxSize)
710 : {
711 0 : MOZ_ASSERT(initialSize % wasm::PageSize == 0);
712 0 : MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
713 0 : MOZ_RELEASE_ASSERT((initialSize / wasm::PageSize) <= wasm::MaxMemoryInitialPages);
714 :
715 : // Prevent applications specifying a large max (like UINT32_MAX) from
716 : // unintentially OOMing the browser on 32-bit: they just want "a lot of
717 : // memory". Maintain the invariant that initialSize <= maxSize.
718 0 : Maybe<uint32_t> maxSize = maybeMaxSize;
719 0 : if (sizeof(void*) == 4 && maybeMaxSize) {
720 : static const uint32_t OneGiB = 1 << 30;
721 0 : uint32_t clamp = Max(OneGiB, initialSize);
722 0 : maxSize = Some(Min(clamp, maybeMaxSize.value()));
723 : }
724 :
725 0 : RootedArrayBufferObject buffer(cx, ArrayBufferObject::createEmpty(cx));
726 0 : if (!buffer)
727 0 : return nullptr;
728 :
729 : // Try to reserve the maximum requested memory
730 0 : WasmArrayRawBuffer* wasmBuf = WasmArrayRawBuffer::Allocate(initialSize, maxSize);
731 0 : if (!wasmBuf) {
732 : #ifdef WASM_HUGE_MEMORY
733 0 : ReportOutOfMemory(cx);
734 0 : return nullptr;
735 : #else
736 : // If we fail, and have a maxSize, try to reserve the biggest chunk in
737 : // the range [initialSize, maxSize) using log backoff.
738 : if (!maxSize) {
739 : ReportOutOfMemory(cx);
740 : return nullptr;
741 : }
742 :
743 : uint32_t cur = maxSize.value() / 2;
744 :
745 : for (; cur > initialSize; cur /= 2) {
746 : wasmBuf = WasmArrayRawBuffer::Allocate(initialSize, Some(ROUND_UP(cur, wasm::PageSize)));
747 : if (wasmBuf)
748 : break;
749 : }
750 :
751 : if (!wasmBuf) {
752 : ReportOutOfMemory(cx);
753 : return nullptr;
754 : }
755 :
756 : // Try to grow our chunk as much as possible.
757 : for (size_t d = cur / 2; d >= wasm::PageSize; d /= 2)
758 : wasmBuf->tryGrowMaxSizeInPlace(ROUND_UP(d, wasm::PageSize));
759 : #endif
760 : }
761 :
762 0 : auto contents = BufferContents::create<WASM>(wasmBuf->dataPointer());
763 0 : buffer->initialize(initialSize, contents, OwnsData);
764 0 : cx->zone()->updateMallocCounter(wasmBuf->mappedSize());
765 0 : return buffer;
766 : }
767 :
768 : // Note this function can return false with or without an exception pending. The
769 : // asm.js caller checks cx->isExceptionPending before propagating failure.
770 : // Returning false without throwing means that asm.js linking will fail which
771 : // will recompile as non-asm.js.
772 : /* static */ bool
773 0 : ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer, bool needGuard)
774 : {
775 : #ifdef WASM_HUGE_MEMORY
776 0 : MOZ_ASSERT(needGuard);
777 : #endif
778 0 : MOZ_ASSERT(buffer->byteLength() % wasm::PageSize == 0);
779 0 : MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
780 :
781 0 : if (buffer->forInlineTypedObject())
782 0 : return false;
783 :
784 0 : if (needGuard) {
785 0 : if (buffer->isWasm() && buffer->isPreparedForAsmJS())
786 0 : return true;
787 :
788 : // Non-prepared-for-asm.js wasm buffers can be detached at any time.
789 : // This error can only be triggered for SIMD.js (which isn't shipping)
790 : // on !WASM_HUGE_MEMORY so this error is only visible in testing.
791 0 : if (buffer->isWasm() || buffer->isPreparedForAsmJS())
792 0 : return false;
793 :
794 0 : uint32_t length = buffer->byteLength();
795 0 : WasmArrayRawBuffer* wasmBuf = WasmArrayRawBuffer::Allocate(length, Some(length));
796 0 : if (!wasmBuf) {
797 0 : ReportOutOfMemory(cx);
798 0 : return false;
799 : }
800 :
801 0 : void* data = wasmBuf->dataPointer();
802 0 : memcpy(data, buffer->dataPointer(), length);
803 :
804 : // Swap the new elements into the ArrayBufferObject. Mark the
805 : // ArrayBufferObject so we don't do this again.
806 0 : buffer->changeContents(cx, BufferContents::create<WASM>(data), OwnsData);
807 0 : buffer->setIsPreparedForAsmJS();
808 0 : MOZ_ASSERT(data == buffer->dataPointer());
809 0 : cx->zone()->updateMallocCounter(wasmBuf->mappedSize());
810 0 : return true;
811 : }
812 :
813 0 : if (!buffer->isWasm() && buffer->isPreparedForAsmJS())
814 0 : return true;
815 :
816 : // Non-prepared-for-asm.js wasm buffers can be detached at any time.
817 0 : if (buffer->isWasm())
818 0 : return false;
819 :
820 0 : if (!buffer->ownsData()) {
821 0 : BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
822 0 : if (!contents)
823 0 : return false;
824 0 : memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
825 0 : buffer->changeContents(cx, contents, OwnsData);
826 : }
827 :
828 0 : buffer->setIsPreparedForAsmJS();
829 0 : return true;
830 : }
831 :
832 : ArrayBufferObject::BufferContents
833 0 : ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length)
834 : {
835 0 : void* data = AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT);
836 0 : MemProfiler::SampleNative(data, length);
837 0 : return BufferContents::create<MAPPED>(data);
838 : }
839 :
840 : uint8_t*
841 0 : ArrayBufferObject::inlineDataPointer() const
842 : {
843 0 : return static_cast<uint8_t*>(fixedData(JSCLASS_RESERVED_SLOTS(&class_)));
844 : }
845 :
846 : uint8_t*
847 0 : ArrayBufferObject::dataPointer() const
848 : {
849 0 : return static_cast<uint8_t*>(getSlot(DATA_SLOT).toPrivate());
850 : }
851 :
852 : SharedMem<uint8_t*>
853 0 : ArrayBufferObject::dataPointerShared() const
854 : {
855 0 : return SharedMem<uint8_t*>::unshared(getSlot(DATA_SLOT).toPrivate());
856 : }
857 :
858 : void
859 0 : ArrayBufferObject::releaseData(FreeOp* fop)
860 : {
861 0 : MOZ_ASSERT(ownsData());
862 :
863 0 : switch (bufferKind()) {
864 : case PLAIN:
865 0 : fop->free_(dataPointer());
866 0 : break;
867 : case MAPPED:
868 0 : MemProfiler::RemoveNative(dataPointer());
869 0 : DeallocateMappedContent(dataPointer(), byteLength());
870 0 : break;
871 : case WASM:
872 0 : WasmArrayRawBuffer::Release(dataPointer());
873 0 : break;
874 : case KIND_MASK:
875 0 : MOZ_CRASH("bad bufferKind()");
876 : }
877 0 : }
878 :
879 : void
880 0 : ArrayBufferObject::setDataPointer(BufferContents contents, OwnsState ownsData)
881 : {
882 0 : setSlot(DATA_SLOT, PrivateValue(contents.data()));
883 0 : setOwnsData(ownsData);
884 0 : setFlags((flags() & ~KIND_MASK) | contents.kind());
885 0 : }
886 :
887 : uint32_t
888 0 : ArrayBufferObject::byteLength() const
889 : {
890 0 : return getSlot(BYTE_LENGTH_SLOT).toInt32();
891 : }
892 :
893 : void
894 0 : ArrayBufferObject::setByteLength(uint32_t length)
895 : {
896 0 : MOZ_ASSERT(length <= INT32_MAX);
897 0 : setSlot(BYTE_LENGTH_SLOT, Int32Value(length));
898 0 : }
899 :
900 : size_t
901 0 : ArrayBufferObject::wasmMappedSize() const
902 : {
903 0 : if (isWasm())
904 0 : return contents().wasmBuffer()->mappedSize();
905 0 : return byteLength();
906 : }
907 :
908 : size_t
909 0 : js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf)
910 : {
911 0 : if (buf->is<ArrayBufferObject>())
912 0 : return buf->as<ArrayBufferObject>().wasmMappedSize();
913 : #ifdef WASM_HUGE_MEMORY
914 0 : return wasm::HugeMappedSize;
915 : #else
916 : return buf->as<SharedArrayBufferObject>().byteLength();
917 : #endif
918 : }
919 :
920 : Maybe<uint32_t>
921 0 : ArrayBufferObject::wasmMaxSize() const
922 : {
923 0 : if (isWasm())
924 0 : return contents().wasmBuffer()->maxSize();
925 : else
926 0 : return Some<uint32_t>(byteLength());
927 : }
928 :
929 : Maybe<uint32_t>
930 0 : js::WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf)
931 : {
932 0 : if (buf->is<ArrayBufferObject>())
933 0 : return buf->as<ArrayBufferObject>().wasmMaxSize();
934 :
935 0 : return Some(buf->as<SharedArrayBufferObject>().byteLength());
936 : }
937 :
938 : /* static */ bool
939 0 : ArrayBufferObject::wasmGrowToSizeInPlace(uint32_t newSize,
940 : HandleArrayBufferObject oldBuf,
941 : MutableHandleArrayBufferObject newBuf,
942 : JSContext* cx)
943 : {
944 : // On failure, do not throw and ensure that the original buffer is
945 : // unmodified and valid. After WasmArrayRawBuffer::growToSizeInPlace(), the
946 : // wasm-visible length of the buffer has been increased so it must be the
947 : // last fallible operation.
948 :
949 : // byteLength can be at most INT32_MAX. Note: if this hard limit changes,
950 : // update the clamping behavior in wasm::DecodeMemoryLimits and remove this
951 : // comment as well as the one in wasmMovingGrowToSize.
952 0 : if (newSize > INT32_MAX)
953 0 : return false;
954 :
955 0 : newBuf.set(ArrayBufferObject::createEmpty(cx));
956 0 : if (!newBuf) {
957 0 : cx->clearPendingException();
958 0 : return false;
959 : }
960 :
961 0 : if (!oldBuf->contents().wasmBuffer()->growToSizeInPlace(oldBuf->byteLength(), newSize))
962 0 : return false;
963 :
964 0 : bool hasStealableContents = true;
965 0 : BufferContents contents = ArrayBufferObject::stealContents(cx, oldBuf, hasStealableContents);
966 0 : MOZ_ASSERT(contents);
967 0 : newBuf->initialize(newSize, contents, OwnsData);
968 0 : return true;
969 : }
970 :
971 : #ifndef WASM_HUGE_MEMORY
972 : /* static */ bool
973 : ArrayBufferObject::wasmMovingGrowToSize(uint32_t newSize,
974 : HandleArrayBufferObject oldBuf,
975 : MutableHandleArrayBufferObject newBuf,
976 : JSContext* cx)
977 : {
978 : // On failure, do not throw and ensure that the original buffer is
979 : // unmodified and valid.
980 :
981 : // byteLength can be at most INT32_MAX.
982 : // See comment in wasmGrowToSizeInPlace about wasm::DecodeMemoryLimits.
983 : if (newSize > INT32_MAX)
984 : return false;
985 :
986 : if (newSize <= oldBuf->wasmBoundsCheckLimit() ||
987 : oldBuf->contents().wasmBuffer()->extendMappedSize(newSize))
988 : {
989 : return wasmGrowToSizeInPlace(newSize, oldBuf, newBuf, cx);
990 : }
991 :
992 : newBuf.set(ArrayBufferObject::createEmpty(cx));
993 : if (!newBuf) {
994 : cx->clearPendingException();
995 : return false;
996 : }
997 :
998 : WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::Allocate(newSize, Nothing());
999 : if (!newRawBuf)
1000 : return false;
1001 : BufferContents contents = BufferContents::create<WASM>(newRawBuf->dataPointer());
1002 : newBuf->initialize(newSize, contents, OwnsData);
1003 :
1004 : memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
1005 : ArrayBufferObject::detach(cx, oldBuf, BufferContents::createPlain(nullptr));
1006 : return true;
1007 : }
1008 :
1009 : uint32_t
1010 : ArrayBufferObject::wasmBoundsCheckLimit() const
1011 : {
1012 : if (isWasm())
1013 : return contents().wasmBuffer()->boundsCheckLimit();
1014 : else
1015 : return byteLength();
1016 : }
1017 :
1018 : uint32_t
1019 : ArrayBufferObjectMaybeShared::wasmBoundsCheckLimit() const
1020 : {
1021 : if (is<ArrayBufferObject>())
1022 : return as<ArrayBufferObject>().wasmBoundsCheckLimit();
1023 :
1024 : return as<SharedArrayBufferObject>().byteLength();
1025 : }
1026 : #endif
1027 :
1028 : uint32_t
1029 0 : ArrayBufferObject::flags() const
1030 : {
1031 0 : return uint32_t(getSlot(FLAGS_SLOT).toInt32());
1032 : }
1033 :
1034 : void
1035 0 : ArrayBufferObject::setFlags(uint32_t flags)
1036 : {
1037 0 : setSlot(FLAGS_SLOT, Int32Value(flags));
1038 0 : }
1039 :
1040 : ArrayBufferObject*
1041 0 : ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents,
1042 : OwnsState ownsState /* = OwnsData */,
1043 : HandleObject proto /* = nullptr */,
1044 : NewObjectKind newKind /* = GenericObject */)
1045 : {
1046 0 : MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
1047 :
1048 : // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
1049 : // Refuse to allocate too large buffers, currently limited to ~2 GiB.
1050 0 : if (nbytes > INT32_MAX) {
1051 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
1052 0 : return nullptr;
1053 : }
1054 :
1055 : // If we need to allocate data, try to use a larger object size class so
1056 : // that the array buffer's data can be allocated inline with the object.
1057 : // The extra space will be left unused by the object's fixed slots and
1058 : // available for the buffer's data, see NewObject().
1059 0 : size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
1060 :
1061 0 : size_t nslots = reservedSlots;
1062 0 : bool allocated = false;
1063 0 : if (contents) {
1064 0 : if (ownsState == OwnsData) {
1065 : // The ABO is taking ownership, so account the bytes against the zone.
1066 0 : size_t nAllocated = nbytes;
1067 0 : if (contents.kind() == MAPPED)
1068 0 : nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
1069 0 : else if (contents.kind() == WASM)
1070 0 : nAllocated = contents.wasmBuffer()->allocatedBytes();
1071 0 : cx->zone()->updateMallocCounter(nAllocated);
1072 : }
1073 : } else {
1074 0 : MOZ_ASSERT(ownsState == OwnsData);
1075 0 : size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
1076 0 : if (nbytes <= usableSlots * sizeof(Value)) {
1077 0 : int newSlots = JS_HOWMANY(nbytes, sizeof(Value));
1078 0 : MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
1079 0 : nslots = reservedSlots + newSlots;
1080 0 : contents = BufferContents::createPlain(nullptr);
1081 : } else {
1082 0 : contents = AllocateArrayBufferContents(cx, nbytes);
1083 0 : if (!contents)
1084 0 : return nullptr;
1085 0 : allocated = true;
1086 : }
1087 : }
1088 :
1089 0 : MOZ_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
1090 0 : gc::AllocKind allocKind = GetGCObjectKind(nslots);
1091 :
1092 0 : AutoSetNewObjectMetadata metadata(cx);
1093 : Rooted<ArrayBufferObject*> obj(cx,
1094 0 : NewObjectWithClassProto<ArrayBufferObject>(cx, proto, allocKind, newKind));
1095 0 : if (!obj) {
1096 0 : if (allocated)
1097 0 : js_free(contents.data());
1098 0 : return nullptr;
1099 : }
1100 :
1101 0 : MOZ_ASSERT(obj->getClass() == &class_);
1102 0 : MOZ_ASSERT(!gc::IsInsideNursery(obj));
1103 :
1104 0 : if (!contents) {
1105 0 : void* data = obj->inlineDataPointer();
1106 0 : memset(data, 0, nbytes);
1107 0 : obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
1108 : } else {
1109 0 : obj->initialize(nbytes, contents, ownsState);
1110 : }
1111 :
1112 0 : return obj;
1113 : }
1114 :
1115 : ArrayBufferObject*
1116 0 : ArrayBufferObject::create(JSContext* cx, uint32_t nbytes,
1117 : HandleObject proto /* = nullptr */,
1118 : NewObjectKind newKind /* = GenericObject */)
1119 : {
1120 0 : return create(cx, nbytes, BufferContents::createPlain(nullptr),
1121 0 : OwnsState::OwnsData, proto);
1122 : }
1123 :
1124 : ArrayBufferObject*
1125 0 : ArrayBufferObject::createEmpty(JSContext* cx)
1126 : {
1127 0 : AutoSetNewObjectMetadata metadata(cx);
1128 0 : ArrayBufferObject* obj = NewObjectWithClassProto<ArrayBufferObject>(cx, nullptr);
1129 0 : if (!obj)
1130 0 : return nullptr;
1131 :
1132 0 : obj->initEmpty();
1133 0 : return obj;
1134 : }
1135 :
1136 : /* static */ ArrayBufferObject::BufferContents
1137 0 : ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
1138 : bool hasStealableContents)
1139 : {
1140 0 : MOZ_ASSERT(buffer->isPlain(), "Only support doing this on plain ABOs");
1141 0 : MOZ_ASSERT(!buffer->isDetached(), "must have contents to externalize");
1142 0 : MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents());
1143 :
1144 0 : BufferContents contents(buffer->dataPointer(), buffer->bufferKind());
1145 :
1146 0 : if (hasStealableContents) {
1147 0 : buffer->setOwnsData(DoesntOwnData);
1148 0 : return contents;
1149 : }
1150 :
1151 : // Create a new chunk of memory to return since we cannot steal the
1152 : // existing contents away from the buffer.
1153 0 : BufferContents newContents = AllocateArrayBufferContents(cx, buffer->byteLength());
1154 0 : if (!newContents)
1155 0 : return BufferContents::createPlain(nullptr);
1156 0 : memcpy(newContents.data(), contents.data(), buffer->byteLength());
1157 0 : buffer->changeContents(cx, newContents, DoesntOwnData);
1158 :
1159 0 : return newContents;
1160 : }
1161 :
1162 : /* static */ ArrayBufferObject::BufferContents
1163 0 : ArrayBufferObject::stealContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
1164 : bool hasStealableContents)
1165 : {
1166 : // While wasm buffers cannot generally be transferred by content, the
1167 : // stealContents() is used internally by the impl of memory growth.
1168 0 : MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents() ||
1169 : (buffer->isWasm() && !buffer->isPreparedForAsmJS()));
1170 0 : assertSameCompartment(cx, buffer);
1171 :
1172 0 : BufferContents oldContents(buffer->dataPointer(), buffer->bufferKind());
1173 :
1174 0 : if (hasStealableContents) {
1175 : // Return the old contents and reset the detached buffer's data
1176 : // pointer. This pointer should never be accessed.
1177 0 : auto newContents = BufferContents::createPlain(nullptr);
1178 0 : buffer->setOwnsData(DoesntOwnData); // Do not free the stolen data.
1179 0 : ArrayBufferObject::detach(cx, buffer, newContents);
1180 0 : buffer->setOwnsData(DoesntOwnData); // Do not free the nullptr.
1181 0 : return oldContents;
1182 : }
1183 :
1184 : // Create a new chunk of memory to return since we cannot steal the
1185 : // existing contents away from the buffer.
1186 0 : BufferContents contentsCopy = AllocateArrayBufferContents(cx, buffer->byteLength());
1187 0 : if (!contentsCopy)
1188 0 : return BufferContents::createPlain(nullptr);
1189 :
1190 0 : if (buffer->byteLength() > 0)
1191 0 : memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
1192 0 : ArrayBufferObject::detach(cx, buffer, oldContents);
1193 0 : return contentsCopy;
1194 : }
1195 :
1196 : /* static */ void
1197 0 : ArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
1198 : JS::ClassInfo* info)
1199 : {
1200 0 : ArrayBufferObject& buffer = AsArrayBuffer(obj);
1201 :
1202 0 : if (!buffer.ownsData())
1203 0 : return;
1204 :
1205 0 : switch (buffer.bufferKind()) {
1206 : case PLAIN:
1207 0 : if (buffer.isPreparedForAsmJS())
1208 0 : info->objectsMallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
1209 : else
1210 0 : info->objectsMallocHeapElementsNormal += mallocSizeOf(buffer.dataPointer());
1211 0 : break;
1212 : case MAPPED:
1213 0 : info->objectsNonHeapElementsNormal += buffer.byteLength();
1214 0 : break;
1215 : case WASM:
1216 0 : info->objectsNonHeapElementsWasm += buffer.byteLength();
1217 0 : MOZ_ASSERT(buffer.wasmMappedSize() >= buffer.byteLength());
1218 0 : info->wasmGuardPages += buffer.wasmMappedSize() - buffer.byteLength();
1219 0 : break;
1220 : case KIND_MASK:
1221 0 : MOZ_CRASH("bad bufferKind()");
1222 : }
1223 : }
1224 :
1225 : /* static */ void
1226 0 : ArrayBufferObject::finalize(FreeOp* fop, JSObject* obj)
1227 : {
1228 0 : ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
1229 :
1230 0 : if (buffer.ownsData())
1231 0 : buffer.releaseData(fop);
1232 0 : }
1233 :
1234 : /* static */ void
1235 0 : ArrayBufferObject::copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
1236 : Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
1237 : uint32_t count)
1238 : {
1239 0 : MOZ_ASSERT(toBuffer->byteLength() >= count);
1240 0 : MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
1241 0 : MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
1242 0 : MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
1243 :
1244 0 : memcpy(toBuffer->dataPointer() + toIndex, fromBuffer->dataPointer() + fromIndex, count);
1245 0 : }
1246 :
1247 : /* static */ void
1248 0 : ArrayBufferObject::trace(JSTracer* trc, JSObject* obj)
1249 : {
1250 : // If this buffer is associated with an inline typed object,
1251 : // fix up the data pointer if the typed object was moved.
1252 0 : ArrayBufferObject& buf = obj->as<ArrayBufferObject>();
1253 :
1254 0 : if (!buf.forInlineTypedObject())
1255 0 : return;
1256 :
1257 0 : JSObject* view = MaybeForwarded(buf.firstView());
1258 0 : MOZ_ASSERT(view && view->is<InlineTransparentTypedObject>());
1259 :
1260 0 : TraceManuallyBarrieredEdge(trc, &view, "array buffer inline typed object owner");
1261 0 : buf.setSlot(DATA_SLOT, PrivateValue(view->as<InlineTransparentTypedObject>().inlineTypedMem()));
1262 : }
1263 :
1264 : /* static */ void
1265 0 : ArrayBufferObject::objectMoved(JSObject* obj, const JSObject* old)
1266 : {
1267 0 : ArrayBufferObject& dst = obj->as<ArrayBufferObject>();
1268 0 : const ArrayBufferObject& src = old->as<ArrayBufferObject>();
1269 :
1270 : // Fix up possible inline data pointer.
1271 0 : if (src.hasInlineData())
1272 0 : dst.setSlot(DATA_SLOT, PrivateValue(dst.inlineDataPointer()));
1273 0 : }
1274 :
1275 : ArrayBufferViewObject*
1276 0 : ArrayBufferObject::firstView()
1277 : {
1278 0 : return getSlot(FIRST_VIEW_SLOT).isObject()
1279 0 : ? static_cast<ArrayBufferViewObject*>(&getSlot(FIRST_VIEW_SLOT).toObject())
1280 0 : : nullptr;
1281 : }
1282 :
1283 : void
1284 0 : ArrayBufferObject::setFirstView(ArrayBufferViewObject* view)
1285 : {
1286 0 : setSlot(FIRST_VIEW_SLOT, ObjectOrNullValue(view));
1287 0 : }
1288 :
1289 : bool
1290 0 : ArrayBufferObject::addView(JSContext* cx, JSObject* viewArg)
1291 : {
1292 : // Note: we don't pass in an ArrayBufferViewObject as the argument due to
1293 : // tricky inheritance in the various view classes. View classes do not
1294 : // inherit from ArrayBufferViewObject so won't be upcast automatically.
1295 0 : MOZ_ASSERT(viewArg->is<ArrayBufferViewObject>() || viewArg->is<TypedObject>());
1296 0 : ArrayBufferViewObject* view = static_cast<ArrayBufferViewObject*>(viewArg);
1297 :
1298 0 : if (!firstView()) {
1299 0 : setFirstView(view);
1300 0 : return true;
1301 : }
1302 0 : return cx->compartment()->innerViews.get().addView(cx, this, view);
1303 : }
1304 :
1305 : /*
1306 : * InnerViewTable
1307 : */
1308 :
1309 : static size_t VIEW_LIST_MAX_LENGTH = 500;
1310 :
1311 : bool
1312 0 : InnerViewTable::addView(JSContext* cx, ArrayBufferObject* buffer, ArrayBufferViewObject* view)
1313 : {
1314 : // ArrayBufferObject entries are only added when there are multiple views.
1315 0 : MOZ_ASSERT(buffer->firstView());
1316 :
1317 0 : if (!map.initialized() && !map.init()) {
1318 0 : ReportOutOfMemory(cx);
1319 0 : return false;
1320 : }
1321 :
1322 0 : Map::AddPtr p = map.lookupForAdd(buffer);
1323 :
1324 0 : MOZ_ASSERT(!gc::IsInsideNursery(buffer));
1325 0 : bool addToNursery = nurseryKeysValid && gc::IsInsideNursery(view);
1326 :
1327 0 : if (p) {
1328 0 : ViewVector& views = p->value();
1329 0 : MOZ_ASSERT(!views.empty());
1330 :
1331 0 : if (addToNursery) {
1332 : // Only add the entry to |nurseryKeys| if it isn't already there.
1333 0 : if (views.length() >= VIEW_LIST_MAX_LENGTH) {
1334 : // To avoid quadratic blowup, skip the loop below if we end up
1335 : // adding enormous numbers of views for the same object.
1336 0 : nurseryKeysValid = false;
1337 : } else {
1338 0 : for (size_t i = 0; i < views.length(); i++) {
1339 0 : if (gc::IsInsideNursery(views[i])) {
1340 0 : addToNursery = false;
1341 0 : break;
1342 : }
1343 : }
1344 : }
1345 : }
1346 :
1347 0 : if (!views.append(view)) {
1348 0 : ReportOutOfMemory(cx);
1349 0 : return false;
1350 : }
1351 : } else {
1352 0 : if (!map.add(p, buffer, ViewVector())) {
1353 0 : ReportOutOfMemory(cx);
1354 0 : return false;
1355 : }
1356 : // ViewVector has one inline element, so the first insertion is
1357 : // guaranteed to succeed.
1358 0 : MOZ_ALWAYS_TRUE(p->value().append(view));
1359 : }
1360 :
1361 0 : if (addToNursery && !nurseryKeys.append(buffer))
1362 0 : nurseryKeysValid = false;
1363 :
1364 0 : return true;
1365 : }
1366 :
1367 : InnerViewTable::ViewVector*
1368 0 : InnerViewTable::maybeViewsUnbarriered(ArrayBufferObject* buffer)
1369 : {
1370 0 : if (!map.initialized())
1371 0 : return nullptr;
1372 :
1373 0 : Map::Ptr p = map.lookup(buffer);
1374 0 : if (p)
1375 0 : return &p->value();
1376 0 : return nullptr;
1377 : }
1378 :
1379 : void
1380 0 : InnerViewTable::removeViews(ArrayBufferObject* buffer)
1381 : {
1382 0 : Map::Ptr p = map.lookup(buffer);
1383 0 : MOZ_ASSERT(p);
1384 :
1385 0 : map.remove(p);
1386 0 : }
1387 :
1388 : /* static */ bool
1389 0 : InnerViewTable::sweepEntry(JSObject** pkey, ViewVector& views)
1390 : {
1391 0 : if (IsAboutToBeFinalizedUnbarriered(pkey))
1392 0 : return true;
1393 :
1394 0 : MOZ_ASSERT(!views.empty());
1395 0 : for (size_t i = 0; i < views.length(); i++) {
1396 0 : if (IsAboutToBeFinalizedUnbarriered(&views[i])) {
1397 0 : views[i--] = views.back();
1398 0 : views.popBack();
1399 : }
1400 : }
1401 :
1402 0 : return views.empty();
1403 : }
1404 :
1405 : void
1406 0 : InnerViewTable::sweep()
1407 : {
1408 0 : MOZ_ASSERT(nurseryKeys.empty());
1409 0 : map.sweep();
1410 0 : }
1411 :
1412 : void
1413 0 : InnerViewTable::sweepAfterMinorGC()
1414 : {
1415 0 : MOZ_ASSERT(needsSweepAfterMinorGC());
1416 :
1417 0 : if (nurseryKeysValid) {
1418 0 : for (size_t i = 0; i < nurseryKeys.length(); i++) {
1419 0 : JSObject* buffer = MaybeForwarded(nurseryKeys[i]);
1420 0 : Map::Ptr p = map.lookup(buffer);
1421 0 : if (!p)
1422 0 : continue;
1423 :
1424 0 : if (sweepEntry(&p->mutableKey(), p->value()))
1425 0 : map.remove(buffer);
1426 : }
1427 0 : nurseryKeys.clear();
1428 : } else {
1429 : // Do the required sweeping by looking at every map entry.
1430 0 : nurseryKeys.clear();
1431 0 : sweep();
1432 :
1433 0 : nurseryKeysValid = true;
1434 : }
1435 0 : }
1436 :
1437 : size_t
1438 0 : InnerViewTable::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
1439 : {
1440 0 : if (!map.initialized())
1441 0 : return 0;
1442 :
1443 0 : size_t vectorSize = 0;
1444 0 : for (Map::Enum e(map); !e.empty(); e.popFront())
1445 0 : vectorSize += e.front().value().sizeOfExcludingThis(mallocSizeOf);
1446 :
1447 : return vectorSize
1448 0 : + map.sizeOfExcludingThis(mallocSizeOf)
1449 0 : + nurseryKeys.sizeOfExcludingThis(mallocSizeOf);
1450 : }
1451 :
1452 : /*
1453 : * ArrayBufferViewObject
1454 : */
1455 :
1456 : /*
1457 : * This method is used to trace TypedArrayObjects and DataViewObjects. We need
1458 : * a custom tracer to move the object's data pointer if its owner was moved and
1459 : * stores its data inline.
1460 : */
1461 : /* static */ void
1462 0 : ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
1463 : {
1464 0 : NativeObject* obj = &objArg->as<NativeObject>();
1465 0 : HeapSlot& bufSlot = obj->getFixedSlotRef(TypedArrayObject::BUFFER_SLOT);
1466 0 : TraceEdge(trc, &bufSlot, "typedarray.buffer");
1467 :
1468 : // Update obj's data pointer if it moved.
1469 0 : if (bufSlot.isObject()) {
1470 0 : if (IsArrayBuffer(&bufSlot.toObject())) {
1471 0 : ArrayBufferObject& buf = AsArrayBuffer(MaybeForwarded(&bufSlot.toObject()));
1472 0 : uint32_t offset = uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32());
1473 0 : MOZ_ASSERT(offset <= INT32_MAX);
1474 :
1475 0 : if (buf.forInlineTypedObject()) {
1476 0 : MOZ_ASSERT(buf.dataPointer() != nullptr);
1477 :
1478 : // The data is inline with an InlineTypedObject associated with the
1479 : // buffer. Get a new address for the typed object if it moved.
1480 0 : JSObject* view = buf.firstView();
1481 :
1482 : // Mark the object to move it into the tenured space.
1483 0 : TraceManuallyBarrieredEdge(trc, &view, "typed array nursery owner");
1484 0 : MOZ_ASSERT(view->is<InlineTypedObject>());
1485 0 : MOZ_ASSERT(view != obj);
1486 :
1487 0 : void* srcData = obj->getPrivate();
1488 0 : void* dstData = view->as<InlineTypedObject>().inlineTypedMemForGC() + offset;
1489 0 : obj->setPrivateUnbarriered(dstData);
1490 :
1491 : // We can't use a direct forwarding pointer here, as there might
1492 : // not be enough bytes available, and other views might have data
1493 : // pointers whose forwarding pointers would overlap this one.
1494 0 : if (trc->isTenuringTracer()) {
1495 0 : Nursery& nursery = obj->zoneFromAnyThread()->group()->nursery();
1496 0 : nursery.maybeSetForwardingPointer(trc, srcData, dstData, /* direct = */ false);
1497 : }
1498 : } else {
1499 0 : MOZ_ASSERT_IF(buf.dataPointer() == nullptr, offset == 0);
1500 :
1501 : // The data may or may not be inline with the buffer. The buffer
1502 : // can only move during a compacting GC, in which case its
1503 : // objectMoved hook has already updated the buffer's data pointer.
1504 0 : obj->initPrivate(buf.dataPointer() + offset);
1505 : }
1506 : }
1507 : }
1508 0 : }
1509 :
1510 : template <>
1511 : bool
1512 0 : JSObject::is<js::ArrayBufferViewObject>() const
1513 : {
1514 0 : return is<DataViewObject>() || is<TypedArrayObject>();
1515 : }
1516 :
1517 : template <>
1518 : bool
1519 0 : JSObject::is<js::ArrayBufferObjectMaybeShared>() const
1520 : {
1521 0 : return is<ArrayBufferObject>() || is<SharedArrayBufferObject>();
1522 : }
1523 :
1524 : void
1525 0 : ArrayBufferViewObject::notifyBufferDetached(JSContext* cx, void* newData)
1526 : {
1527 0 : if (is<DataViewObject>()) {
1528 0 : if (as<DataViewObject>().isSharedMemory())
1529 0 : return;
1530 0 : as<DataViewObject>().notifyBufferDetached(newData);
1531 0 : } else if (is<TypedArrayObject>()) {
1532 0 : if (as<TypedArrayObject>().isSharedMemory())
1533 0 : return;
1534 0 : as<TypedArrayObject>().notifyBufferDetached(cx, newData);
1535 : } else {
1536 0 : as<OutlineTypedObject>().notifyBufferDetached(newData);
1537 : }
1538 : }
1539 :
1540 : uint8_t*
1541 0 : ArrayBufferViewObject::dataPointerUnshared(const JS::AutoRequireNoGC& nogc)
1542 : {
1543 0 : if (is<DataViewObject>()) {
1544 0 : MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());
1545 0 : return static_cast<uint8_t*>(as<DataViewObject>().dataPointerUnshared());
1546 : }
1547 0 : if (is<TypedArrayObject>()) {
1548 0 : MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
1549 0 : return static_cast<uint8_t*>(as<TypedArrayObject>().viewDataUnshared());
1550 : }
1551 0 : return as<TypedObject>().typedMem(nogc);
1552 : }
1553 :
1554 : #ifdef DEBUG
1555 : bool
1556 0 : ArrayBufferViewObject::isSharedMemory()
1557 : {
1558 0 : if (is<TypedArrayObject>())
1559 0 : return as<TypedArrayObject>().isSharedMemory();
1560 0 : return false;
1561 : }
1562 : #endif
1563 :
1564 : void
1565 0 : ArrayBufferViewObject::setDataPointerUnshared(uint8_t* data)
1566 : {
1567 0 : if (is<DataViewObject>()) {
1568 0 : MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());
1569 0 : as<DataViewObject>().setPrivate(data);
1570 0 : } else if (is<TypedArrayObject>()) {
1571 0 : MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
1572 0 : as<TypedArrayObject>().setPrivate(data);
1573 0 : } else if (is<OutlineTypedObject>()) {
1574 0 : as<OutlineTypedObject>().setData(data);
1575 : } else {
1576 0 : MOZ_CRASH();
1577 : }
1578 0 : }
1579 :
1580 : /* static */ ArrayBufferObjectMaybeShared*
1581 0 : ArrayBufferViewObject::bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*> thisObject)
1582 : {
1583 0 : if (thisObject->is<TypedArrayObject>()) {
1584 0 : Rooted<TypedArrayObject*> typedArray(cx, &thisObject->as<TypedArrayObject>());
1585 0 : if (!TypedArrayObject::ensureHasBuffer(cx, typedArray))
1586 0 : return nullptr;
1587 0 : return thisObject->as<TypedArrayObject>().bufferEither();
1588 : }
1589 0 : MOZ_ASSERT(thisObject->is<DataViewObject>());
1590 0 : return &thisObject->as<DataViewObject>().arrayBufferEither();
1591 : }
1592 :
1593 : /* JS Friend API */
1594 :
1595 : JS_FRIEND_API(bool)
1596 0 : JS_IsArrayBufferViewObject(JSObject* obj)
1597 : {
1598 0 : obj = CheckedUnwrap(obj);
1599 0 : return obj && obj->is<ArrayBufferViewObject>();
1600 : }
1601 :
1602 : JS_FRIEND_API(JSObject*)
1603 0 : js::UnwrapArrayBufferView(JSObject* obj)
1604 : {
1605 0 : if (JSObject* unwrapped = CheckedUnwrap(obj))
1606 0 : return unwrapped->is<ArrayBufferViewObject>() ? unwrapped : nullptr;
1607 0 : return nullptr;
1608 : }
1609 :
1610 : JS_FRIEND_API(uint32_t)
1611 0 : JS_GetArrayBufferByteLength(JSObject* obj)
1612 : {
1613 0 : obj = CheckedUnwrap(obj);
1614 0 : return obj ? AsArrayBuffer(obj).byteLength() : 0;
1615 : }
1616 :
1617 : JS_FRIEND_API(uint8_t*)
1618 0 : JS_GetArrayBufferData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
1619 : {
1620 0 : obj = CheckedUnwrap(obj);
1621 0 : if (!obj)
1622 0 : return nullptr;
1623 0 : if (!IsArrayBuffer(obj))
1624 0 : return nullptr;
1625 0 : *isSharedMemory = false;
1626 0 : return AsArrayBuffer(obj).dataPointer();
1627 : }
1628 :
1629 : JS_FRIEND_API(bool)
1630 0 : JS_DetachArrayBuffer(JSContext* cx, HandleObject obj)
1631 : {
1632 0 : AssertHeapIsIdle();
1633 0 : CHECK_REQUEST(cx);
1634 0 : assertSameCompartment(cx, obj);
1635 :
1636 0 : if (!obj->is<ArrayBufferObject>()) {
1637 0 : JS_ReportErrorASCII(cx, "ArrayBuffer object required");
1638 0 : return false;
1639 : }
1640 :
1641 0 : Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
1642 :
1643 0 : if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
1644 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_TRANSFER);
1645 0 : return false;
1646 : }
1647 :
1648 : ArrayBufferObject::BufferContents newContents =
1649 0 : buffer->hasStealableContents() ? ArrayBufferObject::BufferContents::createPlain(nullptr)
1650 0 : : buffer->contents();
1651 :
1652 0 : ArrayBufferObject::detach(cx, buffer, newContents);
1653 :
1654 0 : return true;
1655 : }
1656 :
1657 : JS_FRIEND_API(bool)
1658 0 : JS_IsDetachedArrayBufferObject(JSObject* obj)
1659 : {
1660 0 : obj = CheckedUnwrap(obj);
1661 0 : if (!obj)
1662 0 : return false;
1663 :
1664 0 : return obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().isDetached();
1665 : }
1666 :
1667 : JS_FRIEND_API(JSObject*)
1668 0 : JS_NewArrayBuffer(JSContext* cx, uint32_t nbytes)
1669 : {
1670 0 : AssertHeapIsIdle();
1671 0 : CHECK_REQUEST(cx);
1672 0 : MOZ_ASSERT(nbytes <= INT32_MAX);
1673 0 : return ArrayBufferObject::create(cx, nbytes);
1674 : }
1675 :
1676 : JS_PUBLIC_API(JSObject*)
1677 0 : JS_NewArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data)
1678 : {
1679 0 : AssertHeapIsIdle();
1680 0 : CHECK_REQUEST(cx);
1681 0 : MOZ_ASSERT_IF(!data, nbytes == 0);
1682 : ArrayBufferObject::BufferContents contents =
1683 0 : ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);
1684 0 : return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData,
1685 0 : /* proto = */ nullptr, TenuredObject);
1686 : }
1687 :
1688 : JS_PUBLIC_API(JSObject*)
1689 0 : JS_NewArrayBufferWithExternalContents(JSContext* cx, size_t nbytes, void* data)
1690 : {
1691 0 : AssertHeapIsIdle();
1692 0 : CHECK_REQUEST(cx);
1693 0 : MOZ_ASSERT_IF(!data, nbytes == 0);
1694 : ArrayBufferObject::BufferContents contents =
1695 0 : ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);
1696 0 : return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData,
1697 0 : /* proto = */ nullptr, TenuredObject);
1698 : }
1699 :
1700 : JS_FRIEND_API(bool)
1701 138 : JS_IsArrayBufferObject(JSObject* obj)
1702 : {
1703 138 : obj = CheckedUnwrap(obj);
1704 138 : return obj && obj->is<ArrayBufferObject>();
1705 : }
1706 :
1707 : JS_FRIEND_API(bool)
1708 0 : JS_ArrayBufferHasData(JSObject* obj)
1709 : {
1710 0 : return CheckedUnwrap(obj)->as<ArrayBufferObject>().hasData();
1711 : }
1712 :
1713 : JS_FRIEND_API(JSObject*)
1714 0 : js::UnwrapArrayBuffer(JSObject* obj)
1715 : {
1716 0 : if (JSObject* unwrapped = CheckedUnwrap(obj))
1717 0 : return unwrapped->is<ArrayBufferObject>() ? unwrapped : nullptr;
1718 0 : return nullptr;
1719 : }
1720 :
1721 : JS_FRIEND_API(JSObject*)
1722 0 : js::UnwrapSharedArrayBuffer(JSObject* obj)
1723 : {
1724 0 : if (JSObject* unwrapped = CheckedUnwrap(obj))
1725 0 : return unwrapped->is<SharedArrayBufferObject>() ? unwrapped : nullptr;
1726 0 : return nullptr;
1727 : }
1728 :
1729 : JS_PUBLIC_API(void*)
1730 0 : JS_ExternalizeArrayBufferContents(JSContext* cx, HandleObject obj)
1731 : {
1732 0 : AssertHeapIsIdle();
1733 0 : CHECK_REQUEST(cx);
1734 0 : assertSameCompartment(cx, obj);
1735 :
1736 0 : if (!obj->is<ArrayBufferObject>()) {
1737 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
1738 0 : return nullptr;
1739 : }
1740 :
1741 0 : Handle<ArrayBufferObject*> buffer = obj.as<ArrayBufferObject>();
1742 0 : if (!buffer->isPlain()) {
1743 : // This operation isn't supported on mapped or wsm ArrayBufferObjects.
1744 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
1745 0 : return nullptr;
1746 : }
1747 0 : if (buffer->isDetached()) {
1748 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1749 0 : return nullptr;
1750 : }
1751 :
1752 : // The caller assumes that a plain malloc'd buffer is returned.
1753 : // hasStealableContents is true for mapped buffers, so we must additionally
1754 : // require that the buffer is plain. In the future, we could consider
1755 : // returning something that handles releasing the memory.
1756 0 : bool hasStealableContents = buffer->hasStealableContents();
1757 :
1758 0 : return ArrayBufferObject::externalizeContents(cx, buffer, hasStealableContents).data();
1759 : }
1760 :
1761 : JS_PUBLIC_API(void*)
1762 0 : JS_StealArrayBufferContents(JSContext* cx, HandleObject objArg)
1763 : {
1764 0 : AssertHeapIsIdle();
1765 0 : CHECK_REQUEST(cx);
1766 0 : assertSameCompartment(cx, objArg);
1767 :
1768 0 : JSObject* obj = CheckedUnwrap(objArg);
1769 0 : if (!obj)
1770 0 : return nullptr;
1771 :
1772 0 : if (!obj->is<ArrayBufferObject>()) {
1773 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
1774 0 : return nullptr;
1775 : }
1776 :
1777 0 : Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
1778 0 : if (buffer->isDetached()) {
1779 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1780 0 : return nullptr;
1781 : }
1782 :
1783 0 : if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
1784 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_TRANSFER);
1785 0 : return nullptr;
1786 : }
1787 :
1788 : // The caller assumes that a plain malloc'd buffer is returned.
1789 : // hasStealableContents is true for mapped buffers, so we must additionally
1790 : // require that the buffer is plain. In the future, we could consider
1791 : // returning something that handles releasing the memory.
1792 0 : bool hasStealableContents = buffer->hasStealableContents() && buffer->isPlain();
1793 :
1794 0 : AutoCompartment ac(cx, buffer);
1795 0 : return ArrayBufferObject::stealContents(cx, buffer, hasStealableContents).data();
1796 : }
1797 :
1798 : JS_PUBLIC_API(JSObject*)
1799 0 : JS_NewMappedArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data)
1800 : {
1801 0 : AssertHeapIsIdle();
1802 0 : CHECK_REQUEST(cx);
1803 :
1804 0 : MOZ_ASSERT(data);
1805 : ArrayBufferObject::BufferContents contents =
1806 0 : ArrayBufferObject::BufferContents::create<ArrayBufferObject::MAPPED>(data);
1807 0 : return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData,
1808 0 : /* proto = */ nullptr, TenuredObject);
1809 : }
1810 :
1811 : JS_PUBLIC_API(void*)
1812 0 : JS_CreateMappedArrayBufferContents(int fd, size_t offset, size_t length)
1813 : {
1814 0 : return ArrayBufferObject::createMappedContents(fd, offset, length).data();
1815 : }
1816 :
1817 : JS_PUBLIC_API(void)
1818 0 : JS_ReleaseMappedArrayBufferContents(void* contents, size_t length)
1819 : {
1820 0 : MemProfiler::RemoveNative(contents);
1821 0 : DeallocateMappedContent(contents, length);
1822 0 : }
1823 :
1824 : JS_FRIEND_API(bool)
1825 0 : JS_IsMappedArrayBufferObject(JSObject* obj)
1826 : {
1827 0 : obj = CheckedUnwrap(obj);
1828 0 : if (!obj)
1829 0 : return false;
1830 :
1831 0 : return obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().isMapped();
1832 : }
1833 :
1834 : JS_FRIEND_API(void*)
1835 0 : JS_GetArrayBufferViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
1836 : {
1837 0 : obj = CheckedUnwrap(obj);
1838 0 : if (!obj)
1839 0 : return nullptr;
1840 0 : if (obj->is<DataViewObject>()) {
1841 0 : DataViewObject& dv = obj->as<DataViewObject>();
1842 0 : *isSharedMemory = dv.isSharedMemory();
1843 0 : return dv.dataPointerEither().unwrap(/*safe - caller sees isSharedMemory flag*/);
1844 : }
1845 0 : TypedArrayObject& ta = obj->as<TypedArrayObject>();
1846 0 : *isSharedMemory = ta.isSharedMemory();
1847 0 : return ta.viewDataEither().unwrap(/*safe - caller sees isSharedMemory flag*/);
1848 : }
1849 :
1850 : JS_FRIEND_API(JSObject*)
1851 0 : JS_GetArrayBufferViewBuffer(JSContext* cx, HandleObject objArg, bool* isSharedMemory)
1852 : {
1853 0 : AssertHeapIsIdle();
1854 0 : CHECK_REQUEST(cx);
1855 0 : assertSameCompartment(cx, objArg);
1856 :
1857 0 : JSObject* obj = CheckedUnwrap(objArg);
1858 0 : if (!obj)
1859 0 : return nullptr;
1860 0 : MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
1861 :
1862 0 : Rooted<ArrayBufferViewObject*> viewObject(cx, static_cast<ArrayBufferViewObject*>(obj));
1863 0 : ArrayBufferObjectMaybeShared* buffer = ArrayBufferViewObject::bufferObject(cx, viewObject);
1864 0 : *isSharedMemory = buffer->is<SharedArrayBufferObject>();
1865 0 : return buffer;
1866 : }
1867 :
1868 : JS_FRIEND_API(uint32_t)
1869 0 : JS_GetArrayBufferViewByteLength(JSObject* obj)
1870 : {
1871 0 : obj = CheckedUnwrap(obj);
1872 0 : if (!obj)
1873 0 : return 0;
1874 0 : return obj->is<DataViewObject>()
1875 0 : ? obj->as<DataViewObject>().byteLength()
1876 0 : : obj->as<TypedArrayObject>().byteLength();
1877 : }
1878 :
1879 : JS_FRIEND_API(JSObject*)
1880 0 : JS_GetObjectAsArrayBufferView(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
1881 : {
1882 0 : if (!(obj = CheckedUnwrap(obj)))
1883 0 : return nullptr;
1884 0 : if (!(obj->is<ArrayBufferViewObject>()))
1885 0 : return nullptr;
1886 :
1887 0 : js::GetArrayBufferViewLengthAndData(obj, length, isSharedMemory, data);
1888 0 : return obj;
1889 : }
1890 :
1891 : JS_FRIEND_API(void)
1892 0 : js::GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory,
1893 : uint8_t** data)
1894 : {
1895 0 : MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
1896 :
1897 0 : *length = obj->is<DataViewObject>()
1898 0 : ? obj->as<DataViewObject>().byteLength()
1899 0 : : obj->as<TypedArrayObject>().byteLength();
1900 :
1901 0 : if (obj->is<DataViewObject>()) {
1902 0 : DataViewObject& dv = obj->as<DataViewObject>();
1903 0 : *isSharedMemory = dv.isSharedMemory();
1904 0 : *data = static_cast<uint8_t*>(
1905 0 : dv.dataPointerEither().unwrap(/*safe - caller sees isShared flag*/));
1906 : }
1907 : else {
1908 0 : TypedArrayObject& ta = obj->as<TypedArrayObject>();
1909 0 : *isSharedMemory = ta.isSharedMemory();
1910 0 : *data = static_cast<uint8_t*>(
1911 0 : ta.viewDataEither().unwrap(/*safe - caller sees isShared flag*/));
1912 : }
1913 0 : }
1914 :
1915 : JS_FRIEND_API(JSObject*)
1916 0 : JS_GetObjectAsArrayBuffer(JSObject* obj, uint32_t* length, uint8_t** data)
1917 : {
1918 0 : if (!(obj = CheckedUnwrap(obj)))
1919 0 : return nullptr;
1920 0 : if (!IsArrayBuffer(obj))
1921 0 : return nullptr;
1922 :
1923 0 : *length = AsArrayBuffer(obj).byteLength();
1924 0 : *data = AsArrayBuffer(obj).dataPointer();
1925 :
1926 0 : return obj;
1927 : }
1928 :
1929 : JS_FRIEND_API(void)
1930 0 : js::GetArrayBufferLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
1931 : {
1932 0 : MOZ_ASSERT(IsArrayBuffer(obj));
1933 0 : *length = AsArrayBuffer(obj).byteLength();
1934 0 : *data = AsArrayBuffer(obj).dataPointer();
1935 0 : *isSharedMemory = false;
1936 0 : }
|