1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/ArrayBufferObject.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1227 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "vm/ArrayBufferObject.h" 1.11 + 1.12 +#include "mozilla/Alignment.h" 1.13 +#include "mozilla/FloatingPoint.h" 1.14 +#include "mozilla/PodOperations.h" 1.15 + 1.16 +#include <string.h> 1.17 +#ifndef XP_WIN 1.18 +# include <sys/mman.h> 1.19 +#endif 1.20 + 1.21 +#ifdef MOZ_VALGRIND 1.22 +# include <valgrind/memcheck.h> 1.23 +#endif 1.24 + 1.25 +#include "jsapi.h" 1.26 +#include "jsarray.h" 1.27 +#include "jscntxt.h" 1.28 +#include "jscpucfg.h" 1.29 +#include "jsnum.h" 1.30 +#include "jsobj.h" 1.31 +#include "jstypes.h" 1.32 +#include "jsutil.h" 1.33 +#ifdef XP_WIN 1.34 +# include "jswin.h" 1.35 +#endif 1.36 +#include "jswrapper.h" 1.37 + 1.38 +#include "gc/Barrier.h" 1.39 +#include "gc/Marking.h" 1.40 +#include "gc/Memory.h" 1.41 +#include "jit/AsmJS.h" 1.42 +#include "jit/AsmJSModule.h" 1.43 +#include "js/MemoryMetrics.h" 1.44 +#include "vm/GlobalObject.h" 1.45 +#include "vm/Interpreter.h" 1.46 +#include "vm/NumericConversions.h" 1.47 +#include "vm/SharedArrayObject.h" 1.48 +#include "vm/WrapperObject.h" 1.49 + 1.50 +#include "jsatominlines.h" 1.51 +#include "jsinferinlines.h" 1.52 +#include "jsobjinlines.h" 1.53 + 1.54 +#include "vm/Shape-inl.h" 1.55 + 1.56 +using mozilla::DebugOnly; 1.57 + 1.58 +using namespace js; 1.59 +using namespace js::gc; 1.60 +using namespace js::types; 1.61 + 1.62 +/* 1.63 + * Convert |v| to an array index for an array of length |length| per 1.64 + * the Typed Array Specification section 7.0, |subarray|. If successful, 1.65 + * the output value is in the range [0, length]. 1.66 + */ 1.67 +bool 1.68 +js::ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out) 1.69 +{ 1.70 + int32_t result; 1.71 + if (!ToInt32(cx, v, &result)) 1.72 + return false; 1.73 + if (result < 0) { 1.74 + result += length; 1.75 + if (result < 0) 1.76 + result = 0; 1.77 + } else if (uint32_t(result) > length) { 1.78 + result = length; 1.79 + } 1.80 + *out = uint32_t(result); 1.81 + return true; 1.82 +} 1.83 + 1.84 +/* 1.85 + * ArrayBufferObject 1.86 + * 1.87 + * This class holds the underlying raw buffer that the TypedArrayObject classes 1.88 + * access. It can be created explicitly and passed to a TypedArrayObject, or 1.89 + * can be created implicitly by constructing a TypedArrayObject with a size. 1.90 + */ 1.91 + 1.92 +/* 1.93 + * ArrayBufferObject (base) 1.94 + */ 1.95 + 1.96 +const Class ArrayBufferObject::protoClass = { 1.97 + "ArrayBufferPrototype", 1.98 + JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), 1.99 + JS_PropertyStub, /* addProperty */ 1.100 + JS_DeletePropertyStub, /* delProperty */ 1.101 + JS_PropertyStub, /* getProperty */ 1.102 + JS_StrictPropertyStub, /* setProperty */ 1.103 + JS_EnumerateStub, 1.104 + JS_ResolveStub, 1.105 + JS_ConvertStub 1.106 +}; 1.107 + 1.108 +const Class ArrayBufferObject::class_ = { 1.109 + "ArrayBuffer", 1.110 + JSCLASS_IMPLEMENTS_BARRIERS | 1.111 + JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | 1.112 + JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) | 1.113 + JSCLASS_BACKGROUND_FINALIZE, 1.114 + JS_PropertyStub, /* addProperty */ 1.115 + JS_DeletePropertyStub, /* delProperty */ 1.116 + JS_PropertyStub, /* getProperty */ 1.117 + JS_StrictPropertyStub, /* setProperty */ 1.118 + JS_EnumerateStub, 1.119 + JS_ResolveStub, 1.120 + JS_ConvertStub, 1.121 + ArrayBufferObject::finalize, 1.122 + nullptr, /* call */ 1.123 + nullptr, /* hasInstance */ 1.124 + nullptr, /* construct */ 1.125 + ArrayBufferObject::obj_trace, 1.126 + JS_NULL_CLASS_SPEC, 1.127 + JS_NULL_CLASS_EXT 1.128 +}; 1.129 + 1.130 +const JSFunctionSpec ArrayBufferObject::jsfuncs[] = { 1.131 + JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE), 1.132 + JS_FS_END 1.133 +}; 1.134 + 1.135 +const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = { 1.136 + JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0), 1.137 + JS_FS_END 1.138 +}; 1.139 + 1.140 +bool 1.141 +js::IsArrayBuffer(HandleValue v) 1.142 +{ 1.143 + return v.isObject() && 1.144 + (v.toObject().is<ArrayBufferObject>() || 1.145 + v.toObject().is<SharedArrayBufferObject>()); 1.146 +} 1.147 + 1.148 +bool 1.149 +js::IsArrayBuffer(HandleObject obj) 1.150 +{ 1.151 + return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>(); 1.152 +} 1.153 + 1.154 +bool 1.155 +js::IsArrayBuffer(JSObject *obj) 1.156 +{ 1.157 + return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>(); 1.158 +} 1.159 + 1.160 +ArrayBufferObject & 1.161 +js::AsArrayBuffer(HandleObject obj) 1.162 +{ 1.163 + JS_ASSERT(IsArrayBuffer(obj)); 1.164 + if (obj->is<SharedArrayBufferObject>()) 1.165 + return obj->as<SharedArrayBufferObject>(); 1.166 + return obj->as<ArrayBufferObject>(); 1.167 +} 1.168 + 1.169 +ArrayBufferObject & 1.170 +js::AsArrayBuffer(JSObject *obj) 1.171 +{ 1.172 + JS_ASSERT(IsArrayBuffer(obj)); 1.173 + if (obj->is<SharedArrayBufferObject>()) 1.174 + return obj->as<SharedArrayBufferObject>(); 1.175 + return obj->as<ArrayBufferObject>(); 1.176 +} 1.177 + 1.178 +MOZ_ALWAYS_INLINE bool 1.179 +ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args) 1.180 +{ 1.181 + JS_ASSERT(IsArrayBuffer(args.thisv())); 1.182 + args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength()); 1.183 + return true; 1.184 +} 1.185 + 1.186 +bool 1.187 +ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp) 1.188 +{ 1.189 + CallArgs args = CallArgsFromVp(argc, vp); 1.190 + return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args); 1.191 +} 1.192 + 1.193 +bool 1.194 +ArrayBufferObject::fun_slice_impl(JSContext *cx, CallArgs args) 1.195 +{ 1.196 + JS_ASSERT(IsArrayBuffer(args.thisv())); 1.197 + 1.198 + Rooted<ArrayBufferObject*> thisObj(cx, &args.thisv().toObject().as<ArrayBufferObject>()); 1.199 + 1.200 + // these are the default values 1.201 + uint32_t length = thisObj->byteLength(); 1.202 + uint32_t begin = 0, end = length; 1.203 + 1.204 + if (args.length() > 0) { 1.205 + if (!ToClampedIndex(cx, args[0], length, &begin)) 1.206 + return false; 1.207 + 1.208 + if (args.length() > 1) { 1.209 + if (!ToClampedIndex(cx, args[1], length, &end)) 1.210 + return false; 1.211 + } 1.212 + } 1.213 + 1.214 + if (begin > end) 1.215 + begin = end; 1.216 + 1.217 + JSObject *nobj = createSlice(cx, thisObj, begin, end); 1.218 + if (!nobj) 1.219 + return false; 1.220 + args.rval().setObject(*nobj); 1.221 + return true; 1.222 +} 1.223 + 1.224 +bool 1.225 +ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp) 1.226 +{ 1.227 + CallArgs args = CallArgsFromVp(argc, vp); 1.228 + return CallNonGenericMethod<IsArrayBuffer, fun_slice_impl>(cx, args); 1.229 +} 1.230 + 1.231 +/* 1.232 + * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1 1.233 + */ 1.234 +bool 1.235 +ArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp) 1.236 +{ 1.237 + CallArgs args = CallArgsFromVp(argc, vp); 1.238 + args.rval().setBoolean(args.get(0).isObject() && 1.239 + JS_IsArrayBufferViewObject(&args.get(0).toObject())); 1.240 + return true; 1.241 +} 1.242 + 1.243 +/* 1.244 + * new ArrayBuffer(byteLength) 1.245 + */ 1.246 +bool 1.247 +ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) 1.248 +{ 1.249 + int32_t nbytes = 0; 1.250 + CallArgs args = CallArgsFromVp(argc, vp); 1.251 + if (argc > 0 && !ToInt32(cx, args[0], &nbytes)) 1.252 + return false; 1.253 + 1.254 + if (nbytes < 0) { 1.255 + /* 1.256 + * We're just not going to support arrays that are bigger than what will fit 1.257 + * as an integer value; if someone actually ever complains (validly), then we 1.258 + * can fix. 1.259 + */ 1.260 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); 1.261 + return false; 1.262 + } 1.263 + 1.264 + JSObject *bufobj = create(cx, uint32_t(nbytes)); 1.265 + if (!bufobj) 1.266 + return false; 1.267 + args.rval().setObject(*bufobj); 1.268 + return true; 1.269 +} 1.270 + 1.271 +/* 1.272 + * Note that some callers are allowed to pass in a nullptr cx, so we allocate 1.273 + * with the cx if available and fall back to the runtime. If oldptr is given, 1.274 + * it's expected to be a previously-allocated contents pointer that we then 1.275 + * realloc. 1.276 + */ 1.277 +static void * 1.278 +AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr, size_t oldnbytes = 0) 1.279 +{ 1.280 + void *p; 1.281 + 1.282 + // if oldptr is given, then we need to do a realloc 1.283 + if (oldptr) { 1.284 + p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, nbytes) : js_realloc(oldptr, nbytes); 1.285 + 1.286 + // if we grew the array, we need to set the new bytes to 0 1.287 + if (p && nbytes > oldnbytes) 1.288 + memset(reinterpret_cast<uint8_t *>(p) + oldnbytes, 0, nbytes - oldnbytes); 1.289 + } else { 1.290 + p = maybecx ? maybecx->runtime()->callocCanGC(nbytes) : js_calloc(nbytes); 1.291 + } 1.292 + 1.293 + if (!p && maybecx) 1.294 + js_ReportOutOfMemory(maybecx); 1.295 + 1.296 + return p; 1.297 +} 1.298 + 1.299 +ArrayBufferViewObject * 1.300 +ArrayBufferObject::viewList() const 1.301 +{ 1.302 + return reinterpret_cast<ArrayBufferViewObject *>(getSlot(VIEW_LIST_SLOT).toPrivate()); 1.303 +} 1.304 + 1.305 +void 1.306 +ArrayBufferObject::setViewListNoBarrier(ArrayBufferViewObject *viewsHead) 1.307 +{ 1.308 + setSlot(VIEW_LIST_SLOT, PrivateValue(viewsHead)); 1.309 +} 1.310 + 1.311 +void 1.312 +ArrayBufferObject::setViewList(ArrayBufferViewObject *viewsHead) 1.313 +{ 1.314 + if (ArrayBufferViewObject *oldHead = viewList()) 1.315 + ArrayBufferViewObject::writeBarrierPre(oldHead); 1.316 + setViewListNoBarrier(viewsHead); 1.317 + PostBarrierTypedArrayObject(this); 1.318 +} 1.319 + 1.320 +bool 1.321 +ArrayBufferObject::canNeuter(JSContext *cx) 1.322 +{ 1.323 + if (isSharedArrayBuffer()) 1.324 + return false; 1.325 + 1.326 + if (isAsmJSArrayBuffer()) { 1.327 + if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this)) 1.328 + return false; 1.329 + } 1.330 + 1.331 + return true; 1.332 +} 1.333 + 1.334 +/* static */ void 1.335 +ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer, void *newData) 1.336 +{ 1.337 + JS_ASSERT(buffer->canNeuter(cx)); 1.338 + 1.339 + // Neuter all views on the buffer, clear out the list of views and the 1.340 + // buffer's data. 1.341 + 1.342 + for (ArrayBufferViewObject *view = buffer->viewList(); view; view = view->nextView()) { 1.343 + view->neuter(newData); 1.344 + 1.345 + // Notify compiled jit code that the base pointer has moved. 1.346 + MarkObjectStateChange(cx, view); 1.347 + } 1.348 + 1.349 + if (newData != buffer->dataPointer()) 1.350 + buffer->setNewOwnedData(cx->runtime()->defaultFreeOp(), newData); 1.351 + 1.352 + buffer->setByteLength(0); 1.353 + buffer->setViewList(nullptr); 1.354 + buffer->setIsNeutered(); 1.355 + 1.356 + // If this is happening during an incremental GC, remove the buffer from 1.357 + // the list of live buffers with multiple views if necessary. 1.358 + if (buffer->inLiveList()) { 1.359 + ArrayBufferVector &gcLiveArrayBuffers = cx->compartment()->gcLiveArrayBuffers; 1.360 + DebugOnly<bool> found = false; 1.361 + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { 1.362 + if (buffer == gcLiveArrayBuffers[i]) { 1.363 + found = true; 1.364 + gcLiveArrayBuffers[i] = gcLiveArrayBuffers.back(); 1.365 + gcLiveArrayBuffers.popBack(); 1.366 + break; 1.367 + } 1.368 + } 1.369 + JS_ASSERT(found); 1.370 + buffer->setInLiveList(false); 1.371 + } 1.372 +} 1.373 + 1.374 +void 1.375 +ArrayBufferObject::setNewOwnedData(FreeOp* fop, void *newData) 1.376 +{ 1.377 + JS_ASSERT(!isAsmJSArrayBuffer()); 1.378 + JS_ASSERT(!isSharedArrayBuffer()); 1.379 + 1.380 + if (ownsData()) { 1.381 + JS_ASSERT(newData != dataPointer()); 1.382 + releaseData(fop); 1.383 + } 1.384 + 1.385 + setDataPointer(static_cast<uint8_t *>(newData), OwnsData); 1.386 +} 1.387 + 1.388 +void 1.389 +ArrayBufferObject::changeContents(JSContext *cx, void *newData) 1.390 +{ 1.391 + // Change buffer contents. 1.392 + uint8_t* oldDataPointer = dataPointer(); 1.393 + setNewOwnedData(cx->runtime()->defaultFreeOp(), newData); 1.394 + 1.395 + // Update all views. 1.396 + ArrayBufferViewObject *viewListHead = viewList(); 1.397 + for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) { 1.398 + // Watch out for NULL data pointers in views. This means that the view 1.399 + // is not fully initialized (in which case it'll be initialized later 1.400 + // with the correct pointer). 1.401 + uint8_t *viewDataPointer = view->dataPointer(); 1.402 + if (viewDataPointer) { 1.403 + JS_ASSERT(newData); 1.404 + ptrdiff_t offset = viewDataPointer - oldDataPointer; 1.405 + viewDataPointer = static_cast<uint8_t *>(newData) + offset; 1.406 + view->setPrivate(viewDataPointer); 1.407 + } 1.408 + 1.409 + // Notify compiled jit code that the base pointer has moved. 1.410 + MarkObjectStateChange(cx, view); 1.411 + } 1.412 +} 1.413 + 1.414 +#if defined(JS_CPU_X64) 1.415 +// Refer to comment above AsmJSMappedSize in AsmJS.h. 1.416 +JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize); 1.417 +#endif 1.418 + 1.419 +#if defined(JS_ION) && defined(JS_CPU_X64) 1.420 +/* static */ bool 1.421 +ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer) 1.422 +{ 1.423 + if (buffer->isAsmJSArrayBuffer()) 1.424 + return true; 1.425 + 1.426 + // SharedArrayBuffers are already created with AsmJS support in mind. 1.427 + if (buffer->isSharedArrayBuffer()) 1.428 + return true; 1.429 + 1.430 + // Get the entire reserved region (with all pages inaccessible). 1.431 + void *data; 1.432 +# ifdef XP_WIN 1.433 + data = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS); 1.434 + if (!data) 1.435 + return false; 1.436 +# else 1.437 + data = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 1.438 + if (data == MAP_FAILED) 1.439 + return false; 1.440 +# endif 1.441 + 1.442 + // Enable access to the valid region. 1.443 + JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0); 1.444 +# ifdef XP_WIN 1.445 + if (!VirtualAlloc(data, buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) { 1.446 + VirtualFree(data, 0, MEM_RELEASE); 1.447 + return false; 1.448 + } 1.449 +# else 1.450 + size_t validLength = buffer->byteLength(); 1.451 + if (mprotect(data, validLength, PROT_READ | PROT_WRITE)) { 1.452 + munmap(data, AsmJSMappedSize); 1.453 + return false; 1.454 + } 1.455 +# if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE) 1.456 + // Tell Valgrind/Memcheck to not report accesses in the inaccessible region. 1.457 + VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + validLength, 1.458 + AsmJSMappedSize-validLength); 1.459 +# endif 1.460 +# endif 1.461 + 1.462 + // Copy over the current contents of the typed array. 1.463 + memcpy(data, buffer->dataPointer(), buffer->byteLength()); 1.464 + 1.465 + // Swap the new elements into the ArrayBufferObject. 1.466 + buffer->changeContents(cx, data); 1.467 + JS_ASSERT(data == buffer->dataPointer()); 1.468 + 1.469 + // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not 1.470 + // to js_free the data in the normal way. 1.471 + buffer->setIsAsmJSArrayBuffer(); 1.472 + 1.473 + return true; 1.474 +} 1.475 + 1.476 +void 1.477 +ArrayBufferObject::releaseAsmJSArray(FreeOp *fop) 1.478 +{ 1.479 + void *data = dataPointer(); 1.480 + 1.481 + JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0); 1.482 +# ifdef XP_WIN 1.483 + VirtualFree(data, 0, MEM_RELEASE); 1.484 +# else 1.485 + munmap(data, AsmJSMappedSize); 1.486 +# if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE) 1.487 + // Tell Valgrind/Memcheck to recommence reporting accesses in the 1.488 + // previously-inaccessible region. 1.489 + if (AsmJSMappedSize > 0) { 1.490 + VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(data, AsmJSMappedSize); 1.491 + } 1.492 +# endif 1.493 +# endif 1.494 +} 1.495 +#else /* defined(JS_ION) && defined(JS_CPU_X64) */ 1.496 +bool 1.497 +ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer) 1.498 +{ 1.499 + if (buffer->isAsmJSArrayBuffer()) 1.500 + return true; 1.501 + 1.502 + if (buffer->isSharedArrayBuffer()) 1.503 + return true; 1.504 + 1.505 + if (!ensureNonInline(cx, buffer)) 1.506 + return false; 1.507 + 1.508 + buffer->setIsAsmJSArrayBuffer(); 1.509 + return true; 1.510 +} 1.511 + 1.512 +void 1.513 +ArrayBufferObject::releaseAsmJSArray(FreeOp *fop) 1.514 +{ 1.515 + fop->free_(dataPointer()); 1.516 +} 1.517 +#endif 1.518 + 1.519 +bool 1.520 +ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer) 1.521 +{ 1.522 + JS_ASSERT(!buffer.isSharedArrayBuffer()); 1.523 +#ifdef JS_ION 1.524 + AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread(); 1.525 + for (; act; act = act->prevAsmJS()) { 1.526 + if (act->module().maybeHeapBufferObject() == &buffer) 1.527 + break; 1.528 + } 1.529 + if (!act) 1.530 + return true; 1.531 + 1.532 + return false; 1.533 +#else 1.534 + return true; 1.535 +#endif 1.536 +} 1.537 + 1.538 +void * 1.539 +ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length) 1.540 +{ 1.541 + return AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT); 1.542 +} 1.543 + 1.544 +void 1.545 +ArrayBufferObject::releaseMappedArray() 1.546 +{ 1.547 + if(!isMappedArrayBuffer() || isNeutered()) 1.548 + return; 1.549 + 1.550 + DeallocateMappedContent(dataPointer(), byteLength()); 1.551 +} 1.552 + 1.553 +void 1.554 +ArrayBufferObject::addView(ArrayBufferViewObject *view) 1.555 +{ 1.556 + // Note that pre-barriers are not needed here because either the list was 1.557 + // previously empty, in which case no pointer is being overwritten, or the 1.558 + // list was nonempty and will be made weak during this call (and weak 1.559 + // pointers cannot violate the snapshot-at-the-beginning invariant.) 1.560 + 1.561 + ArrayBufferViewObject *viewsHead = viewList(); 1.562 + if (viewsHead == nullptr) { 1.563 + // This ArrayBufferObject will have a single view at this point, so it 1.564 + // is a strong pointer (it will be marked during tracing.) 1.565 + JS_ASSERT(view->nextView() == nullptr); 1.566 + } else { 1.567 + view->setNextView(viewsHead); 1.568 + } 1.569 + 1.570 + setViewList(view); 1.571 +} 1.572 + 1.573 +uint8_t * 1.574 +ArrayBufferObject::dataPointer() const 1.575 +{ 1.576 + if (isSharedArrayBuffer()) 1.577 + return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer(); 1.578 + return static_cast<uint8_t *>(getSlot(DATA_SLOT).toPrivate()); 1.579 +} 1.580 + 1.581 +void 1.582 +ArrayBufferObject::releaseData(FreeOp *fop) 1.583 +{ 1.584 + JS_ASSERT(ownsData()); 1.585 + 1.586 + if (isAsmJSArrayBuffer()) 1.587 + releaseAsmJSArray(fop); 1.588 + else if (isMappedArrayBuffer()) 1.589 + releaseMappedArray(); 1.590 + else 1.591 + fop->free_(dataPointer()); 1.592 +} 1.593 + 1.594 +void 1.595 +ArrayBufferObject::setDataPointer(void *data, OwnsState ownsData) 1.596 +{ 1.597 + MOZ_ASSERT_IF(!is<SharedArrayBufferObject>() && !isMappedArrayBuffer(), data != nullptr); 1.598 + setSlot(DATA_SLOT, PrivateValue(data)); 1.599 + setOwnsData(ownsData); 1.600 +} 1.601 + 1.602 +size_t 1.603 +ArrayBufferObject::byteLength() const 1.604 +{ 1.605 + return size_t(getSlot(BYTE_LENGTH_SLOT).toDouble()); 1.606 +} 1.607 + 1.608 +void 1.609 +ArrayBufferObject::setByteLength(size_t length) 1.610 +{ 1.611 + setSlot(BYTE_LENGTH_SLOT, DoubleValue(length)); 1.612 +} 1.613 + 1.614 +uint32_t 1.615 +ArrayBufferObject::flags() const 1.616 +{ 1.617 + return uint32_t(getSlot(FLAGS_SLOT).toInt32()); 1.618 +} 1.619 + 1.620 +void 1.621 +ArrayBufferObject::setFlags(uint32_t flags) 1.622 +{ 1.623 + setSlot(FLAGS_SLOT, Int32Value(flags)); 1.624 +} 1.625 + 1.626 +ArrayBufferObject * 1.627 +ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, void *data /* = nullptr */, 1.628 + NewObjectKind newKind /* = GenericObject */, 1.629 + bool mapped /* = false */) 1.630 +{ 1.631 + JS_ASSERT_IF(mapped, data); 1.632 + 1.633 + // If we need to allocate data, try to use a larger object size class so 1.634 + // that the array buffer's data can be allocated inline with the object. 1.635 + // The extra space will be left unused by the object's fixed slots and 1.636 + // available for the buffer's data, see NewObject(). 1.637 + size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_); 1.638 + 1.639 + size_t nslots = reservedSlots; 1.640 + if (!data) { 1.641 + size_t usableSlots = JSObject::MAX_FIXED_SLOTS - reservedSlots; 1.642 + if (nbytes <= usableSlots * sizeof(Value)) { 1.643 + int newSlots = (nbytes - 1) / sizeof(Value) + 1; 1.644 + JS_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value))); 1.645 + nslots = reservedSlots + newSlots; 1.646 + } else { 1.647 + data = AllocateArrayBufferContents(cx, nbytes); 1.648 + if (!data) 1.649 + return nullptr; 1.650 + } 1.651 + } 1.652 + 1.653 + JS_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE)); 1.654 + gc::AllocKind allocKind = GetGCObjectKind(nslots); 1.655 + 1.656 + Rooted<ArrayBufferObject*> obj(cx, NewBuiltinClassInstance<ArrayBufferObject>(cx, allocKind, newKind)); 1.657 + if (!obj) 1.658 + return nullptr; 1.659 + 1.660 + JS_ASSERT(obj->getClass() == &class_); 1.661 + 1.662 + JS_ASSERT(!gc::IsInsideNursery(cx->runtime(), obj)); 1.663 + 1.664 + if (data) { 1.665 + obj->initialize(nbytes, data, OwnsData); 1.666 + if (mapped) 1.667 + obj->setIsMappedArrayBuffer(); 1.668 + } else { 1.669 + void *data = obj->fixedData(reservedSlots); 1.670 + memset(data, 0, nbytes); 1.671 + obj->initialize(nbytes, data, DoesntOwnData); 1.672 + } 1.673 + 1.674 + return obj; 1.675 +} 1.676 + 1.677 +JSObject * 1.678 +ArrayBufferObject::createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer, 1.679 + uint32_t begin, uint32_t end) 1.680 +{ 1.681 + uint32_t bufLength = arrayBuffer->byteLength(); 1.682 + if (begin > bufLength || end > bufLength || begin > end) { 1.683 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPE_ERR_BAD_ARGS); 1.684 + return nullptr; 1.685 + } 1.686 + 1.687 + uint32_t length = end - begin; 1.688 + 1.689 + if (!arrayBuffer->hasData()) 1.690 + return create(cx, 0); 1.691 + 1.692 + ArrayBufferObject *slice = create(cx, length); 1.693 + if (!slice) 1.694 + return nullptr; 1.695 + memcpy(slice->dataPointer(), arrayBuffer->dataPointer() + begin, length); 1.696 + return slice; 1.697 +} 1.698 + 1.699 +bool 1.700 +ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args) 1.701 +{ 1.702 + JS_ASSERT(IsArrayBuffer(args.thisv())); 1.703 + 1.704 + /* 1.705 + * This method is only called for |DataView(alienBuf, ...)| which calls 1.706 + * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|, 1.707 + * ergo there must be at least two arguments. 1.708 + */ 1.709 + JS_ASSERT(args.length() >= 2); 1.710 + 1.711 + Rooted<JSObject*> proto(cx, &args[args.length() - 1].toObject()); 1.712 + 1.713 + Rooted<JSObject*> buffer(cx, &args.thisv().toObject()); 1.714 + 1.715 + /* 1.716 + * Pop off the passed-along prototype and delegate to normal DataViewObject 1.717 + * construction. 1.718 + */ 1.719 + CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base()); 1.720 + return DataViewObject::construct(cx, buffer, frobbedArgs, proto); 1.721 +} 1.722 + 1.723 +bool 1.724 +ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp) 1.725 +{ 1.726 + CallArgs args = CallArgsFromVp(argc, vp); 1.727 + return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args); 1.728 +} 1.729 + 1.730 +/* static */ bool 1.731 +ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer) 1.732 +{ 1.733 + if (!buffer->ownsData()) { 1.734 + void *data = AllocateArrayBufferContents(cx, buffer->byteLength()); 1.735 + if (!data) 1.736 + return false; 1.737 + memcpy(data, buffer->dataPointer(), buffer->byteLength()); 1.738 + buffer->changeContents(cx, data); 1.739 + } 1.740 + 1.741 + return true; 1.742 +} 1.743 + 1.744 +/* static */ void * 1.745 +ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer) 1.746 +{ 1.747 + if (!buffer->canNeuter(cx)) { 1.748 + js_ReportOverRecursed(cx); 1.749 + return nullptr; 1.750 + } 1.751 + 1.752 + void *oldData = buffer->dataPointer(); 1.753 + void *newData = AllocateArrayBufferContents(cx, buffer->byteLength()); 1.754 + if (!newData) 1.755 + return nullptr; 1.756 + 1.757 + if (buffer->hasStealableContents()) { 1.758 + buffer->setOwnsData(DoesntOwnData); 1.759 + ArrayBufferObject::neuter(cx, buffer, newData); 1.760 + return oldData; 1.761 + } else { 1.762 + memcpy(newData, oldData, buffer->byteLength()); 1.763 + ArrayBufferObject::neuter(cx, buffer, oldData); 1.764 + return newData; 1.765 + } 1.766 + 1.767 + return oldData; 1.768 +} 1.769 + 1.770 +/* static */ void 1.771 +ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes) 1.772 +{ 1.773 + ArrayBufferObject &buffer = AsArrayBuffer(obj); 1.774 + 1.775 + if (!buffer.ownsData()) 1.776 + return; 1.777 + 1.778 + if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) { 1.779 +#if defined (JS_CPU_X64) 1.780 + // On x64, ArrayBufferObject::prepareForAsmJS switches the 1.781 + // ArrayBufferObject to use mmap'd storage. 1.782 + sizes->nonHeapElementsAsmJS += buffer.byteLength(); 1.783 +#else 1.784 + sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer()); 1.785 +#endif 1.786 + } else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) { 1.787 + sizes->nonHeapElementsMapped += buffer.byteLength(); 1.788 + } else if (buffer.dataPointer()) { 1.789 + sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer()); 1.790 + } 1.791 +} 1.792 + 1.793 +/* static */ void 1.794 +ArrayBufferObject::finalize(FreeOp *fop, JSObject *obj) 1.795 +{ 1.796 + ArrayBufferObject &buffer = obj->as<ArrayBufferObject>(); 1.797 + 1.798 + if (buffer.ownsData()) 1.799 + buffer.releaseData(fop); 1.800 +} 1.801 + 1.802 +/* static */ void 1.803 +ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj) 1.804 +{ 1.805 + if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime()->isHeapMinorCollecting()) 1.806 + return; 1.807 + 1.808 + // ArrayBufferObjects need to maintain a list of possibly-weak pointers to 1.809 + // their views. The straightforward way to update the weak pointers would 1.810 + // be in the views' finalizers, but giving views finalizers means they 1.811 + // cannot be swept in the background. This results in a very high 1.812 + // performance cost. Instead, ArrayBufferObjects with a single view hold a 1.813 + // strong pointer to the view. This can entrain garbage when the single 1.814 + // view becomes otherwise unreachable while the buffer is still live, but 1.815 + // this is expected to be rare. ArrayBufferObjects with 0-1 views are 1.816 + // expected to be by far the most common cases. ArrayBufferObjects with 1.817 + // multiple views are collected into a linked list during collection, and 1.818 + // then swept to prune out their dead views. 1.819 + 1.820 + ArrayBufferObject &buffer = AsArrayBuffer(obj); 1.821 + ArrayBufferViewObject *viewsHead = buffer.viewList(); 1.822 + if (!viewsHead) 1.823 + return; 1.824 + 1.825 + buffer.setViewList(UpdateObjectIfRelocated(trc->runtime(), &viewsHead)); 1.826 + 1.827 + if (viewsHead->nextView() == nullptr) { 1.828 + // Single view: mark it, but only if we're actually doing a GC pass 1.829 + // right now. Otherwise, the tracing pass for barrier verification will 1.830 + // fail if we add another view and the pointer becomes weak. 1.831 + MarkObjectUnbarriered(trc, &viewsHead, "arraybuffer.singleview"); 1.832 + buffer.setViewListNoBarrier(viewsHead); 1.833 + } else { 1.834 + // Multiple views: do not mark, but append buffer to list. 1.835 + ArrayBufferVector &gcLiveArrayBuffers = buffer.compartment()->gcLiveArrayBuffers; 1.836 + 1.837 + // obj_trace may be called multiple times before sweep(), so avoid 1.838 + // adding this buffer to the list multiple times. 1.839 + if (buffer.inLiveList()) { 1.840 +#ifdef DEBUG 1.841 + bool found = false; 1.842 + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) 1.843 + found |= gcLiveArrayBuffers[i] == &buffer; 1.844 + JS_ASSERT(found); 1.845 +#endif 1.846 + } else if (gcLiveArrayBuffers.append(&buffer)) { 1.847 + buffer.setInLiveList(true); 1.848 + } else { 1.849 + CrashAtUnhandlableOOM("OOM while updating live array buffers"); 1.850 + } 1.851 + } 1.852 +} 1.853 + 1.854 +/* static */ void 1.855 +ArrayBufferObject::sweep(JSCompartment *compartment) 1.856 +{ 1.857 + JSRuntime *rt = compartment->runtimeFromMainThread(); 1.858 + ArrayBufferVector &gcLiveArrayBuffers = compartment->gcLiveArrayBuffers; 1.859 + 1.860 + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { 1.861 + ArrayBufferObject *buffer = gcLiveArrayBuffers[i]; 1.862 + 1.863 + JS_ASSERT(buffer->inLiveList()); 1.864 + buffer->setInLiveList(false); 1.865 + 1.866 + ArrayBufferViewObject *viewsHead = buffer->viewList(); 1.867 + JS_ASSERT(viewsHead); 1.868 + buffer->setViewList(UpdateObjectIfRelocated(rt, &viewsHead)); 1.869 + 1.870 + // Rebuild the list of views of the ArrayBufferObject, discarding dead 1.871 + // views. If there is only one view, it will have already been marked. 1.872 + ArrayBufferViewObject *prevLiveView = nullptr; 1.873 + ArrayBufferViewObject *view = viewsHead; 1.874 + while (view) { 1.875 + JS_ASSERT(buffer->compartment() == view->compartment()); 1.876 + ArrayBufferViewObject *nextView = view->nextView(); 1.877 + if (!IsObjectAboutToBeFinalized(&view)) { 1.878 + view->setNextView(prevLiveView); 1.879 + prevLiveView = view; 1.880 + } 1.881 + view = UpdateObjectIfRelocated(rt, &nextView); 1.882 + } 1.883 + 1.884 + buffer->setViewList(prevLiveView); 1.885 + } 1.886 + 1.887 + gcLiveArrayBuffers.clear(); 1.888 +} 1.889 + 1.890 +void 1.891 +ArrayBufferObject::resetArrayBufferList(JSCompartment *comp) 1.892 +{ 1.893 + ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers; 1.894 + 1.895 + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { 1.896 + ArrayBufferObject *buffer = gcLiveArrayBuffers[i]; 1.897 + 1.898 + JS_ASSERT(buffer->inLiveList()); 1.899 + buffer->setInLiveList(false); 1.900 + } 1.901 + 1.902 + gcLiveArrayBuffers.clear(); 1.903 +} 1.904 + 1.905 +/* static */ bool 1.906 +ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector) 1.907 +{ 1.908 + const ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers; 1.909 + 1.910 + for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) { 1.911 + if (!vector.append(gcLiveArrayBuffers[i])) 1.912 + return false; 1.913 + } 1.914 + 1.915 + return true; 1.916 +} 1.917 + 1.918 +/* static */ void 1.919 +ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector) 1.920 +{ 1.921 + for (size_t i = 0; i < vector.length(); i++) { 1.922 + ArrayBufferObject *buffer = vector[i]; 1.923 + 1.924 + JS_ASSERT(!buffer->inLiveList()); 1.925 + buffer->setInLiveList(true); 1.926 + 1.927 + buffer->compartment()->gcLiveArrayBuffers.infallibleAppend(buffer); 1.928 + } 1.929 +} 1.930 + 1.931 +/* 1.932 + * ArrayBufferViewObject 1.933 + */ 1.934 + 1.935 +/* 1.936 + * This method is used to trace TypedArrayObjects and DataViewObjects. We need 1.937 + * a custom tracer because some of an ArrayBufferViewObject's reserved slots 1.938 + * are weak references, and some need to be updated specially during moving 1.939 + * GCs. 1.940 + */ 1.941 +/* static */ void 1.942 +ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj) 1.943 +{ 1.944 + HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT); 1.945 + MarkSlot(trc, &bufSlot, "typedarray.buffer"); 1.946 + 1.947 + // Update obj's data pointer if the array buffer moved. Note that during 1.948 + // initialization, bufSlot may still contain |undefined|. 1.949 + if (bufSlot.isObject()) { 1.950 + ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject()); 1.951 + int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32(); 1.952 + MOZ_ASSERT(buf.dataPointer() != nullptr); 1.953 + obj->initPrivate(buf.dataPointer() + offset); 1.954 + } 1.955 + 1.956 + /* Update NEXT_VIEW_SLOT, if the view moved. */ 1.957 + IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT)); 1.958 +} 1.959 + 1.960 +void 1.961 +ArrayBufferViewObject::neuter(void *newData) 1.962 +{ 1.963 + MOZ_ASSERT(newData != nullptr); 1.964 + if (is<DataViewObject>()) 1.965 + as<DataViewObject>().neuter(newData); 1.966 + else if (is<TypedArrayObject>()) 1.967 + as<TypedArrayObject>().neuter(newData); 1.968 + else 1.969 + as<TypedObject>().neuter(newData); 1.970 +} 1.971 + 1.972 +/* static */ ArrayBufferObject * 1.973 +ArrayBufferViewObject::bufferObject(JSContext *cx, Handle<ArrayBufferViewObject *> thisObject) 1.974 +{ 1.975 + if (thisObject->is<TypedArrayObject>()) { 1.976 + Rooted<TypedArrayObject *> typedArray(cx, &thisObject->as<TypedArrayObject>()); 1.977 + if (!TypedArrayObject::ensureHasBuffer(cx, typedArray)) 1.978 + return nullptr; 1.979 + } 1.980 + return &thisObject->getFixedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObject>(); 1.981 +} 1.982 + 1.983 +/* JS Friend API */ 1.984 + 1.985 +JS_FRIEND_API(bool) 1.986 +JS_IsArrayBufferViewObject(JSObject *obj) 1.987 +{ 1.988 + obj = CheckedUnwrap(obj); 1.989 + return obj ? obj->is<ArrayBufferViewObject>() : false; 1.990 +} 1.991 + 1.992 +JS_FRIEND_API(JSObject *) 1.993 +js::UnwrapArrayBufferView(JSObject *obj) 1.994 +{ 1.995 + if (JSObject *unwrapped = CheckedUnwrap(obj)) 1.996 + return unwrapped->is<ArrayBufferViewObject>() ? unwrapped : nullptr; 1.997 + return nullptr; 1.998 +} 1.999 + 1.1000 +JS_FRIEND_API(uint32_t) 1.1001 +JS_GetArrayBufferByteLength(JSObject *obj) 1.1002 +{ 1.1003 + obj = CheckedUnwrap(obj); 1.1004 + return obj ? AsArrayBuffer(obj).byteLength() : 0; 1.1005 +} 1.1006 + 1.1007 +JS_FRIEND_API(uint8_t *) 1.1008 +JS_GetArrayBufferData(JSObject *obj) 1.1009 +{ 1.1010 + obj = CheckedUnwrap(obj); 1.1011 + if (!obj) 1.1012 + return nullptr; 1.1013 + return AsArrayBuffer(obj).dataPointer(); 1.1014 +} 1.1015 + 1.1016 +JS_FRIEND_API(uint8_t *) 1.1017 +JS_GetStableArrayBufferData(JSContext *cx, HandleObject objArg) 1.1018 +{ 1.1019 + JSObject *obj = CheckedUnwrap(objArg); 1.1020 + if (!obj) 1.1021 + return nullptr; 1.1022 + 1.1023 + Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(obj)); 1.1024 + if (!ArrayBufferObject::ensureNonInline(cx, buffer)) 1.1025 + return nullptr; 1.1026 + 1.1027 + return buffer->dataPointer(); 1.1028 +} 1.1029 + 1.1030 +JS_FRIEND_API(bool) 1.1031 +JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj, 1.1032 + NeuterDataDisposition changeData) 1.1033 +{ 1.1034 + if (!obj->is<ArrayBufferObject>()) { 1.1035 + JS_ReportError(cx, "ArrayBuffer object required"); 1.1036 + return false; 1.1037 + } 1.1038 + 1.1039 + Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>()); 1.1040 + 1.1041 + if (!buffer->canNeuter(cx)) { 1.1042 + js_ReportOverRecursed(cx); 1.1043 + return false; 1.1044 + } 1.1045 + 1.1046 + void *newData; 1.1047 + if (changeData == ChangeData && buffer->hasStealableContents()) { 1.1048 + newData = AllocateArrayBufferContents(cx, buffer->byteLength()); 1.1049 + if (!newData) 1.1050 + return false; 1.1051 + } else { 1.1052 + newData = buffer->dataPointer(); 1.1053 + } 1.1054 + 1.1055 + ArrayBufferObject::neuter(cx, buffer, newData); 1.1056 + return true; 1.1057 +} 1.1058 + 1.1059 +JS_FRIEND_API(JSObject *) 1.1060 +JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes) 1.1061 +{ 1.1062 + JS_ASSERT(nbytes <= INT32_MAX); 1.1063 + return ArrayBufferObject::create(cx, nbytes); 1.1064 +} 1.1065 + 1.1066 +JS_PUBLIC_API(JSObject *) 1.1067 +JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents) 1.1068 +{ 1.1069 + JS_ASSERT(contents); 1.1070 + return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, false); 1.1071 +} 1.1072 + 1.1073 +JS_PUBLIC_API(void *) 1.1074 +JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes) 1.1075 +{ 1.1076 + return AllocateArrayBufferContents(maybecx, nbytes); 1.1077 +} 1.1078 + 1.1079 +JS_PUBLIC_API(void *) 1.1080 +JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes) 1.1081 +{ 1.1082 + return AllocateArrayBufferContents(maybecx, nbytes, oldContents, oldNbytes); 1.1083 +} 1.1084 + 1.1085 +JS_FRIEND_API(bool) 1.1086 +JS_IsArrayBufferObject(JSObject *obj) 1.1087 +{ 1.1088 + obj = CheckedUnwrap(obj); 1.1089 + return obj ? obj->is<ArrayBufferObject>() : false; 1.1090 +} 1.1091 + 1.1092 +JS_FRIEND_API(JSObject *) 1.1093 +js::UnwrapArrayBuffer(JSObject *obj) 1.1094 +{ 1.1095 + if (JSObject *unwrapped = CheckedUnwrap(obj)) 1.1096 + return unwrapped->is<ArrayBufferObject>() ? unwrapped : nullptr; 1.1097 + return nullptr; 1.1098 +} 1.1099 + 1.1100 +JS_PUBLIC_API(void *) 1.1101 +JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg) 1.1102 +{ 1.1103 + JSObject *obj = CheckedUnwrap(objArg); 1.1104 + if (!obj) 1.1105 + return nullptr; 1.1106 + 1.1107 + if (!obj->is<ArrayBufferObject>()) { 1.1108 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); 1.1109 + return nullptr; 1.1110 + } 1.1111 + 1.1112 + Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>()); 1.1113 + return ArrayBufferObject::stealContents(cx, buffer); 1.1114 +} 1.1115 + 1.1116 +JS_PUBLIC_API(JSObject *) 1.1117 +JS_NewMappedArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents) 1.1118 +{ 1.1119 + JS_ASSERT(contents); 1.1120 + return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, true); 1.1121 +} 1.1122 + 1.1123 +JS_PUBLIC_API(void *) 1.1124 +JS_CreateMappedArrayBufferContents(int fd, size_t offset, size_t length) 1.1125 +{ 1.1126 + return ArrayBufferObject::createMappedContents(fd, offset, length); 1.1127 +} 1.1128 + 1.1129 +JS_PUBLIC_API(void) 1.1130 +JS_ReleaseMappedArrayBufferContents(void *contents, size_t length) 1.1131 +{ 1.1132 + DeallocateMappedContent(contents, length); 1.1133 +} 1.1134 + 1.1135 +JS_FRIEND_API(bool) 1.1136 +JS_IsMappedArrayBufferObject(JSObject *obj) 1.1137 +{ 1.1138 + obj = CheckedUnwrap(obj); 1.1139 + if (!obj) 1.1140 + return false; 1.1141 + 1.1142 + return obj->is<ArrayBufferObject>() 1.1143 + ? obj->as<ArrayBufferObject>().isMappedArrayBuffer() 1.1144 + : false; 1.1145 +} 1.1146 + 1.1147 +JS_FRIEND_API(void *) 1.1148 +JS_GetArrayBufferViewData(JSObject *obj) 1.1149 +{ 1.1150 + obj = CheckedUnwrap(obj); 1.1151 + if (!obj) 1.1152 + return nullptr; 1.1153 + return obj->is<DataViewObject>() ? obj->as<DataViewObject>().dataPointer() 1.1154 + : obj->as<TypedArrayObject>().viewData(); 1.1155 +} 1.1156 + 1.1157 +JS_FRIEND_API(JSObject *) 1.1158 +JS_GetArrayBufferViewBuffer(JSContext *cx, JSObject *obj) 1.1159 +{ 1.1160 + obj = CheckedUnwrap(obj); 1.1161 + if (!obj) 1.1162 + return nullptr; 1.1163 + Rooted<ArrayBufferViewObject *> viewObject(cx, &obj->as<ArrayBufferViewObject>()); 1.1164 + return ArrayBufferViewObject::bufferObject(cx, viewObject); 1.1165 +} 1.1166 + 1.1167 +JS_FRIEND_API(uint32_t) 1.1168 +JS_GetArrayBufferViewByteLength(JSObject *obj) 1.1169 +{ 1.1170 + obj = CheckedUnwrap(obj); 1.1171 + if (!obj) 1.1172 + return 0; 1.1173 + return obj->is<DataViewObject>() 1.1174 + ? obj->as<DataViewObject>().byteLength() 1.1175 + : obj->as<TypedArrayObject>().byteLength(); 1.1176 +} 1.1177 + 1.1178 +JS_FRIEND_API(JSObject *) 1.1179 +JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data) 1.1180 +{ 1.1181 + if (!(obj = CheckedUnwrap(obj))) 1.1182 + return nullptr; 1.1183 + if (!(obj->is<ArrayBufferViewObject>())) 1.1184 + return nullptr; 1.1185 + 1.1186 + *length = obj->is<DataViewObject>() 1.1187 + ? obj->as<DataViewObject>().byteLength() 1.1188 + : obj->as<TypedArrayObject>().byteLength(); 1.1189 + 1.1190 + *data = static_cast<uint8_t*>(obj->is<DataViewObject>() 1.1191 + ? obj->as<DataViewObject>().dataPointer() 1.1192 + : obj->as<TypedArrayObject>().viewData()); 1.1193 + return obj; 1.1194 +} 1.1195 + 1.1196 +JS_FRIEND_API(void) 1.1197 +js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data) 1.1198 +{ 1.1199 + MOZ_ASSERT(obj->is<ArrayBufferViewObject>()); 1.1200 + 1.1201 + *length = obj->is<DataViewObject>() 1.1202 + ? obj->as<DataViewObject>().byteLength() 1.1203 + : obj->as<TypedArrayObject>().byteLength(); 1.1204 + 1.1205 + *data = static_cast<uint8_t*>(obj->is<DataViewObject>() 1.1206 + ? obj->as<DataViewObject>().dataPointer() 1.1207 + : obj->as<TypedArrayObject>().viewData()); 1.1208 +} 1.1209 + 1.1210 +JS_FRIEND_API(JSObject *) 1.1211 +JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data) 1.1212 +{ 1.1213 + if (!(obj = CheckedUnwrap(obj))) 1.1214 + return nullptr; 1.1215 + if (!IsArrayBuffer(obj)) 1.1216 + return nullptr; 1.1217 + 1.1218 + *length = AsArrayBuffer(obj).byteLength(); 1.1219 + *data = AsArrayBuffer(obj).dataPointer(); 1.1220 + 1.1221 + return obj; 1.1222 +} 1.1223 + 1.1224 +JS_FRIEND_API(void) 1.1225 +js::GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data) 1.1226 +{ 1.1227 + MOZ_ASSERT(IsArrayBuffer(obj)); 1.1228 + *length = AsArrayBuffer(obj).byteLength(); 1.1229 + *data = AsArrayBuffer(obj).dataPointer(); 1.1230 +}