https://github.com/mozilla/gecko-dev
Tip revision: 68bba3abd53ee78d9996e00a47b9d024604d30bf authored by Ryan VanderMeulen on 29 July 2015, 14:12:35 UTC
Added tag B2G_2_0_END for changeset 2e6f1d4deff9 on a CLOSED TREE
Added tag B2G_2_0_END for changeset 2e6f1d4deff9 on a CLOSED TREE
Tip revision: 68bba3a
SharedArrayObject.cpp
/* -*- 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
#ifdef MOZ_VALGRIND
# include <valgrind/memcheck.h>
#endif
#include "mozilla/Atomics.h"
#include "jit/AsmJS.h"
using namespace js;
using mozilla::IsNaN;
using mozilla::PodCopy;
/*
* 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;
size_t validLength = AsmJSPageSize + length;
if (!MarkValidRegion(p, validLength)) {
UnmapMemory(p, AsmJSMappedSize);
return nullptr;
}
# if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
// Tell Valgrind/Memcheck to not report accesses in the inaccessible region.
VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)p + validLength,
AsmJSMappedSize-validLength);
# endif
#else
uint32_t allocSize = length + AsmJSPageSize;
if (allocSize <= length)
return nullptr;
void* p = MapMemory(allocSize, true);
if (!p)
return nullptr;
#endif
uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + AsmJSPageSize;
uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
return new (base) SharedArrayRawBuffer(buffer, length);
}
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) {
uint8_t* p = this->dataPointer() - AsmJSPageSize;
JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
#ifdef JS_CPU_X64
UnmapMemory(p, AsmJSMappedSize);
# if defined(MOZ_VALGRIND) \
&& defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
// Tell Valgrind/Memcheck to recommence reporting accesses in the
// previously-inaccessible region.
if (AsmJSMappedSize > 0) {
VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(p, AsmJSMappedSize);
}
# endif
#else
UnmapMemory(p, this->length + AsmJSPageSize);
#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 (args.length() > 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;
}
SharedArrayRawBuffer* buffer = SharedArrayRawBuffer::New(length);
if (!buffer)
return nullptr;
return New(cx, buffer);
}
JSObject*
SharedArrayBufferObject::New(JSContext* cx, SharedArrayRawBuffer* buffer)
{
Rooted<SharedArrayBufferObject*> obj(cx, NewBuiltinClassInstance<SharedArrayBufferObject>(cx));
if (!obj)
return nullptr;
JS_ASSERT(obj->getClass() == &class_);
obj->initialize(buffer->byteLength(), nullptr, DoesntOwnData);
obj->acceptRawBuffer(buffer);
obj->setIsSharedArrayBuffer();
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_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_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::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
};
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 attrs = 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, attrs))
{
return nullptr;
}
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
return nullptr;
return proto;
}