mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 14:25:52 +00:00
Bug 925233 - OdinMonkey: fix neutering interaction with asm.js (r=sfink)
This commit is contained in:
parent
db439a9bf3
commit
2abbde5d32
74
js/src/jit-test/tests/asm.js/testNeuter.js
Normal file
74
js/src/jit-test/tests/asm.js/testNeuter.js
Normal file
@ -0,0 +1,74 @@
|
||||
load(libdir + "asm.js");
|
||||
|
||||
function f(stdlib, foreign, buffer) {
|
||||
"use asm";
|
||||
var i32 = new stdlib.Int32Array(buffer);
|
||||
function set(i,j) {
|
||||
i=i|0;
|
||||
j=j|0;
|
||||
i32[i>>2] = j;
|
||||
}
|
||||
function get(i) {
|
||||
i=i|0;
|
||||
return i32[i>>2]|0
|
||||
}
|
||||
return {get:get, set:set}
|
||||
}
|
||||
if (isAsmJSCompilationAvailable())
|
||||
assertEq(isAsmJSModule(f), true);
|
||||
|
||||
var i32 = new Int32Array(1024);
|
||||
var buffer = i32.buffer;
|
||||
var {get, set} = f(this, null, buffer);
|
||||
if (isAsmJSCompilationAvailable())
|
||||
assertEq(isAsmJSFunction(get) && isAsmJSFunction(set), true);
|
||||
|
||||
set(4, 42);
|
||||
assertEq(get(4), 42);
|
||||
|
||||
neuter(buffer);
|
||||
|
||||
// These operations may throw internal errors
|
||||
try {
|
||||
assertEq(get(4), 0);
|
||||
set(0, 42);
|
||||
assertEq(get(0), 0);
|
||||
} catch (e) {
|
||||
assertEq(String(e).indexOf("InternalError"), 0);
|
||||
}
|
||||
|
||||
function f2(stdlib, foreign, buffer) {
|
||||
"use asm";
|
||||
var i32 = new stdlib.Int32Array(buffer);
|
||||
var ffi = foreign.ffi;
|
||||
function inner(i) {
|
||||
i=i|0;
|
||||
ffi();
|
||||
return i32[i>>2]|0
|
||||
}
|
||||
return inner
|
||||
}
|
||||
if (isAsmJSCompilationAvailable())
|
||||
assertEq(isAsmJSModule(f2), true);
|
||||
|
||||
var i32 = new Int32Array(1024);
|
||||
var buffer = i32.buffer;
|
||||
var threw = false;
|
||||
function ffi() {
|
||||
try {
|
||||
neuter(buffer);
|
||||
} catch (e) {
|
||||
assertEq(String(e).indexOf("InternalError"), 0);
|
||||
threw = true;
|
||||
}
|
||||
}
|
||||
var inner = f2(this, {ffi:ffi}, buffer);
|
||||
if (isAsmJSCompilationAvailable())
|
||||
assertEq(isAsmJSFunction(inner), true);
|
||||
i32[2] = 13;
|
||||
var result = inner(8);
|
||||
if (threw)
|
||||
assertEq(result, 13);
|
||||
else
|
||||
assertEq(result, 0);
|
||||
|
@ -60,6 +60,7 @@ class AsmJSActivation
|
||||
|
||||
JSContext *cx() { return cx_; }
|
||||
AsmJSModule &module() const { return module_; }
|
||||
AsmJSActivation *prev() const { return prev_; }
|
||||
|
||||
// Read by JIT code:
|
||||
static unsigned offsetOfContext() { return offsetof(AsmJSActivation, cx_); }
|
||||
|
@ -331,6 +331,15 @@ CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
|
||||
unsigned exportIndex = callee->getExtendedSlot(ASM_EXPORT_INDEX_SLOT).toInt32();
|
||||
const AsmJSModule::ExportedFunction &func = module.exportedFunction(exportIndex);
|
||||
|
||||
// An asm.js module is specialized to its heap's base address and length
|
||||
// which is normally immutable except for the neuter operation that occurs
|
||||
// when an ArrayBuffer is transfered. Throw an internal error if we try to
|
||||
// run with a neutered heap.
|
||||
if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) {
|
||||
js_ReportOverRecursed(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The calling convention for an external call into asm.js is to pass an
|
||||
// array of 8-byte values where each value contains either a coerced int32
|
||||
// (in the low word) or double value, with the coercions specified by the
|
||||
|
@ -729,6 +729,10 @@ class AsmJSModule
|
||||
JS_ASSERT(linked_);
|
||||
return heapDatum();
|
||||
}
|
||||
ArrayBufferObject *maybeHeapBufferObject() const {
|
||||
JS_ASSERT(linked_);
|
||||
return maybeHeap_;
|
||||
}
|
||||
size_t heapLength() const {
|
||||
JS_ASSERT(linked_);
|
||||
return maybeHeap_ ? maybeHeap_->byteLength() : 0;
|
||||
|
@ -755,12 +755,13 @@ class ObjectElements
|
||||
{
|
||||
public:
|
||||
enum Flags {
|
||||
CONVERT_DOUBLE_ELEMENTS = 0x1,
|
||||
ASMJS_ARRAY_BUFFER = 0x2,
|
||||
CONVERT_DOUBLE_ELEMENTS = 0x1,
|
||||
ASMJS_ARRAY_BUFFER = 0x2,
|
||||
NEUTERED_BUFFER = 0x4,
|
||||
|
||||
// Present only if these elements correspond to an array with
|
||||
// non-writable length; never present for non-arrays.
|
||||
NONWRITABLE_ARRAY_LENGTH = 0x4
|
||||
NONWRITABLE_ARRAY_LENGTH = 0x8
|
||||
};
|
||||
|
||||
private:
|
||||
@ -768,6 +769,7 @@ class ObjectElements
|
||||
friend class ObjectImpl;
|
||||
friend class ArrayObject;
|
||||
friend class ArrayBufferObject;
|
||||
friend class TypedArrayObject;
|
||||
friend class Nursery;
|
||||
|
||||
template <ExecutionMode mode>
|
||||
@ -817,6 +819,12 @@ class ObjectElements
|
||||
void setIsAsmJSArrayBuffer() {
|
||||
flags |= ASMJS_ARRAY_BUFFER;
|
||||
}
|
||||
bool isNeuteredBuffer() const {
|
||||
return flags & NEUTERED_BUFFER;
|
||||
}
|
||||
void setIsNeuteredBuffer() {
|
||||
flags |= NEUTERED_BUFFER;
|
||||
}
|
||||
bool hasNonwritableArrayLength() const {
|
||||
return flags & NONWRITABLE_ARRAY_LENGTH;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/Marking.h"
|
||||
#include "jit/AsmJS.h"
|
||||
#include "jit/AsmJSModule.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/NumericConversions.h"
|
||||
@ -330,27 +331,32 @@ GetViewListRef(ArrayBufferObject *obj)
|
||||
return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::neuterViews(JSContext *maybecx)
|
||||
bool
|
||||
ArrayBufferObject::neuterViews(JSContext *cx)
|
||||
{
|
||||
ArrayBufferViewObject *view;
|
||||
for (view = GetViewList(this); view; view = view->nextView()) {
|
||||
view->neuter();
|
||||
|
||||
// Notify compiled jit code that the base pointer has moved.
|
||||
if (maybecx)
|
||||
MarkObjectStateChange(maybecx, view);
|
||||
MarkObjectStateChange(cx, view);
|
||||
}
|
||||
|
||||
// neuterAsmJSArrayBuffer adjusts state specific to the ArrayBuffer data
|
||||
// itself, but it only affects the behavior of views
|
||||
if (isAsmJSArrayBuffer())
|
||||
ArrayBufferObject::neuterAsmJSArrayBuffer(*this);
|
||||
if (isAsmJSArrayBuffer()) {
|
||||
if (!ArrayBufferObject::neuterAsmJSArrayBuffer(cx, *this))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::changeContents(JSContext *maybecx, ObjectElements *newHeader)
|
||||
{
|
||||
JS_ASSERT(!isAsmJSArrayBuffer());
|
||||
|
||||
// Grab out data before invalidating it.
|
||||
uint32_t byteLengthCopy = byteLength();
|
||||
uintptr_t oldDataPointer = uintptr_t(dataPointer());
|
||||
@ -391,6 +397,8 @@ ArrayBufferObject::neuter(JSContext *cx)
|
||||
|
||||
uint32_t byteLen = 0;
|
||||
updateElementsHeader(getElementsHeader(), byteLen);
|
||||
|
||||
getElementsHeader()->setIsNeuteredBuffer();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -523,22 +531,6 @@ ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
|
||||
munmap(p, AsmJSMappedSize);
|
||||
# endif
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::neuterAsmJSArrayBuffer(ArrayBufferObject &buffer)
|
||||
{
|
||||
// Protect all the pages so that any read/write will generate a fault which
|
||||
// the AsmJSMemoryFaultHandler will turn into the expected result value.
|
||||
JS_ASSERT(buffer.isAsmJSArrayBuffer());
|
||||
JS_ASSERT(buffer.byteLength() % AsmJSAllocationGranularity == 0);
|
||||
#ifdef XP_WIN
|
||||
if (!VirtualAlloc(buffer.dataPointer(), buffer.byteLength(), MEM_RESERVE, PAGE_NOACCESS))
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
if (mprotect(buffer.dataPointer(), buffer.byteLength(), PROT_NONE))
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
#else /* defined(JS_ION) && defined(JS_CPU_X64) */
|
||||
bool
|
||||
ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
|
||||
@ -559,14 +551,23 @@ ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
fop->free_(obj->as<ArrayBufferObject>().getElementsHeader());
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::neuterAsmJSArrayBuffer(ArrayBufferObject &buffer)
|
||||
{
|
||||
// TODO: be ever-so-slightly unsound (but safe) for now.
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
ArrayBufferObject::neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
|
||||
{
|
||||
AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread();
|
||||
for (; act; act = act->prev()) {
|
||||
if (act->module().maybeHeapBufferObject() == &buffer)
|
||||
break;
|
||||
}
|
||||
if (!act)
|
||||
return true;
|
||||
|
||||
js_ReportOverRecursed(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::addView(ArrayBufferViewObject *view)
|
||||
{
|
||||
@ -679,7 +680,8 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents,
|
||||
|
||||
// Neuter the views, which may also mprotect(PROT_NONE) the buffer. So do
|
||||
// it after copying out the data.
|
||||
buffer.neuterViews(cx);
|
||||
if (!buffer.neuterViews(cx))
|
||||
return false;
|
||||
|
||||
if (!own) {
|
||||
// If header has dynamically allocated elements, revert it back to
|
||||
@ -4024,12 +4026,14 @@ JS_GetArrayBufferData(JSObject *obj)
|
||||
return buffer.dataPointer();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
JS_NeuterArrayBuffer(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(bool)
|
||||
JS_NeuterArrayBuffer(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
|
||||
buffer.neuterViews(cx);
|
||||
if (!buffer.neuterViews(cx))
|
||||
return false;
|
||||
buffer.neuter(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
|
@ -198,7 +198,7 @@ class ArrayBufferObject : public JSObject
|
||||
/*
|
||||
* Neuter all views of an ArrayBuffer.
|
||||
*/
|
||||
void neuterViews(JSContext *maybecx);
|
||||
bool neuterViews(JSContext *cx);
|
||||
|
||||
inline uint8_t * dataPointer() const {
|
||||
return (uint8_t *) elements;
|
||||
@ -221,8 +221,11 @@ class ArrayBufferObject : public JSObject
|
||||
bool isAsmJSArrayBuffer() const {
|
||||
return getElementsHeader()->isAsmJSArrayBuffer();
|
||||
}
|
||||
bool isNeutered() const {
|
||||
return getElementsHeader()->isNeuteredBuffer();
|
||||
}
|
||||
static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
|
||||
static void neuterAsmJSArrayBuffer(ArrayBufferObject &buffer);
|
||||
static bool neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
|
||||
static void releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user