--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -479,16 +479,24 @@ AsmJSModule::setAutoFlushICacheRange()
static void
AsmJSReportOverRecursed()
{
JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
js_ReportOverRecursed(cx);
}
+static void
+OnDetached()
+{
+ // See hasDetachedHeap comment in LinkAsmJS.
+ JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
+}
+
static bool
AsmJSHandleExecutionInterrupt()
{
AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
act->module().setInterrupted(true);
bool ret = HandleExecutionInterrupt(act->cx());
act->module().setInterrupted(false);
return ret;
@@ -662,16 +670,18 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
case AsmJSImm_Runtime:
return cx->runtimeAddressForJit();
case AsmJSImm_RuntimeInterrupt:
return cx->runtimeAddressOfInterrupt();
case AsmJSImm_StackLimit:
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
case AsmJSImm_ReportOverRecursed:
return RedirectCall(FuncCast(AsmJSReportOverRecursed), Args_General0);
+ case AsmJSImm_OnDetached:
+ return RedirectCall(FuncCast(OnDetached), Args_General0);
case AsmJSImm_HandleExecutionInterrupt:
return RedirectCall(FuncCast(AsmJSHandleExecutionInterrupt), Args_General0);
case AsmJSImm_InvokeFromAsmJS_Ignore:
return RedirectCall(FuncCast(InvokeFromAsmJS_Ignore), Args_General3);
case AsmJSImm_InvokeFromAsmJS_ToInt32:
return RedirectCall(FuncCast(InvokeFromAsmJS_ToInt32), Args_General3);
case AsmJSImm_InvokeFromAsmJS_ToNumber:
return RedirectCall(FuncCast(InvokeFromAsmJS_ToNumber), Args_General3);
@@ -875,35 +885,43 @@ AsmJSModule::restoreToInitialState(Array
}
bool
AsmJSModule::detachHeap(JSContext *cx)
{
MOZ_ASSERT(isDynamicallyLinked());
MOZ_ASSERT(maybeHeap_);
+ // Content JS should not be able to run (and detach heap) from within an
+ // interrupt callback, but in case it does, fail. Otherwise, the heap can
+ // change at an arbitrary instruction and break the assumption below.
+ if (interrupted_) {
+ JS_ReportError(cx, "attempt to detach from inside interrupt handler");
+ return false;
+ }
+
+ // Even if this->active(), to reach here, the activation must have called
+ // out via an FFI stub. FFI stubs check if heapDatum() is null on reentry
+ // and throw an exception if so.
+ MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI ||
+ activation()->exitReason() == AsmJSExit::Reason_SlowFFI);
+
AutoUnprotectCode auc(cx, *this);
restoreHeapToInitialState(maybeHeap_);
MOZ_ASSERT(hasDetachedHeap());
return true;
}
bool
js::OnDetachAsmJSArrayBuffer(JSContext *cx, Handle<ArrayBufferObject*> buffer)
{
for (AsmJSModule *m = cx->runtime()->linkedAsmJSModules; m; m = m->nextLinked()) {
- if (buffer == m->maybeHeapBufferObject()) {
- if (m->active()) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
- return false;
- }
- if (!m->detachHeap(cx))
- return false;
- }
+ if (buffer == m->maybeHeapBufferObject() && !m->detachHeap(cx))
+ return false;
}
return true;
}
static void
AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
{
fop->delete_(&obj->as<AsmJSModuleObject>().module());
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -1049,16 +1049,20 @@ class AsmJSModule
MOZ_ASSERT(!isFinishedWithModulePrologue());
pod.funcPtrTableAndExitBytes_ = 0;
MOZ_ASSERT(isFinishedWithModulePrologue());
}
/*************************************************************************/
// These functions are called while parsing/compiling function bodies:
+ bool hasArrayView() const {
+ MOZ_ASSERT(isFinishedWithModulePrologue());
+ return pod.hasArrayView_;
+ }
void addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
MOZ_ASSERT(isFinishedWithModulePrologue());
MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
MOZ_ASSERT(max <= pod.maxHeapLength_);
MOZ_ASSERT(min <= max);
pod.heapLengthMask_ = mask;
@@ -1238,20 +1242,16 @@ class AsmJSModule
bool finish(ExclusiveContext *cx,
frontend::TokenStream &tokenStream,
jit::MacroAssembler &masm,
const jit::Label &interruptLabel);
/*************************************************************************/
// These accessor functions can be used after finish():
- bool hasArrayView() const {
- MOZ_ASSERT(isFinished());
- return pod.hasArrayView_;
- }
unsigned numFFIs() const {
MOZ_ASSERT(isFinished());
return pod.numFFIs_;
}
uint32_t srcEndBeforeCurly() const {
MOZ_ASSERT(isFinished());
return srcStart_ + pod.srcLength_;
}
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1324,16 +1324,17 @@ class MOZ_STACK_CLASS ModuleCompiler
FuncPtrTableVector funcPtrTables_;
ArrayViewVector arrayViews_;
ExitMap exits_;
MathNameMap standardLibraryMathNames_;
SimdOperationNameMap standardLibrarySimdOpNames_;
NonAssertingLabel stackOverflowLabel_;
NonAssertingLabel asyncInterruptLabel_;
NonAssertingLabel syncInterruptLabel_;
+ NonAssertingLabel onDetachedLabel_;
UniquePtr<char[], JS::FreePolicy> errorString_;
uint32_t errorOffset_;
bool errorOverRecursed_;
int64_t usecBefore_;
SlowFunctionVector slowFunctions_;
@@ -1517,16 +1518,17 @@ class MOZ_STACK_CLASS ModuleCompiler
ExclusiveContext *cx() const { return cx_; }
AsmJSParser &parser() const { return parser_; }
TokenStream &tokenStream() const { return parser_.tokenStream; }
MacroAssembler &masm() { return masm_; }
Label &stackOverflowLabel() { return stackOverflowLabel_; }
Label &asyncInterruptLabel() { return asyncInterruptLabel_; }
Label &syncInterruptLabel() { return syncInterruptLabel_; }
+ Label &onDetachedLabel() { return onDetachedLabel_; }
bool hasError() const { return errorString_ != nullptr; }
const AsmJSModule &module() const { return *module_.get(); }
bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); }
bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); }
bool supportsSimd() const { return supportsSimd_; }
ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; }
PropertyName *moduleFunctionName() const { return moduleFunctionName_; }
@@ -7633,16 +7635,37 @@ FillArgumentArray(ModuleCompiler &m, con
masm.canonicalizeDouble(ScratchDoubleReg);
masm.storeDouble(ScratchDoubleReg, dstAddr);
}
break;
}
}
}
+// If an FFI detaches its heap (viz., via ArrayBuffer.transfer), it must
+// call change-heap to another heap (viz., the new heap returned by transfer)
+// before returning to asm.js code. If the application fails to do this (if the
+// heap pointer is null), jump to a stub.
+static void
+GenerateCheckForHeapDetachment(ModuleCompiler &m, Register scratch)
+{
+ if (!m.module().hasArrayView())
+ return;
+
+ MacroAssembler &masm = m.masm();
+ AssertStackAlignment(masm, ABIStackAlignment);
+#if defined(JS_CODEGEN_X86)
+ CodeOffsetLabel label = masm.movlWithPatch(PatchedAbsoluteAddress(), scratch);
+ masm.append(AsmJSGlobalAccess(label, AsmJSHeapGlobalDataOffset));
+ masm.branchTestPtr(Assembler::Zero, scratch, scratch, &m.onDetachedLabel());
+#else
+ masm.branchTestPtr(Assembler::Zero, HeapReg, HeapReg, &m.onDetachedLabel());
+#endif
+}
+
static bool
GenerateFFIInterpExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
unsigned exitIndex, Label *throwLabel)
{
MacroAssembler &masm = m.masm();
MOZ_ASSERT(masm.framePushed() == 0);
// Argument types for InvokeFromAsmJS_*:
@@ -7716,18 +7739,20 @@ GenerateFFIInterpExit(ModuleCompiler &m,
break;
case RetType::Float:
MOZ_CRASH("Float32 shouldn't be returned from a FFI");
case RetType::Int32x4:
case RetType::Float32x4:
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
}
- // The heap pointer may have changed during the FFI, so reload it.
+ // The heap pointer may have changed during the FFI, so reload it and test
+ // for detachment.
masm.loadAsmJSHeapRegisterFromGlobalData();
+ GenerateCheckForHeapDetachment(m, ABIArgGenerator::NonReturn_VolatileReg0);
Label profilingReturn;
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::SlowFFI, &profilingReturn);
return m.finishGeneratingInterpExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
}
// On ARM/MIPS, we need to include an extra word of space at the top of the
// stack so we can explicitly store the return address before making the call
@@ -7926,24 +7951,28 @@ GenerateFFIIonExit(ModuleCompiler &m, co
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
}
Label done;
masm.bind(&done);
MOZ_ASSERT(masm.framePushed() == framePushed);
- // Reload pinned registers after all calls into arbitrary JS.
+ // Reload the global register since Ion code can clobber any register.
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
JS_STATIC_ASSERT(MaybeSavedGlobalReg > 0);
masm.loadPtr(Address(StackPointer, savedGlobalOffset), GlobalReg);
#else
JS_STATIC_ASSERT(MaybeSavedGlobalReg == 0);
#endif
+
+ // The heap pointer has to be reloaded anyway since Ion could have clobbered
+ // it. Additionally, the FFI may have detached the heap buffer.
masm.loadAsmJSHeapRegisterFromGlobalData();
+ GenerateCheckForHeapDetachment(m, ABIArgGenerator::NonReturn_VolatileReg0);
Label profilingReturn;
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::IonFFI, &profilingReturn);
if (oolConvert.used()) {
masm.bind(&oolConvert);
masm.setFramePushed(framePushed);
@@ -8094,16 +8123,30 @@ GenerateBuiltinThunk(ModuleCompiler &m,
static bool
GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
{
MacroAssembler &masm = m.masm();
GenerateAsmJSStackOverflowExit(masm, &m.stackOverflowLabel(), throwLabel);
return m.finishGeneratingInlineStub(&m.stackOverflowLabel()) && !masm.oom();
}
+static bool
+GenerateOnDetachedLabelExit(ModuleCompiler &m, Label *throwLabel)
+{
+ MacroAssembler &masm = m.masm();
+ masm.bind(&m.onDetachedLabel());
+ masm.assertStackAlignment(ABIStackAlignment);
+
+ // For now, OnDetached always throws (see OnDetached comment).
+ masm.call(AsmJSImmPtr(AsmJSImm_OnDetached));
+ masm.jump(throwLabel);
+
+ return m.finishGeneratingInlineStub(&m.onDetachedLabel()) && !masm.oom();
+}
+
static const RegisterSet AllRegsExceptSP =
RegisterSet(GeneralRegisterSet(Registers::AllMask &
~(uint32_t(1) << Registers::StackPointer)),
FloatRegisterSet(FloatRegisters::AllDoubleMask));
// The async interrupt-callback exit is called from arbitrarily-interrupted asm.js
// code. That means we must first save *all* registers and restore *all*
// registers (except the stack pointer) when we resume. The address to resume to
@@ -8147,17 +8190,16 @@ GenerateAsyncInterruptExit(ModuleCompile
masm.branchIfFalseBool(ReturnReg, throwLabel);
// Restore the StackPointer to it's position before the call.
masm.mov(ABIArgGenerator::NonVolatileReg, StackPointer);
// Restore the machine state to before the interrupt.
masm.PopRegsInMask(AllRegsExceptSP, AllRegsExceptSP.fpus()); // restore all GP/FP registers (except SP)
- masm.loadAsmJSHeapRegisterFromGlobalData(); // In case there was a changeHeap
masm.popFlags(); // after this, nothing that sets conditions
masm.ret(); // pop resumePC into PC
#elif defined(JS_CODEGEN_MIPS)
// Reserve space to store resumePC.
masm.subPtr(Imm32(sizeof(intptr_t)), StackPointer);
// set to zero so we can use masm.framePushed() below.
masm.setFramePushed(0);
// When this platform supports SIMD extensions, we'll need to push high lanes
@@ -8189,17 +8231,16 @@ GenerateAsyncInterruptExit(ModuleCompile
// This will restore stack to the address before the call.
masm.movePtr(s0, StackPointer);
masm.PopRegsInMask(AllRegsExceptSP);
// Pop resumePC into PC. Clobber HeapReg to make the jump and restore it
// during jump delay slot.
masm.pop(HeapReg);
masm.as_jr(HeapReg);
- masm.loadAsmJSHeapRegisterFromGlobalData(); // In case there was a changeHeap
#elif defined(JS_CODEGEN_ARM)
masm.setFramePushed(0); // set to zero so we can use masm.framePushed() below
masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(Registers::AllMask & ~(1<<Registers::sp)), FloatRegisterSet(uint32_t(0)))); // save all GP registers,excep sp
// Save both the APSR and FPSCR in non-volatile registers.
masm.as_mrs(r4);
masm.as_vmrs(r5);
// Save the stack pointer in a non-volatile register.
@@ -8239,17 +8280,16 @@ GenerateAsyncInterruptExit(ModuleCompile
masm.transferReg(r7);
masm.transferReg(r8);
masm.transferReg(r9);
masm.transferReg(r10);
masm.transferReg(r11);
masm.transferReg(r12);
masm.transferReg(lr);
masm.finishDataTransfer();
- masm.loadAsmJSHeapRegisterFromGlobalData(); // In case there was a changeHeap
masm.ret();
#elif defined (JS_CODEGEN_NONE)
MOZ_CRASH();
#else
# error "Unknown architecture!"
#endif
@@ -8265,19 +8305,16 @@ GenerateSyncInterruptExit(ModuleCompiler
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace);
GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::Interrupt, &m.syncInterruptLabel());
AssertStackAlignment(masm, ABIStackAlignment);
masm.call(AsmJSImmPtr(AsmJSImm_HandleExecutionInterrupt));
masm.branchIfFalseBool(ReturnReg, throwLabel);
- // Reload the heap register in case the callback changed heaps.
- masm.loadAsmJSHeapRegisterFromGlobalData();
-
Label profilingReturn;
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::Interrupt, &profilingReturn);
return m.finishGeneratingInterrupt(&m.syncInterruptLabel(), &profilingReturn) && !masm.oom();
}
// If an exception is thrown, simply pop all frames (since asm.js does not
// contain try/catch). To do this:
// 1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
@@ -8324,16 +8361,19 @@ GenerateStubs(ModuleCompiler &m)
for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) {
if (!GenerateFFIExits(m, r.front().key(), r.front().value(), &throwLabel))
return false;
}
if (m.stackOverflowLabel().used() && !GenerateStackOverflowExit(m, &throwLabel))
return false;
+ if (m.onDetachedLabel().used() && !GenerateOnDetachedLabelExit(m, &throwLabel))
+ return false;
+
if (!GenerateAsyncInterruptExit(m, &throwLabel))
return false;
if (m.syncInterruptLabel().used() && !GenerateSyncInterruptExit(m, &throwLabel))
return false;
if (!GenerateThrowStub(m, &throwLabel))
return false;
--- a/js/src/jit-test/tests/asm.js/testNeuter.js
+++ b/js/src/jit-test/tests/asm.js/testNeuter.js
@@ -1,81 +1,113 @@
load(libdir + "asm.js");
+load(libdir + "asserts.js");
+
+if (!isAsmJSCompilationAvailable())
+ quit();
-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 m = asmCompile('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}`);
-var i32 = new Int32Array(1024);
-var buffer = i32.buffer;
-var {get, set} = f(this, null, buffer);
-if (isAsmJSCompilationAvailable())
- assertEq(isAsmJSFunction(get) && isAsmJSFunction(set), true);
-
+var buffer = new ArrayBuffer(BUF_MIN);
+var {get, set} = asmLink(m, this, null, buffer);
set(4, 42);
assertEq(get(4), 42);
-
neuter(buffer, "change-data");
neuter(buffer, "same-data");
+assertThrowsInstanceOf(() => get(4), InternalError);
-// These operations may throw errors
-try {
- assertEq(get(4), 0);
- set(0, 42);
- assertEq(get(0), 0);
-} catch (e) {
- assertEq(String(e).indexOf("InternalError"), 0);
-}
+var buf1 = new ArrayBuffer(BUF_MIN);
+var buf2 = new ArrayBuffer(BUF_MIN);
+var {get:get1, set:set1} = asmLink(m, this, null, buf1);
+var {get:get2, set:set2} = asmLink(m, this, null, buf2);
+set1(0, 13);
+set2(0, 42);
+neuter(buf1, "change-data");
+assertThrowsInstanceOf(() => get1(0), InternalError);
+assertEq(get2(0), 42);
-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 m = asmCompile('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`);
+
+var buffer = new ArrayBuffer(BUF_MIN);
+function ffi1() { neuter(buffer, "change-data"); }
+var inner = asmLink(m, this, {ffi:ffi1}, buffer);
+assertThrowsInstanceOf(() => inner(8), InternalError);
-var i32 = new Int32Array(1024);
-var buffer = i32.buffer;
-var threw = false;
-function ffi() {
- try {
- neuter(buffer, "same-data");
- } catch (e) {
- assertEq(String(e).indexOf("InternalError"), 0);
- threw = true;
- }
- try {
- neuter(buffer, "change-data");
- } catch (e) {
- assertEq(String(e).indexOf("InternalError"), 0);
- threw = true;
- }
+var byteLength = Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
+var m = asmCompile('stdlib', 'foreign', 'buffer',
+ `"use asm";
+ var ffi = foreign.ffi;
+ var I32 = stdlib.Int32Array;
+ var i32 = new I32(buffer);
+ var len = stdlib.byteLength;
+ function changeHeap(newBuffer) {
+ if (len(newBuffer) & 0xffffff || len(newBuffer) <= 0xffffff || len(newBuffer) > 0x80000000)
+ return false;
+ i32 = new I32(newBuffer);
+ buffer = newBuffer;
+ return true;
+ }
+ function get(i) {
+ i=i|0;
+ return i32[i>>2]|0;
+ }
+ function inner(i) {
+ i=i|0;
+ ffi();
+ return get(i)|0;
+ }
+ return {changeHeap:changeHeap, get:get, inner:inner}`);
+
+var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
+var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
+var buf3 = new ArrayBuffer(BUF_CHANGE_MIN);
+var buf4 = new ArrayBuffer(BUF_CHANGE_MIN);
+new Int32Array(buf2)[13] = 42;
+new Int32Array(buf3)[13] = 1024;
+new Int32Array(buf4)[13] = 1337;
+
+function ffi2() { neuter(buf1, "change-data"); assertEq(changeHeap(buf2), true); }
+var {changeHeap, get:get2, inner} = asmLink(m, this, {ffi:ffi2}, buf1);
+assertEq(inner(13*4), 42);
+
+function ffi3() {
+ assertEq(get2(13*4), 42);
+ assertEq(get2(BUF_CHANGE_MIN), 0)
+ assertEq(get3(13*4), 42);
+ assertEq(get3(BUF_CHANGE_MIN), 0)
+ neuter(buf2, "change-data");
+ assertThrowsInstanceOf(()=>get2(13*4), InternalError);
+ assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
+ assertThrowsInstanceOf(()=>get3(13*4), InternalError);
+ assertThrowsInstanceOf(()=>get3(BUF_CHANGE_MIN), InternalError);
+ assertEq(changeHeap(buf3), true);
+ assertThrowsInstanceOf(()=>get2(13*4), InternalError);
+ assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
+ assertEq(get3(13*4), 1024);
+ assertEq(get3(BUF_CHANGE_MIN), 0);
+ assertEq(changeHeap(buf4), 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);
-
+var {changeHeap, get:get3, inner} = asmLink(m, this, {ffi:ffi3}, buf2);
+assertEq(inner(13*4), 1337);
+assertThrowsInstanceOf(()=>get2(0), InternalError);
+assertEq(get3(BUF_CHANGE_MIN), 0);
+assertEq(get3(13*4), 1337);
--- a/js/src/jit-test/tests/asm.js/testProfiling.js
+++ b/js/src/jit-test/tests/asm.js/testProfiling.js
@@ -1,9 +1,10 @@
load(libdir + "asm.js");
+load(libdir + "asserts.js");
// Single-step profiling currently only works in the ARM simulator
if (!getBuildConfiguration()["arm-simulator"])
quit();
function assertEqualStacks(got, expect)
{
// Strip off the " (script/library info)"
@@ -119,16 +120,25 @@ assertEqualStacks(stacks, ",>,f1>,<f1>,>
// Ion FFI exit
for (var i = 0; i < 20; i++)
assertEq(f1(), 32);
enableSingleStepProfiling();
assertEq(f1(), 32);
var stacks = disableSingleStepProfiling();
assertEqualStacks(stacks, ",>,f1>,<f1>,><f1>,f2><f1>,<f2><f1>,f2><f1>,><f1>,<f1>,f1>,>,");
+// Detachment exit
+var buf = new ArrayBuffer(BUF_CHANGE_MIN);
+var ffi = function() { neuter(buf, 'change-data') }
+var f = asmLink(asmCompile('g','ffis','buf', USE_ASM + 'var ffi = ffis.ffi; var i32 = new g.Int32Array(buf); function f() { ffi() } return f'), this, {ffi:ffi}, buf);
+enableSingleStepProfiling();
+assertThrowsInstanceOf(f, InternalError);
+var stacks = disableSingleStepProfiling();
+assertEqualStacks(stacks, ",>,f>,<f>,inline stubf>,<f>,inline stubf>,");
+
// This takes forever to run.
// Stack-overflow exit test
//var limit = -1;
//var maxct = 0;
//function ffi(ct) { if (ct == limit) { enableSingleStepProfiling(); print("enabled"); } maxct = ct; }
//var f = asmLink(asmCompile('g', 'ffis',USE_ASM + "var ffi=ffis.ffi; var ct=0; function rec(){ ct=(ct+1)|0; ffi(ct|0); rec() } function f() { ct=0; rec() } return f"), null, {ffi});
//// First find the stack limit:
//var caught = false;
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -762,16 +762,17 @@ enum AsmJSImmKind
AsmJSImm_ExpD = AsmJSExit::Builtin_ExpD,
AsmJSImm_LogD = AsmJSExit::Builtin_LogD,
AsmJSImm_PowD = AsmJSExit::Builtin_PowD,
AsmJSImm_ATan2D = AsmJSExit::Builtin_ATan2D,
AsmJSImm_Runtime,
AsmJSImm_RuntimeInterrupt,
AsmJSImm_StackLimit,
AsmJSImm_ReportOverRecursed,
+ AsmJSImm_OnDetached,
AsmJSImm_HandleExecutionInterrupt,
AsmJSImm_InvokeFromAsmJS_Ignore,
AsmJSImm_InvokeFromAsmJS_ToInt32,
AsmJSImm_InvokeFromAsmJS_ToNumber,
AsmJSImm_CoerceInPlace_ToInt32,
AsmJSImm_CoerceInPlace_ToNumber,
AsmJSImm_Limit
};