mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset 10616214c160 (bug 1091916) for Android x86 S4 permafail.
This commit is contained in:
parent
418030317f
commit
e9bc83a22c
@ -500,7 +500,7 @@ AsmJSHandleExecutionInterrupt()
|
|||||||
{
|
{
|
||||||
AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
|
AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
|
||||||
act->module().setInterrupted(true);
|
act->module().setInterrupted(true);
|
||||||
bool ret = CheckForInterrupt(act->cx());
|
bool ret = HandleExecutionInterrupt(act->cx());
|
||||||
act->module().setInterrupted(false);
|
act->module().setInterrupted(false);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -672,8 +672,8 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
|
|||||||
switch (kind) {
|
switch (kind) {
|
||||||
case AsmJSImm_Runtime:
|
case AsmJSImm_Runtime:
|
||||||
return cx->runtimeAddressForJit();
|
return cx->runtimeAddressForJit();
|
||||||
case AsmJSImm_RuntimeInterruptUint32:
|
case AsmJSImm_RuntimeInterrupt:
|
||||||
return cx->runtimeAddressOfInterruptUint32();
|
return cx->runtimeAddressOfInterrupt();
|
||||||
case AsmJSImm_StackLimit:
|
case AsmJSImm_StackLimit:
|
||||||
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
|
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
|
||||||
case AsmJSImm_ReportOverRecursed:
|
case AsmJSImm_ReportOverRecursed:
|
||||||
|
@ -152,7 +152,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx, bool match_only)
|
|||||||
|
|
||||||
// Check if we have space on the stack.
|
// Check if we have space on the stack.
|
||||||
Label stack_ok;
|
Label stack_ok;
|
||||||
void *stack_limit = runtime->mainThread.addressofJitStackLimit();
|
void *stack_limit = &runtime->mainThread.jitStackLimit;
|
||||||
masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok);
|
masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok);
|
||||||
|
|
||||||
// Exit with an exception. There is not enough space on the stack
|
// Exit with an exception. There is not enough space on the stack
|
||||||
@ -502,7 +502,7 @@ NativeRegExpMacroAssembler::Backtrack()
|
|||||||
// Check for an interrupt.
|
// Check for an interrupt.
|
||||||
Label noInterrupt;
|
Label noInterrupt;
|
||||||
masm.branch32(Assembler::Equal,
|
masm.branch32(Assembler::Equal,
|
||||||
AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0),
|
AbsoluteAddress(&runtime->interrupt), Imm32(0),
|
||||||
&noInterrupt);
|
&noInterrupt);
|
||||||
masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
|
masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
|
||||||
masm.jump(&exit_label_);
|
masm.jump(&exit_label_);
|
||||||
|
@ -496,7 +496,7 @@ bool
|
|||||||
BaselineCompiler::emitStackCheck(bool earlyCheck)
|
BaselineCompiler::emitStackCheck(bool earlyCheck)
|
||||||
{
|
{
|
||||||
Label skipCall;
|
Label skipCall;
|
||||||
void *limitAddr = cx->runtime()->mainThread.addressofJitStackLimit();
|
uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit;
|
||||||
uint32_t slotsSize = script->nslots() * sizeof(Value);
|
uint32_t slotsSize = script->nslots() * sizeof(Value);
|
||||||
uint32_t tolerance = earlyCheck ? slotsSize : 0;
|
uint32_t tolerance = earlyCheck ? slotsSize : 0;
|
||||||
|
|
||||||
@ -646,7 +646,7 @@ BaselineCompiler::emitInterruptCheck()
|
|||||||
frame.syncStack(0);
|
frame.syncStack(0);
|
||||||
|
|
||||||
Label done;
|
Label done;
|
||||||
void *interrupt = cx->runtimeAddressOfInterruptUint32();
|
void *interrupt = (void *)&cx->runtime()->interrupt;
|
||||||
masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done);
|
masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done);
|
||||||
|
|
||||||
prepareVMCall();
|
prepareVMCall();
|
||||||
|
@ -3747,7 +3747,7 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
|
|||||||
Register tempReg = ToRegister(lir->getTempReg());
|
Register tempReg = ToRegister(lir->getTempReg());
|
||||||
|
|
||||||
masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
|
masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
|
||||||
masm.loadPtr(Address(tempReg, PerThreadData::offsetOfJitStackLimit()), tempReg);
|
masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
|
||||||
|
|
||||||
// Conditional forward (unlikely) branch to failure.
|
// Conditional forward (unlikely) branch to failure.
|
||||||
CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
|
CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
|
||||||
@ -9789,7 +9789,7 @@ CodeGenerator::visitInterruptCheck(LInterruptCheck *lir)
|
|||||||
if (!ool)
|
if (!ool)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterruptUint32());
|
AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterrupt());
|
||||||
masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry());
|
masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry());
|
||||||
masm.bind(ool->rejoin());
|
masm.bind(ool->rejoin());
|
||||||
return true;
|
return true;
|
||||||
@ -9799,8 +9799,8 @@ bool
|
|||||||
CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir)
|
CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir)
|
||||||
{
|
{
|
||||||
Register scratch = ToRegister(lir->scratch());
|
Register scratch = ToRegister(lir->scratch());
|
||||||
masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterruptUint32), scratch);
|
masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch);
|
||||||
masm.load32(Address(scratch, 0), scratch);
|
masm.load8ZeroExtend(Address(scratch, 0), scratch);
|
||||||
Label rejoin;
|
Label rejoin;
|
||||||
masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin);
|
masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin);
|
||||||
{
|
{
|
||||||
|
@ -43,7 +43,7 @@ CompileRuntime::addressOfJitTop()
|
|||||||
const void *
|
const void *
|
||||||
CompileRuntime::addressOfJitStackLimit()
|
CompileRuntime::addressOfJitStackLimit()
|
||||||
{
|
{
|
||||||
return runtime()->mainThread.addressofJitStackLimit();
|
return &runtime()->mainThread.jitStackLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *
|
const void *
|
||||||
@ -73,15 +73,15 @@ CompileRuntime::addressOfGCZeal()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
const void *
|
const void *
|
||||||
CompileRuntime::addressOfInterruptUint32()
|
CompileRuntime::addressOfInterrupt()
|
||||||
{
|
{
|
||||||
return runtime()->addressOfInterruptUint32();
|
return &runtime()->interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *
|
const void *
|
||||||
CompileRuntime::addressOfInterruptParUint32()
|
CompileRuntime::addressOfInterruptPar()
|
||||||
{
|
{
|
||||||
return runtime()->addressOfInterruptParUint32();
|
return &runtime()->interruptPar;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *
|
const void *
|
||||||
|
@ -50,8 +50,8 @@ class CompileRuntime
|
|||||||
const void *addressOfGCZeal();
|
const void *addressOfGCZeal();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const void *addressOfInterruptUint32();
|
const void *addressOfInterrupt();
|
||||||
const void *addressOfInterruptParUint32();
|
const void *addressOfInterruptPar();
|
||||||
|
|
||||||
const void *addressOfThreadPool();
|
const void *addressOfThreadPool();
|
||||||
|
|
||||||
|
@ -423,7 +423,7 @@ JitRuntime::ensureIonCodeAccessible(JSRuntime *rt)
|
|||||||
ionCodeProtected_ = false;
|
ionCodeProtected_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rt->hasPendingInterrupt()) {
|
if (rt->interrupt) {
|
||||||
// The interrupt handler needs to be invoked by this thread, but we may
|
// The interrupt handler needs to be invoked by this thread, but we may
|
||||||
// be inside a signal handler and have no idea what is above us on the
|
// be inside a signal handler and have no idea what is above us on the
|
||||||
// stack (probably we are executing Ion code at an arbitrary point, but
|
// stack (probably we are executing Ion code at an arbitrary point, but
|
||||||
@ -1157,7 +1157,7 @@ IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code,
|
|||||||
// whether an interrupt is currently desired, matching the targets
|
// whether an interrupt is currently desired, matching the targets
|
||||||
// established by ensureIonCodeAccessible() above. We don't handle the
|
// established by ensureIonCodeAccessible() above. We don't handle the
|
||||||
// interrupt immediately as the interrupt lock is held here.
|
// interrupt immediately as the interrupt lock is held here.
|
||||||
if (cx->runtime()->hasPendingInterrupt())
|
if (cx->runtime()->interrupt)
|
||||||
PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck);
|
PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck);
|
||||||
else
|
else
|
||||||
PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader);
|
PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader);
|
||||||
|
@ -1168,7 +1168,7 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output)
|
|||||||
void
|
void
|
||||||
MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail)
|
MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail)
|
||||||
{
|
{
|
||||||
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptParUint32()), tempReg);
|
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg);
|
||||||
branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail);
|
branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit(), &stackDummy_)) {
|
if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit, &stackDummy_)) {
|
||||||
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
|
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -113,17 +113,28 @@ NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap)
|
|||||||
bool
|
bool
|
||||||
CheckOverRecursed(JSContext *cx)
|
CheckOverRecursed(JSContext *cx)
|
||||||
{
|
{
|
||||||
// We just failed the jitStackLimit check. There are two possible reasons:
|
// IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
|
||||||
// - jitStackLimit was the real stack limit and we're over-recursed
|
// request an interrupt, we set the jitStackLimit to nullptr, which causes
|
||||||
// - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
|
// the stack limit check to fail.
|
||||||
// and we need to call JSRuntime::handleInterrupt.
|
//
|
||||||
|
// There are two states we're concerned about here:
|
||||||
|
// (1) The interrupt bit is set, and we need to fire the interrupt callback.
|
||||||
|
// (2) The stack limit has been exceeded, and we need to throw an error.
|
||||||
|
//
|
||||||
|
// Note that we can reach here if jitStackLimit is MAXADDR, but interrupt
|
||||||
|
// has not yet been set to 1. That's okay; it will be set to 1 very shortly,
|
||||||
|
// and in the interim we might just fire a few useless calls to
|
||||||
|
// CheckOverRecursed.
|
||||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||||
JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false);
|
JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false);
|
||||||
#else
|
#else
|
||||||
JS_CHECK_RECURSION(cx, return false);
|
JS_CHECK_RECURSION(cx, return false);
|
||||||
#endif
|
#endif
|
||||||
gc::MaybeVerifyBarriers(cx);
|
|
||||||
return cx->runtime()->handleInterrupt(cx);
|
if (cx->runtime()->interrupt)
|
||||||
|
return InterruptCheck(cx);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function can get called in two contexts. In the usual context, it's
|
// This function can get called in two contexts. In the usual context, it's
|
||||||
@ -167,8 +178,10 @@ CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame,
|
|||||||
JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false);
|
JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gc::MaybeVerifyBarriers(cx);
|
if (cx->runtime()->interrupt)
|
||||||
return cx->runtime()->handleInterrupt(cx);
|
return InterruptCheck(cx);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -783,7 +783,7 @@ enum AsmJSImmKind
|
|||||||
AsmJSImm_PowD = AsmJSExit::Builtin_PowD,
|
AsmJSImm_PowD = AsmJSExit::Builtin_PowD,
|
||||||
AsmJSImm_ATan2D = AsmJSExit::Builtin_ATan2D,
|
AsmJSImm_ATan2D = AsmJSExit::Builtin_ATan2D,
|
||||||
AsmJSImm_Runtime,
|
AsmJSImm_Runtime,
|
||||||
AsmJSImm_RuntimeInterruptUint32,
|
AsmJSImm_RuntimeInterrupt,
|
||||||
AsmJSImm_StackLimit,
|
AsmJSImm_StackLimit,
|
||||||
AsmJSImm_ReportOverRecursed,
|
AsmJSImm_ReportOverRecursed,
|
||||||
AsmJSImm_OnDetached,
|
AsmJSImm_OnDetached,
|
||||||
|
@ -2029,48 +2029,68 @@ JS_GetExternalStringFinalizer(JSString *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
SetNativeStackQuotaAndLimit(JSRuntime *rt, StackKind kind, size_t stackSize)
|
SetNativeStackQuota(JSRuntime *rt, StackKind kind, size_t stackSize)
|
||||||
{
|
{
|
||||||
rt->nativeStackQuota[kind] = stackSize;
|
rt->nativeStackQuota[kind] = stackSize;
|
||||||
|
if (rt->nativeStackBase)
|
||||||
|
RecomputeStackLimit(rt, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js::RecomputeStackLimit(JSRuntime *rt, StackKind kind)
|
||||||
|
{
|
||||||
|
size_t stackSize = rt->nativeStackQuota[kind];
|
||||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||||
if (stackSize == 0) {
|
if (stackSize == 0) {
|
||||||
rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX;
|
rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX;
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
|
MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
|
||||||
rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase + stackSize - 1;
|
rt->mainThread.nativeStackLimit[kind] =
|
||||||
|
rt->nativeStackBase + stackSize - 1;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (stackSize == 0) {
|
if (stackSize == 0) {
|
||||||
rt->mainThread.nativeStackLimit[kind] = 0;
|
rt->mainThread.nativeStackLimit[kind] = 0;
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(rt->nativeStackBase >= stackSize);
|
MOZ_ASSERT(rt->nativeStackBase >= stackSize);
|
||||||
rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase - (stackSize - 1);
|
rt->mainThread.nativeStackLimit[kind] =
|
||||||
|
rt->nativeStackBase - (stackSize - 1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// If there's no pending interrupt request set on the runtime's main thread's
|
||||||
|
// jitStackLimit, then update it so that it reflects the new nativeStacklimit.
|
||||||
|
//
|
||||||
|
// Note that, for now, we use the untrusted limit for ion. This is fine,
|
||||||
|
// because it's the most conservative limit, and if we hit it, we'll bail
|
||||||
|
// out of ion into the interpeter, which will do a proper recursion check.
|
||||||
|
if (kind == StackForUntrustedScript) {
|
||||||
|
JSRuntime::AutoLockForInterrupt lock(rt);
|
||||||
|
if (rt->mainThread.jitStackLimit != uintptr_t(-1)) {
|
||||||
|
rt->mainThread.jitStackLimit = rt->mainThread.nativeStackLimit[kind];
|
||||||
|
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||||
|
rt->mainThread.jitStackLimit = jit::Simulator::StackLimit();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, size_t trustedScriptStackSize,
|
JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize,
|
||||||
|
size_t trustedScriptStackSize,
|
||||||
size_t untrustedScriptStackSize)
|
size_t untrustedScriptStackSize)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(rt->requestDepth == 0);
|
MOZ_ASSERT_IF(trustedScriptStackSize,
|
||||||
|
trustedScriptStackSize < systemCodeStackSize);
|
||||||
if (!trustedScriptStackSize)
|
if (!trustedScriptStackSize)
|
||||||
trustedScriptStackSize = systemCodeStackSize;
|
trustedScriptStackSize = systemCodeStackSize;
|
||||||
else
|
MOZ_ASSERT_IF(untrustedScriptStackSize,
|
||||||
MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize);
|
untrustedScriptStackSize < trustedScriptStackSize);
|
||||||
|
|
||||||
if (!untrustedScriptStackSize)
|
if (!untrustedScriptStackSize)
|
||||||
untrustedScriptStackSize = trustedScriptStackSize;
|
untrustedScriptStackSize = trustedScriptStackSize;
|
||||||
else
|
SetNativeStackQuota(rt, StackForSystemCode, systemCodeStackSize);
|
||||||
MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize);
|
SetNativeStackQuota(rt, StackForTrustedScript, trustedScriptStackSize);
|
||||||
|
SetNativeStackQuota(rt, StackForUntrustedScript, untrustedScriptStackSize);
|
||||||
SetNativeStackQuotaAndLimit(rt, StackForSystemCode, systemCodeStackSize);
|
|
||||||
SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize);
|
|
||||||
SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize);
|
|
||||||
|
|
||||||
rt->mainThread.initJitStackLimit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
@ -2272,9 +2272,6 @@ JS_GetExternalStringFinalizer(JSString *str);
|
|||||||
* The stack quotas for each kind of code should be monotonically descending,
|
* The stack quotas for each kind of code should be monotonically descending,
|
||||||
* and may be specified with this function. If 0 is passed for a given kind
|
* and may be specified with this function. If 0 is passed for a given kind
|
||||||
* of code, it defaults to the value of the next-highest-priority kind.
|
* of code, it defaults to the value of the next-highest-priority kind.
|
||||||
*
|
|
||||||
* This function may only be called immediately after the runtime is initialized
|
|
||||||
* and before any code is executed and/or interrupts requested.
|
|
||||||
*/
|
*/
|
||||||
extern JS_PUBLIC_API(void)
|
extern JS_PUBLIC_API(void)
|
||||||
JS_SetNativeStackQuota(JSRuntime *cx, size_t systemCodeStackSize,
|
JS_SetNativeStackQuota(JSRuntime *cx, size_t systemCodeStackSize,
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "jit/Ion.h"
|
#include "jit/Ion.h"
|
||||||
#include "js/CharacterEncoding.h"
|
#include "js/CharacterEncoding.h"
|
||||||
|
#include "vm/Debugger.h"
|
||||||
#include "vm/HelperThreads.h"
|
#include "vm/HelperThreads.h"
|
||||||
#include "vm/Shape.h"
|
#include "vm/Shape.h"
|
||||||
|
|
||||||
@ -970,6 +971,90 @@ js_GetErrorMessage(void *userRef, const unsigned errorNumber)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::InvokeInterruptCallback(JSContext *cx)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
|
||||||
|
|
||||||
|
JSRuntime *rt = cx->runtime();
|
||||||
|
MOZ_ASSERT(rt->interrupt);
|
||||||
|
|
||||||
|
// Reset the callback counter first, then run GC and yield. If another
|
||||||
|
// thread is racing us here we will accumulate another callback request
|
||||||
|
// which will be serviced at the next opportunity.
|
||||||
|
rt->interrupt = false;
|
||||||
|
|
||||||
|
// IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt
|
||||||
|
// callbacks.
|
||||||
|
rt->resetJitStackLimit();
|
||||||
|
|
||||||
|
cx->gcIfNeeded();
|
||||||
|
|
||||||
|
rt->interruptPar = false;
|
||||||
|
|
||||||
|
// A worker thread may have requested an interrupt after finishing an Ion
|
||||||
|
// compilation.
|
||||||
|
jit::AttachFinishedCompilations(cx);
|
||||||
|
|
||||||
|
// Important: Additional callbacks can occur inside the callback handler
|
||||||
|
// if it re-enters the JS engine. The embedding must ensure that the
|
||||||
|
// callback is disconnected before attempting such re-entry.
|
||||||
|
JSInterruptCallback cb = cx->runtime()->interruptCallback;
|
||||||
|
if (!cb)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (cb(cx)) {
|
||||||
|
// Debugger treats invoking the interrupt callback as a "step", so
|
||||||
|
// invoke the onStep handler.
|
||||||
|
if (cx->compartment()->debugMode()) {
|
||||||
|
ScriptFrameIter iter(cx);
|
||||||
|
if (iter.script()->stepModeEnabled()) {
|
||||||
|
RootedValue rval(cx);
|
||||||
|
switch (Debugger::onSingleStep(cx, &rval)) {
|
||||||
|
case JSTRAP_ERROR:
|
||||||
|
return false;
|
||||||
|
case JSTRAP_CONTINUE:
|
||||||
|
return true;
|
||||||
|
case JSTRAP_RETURN:
|
||||||
|
// See note in Debugger::propagateForcedReturn.
|
||||||
|
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
|
||||||
|
return false;
|
||||||
|
case JSTRAP_THROW:
|
||||||
|
cx->setPendingException(rval);
|
||||||
|
return false;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to set aside any pending exception here: ComputeStackString
|
||||||
|
// already does that.
|
||||||
|
JSString *stack = ComputeStackString(cx);
|
||||||
|
JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
|
||||||
|
|
||||||
|
const char16_t *chars;
|
||||||
|
AutoStableStringChars stableChars(cx);
|
||||||
|
if (flat && stableChars.initTwoByte(cx, flat))
|
||||||
|
chars = stableChars.twoByteRange().start().get();
|
||||||
|
else
|
||||||
|
chars = MOZ_UTF16("(stack not available)");
|
||||||
|
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
|
||||||
|
JSMSG_TERMINATED, chars);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::HandleExecutionInterrupt(JSContext *cx)
|
||||||
|
{
|
||||||
|
if (cx->runtime()->interrupt)
|
||||||
|
return InvokeInterruptCallback(cx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
|
ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
|
||||||
: ContextFriendFields(rt),
|
: ContextFriendFields(rt),
|
||||||
contextKind_(kind),
|
contextKind_(kind),
|
||||||
|
@ -289,7 +289,7 @@ struct ThreadSafeContext : ContextFriendFields,
|
|||||||
PropertyName *emptyString() { return runtime_->emptyString; }
|
PropertyName *emptyString() { return runtime_->emptyString; }
|
||||||
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
|
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
|
||||||
void *runtimeAddressForJit() { return runtime_; }
|
void *runtimeAddressForJit() { return runtime_; }
|
||||||
void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
|
void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; }
|
||||||
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
||||||
void *stackLimitAddressForJitCode(StackKind kind);
|
void *stackLimitAddressForJitCode(StackKind kind);
|
||||||
size_t gcSystemPageSize() { return gc::SystemPageSize(); }
|
size_t gcSystemPageSize() { return gc::SystemPageSize(); }
|
||||||
@ -782,15 +782,33 @@ extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
|||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invoke the interrupt callback and return false if the current execution
|
||||||
|
* is to be terminated.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
InvokeInterruptCallback(JSContext *cx);
|
||||||
|
|
||||||
|
bool
|
||||||
|
HandleExecutionInterrupt(JSContext *cx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process any pending interrupt requests. Long-running inner loops in C++ must
|
||||||
|
* call this periodically to make sure they are interruptible --- that is, to
|
||||||
|
* make sure they do not prevent the slow script dialog from appearing.
|
||||||
|
*
|
||||||
|
* This can run a full GC or call the interrupt callback, which could do
|
||||||
|
* anything. In the browser, it displays the slow script dialog.
|
||||||
|
*
|
||||||
|
* If this returns true, the caller can continue; if false, the caller must
|
||||||
|
* break out of its loop. This happens if, for example, the user clicks "Stop
|
||||||
|
* script" on the slow script dialog; treat it as an uncatchable error.
|
||||||
|
*/
|
||||||
MOZ_ALWAYS_INLINE bool
|
MOZ_ALWAYS_INLINE bool
|
||||||
CheckForInterrupt(JSContext *cx)
|
CheckForInterrupt(JSContext *cx)
|
||||||
{
|
{
|
||||||
// Add an inline fast-path since we have to check for interrupts in some hot
|
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
|
||||||
// C++ loops of library builtins.
|
return !cx->runtime()->interrupt || InvokeInterruptCallback(cx);
|
||||||
JSRuntime *rt = cx->runtime();
|
|
||||||
if (rt->hasPendingInterrupt())
|
|
||||||
return rt->handleInterrupt(cx);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
@ -526,7 +526,7 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
|
|||||||
rt->gc.runDebugGC();
|
rt->gc.runDebugGC();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (rt->hasPendingInterrupt()) {
|
if (rt->interrupt) {
|
||||||
// Invoking the interrupt callback can fail and we can't usefully
|
// Invoking the interrupt callback can fail and we can't usefully
|
||||||
// handle that here. Just check in case we need to collect instead.
|
// handle that here. Just check in case we need to collect instead.
|
||||||
ncx->gcIfNeeded();
|
ncx->gcIfNeeded();
|
||||||
|
@ -18,7 +18,6 @@ inline uintptr_t
|
|||||||
GetNativeStackBase()
|
GetNativeStackBase()
|
||||||
{
|
{
|
||||||
uintptr_t stackBase = reinterpret_cast<uintptr_t>(GetNativeStackBaseImpl());
|
uintptr_t stackBase = reinterpret_cast<uintptr_t>(GetNativeStackBaseImpl());
|
||||||
MOZ_ASSERT(stackBase != 0);
|
|
||||||
MOZ_ASSERT(stackBase % sizeof(void *) == 0);
|
MOZ_ASSERT(stackBase % sizeof(void *) == 0);
|
||||||
return stackBase;
|
return stackBase;
|
||||||
}
|
}
|
||||||
|
@ -1440,7 +1440,7 @@ ForkJoinShared::execute()
|
|||||||
// Sometimes a GC request occurs *just before* we enter into the
|
// Sometimes a GC request occurs *just before* we enter into the
|
||||||
// parallel section. Rather than enter into the parallel section
|
// parallel section. Rather than enter into the parallel section
|
||||||
// and then abort, we just check here and abort early.
|
// and then abort, we just check here and abort early.
|
||||||
if (cx_->runtime()->hasPendingInterruptPar())
|
if (cx_->runtime()->interruptPar)
|
||||||
return TP_RETRY_SEQUENTIALLY;
|
return TP_RETRY_SEQUENTIALLY;
|
||||||
|
|
||||||
AutoLockMonitor lock(*this);
|
AutoLockMonitor lock(*this);
|
||||||
@ -1518,7 +1518,7 @@ ForkJoinShared::executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit
|
|||||||
|
|
||||||
// Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
|
// Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
|
||||||
// lock has not been initialized in these cases.
|
// lock has not been initialized in these cases.
|
||||||
thisThread.initJitStackLimitPar(stackLimit);
|
thisThread.jitStackLimit = stackLimit;
|
||||||
executePortion(&thisThread, worker);
|
executePortion(&thisThread, worker);
|
||||||
TlsPerThreadData.set(nullptr);
|
TlsPerThreadData.set(nullptr);
|
||||||
|
|
||||||
@ -1551,7 +1551,7 @@ ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker)
|
|||||||
//
|
//
|
||||||
// Thus, use GetNativeStackLimit instead of just propagating the
|
// Thus, use GetNativeStackLimit instead of just propagating the
|
||||||
// main thread's.
|
// main thread's.
|
||||||
thisThread.initJitStackLimitPar(GetNativeStackLimit(cx_));
|
thisThread.jitStackLimit = GetNativeStackLimit(cx_);
|
||||||
executePortion(&thisThread, worker);
|
executePortion(&thisThread, worker);
|
||||||
TlsPerThreadData.set(oldData);
|
TlsPerThreadData.set(oldData);
|
||||||
|
|
||||||
@ -1647,7 +1647,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
|
|||||||
void
|
void
|
||||||
ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
|
ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(cx_->runtime()->hasPendingInterruptPar());
|
MOZ_ASSERT(cx_->runtime()->interruptPar);
|
||||||
// The GC Needed flag should not be set during parallel
|
// The GC Needed flag should not be set during parallel
|
||||||
// execution. Instead, one of the requestGC() or
|
// execution. Instead, one of the requestGC() or
|
||||||
// requestZoneGC() methods should be invoked.
|
// requestZoneGC() methods should be invoked.
|
||||||
@ -1826,7 +1826,7 @@ ForkJoinContext::hasAcquiredJSContext() const
|
|||||||
bool
|
bool
|
||||||
ForkJoinContext::check()
|
ForkJoinContext::check()
|
||||||
{
|
{
|
||||||
if (runtime()->hasPendingInterruptPar()) {
|
if (runtime()->interruptPar) {
|
||||||
shared_->setAbortFlagDueToInterrupt(*this);
|
shared_->setAbortFlagDueToInterrupt(*this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2273,6 +2273,13 @@ js::ParallelTestsShouldPass(JSContext *cx)
|
|||||||
cx->runtime()->gcZeal() == 0;
|
cx->runtime()->gcZeal() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode)
|
||||||
|
{
|
||||||
|
if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon)
|
||||||
|
rt->interruptPar = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
|
js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
|
||||||
{
|
{
|
||||||
|
@ -546,6 +546,8 @@ bool InExclusiveParallelSection();
|
|||||||
|
|
||||||
bool ParallelTestsShouldPass(JSContext *cx);
|
bool ParallelTestsShouldPass(JSContext *cx);
|
||||||
|
|
||||||
|
void RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode);
|
||||||
|
|
||||||
bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp);
|
bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp);
|
||||||
extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo;
|
extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo;
|
||||||
|
|
||||||
|
@ -621,8 +621,14 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start,
|
|||||||
// in the bytecode interpreter, which can execute while tolerating
|
// in the bytecode interpreter, which can execute while tolerating
|
||||||
// future interrupts. Otherwise, if we keep getting interrupted we
|
// future interrupts. Otherwise, if we keep getting interrupted we
|
||||||
// will never finish executing the regexp.
|
// will never finish executing the regexp.
|
||||||
if (cx->runtime()->hasPendingInterrupt()) {
|
bool interrupted;
|
||||||
if (!cx->runtime()->handleInterrupt(cx))
|
{
|
||||||
|
JSRuntime::AutoLockForInterrupt lock(cx->runtime());
|
||||||
|
interrupted = cx->runtime()->interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupted) {
|
||||||
|
if (!InvokeInterruptCallback(cx))
|
||||||
return RegExpRunStatus_Error;
|
return RegExpRunStatus_Error;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
#include "jit/PcScriptCache.h"
|
#include "jit/PcScriptCache.h"
|
||||||
#include "js/MemoryMetrics.h"
|
#include "js/MemoryMetrics.h"
|
||||||
#include "js/SliceBudget.h"
|
#include "js/SliceBudget.h"
|
||||||
#include "vm/Debugger.h"
|
|
||||||
|
|
||||||
#include "jscntxtinlines.h"
|
#include "jscntxtinlines.h"
|
||||||
#include "jsgcinlines.h"
|
#include "jsgcinlines.h"
|
||||||
@ -74,7 +73,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime)
|
|||||||
runtime_(runtime),
|
runtime_(runtime),
|
||||||
jitTop(nullptr),
|
jitTop(nullptr),
|
||||||
jitJSContext(nullptr),
|
jitJSContext(nullptr),
|
||||||
jitStackLimit_(0xbad),
|
jitStackLimit(0),
|
||||||
#ifdef JS_TRACE_LOGGING
|
#ifdef JS_TRACE_LOGGING
|
||||||
traceLogger(nullptr),
|
traceLogger(nullptr),
|
||||||
#endif
|
#endif
|
||||||
@ -136,8 +135,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
|
|||||||
),
|
),
|
||||||
mainThread(this),
|
mainThread(this),
|
||||||
parentRuntime(parentRuntime),
|
parentRuntime(parentRuntime),
|
||||||
interrupt_(false),
|
interrupt(false),
|
||||||
interruptPar_(false),
|
interruptPar(false),
|
||||||
handlingSignal(false),
|
handlingSignal(false),
|
||||||
interruptCallback(nullptr),
|
interruptCallback(nullptr),
|
||||||
interruptLock(nullptr),
|
interruptLock(nullptr),
|
||||||
@ -157,7 +156,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
|
|||||||
execAlloc_(nullptr),
|
execAlloc_(nullptr),
|
||||||
jitRuntime_(nullptr),
|
jitRuntime_(nullptr),
|
||||||
selfHostingGlobal_(nullptr),
|
selfHostingGlobal_(nullptr),
|
||||||
nativeStackBase(GetNativeStackBase()),
|
nativeStackBase(0),
|
||||||
cxCallback(nullptr),
|
cxCallback(nullptr),
|
||||||
destroyCompartmentCallback(nullptr),
|
destroyCompartmentCallback(nullptr),
|
||||||
destroyZoneCallback(nullptr),
|
destroyZoneCallback(nullptr),
|
||||||
@ -323,6 +322,8 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
nativeStackBase = GetNativeStackBase();
|
||||||
|
|
||||||
jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
|
jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
|
||||||
jitSupportsSimd = js::jit::JitSupportsSimd();
|
jitSupportsSimd = js::jit::JitSupportsSimd();
|
||||||
|
|
||||||
@ -464,6 +465,17 @@ NewObjectCache::clearNurseryObjects(JSRuntime *rt)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSRuntime::resetJitStackLimit()
|
||||||
|
{
|
||||||
|
AutoLockForInterrupt lock(this);
|
||||||
|
mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]);
|
||||||
|
|
||||||
|
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||||
|
mainThread.setJitStackLimit(js::jit::Simulator::StackLimit());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes)
|
JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes)
|
||||||
{
|
{
|
||||||
@ -518,120 +530,33 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
InvokeInterruptCallback(JSContext *cx)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
|
|
||||||
|
|
||||||
cx->gcIfNeeded();
|
|
||||||
|
|
||||||
// A worker thread may have requested an interrupt after finishing an Ion
|
|
||||||
// compilation.
|
|
||||||
jit::AttachFinishedCompilations(cx);
|
|
||||||
|
|
||||||
// Important: Additional callbacks can occur inside the callback handler
|
|
||||||
// if it re-enters the JS engine. The embedding must ensure that the
|
|
||||||
// callback is disconnected before attempting such re-entry.
|
|
||||||
JSInterruptCallback cb = cx->runtime()->interruptCallback;
|
|
||||||
if (!cb)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (cb(cx)) {
|
|
||||||
// Debugger treats invoking the interrupt callback as a "step", so
|
|
||||||
// invoke the onStep handler.
|
|
||||||
if (cx->compartment()->debugMode()) {
|
|
||||||
ScriptFrameIter iter(cx);
|
|
||||||
if (iter.script()->stepModeEnabled()) {
|
|
||||||
RootedValue rval(cx);
|
|
||||||
switch (Debugger::onSingleStep(cx, &rval)) {
|
|
||||||
case JSTRAP_ERROR:
|
|
||||||
return false;
|
|
||||||
case JSTRAP_CONTINUE:
|
|
||||||
return true;
|
|
||||||
case JSTRAP_RETURN:
|
|
||||||
// See note in Debugger::propagateForcedReturn.
|
|
||||||
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
|
|
||||||
return false;
|
|
||||||
case JSTRAP_THROW:
|
|
||||||
cx->setPendingException(rval);
|
|
||||||
return false;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No need to set aside any pending exception here: ComputeStackString
|
|
||||||
// already does that.
|
|
||||||
JSString *stack = ComputeStackString(cx);
|
|
||||||
JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
|
|
||||||
|
|
||||||
const char16_t *chars;
|
|
||||||
AutoStableStringChars stableChars(cx);
|
|
||||||
if (flat && stableChars.initTwoByte(cx, flat))
|
|
||||||
chars = stableChars.twoByteRange().start().get();
|
|
||||||
else
|
|
||||||
chars = MOZ_UTF16("(stack not available)");
|
|
||||||
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
|
|
||||||
JSMSG_TERMINATED, chars);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PerThreadData::resetJitStackLimit()
|
|
||||||
{
|
|
||||||
// Note that, for now, we use the untrusted limit for ion. This is fine,
|
|
||||||
// because it's the most conservative limit, and if we hit it, we'll bail
|
|
||||||
// out of ion into the interpeter, which will do a proper recursion check.
|
|
||||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
|
||||||
jitStackLimit_ = jit::Simulator::StackLimit();
|
|
||||||
#else
|
|
||||||
jitStackLimit_ = nativeStackLimit[StackForUntrustedScript];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PerThreadData::initJitStackLimit()
|
|
||||||
{
|
|
||||||
resetJitStackLimit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PerThreadData::initJitStackLimitPar(uintptr_t limit)
|
|
||||||
{
|
|
||||||
jitStackLimit_ = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSRuntime::requestInterrupt(InterruptMode mode)
|
JSRuntime::requestInterrupt(InterruptMode mode)
|
||||||
{
|
{
|
||||||
interrupt_ = true;
|
AutoLockForInterrupt lock(this);
|
||||||
interruptPar_ = true;
|
|
||||||
mainThread.jitStackLimit_ = UINTPTR_MAX;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invalidate ionTop to trigger its over-recursion check. Note this must be
|
||||||
|
* set before interrupt, to avoid racing with js::InvokeInterruptCallback,
|
||||||
|
* into a weird state where interrupt is stuck at 0 but jitStackLimit is
|
||||||
|
* MAXADDR.
|
||||||
|
*/
|
||||||
|
mainThread.setJitStackLimit(-1);
|
||||||
|
|
||||||
|
interrupt = true;
|
||||||
|
|
||||||
|
RequestInterruptForForkJoin(this, mode);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* asm.js and normal Ion code optionally use memory protection and signal
|
||||||
|
* handlers to halt running code.
|
||||||
|
*/
|
||||||
if (canUseSignalHandlers()) {
|
if (canUseSignalHandlers()) {
|
||||||
AutoLockForInterrupt lock(this);
|
|
||||||
RequestInterruptForAsmJSCode(this, mode);
|
RequestInterruptForAsmJSCode(this, mode);
|
||||||
jit::RequestInterruptForIonCode(this, mode);
|
jit::RequestInterruptForIonCode(this, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
JSRuntime::handleInterrupt(JSContext *cx)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
|
||||||
if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) {
|
|
||||||
interrupt_ = false;
|
|
||||||
interruptPar_ = false;
|
|
||||||
mainThread.resetJitStackLimit();
|
|
||||||
return InvokeInterruptCallback(cx);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit::ExecutableAllocator *
|
jit::ExecutableAllocator *
|
||||||
JSRuntime::createExecutableAllocator(JSContext *cx)
|
JSRuntime::createExecutableAllocator(JSContext *cx)
|
||||||
{
|
{
|
||||||
|
@ -519,20 +519,13 @@ class PerThreadData : public PerThreadDataFriendFields
|
|||||||
*/
|
*/
|
||||||
JSContext *jitJSContext;
|
JSContext *jitJSContext;
|
||||||
|
|
||||||
/* See comment for JSRuntime::interrupt_. */
|
/*
|
||||||
private:
|
* The stack limit checked by JIT code. This stack limit may be temporarily
|
||||||
mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
|
* set to null to force JIT code to exit (e.g., for the operation callback).
|
||||||
void resetJitStackLimit();
|
*/
|
||||||
friend struct ::JSRuntime;
|
uintptr_t jitStackLimit;
|
||||||
public:
|
|
||||||
void initJitStackLimit();
|
|
||||||
void initJitStackLimitPar(uintptr_t limit);
|
|
||||||
|
|
||||||
uintptr_t jitStackLimit() const { return jitStackLimit_; }
|
inline void setJitStackLimit(uintptr_t limit);
|
||||||
|
|
||||||
// For read-only JIT use:
|
|
||||||
void *addressofJitStackLimit() { return &jitStackLimit_; }
|
|
||||||
static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); }
|
|
||||||
|
|
||||||
// Information about the heap allocated backtrack stack used by RegExp JIT code.
|
// Information about the heap allocated backtrack stack used by RegExp JIT code.
|
||||||
irregexp::RegExpStack regexpStack;
|
irregexp::RegExpStack regexpStack;
|
||||||
@ -685,6 +678,8 @@ class PerThreadData : public PerThreadDataFriendFields
|
|||||||
|
|
||||||
class AutoLockForExclusiveAccess;
|
class AutoLockForExclusiveAccess;
|
||||||
|
|
||||||
|
void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
struct JSRuntime : public JS::shadow::Runtime,
|
struct JSRuntime : public JS::shadow::Runtime,
|
||||||
@ -708,56 +703,18 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
*/
|
*/
|
||||||
JSRuntime *parentRuntime;
|
JSRuntime *parentRuntime;
|
||||||
|
|
||||||
private:
|
/*
|
||||||
mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
|
* If true, we've been asked to call the interrupt callback as soon as
|
||||||
mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptPar_;
|
* possible.
|
||||||
public:
|
*/
|
||||||
|
mozilla::Atomic<bool, mozilla::Relaxed> interrupt;
|
||||||
|
|
||||||
enum InterruptMode {
|
/*
|
||||||
RequestInterruptMainThread,
|
* If non-zero, ForkJoin should service an interrupt. This is a separate
|
||||||
RequestInterruptAnyThread,
|
* flag from |interrupt| because we cannot use the mprotect trick with PJS
|
||||||
RequestInterruptAnyThreadDontStopIon,
|
* code and ignore the TriggerCallbackAnyThreadDontStopIon trigger.
|
||||||
RequestInterruptAnyThreadForkJoin
|
*/
|
||||||
};
|
mozilla::Atomic<bool, mozilla::Relaxed> interruptPar;
|
||||||
|
|
||||||
// Any thread can call requestInterrupt() to request that the main JS thread
|
|
||||||
// stop running and call the interrupt callback (allowing the interrupt
|
|
||||||
// callback to halt execution). To stop the main JS thread, requestInterrupt
|
|
||||||
// sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
|
|
||||||
// UINTPTR_MAX). The JS engine must continually poll one of these fields
|
|
||||||
// and call handleInterrupt if either field has the interrupt value. (The
|
|
||||||
// point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
|
|
||||||
// needs to guard on jitStackLimit_ in every function prologue to avoid
|
|
||||||
// stack overflow, so we avoid a second branch on interrupt_ by setting
|
|
||||||
// jitStackLimit_ to a value that is guaranteed to fail the guard.)
|
|
||||||
//
|
|
||||||
// Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
|
|
||||||
// Atomic so, while the writes are guaranteed to eventually be visible to
|
|
||||||
// the main thread, it can happen in any order. handleInterrupt calls the
|
|
||||||
// interrupt callback if either is set, so it really doesn't matter as long
|
|
||||||
// as the JS engine is continually polling at least one field. In corner
|
|
||||||
// cases, this relaxed ordering could lead to an interrupt handler being
|
|
||||||
// called twice in succession after a single requestInterrupt call, but
|
|
||||||
// that's fine.
|
|
||||||
void requestInterrupt(InterruptMode mode);
|
|
||||||
bool handleInterrupt(JSContext *cx);
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const {
|
|
||||||
return interrupt_;
|
|
||||||
}
|
|
||||||
MOZ_ALWAYS_INLINE bool hasPendingInterruptPar() const {
|
|
||||||
return interruptPar_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For read-only JIT use:
|
|
||||||
void *addressOfInterruptUint32() {
|
|
||||||
static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers");
|
|
||||||
return &interrupt_;
|
|
||||||
}
|
|
||||||
void *addressOfInterruptParUint32() {
|
|
||||||
static_assert(sizeof(interruptPar_) == sizeof(uint32_t), "Assumed by JIT callers");
|
|
||||||
return &interruptPar_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set when handling a signal for a thread associated with this runtime. */
|
/* Set when handling a signal for a thread associated with this runtime. */
|
||||||
bool handlingSignal;
|
bool handlingSignal;
|
||||||
@ -949,7 +906,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
void setDefaultVersion(JSVersion v) { defaultVersion_ = v; }
|
void setDefaultVersion(JSVersion v) { defaultVersion_ = v; }
|
||||||
|
|
||||||
/* Base address of the native stack for the current thread. */
|
/* Base address of the native stack for the current thread. */
|
||||||
const uintptr_t nativeStackBase;
|
uintptr_t nativeStackBase;
|
||||||
|
|
||||||
/* The native stack size limit that runtime should not exceed. */
|
/* The native stack size limit that runtime should not exceed. */
|
||||||
size_t nativeStackQuota[js::StackKindCount];
|
size_t nativeStackQuota[js::StackKindCount];
|
||||||
@ -1315,6 +1272,10 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
bool jitSupportsFloatingPoint;
|
bool jitSupportsFloatingPoint;
|
||||||
bool jitSupportsSimd;
|
bool jitSupportsSimd;
|
||||||
|
|
||||||
|
// Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1)
|
||||||
|
// has been noticed by Ion/Baseline.
|
||||||
|
void resetJitStackLimit();
|
||||||
|
|
||||||
// Cache for jit::GetPcScript().
|
// Cache for jit::GetPcScript().
|
||||||
js::jit::PcScriptCache *ionPcScriptCache;
|
js::jit::PcScriptCache *ionPcScriptCache;
|
||||||
|
|
||||||
@ -1375,6 +1336,17 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
/* onOutOfMemory but can call the largeAllocationFailureCallback. */
|
/* onOutOfMemory but can call the largeAllocationFailureCallback. */
|
||||||
JS_FRIEND_API(void *) onOutOfMemoryCanGC(void *p, size_t bytes);
|
JS_FRIEND_API(void *) onOutOfMemoryCanGC(void *p, size_t bytes);
|
||||||
|
|
||||||
|
// Ways in which the interrupt callback on the runtime can be triggered,
|
||||||
|
// varying based on which thread is triggering the callback.
|
||||||
|
enum InterruptMode {
|
||||||
|
RequestInterruptMainThread,
|
||||||
|
RequestInterruptAnyThread,
|
||||||
|
RequestInterruptAnyThreadDontStopIon,
|
||||||
|
RequestInterruptAnyThreadForkJoin
|
||||||
|
};
|
||||||
|
|
||||||
|
void requestInterrupt(InterruptMode mode);
|
||||||
|
|
||||||
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime);
|
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1598,6 +1570,13 @@ class MOZ_STACK_CLASS AutoKeepAtoms
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void
|
||||||
|
PerThreadData::setJitStackLimit(uintptr_t limit)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(runtime_->currentThreadOwnsInterruptLock());
|
||||||
|
jitStackLimit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
inline JSRuntime *
|
inline JSRuntime *
|
||||||
PerThreadData::runtimeFromMainThread()
|
PerThreadData::runtimeFromMainThread()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user