js/src/vm/ArrayBufferObject.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "vm/ArrayBufferObject.h"
michael@0 8
michael@0 9 #include "mozilla/Alignment.h"
michael@0 10 #include "mozilla/FloatingPoint.h"
michael@0 11 #include "mozilla/PodOperations.h"
michael@0 12
michael@0 13 #include <string.h>
michael@0 14 #ifndef XP_WIN
michael@0 15 # include <sys/mman.h>
michael@0 16 #endif
michael@0 17
michael@0 18 #ifdef MOZ_VALGRIND
michael@0 19 # include <valgrind/memcheck.h>
michael@0 20 #endif
michael@0 21
michael@0 22 #include "jsapi.h"
michael@0 23 #include "jsarray.h"
michael@0 24 #include "jscntxt.h"
michael@0 25 #include "jscpucfg.h"
michael@0 26 #include "jsnum.h"
michael@0 27 #include "jsobj.h"
michael@0 28 #include "jstypes.h"
michael@0 29 #include "jsutil.h"
michael@0 30 #ifdef XP_WIN
michael@0 31 # include "jswin.h"
michael@0 32 #endif
michael@0 33 #include "jswrapper.h"
michael@0 34
michael@0 35 #include "gc/Barrier.h"
michael@0 36 #include "gc/Marking.h"
michael@0 37 #include "gc/Memory.h"
michael@0 38 #include "jit/AsmJS.h"
michael@0 39 #include "jit/AsmJSModule.h"
michael@0 40 #include "js/MemoryMetrics.h"
michael@0 41 #include "vm/GlobalObject.h"
michael@0 42 #include "vm/Interpreter.h"
michael@0 43 #include "vm/NumericConversions.h"
michael@0 44 #include "vm/SharedArrayObject.h"
michael@0 45 #include "vm/WrapperObject.h"
michael@0 46
michael@0 47 #include "jsatominlines.h"
michael@0 48 #include "jsinferinlines.h"
michael@0 49 #include "jsobjinlines.h"
michael@0 50
michael@0 51 #include "vm/Shape-inl.h"
michael@0 52
michael@0 53 using mozilla::DebugOnly;
michael@0 54
michael@0 55 using namespace js;
michael@0 56 using namespace js::gc;
michael@0 57 using namespace js::types;
michael@0 58
michael@0 59 /*
michael@0 60 * Convert |v| to an array index for an array of length |length| per
michael@0 61 * the Typed Array Specification section 7.0, |subarray|. If successful,
michael@0 62 * the output value is in the range [0, length].
michael@0 63 */
michael@0 64 bool
michael@0 65 js::ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out)
michael@0 66 {
michael@0 67 int32_t result;
michael@0 68 if (!ToInt32(cx, v, &result))
michael@0 69 return false;
michael@0 70 if (result < 0) {
michael@0 71 result += length;
michael@0 72 if (result < 0)
michael@0 73 result = 0;
michael@0 74 } else if (uint32_t(result) > length) {
michael@0 75 result = length;
michael@0 76 }
michael@0 77 *out = uint32_t(result);
michael@0 78 return true;
michael@0 79 }
michael@0 80
michael@0 81 /*
michael@0 82 * ArrayBufferObject
michael@0 83 *
michael@0 84 * This class holds the underlying raw buffer that the TypedArrayObject classes
michael@0 85 * access. It can be created explicitly and passed to a TypedArrayObject, or
michael@0 86 * can be created implicitly by constructing a TypedArrayObject with a size.
michael@0 87 */
michael@0 88
michael@0 89 /*
michael@0 90 * ArrayBufferObject (base)
michael@0 91 */
michael@0 92
michael@0 93 const Class ArrayBufferObject::protoClass = {
michael@0 94 "ArrayBufferPrototype",
michael@0 95 JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
michael@0 96 JS_PropertyStub, /* addProperty */
michael@0 97 JS_DeletePropertyStub, /* delProperty */
michael@0 98 JS_PropertyStub, /* getProperty */
michael@0 99 JS_StrictPropertyStub, /* setProperty */
michael@0 100 JS_EnumerateStub,
michael@0 101 JS_ResolveStub,
michael@0 102 JS_ConvertStub
michael@0 103 };
michael@0 104
michael@0 105 const Class ArrayBufferObject::class_ = {
michael@0 106 "ArrayBuffer",
michael@0 107 JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 108 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
michael@0 109 JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
michael@0 110 JSCLASS_BACKGROUND_FINALIZE,
michael@0 111 JS_PropertyStub, /* addProperty */
michael@0 112 JS_DeletePropertyStub, /* delProperty */
michael@0 113 JS_PropertyStub, /* getProperty */
michael@0 114 JS_StrictPropertyStub, /* setProperty */
michael@0 115 JS_EnumerateStub,
michael@0 116 JS_ResolveStub,
michael@0 117 JS_ConvertStub,
michael@0 118 ArrayBufferObject::finalize,
michael@0 119 nullptr, /* call */
michael@0 120 nullptr, /* hasInstance */
michael@0 121 nullptr, /* construct */
michael@0 122 ArrayBufferObject::obj_trace,
michael@0 123 JS_NULL_CLASS_SPEC,
michael@0 124 JS_NULL_CLASS_EXT
michael@0 125 };
michael@0 126
michael@0 127 const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
michael@0 128 JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
michael@0 129 JS_FS_END
michael@0 130 };
michael@0 131
michael@0 132 const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
michael@0 133 JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
michael@0 134 JS_FS_END
michael@0 135 };
michael@0 136
michael@0 137 bool
michael@0 138 js::IsArrayBuffer(HandleValue v)
michael@0 139 {
michael@0 140 return v.isObject() &&
michael@0 141 (v.toObject().is<ArrayBufferObject>() ||
michael@0 142 v.toObject().is<SharedArrayBufferObject>());
michael@0 143 }
michael@0 144
michael@0 145 bool
michael@0 146 js::IsArrayBuffer(HandleObject obj)
michael@0 147 {
michael@0 148 return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
michael@0 149 }
michael@0 150
michael@0 151 bool
michael@0 152 js::IsArrayBuffer(JSObject *obj)
michael@0 153 {
michael@0 154 return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
michael@0 155 }
michael@0 156
michael@0 157 ArrayBufferObject &
michael@0 158 js::AsArrayBuffer(HandleObject obj)
michael@0 159 {
michael@0 160 JS_ASSERT(IsArrayBuffer(obj));
michael@0 161 if (obj->is<SharedArrayBufferObject>())
michael@0 162 return obj->as<SharedArrayBufferObject>();
michael@0 163 return obj->as<ArrayBufferObject>();
michael@0 164 }
michael@0 165
michael@0 166 ArrayBufferObject &
michael@0 167 js::AsArrayBuffer(JSObject *obj)
michael@0 168 {
michael@0 169 JS_ASSERT(IsArrayBuffer(obj));
michael@0 170 if (obj->is<SharedArrayBufferObject>())
michael@0 171 return obj->as<SharedArrayBufferObject>();
michael@0 172 return obj->as<ArrayBufferObject>();
michael@0 173 }
michael@0 174
michael@0 175 MOZ_ALWAYS_INLINE bool
michael@0 176 ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
michael@0 177 {
michael@0 178 JS_ASSERT(IsArrayBuffer(args.thisv()));
michael@0 179 args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
michael@0 180 return true;
michael@0 181 }
michael@0 182
michael@0 183 bool
michael@0 184 ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
michael@0 185 {
michael@0 186 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 187 return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
michael@0 188 }
michael@0 189
michael@0 190 bool
michael@0 191 ArrayBufferObject::fun_slice_impl(JSContext *cx, CallArgs args)
michael@0 192 {
michael@0 193 JS_ASSERT(IsArrayBuffer(args.thisv()));
michael@0 194
michael@0 195 Rooted<ArrayBufferObject*> thisObj(cx, &args.thisv().toObject().as<ArrayBufferObject>());
michael@0 196
michael@0 197 // these are the default values
michael@0 198 uint32_t length = thisObj->byteLength();
michael@0 199 uint32_t begin = 0, end = length;
michael@0 200
michael@0 201 if (args.length() > 0) {
michael@0 202 if (!ToClampedIndex(cx, args[0], length, &begin))
michael@0 203 return false;
michael@0 204
michael@0 205 if (args.length() > 1) {
michael@0 206 if (!ToClampedIndex(cx, args[1], length, &end))
michael@0 207 return false;
michael@0 208 }
michael@0 209 }
michael@0 210
michael@0 211 if (begin > end)
michael@0 212 begin = end;
michael@0 213
michael@0 214 JSObject *nobj = createSlice(cx, thisObj, begin, end);
michael@0 215 if (!nobj)
michael@0 216 return false;
michael@0 217 args.rval().setObject(*nobj);
michael@0 218 return true;
michael@0 219 }
michael@0 220
michael@0 221 bool
michael@0 222 ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp)
michael@0 223 {
michael@0 224 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 225 return CallNonGenericMethod<IsArrayBuffer, fun_slice_impl>(cx, args);
michael@0 226 }
michael@0 227
michael@0 228 /*
michael@0 229 * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
michael@0 230 */
michael@0 231 bool
michael@0 232 ArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp)
michael@0 233 {
michael@0 234 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 235 args.rval().setBoolean(args.get(0).isObject() &&
michael@0 236 JS_IsArrayBufferViewObject(&args.get(0).toObject()));
michael@0 237 return true;
michael@0 238 }
michael@0 239
michael@0 240 /*
michael@0 241 * new ArrayBuffer(byteLength)
michael@0 242 */
michael@0 243 bool
michael@0 244 ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
michael@0 245 {
michael@0 246 int32_t nbytes = 0;
michael@0 247 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 248 if (argc > 0 && !ToInt32(cx, args[0], &nbytes))
michael@0 249 return false;
michael@0 250
michael@0 251 if (nbytes < 0) {
michael@0 252 /*
michael@0 253 * We're just not going to support arrays that are bigger than what will fit
michael@0 254 * as an integer value; if someone actually ever complains (validly), then we
michael@0 255 * can fix.
michael@0 256 */
michael@0 257 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
michael@0 258 return false;
michael@0 259 }
michael@0 260
michael@0 261 JSObject *bufobj = create(cx, uint32_t(nbytes));
michael@0 262 if (!bufobj)
michael@0 263 return false;
michael@0 264 args.rval().setObject(*bufobj);
michael@0 265 return true;
michael@0 266 }
michael@0 267
michael@0 268 /*
michael@0 269 * Note that some callers are allowed to pass in a nullptr cx, so we allocate
michael@0 270 * with the cx if available and fall back to the runtime. If oldptr is given,
michael@0 271 * it's expected to be a previously-allocated contents pointer that we then
michael@0 272 * realloc.
michael@0 273 */
michael@0 274 static void *
michael@0 275 AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr, size_t oldnbytes = 0)
michael@0 276 {
michael@0 277 void *p;
michael@0 278
michael@0 279 // if oldptr is given, then we need to do a realloc
michael@0 280 if (oldptr) {
michael@0 281 p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, nbytes) : js_realloc(oldptr, nbytes);
michael@0 282
michael@0 283 // if we grew the array, we need to set the new bytes to 0
michael@0 284 if (p && nbytes > oldnbytes)
michael@0 285 memset(reinterpret_cast<uint8_t *>(p) + oldnbytes, 0, nbytes - oldnbytes);
michael@0 286 } else {
michael@0 287 p = maybecx ? maybecx->runtime()->callocCanGC(nbytes) : js_calloc(nbytes);
michael@0 288 }
michael@0 289
michael@0 290 if (!p && maybecx)
michael@0 291 js_ReportOutOfMemory(maybecx);
michael@0 292
michael@0 293 return p;
michael@0 294 }
michael@0 295
michael@0 296 ArrayBufferViewObject *
michael@0 297 ArrayBufferObject::viewList() const
michael@0 298 {
michael@0 299 return reinterpret_cast<ArrayBufferViewObject *>(getSlot(VIEW_LIST_SLOT).toPrivate());
michael@0 300 }
michael@0 301
michael@0 302 void
michael@0 303 ArrayBufferObject::setViewListNoBarrier(ArrayBufferViewObject *viewsHead)
michael@0 304 {
michael@0 305 setSlot(VIEW_LIST_SLOT, PrivateValue(viewsHead));
michael@0 306 }
michael@0 307
michael@0 308 void
michael@0 309 ArrayBufferObject::setViewList(ArrayBufferViewObject *viewsHead)
michael@0 310 {
michael@0 311 if (ArrayBufferViewObject *oldHead = viewList())
michael@0 312 ArrayBufferViewObject::writeBarrierPre(oldHead);
michael@0 313 setViewListNoBarrier(viewsHead);
michael@0 314 PostBarrierTypedArrayObject(this);
michael@0 315 }
michael@0 316
michael@0 317 bool
michael@0 318 ArrayBufferObject::canNeuter(JSContext *cx)
michael@0 319 {
michael@0 320 if (isSharedArrayBuffer())
michael@0 321 return false;
michael@0 322
michael@0 323 if (isAsmJSArrayBuffer()) {
michael@0 324 if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this))
michael@0 325 return false;
michael@0 326 }
michael@0 327
michael@0 328 return true;
michael@0 329 }
michael@0 330
michael@0 331 /* static */ void
michael@0 332 ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer, void *newData)
michael@0 333 {
michael@0 334 JS_ASSERT(buffer->canNeuter(cx));
michael@0 335
michael@0 336 // Neuter all views on the buffer, clear out the list of views and the
michael@0 337 // buffer's data.
michael@0 338
michael@0 339 for (ArrayBufferViewObject *view = buffer->viewList(); view; view = view->nextView()) {
michael@0 340 view->neuter(newData);
michael@0 341
michael@0 342 // Notify compiled jit code that the base pointer has moved.
michael@0 343 MarkObjectStateChange(cx, view);
michael@0 344 }
michael@0 345
michael@0 346 if (newData != buffer->dataPointer())
michael@0 347 buffer->setNewOwnedData(cx->runtime()->defaultFreeOp(), newData);
michael@0 348
michael@0 349 buffer->setByteLength(0);
michael@0 350 buffer->setViewList(nullptr);
michael@0 351 buffer->setIsNeutered();
michael@0 352
michael@0 353 // If this is happening during an incremental GC, remove the buffer from
michael@0 354 // the list of live buffers with multiple views if necessary.
michael@0 355 if (buffer->inLiveList()) {
michael@0 356 ArrayBufferVector &gcLiveArrayBuffers = cx->compartment()->gcLiveArrayBuffers;
michael@0 357 DebugOnly<bool> found = false;
michael@0 358 for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
michael@0 359 if (buffer == gcLiveArrayBuffers[i]) {
michael@0 360 found = true;
michael@0 361 gcLiveArrayBuffers[i] = gcLiveArrayBuffers.back();
michael@0 362 gcLiveArrayBuffers.popBack();
michael@0 363 break;
michael@0 364 }
michael@0 365 }
michael@0 366 JS_ASSERT(found);
michael@0 367 buffer->setInLiveList(false);
michael@0 368 }
michael@0 369 }
michael@0 370
michael@0 371 void
michael@0 372 ArrayBufferObject::setNewOwnedData(FreeOp* fop, void *newData)
michael@0 373 {
michael@0 374 JS_ASSERT(!isAsmJSArrayBuffer());
michael@0 375 JS_ASSERT(!isSharedArrayBuffer());
michael@0 376
michael@0 377 if (ownsData()) {
michael@0 378 JS_ASSERT(newData != dataPointer());
michael@0 379 releaseData(fop);
michael@0 380 }
michael@0 381
michael@0 382 setDataPointer(static_cast<uint8_t *>(newData), OwnsData);
michael@0 383 }
michael@0 384
michael@0 385 void
michael@0 386 ArrayBufferObject::changeContents(JSContext *cx, void *newData)
michael@0 387 {
michael@0 388 // Change buffer contents.
michael@0 389 uint8_t* oldDataPointer = dataPointer();
michael@0 390 setNewOwnedData(cx->runtime()->defaultFreeOp(), newData);
michael@0 391
michael@0 392 // Update all views.
michael@0 393 ArrayBufferViewObject *viewListHead = viewList();
michael@0 394 for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
michael@0 395 // Watch out for NULL data pointers in views. This means that the view
michael@0 396 // is not fully initialized (in which case it'll be initialized later
michael@0 397 // with the correct pointer).
michael@0 398 uint8_t *viewDataPointer = view->dataPointer();
michael@0 399 if (viewDataPointer) {
michael@0 400 JS_ASSERT(newData);
michael@0 401 ptrdiff_t offset = viewDataPointer - oldDataPointer;
michael@0 402 viewDataPointer = static_cast<uint8_t *>(newData) + offset;
michael@0 403 view->setPrivate(viewDataPointer);
michael@0 404 }
michael@0 405
michael@0 406 // Notify compiled jit code that the base pointer has moved.
michael@0 407 MarkObjectStateChange(cx, view);
michael@0 408 }
michael@0 409 }
michael@0 410
michael@0 411 #if defined(JS_CPU_X64)
michael@0 412 // Refer to comment above AsmJSMappedSize in AsmJS.h.
michael@0 413 JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
michael@0 414 #endif
michael@0 415
michael@0 416 #if defined(JS_ION) && defined(JS_CPU_X64)
michael@0 417 /* static */ bool
michael@0 418 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
michael@0 419 {
michael@0 420 if (buffer->isAsmJSArrayBuffer())
michael@0 421 return true;
michael@0 422
michael@0 423 // SharedArrayBuffers are already created with AsmJS support in mind.
michael@0 424 if (buffer->isSharedArrayBuffer())
michael@0 425 return true;
michael@0 426
michael@0 427 // Get the entire reserved region (with all pages inaccessible).
michael@0 428 void *data;
michael@0 429 # ifdef XP_WIN
michael@0 430 data = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS);
michael@0 431 if (!data)
michael@0 432 return false;
michael@0 433 # else
michael@0 434 data = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
michael@0 435 if (data == MAP_FAILED)
michael@0 436 return false;
michael@0 437 # endif
michael@0 438
michael@0 439 // Enable access to the valid region.
michael@0 440 JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0);
michael@0 441 # ifdef XP_WIN
michael@0 442 if (!VirtualAlloc(data, buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) {
michael@0 443 VirtualFree(data, 0, MEM_RELEASE);
michael@0 444 return false;
michael@0 445 }
michael@0 446 # else
michael@0 447 size_t validLength = buffer->byteLength();
michael@0 448 if (mprotect(data, validLength, PROT_READ | PROT_WRITE)) {
michael@0 449 munmap(data, AsmJSMappedSize);
michael@0 450 return false;
michael@0 451 }
michael@0 452 # if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
michael@0 453 // Tell Valgrind/Memcheck to not report accesses in the inaccessible region.
michael@0 454 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + validLength,
michael@0 455 AsmJSMappedSize-validLength);
michael@0 456 # endif
michael@0 457 # endif
michael@0 458
michael@0 459 // Copy over the current contents of the typed array.
michael@0 460 memcpy(data, buffer->dataPointer(), buffer->byteLength());
michael@0 461
michael@0 462 // Swap the new elements into the ArrayBufferObject.
michael@0 463 buffer->changeContents(cx, data);
michael@0 464 JS_ASSERT(data == buffer->dataPointer());
michael@0 465
michael@0 466 // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
michael@0 467 // to js_free the data in the normal way.
michael@0 468 buffer->setIsAsmJSArrayBuffer();
michael@0 469
michael@0 470 return true;
michael@0 471 }
michael@0 472
michael@0 473 void
michael@0 474 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
michael@0 475 {
michael@0 476 void *data = dataPointer();
michael@0 477
michael@0 478 JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0);
michael@0 479 # ifdef XP_WIN
michael@0 480 VirtualFree(data, 0, MEM_RELEASE);
michael@0 481 # else
michael@0 482 munmap(data, AsmJSMappedSize);
michael@0 483 # if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
michael@0 484 // Tell Valgrind/Memcheck to recommence reporting accesses in the
michael@0 485 // previously-inaccessible region.
michael@0 486 if (AsmJSMappedSize > 0) {
michael@0 487 VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(data, AsmJSMappedSize);
michael@0 488 }
michael@0 489 # endif
michael@0 490 # endif
michael@0 491 }
michael@0 492 #else /* defined(JS_ION) && defined(JS_CPU_X64) */
michael@0 493 bool
michael@0 494 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
michael@0 495 {
michael@0 496 if (buffer->isAsmJSArrayBuffer())
michael@0 497 return true;
michael@0 498
michael@0 499 if (buffer->isSharedArrayBuffer())
michael@0 500 return true;
michael@0 501
michael@0 502 if (!ensureNonInline(cx, buffer))
michael@0 503 return false;
michael@0 504
michael@0 505 buffer->setIsAsmJSArrayBuffer();
michael@0 506 return true;
michael@0 507 }
michael@0 508
michael@0 509 void
michael@0 510 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
michael@0 511 {
michael@0 512 fop->free_(dataPointer());
michael@0 513 }
michael@0 514 #endif
michael@0 515
michael@0 516 bool
michael@0 517 ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
michael@0 518 {
michael@0 519 JS_ASSERT(!buffer.isSharedArrayBuffer());
michael@0 520 #ifdef JS_ION
michael@0 521 AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread();
michael@0 522 for (; act; act = act->prevAsmJS()) {
michael@0 523 if (act->module().maybeHeapBufferObject() == &buffer)
michael@0 524 break;
michael@0 525 }
michael@0 526 if (!act)
michael@0 527 return true;
michael@0 528
michael@0 529 return false;
michael@0 530 #else
michael@0 531 return true;
michael@0 532 #endif
michael@0 533 }
michael@0 534
michael@0 535 void *
michael@0 536 ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length)
michael@0 537 {
michael@0 538 return AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT);
michael@0 539 }
michael@0 540
michael@0 541 void
michael@0 542 ArrayBufferObject::releaseMappedArray()
michael@0 543 {
michael@0 544 if(!isMappedArrayBuffer() || isNeutered())
michael@0 545 return;
michael@0 546
michael@0 547 DeallocateMappedContent(dataPointer(), byteLength());
michael@0 548 }
michael@0 549
michael@0 550 void
michael@0 551 ArrayBufferObject::addView(ArrayBufferViewObject *view)
michael@0 552 {
michael@0 553 // Note that pre-barriers are not needed here because either the list was
michael@0 554 // previously empty, in which case no pointer is being overwritten, or the
michael@0 555 // list was nonempty and will be made weak during this call (and weak
michael@0 556 // pointers cannot violate the snapshot-at-the-beginning invariant.)
michael@0 557
michael@0 558 ArrayBufferViewObject *viewsHead = viewList();
michael@0 559 if (viewsHead == nullptr) {
michael@0 560 // This ArrayBufferObject will have a single view at this point, so it
michael@0 561 // is a strong pointer (it will be marked during tracing.)
michael@0 562 JS_ASSERT(view->nextView() == nullptr);
michael@0 563 } else {
michael@0 564 view->setNextView(viewsHead);
michael@0 565 }
michael@0 566
michael@0 567 setViewList(view);
michael@0 568 }
michael@0 569
michael@0 570 uint8_t *
michael@0 571 ArrayBufferObject::dataPointer() const
michael@0 572 {
michael@0 573 if (isSharedArrayBuffer())
michael@0 574 return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer();
michael@0 575 return static_cast<uint8_t *>(getSlot(DATA_SLOT).toPrivate());
michael@0 576 }
michael@0 577
michael@0 578 void
michael@0 579 ArrayBufferObject::releaseData(FreeOp *fop)
michael@0 580 {
michael@0 581 JS_ASSERT(ownsData());
michael@0 582
michael@0 583 if (isAsmJSArrayBuffer())
michael@0 584 releaseAsmJSArray(fop);
michael@0 585 else if (isMappedArrayBuffer())
michael@0 586 releaseMappedArray();
michael@0 587 else
michael@0 588 fop->free_(dataPointer());
michael@0 589 }
michael@0 590
michael@0 591 void
michael@0 592 ArrayBufferObject::setDataPointer(void *data, OwnsState ownsData)
michael@0 593 {
michael@0 594 MOZ_ASSERT_IF(!is<SharedArrayBufferObject>() && !isMappedArrayBuffer(), data != nullptr);
michael@0 595 setSlot(DATA_SLOT, PrivateValue(data));
michael@0 596 setOwnsData(ownsData);
michael@0 597 }
michael@0 598
michael@0 599 size_t
michael@0 600 ArrayBufferObject::byteLength() const
michael@0 601 {
michael@0 602 return size_t(getSlot(BYTE_LENGTH_SLOT).toDouble());
michael@0 603 }
michael@0 604
michael@0 605 void
michael@0 606 ArrayBufferObject::setByteLength(size_t length)
michael@0 607 {
michael@0 608 setSlot(BYTE_LENGTH_SLOT, DoubleValue(length));
michael@0 609 }
michael@0 610
michael@0 611 uint32_t
michael@0 612 ArrayBufferObject::flags() const
michael@0 613 {
michael@0 614 return uint32_t(getSlot(FLAGS_SLOT).toInt32());
michael@0 615 }
michael@0 616
michael@0 617 void
michael@0 618 ArrayBufferObject::setFlags(uint32_t flags)
michael@0 619 {
michael@0 620 setSlot(FLAGS_SLOT, Int32Value(flags));
michael@0 621 }
michael@0 622
michael@0 623 ArrayBufferObject *
michael@0 624 ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, void *data /* = nullptr */,
michael@0 625 NewObjectKind newKind /* = GenericObject */,
michael@0 626 bool mapped /* = false */)
michael@0 627 {
michael@0 628 JS_ASSERT_IF(mapped, data);
michael@0 629
michael@0 630 // If we need to allocate data, try to use a larger object size class so
michael@0 631 // that the array buffer's data can be allocated inline with the object.
michael@0 632 // The extra space will be left unused by the object's fixed slots and
michael@0 633 // available for the buffer's data, see NewObject().
michael@0 634 size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
michael@0 635
michael@0 636 size_t nslots = reservedSlots;
michael@0 637 if (!data) {
michael@0 638 size_t usableSlots = JSObject::MAX_FIXED_SLOTS - reservedSlots;
michael@0 639 if (nbytes <= usableSlots * sizeof(Value)) {
michael@0 640 int newSlots = (nbytes - 1) / sizeof(Value) + 1;
michael@0 641 JS_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
michael@0 642 nslots = reservedSlots + newSlots;
michael@0 643 } else {
michael@0 644 data = AllocateArrayBufferContents(cx, nbytes);
michael@0 645 if (!data)
michael@0 646 return nullptr;
michael@0 647 }
michael@0 648 }
michael@0 649
michael@0 650 JS_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
michael@0 651 gc::AllocKind allocKind = GetGCObjectKind(nslots);
michael@0 652
michael@0 653 Rooted<ArrayBufferObject*> obj(cx, NewBuiltinClassInstance<ArrayBufferObject>(cx, allocKind, newKind));
michael@0 654 if (!obj)
michael@0 655 return nullptr;
michael@0 656
michael@0 657 JS_ASSERT(obj->getClass() == &class_);
michael@0 658
michael@0 659 JS_ASSERT(!gc::IsInsideNursery(cx->runtime(), obj));
michael@0 660
michael@0 661 if (data) {
michael@0 662 obj->initialize(nbytes, data, OwnsData);
michael@0 663 if (mapped)
michael@0 664 obj->setIsMappedArrayBuffer();
michael@0 665 } else {
michael@0 666 void *data = obj->fixedData(reservedSlots);
michael@0 667 memset(data, 0, nbytes);
michael@0 668 obj->initialize(nbytes, data, DoesntOwnData);
michael@0 669 }
michael@0 670
michael@0 671 return obj;
michael@0 672 }
michael@0 673
michael@0 674 JSObject *
michael@0 675 ArrayBufferObject::createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
michael@0 676 uint32_t begin, uint32_t end)
michael@0 677 {
michael@0 678 uint32_t bufLength = arrayBuffer->byteLength();
michael@0 679 if (begin > bufLength || end > bufLength || begin > end) {
michael@0 680 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPE_ERR_BAD_ARGS);
michael@0 681 return nullptr;
michael@0 682 }
michael@0 683
michael@0 684 uint32_t length = end - begin;
michael@0 685
michael@0 686 if (!arrayBuffer->hasData())
michael@0 687 return create(cx, 0);
michael@0 688
michael@0 689 ArrayBufferObject *slice = create(cx, length);
michael@0 690 if (!slice)
michael@0 691 return nullptr;
michael@0 692 memcpy(slice->dataPointer(), arrayBuffer->dataPointer() + begin, length);
michael@0 693 return slice;
michael@0 694 }
michael@0 695
michael@0 696 bool
michael@0 697 ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args)
michael@0 698 {
michael@0 699 JS_ASSERT(IsArrayBuffer(args.thisv()));
michael@0 700
michael@0 701 /*
michael@0 702 * This method is only called for |DataView(alienBuf, ...)| which calls
michael@0 703 * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|,
michael@0 704 * ergo there must be at least two arguments.
michael@0 705 */
michael@0 706 JS_ASSERT(args.length() >= 2);
michael@0 707
michael@0 708 Rooted<JSObject*> proto(cx, &args[args.length() - 1].toObject());
michael@0 709
michael@0 710 Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
michael@0 711
michael@0 712 /*
michael@0 713 * Pop off the passed-along prototype and delegate to normal DataViewObject
michael@0 714 * construction.
michael@0 715 */
michael@0 716 CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base());
michael@0 717 return DataViewObject::construct(cx, buffer, frobbedArgs, proto);
michael@0 718 }
michael@0 719
michael@0 720 bool
michael@0 721 ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
michael@0 722 {
michael@0 723 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 724 return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
michael@0 725 }
michael@0 726
michael@0 727 /* static */ bool
michael@0 728 ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
michael@0 729 {
michael@0 730 if (!buffer->ownsData()) {
michael@0 731 void *data = AllocateArrayBufferContents(cx, buffer->byteLength());
michael@0 732 if (!data)
michael@0 733 return false;
michael@0 734 memcpy(data, buffer->dataPointer(), buffer->byteLength());
michael@0 735 buffer->changeContents(cx, data);
michael@0 736 }
michael@0 737
michael@0 738 return true;
michael@0 739 }
michael@0 740
michael@0 741 /* static */ void *
michael@0 742 ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer)
michael@0 743 {
michael@0 744 if (!buffer->canNeuter(cx)) {
michael@0 745 js_ReportOverRecursed(cx);
michael@0 746 return nullptr;
michael@0 747 }
michael@0 748
michael@0 749 void *oldData = buffer->dataPointer();
michael@0 750 void *newData = AllocateArrayBufferContents(cx, buffer->byteLength());
michael@0 751 if (!newData)
michael@0 752 return nullptr;
michael@0 753
michael@0 754 if (buffer->hasStealableContents()) {
michael@0 755 buffer->setOwnsData(DoesntOwnData);
michael@0 756 ArrayBufferObject::neuter(cx, buffer, newData);
michael@0 757 return oldData;
michael@0 758 } else {
michael@0 759 memcpy(newData, oldData, buffer->byteLength());
michael@0 760 ArrayBufferObject::neuter(cx, buffer, oldData);
michael@0 761 return newData;
michael@0 762 }
michael@0 763
michael@0 764 return oldData;
michael@0 765 }
michael@0 766
michael@0 767 /* static */ void
michael@0 768 ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
michael@0 769 {
michael@0 770 ArrayBufferObject &buffer = AsArrayBuffer(obj);
michael@0 771
michael@0 772 if (!buffer.ownsData())
michael@0 773 return;
michael@0 774
michael@0 775 if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) {
michael@0 776 #if defined (JS_CPU_X64)
michael@0 777 // On x64, ArrayBufferObject::prepareForAsmJS switches the
michael@0 778 // ArrayBufferObject to use mmap'd storage.
michael@0 779 sizes->nonHeapElementsAsmJS += buffer.byteLength();
michael@0 780 #else
michael@0 781 sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
michael@0 782 #endif
michael@0 783 } else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) {
michael@0 784 sizes->nonHeapElementsMapped += buffer.byteLength();
michael@0 785 } else if (buffer.dataPointer()) {
michael@0 786 sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
michael@0 787 }
michael@0 788 }
michael@0 789
michael@0 790 /* static */ void
michael@0 791 ArrayBufferObject::finalize(FreeOp *fop, JSObject *obj)
michael@0 792 {
michael@0 793 ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
michael@0 794
michael@0 795 if (buffer.ownsData())
michael@0 796 buffer.releaseData(fop);
michael@0 797 }
michael@0 798
michael@0 799 /* static */ void
michael@0 800 ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
michael@0 801 {
michael@0 802 if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime()->isHeapMinorCollecting())
michael@0 803 return;
michael@0 804
michael@0 805 // ArrayBufferObjects need to maintain a list of possibly-weak pointers to
michael@0 806 // their views. The straightforward way to update the weak pointers would
michael@0 807 // be in the views' finalizers, but giving views finalizers means they
michael@0 808 // cannot be swept in the background. This results in a very high
michael@0 809 // performance cost. Instead, ArrayBufferObjects with a single view hold a
michael@0 810 // strong pointer to the view. This can entrain garbage when the single
michael@0 811 // view becomes otherwise unreachable while the buffer is still live, but
michael@0 812 // this is expected to be rare. ArrayBufferObjects with 0-1 views are
michael@0 813 // expected to be by far the most common cases. ArrayBufferObjects with
michael@0 814 // multiple views are collected into a linked list during collection, and
michael@0 815 // then swept to prune out their dead views.
michael@0 816
michael@0 817 ArrayBufferObject &buffer = AsArrayBuffer(obj);
michael@0 818 ArrayBufferViewObject *viewsHead = buffer.viewList();
michael@0 819 if (!viewsHead)
michael@0 820 return;
michael@0 821
michael@0 822 buffer.setViewList(UpdateObjectIfRelocated(trc->runtime(), &viewsHead));
michael@0 823
michael@0 824 if (viewsHead->nextView() == nullptr) {
michael@0 825 // Single view: mark it, but only if we're actually doing a GC pass
michael@0 826 // right now. Otherwise, the tracing pass for barrier verification will
michael@0 827 // fail if we add another view and the pointer becomes weak.
michael@0 828 MarkObjectUnbarriered(trc, &viewsHead, "arraybuffer.singleview");
michael@0 829 buffer.setViewListNoBarrier(viewsHead);
michael@0 830 } else {
michael@0 831 // Multiple views: do not mark, but append buffer to list.
michael@0 832 ArrayBufferVector &gcLiveArrayBuffers = buffer.compartment()->gcLiveArrayBuffers;
michael@0 833
michael@0 834 // obj_trace may be called multiple times before sweep(), so avoid
michael@0 835 // adding this buffer to the list multiple times.
michael@0 836 if (buffer.inLiveList()) {
michael@0 837 #ifdef DEBUG
michael@0 838 bool found = false;
michael@0 839 for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++)
michael@0 840 found |= gcLiveArrayBuffers[i] == &buffer;
michael@0 841 JS_ASSERT(found);
michael@0 842 #endif
michael@0 843 } else if (gcLiveArrayBuffers.append(&buffer)) {
michael@0 844 buffer.setInLiveList(true);
michael@0 845 } else {
michael@0 846 CrashAtUnhandlableOOM("OOM while updating live array buffers");
michael@0 847 }
michael@0 848 }
michael@0 849 }
michael@0 850
michael@0 851 /* static */ void
michael@0 852 ArrayBufferObject::sweep(JSCompartment *compartment)
michael@0 853 {
michael@0 854 JSRuntime *rt = compartment->runtimeFromMainThread();
michael@0 855 ArrayBufferVector &gcLiveArrayBuffers = compartment->gcLiveArrayBuffers;
michael@0 856
michael@0 857 for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
michael@0 858 ArrayBufferObject *buffer = gcLiveArrayBuffers[i];
michael@0 859
michael@0 860 JS_ASSERT(buffer->inLiveList());
michael@0 861 buffer->setInLiveList(false);
michael@0 862
michael@0 863 ArrayBufferViewObject *viewsHead = buffer->viewList();
michael@0 864 JS_ASSERT(viewsHead);
michael@0 865 buffer->setViewList(UpdateObjectIfRelocated(rt, &viewsHead));
michael@0 866
michael@0 867 // Rebuild the list of views of the ArrayBufferObject, discarding dead
michael@0 868 // views. If there is only one view, it will have already been marked.
michael@0 869 ArrayBufferViewObject *prevLiveView = nullptr;
michael@0 870 ArrayBufferViewObject *view = viewsHead;
michael@0 871 while (view) {
michael@0 872 JS_ASSERT(buffer->compartment() == view->compartment());
michael@0 873 ArrayBufferViewObject *nextView = view->nextView();
michael@0 874 if (!IsObjectAboutToBeFinalized(&view)) {
michael@0 875 view->setNextView(prevLiveView);
michael@0 876 prevLiveView = view;
michael@0 877 }
michael@0 878 view = UpdateObjectIfRelocated(rt, &nextView);
michael@0 879 }
michael@0 880
michael@0 881 buffer->setViewList(prevLiveView);
michael@0 882 }
michael@0 883
michael@0 884 gcLiveArrayBuffers.clear();
michael@0 885 }
michael@0 886
michael@0 887 void
michael@0 888 ArrayBufferObject::resetArrayBufferList(JSCompartment *comp)
michael@0 889 {
michael@0 890 ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers;
michael@0 891
michael@0 892 for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
michael@0 893 ArrayBufferObject *buffer = gcLiveArrayBuffers[i];
michael@0 894
michael@0 895 JS_ASSERT(buffer->inLiveList());
michael@0 896 buffer->setInLiveList(false);
michael@0 897 }
michael@0 898
michael@0 899 gcLiveArrayBuffers.clear();
michael@0 900 }
michael@0 901
michael@0 902 /* static */ bool
michael@0 903 ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector)
michael@0 904 {
michael@0 905 const ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers;
michael@0 906
michael@0 907 for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
michael@0 908 if (!vector.append(gcLiveArrayBuffers[i]))
michael@0 909 return false;
michael@0 910 }
michael@0 911
michael@0 912 return true;
michael@0 913 }
michael@0 914
michael@0 915 /* static */ void
michael@0 916 ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector)
michael@0 917 {
michael@0 918 for (size_t i = 0; i < vector.length(); i++) {
michael@0 919 ArrayBufferObject *buffer = vector[i];
michael@0 920
michael@0 921 JS_ASSERT(!buffer->inLiveList());
michael@0 922 buffer->setInLiveList(true);
michael@0 923
michael@0 924 buffer->compartment()->gcLiveArrayBuffers.infallibleAppend(buffer);
michael@0 925 }
michael@0 926 }
michael@0 927
michael@0 928 /*
michael@0 929 * ArrayBufferViewObject
michael@0 930 */
michael@0 931
michael@0 932 /*
michael@0 933 * This method is used to trace TypedArrayObjects and DataViewObjects. We need
michael@0 934 * a custom tracer because some of an ArrayBufferViewObject's reserved slots
michael@0 935 * are weak references, and some need to be updated specially during moving
michael@0 936 * GCs.
michael@0 937 */
michael@0 938 /* static */ void
michael@0 939 ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj)
michael@0 940 {
michael@0 941 HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT);
michael@0 942 MarkSlot(trc, &bufSlot, "typedarray.buffer");
michael@0 943
michael@0 944 // Update obj's data pointer if the array buffer moved. Note that during
michael@0 945 // initialization, bufSlot may still contain |undefined|.
michael@0 946 if (bufSlot.isObject()) {
michael@0 947 ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject());
michael@0 948 int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32();
michael@0 949 MOZ_ASSERT(buf.dataPointer() != nullptr);
michael@0 950 obj->initPrivate(buf.dataPointer() + offset);
michael@0 951 }
michael@0 952
michael@0 953 /* Update NEXT_VIEW_SLOT, if the view moved. */
michael@0 954 IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT));
michael@0 955 }
michael@0 956
michael@0 957 void
michael@0 958 ArrayBufferViewObject::neuter(void *newData)
michael@0 959 {
michael@0 960 MOZ_ASSERT(newData != nullptr);
michael@0 961 if (is<DataViewObject>())
michael@0 962 as<DataViewObject>().neuter(newData);
michael@0 963 else if (is<TypedArrayObject>())
michael@0 964 as<TypedArrayObject>().neuter(newData);
michael@0 965 else
michael@0 966 as<TypedObject>().neuter(newData);
michael@0 967 }
michael@0 968
michael@0 969 /* static */ ArrayBufferObject *
michael@0 970 ArrayBufferViewObject::bufferObject(JSContext *cx, Handle<ArrayBufferViewObject *> thisObject)
michael@0 971 {
michael@0 972 if (thisObject->is<TypedArrayObject>()) {
michael@0 973 Rooted<TypedArrayObject *> typedArray(cx, &thisObject->as<TypedArrayObject>());
michael@0 974 if (!TypedArrayObject::ensureHasBuffer(cx, typedArray))
michael@0 975 return nullptr;
michael@0 976 }
michael@0 977 return &thisObject->getFixedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObject>();
michael@0 978 }
michael@0 979
michael@0 980 /* JS Friend API */
michael@0 981
michael@0 982 JS_FRIEND_API(bool)
michael@0 983 JS_IsArrayBufferViewObject(JSObject *obj)
michael@0 984 {
michael@0 985 obj = CheckedUnwrap(obj);
michael@0 986 return obj ? obj->is<ArrayBufferViewObject>() : false;
michael@0 987 }
michael@0 988
michael@0 989 JS_FRIEND_API(JSObject *)
michael@0 990 js::UnwrapArrayBufferView(JSObject *obj)
michael@0 991 {
michael@0 992 if (JSObject *unwrapped = CheckedUnwrap(obj))
michael@0 993 return unwrapped->is<ArrayBufferViewObject>() ? unwrapped : nullptr;
michael@0 994 return nullptr;
michael@0 995 }
michael@0 996
michael@0 997 JS_FRIEND_API(uint32_t)
michael@0 998 JS_GetArrayBufferByteLength(JSObject *obj)
michael@0 999 {
michael@0 1000 obj = CheckedUnwrap(obj);
michael@0 1001 return obj ? AsArrayBuffer(obj).byteLength() : 0;
michael@0 1002 }
michael@0 1003
michael@0 1004 JS_FRIEND_API(uint8_t *)
michael@0 1005 JS_GetArrayBufferData(JSObject *obj)
michael@0 1006 {
michael@0 1007 obj = CheckedUnwrap(obj);
michael@0 1008 if (!obj)
michael@0 1009 return nullptr;
michael@0 1010 return AsArrayBuffer(obj).dataPointer();
michael@0 1011 }
michael@0 1012
michael@0 1013 JS_FRIEND_API(uint8_t *)
michael@0 1014 JS_GetStableArrayBufferData(JSContext *cx, HandleObject objArg)
michael@0 1015 {
michael@0 1016 JSObject *obj = CheckedUnwrap(objArg);
michael@0 1017 if (!obj)
michael@0 1018 return nullptr;
michael@0 1019
michael@0 1020 Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(obj));
michael@0 1021 if (!ArrayBufferObject::ensureNonInline(cx, buffer))
michael@0 1022 return nullptr;
michael@0 1023
michael@0 1024 return buffer->dataPointer();
michael@0 1025 }
michael@0 1026
michael@0 1027 JS_FRIEND_API(bool)
michael@0 1028 JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj,
michael@0 1029 NeuterDataDisposition changeData)
michael@0 1030 {
michael@0 1031 if (!obj->is<ArrayBufferObject>()) {
michael@0 1032 JS_ReportError(cx, "ArrayBuffer object required");
michael@0 1033 return false;
michael@0 1034 }
michael@0 1035
michael@0 1036 Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
michael@0 1037
michael@0 1038 if (!buffer->canNeuter(cx)) {
michael@0 1039 js_ReportOverRecursed(cx);
michael@0 1040 return false;
michael@0 1041 }
michael@0 1042
michael@0 1043 void *newData;
michael@0 1044 if (changeData == ChangeData && buffer->hasStealableContents()) {
michael@0 1045 newData = AllocateArrayBufferContents(cx, buffer->byteLength());
michael@0 1046 if (!newData)
michael@0 1047 return false;
michael@0 1048 } else {
michael@0 1049 newData = buffer->dataPointer();
michael@0 1050 }
michael@0 1051
michael@0 1052 ArrayBufferObject::neuter(cx, buffer, newData);
michael@0 1053 return true;
michael@0 1054 }
michael@0 1055
michael@0 1056 JS_FRIEND_API(JSObject *)
michael@0 1057 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
michael@0 1058 {
michael@0 1059 JS_ASSERT(nbytes <= INT32_MAX);
michael@0 1060 return ArrayBufferObject::create(cx, nbytes);
michael@0 1061 }
michael@0 1062
michael@0 1063 JS_PUBLIC_API(JSObject *)
michael@0 1064 JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents)
michael@0 1065 {
michael@0 1066 JS_ASSERT(contents);
michael@0 1067 return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, false);
michael@0 1068 }
michael@0 1069
michael@0 1070 JS_PUBLIC_API(void *)
michael@0 1071 JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes)
michael@0 1072 {
michael@0 1073 return AllocateArrayBufferContents(maybecx, nbytes);
michael@0 1074 }
michael@0 1075
michael@0 1076 JS_PUBLIC_API(void *)
michael@0 1077 JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes)
michael@0 1078 {
michael@0 1079 return AllocateArrayBufferContents(maybecx, nbytes, oldContents, oldNbytes);
michael@0 1080 }
michael@0 1081
michael@0 1082 JS_FRIEND_API(bool)
michael@0 1083 JS_IsArrayBufferObject(JSObject *obj)
michael@0 1084 {
michael@0 1085 obj = CheckedUnwrap(obj);
michael@0 1086 return obj ? obj->is<ArrayBufferObject>() : false;
michael@0 1087 }
michael@0 1088
michael@0 1089 JS_FRIEND_API(JSObject *)
michael@0 1090 js::UnwrapArrayBuffer(JSObject *obj)
michael@0 1091 {
michael@0 1092 if (JSObject *unwrapped = CheckedUnwrap(obj))
michael@0 1093 return unwrapped->is<ArrayBufferObject>() ? unwrapped : nullptr;
michael@0 1094 return nullptr;
michael@0 1095 }
michael@0 1096
michael@0 1097 JS_PUBLIC_API(void *)
michael@0 1098 JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg)
michael@0 1099 {
michael@0 1100 JSObject *obj = CheckedUnwrap(objArg);
michael@0 1101 if (!obj)
michael@0 1102 return nullptr;
michael@0 1103
michael@0 1104 if (!obj->is<ArrayBufferObject>()) {
michael@0 1105 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
michael@0 1106 return nullptr;
michael@0 1107 }
michael@0 1108
michael@0 1109 Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
michael@0 1110 return ArrayBufferObject::stealContents(cx, buffer);
michael@0 1111 }
michael@0 1112
michael@0 1113 JS_PUBLIC_API(JSObject *)
michael@0 1114 JS_NewMappedArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents)
michael@0 1115 {
michael@0 1116 JS_ASSERT(contents);
michael@0 1117 return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, true);
michael@0 1118 }
michael@0 1119
michael@0 1120 JS_PUBLIC_API(void *)
michael@0 1121 JS_CreateMappedArrayBufferContents(int fd, size_t offset, size_t length)
michael@0 1122 {
michael@0 1123 return ArrayBufferObject::createMappedContents(fd, offset, length);
michael@0 1124 }
michael@0 1125
michael@0 1126 JS_PUBLIC_API(void)
michael@0 1127 JS_ReleaseMappedArrayBufferContents(void *contents, size_t length)
michael@0 1128 {
michael@0 1129 DeallocateMappedContent(contents, length);
michael@0 1130 }
michael@0 1131
michael@0 1132 JS_FRIEND_API(bool)
michael@0 1133 JS_IsMappedArrayBufferObject(JSObject *obj)
michael@0 1134 {
michael@0 1135 obj = CheckedUnwrap(obj);
michael@0 1136 if (!obj)
michael@0 1137 return false;
michael@0 1138
michael@0 1139 return obj->is<ArrayBufferObject>()
michael@0 1140 ? obj->as<ArrayBufferObject>().isMappedArrayBuffer()
michael@0 1141 : false;
michael@0 1142 }
michael@0 1143
michael@0 1144 JS_FRIEND_API(void *)
michael@0 1145 JS_GetArrayBufferViewData(JSObject *obj)
michael@0 1146 {
michael@0 1147 obj = CheckedUnwrap(obj);
michael@0 1148 if (!obj)
michael@0 1149 return nullptr;
michael@0 1150 return obj->is<DataViewObject>() ? obj->as<DataViewObject>().dataPointer()
michael@0 1151 : obj->as<TypedArrayObject>().viewData();
michael@0 1152 }
michael@0 1153
michael@0 1154 JS_FRIEND_API(JSObject *)
michael@0 1155 JS_GetArrayBufferViewBuffer(JSContext *cx, JSObject *obj)
michael@0 1156 {
michael@0 1157 obj = CheckedUnwrap(obj);
michael@0 1158 if (!obj)
michael@0 1159 return nullptr;
michael@0 1160 Rooted<ArrayBufferViewObject *> viewObject(cx, &obj->as<ArrayBufferViewObject>());
michael@0 1161 return ArrayBufferViewObject::bufferObject(cx, viewObject);
michael@0 1162 }
michael@0 1163
michael@0 1164 JS_FRIEND_API(uint32_t)
michael@0 1165 JS_GetArrayBufferViewByteLength(JSObject *obj)
michael@0 1166 {
michael@0 1167 obj = CheckedUnwrap(obj);
michael@0 1168 if (!obj)
michael@0 1169 return 0;
michael@0 1170 return obj->is<DataViewObject>()
michael@0 1171 ? obj->as<DataViewObject>().byteLength()
michael@0 1172 : obj->as<TypedArrayObject>().byteLength();
michael@0 1173 }
michael@0 1174
michael@0 1175 JS_FRIEND_API(JSObject *)
michael@0 1176 JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
michael@0 1177 {
michael@0 1178 if (!(obj = CheckedUnwrap(obj)))
michael@0 1179 return nullptr;
michael@0 1180 if (!(obj->is<ArrayBufferViewObject>()))
michael@0 1181 return nullptr;
michael@0 1182
michael@0 1183 *length = obj->is<DataViewObject>()
michael@0 1184 ? obj->as<DataViewObject>().byteLength()
michael@0 1185 : obj->as<TypedArrayObject>().byteLength();
michael@0 1186
michael@0 1187 *data = static_cast<uint8_t*>(obj->is<DataViewObject>()
michael@0 1188 ? obj->as<DataViewObject>().dataPointer()
michael@0 1189 : obj->as<TypedArrayObject>().viewData());
michael@0 1190 return obj;
michael@0 1191 }
michael@0 1192
michael@0 1193 JS_FRIEND_API(void)
michael@0 1194 js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
michael@0 1195 {
michael@0 1196 MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
michael@0 1197
michael@0 1198 *length = obj->is<DataViewObject>()
michael@0 1199 ? obj->as<DataViewObject>().byteLength()
michael@0 1200 : obj->as<TypedArrayObject>().byteLength();
michael@0 1201
michael@0 1202 *data = static_cast<uint8_t*>(obj->is<DataViewObject>()
michael@0 1203 ? obj->as<DataViewObject>().dataPointer()
michael@0 1204 : obj->as<TypedArrayObject>().viewData());
michael@0 1205 }
michael@0 1206
michael@0 1207 JS_FRIEND_API(JSObject *)
michael@0 1208 JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
michael@0 1209 {
michael@0 1210 if (!(obj = CheckedUnwrap(obj)))
michael@0 1211 return nullptr;
michael@0 1212 if (!IsArrayBuffer(obj))
michael@0 1213 return nullptr;
michael@0 1214
michael@0 1215 *length = AsArrayBuffer(obj).byteLength();
michael@0 1216 *data = AsArrayBuffer(obj).dataPointer();
michael@0 1217
michael@0 1218 return obj;
michael@0 1219 }
michael@0 1220
michael@0 1221 JS_FRIEND_API(void)
michael@0 1222 js::GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
michael@0 1223 {
michael@0 1224 MOZ_ASSERT(IsArrayBuffer(obj));
michael@0 1225 *length = AsArrayBuffer(obj).byteLength();
michael@0 1226 *data = AsArrayBuffer(obj).dataPointer();
michael@0 1227 }

mercurial