Bug 933001 - Part 1/5 - Define SharedArrayBufferObject. r=sfink

This commit is contained in:
Sean Stangl 2014-02-20 14:43:03 -08:00
Родитель 7cb1860267
Коммит ceed1869bc
18 изменённых файлов: 665 добавлений и 60 удалений

Просмотреть файл

@ -432,6 +432,7 @@ DeclMarkerImpl(JitCode, jit::JitCode)
DeclMarkerImpl(Object, ArgumentsObject)
DeclMarkerImpl(Object, ArrayBufferObject)
DeclMarkerImpl(Object, ArrayBufferViewObject)
DeclMarkerImpl(Object, SharedArrayBufferObject)
DeclMarkerImpl(Object, DebugScopeObject)
DeclMarkerImpl(Object, GlobalObject)
DeclMarkerImpl(Object, JSObject)

Просмотреть файл

@ -17,6 +17,7 @@ namespace js {
class ArgumentsObject;
class ArrayBufferObject;
class ArrayBufferViewObject;
class SharedArrayBufferObject;
class BaseShape;
class DebugScopeObject;
struct GCMarker;
@ -97,6 +98,7 @@ DeclMarker(JitCode, jit::JitCode)
DeclMarker(Object, ArgumentsObject)
DeclMarker(Object, ArrayBufferObject)
DeclMarker(Object, ArrayBufferViewObject)
DeclMarker(Object, SharedArrayBufferObject)
DeclMarker(Object, DebugScopeObject)
DeclMarker(Object, GlobalObject)
DeclMarker(Object, JSObject)

Просмотреть файл

@ -10,6 +10,7 @@
#include <stddef.h>
#include "js/TypeDecls.h"
#include "vm/ObjectImpl.h"
namespace js {
@ -91,7 +92,29 @@ IsValidAsmJSHeapLength(uint32_t length);
// byteLength portion of which is accessible) so that out-of-bounds accesses
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
static const size_t AsmJSBufferProtectedSize = 4 * 1024ULL * 1024ULL * 1024ULL;
#endif
// To avoid dynamically checking bounds on each load/store, asm.js code relies
// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
// if we can guarantee that *any* out-of-bounds access generates a fault. This
// isn't generally true since an out-of-bounds access could land on other
// Mozilla data. To overcome this on x64, we reserve an entire 4GB space,
// making only the range [0, byteLength) accessible, and use a 32-bit unsigned
// index into this space. (x86 and ARM require different tricks.)
//
// One complication is that we need to put an ObjectElements struct immediately
// before the data array (as required by the general JSObject data structure).
// Thus, we must stick a page before the elements to hold ObjectElements.
//
// |<------------------------------ 4GB + 1 pages --------------------->|
// |<--- sizeof --->|<------------------- 4GB ----------------->|
//
// | waste | ObjectElements | data array | inaccessible reserved memory |
// ^ ^ ^
// | \ /
// obj->elements required to be page boundaries
//
static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize;
#endif // JS_CODEGEN_X64
#ifdef JS_ION

Просмотреть файл

@ -78,6 +78,7 @@
#include "vm/RegExpStatics.h"
#include "vm/Runtime.h"
#include "vm/Shape.h"
#include "vm/SharedArrayObject.h"
#include "vm/StopIterationObject.h"
#include "vm/StringBuffer.h"
#include "vm/TypedArrayObject.h"

Просмотреть файл

@ -30,6 +30,7 @@ namespace js {
class ArgumentsObject;
class ArrayBufferObject;
class ArrayBufferViewObject;
class SharedArrayBufferObject;
class BaseShape;
class DebugScopeObject;
class GCHelperThread;
@ -132,6 +133,7 @@ template <> struct MapTypeToTraceKind<JSFunction> { static const JSGCTrace
template <> struct MapTypeToTraceKind<ArgumentsObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<GlobalObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };

Просмотреть файл

@ -19,6 +19,7 @@
#include "vm/ArrayObject.h"
#include "vm/BooleanObject.h"
#include "vm/NumberObject.h"
#include "vm/SharedArrayObject.h"
#include "vm/StringObject.h"
#include "vm/TypedArrayObject.h"
@ -305,6 +306,9 @@ GetClassForProtoKey(JSProtoKey key)
case JSProto_ArrayBuffer:
return &ArrayBufferObject::class_;
case JSProto_SharedArrayBuffer:
return &SharedArrayBufferObject::class_;
case JSProto_DataView:
return &DataViewObject::class_;

Просмотреть файл

@ -1034,7 +1034,8 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
case ESClass_String: return obj->is<StringObject>();
case ESClass_Boolean: return obj->is<BooleanObject>();
case ESClass_RegExp: return obj->is<RegExpObject>();
case ESClass_ArrayBuffer: return obj->is<ArrayBufferObject>();
case ESClass_ArrayBuffer:
return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
case ESClass_Date: return obj->is<DateObject>();
}
MOZ_ASSUME_UNREACHABLE("bad classValue");

Просмотреть файл

@ -91,10 +91,11 @@
real(Map, 33, js_InitMapClass, OCLASP(Map)) \
real(Set, 34, js_InitSetClass, OCLASP(Set)) \
real(DataView, 35, js_InitTypedArrayClasses, OCLASP(DataView)) \
IF_INTL(real,imaginary) (Intl, 36, js_InitIntlClass, CLASP(Intl)) \
IF_BDATA(real,imaginary)(TypedObject, 37, js_InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
imaginary(GeneratorFunction, 38, js_InitIteratorClasses, dummy) \
IF_BDATA(real,imaginary)(SIMD, 39, js_InitSIMDClass, OCLASP(SIMD)) \
real(SharedArrayBuffer, 36, js_InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \
IF_INTL(real,imaginary) (Intl, 37, js_InitIntlClass, CLASP(Intl)) \
IF_BDATA(real,imaginary)(TypedObject, 38, js_InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
imaginary(GeneratorFunction, 39, js_InitIteratorClasses, dummy) \
IF_BDATA(real,imaginary)(SIMD, 40, js_InitSIMDClass, OCLASP(SIMD)) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)

Просмотреть файл

@ -181,6 +181,7 @@ UNIFIED_SOURCES += [
'vm/ScopeObject.cpp',
'vm/SelfHosting.cpp',
'vm/Shape.cpp',
'vm/SharedArrayObject.cpp',
'vm/SPSProfiler.cpp',
'vm/Stack.cpp',
'vm/String.cpp',

Просмотреть файл

@ -35,6 +35,7 @@
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/NumericConversions.h"
#include "vm/SharedArrayObject.h"
#include "vm/WrapperObject.h"
#include "jsatominlines.h"
@ -171,6 +172,44 @@ const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
JS_FS_END
};
bool
js::IsArrayBuffer(HandleValue v)
{
return v.isObject() &&
(v.toObject().is<ArrayBufferObject>() ||
v.toObject().is<SharedArrayBufferObject>());
}
bool
js::IsArrayBuffer(HandleObject obj)
{
return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
}
bool
js::IsArrayBuffer(JSObject *obj)
{
return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
}
ArrayBufferObject &
js::AsArrayBuffer(HandleObject obj)
{
JS_ASSERT(IsArrayBuffer(obj));
if (obj->is<SharedArrayBufferObject>())
return obj->as<SharedArrayBufferObject>();
return obj->as<ArrayBufferObject>();
}
ArrayBufferObject &
js::AsArrayBuffer(JSObject *obj)
{
JS_ASSERT(IsArrayBuffer(obj));
if (obj->is<SharedArrayBufferObject>())
return obj->as<SharedArrayBufferObject>();
return obj->as<ArrayBufferObject>();
}
MOZ_ALWAYS_INLINE bool
ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
{
@ -383,10 +422,18 @@ ArrayBufferObject::neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer)
return true;
}
uint8_t *
ArrayBufferObject::dataPointer() const {
if (isSharedArrayBuffer())
return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer();
return (uint8_t *)elements;
}
void
ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader)
{
JS_ASSERT(!isAsmJSArrayBuffer());
JS_ASSERT(!isSharedArrayBuffer());
// Grab out data before invalidating it.
uint32_t byteLengthCopy = byteLength();
@ -438,6 +485,8 @@ ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader)
void
ArrayBufferObject::neuter(JSContext *cx)
{
JS_ASSERT(!isSharedArrayBuffer());
JS_ASSERT(cx);
if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
ObjectElements *oldHeader = getElementsHeader();
@ -456,6 +505,7 @@ ArrayBufferObject::neuter(JSContext *cx)
/* static */ bool
ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
{
JS_ASSERT(!buffer->isSharedArrayBuffer());
if (buffer->hasDynamicElements())
return true;
@ -470,31 +520,13 @@ ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buf
return true;
}
#if defined(JS_ION) && defined(JS_CPU_X64)
// To avoid dynamically checking bounds on each load/store, asm.js code relies
// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
// if we can guarantee that *any* out-of-bounds access generates a fault. This
// isn't generally true since an out-of-bounds access could land on other
// Mozilla data. To overcome this on x64, we reserve an entire 4GB space,
// making only the range [0, byteLength) accessible, and use a 32-bit unsigned
// index into this space. (x86 and ARM require different tricks.)
//
// One complication is that we need to put an ObjectElements struct immediately
// before the data array (as required by the general JSObject data structure).
// Thus, we must stick a page before the elements to hold ObjectElements.
//
// |<------------------------------ 4GB + 1 pages --------------------->|
// |<--- sizeof --->|<------------------- 4GB ----------------->|
//
// | waste | ObjectElements | data array | inaccessible reserved memory |
// ^ ^ ^
// | \ /
// obj->elements required to be page boundaries
//
#if defined(JS_CPU_X64)
// Refer to comment above AsmJSMappedSize in AsmJS.h.
JS_STATIC_ASSERT(sizeof(ObjectElements) < AsmJSPageSize);
JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize;
#endif
#if defined(JS_ION) && defined(JS_CPU_X64)
bool
ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
{
@ -659,14 +691,15 @@ ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, bool clear /* = true *
JSRuntime *rt = obj->runtimeFromMainThread();
rt->gcNursery.notifyNewElements(obj, header);
#endif
obj->initElementsHeader(obj->getElementsHeader(), nbytes);
} else {
// Elements header must be initialized before dataPointer() is callable.
obj->setFixedElements();
obj->initElementsHeader(obj->getElementsHeader(), nbytes);
if (clear)
memset(obj->dataPointer(), 0, nbytes);
}
obj->initElementsHeader(obj->getElementsHeader(), nbytes);
return obj;
}
@ -791,7 +824,7 @@ ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
// multiple views are collected into a linked list during collection, and
// then swept to prune out their dead views.
ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
ArrayBufferObject &buffer = AsArrayBuffer(obj);
ArrayBufferViewObject *viewsHead = GetViewList(&buffer);
if (!viewsHead)
return;
@ -826,7 +859,7 @@ ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
JS_ASSERT(obj->compartment() == firstView->compartment());
ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers;
firstView->setBufferLink(*bufList);
*bufList = &obj->as<ArrayBufferObject>();
*bufList = &AsArrayBuffer(obj);
} else {
#ifdef DEBUG
bool found = false;
@ -1208,7 +1241,7 @@ ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj)
/* Update obj's data slot if the array buffer moved. Note that during
* initialization, bufSlot may still be JSVAL_VOID. */
if (bufSlot.isObject()) {
ArrayBufferObject &buf = bufSlot.toObject().as<ArrayBufferObject>();
ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject());
if (buf.getElementsHeader()->isNeuteredBuffer()) {
// When a view is neutered, it is set to NULL
JS_ASSERT(obj->getPrivate() == nullptr);
@ -1257,7 +1290,7 @@ JS_FRIEND_API(uint32_t)
JS_GetArrayBufferByteLength(JSObject *obj)
{
obj = CheckedUnwrap(obj);
return obj ? obj->as<ArrayBufferObject>().byteLength() : 0;
return obj ? AsArrayBuffer(obj).byteLength() : 0;
}
JS_FRIEND_API(uint8_t *)
@ -1266,7 +1299,7 @@ JS_GetArrayBufferData(JSObject *obj)
obj = CheckedUnwrap(obj);
if (!obj)
return nullptr;
return obj->as<ArrayBufferObject>().dataPointer();
return AsArrayBuffer(obj).dataPointer();
}
JS_FRIEND_API(uint8_t *)
@ -1276,7 +1309,7 @@ JS_GetStableArrayBufferData(JSContext *cx, JSObject *obj)
if (!obj)
return nullptr;
Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(obj));
if (!ArrayBufferObject::ensureNonInline(cx, buffer))
return nullptr;
@ -1430,11 +1463,11 @@ JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
{
if (!(obj = CheckedUnwrap(obj)))
return nullptr;
if (!obj->is<ArrayBufferObject>())
if (!IsArrayBuffer(obj))
return nullptr;
*length = obj->as<ArrayBufferObject>().byteLength();
*data = obj->as<ArrayBufferObject>().dataPointer();
*length = AsArrayBuffer(obj).byteLength();
*data = AsArrayBuffer(obj).dataPointer();
return obj;
}

Просмотреть файл

@ -23,6 +23,7 @@ class ArrayBufferViewObject;
//
// - JSObject
// - ArrayBufferObject
// - SharedArrayBufferObject
// - ArrayBufferViewObject
// - DataViewObject
// - TypedArrayObject (declared in vm/TypedArrayObject.h)
@ -185,9 +186,7 @@ class ArrayBufferObject : public JSObject
*/
static bool neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer);
inline uint8_t * dataPointer() const {
return (uint8_t *) elements;
}
uint8_t * dataPointer() const;
/*
* Discard the ArrayBuffer contents. For asm.js buffers, at least, should
@ -203,6 +202,10 @@ class ArrayBufferObject : public JSObject
return getClass() == &class_;
}
bool isSharedArrayBuffer() const {
return getElementsHeader()->isSharedArrayBuffer();
}
bool isAsmJSArrayBuffer() const {
return getElementsHeader()->isAsmJSArrayBuffer();
}
@ -292,11 +295,15 @@ InitArrayBufferViewDataPointer(ArrayBufferViewObject *obj, ArrayBufferObject *bu
PostBarrierTypedArrayObject(obj);
}
MOZ_ALWAYS_INLINE bool
IsArrayBuffer(HandleValue v)
{
return v.isObject() && v.toObject().is<ArrayBufferObject>();
}
/*
* Tests for either ArrayBufferObject or SharedArrayBufferObject.
* For specific class testing, use e.g., obj->is<ArrayBufferObject>().
*/
bool IsArrayBuffer(HandleValue v);
bool IsArrayBuffer(HandleObject obj);
bool IsArrayBuffer(JSObject *obj);
ArrayBufferObject &AsArrayBuffer(HandleObject obj);
ArrayBufferObject &AsArrayBuffer(JSObject *obj);
inline void
ArrayBufferViewObject::setBufferLink(ArrayBufferObject *buffer)

Просмотреть файл

@ -26,6 +26,9 @@ js_InitFunctionClass(JSContext *cx, js::HandleObject obj);
extern JSObject *
js_InitTypedArrayClasses(JSContext *cx, js::HandleObject obj);
extern JSObject *
js_InitSharedArrayBufferClass(JSContext *cx, js::HandleObject obj);
namespace js {
class Debugger;
@ -262,6 +265,9 @@ class GlobalObject : public JSObject
bool arrayBufferClassInitialized() const {
return classIsInitialized(JSProto_ArrayBuffer);
}
bool sharedArrayBufferClassInitialized() const {
return classIsInitialized(JSProto_SharedArrayBuffer);
}
bool errorClassesInitialized() const {
return classIsInitialized(JSProto_Error);
}
@ -388,6 +394,12 @@ class GlobalObject : public JSObject
return &global->getPrototype(JSProto_ArrayBuffer).toObject();
}
JSObject *getOrCreateSharedArrayBufferPrototype(JSContext *cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_SharedArrayBuffer))
return nullptr;
return &global->getPrototype(JSProto_SharedArrayBuffer).toObject();
}
static JSObject *getOrCreateCustomErrorPrototype(JSContext *cx,
Handle<GlobalObject*> global,
JSExnType exnType)

Просмотреть файл

@ -533,7 +533,9 @@ DenseElementsHeader::defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint3
JSObject *
js::ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
{
MOZ_ASSERT(obj->hasClass(&ArrayBufferObject::class_));
MOZ_ASSERT(obj->hasClass(&ArrayBufferObject::class_) ||
obj->hasClass(&SharedArrayBufferObject::class_));
if (obj->getPrivate())
return static_cast<JSObject *>(obj->getPrivate());
JSObject *delegate = NewObjectWithGivenProto(cx, &JSObject::class_,

Просмотреть файл

@ -759,10 +759,11 @@ class ObjectElements
CONVERT_DOUBLE_ELEMENTS = 0x1,
ASMJS_ARRAY_BUFFER = 0x2,
NEUTERED_BUFFER = 0x4,
SHARED_ARRAY_BUFFER = 0x8,
// Present only if these elements correspond to an array with
// non-writable length; never present for non-arrays.
NONWRITABLE_ARRAY_LENGTH = 0x8
NONWRITABLE_ARRAY_LENGTH = 0x10
};
private:
@ -771,6 +772,7 @@ class ObjectElements
friend class ArrayObject;
friend class ArrayBufferObject;
friend class ArrayBufferViewObject;
friend class SharedArrayBufferObject;
friend class TypedArrayObject;
friend class Nursery;
@ -830,6 +832,12 @@ class ObjectElements
void setIsNeuteredBuffer() {
flags |= NEUTERED_BUFFER;
}
bool isSharedArrayBuffer() const {
return flags & SHARED_ARRAY_BUFFER;
}
void setIsSharedArrayBuffer() {
flags |= SHARED_ARRAY_BUFFER;
}
bool hasNonwritableArrayLength() const {
return flags & NONWRITABLE_ARRAY_LENGTH;
}

Просмотреть файл

@ -0,0 +1,387 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "vm/SharedArrayObject.h"
#include "jsprf.h"
#include "jsobjinlines.h"
#ifdef XP_WIN
# include "jswin.h"
#else
# include <sys/mman.h>
#endif
#include "mozilla/Atomics.h"
#include "jit/AsmJS.h"
using namespace js;
using mozilla::IsNaN;
using mozilla::PodCopy;
#define SHAREDARRAYBUFFER_RESERVED_SLOTS 15
/*
* SharedArrayRawBuffer
*/
static inline void *
MapMemory(size_t length, bool commit)
{
#ifdef XP_WIN
int prot = (commit ? MEM_COMMIT : MEM_RESERVE);
int flags = (commit ? PAGE_READWRITE : PAGE_NOACCESS);
return VirtualAlloc(nullptr, length, prot, flags);
#else
int prot = (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE);
void *p = mmap(nullptr, length, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
if (p == MAP_FAILED)
return nullptr;
return p;
#endif
}
static inline void
UnmapMemory(void *addr, size_t len)
{
#ifdef XP_WIN
VirtualFree(addr, 0, MEM_RELEASE);
#else
munmap(addr, len);
#endif
}
static inline bool
MarkValidRegion(void *addr, size_t len)
{
#ifdef XP_WIN
if (!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE))
return false;
return true;
#else
if (mprotect(addr, len, PROT_READ | PROT_WRITE))
return false;
return true;
#endif
}
SharedArrayRawBuffer *
SharedArrayRawBuffer::New(uint32_t length)
{
// Enforced by SharedArrayBufferObject constructor.
JS_ASSERT(IsValidAsmJSHeapLength(length));
#ifdef JS_CPU_X64
// Get the entire reserved region (with all pages inaccessible)
void *p = MapMemory(AsmJSMappedSize, false);
if (!p)
return nullptr;
if (!MarkValidRegion(p, AsmJSPageSize + length)) {
UnmapMemory(p, AsmJSMappedSize);
return nullptr;
}
uint8_t *buffer = reinterpret_cast<uint8_t*>(p) + AsmJSPageSize;
uint8_t *base = buffer - sizeof(SharedArrayRawBuffer);
return new (base) SharedArrayRawBuffer(buffer, length);
#else
uint32_t allocSize = sizeof(SharedArrayRawBuffer) + length;
if (allocSize <= length)
return nullptr;
void *base = MapMemory(allocSize, true);
if (!base)
return nullptr;
uint8_t *buffer = reinterpret_cast<uint8_t*>(base) + sizeof(SharedArrayRawBuffer);
return new (base) SharedArrayRawBuffer(buffer, length);
#endif
}
void
SharedArrayRawBuffer::addReference()
{
JS_ASSERT(this->refcount > 0);
++this->refcount; // Atomic.
}
void
SharedArrayRawBuffer::dropReference()
{
// Drop the reference to the buffer.
uint32_t refcount = --this->refcount; // Atomic.
// If this was the final reference, release the buffer.
if (refcount == 0) {
#ifdef JS_CPU_X64
uint8_t *p = this->dataPointer() - AsmJSPageSize;
JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
UnmapMemory(p, AsmJSMappedSize);
#else
uint8_t *p = (uint8_t *)this;
UnmapMemory(p, this->length);
#endif
}
}
/*
* SharedArrayBufferObject
*/
bool
js::IsSharedArrayBuffer(HandleValue v)
{
return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
}
MOZ_ALWAYS_INLINE bool
SharedArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsSharedArrayBuffer(args.thisv()));
args.rval().setInt32(args.thisv().toObject().as<SharedArrayBufferObject>().byteLength());
return true;
}
bool
SharedArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
}
bool
SharedArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
{
int32_t length = 0;
CallArgs args = CallArgsFromVp(argc, vp);
if (argc > 0 && !ToInt32(cx, args[0], &length))
return false;
if (length < 0) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
JSObject *bufobj = New(cx, uint32_t(length));
if (!bufobj)
return false;
args.rval().setObject(*bufobj);
return true;
}
JSObject *
SharedArrayBufferObject::New(JSContext *cx, uint32_t length)
{
if (!IsValidAsmJSHeapLength(length)) {
ScopedJSFreePtr<char> msg(
JS_smprintf("SharedArrayBuffer byteLength 0x%x is not a valid length. The next valid "
"length is 0x%x", length, RoundUpToNextValidAsmJSHeapLength(length)));
JS_ReportError(cx, msg.get());
return nullptr;
}
RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
if (!obj)
return nullptr;
JS_ASSERT(obj->getClass() == &class_);
Rooted<js::Shape*> empty(cx);
empty = EmptyShape::getInitialShape(cx, &class_, obj->getProto(), obj->getParent(),
obj->getMetadata(), gc::FINALIZE_OBJECT16_BACKGROUND);
if (!empty)
return nullptr;
obj->setLastPropertyInfallible(empty);
obj->setFixedElements();
obj->as<SharedArrayBufferObject>().initElementsHeader(obj->getElementsHeader(), length);
obj->getElementsHeader()->setIsSharedArrayBuffer();
SharedArrayRawBuffer *buffer = SharedArrayRawBuffer::New(length);
if (!buffer)
return nullptr;
obj->as<SharedArrayBufferObject>().acceptRawBuffer(buffer);
return obj;
}
JSObject *
SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
{
RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
if (!obj)
return nullptr;
JS_ASSERT(obj->getClass() == &class_);
Rooted<js::Shape*> empty(cx);
empty = EmptyShape::getInitialShape(cx, &class_, obj->getProto(), obj->getParent(),
obj->getMetadata(), gc::FINALIZE_OBJECT16_BACKGROUND);
if (!empty)
return nullptr;
obj->setLastPropertyInfallible(empty);
obj->setFixedElements();
obj->as<SharedArrayBufferObject>().initElementsHeader(obj->getElementsHeader(),
buffer->byteLength());
obj->getElementsHeader()->setIsSharedArrayBuffer();
obj->as<SharedArrayBufferObject>().acceptRawBuffer(buffer);
return obj;
}
void
SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer *buffer)
{
setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, PrivateValue(buffer));
}
void
SharedArrayBufferObject::dropRawBuffer()
{
setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, UndefinedValue());
}
SharedArrayRawBuffer *
SharedArrayBufferObject::rawBufferObject() const
{
// RAWBUF_SLOT must be populated via acceptRawBuffer(),
// and the raw buffer must not have been dropped.
Value v = getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
return (SharedArrayRawBuffer *)v.toPrivate();
}
uint8_t *
SharedArrayBufferObject::dataPointer() const
{
return rawBufferObject()->dataPointer();
}
uint32_t
SharedArrayBufferObject::byteLength() const
{
return rawBufferObject()->byteLength();
}
void
SharedArrayBufferObject::Finalize(FreeOp *fop, JSObject *obj)
{
SharedArrayBufferObject &buf = obj->as<SharedArrayBufferObject>();
// Detect the case of failure during SharedArrayBufferObject creation,
// which causes a SharedArrayRawBuffer to never be attached.
Value v = buf.getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
if (!v.isUndefined()) {
buf.rawBufferObject()->dropReference();
buf.dropRawBuffer();
}
}
/*
* SharedArrayBufferObject
*/
const Class SharedArrayBufferObject::protoClass = {
"SharedArrayBufferPrototype",
JSCLASS_HAS_PRIVATE |
JSCLASS_HAS_RESERVED_SLOTS(SHAREDARRAYBUFFER_RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
JS_PropertyStub, /* addProperty */
JS_DeletePropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub
};
const Class SharedArrayBufferObject::class_ = {
"SharedArrayBuffer",
JSCLASS_HAS_PRIVATE |
JSCLASS_IMPLEMENTS_BARRIERS |
Class::NON_NATIVE |
JSCLASS_HAS_RESERVED_SLOTS(SHAREDARRAYBUFFER_RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
JS_PropertyStub, /* addProperty */
JS_DeletePropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
SharedArrayBufferObject::Finalize,
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
ArrayBufferObject::obj_trace,
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
{
ArrayBufferObject::obj_lookupGeneric,
ArrayBufferObject::obj_lookupProperty,
ArrayBufferObject::obj_lookupElement,
ArrayBufferObject::obj_lookupSpecial,
ArrayBufferObject::obj_defineGeneric,
ArrayBufferObject::obj_defineProperty,
ArrayBufferObject::obj_defineElement,
ArrayBufferObject::obj_defineSpecial,
ArrayBufferObject::obj_getGeneric,
ArrayBufferObject::obj_getProperty,
ArrayBufferObject::obj_getElement,
ArrayBufferObject::obj_getSpecial,
ArrayBufferObject::obj_setGeneric,
ArrayBufferObject::obj_setProperty,
ArrayBufferObject::obj_setElement,
ArrayBufferObject::obj_setSpecial,
ArrayBufferObject::obj_getGenericAttributes,
ArrayBufferObject::obj_setGenericAttributes,
ArrayBufferObject::obj_deleteProperty,
ArrayBufferObject::obj_deleteElement,
ArrayBufferObject::obj_deleteSpecial,
nullptr, nullptr, /* watch/unwatch */
nullptr, /* slice */
ArrayBufferObject::obj_enumerate,
nullptr, /* thisObject */
}
};
JSObject *
js_InitSharedArrayBufferClass(JSContext *cx, HandleObject obj)
{
JS_ASSERT(obj->isNative());
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
RootedObject proto(cx, global->createBlankPrototype(cx, &SharedArrayBufferObject::protoClass));
if (!proto)
return nullptr;
RootedFunction ctor(cx, global->createConstructor(cx, SharedArrayBufferObject::class_constructor,
cx->names().SharedArrayBuffer, 1));
if (!ctor)
return nullptr;
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
RootedId byteLengthId(cx, NameToId(cx->names().byteLength));
unsigned flags = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
JSObject *getter = NewFunction(cx, NullPtr(), SharedArrayBufferObject::byteLengthGetter, 0,
JSFunction::NATIVE_FUN, global, NullPtr());
if (!getter)
return nullptr;
RootedValue value(cx, UndefinedValue());
if (!DefineNativeProperty(cx, proto, byteLengthId, value,
JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, flags, 0, 0))
{
return nullptr;
}
if (!DefineConstructorAndPrototype(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
return nullptr;
return proto;
}

Просмотреть файл

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_SharedArrayObject_h
#define vm_SharedArrayObject_h
#include "mozilla/Atomics.h"
#include "jsapi.h"
#include "jsobj.h"
#include "jstypes.h"
#include "gc/Barrier.h"
#include "vm/ArrayBufferObject.h"
typedef struct JSProperty JSProperty;
namespace js {
/*
* SharedArrayRawBuffer
*
* A bookkeeping object always stored immediately before the raw buffer.
* The buffer itself is mmap()'d and refcounted.
* SharedArrayBufferObjects and AsmJS code may hold references.
*
* |<------ sizeof ------>|<- length ->|
*
* | waste | SharedArrayRawBuffer | data array | waste |
*/
class SharedArrayRawBuffer
{
private:
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount;
uint32_t length;
protected:
SharedArrayRawBuffer(uint8_t *buffer, uint32_t length)
: refcount(1), length(length)
{
JS_ASSERT(buffer == dataPointer());
}
public:
static SharedArrayRawBuffer *New(uint32_t length);
inline uint8_t *dataPointer() const {
return ((uint8_t *)this) + sizeof(SharedArrayRawBuffer);
}
inline uint32_t byteLength() const {
return length;
}
void addReference();
void dropReference();
};
/*
* SharedArrayBufferObject
*
* When transferred to a WebWorker, the buffer is not neutered on the parent side,
* and both child and parent reference the same buffer.
*/
class SharedArrayBufferObject : public ArrayBufferObject
{
static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
public:
static const Class class_;
static const Class protoClass;
// Slot used for storing a pointer to the SharedArrayRawBuffer.
// First two slots hold the ObjectElements.
static const int32_t RAWBUF_SLOT = 2;
static bool class_constructor(JSContext *cx, unsigned argc, Value *vp);
// Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
static JSObject *New(JSContext *cx, uint32_t length);
// Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer.
static JSObject *New(JSContext *cx, SharedArrayRawBuffer *buffer);
static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
static void Finalize(FreeOp *fop, JSObject *obj);
void acceptRawBuffer(SharedArrayRawBuffer *buffer);
void dropRawBuffer();
SharedArrayRawBuffer *rawBufferObject() const;
uint8_t *dataPointer() const;
uint32_t byteLength() const;
};
bool
IsSharedArrayBuffer(HandleValue v);
} // namespace js
#endif // vm_SharedArrayObject_h

Просмотреть файл

@ -32,9 +32,11 @@
#include "gc/Marking.h"
#include "jit/AsmJS.h"
#include "jit/AsmJSModule.h"
#include "vm/ArrayBufferObject.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/NumericConversions.h"
#include "vm/SharedArrayObject.h"
#include "vm/WrapperObject.h"
#include "jsatominlines.h"
@ -109,6 +111,12 @@ TypedArrayObject::neuter(JSContext *cx)
setPrivate(nullptr);
}
ArrayBufferObject *
TypedArrayObject::sharedBuffer() const
{
return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
}
bool
TypedArrayObject::obj_lookupGeneric(JSContext *cx, HandleObject tarray, HandleId id,
MutableHandleObject objp, MutableHandleShape propp)
@ -612,7 +620,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID()));
obj->setSlot(BUFFER_SLOT, ObjectValue(*bufobj));
Rooted<ArrayBufferObject *> buffer(cx, &bufobj->as<ArrayBufferObject>());
Rooted<ArrayBufferObject *> buffer(cx, &AsArrayBuffer(bufobj));
InitArrayBufferViewDataPointer(obj, buffer, byteOffset);
obj->setSlot(LENGTH_SLOT, Int32Value(len));
@ -701,8 +709,11 @@ class TypedArrayObjectTemplate : public TypedArrayObject
* properties from the object, treating it as some sort of array.
* Note that offset and length will be ignored
*/
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>())
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>() &&
!UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>())
{
return fromArray(cx, dataObj);
}
/* (ArrayBuffer, [byteOffset, [length]]) */
int32_t byteOffset = 0;
@ -974,7 +985,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return nullptr; // must be arrayBuffer
}
JS_ASSERT(bufobj->is<ArrayBufferObject>() || bufobj->is<ProxyObject>());
JS_ASSERT(IsArrayBuffer(bufobj) || bufobj->is<ProxyObject>());
if (bufobj->is<ProxyObject>()) {
/*
* Normally, NonGenericMethodGuard handles the case of transparent
@ -992,7 +1003,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
JS_ReportError(cx, "Permission denied to access object");
return nullptr;
}
if (wrapped->is<ArrayBufferObject>()) {
if (IsArrayBuffer(wrapped)) {
/*
* And for even more fun, the new view's prototype should be
* set to the origin compartment's prototype object, not the
@ -1027,12 +1038,12 @@ class TypedArrayObjectTemplate : public TypedArrayObject
}
}
if (!bufobj->is<ArrayBufferObject>()) {
if (!IsArrayBuffer(bufobj)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return nullptr; // must be arrayBuffer
}
ArrayBufferObject &buffer = bufobj->as<ArrayBufferObject>();
ArrayBufferObject &buffer = AsArrayBuffer(bufobj);
if (byteOffset > buffer.byteLength() || byteOffset % sizeof(NativeType) != 0) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
@ -1625,7 +1636,7 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
// Verify that the private slot is at the expected place
JS_ASSERT(dvobj.numFixedSlots() == DATA_SLOT);
arrayBuffer->as<ArrayBufferObject>().addView(&dvobj);
arrayBuffer->addView(&dvobj);
return &dvobj;
}
@ -1633,13 +1644,13 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
bool
DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, HandleObject proto)
{
if (!bufobj->is<ArrayBufferObject>()) {
if (!IsArrayBuffer(bufobj)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
"DataView", "ArrayBuffer", bufobj->getClass()->name);
return false;
}
Rooted<ArrayBufferObject*> buffer(cx, &bufobj->as<ArrayBufferObject>());
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
uint32_t bufferLength = buffer->byteLength();
uint32_t byteOffset = 0;
uint32_t byteLength = bufferLength;
@ -1697,7 +1708,7 @@ DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj))
return false;
if (bufobj->is<WrapperObject>() && UncheckedUnwrap(bufobj)->is<ArrayBufferObject>()) {
if (bufobj->is<WrapperObject>() && IsArrayBuffer(UncheckedUnwrap(bufobj))) {
Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
Rooted<JSObject*> proto(cx, global->getOrCreateDataViewPrototype(cx));
if (!proto)

Просмотреть файл

@ -67,8 +67,12 @@ class TypedArrayObject : public ArrayBufferViewObject
return tarr->getFixedSlot(LENGTH_SLOT);
}
ArrayBufferObject *sharedBuffer() const;
ArrayBufferObject *buffer() const {
return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<ArrayBufferObject>();
JSObject &obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObject();
if (obj.is<ArrayBufferObject>())
return &obj.as<ArrayBufferObject>();
return sharedBuffer();
}
uint32_t byteOffset() const {
return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();