Bug 881902 - Remove ContextStack and StackSpace. r=luke,njn

This commit is contained in:
Jan de Mooij 2013-06-21 08:28:06 +02:00
parent bf9d0ded51
commit f957967378
43 changed files with 702 additions and 1659 deletions

View File

@ -135,7 +135,7 @@ struct RuntimeSizes
size_t dtoa; size_t dtoa;
size_t temporary; size_t temporary;
size_t regexpData; size_t regexpData;
size_t stack; size_t interpreterStack;
size_t gcMarker; size_t gcMarker;
size_t mathCache; size_t mathCache;
size_t scriptData; size_t scriptData;

View File

@ -405,7 +405,7 @@ static inline bool
WarnOnTooManyArgs(JSContext *cx, const CallArgs &args) WarnOnTooManyArgs(JSContext *cx, const CallArgs &args)
{ {
if (args.length() > 1) { if (args.length() > 1) {
Rooted<JSScript*> script(cx, cx->stack.currentScript()); Rooted<JSScript*> script(cx, cx->currentScript());
if (script && !script->warnedAboutTwoArgumentEval) { if (script && !script->warnedAboutTwoArgumentEval) {
static const char TWO_ARGUMENT_WARNING[] = static const char TWO_ARGUMENT_WARNING[] =
"Support for eval(code, scopeObject) has been removed. " "Support for eval(code, scopeObject) has been removed. "

View File

@ -443,8 +443,8 @@ IntlInitialize(JSContext *cx, HandleObject obj, Handle<PropertyName*> initialize
JS_ASSERT(initializerValue.isObject()); JS_ASSERT(initializerValue.isObject());
JS_ASSERT(initializerValue.toObject().is<JSFunction>()); JS_ASSERT(initializerValue.toObject().is<JSFunction>());
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 3, &args)) if (!args.init(3))
return false; return false;
args.setCallee(initializerValue); args.setCallee(initializerValue);
@ -509,8 +509,8 @@ GetInternals(JSContext *cx, HandleObject obj, MutableHandleObject internals)
JS_ASSERT(getInternalsValue.isObject()); JS_ASSERT(getInternalsValue.isObject());
JS_ASSERT(getInternalsValue.toObject().is<JSFunction>()); JS_ASSERT(getInternalsValue.toObject().is<JSFunction>());
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 1, &args)) if (!args.init(1))
return false; return false;
args.setCallee(getInternalsValue); args.setCallee(getInternalsValue);

View File

@ -530,14 +530,14 @@ obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
* Implement [[Prototype]]-getting -- particularly across compartment * Implement [[Prototype]]-getting -- particularly across compartment
* boundaries -- by calling a cached __proto__ getter function. * boundaries -- by calling a cached __proto__ getter function.
*/ */
InvokeArgsGuard nested; InvokeArgs args2(cx);
if (!cx->stack.pushInvokeArgs(cx, 0, &nested)) if (!args2.init(0))
return false; return false;
nested.setCallee(cx->global()->protoGetter()); args2.setCallee(cx->global()->protoGetter());
nested.setThis(args[0]); args2.setThis(args[0]);
if (!Invoke(cx, nested)) if (!Invoke(cx, args2))
return false; return false;
args.rval().set(nested.rval()); args.rval().set(args2.rval());
return true; return true;
} }

View File

@ -133,7 +133,7 @@ ParallelArrayObject::constructHelper(JSContext *cx, MutableHandleFunction ctor,
if (cx->typeInferenceEnabled()) { if (cx->typeInferenceEnabled()) {
jsbytecode *pc; jsbytecode *pc;
RootedScript script(cx, cx->stack.currentScript(&pc)); RootedScript script(cx, cx->currentScript(&pc));
if (script) { if (script) {
if (ctor->nonLazyScript()->shouldCloneAtCallsite) { if (ctor->nonLazyScript()->shouldCloneAtCallsite) {
ctor.set(CloneFunctionAtCallsite(cx, ctor, script, pc)); ctor.set(CloneFunctionAtCallsite(cx, ctor, script, pc));
@ -163,8 +163,8 @@ ParallelArrayObject::constructHelper(JSContext *cx, MutableHandleFunction ctor,
} }
} }
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, args0.length(), &args)) if (!args.init(args0.length()))
return false; return false;
args.setCallee(ObjectValue(*ctor)); args.setCallee(ObjectValue(*ctor));

View File

@ -513,24 +513,6 @@ MarkValueInternal(JSTracer *trc, Value *v)
} }
} }
static inline void
MarkValueInternalMaybeNullPayload(JSTracer *trc, Value *v)
{
if (v->isMarkable()) {
void *thing = v->toGCThing();
if (thing) {
JS_SET_TRACING_LOCATION(trc, (void *)v);
MarkKind(trc, &thing, v->gcKind());
if (v->isString())
v->setString((JSString *)thing);
else
v->setObjectOrNull((JSObject *)thing);
return;
}
}
JS_UNSET_TRACING_LOCATION(trc);
}
void void
gc::MarkValue(JSTracer *trc, EncapsulatedValue *v, const char *name) gc::MarkValue(JSTracer *trc, EncapsulatedValue *v, const char *name)
{ {
@ -581,16 +563,6 @@ gc::MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name)
} }
} }
void
gc::MarkValueRootRangeMaybeNullPayload(JSTracer *trc, size_t len, Value *vec, const char *name)
{
JS_ROOT_MARKING_ASSERT(trc);
for (size_t i = 0; i < len; ++i) {
JS_SET_TRACING_INDEX(trc, name, i);
MarkValueInternalMaybeNullPayload(trc, &vec[i]);
}
}
bool bool
gc::IsValueMarked(Value *v) gc::IsValueMarked(Value *v)
{ {

View File

@ -181,9 +181,6 @@ MarkValueRootRange(JSTracer *trc, Value *begin, Value *end, const char *name)
MarkValueRootRange(trc, end - begin, begin, name); MarkValueRootRange(trc, end - begin, begin, name);
} }
void
MarkValueRootRangeMaybeNullPayload(JSTracer *trc, size_t len, Value *vec, const char *name);
void void
MarkTypeRoot(JSTracer *trc, types::Type *v, const char *name); MarkTypeRoot(JSTracer *trc, types::Type *v, const char *name);

View File

@ -737,7 +737,7 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots)
c->debugScopes->mark(trc); c->debugScopes->mark(trc);
} }
rt->stackSpace.mark(trc); MarkInterpreterActivations(rt, trc);
#ifdef JS_ION #ifdef JS_ION
ion::MarkJitActivations(rt, trc); ion::MarkJitActivations(rt, trc);

View File

@ -411,8 +411,8 @@ HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, Hand
unsigned argc = args.length(); unsigned argc = args.length();
InvokeArgsGuard args2; InvokeArgs args2(cx);
if (!cx->stack.pushInvokeArgs(cx, argc, &args2)) if (!args2.init(argc))
return false; return false;
args2.setCallee(ObjectValue(*fun)); args2.setCallee(ObjectValue(*fun));

View File

@ -1204,7 +1204,7 @@ ion::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
// Check that we can get the current script's PC. // Check that we can get the current script's PC.
#ifdef DEBUG #ifdef DEBUG
jsbytecode *pc; jsbytecode *pc;
cx->stack.currentScript(&pc); cx->currentScript(&pc);
IonSpew(IonSpew_BaselineBailouts, " Got pc=%p", pc); IonSpew(IonSpew_BaselineBailouts, " Got pc=%p", pc);
#endif #endif

View File

@ -963,9 +963,6 @@ JSRuntime::init(uint32_t maxbytes)
dateTimeInfo.updateTimeZoneAdjustment(); dateTimeInfo.updateTimeZoneAdjustment();
if (!stackSpace.init())
return false;
if (!scriptDataTable.init()) if (!scriptDataTable.init())
return false; return false;
@ -5784,8 +5781,8 @@ JS_New(JSContext *cx, JSObject *ctorArg, unsigned argc, jsval *argv)
// is not a simple variation of JSOP_CALL. We have to determine what class // is not a simple variation of JSOP_CALL. We have to determine what class
// of object to create, create it, and clamp the return value to an object, // of object to create, create it, and clamp the return value to an object,
// among other details. InvokeConstructor does the hard work. // among other details. InvokeConstructor does the hard work.
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, argc, &args)) if (!args.init(argc))
return NULL; return NULL;
args.setCallee(ObjectValue(*ctor)); args.setCallee(ObjectValue(*ctor));

View File

@ -1009,17 +1009,17 @@ array_toString(JSContext *cx, unsigned argc, Value *vp)
return true; return true;
} }
InvokeArgsGuard ag; InvokeArgs args2(cx);
if (!cx->stack.pushInvokeArgs(cx, 0, &ag)) if (!args2.init(0))
return false; return false;
ag.setCallee(join); args2.setCallee(join);
ag.setThis(ObjectValue(*obj)); args2.setThis(ObjectValue(*obj));
/* Do the call. */ /* Do the call. */
if (!Invoke(cx, ag)) if (!Invoke(cx, args2))
return false; return false;
args.rval().set(ag.rval()); args.rval().set(args2.rval());
return true; return true;
} }
@ -1421,20 +1421,20 @@ SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrE
if (!JS_CHECK_OPERATION_LIMIT(cx)) if (!JS_CHECK_OPERATION_LIMIT(cx))
return false; return false;
InvokeArgsGuard &ag = fig.args(); InvokeArgs &args = fig.args();
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &ag)) if (!args.init(2))
return false; return false;
ag.setCallee(fval); args.setCallee(fval);
ag.setThis(UndefinedValue()); args.setThis(UndefinedValue());
ag[0] = a; args[0] = a;
ag[1] = b; args[1] = b;
if (!fig.invoke(cx)) if (!fig.invoke(cx))
return false; return false;
double cmp; double cmp;
if (!ToNumber(cx, ag.rval(), &cmp)) if (!ToNumber(cx, args.rval(), &cmp))
return false; return false;
/* /*
@ -2637,7 +2637,7 @@ array_filter(JSContext *cx, unsigned argc, Value *vp)
/* Step 9. */ /* Step 9. */
JS_ASSERT(!InParallelSection()); JS_ASSERT(!InParallelSection());
FastInvokeGuard fig(cx, ObjectValue(*callable)); FastInvokeGuard fig(cx, ObjectValue(*callable));
InvokeArgsGuard &ag = fig.args(); InvokeArgs &args2 = fig.args();
RootedValue kValue(cx); RootedValue kValue(cx);
while (k < len) { while (k < len) {
if (!JS_CHECK_OPERATION_LIMIT(cx)) if (!JS_CHECK_OPERATION_LIMIT(cx))
@ -2650,17 +2650,17 @@ array_filter(JSContext *cx, unsigned argc, Value *vp)
/* Step c.ii-iii. */ /* Step c.ii-iii. */
if (!kNotPresent) { if (!kNotPresent) {
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag)) if (!args2.init(3))
return false; return false;
ag.setCallee(ObjectValue(*callable)); args2.setCallee(ObjectValue(*callable));
ag.setThis(thisv); args2.setThis(thisv);
ag[0] = kValue; args2[0] = kValue;
ag[1] = NumberValue(k); args2[1] = NumberValue(k);
ag[2] = ObjectValue(*obj); args2[2] = ObjectValue(*obj);
if (!fig.invoke(cx)) if (!fig.invoke(cx))
return false; return false;
if (ToBoolean(ag.rval())) { if (ToBoolean(args2.rval())) {
if (!SetArrayElement(cx, arr, to, kValue)) if (!SetArrayElement(cx, arr, to, kValue))
return false; return false;
to++; to++;

View File

@ -126,7 +126,7 @@ JSRuntime::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, JS::RuntimeSizes
rtSizes->regexpData = bumpAlloc_ ? bumpAlloc_->sizeOfNonHeapData() : 0; rtSizes->regexpData = bumpAlloc_ ? bumpAlloc_->sizeOfNonHeapData() : 0;
rtSizes->stack = stackSpace.sizeOf(); rtSizes->interpreterStack = interpreterStack_.sizeOfExcludingThis(mallocSizeOf);
rtSizes->gcMarker = gcMarker.sizeOfExcludingThis(mallocSizeOf); rtSizes->gcMarker = gcMarker.sizeOfExcludingThis(mallocSizeOf);
@ -560,7 +560,7 @@ checkReportFlags(JSContext *cx, unsigned *flags)
* otherwise. We assume that if the top frame is a native, then it is * otherwise. We assume that if the top frame is a native, then it is
* strict if the nearest scripted frame is strict, see bug 536306. * strict if the nearest scripted frame is strict, see bug 536306.
*/ */
JSScript *script = cx->stack.currentScript(); JSScript *script = cx->currentScript();
if (script && script->strict) if (script && script->strict)
*flags &= ~JSREPORT_WARNING; *flags &= ~JSREPORT_WARNING;
else if (cx->hasExtraWarningsOption()) else if (cx->hasExtraWarningsOption())
@ -1179,7 +1179,6 @@ JSContext::JSContext(JSRuntime *rt)
enterCompartmentDepth_(0), enterCompartmentDepth_(0),
savedFrameChains_(), savedFrameChains_(),
defaultCompartmentObject_(NULL), defaultCompartmentObject_(NULL),
stack(thisDuringConstruction()),
cycleDetectorSet(thisDuringConstruction()), cycleDetectorSet(thisDuringConstruction()),
errorReporter(NULL), errorReporter(NULL),
operationCallback(NULL), operationCallback(NULL),
@ -1302,14 +1301,9 @@ JSContext::runningWithTrustedPrincipals() const
bool bool
JSContext::saveFrameChain() JSContext::saveFrameChain()
{ {
if (!stack.saveFrameChain()) if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_)))
return false; return false;
if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_))) {
stack.restoreFrameChain();
return false;
}
if (Activation *act = mainThread().activation()) if (Activation *act = mainThread().activation())
act->saveFrameChain(); act->saveFrameChain();
@ -1331,8 +1325,6 @@ JSContext::restoreFrameChain()
setCompartment(sfc.compartment); setCompartment(sfc.compartment);
enterCompartmentDepth_ = sfc.enterCompartmentCount; enterCompartmentDepth_ = sfc.enterCompartmentCount;
stack.restoreFrameChain();
if (Activation *act = mainThread().activation()) if (Activation *act = mainThread().activation())
act->restoreFrameChain(); act->restoreFrameChain();
@ -1532,7 +1524,7 @@ JSContext::findVersion() const
if (hasVersionOverride) if (hasVersionOverride)
return versionOverride; return versionOverride;
if (JSScript *script = stack.currentScript(NULL, js::ContextStack::ALLOW_CROSS_COMPARTMENT)) if (JSScript *script = currentScript(NULL, ALLOW_CROSS_COMPARTMENT))
return script->getVersion(); return script->getVersion();
return defaultVersion; return defaultVersion;

View File

@ -726,9 +726,6 @@ struct JSRuntime : public JS::shadow::Runtime,
void assertValidThread() const {} void assertValidThread() const {}
#endif #endif
/* Keeper of the contiguous stack used by all contexts in this thread. */
js::StackSpace stackSpace;
/* Temporary arena pool used while compiling and decompiling. */ /* Temporary arena pool used while compiling and decompiling. */
static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024; static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024;
js::LifoAlloc tempLifoAlloc; js::LifoAlloc tempLifoAlloc;
@ -750,6 +747,9 @@ struct JSRuntime : public JS::shadow::Runtime,
JSObject *selfHostingGlobal_; JSObject *selfHostingGlobal_;
/* Space for interpreter frames. */
js::InterpreterStack interpreterStack_;
JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx); JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx); WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
js::ion::IonRuntime *createIonRuntime(JSContext *cx); js::ion::IonRuntime *createIonRuntime(JSContext *cx);
@ -777,6 +777,9 @@ struct JSRuntime : public JS::shadow::Runtime,
bool hasIonRuntime() const { bool hasIonRuntime() const {
return !!ionRuntime_; return !!ionRuntime_;
} }
js::InterpreterStack &interpreterStack() {
return interpreterStack_;
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Self-hosting support // Self-hosting support
@ -1686,9 +1689,6 @@ struct JSContext : js::ThreadSafeContext,
inline void setDefaultCompartmentObjectIfUnset(JSObject *obj); inline void setDefaultCompartmentObjectIfUnset(JSObject *obj);
JSObject *maybeDefaultCompartmentObject() const { return defaultCompartmentObject_; } JSObject *maybeDefaultCompartmentObject() const { return defaultCompartmentObject_; }
/* Current execution stack. */
js::ContextStack stack;
/* /*
* Current global. This is only safe to use within the scope of the * Current global. This is only safe to use within the scope of the
* AutoCompartment from which it's called. * AutoCompartment from which it's called.
@ -1748,8 +1748,7 @@ struct JSContext : js::ThreadSafeContext,
* default version. * default version.
*/ */
void maybeMigrateVersionOverride() { void maybeMigrateVersionOverride() {
JS_ASSERT(stack.empty()); if (JS_UNLIKELY(isVersionOverridden()) && !currentlyRunning()) {
if (JS_UNLIKELY(isVersionOverridden())) {
defaultVersion = versionOverride; defaultVersion = versionOverride;
clearVersionOverride(); clearVersionOverride();
} }
@ -1820,6 +1819,19 @@ struct JSContext : js::ThreadSafeContext,
return mainThread().activation()->asInterpreter()->regs(); return mainThread().activation()->asInterpreter()->regs();
} }
/*
* Get the topmost script and optional pc on the stack. By default, this
* function only returns a JSScript in the current compartment, returning
* NULL if the current script is in a different compartment. This behavior
* can be overridden by passing ALLOW_CROSS_COMPARTMENT.
*/
enum MaybeAllowCrossCompartment {
DONT_ALLOW_CROSS_COMPARTMENT = false,
ALLOW_CROSS_COMPARTMENT = true
};
inline JSScript *currentScript(jsbytecode **pc = NULL,
MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const;
#ifdef MOZ_TRACE_JSCALLS #ifdef MOZ_TRACE_JSCALLS
/* Function entry/exit debugging callback. */ /* Function entry/exit debugging callback. */
JSFunctionCallback functionCallback; JSFunctionCallback functionCallback;
@ -2015,6 +2027,12 @@ class MOZ_STACK_CLASS AutoKeepAtoms
~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); }
}; };
// Maximum supported value of arguments.length. This bounds the maximum
// number of arguments that can be supplied to Function.prototype.apply.
// This value also bounds the number of elements parsed in an array
// initialiser.
static const unsigned ARGS_LENGTH_MAX = 500 * 1000;
} /* namespace js */ } /* namespace js */
class JSAutoResolveFlags class JSAutoResolveFlags

View File

@ -16,6 +16,7 @@
#include "builtin/Object.h" // For js::obj_construct #include "builtin/Object.h" // For js::obj_construct
#include "frontend/ParseMaps.h" #include "frontend/ParseMaps.h"
#include "ion/IonFrames.h" // For GetPcScript
#include "vm/Interpreter.h" #include "vm/Interpreter.h"
#include "vm/Probes.h" #include "vm/Probes.h"
#include "vm/RegExpObject.h" #include "vm/RegExpObject.h"
@ -123,25 +124,6 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
return NULL; return NULL;
} }
struct PreserveRegsGuard
{
PreserveRegsGuard(JSContext *cx, FrameRegs &regs)
: prevContextRegs(cx->stack.maybeRegs()), cx(cx), regs_(regs) {
cx->stack.repointRegs(&regs_);
}
~PreserveRegsGuard() {
JS_ASSERT(cx->stack.maybeRegs() == &regs_);
*prevContextRegs = regs_;
cx->stack.repointRegs(prevContextRegs);
}
FrameRegs *prevContextRegs;
private:
JSContext *cx;
FrameRegs &regs_;
};
#ifdef JS_CRASH_DIAGNOSTICS #ifdef JS_CRASH_DIAGNOSTICS
class CompartmentChecker class CompartmentChecker
{ {
@ -575,6 +557,48 @@ JSContext::setCompartment(JSCompartment *comp)
allocator_ = zone_ ? &zone_->allocator : NULL; allocator_ = zone_ ? &zone_->allocator : NULL;
} }
inline JSScript *
JSContext::currentScript(jsbytecode **ppc,
MaybeAllowCrossCompartment allowCrossCompartment) const
{
if (ppc)
*ppc = NULL;
js::Activation *act = mainThread().activation();
while (act && (act->cx() != this || !act->isActive()))
act = act->prev();
if (!act)
return NULL;
JS_ASSERT(act->cx() == this);
#ifdef JS_ION
if (act->isJit()) {
JSScript *script = NULL;
js::ion::GetPcScript(const_cast<JSContext *>(this), &script, ppc);
if (!allowCrossCompartment && script->compartment() != compartment())
return NULL;
return script;
}
#endif
JS_ASSERT(act->isInterpreter());
js::StackFrame *fp = act->asInterpreter()->current();
JS_ASSERT(!fp->runningInJit());
JSScript *script = fp->script();
if (!allowCrossCompartment && script->compartment() != compartment())
return NULL;
if (ppc) {
*ppc = act->asInterpreter()->regs().pc;
JS_ASSERT(*ppc >= script->code && *ppc < script->code + script->length);
}
return script;
}
template <typename T> template <typename T>
inline bool inline bool
js::ThreadSafeContext::isInsideCurrentZone(T thing) const js::ThreadSafeContext::isInsideCurrentZone(T thing) const

View File

@ -2590,16 +2590,16 @@ date_toJSON(JSContext *cx, unsigned argc, Value *vp)
} }
/* Step 6. */ /* Step 6. */
InvokeArgsGuard ag; InvokeArgs args2(cx);
if (!cx->stack.pushInvokeArgs(cx, 0, &ag)) if (!args2.init(0))
return false; return false;
ag.setCallee(toISO); args2.setCallee(toISO);
ag.setThis(ObjectValue(*obj)); args2.setThis(ObjectValue(*obj));
if (!Invoke(cx, ag)) if (!Invoke(cx, args2))
return false; return false;
args.rval().set(ag.rval()); args.rval().set(args2.rval());
return true; return true;
} }

View File

@ -1238,8 +1238,6 @@ JSObject *
JSAbstractFramePtr::scopeChain(JSContext *cx) JSAbstractFramePtr::scopeChain(JSContext *cx)
{ {
AbstractFramePtr frame = Valueify(*this); AbstractFramePtr frame = Valueify(*this);
JS_ASSERT_IF(frame.isStackFrame(),
cx->stack.space().containsSlow(frame.asStackFrame()));
RootedObject scopeChain(cx, frame.scopeChain()); RootedObject scopeChain(cx, frame.scopeChain());
AutoCompartment ac(cx, scopeChain); AutoCompartment ac(cx, scopeChain);
return GetDebugScopeForFrame(cx, frame); return GetDebugScopeForFrame(cx, frame);
@ -1249,9 +1247,6 @@ JSObject *
JSAbstractFramePtr::callObject(JSContext *cx) JSAbstractFramePtr::callObject(JSContext *cx)
{ {
AbstractFramePtr frame = Valueify(*this); AbstractFramePtr frame = Valueify(*this);
JS_ASSERT_IF(frame.isStackFrame(),
cx->stack.space().containsSlow(frame.asStackFrame()));
if (!frame.isFunctionFrame()) if (!frame.isFunctionFrame())
return NULL; return NULL;

View File

@ -800,7 +800,7 @@ js::SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg)
JS_FRIEND_API(bool) JS_FRIEND_API(bool)
js::IsContextRunningJS(JSContext *cx) js::IsContextRunningJS(JSContext *cx)
{ {
return !cx->stack.empty(); return cx->currentlyRunning();
} }
JS_FRIEND_API(JS::GCSliceCallback) JS_FRIEND_API(JS::GCSliceCallback)

View File

@ -817,8 +817,8 @@ js_fun_call(JSContext *cx, unsigned argc, Value *vp)
} }
/* Allocate stack space for fval, obj, and the args. */ /* Allocate stack space for fval, obj, and the args. */
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, argc, &args)) if (!args.init(argc))
return JS_FALSE; return JS_FALSE;
/* Push fval, thisv, and the args. */ /* Push fval, thisv, and the args. */
@ -833,13 +833,13 @@ js_fun_call(JSContext *cx, unsigned argc, Value *vp)
#ifdef JS_ION #ifdef JS_ION
static bool static bool
PushBaselineFunApplyArguments(JSContext *cx, ion::IonFrameIterator &frame, InvokeArgsGuard &args, PushBaselineFunApplyArguments(JSContext *cx, ion::IonFrameIterator &frame, InvokeArgs &args,
Value *vp) Value *vp)
{ {
unsigned length = frame.numActualArgs(); unsigned length = frame.numActualArgs();
JS_ASSERT(length <= StackSpace::ARGS_LENGTH_MAX); JS_ASSERT(length <= ARGS_LENGTH_MAX);
if (!cx->stack.pushInvokeArgs(cx, length, &args)) if (!args.init(length))
return false; return false;
/* Push fval, obj, and aobj's elements as args. */ /* Push fval, obj, and aobj's elements as args. */
@ -867,7 +867,7 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
if (argc < 2 || vp[3].isNullOrUndefined()) if (argc < 2 || vp[3].isNullOrUndefined())
return js_fun_call(cx, (argc > 0) ? 1 : 0, vp); return js_fun_call(cx, (argc > 0) ? 1 : 0, vp);
InvokeArgsGuard args; InvokeArgs args(cx);
/* /*
* GuardFunApplyArgumentsOptimization already called IsOptimizedArguments, * GuardFunApplyArgumentsOptimization already called IsOptimizedArguments,
@ -895,9 +895,9 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
ion::InlineFrameIterator iter(cx, &frame); ion::InlineFrameIterator iter(cx, &frame);
unsigned length = iter.numActualArgs(); unsigned length = iter.numActualArgs();
JS_ASSERT(length <= StackSpace::ARGS_LENGTH_MAX); JS_ASSERT(length <= ARGS_LENGTH_MAX);
if (!cx->stack.pushInvokeArgs(cx, length, &args)) if (!args.init(length))
return false; return false;
/* Push fval, obj, and aobj's elements as args. */ /* Push fval, obj, and aobj's elements as args. */
@ -932,9 +932,9 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
{ {
StackFrame *fp = cx->interpreterFrame(); StackFrame *fp = cx->interpreterFrame();
unsigned length = fp->numActualArgs(); unsigned length = fp->numActualArgs();
JS_ASSERT(length <= StackSpace::ARGS_LENGTH_MAX); JS_ASSERT(length <= ARGS_LENGTH_MAX);
if (!cx->stack.pushInvokeArgs(cx, length, &args)) if (!args.init(length))
return false; return false;
/* Push fval, obj, and aobj's elements as args. */ /* Push fval, obj, and aobj's elements as args. */
@ -961,12 +961,12 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
return false; return false;
/* Step 6. */ /* Step 6. */
if (length > StackSpace::ARGS_LENGTH_MAX) { if (length > ARGS_LENGTH_MAX) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS);
return false; return false;
} }
if (!cx->stack.pushInvokeArgs(cx, length, &args)) if (!args.init(length))
return false; return false;
/* Push fval, obj, and aobj's elements as args. */ /* Push fval, obj, and aobj's elements as args. */
@ -1118,7 +1118,7 @@ js::CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp)
/* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */ /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
unsigned argslen = fun->getBoundFunctionArgumentCount(); unsigned argslen = fun->getBoundFunctionArgumentCount();
if (argc + argslen > StackSpace::ARGS_LENGTH_MAX) { if (argc + argslen > ARGS_LENGTH_MAX) {
js_ReportAllocationOverflow(cx); js_ReportAllocationOverflow(cx);
return false; return false;
} }
@ -1129,8 +1129,8 @@ js::CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp)
/* 15.3.4.5.1 step 2. */ /* 15.3.4.5.1 step 2. */
const Value &boundThis = fun->getBoundFunctionThis(); const Value &boundThis = fun->getBoundFunctionThis();
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, argc + argslen, &args)) if (!args.init(argc + argslen))
return false; return false;
/* 15.3.4.5.1, 15.3.4.5.2 step 4. */ /* 15.3.4.5.1, 15.3.4.5.2 step 4. */

View File

@ -2558,6 +2558,7 @@ PurgeRuntime(JSRuntime *rt)
comp->purge(); comp->purge();
rt->freeLifoAlloc.transferUnusedFrom(&rt->tempLifoAlloc); rt->freeLifoAlloc.transferUnusedFrom(&rt->tempLifoAlloc);
rt->interpreterStack().purge(rt);
rt->gsnCache.purge(); rt->gsnCache.purge();
rt->newObjectCache.purge(); rt->newObjectCache.purge();
@ -4132,8 +4133,6 @@ AutoGCSlice::AutoGCSlice(JSRuntime *rt)
* is set at the beginning of the mark phase. During incremental GC, we also * is set at the beginning of the mark phase. During incremental GC, we also
* set it at the start of every phase. * set it at the start of every phase.
*/ */
rt->stackSpace.markActiveCompartments();
for (ActivationIterator iter(rt); !iter.done(); ++iter) for (ActivationIterator iter(rt); !iter.done(); ++iter)
iter.activation()->compartment()->zone()->active = true; iter.activation()->compartment()->zone()->active = true;

View File

@ -5426,7 +5426,7 @@ types::MarkIteratorUnknownSlow(JSContext *cx)
/* Check whether we are actually at an ITER opcode. */ /* Check whether we are actually at an ITER opcode. */
jsbytecode *pc; jsbytecode *pc;
RootedScript script(cx, cx->stack.currentScript(&pc)); RootedScript script(cx, cx->currentScript(&pc));
if (!script || !pc) if (!script || !pc)
return; return;

View File

@ -521,7 +521,7 @@ GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
{ {
if (cx->typeInferenceEnabled()) { if (cx->typeInferenceEnabled()) {
jsbytecode *pc; jsbytecode *pc;
RootedScript script(cx, cx->stack.currentScript(&pc)); RootedScript script(cx, cx->currentScript(&pc));
if (script) if (script)
return TypeScript::InitObject(cx, script, pc, key); return TypeScript::InitObject(cx, script, pc, key);
} }
@ -961,7 +961,7 @@ TypeScript::MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc)
/* static */ inline void /* static */ inline void
TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc) TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc)
{ {
*script = cx->stack.currentScript(pc); *script = cx->currentScript(pc);
} }
/* static */ inline void /* static */ inline void

View File

@ -1392,15 +1392,15 @@ GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState
GeneratorState::~GeneratorState() GeneratorState::~GeneratorState()
{ {
gen_->fp->setSuspended();
if (entered_) if (entered_)
cx_->leaveGenerator(gen_); cx_->leaveGenerator(gen_);
} }
StackFrame * StackFrame *
GeneratorState::pushInterpreterFrame(JSContext *cx) GeneratorState::pushInterpreterFrame(JSContext *cx, FrameGuard *)
{ {
gfg_.construct();
/* /*
* Write barrier is needed since the generator stack can be updated, * Write barrier is needed since the generator stack can be updated,
* and it's not barriered in any other way. We need to do it before * and it's not barriered in any other way. We need to do it before
@ -1414,21 +1414,17 @@ GeneratorState::pushInterpreterFrame(JSContext *cx)
*/ */
GeneratorWriteBarrierPre(cx, gen_); GeneratorWriteBarrierPre(cx, gen_);
if (!cx->stack.pushGeneratorFrame(cx, gen_, gfg_.addr())) {
SetGeneratorClosed(cx, gen_);
return NULL;
}
/* /*
* Don't change the state until after the frame is successfully pushed * Don't change the state until after the frame is successfully pushed
* or else we might fail to scan some generator values. * or else we might fail to scan some generator values.
*/ */
gen_->state = futureState_; gen_->state = futureState_;
gen_->regs = cx->stack.regs();
gen_->fp->clearSuspended();
cx->enterGenerator(gen_); /* OOM check above. */ cx->enterGenerator(gen_); /* OOM check above. */
entered_ = true; entered_ = true;
return gfg_.ref().fp(); return gen_->fp;
} }
static void static void
@ -1504,13 +1500,14 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
JS_ASSERT(nbytes % sizeof(Value) == 0); JS_ASSERT(nbytes % sizeof(Value) == 0);
JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(HeapValue) == 0); JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(HeapValue) == 0);
JSGenerator *gen = (JSGenerator *) cx->malloc_(nbytes); JSGenerator *gen = (JSGenerator *) cx->calloc_(nbytes);
if (!gen) if (!gen)
return NULL; return NULL;
SetValueRangeToUndefined((Value *)gen, nbytes / sizeof(Value));
/* Cut up floatingStack space. */ /* Cut up floatingStack space. */
HeapValue *genvp = gen->stackSnapshot; HeapValue *genvp = gen->stackSnapshot;
SetValueRangeToUndefined((Value *)genvp, vplen);
StackFrame *genfp = reinterpret_cast<StackFrame *>(genvp + vplen); StackFrame *genfp = reinterpret_cast<StackFrame *>(genvp + vplen);
/* Initialize JSGenerator. */ /* Initialize JSGenerator. */
@ -1523,7 +1520,7 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
gen->regs.rebaseFromTo(stackRegs, *genfp); gen->regs.rebaseFromTo(stackRegs, *genfp);
genfp->copyFrameAndValues<StackFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp, genfp->copyFrameAndValues<StackFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp,
stackvp, stackRegs.sp); stackvp, stackRegs.sp);
genfp->setSuspended();
obj->setPrivate(gen); obj->setPrivate(gen);
return obj; return obj;
} }

View File

@ -1433,7 +1433,7 @@ bool
js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj) js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj)
{ {
jsbytecode *pc; jsbytecode *pc;
RootedScript script(cx, cx->stack.currentScript(&pc)); RootedScript script(cx, cx->currentScript(&pc));
gc::AllocKind allocKind = NewObjectGCKind(&ObjectClass); gc::AllocKind allocKind = NewObjectGCKind(&ObjectClass);
NewObjectKind newKind = script NewObjectKind newKind = script
? UseNewTypeForInitializer(cx, script, pc, &ObjectClass) ? UseNewTypeForInitializer(cx, script, pc, &ObjectClass)
@ -1641,7 +1641,7 @@ js_InferFlags(JSContext *cx, unsigned defaultFlags)
* handle the case of cross-compartment property access. * handle the case of cross-compartment property access.
*/ */
jsbytecode *pc; jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc, ContextStack::ALLOW_CROSS_COMPARTMENT); JSScript *script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
if (!script) if (!script)
return defaultFlags; return defaultFlags;
@ -3795,7 +3795,7 @@ NativeGetInline(JSContext *cx,
{ {
jsbytecode *pc; jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc); JSScript *script = cx->currentScript(&pc);
if (script && script->hasAnalysis()) { if (script && script->hasAnalysis()) {
analyze::Bytecode *code = script->analysis()->maybeCode(pc); analyze::Bytecode *code = script->analysis()->maybeCode(pc);
if (code) if (code)
@ -3930,7 +3930,7 @@ GetPropertyHelperInline(JSContext *cx,
*/ */
if (vp.isUndefined()) { if (vp.isUndefined()) {
jsbytecode *pc = NULL; jsbytecode *pc = NULL;
RootedScript script(cx, cx->stack.currentScript(&pc)); RootedScript script(cx, cx->currentScript(&pc));
if (!pc) if (!pc)
return true; return true;
JSOp op = (JSOp) *pc; JSOp op = (JSOp) *pc;
@ -4157,7 +4157,7 @@ static bool
MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname) MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
{ {
{ {
JSScript *script = cx->stack.currentScript(NULL, ContextStack::ALLOW_CROSS_COMPARTMENT); JSScript *script = cx->currentScript(NULL, JSContext::ALLOW_CROSS_COMPARTMENT);
if (!script) if (!script)
return true; return true;
@ -4181,7 +4181,7 @@ js::ReportIfUndeclaredVarAssignment(JSContext *cx, HandleString propname)
{ {
{ {
jsbytecode *pc; jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc, ContextStack::ALLOW_CROSS_COMPARTMENT); JSScript *script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
if (!script) if (!script)
return true; return true;

View File

@ -228,8 +228,8 @@ PreprocessValue(JSContext *cx, HandleObject holder, KeyType key, MutableHandleVa
if (!keyStr) if (!keyStr)
return false; return false;
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 1, &args)) if (!args.init(1))
return false; return false;
args.setCallee(toJSON); args.setCallee(toJSON);
@ -250,8 +250,8 @@ PreprocessValue(JSContext *cx, HandleObject holder, KeyType key, MutableHandleVa
return false; return false;
} }
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 2, &args)) if (!args.init(2))
return false; return false;
args.setCallee(ObjectValue(*scx->replacer)); args.setCallee(ObjectValue(*scx->replacer));
@ -771,8 +771,8 @@ Walk(JSContext *cx, HandleObject holder, HandleId name, HandleValue reviver, Mut
if (!key) if (!key)
return false; return false;
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 2, &args)) if (!args.init(2))
return false; return false;
args.setCallee(reviver); args.setCallee(reviver);

View File

@ -91,16 +91,10 @@ enum RegExpFlag
AllFlags = 0x0f AllFlags = 0x0f
}; };
class ExecuteArgsGuard;
class InvokeFrameGuard;
class InvokeArgsGuard;
class StringBuffer; class StringBuffer;
class FrameRegs; class FrameRegs;
class StackFrame; class StackFrame;
class StackSegment;
class StackSpace;
class ContextStack;
class ScriptFrameIter; class ScriptFrameIter;
class Proxy; class Proxy;

View File

@ -2131,8 +2131,8 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
unsigned p = res->getMatches().parenCount(); unsigned p = res->getMatches().parenCount();
unsigned argc = 1 + p + 2; unsigned argc = 1 + p + 2;
InvokeArgsGuard &args = rdata.fig.args(); InvokeArgs &args = rdata.fig.args();
if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args)) if (!args.init(argc))
return false; return false;
args.setCallee(ObjectValue(*lambda)); args.setCallee(ObjectValue(*lambda));
@ -2639,7 +2639,7 @@ str_replace_flat_lambda(JSContext *cx, CallArgs outerArgs, ReplaceData &rdata, c
/* lambda(matchStr, matchStart, textstr) */ /* lambda(matchStr, matchStart, textstr) */
static const uint32_t lambdaArgc = 3; static const uint32_t lambdaArgc = 3;
if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.fig.args())) if (!rdata.fig.args().init(lambdaArgc))
return false; return false;
CallArgs &args = rdata.fig.args(); CallArgs &args = rdata.fig.args();
@ -3535,7 +3535,7 @@ js::str_fromCharCode(JSContext *cx, unsigned argc, Value *vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX); JS_ASSERT(args.length() <= ARGS_LENGTH_MAX);
if (args.length() == 1) { if (args.length() == 1) {
uint16_t code; uint16_t code;
if (!ToUint16(cx, args[0], &code)) if (!ToUint16(cx, args[0], &code))

View File

@ -1730,7 +1730,7 @@ class TypedArrayTemplate
return NewBuiltinClassInstance(cx, fastClass(), SingletonObject); return NewBuiltinClassInstance(cx, fastClass(), SingletonObject);
jsbytecode *pc; jsbytecode *pc;
RootedScript script(cx, cx->stack.currentScript(&pc)); RootedScript script(cx, cx->currentScript(&pc));
NewObjectKind newKind = script NewObjectKind newKind = script
? UseNewTypeForInitializer(cx, script, pc, fastClass()) ? UseNewTypeForInitializer(cx, script, pc, fastClass())
: GenericObject; : GenericObject;
@ -2163,19 +2163,19 @@ class TypedArrayTemplate
if (!FindProto(cx, fastClass(), &proto)) if (!FindProto(cx, fastClass(), &proto))
return NULL; return NULL;
InvokeArgsGuard ag; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 3, &ag)) if (!args.init(3))
return NULL; return NULL;
ag.setCallee(cx->compartment()->maybeGlobal()->createArrayFromBuffer<NativeType>()); args.setCallee(cx->compartment()->maybeGlobal()->createArrayFromBuffer<NativeType>());
ag.setThis(ObjectValue(*bufobj)); args.setThis(ObjectValue(*bufobj));
ag[0] = NumberValue(byteOffset); args[0] = NumberValue(byteOffset);
ag[1] = Int32Value(lengthInt); args[1] = Int32Value(lengthInt);
ag[2] = ObjectValue(*proto); args[2] = ObjectValue(*proto);
if (!Invoke(cx, ag)) if (!Invoke(cx, args))
return NULL; return NULL;
return &ag.rval().toObject(); return &args.rval().toObject();
} }
} }
@ -2770,16 +2770,16 @@ DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
if (!proto) if (!proto)
return false; return false;
InvokeArgsGuard ag; InvokeArgs args2(cx);
if (!cx->stack.pushInvokeArgs(cx, args.length() + 1, &ag)) if (!args2.init(args.length() + 1))
return false; return false;
ag.setCallee(global->createDataViewForThis()); args2.setCallee(global->createDataViewForThis());
ag.setThis(ObjectValue(*bufobj)); args2.setThis(ObjectValue(*bufobj));
PodCopy(ag.array(), args.array(), args.length()); PodCopy(args2.array(), args.array(), args.length());
ag[argc] = ObjectValue(*proto); args2[argc] = ObjectValue(*proto);
if (!Invoke(cx, ag)) if (!Invoke(cx, args2))
return false; return false;
args.rval().set(ag.rval()); args.rval().set(args2.rval());
return true; return true;
} }

View File

@ -212,7 +212,7 @@ DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto)
if (!proto && byteLength >= TypedArray::SINGLETON_TYPE_BYTE_LENGTH) if (!proto && byteLength >= TypedArray::SINGLETON_TYPE_BYTE_LENGTH)
return SingletonObject; return SingletonObject;
jsbytecode *pc; jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc); JSScript *script = cx->currentScript(&pc);
if (!script) if (!script)
return GenericObject; return GenericObject;
return types::UseNewTypeForInitializer(cx, script, pc, &DataViewObject::class_); return types::UseNewTypeForInitializer(cx, script, pc, &DataViewObject::class_);
@ -243,7 +243,7 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
JS_ASSERT(obj->hasSingletonType()); JS_ASSERT(obj->hasSingletonType());
} else { } else {
jsbytecode *pc; jsbytecode *pc;
RootedScript script(cx, cx->stack.currentScript(&pc)); RootedScript script(cx, cx->currentScript(&pc));
if (script) { if (script) {
if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
return NULL; return NULL;

View File

@ -204,15 +204,6 @@ UnsignedPtrDiff(const void *bigger, const void *smaller)
return size_t(bigger) - size_t(smaller); return size_t(bigger) - size_t(smaller);
} }
/*
* Ordinarily, a function taking a JSContext* 'cx' parameter reports errors on
* the context. In some cases, functions optionally report and indicate this by
* taking a nullable 'maybecx' parameter. In some cases, though, a function
* always needs a 'cx', but optionally reports. This option is presented by the
* MaybeReportError.
*/
enum MaybeReportError { REPORT_ERROR = true, DONT_REPORT_ERROR = false };
/*****************************************************************************/ /*****************************************************************************/
/* A bit array is an array of bits represented by an array of words (size_t). */ /* A bit array is an array of bits represented by an array of words (size_t). */

View File

@ -479,8 +479,8 @@ CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, Native
RootedObject wrapped(cx, wrappedObject(wrapper)); RootedObject wrapped(cx, wrappedObject(wrapper));
{ {
AutoCompartment call(cx, wrapped); AutoCompartment call(cx, wrapped);
InvokeArgsGuard dstArgs; InvokeArgs dstArgs(cx);
if (!cx->stack.pushInvokeArgs(cx, srcArgs.length(), &dstArgs)) if (!dstArgs.init(srcArgs.length()))
return false; return false;
Value *src = srcArgs.base(); Value *src = srcArgs.base();
@ -513,7 +513,6 @@ CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, Native
return false; return false;
srcArgs.rval().set(dstArgs.rval()); srcArgs.rval().set(dstArgs.rval());
dstArgs.pop();
} }
return cx->compartment()->wrap(cx, srcArgs.rval()); return cx->compartment()->wrap(cx, srcArgs.rval());
} }

View File

@ -3520,7 +3520,7 @@ EnableStackWalkingAssertion(JSContext *cx, unsigned argc, jsval *vp)
static JSBool static JSBool
GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp) GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp)
{ {
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(StackSpace::ARGS_LENGTH_MAX)); JS_SET_RVAL(cx, vp, INT_TO_JSVAL(ARGS_LENGTH_MAX));
return true; return true;
} }

View File

@ -17,7 +17,7 @@ inline uint32_t
ArgumentsObject::initialLength() const ArgumentsObject::initialLength() const
{ {
uint32_t argc = uint32_t(getFixedSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT; uint32_t argc = uint32_t(getFixedSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT;
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX); JS_ASSERT(argc <= ARGS_LENGTH_MAX);
return argc; return argc;
} }

View File

@ -4884,13 +4884,13 @@ ApplyOrCall(JSContext *cx, unsigned argc, Value *vp, ApplyOrCallMode mode)
RootedObject argsobj(cx, &args[1].toObject()); RootedObject argsobj(cx, &args[1].toObject());
if (!GetLengthProperty(cx, argsobj, &callArgc)) if (!GetLengthProperty(cx, argsobj, &callArgc))
return false; return false;
callArgc = unsigned(Min(callArgc, StackSpace::ARGS_LENGTH_MAX)); callArgc = unsigned(Min(callArgc, ARGS_LENGTH_MAX));
if (!argv.growBy(callArgc) || !GetElements(cx, argsobj, callArgc, argv.begin())) if (!argv.growBy(callArgc) || !GetElements(cx, argsobj, callArgc, argv.begin()))
return false; return false;
callArgv = argv.begin(); callArgv = argv.begin();
} }
} else { } else {
callArgc = argc > 0 ? unsigned(Min(argc - 1, StackSpace::ARGS_LENGTH_MAX)) : 0; callArgc = argc > 0 ? unsigned(Min(argc - 1, ARGS_LENGTH_MAX)) : 0;
callArgv = args.array() + 1; callArgv = args.array() + 1;
} }

View File

@ -146,8 +146,8 @@ ExecuteSequentially(JSContext *cx, HandleValue funVal, bool *complete)
FastInvokeGuard fig(cx, funVal); FastInvokeGuard fig(cx, funVal);
bool allComplete = true; bool allComplete = true;
for (uint32_t i = 0; i < numSlices; i++) { for (uint32_t i = 0; i < numSlices; i++) {
InvokeArgsGuard &args = fig.args(); InvokeArgs &args = fig.args();
if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &args)) if (!args.init(3))
return false; return false;
args.setCallee(funVal); args.setCallee(funVal);
args.setThis(UndefinedValue()); args.setThis(UndefinedValue());
@ -1947,7 +1947,7 @@ class ParallelSpewer
if (cx) { if (cx) {
jsbytecode *pc; jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc); JSScript *script = cx->currentScript(&pc);
if (script && pc) { if (script && pc) {
NonBuiltinScriptFrameIter iter(cx); NonBuiltinScriptFrameIter iter(cx);
if (iter.done()) { if (iter.done()) {

View File

@ -268,7 +268,7 @@ FetchNameNoGC(JSObject *pobj, Shape *shape, MutableHandleValue vp)
inline bool inline bool
GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp) GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp)
{ {
RootedPropertyName name(cx, cx->stack.currentScript()->getName(pc)); RootedPropertyName name(cx, cx->currentScript()->getName(pc));
return cx->global()->getIntrinsicValue(cx, name, vp); return cx->global()->getIntrinsicValue(cx, name, vp);
} }
@ -279,45 +279,6 @@ SetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleVal
return cx->global()->setIntrinsicValue(cx, name, val); return cx->global()->setIntrinsicValue(cx, name, val);
} }
inline bool
NameOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp)
{
JSObject *obj = cx->stack.currentScriptedScopeChain();
PropertyName *name = cx->stack.currentScript()->getName(pc);
/*
* Skip along the scope chain to the enclosing global object. This is
* used for GNAME opcodes where the bytecode emitter has determined a
* name access must be on the global. It also insulates us from bugs
* in the emitter: type inference will assume that GNAME opcodes are
* accessing the global object, and the inferred behavior should match
* the actual behavior even if the id could be found on the scope chain
* before the global object.
*/
if (IsGlobalOp(JSOp(*pc)))
obj = &obj->global();
Shape *shape = NULL;
JSObject *scope = NULL, *pobj = NULL;
if (LookupNameNoGC(cx, name, obj, &scope, &pobj, &shape)) {
if (FetchNameNoGC(pobj, shape, vp))
return true;
}
RootedObject objRoot(cx, obj), scopeRoot(cx), pobjRoot(cx);
RootedPropertyName nameRoot(cx, name);
RootedShape shapeRoot(cx);
if (!LookupName(cx, nameRoot, objRoot, &scopeRoot, &pobjRoot, &shapeRoot))
return false;
/* Kludge to allow (typeof foo == "undefined") tests. */
JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
if (op2 == JSOP_TYPEOF)
return FetchName<true>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp);
return FetchName<false>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp);
}
inline bool inline bool
SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope, SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope,
HandleValue val) HandleValue val)
@ -955,7 +916,7 @@ ReportIfNotFunction(JSContext *cx, const Value &v, MaybeConstruct construct = NO
*/ */
class FastInvokeGuard class FastInvokeGuard
{ {
InvokeArgsGuard args_; InvokeArgs args_;
RootedFunction fun_; RootedFunction fun_;
RootedScript script_; RootedScript script_;
#ifdef JS_ION #ifdef JS_ION
@ -967,7 +928,8 @@ class FastInvokeGuard
public: public:
FastInvokeGuard(JSContext *cx, const Value &fval) FastInvokeGuard(JSContext *cx, const Value &fval)
: fun_(cx) : args_(cx)
, fun_(cx)
, script_(cx) , script_(cx)
#ifdef JS_ION #ifdef JS_ION
, useIon_(ion::IsEnabled(cx)) , useIon_(ion::IsEnabled(cx))
@ -985,7 +947,7 @@ class FastInvokeGuard
} }
} }
InvokeArgsGuard &args() { InvokeArgs &args() {
return args_; return args_;
} }

View File

@ -229,8 +229,8 @@ js::OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval_, MutableHandle
static JSBool static JSBool
NoSuchMethod(JSContext *cx, unsigned argc, Value *vp) NoSuchMethod(JSContext *cx, unsigned argc, Value *vp)
{ {
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 2, &args)) if (!args.init(2))
return JS_FALSE; return JS_FALSE;
JS_ASSERT(vp[0].isObject()); JS_ASSERT(vp[0].isObject());
@ -298,6 +298,45 @@ GetPropertyOperation(JSContext *cx, StackFrame *fp, HandleScript script, jsbytec
return true; return true;
} }
static inline bool
NameOperation(JSContext *cx, StackFrame *fp, jsbytecode *pc, MutableHandleValue vp)
{
JSObject *obj = fp->scopeChain();
PropertyName *name = fp->script()->getName(pc);
/*
* Skip along the scope chain to the enclosing global object. This is
* used for GNAME opcodes where the bytecode emitter has determined a
* name access must be on the global. It also insulates us from bugs
* in the emitter: type inference will assume that GNAME opcodes are
* accessing the global object, and the inferred behavior should match
* the actual behavior even if the id could be found on the scope chain
* before the global object.
*/
if (IsGlobalOp(JSOp(*pc)))
obj = &obj->global();
Shape *shape = NULL;
JSObject *scope = NULL, *pobj = NULL;
if (LookupNameNoGC(cx, name, obj, &scope, &pobj, &shape)) {
if (FetchNameNoGC(pobj, shape, vp))
return true;
}
RootedObject objRoot(cx, obj), scopeRoot(cx), pobjRoot(cx);
RootedPropertyName nameRoot(cx, name);
RootedShape shapeRoot(cx);
if (!LookupName(cx, nameRoot, objRoot, &scopeRoot, &pobjRoot, &shapeRoot))
return false;
/* Kludge to allow (typeof foo == "undefined") tests. */
JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
if (op2 == JSOP_TYPEOF)
return FetchName<true>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp);
return FetchName<false>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp);
}
inline bool inline bool
SetPropertyOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lval, SetPropertyOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lval,
HandleValue rval) HandleValue rval)
@ -350,28 +389,16 @@ static JS_NEVER_INLINE bool
Interpret(JSContext *cx, RunState &state); Interpret(JSContext *cx, RunState &state);
StackFrame * StackFrame *
InvokeState::pushInterpreterFrame(JSContext *cx) InvokeState::pushInterpreterFrame(JSContext *cx, FrameGuard *fg)
{ {
ifg_.construct(); return cx->runtime()->interpreterStack().pushInvokeFrame(cx, args_, initial_, fg);
if (!cx->stack.pushInvokeFrame(cx, args_, initial_, ifg_.addr()))
return NULL;
return ifg_.ref().fp();
} }
StackFrame * StackFrame *
ExecuteState::pushInterpreterFrame(JSContext *cx) ExecuteState::pushInterpreterFrame(JSContext *cx, FrameGuard *fg)
{ {
efg_.construct(); return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, scopeChain_,
type_, evalInFrame_, fg);
if (!cx->stack.pushExecuteFrame(cx, script_, thisv_, scopeChain_, type_, evalInFrame_,
efg_.addr()))
{
return NULL;
}
return efg_.ref().fp();
} }
bool bool
@ -420,7 +447,7 @@ js::RunScript(JSContext *cx, RunState &state)
bool bool
js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct) js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
{ {
JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX); JS_ASSERT(args.length() <= ARGS_LENGTH_MAX);
JS_ASSERT(!cx->compartment()->activeAnalysis); JS_ASSERT(!cx->compartment()->activeAnalysis);
/* We should never enter a new script while cx->iterValue is live. */ /* We should never enter a new script while cx->iterValue is live. */
@ -480,8 +507,8 @@ bool
js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv, js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv,
Value *rval) Value *rval)
{ {
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, argc, &args)) if (!args.init(argc))
return false; return false;
args.setCallee(fval); args.setCallee(fval);
@ -547,8 +574,8 @@ js::InvokeConstructor(JSContext *cx, CallArgs args)
bool bool
js::InvokeConstructor(JSContext *cx, const Value &fval, unsigned argc, Value *argv, Value *rval) js::InvokeConstructor(JSContext *cx, const Value &fval, unsigned argc, Value *argv, Value *rval)
{ {
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, argc, &args)) if (!args.init(argc))
return false; return false;
args.setCallee(fval); args.setCallee(fval);
@ -824,8 +851,8 @@ EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stack
void void
js::UnwindScope(JSContext *cx, AbstractFramePtr frame, uint32_t stackDepth) js::UnwindScope(JSContext *cx, AbstractFramePtr frame, uint32_t stackDepth)
{ {
JS_ASSERT_IF(frame.isStackFrame(), cx->stack.fp() == frame.asStackFrame()); JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->interpreterFrame());
JS_ASSERT_IF(frame.isStackFrame(), stackDepth <= cx->stack.regs().stackDepth()); JS_ASSERT_IF(frame.isStackFrame(), stackDepth <= cx->interpreterRegs().stackDepth());
for (ScopeIter si(frame, cx); !si.done(); ++si) { for (ScopeIter si(frame, cx); !si.done(); ++si) {
switch (si.type()) { switch (si.type()) {
@ -1018,6 +1045,26 @@ js::IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
return js_IteratorNext(cx, iterobj, rval); return js_IteratorNext(cx, iterobj, rval);
} }
FrameGuard::FrameGuard(RunState &state, FrameRegs &regs)
: state_(state),
regs_(regs),
stack_(NULL),
fp_(NULL)
{ }
FrameGuard::~FrameGuard()
{
if (state_.isGenerator()) {
JSGenerator *gen = state_.asGenerator()->gen();
gen->fp->unsetPushedSPSFrame();
gen->regs = regs_;
return;
}
if (fp_)
stack_->releaseFrame(fp_);
}
static JS_NEVER_INLINE bool static JS_NEVER_INLINE bool
Interpret(JSContext *cx, RunState &state) Interpret(JSContext *cx, RunState &state)
{ {
@ -1094,16 +1141,21 @@ Interpret(JSContext *cx, RunState &state)
interrupts.enable(); \ interrupts.enable(); \
JS_END_MACRO JS_END_MACRO
StackFrame *entryFrame = state.pushInterpreterFrame(cx); FrameRegs regs;
FrameGuard fg(state, regs);
StackFrame *entryFrame = state.pushInterpreterFrame(cx, &fg);
if (!entryFrame) if (!entryFrame)
return false; return false;
JS_ASSERT_IF(!state.isGenerator(), cx->stack.regs().pc == state.script()->code); if (!state.isGenerator()) {
JS_ASSERT_IF(entryFrame->isEvalFrame(), state.script()->isActiveEval); regs.prepareToRun(*entryFrame, state.script());
JS_ASSERT(regs.pc == state.script()->code);
} else {
regs = state.asGenerator()->gen()->regs;
}
/* Repoint cx->regs to a local variable for faster access. */ JS_ASSERT_IF(entryFrame->isEvalFrame(), state.script()->isActiveEval);
FrameRegs regs = cx->stack.regs();
PreserveRegsGuard interpGuard(cx, regs);
InterpreterActivation activation(cx, entryFrame, regs); InterpreterActivation activation(cx, entryFrame, regs);
@ -1453,8 +1505,7 @@ BEGIN_CASE(JSOP_STOP)
jit_return_pop_frame: jit_return_pop_frame:
#endif #endif
activation.popFrame(regs.fp()); activation.popInlineFrame(regs.fp());
cx->stack.popInlineFrame(regs);
SET_SCRIPT(regs.fp()->script()); SET_SCRIPT(regs.fp()->script());
#if defined(JS_ION) #if defined(JS_ION)
@ -1983,7 +2034,7 @@ BEGIN_CASE(JSOP_DELNAME)
name = script->getName(regs.pc); name = script->getName(regs.pc);
RootedObject &scopeObj = rootObject0; RootedObject &scopeObj = rootObject0;
scopeObj = cx->stack.currentScriptedScopeChain(); scopeObj = regs.fp()->scopeChain();
PUSH_BOOLEAN(true); PUSH_BOOLEAN(true);
MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]); MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
@ -2292,11 +2343,9 @@ BEGIN_CASE(JSOP_FUNCALL)
TypeMonitorCall(cx, args, construct); TypeMonitorCall(cx, args, construct);
funScript = fun->nonLazyScript(); funScript = fun->nonLazyScript();
if (!cx->stack.pushInlineFrame(cx, regs, args, fun, funScript, initial)) if (!activation.pushInlineFrame(args, funScript, initial))
goto error; goto error;
activation.pushFrame(regs.fp());
if (newType) if (newType)
regs.fp()->setUseNewType(); regs.fp()->setUseNewType();
@ -2337,7 +2386,7 @@ BEGIN_CASE(JSOP_IMPLICITTHIS)
name = script->getName(regs.pc); name = script->getName(regs.pc);
RootedObject &scopeObj = rootObject0; RootedObject &scopeObj = rootObject0;
scopeObj = cx->stack.currentScriptedScopeChain(); scopeObj = regs.fp()->scopeChain();
RootedObject &scope = rootObject1; RootedObject &scope = rootObject1;
if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope)) if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope))
@ -2357,7 +2406,7 @@ BEGIN_CASE(JSOP_CALLNAME)
{ {
RootedValue &rval = rootValue0; RootedValue &rval = rootValue0;
if (!NameOperation(cx, regs.pc, &rval)) if (!NameOperation(cx, regs.fp(), regs.pc, &rval))
goto error; goto error;
PUSH_COPY(rval); PUSH_COPY(rval);
@ -3060,7 +3109,6 @@ END_CASE(JSOP_ARRAYPUSH)
} /* for (;;) */ } /* for (;;) */
error: error:
JS_ASSERT(&cx->stack.regs() == &regs);
JS_ASSERT(uint32_t(regs.pc - script->code) < script->length); JS_ASSERT(uint32_t(regs.pc - script->code) < script->length);
if (cx->isExceptionPending()) { if (cx->isExceptionPending()) {

View File

@ -206,7 +206,7 @@ class RunState
JSScript *script() const { return script_; } JSScript *script() const { return script_; }
virtual StackFrame *pushInterpreterFrame(JSContext *cx) = 0; virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg) = 0;
virtual void setReturnValue(Value v) = 0; virtual void setReturnValue(Value v) = 0;
private: private:
@ -220,7 +220,6 @@ class RunState
// Eval or global script. // Eval or global script.
class ExecuteState : public RunState class ExecuteState : public RunState
{ {
mozilla::Maybe<ExecuteFrameGuard> efg_;
ExecuteType type_; ExecuteType type_;
RootedValue thisv_; RootedValue thisv_;
@ -244,7 +243,7 @@ class ExecuteState : public RunState
JSObject *scopeChain() const { return scopeChain_; } JSObject *scopeChain() const { return scopeChain_; }
ExecuteType type() const { return type_; } ExecuteType type() const { return type_; }
virtual StackFrame *pushInterpreterFrame(JSContext *cx); virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg);
virtual void setReturnValue(Value v) { virtual void setReturnValue(Value v) {
if (result_) if (result_)
@ -255,7 +254,6 @@ class ExecuteState : public RunState
// Data to invoke a function. // Data to invoke a function.
class InvokeState : public RunState class InvokeState : public RunState
{ {
mozilla::Maybe<InvokeFrameGuard> ifg_;
CallArgs &args_; CallArgs &args_;
InitialFrameFlags initial_; InitialFrameFlags initial_;
bool useNewType_; bool useNewType_;
@ -274,7 +272,7 @@ class InvokeState : public RunState
bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); } bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); }
CallArgs &args() const { return args_; } CallArgs &args() const { return args_; }
virtual StackFrame *pushInterpreterFrame(JSContext *cx); virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg);
virtual void setReturnValue(Value v) { virtual void setReturnValue(Value v) {
args_.rval().set(v); args_.rval().set(v);
@ -284,7 +282,6 @@ class InvokeState : public RunState
// Generator script. // Generator script.
class GeneratorState : public RunState class GeneratorState : public RunState
{ {
mozilla::Maybe<GeneratorFrameGuard> gfg_;
JSContext *cx_; JSContext *cx_;
JSGenerator *gen_; JSGenerator *gen_;
JSGeneratorState futureState_; JSGeneratorState futureState_;
@ -294,8 +291,10 @@ class GeneratorState : public RunState
GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState); GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState);
~GeneratorState(); ~GeneratorState();
virtual StackFrame *pushInterpreterFrame(JSContext *cx); virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg);
virtual void setReturnValue(Value) { } virtual void setReturnValue(Value) { }
JSGenerator *gen() const { return gen_; }
}; };
extern bool extern bool

View File

@ -685,8 +685,8 @@ js::GetProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> rece
return true; return true;
} }
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 0, &args)) if (!args.init(0))
return false; return false;
args.setCallee(get); args.setCallee(get);
@ -751,8 +751,8 @@ js::GetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> recei
return true; return true;
} }
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 0, &args)) if (!args.init(0))
return false; return false;
/* Push getter, receiver, and no args. */ /* Push getter, receiver, and no args. */
@ -986,8 +986,8 @@ js::SetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> recei
return true; return true;
} }
InvokeArgsGuard args; InvokeArgs args(cx);
if (!cx->stack.pushInvokeArgs(cx, 1, &args)) if (!args.init(1))
return false; return false;
/* Push set, receiver, and v as the sole argument. */ /* Push set, receiver, and v as the sole argument. */

View File

@ -71,41 +71,21 @@ StackFrame::compartment() const
} }
inline void inline void
StackFrame::initPrev(JSContext *cx) StackFrame::initCallFrame(JSContext *cx, StackFrame *prev, jsbytecode *prevpc, Value *prevsp, JSFunction &callee,
{ JSScript *script, Value *argv, uint32_t nactual, StackFrame::Flags flagsArg)
JS_ASSERT(flags_ & HAS_PREVPC);
if (FrameRegs *regs = cx->stack.maybeRegs()) {
prev_ = regs->fp();
prevpc_ = regs->pc;
JS_ASSERT(uint32_t(prevpc_ - prev_->script()->code) < prev_->script()->length);
} else {
prev_ = NULL;
#ifdef DEBUG
prevpc_ = (jsbytecode *)0xbadc;
#endif
}
}
inline void
StackFrame::resetGeneratorPrev(JSContext *cx)
{
flags_ |= HAS_PREVPC;
initPrev(cx);
}
inline void
StackFrame::initCallFrame(JSContext *cx, JSFunction &callee,
JSScript *script, uint32_t nactual, StackFrame::Flags flagsArg)
{ {
JS_ASSERT((flagsArg & ~CONSTRUCTING) == 0); JS_ASSERT((flagsArg & ~CONSTRUCTING) == 0);
JS_ASSERT(callee.nonLazyScript() == script); JS_ASSERT(callee.nonLazyScript() == script);
/* Initialize stack frame members. */ /* Initialize stack frame members. */
flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg; flags_ = FUNCTION | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
argv_ = argv;
exec.fun = &callee; exec.fun = &callee;
u.nactual = nactual; u.nactual = nactual;
scopeChain_ = callee.environment(); scopeChain_ = callee.environment();
initPrev(cx); prev_ = prev;
prevpc_ = prevpc;
prevsp_ = prevsp;
blockChain_= NULL; blockChain_= NULL;
JS_ASSERT(!hasBlockChain()); JS_ASSERT(!hasBlockChain());
JS_ASSERT(!hasHookData()); JS_ASSERT(!hasHookData());
@ -234,144 +214,100 @@ StackFrame::callObj() const
/*****************************************************************************/ /*****************************************************************************/
STATIC_POSTCONDITION(!return || ubound(from) >= nvals) inline void
JS_ALWAYS_INLINE bool InterpreterStack::purge(JSRuntime *rt)
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
{ {
assertInvariants(); rt->freeLifoAlloc.transferUnusedFrom(&allocator_);
JS_ASSERT(from >= firstUnused());
#ifdef XP_WIN
JS_ASSERT(from <= commitEnd_);
#endif
if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
return ensureSpaceSlow(cx, report, from, nvals);
return true;
} }
/*****************************************************************************/ uint8_t *
InterpreterStack::allocateFrame(JSContext *cx, size_t size)
JS_ALWAYS_INLINE StackFrame *
ContextStack::getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
JSFunction *fun, HandleScript script, StackFrame::Flags *flags) const
{ {
JS_ASSERT(fun->nonLazyScript() == script); if (JS_UNLIKELY(frameCount_ >= MAX_FRAMES)) {
unsigned nformal = fun->nargs; js_ReportOverRecursed(cx);
Value *firstUnused = args.end();
JS_ASSERT(firstUnused == space().firstUnused());
unsigned nvals = VALUES_PER_STACK_FRAME + script->nslots;
if (args.length() >= nformal) {
if (!space().ensureSpace(cx, report, firstUnused, nvals))
return NULL; return NULL;
return reinterpret_cast<StackFrame *>(firstUnused);
} }
/* Pad any missing arguments with |undefined|. */ uint8_t *buffer = reinterpret_cast<uint8_t *>(allocator_.alloc(size));
JS_ASSERT(args.length() < nformal); if (!buffer)
unsigned nmissing = nformal - args.length();
if (!space().ensureSpace(cx, report, firstUnused, nmissing + nvals))
return NULL; return NULL;
SetValueRangeToUndefined(firstUnused, nmissing);
return reinterpret_cast<StackFrame *>(firstUnused + nmissing); frameCount_++;
return buffer;
}
JS_ALWAYS_INLINE StackFrame *
InterpreterStack::getCallFrame(JSContext *cx, const CallArgs &args, HandleScript script,
StackFrame::Flags *flags, Value **pargv)
{
JSFunction *fun = &args.callee().as<JSFunction>();
JS_ASSERT(fun->nonLazyScript() == script);
unsigned nformal = fun->nargs;
unsigned nvals = script->nslots;
if (args.length() >= nformal) {
*pargv = args.array();
uint8_t *buffer = allocateFrame(cx, sizeof(StackFrame) + nvals * sizeof(Value));
return reinterpret_cast<StackFrame *>(buffer);
}
// Pad any missing arguments with |undefined|.
JS_ASSERT(args.length() < nformal);
nvals += nformal + 2; // Include callee, |this|.
uint8_t *buffer = allocateFrame(cx, sizeof(StackFrame) + nvals * sizeof(Value));
if (!buffer)
return NULL;
Value *argv = reinterpret_cast<Value *>(buffer);
unsigned nmissing = nformal - args.length();
mozilla::PodCopy(argv, args.base(), 2 + args.length());
SetValueRangeToUndefined(argv + 2 + args.length(), nmissing);
*pargv = argv + 2;
return reinterpret_cast<StackFrame *>(argv + 2 + nformal);
} }
JS_ALWAYS_INLINE bool JS_ALWAYS_INLINE bool
ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args, InterpreterStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
HandleFunction callee, HandleScript script, HandleScript script, InitialFrameFlags initial)
InitialFrameFlags initial, MaybeReportError report)
{ {
JS_ASSERT(onTop()); JSFunction *callee = &args.callee().as<JSFunction>();
JS_ASSERT(regs.sp == args.end()); JS_ASSERT(regs.sp == args.end());
/* Cannot assert callee == args.callee() since this is called from LeaveTree. */
JS_ASSERT(callee->nonLazyScript() == script); JS_ASSERT(callee->nonLazyScript() == script);
StackFrame *prev = regs.fp();
jsbytecode *prevpc = regs.pc;
Value *prevsp = regs.sp;
JS_ASSERT(prev);
LifoAlloc::Mark mark = allocator_.mark();
StackFrame::Flags flags = ToFrameFlags(initial); StackFrame::Flags flags = ToFrameFlags(initial);
StackFrame *fp = getCallFrame(cx, report, args, callee, script, &flags); Value *argv;
StackFrame *fp = getCallFrame(cx, args, script, &flags, &argv);
if (!fp) if (!fp)
return false; return false;
/* Initialize frame, locals, regs. */ fp->mark_ = mark;
fp->initCallFrame(cx, *callee, script, args.length(), flags);
/* Initialize frame, locals, regs. */
fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, args.length(), flags);
/*
* N.B. regs may differ from the active registers, if the parent is about
* to repoint the active registers to regs. See UncachedInlineCall.
*/
regs.prepareToRun(*fp, script); regs.prepareToRun(*fp, script);
return true; return true;
} }
JS_ALWAYS_INLINE bool
ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
HandleFunction callee, HandleScript script,
InitialFrameFlags initial, Value **stackLimit)
{
if (!pushInlineFrame(cx, regs, args, callee, script, initial))
return false;
*stackLimit = space().conservativeEnd_;
return true;
}
JS_ALWAYS_INLINE void JS_ALWAYS_INLINE void
ContextStack::popInlineFrame(FrameRegs &regs) InterpreterStack::popInlineFrame(FrameRegs &regs)
{ {
JS_ASSERT(onTop());
JS_ASSERT(&regs == &seg_->regs());
StackFrame *fp = regs.fp(); StackFrame *fp = regs.fp();
Value *newsp = fp->argv() - 1; regs.popInlineFrame();
JS_ASSERT(newsp >= fp->prev()->base()); regs.sp[-1] = fp->returnValue();
releaseFrame(fp);
newsp[-1] = fp->returnValue(); JS_ASSERT(regs.fp());
regs.popFrame(newsp);
}
inline JSScript *
ContextStack::currentScript(jsbytecode **ppc,
MaybeAllowCrossCompartment allowCrossCompartment) const
{
if (ppc)
*ppc = NULL;
Activation *act = cx_->mainThread().activation();
while (act && (act->cx() != cx_ || !act->isActive()))
act = act->prev();
if (!act)
return NULL;
JS_ASSERT(act->cx() == cx_);
#ifdef JS_ION
if (act->isJit()) {
JSScript *script = NULL;
ion::GetPcScript(cx_, &script, ppc);
if (!allowCrossCompartment && script->compartment() != cx_->compartment())
return NULL;
return script;
}
#endif
JS_ASSERT(act->isInterpreter());
StackFrame *fp = act->asInterpreter()->current();
JS_ASSERT(!fp->runningInJit());
JSScript *script = fp->script();
if (!allowCrossCompartment && script->compartment() != cx_->compartment())
return NULL;
if (ppc)
*ppc = fp->pcQuadratic(*this);
return script;
}
inline HandleObject
ContextStack::currentScriptedScopeChain() const
{
return fp()->scopeChain();
} }
template <class Op> template <class Op>
@ -892,6 +828,7 @@ Activation::~Activation()
{ {
JS_ASSERT(cx_->mainThread().activation_ == this); JS_ASSERT(cx_->mainThread().activation_ == this);
cx_->mainThread().activation_ = prev_; cx_->mainThread().activation_ = prev_;
cx_->maybeMigrateVersionOverride();
} }
InterpreterActivation::InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs) InterpreterActivation::InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs)
@ -899,11 +836,43 @@ InterpreterActivation::InterpreterActivation(JSContext *cx, StackFrame *entry, F
entry_(entry), entry_(entry),
current_(entry), current_(entry),
regs_(regs) regs_(regs)
#ifdef DEBUG
, oldFrameCount_(cx_->runtime()->interpreterStack().frameCount_)
#endif
{} {}
// Define destructor explicitly to silence GCC used-but-never-defined warning.
InterpreterActivation::~InterpreterActivation() InterpreterActivation::~InterpreterActivation()
{} {
// Pop all inline frames.
while (current_ != entry_)
popInlineFrame(current_);
JS_ASSERT(oldFrameCount_ == cx_->runtime()->interpreterStack().frameCount_);
JS_ASSERT_IF(oldFrameCount_ == 0, cx_->runtime()->interpreterStack().allocator_.used() == 0);
}
inline bool
InterpreterActivation::pushInlineFrame(const CallArgs &args, HandleScript script,
InitialFrameFlags initial)
{
if (!cx_->runtime()->interpreterStack().pushInlineFrame(cx_, regs_, args, script, initial))
return false;
JS_ASSERT(regs_.fp()->script()->compartment() == compartment_);
current_ = regs_.fp();
return true;
}
inline void
InterpreterActivation::popInlineFrame(StackFrame *frame)
{
JS_ASSERT(current_ == frame);
JS_ASSERT(current_ != entry_);
current_ = frame->prev();
JS_ASSERT(current_);
cx_->runtime()->interpreterStack().popInlineFrame(regs_);
}
} /* namespace js */ } /* namespace js */

View File

@ -21,24 +21,6 @@
#include "vm/Stack-inl.h" #include "vm/Stack-inl.h"
#include "vm/Probes-inl.h" #include "vm/Probes-inl.h"
/* Includes to get to low-level memory-mapping functionality. */
#ifdef XP_WIN
# include "jswin.h"
#elif defined(XP_OS2)
# define INCL_DOSMEMMGR
# include <os2.h>
#else
# include <unistd.h>
# include <sys/mman.h>
# if !defined(MAP_ANONYMOUS)
# if defined(MAP_ANON)
# define MAP_ANONYMOUS MAP_ANON
# else
# define MAP_ANONYMOUS 0
# endif
# endif
#endif
using namespace js; using namespace js;
using mozilla::PodCopy; using mozilla::PodCopy;
@ -46,27 +28,44 @@ using mozilla::PodCopy;
/*****************************************************************************/ /*****************************************************************************/
void void
StackFrame::initExecuteFrame(JSScript *script, StackFrame *prevLink, AbstractFramePtr prev, StackFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr evalInFramePrev,
FrameRegs *regs, const Value &thisv, JSObject &scopeChain, const Value &thisv, JSObject &scopeChain, ExecuteType type)
ExecuteType type)
{ {
/* /*
* See encoding of ExecuteType. When GLOBAL isn't set, we are executing a * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
* script in the context of another frame and the frame type is determined * script in the context of another frame and the frame type is determined
* by the context. * by the context.
*/ */
flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | HAS_PREVPC; flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN;
if (!(flags_ & GLOBAL)) {
JS_ASSERT(prev.isFunctionFrame() || prev.isGlobalFrame()); JSObject *callee = NULL;
flags_ |= prev.isFunctionFrame() ? FUNCTION : GLOBAL; if (!(flags_ & (GLOBAL))) {
if (evalInFramePrev) {
JS_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame());
if (evalInFramePrev.isFunctionFrame()) {
callee = evalInFramePrev.callee();
flags_ |= FUNCTION;
} else {
flags_ |= GLOBAL;
}
} else {
ScriptFrameIter iter(cx);
JS_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame());
if (iter.isFunctionFrame()) {
callee = iter.callee();
flags_ |= FUNCTION;
} else {
flags_ |= GLOBAL;
}
}
} }
Value *dstvp = (Value *)this - 2; Value *dstvp = (Value *)this - 2;
dstvp[1] = thisv; dstvp[1] = thisv;
if (isFunctionFrame()) { if (isFunctionFrame()) {
dstvp[0] = prev.calleev(); dstvp[0] = ObjectValue(*callee);
exec.fun = prev.fun(); exec.fun = &callee->as<JSFunction>();
u.evalScript = script; u.evalScript = script;
} else { } else {
JS_ASSERT(isGlobalFrame()); JS_ASSERT(isGlobalFrame());
@ -78,13 +77,13 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prevLink, AbstractFra
} }
scopeChain_ = &scopeChain; scopeChain_ = &scopeChain;
prev_ = prevLink; prev_ = NULL;
prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad; prevpc_ = NULL;
prevsp_ = NULL;
blockChain_ = NULL; blockChain_ = NULL;
/* Set evalInFramePrev_ if this is an eval-in-frame. */ JS_ASSERT_IF(evalInFramePrev, isDebuggerFrame());
JS_ASSERT_IF(isDebuggerFrame(), isEvalFrame()); evalInFramePrev_ = evalInFramePrev;
evalInFramePrev_ = isDebuggerFrame() ? prev : (StackFrame *)NULL;
#ifdef DEBUG #ifdef DEBUG
Debug_SetValueRangeToCrashOnTouch(&rval_, 1); Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
@ -97,11 +96,9 @@ void
StackFrame::copyFrameAndValues(JSContext *cx, Value *vp, StackFrame *otherfp, StackFrame::copyFrameAndValues(JSContext *cx, Value *vp, StackFrame *otherfp,
const Value *othervp, Value *othersp) const Value *othervp, Value *othersp)
{ {
JS_ASSERT(vp == (Value *)this - ((Value *)otherfp - othervp));
JS_ASSERT(othervp == otherfp->generatorArgsSnapshotBegin()); JS_ASSERT(othervp == otherfp->generatorArgsSnapshotBegin());
JS_ASSERT(othersp >= otherfp->slots()); JS_ASSERT(othersp >= otherfp->slots());
JS_ASSERT(othersp <= otherfp->generatorSlotsSnapshotBegin() + otherfp->script()->nslots); JS_ASSERT(othersp <= otherfp->generatorSlotsSnapshotBegin() + otherfp->script()->nslots);
JS_ASSERT((Value *)this - vp == (Value *)otherfp - othervp);
/* Copy args, StackFrame, and slots. */ /* Copy args, StackFrame, and slots. */
const Value *srcend = otherfp->generatorArgsSnapshotEnd(); const Value *srcend = otherfp->generatorArgsSnapshotEnd();
@ -113,6 +110,7 @@ StackFrame::copyFrameAndValues(JSContext *cx, Value *vp, StackFrame *otherfp,
} }
*this = *otherfp; *this = *otherfp;
argv_ = vp + 2;
unsetPushedSPSFrame(); unsetPushedSPSFrame();
if (doPostBarrier) if (doPostBarrier)
writeBarrierPost(); writeBarrierPost();
@ -161,9 +159,9 @@ StackFrame::maybeSuspendedGenerator(JSRuntime *rt)
{ {
/* /*
* A suspended generator's frame is embedded inside the JSGenerator object * A suspended generator's frame is embedded inside the JSGenerator object
* instead of on the contiguous stack like all active frames. * and is not currently running.
*/ */
if (!isGeneratorFrame() || rt->stackSpace.containsFast(this)) if (!isGeneratorFrame() || !isSuspended())
return NULL; return NULL;
/* /*
@ -177,31 +175,6 @@ StackFrame::maybeSuspendedGenerator(JSRuntime *rt)
return gen; return gen;
} }
jsbytecode *
StackFrame::pcQuadratic(const ContextStack &stack, size_t maxDepth)
{
StackSegment &seg = stack.space().containingSegment(this);
FrameRegs &regs = seg.regs();
/*
* This isn't just an optimization; seg->computeNextFrame(fp) is only
* defined if fp != seg->regs->fp.
*/
if (regs.fp() == this)
return regs.pc;
/*
* To compute fp's pc, we need the next frame (where next->prev == fp).
* This requires a linear search which we allow the caller to limit (in
* cases where we do not have a hard requirement to find the correct pc).
*/
if (StackFrame *next = seg.computeNextFrame(this, maxDepth))
return next->prevpc();
/* If we hit the limit, just return the beginning of the script. */
return regs.fp()->script()->code;
}
bool bool
StackFrame::copyRawFrameSlots(AutoValueVector *vec) StackFrame::copyRawFrameSlots(AutoValueVector *vec)
{ {
@ -454,622 +427,78 @@ StackFrame::mark(JSTracer *trc)
gc::MarkValueUnbarriered(trc, &returnValue(), "rval"); gc::MarkValueUnbarriered(trc, &returnValue(), "rval");
} }
/*****************************************************************************/
bool
StackSegment::contains(const StackFrame *fp) const
{
/* NB: this depends on the continuity of segments in memory. */
return (Value *)fp >= slotsBegin() && (Value *)fp <= (Value *)maybefp();
}
bool
StackSegment::contains(const FrameRegs *regs) const
{
return regs && contains(regs->fp());
}
StackFrame *
StackSegment::computeNextFrame(const StackFrame *f, size_t maxDepth) const
{
JS_ASSERT(contains(f) && f != fp());
StackFrame *next = fp();
for (size_t i = 0; i <= maxDepth; ++i) {
if (next->prev() == f)
return next;
next = next->prev();
}
return NULL;
}
Value *
StackSegment::end() const
{
/* NB: this depends on the continuity of segments in memory. */
JS_ASSERT_IF(regs_, contains(regs_));
Value *p = regs_ ? regs_->sp : slotsBegin();
if (invokeArgsEnd_ > p)
p = invokeArgsEnd_;
JS_ASSERT(p >= slotsBegin());
return p;
}
FrameRegs *
StackSegment::pushRegs(FrameRegs &regs)
{
JS_ASSERT_IF(contains(regs_), regs.fp()->prev() == regs_->fp());
FrameRegs *prev = regs_;
regs_ = &regs;
return prev;
}
void void
StackSegment::popRegs(FrameRegs *regs) StackFrame::markValues(JSTracer *trc, Value *sp)
{ {
JS_ASSERT_IF(regs && contains(regs->fp()), regs->fp() == regs_->fp()->prev()); JS_ASSERT(sp >= slots());
regs_ = regs; gc::MarkValueRootRange(trc, sp - slots(), slots(), "vm_stack");
if (hasArgs())
gc::MarkValueRootRange(trc, js::Max(numActualArgs(), numFormalArgs()), argv_, "fp argv");
} }
/*****************************************************************************/ static void
MarkInterpreterActivation(JSTracer *trc, InterpreterActivation *act)
StackSpace::StackSpace()
: seg_(NULL),
base_(NULL),
conservativeEnd_(NULL),
#ifdef XP_WIN
commitEnd_(NULL),
#endif
defaultEnd_(NULL),
trustedEnd_(NULL)
{ {
assertInvariants(); for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
} StackFrame *fp = frames.frame();
fp->markValues(trc, frames.sp());
bool
StackSpace::init()
{
void *p;
#ifdef XP_WIN
p = VirtualAlloc(NULL, CAPACITY_BYTES, MEM_RESERVE, PAGE_READWRITE);
if (!p)
return false;
void *check = VirtualAlloc(p, COMMIT_BYTES, MEM_COMMIT, PAGE_READWRITE);
if (p != check)
return false;
base_ = reinterpret_cast<Value *>(p);
conservativeEnd_ = commitEnd_ = base_ + COMMIT_VALS;
trustedEnd_ = base_ + CAPACITY_VALS;
defaultEnd_ = trustedEnd_ - BUFFER_VALS;
Debug_SetValueRangeToCrashOnTouch(base_, commitEnd_);
#elif defined(XP_OS2)
if (DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY) &&
DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE))
return false;
base_ = reinterpret_cast<Value *>(p);
trustedEnd_ = base_ + CAPACITY_VALS;
conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
Debug_SetValueRangeToCrashOnTouch(base_, trustedEnd_);
#else
JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED)
return false;
base_ = reinterpret_cast<Value *>(p);
trustedEnd_ = base_ + CAPACITY_VALS;
conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
Debug_SetValueRangeToCrashOnTouch(base_, trustedEnd_);
#endif
assertInvariants();
return true;
}
StackSpace::~StackSpace()
{
assertInvariants();
JS_ASSERT(!seg_);
if (!base_)
return;
#ifdef XP_WIN
VirtualFree(base_, (commitEnd_ - base_) * sizeof(Value), MEM_DECOMMIT);
VirtualFree(base_, 0, MEM_RELEASE);
#elif defined(XP_OS2)
DosFreeMem(base_);
#else
#ifdef SOLARIS
munmap((caddr_t)base_, CAPACITY_BYTES);
#else
munmap(base_, CAPACITY_BYTES);
#endif
#endif
}
StackSegment &
StackSpace::containingSegment(const StackFrame *target) const
{
for (StackSegment *s = seg_; s; s = s->prevInMemory()) {
if (s->contains(target))
return *s;
}
JS_NOT_REACHED("frame not in stack space");
return *(StackSegment *)NULL;
}
void
StackSpace::markFrame(JSTracer *trc, StackFrame *fp, Value *slotsEnd)
{
/*
* JM may leave values with object/string type but a null payload on the
* stack. This can happen if the script was initially compiled by Ion,
* which replaced dead values with undefined, and later ran under JM which
* assumed values were of the original type.
*/
Value *slotsBegin = fp->slots();
gc::MarkValueRootRangeMaybeNullPayload(trc, slotsEnd - slotsBegin, slotsBegin, "vm_stack");
}
void
StackSpace::mark(JSTracer *trc)
{
/* NB: this depends on the continuity of segments in memory. */
Value *nextSegEnd = firstUnused();
for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
/*
* A segment describes a linear region of memory that contains a stack
* of native and interpreted calls. For marking purposes, though, we
* only need to distinguish between frames and values and mark
* accordingly. Since native calls only push values on the stack, we
* can effectively lump them together and just iterate over interpreted
* calls. Thus, marking can view the stack as the regex:
* (segment slots (frame slots)*)*
* which gets marked in reverse order.
*/
Value *slotsEnd = nextSegEnd;
for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) {
/* Mark from fp->slots() to slotsEnd. */
markFrame(trc, fp, slotsEnd);
fp->mark(trc); fp->mark(trc);
slotsEnd = (Value *)fp;
}
gc::MarkValueRootRange(trc, seg->slotsBegin(), slotsEnd, "vm_stack");
nextSegEnd = (Value *)seg;
} }
} }
void void
StackSpace::markActiveCompartments() js::MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc)
{ {
for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) { for (ActivationIterator iter(rt); !iter.done(); ++iter) {
for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) Activation *act = iter.activation();
MarkCompartmentActive(fp); if (act->isInterpreter())
MarkInterpreterActivation(trc, act->asInterpreter());
} }
} }
JS_FRIEND_API(bool)
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
{
assertInvariants();
JSCompartment *dest = cx->compartment();
bool trusted = dest->principals == cx->runtime()->trustedPrincipals();
Value *end = trusted ? trustedEnd_ : defaultEnd_;
/*
* conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
* to be bumped past defaultEnd_, untrusted JS would be able to consume the
* buffer space at the end of the stack reserved for trusted JS.
*/
if (end - from < nvals) {
if (report)
js_ReportOverRecursed(cx);
return false;
}
#ifdef XP_WIN
if (commitEnd_ - from < nvals) {
Value *newCommit = commitEnd_;
Value *request = from + nvals;
/* Use a dumb loop; will probably execute once. */
JS_ASSERT((trustedEnd_ - newCommit) % COMMIT_VALS == 0);
do {
newCommit += COMMIT_VALS;
JS_ASSERT((trustedEnd_ - newCommit) >= 0);
} while (newCommit < request);
/* The cast is safe because CAPACITY_BYTES is small. */
int32_t size = static_cast<int32_t>(newCommit - commitEnd_) * sizeof(Value);
if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
if (report)
js_ReportOverRecursed(cx);
return false;
}
Debug_SetValueRangeToCrashOnTouch(commitEnd_, newCommit);
commitEnd_ = newCommit;
conservativeEnd_ = Min(commitEnd_, defaultEnd_);
assertInvariants();
}
#endif
return true;
}
size_t
StackSpace::sizeOf()
{
#if defined(XP_UNIX)
/*
* Measure how many of our pages are resident in RAM using mincore, and
* return that as our size. This is slow, but hopefully nobody expects
* this method to be fast.
*
* Note that using mincore means that we don't count pages of the stack
* which are swapped out to disk. We really should, but what we have here
* is better than counting the whole stack!
*/
const int pageSize = getpagesize();
size_t numBytes = (trustedEnd_ - base_) * sizeof(Value);
size_t numPages = (numBytes + pageSize - 1) / pageSize;
// On Linux, mincore's third argument has type unsigned char*.
#ifdef __linux__
typedef unsigned char MincoreArgType;
#else
typedef char MincoreArgType;
#endif
MincoreArgType *vec = (MincoreArgType *) js_malloc(numPages);
int result = mincore(base_, numBytes, vec);
if (result) {
js_free(vec);
/*
* If mincore fails us, return the vsize (like we do below if we're not
* on Windows or Unix).
*/
return (trustedEnd_ - base_) * sizeof(Value);
}
size_t residentBytes = 0;
for (size_t i = 0; i < numPages; i++) {
/* vec[i] has its least-significant bit set iff page i is in RAM. */
if (vec[i] & 0x1)
residentBytes += pageSize;
}
js_free(vec);
return residentBytes;
#elif defined(XP_WIN)
return (commitEnd_ - base_) * sizeof(Value);
#else
/*
* Return the stack's virtual size, which is at least an upper bound on its
* resident size.
*/
return (trustedEnd_ - base_) * sizeof(Value);
#endif
}
#ifdef DEBUG
bool
StackSpace::containsSlow(StackFrame *fp)
{
if (!seg_)
return false;
for (AllFramesIter i(seg_->cx()); !i.done(); ++i) {
/*
* Debug-mode currently disables Ion compilation in the compartment of
* the debuggee.
*/
if (i.isJit())
continue;
if (i.interpFrame() == fp)
return true;
}
return false;
}
#endif
/*****************************************************************************/ /*****************************************************************************/
ContextStack::ContextStack(JSContext *cx)
: seg_(NULL),
space_(&cx->runtime()->stackSpace),
cx_(cx)
{}
ContextStack::~ContextStack()
{
JS_ASSERT(!seg_);
}
bool
ContextStack::onTop() const
{
return seg_ && seg_ == space().seg_;
}
/*
* This helper function brings the ContextStack to the top of the thread stack
* (so that it can be extended to push a frame and/or arguments) by potentially
* pushing a StackSegment. The 'pushedSeg' outparam indicates whether such a
* segment was pushed (and hence whether the caller needs to call popSegment).
*
* Additionally, to minimize calls to ensureSpace, ensureOnTop ensures that
* there is space for nvars slots on top of the stack.
*/
Value *
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
MaybeExtend extend, bool *pushedSeg)
{
Value *firstUnused = space().firstUnused();
FrameRegs *regs = cx->stack.maybeRegs();
if (onTop() && extend) {
if (!space().ensureSpace(cx, report, firstUnused, nvars))
return NULL;
return firstUnused;
}
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
return NULL;
regs = (seg_ && extend) ? seg_->maybeRegs() : NULL;
seg_ = new(firstUnused) StackSegment(cx, seg_, space().seg_, regs);
space().seg_ = seg_;
*pushedSeg = true;
return seg_->slotsBegin();
}
void
ContextStack::popSegment()
{
space().seg_ = seg_->prevInMemory();
seg_ = seg_->prevInContext();
if (!seg_)
cx_->maybeMigrateVersionOverride();
}
bool
ContextStack::pushInvokeArgs(JSContext *cx, unsigned argc, InvokeArgsGuard *iag,
MaybeReportError report)
{
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
unsigned nvars = 2 + argc;
Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &iag->pushedSeg_);
if (!firstUnused)
return false;
MakeRangeGCSafe(firstUnused, nvars);
ImplicitCast<CallArgs>(*iag) = CallArgsFromVp(argc, firstUnused);
seg_->pushInvokeArgsEnd(iag->end(), &iag->prevInvokeArgsEnd_);
JS_ASSERT(space().firstUnused() == iag->end());
iag->setPushed(*this);
return true;
}
void
ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
{
JS_ASSERT(iag.pushed());
JS_ASSERT(onTop());
JS_ASSERT(space().firstUnused() == seg_->invokeArgsEnd());
Value *oldend = seg_->end();
seg_->popInvokeArgsEnd(iag.prevInvokeArgsEnd_);
if (iag.pushedSeg_)
popSegment();
Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
}
StackFrame * StackFrame *
ContextStack::pushInvokeFrame(JSContext *cx, MaybeReportError report, InterpreterStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, InitialFrameFlags initial,
const CallArgs &args, JSFunction *funArg, FrameGuard *fg)
InitialFrameFlags initial, FrameGuard *fg)
{ {
JS_ASSERT(onTop()); LifoAlloc::Mark mark = allocator_.mark();
JS_ASSERT(space().firstUnused() == args.end());
RootedFunction fun(cx, funArg); RootedFunction fun(cx, &args.callee().as<JSFunction>());
RootedScript script(cx, fun->nonLazyScript()); RootedScript script(cx, fun->nonLazyScript());
StackFrame::Flags flags = ToFrameFlags(initial); StackFrame::Flags flags = ToFrameFlags(initial);
StackFrame *fp = getCallFrame(cx, report, args, fun, script, &flags); Value *argv;
StackFrame *fp = getCallFrame(cx, args, script, &flags, &argv);
if (!fp) if (!fp)
return NULL; return NULL;
fp->initCallFrame(cx, *fun, script, args.length(), flags); fp->mark_ = mark;
fg->regs_.prepareToRun(*fp, script); fp->initCallFrame(cx, NULL, NULL, NULL, *fun, script, argv, args.length(), flags);
fg->setPushed(*this, fp);
fg->prevRegs_ = seg_->pushRegs(fg->regs_);
JS_ASSERT(space().firstUnused() == fg->regs_.sp);
fg->setPushed(*this);
return fp; return fp;
} }
bool StackFrame *
ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, InterpreterStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv,
InitialFrameFlags initial, InvokeFrameGuard *ifg)
{
JSObject &callee = args.callee();
JSFunction *fun = &callee.as<JSFunction>();
if (!pushInvokeFrame(cx, REPORT_ERROR, args, fun, initial, ifg))
return false;
return true;
}
bool
ContextStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv,
HandleObject scopeChain, ExecuteType type, HandleObject scopeChain, ExecuteType type,
AbstractFramePtr evalInFrame, ExecuteFrameGuard *efg) AbstractFramePtr evalInFrame, FrameGuard *fg)
{ {
/* LifoAlloc::Mark mark = allocator_.mark();
* Even though global code and indirect eval do not execute in the context
* of the current frame, prev-link these to the current frame so that the
* callstack looks right to the debugger (via CAN_EXTEND). This is safe
* since the scope chain is what determines name lookup and access, not
* prev-links.
*
* Eval-in-frame is the exception since it prev-links to an arbitrary frame
* (possibly in the middle of some previous segment). Thus pass CANT_EXTEND
* (to start a new segment) and link the frame and call chain manually
* below. If |evalInFrame| is a baseline JIT frame, prev-link to its entry
* frame.
*/
MaybeExtend extend;
StackFrame *prevLink;
AbstractFramePtr prev = NullFramePtr();
if (evalInFrame) {
JS_ASSERT_IF(evalInFrame.isStackFrame(), !evalInFrame.asStackFrame()->runningInJit());
prevLink = NULL;
prev = evalInFrame;
extend = CANT_EXTEND;
} else {
prevLink = maybefp();
extend = CAN_EXTEND;
ScriptFrameIter iter(cx);
if (!iter.done())
prev = iter.isIon() ? maybefp() : iter.abstractFramePtr();
}
unsigned nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots; unsigned nvars = 2 /* callee, this */ + script->nslots;
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_); uint8_t *buffer = allocateFrame(cx, sizeof(StackFrame) + nvars * sizeof(Value));
if (!firstUnused) if (!buffer)
return false; return NULL;
StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2); StackFrame *fp = reinterpret_cast<StackFrame *>(buffer + 2 * sizeof(Value));
fp->initExecuteFrame(script, prevLink, prev, seg_->maybeRegs(), thisv, *scopeChain, type); fp->mark_ = mark;
fp->initExecuteFrame(cx, script, evalInFrame, thisv, *scopeChain, type);
fp->initVarsToUndefined(); fp->initVarsToUndefined();
efg->regs_.prepareToRun(*fp, script);
efg->prevRegs_ = seg_->pushRegs(efg->regs_); fg->setPushed(*this, fp);
JS_ASSERT(space().firstUnused() == efg->regs_.sp); return fp;
efg->setPushed(*this);
return true;
}
void
ContextStack::popFrame(const FrameGuard &fg)
{
JS_ASSERT(fg.pushed());
JS_ASSERT(onTop());
JS_ASSERT(space().firstUnused() == fg.regs_.sp);
JS_ASSERT(&fg.regs_ == &seg_->regs());
Value *oldend = seg_->end();
seg_->popRegs(fg.prevRegs_);
if (fg.pushedSeg_)
popSegment();
Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
}
bool
ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg)
{
HeapValue *genvp = gen->stackSnapshot;
JS_ASSERT(genvp == HeapValueify(gen->fp->generatorArgsSnapshotBegin()));
unsigned vplen = HeapValueify(gen->fp->generatorArgsSnapshotEnd()) - genvp;
unsigned nvars = vplen + VALUES_PER_STACK_FRAME + gen->fp->script()->nslots;
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &gfg->pushedSeg_);
if (!firstUnused)
return false;
StackFrame *stackfp = reinterpret_cast<StackFrame *>(firstUnused + vplen);
Value *stackvp = (Value *)stackfp - vplen;
/* Save this for popGeneratorFrame. */
gfg->gen_ = gen;
gfg->stackvp_ = stackvp;
/*
* Trigger incremental barrier on the floating frame's generator object.
* This is normally traced through only by associated arguments/call
* objects, but only when the generator is not actually on the stack.
* We don't need to worry about generational barriers as the generator
* object has a trace hook and cannot be nursery allocated.
*/
JS_ASSERT(gen->obj->getClass()->trace);
JSObject::writeBarrierPre(gen->obj);
/* Copy from the generator's floating frame to the stack. */
stackfp->copyFrameAndValues<StackFrame::NoPostBarrier>(cx, stackvp, gen->fp,
Valueify(genvp), gen->regs.sp);
stackfp->resetGeneratorPrev(cx);
gfg->regs_.rebaseFromTo(gen->regs, *stackfp);
gfg->prevRegs_ = seg_->pushRegs(gfg->regs_);
JS_ASSERT(space().firstUnused() == gfg->regs_.sp);
gfg->setPushed(*this);
return true;
}
void
ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
{
JSGenerator *gen = gfg.gen_;
HeapValue *genvp = gen->stackSnapshot;
const FrameRegs &stackRegs = gfg.regs_;
StackFrame *stackfp = stackRegs.fp();
Value *stackvp = gfg.stackvp_;
/* Copy from the stack to the generator's floating frame. */
if (stackfp->isYielding()) {
/*
* Assert that the frame is not markable so that we don't need an
* incremental write barrier when updating the generator's saved slots.
*/
JS_ASSERT(!GeneratorHasMarkableFrame(gen));
gen->regs.rebaseFromTo(stackRegs, *gen->fp);
gen->fp->copyFrameAndValues<StackFrame::DoPostBarrier>(cx_, (Value *)genvp, stackfp,
stackvp, stackRegs.sp);
}
/* ~FrameGuard/popFrame will finish the popping. */
JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
}
bool
ContextStack::saveFrameChain()
{
bool pushedSeg;
if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg))
return false;
JS_ASSERT(pushedSeg);
JS_ASSERT(!hasfp());
JS_ASSERT(onTop());
JS_ASSERT(seg_->isEmpty());
return true;
}
void
ContextStack::restoreFrameChain()
{
JS_ASSERT(!hasfp());
JS_ASSERT(onTop());
JS_ASSERT(seg_->isEmpty());
popSegment();
} }
/*****************************************************************************/ /*****************************************************************************/
@ -1278,9 +707,8 @@ ScriptFrameIter::operator++()
case DONE: case DONE:
JS_NOT_REACHED("Unexpected state"); JS_NOT_REACHED("Unexpected state");
case SCRIPTED: case SCRIPTED:
if (interpFrame()->isDebuggerFrame()) { if (interpFrame()->isDebuggerFrame() && interpFrame()->evalInFramePrev()) {
AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev(); AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
JS_ASSERT(eifPrev);
// Eval-in-frame can cross contexts and works across saved frame // Eval-in-frame can cross contexts and works across saved frame
// chains. // chains.
@ -1493,9 +921,20 @@ ScriptFrameIter::updatePcQuadratic()
switch (data_.state_) { switch (data_.state_) {
case DONE: case DONE:
break; break;
case SCRIPTED: case SCRIPTED: {
data_.pc_ = interpFrame()->pcQuadratic(data_.cx_); StackFrame *frame = interpFrame();
InterpreterActivation *activation = data_.activations_.activation()->asInterpreter();
// Look for the current frame.
data_.interpFrames_ = InterpreterFrameIterator(activation);
while (data_.interpFrames_.frame() != frame)
++data_.interpFrames_;
// Update the pc.
JS_ASSERT(data_.interpFrames_.frame() == frame);
data_.pc_ = data_.interpFrames_.pc();
return; return;
}
case JIT: case JIT:
#ifdef JS_ION #ifdef JS_ION
if (data_.ionFrames_.isBaselineJS()) { if (data_.ionFrames_.isBaselineJS()) {
@ -1775,8 +1214,8 @@ ScriptFrameIter::numFrameSlots() const
} }
case SCRIPTED: case SCRIPTED:
JS_ASSERT(data_.cx_); JS_ASSERT(data_.cx_);
JS_ASSERT(data_.cx_->stack.regs().spForStackDepth(0) == interpFrame()->base()); JS_ASSERT(data_.cx_->interpreterRegs().spForStackDepth(0) == interpFrame()->base());
return data_.cx_->stack.regs().sp - interpFrame()->base(); return data_.cx_->interpreterRegs().sp - interpFrame()->base();
} }
JS_NOT_REACHED("Unexpected state"); JS_NOT_REACHED("Unexpected state");
return 0; return 0;
@ -1880,8 +1319,15 @@ InterpreterFrameIterator &
InterpreterFrameIterator::operator++() InterpreterFrameIterator::operator++()
{ {
JS_ASSERT(!done()); JS_ASSERT(!done());
if (fp_ != activation_->entry_) {
pc_ = fp_->prevpc(); pc_ = fp_->prevpc();
fp_ = (fp_ != activation_->entry_) ? fp_->prev() : NULL; sp_ = fp_->prevsp();
fp_ = fp_->prev();
} else {
pc_ = NULL;
sp_ = NULL;
fp_ = NULL;
}
return *this; return *this;
} }

View File

@ -19,17 +19,12 @@ namespace js {
class StackFrame; class StackFrame;
class FrameRegs; class FrameRegs;
class StackSegment;
class StackSpace;
class ContextStack;
class InvokeArgsGuard;
class InvokeFrameGuard; class InvokeFrameGuard;
class FrameGuard; class FrameGuard;
class ExecuteFrameGuard; class ExecuteFrameGuard;
class GeneratorFrameGuard; class GeneratorFrameGuard;
class CallIter;
class ScriptFrameIter; class ScriptFrameIter;
class AllFramesIter; class AllFramesIter;
@ -39,77 +34,35 @@ class StaticBlockObject;
struct ScopeCoordinate; struct ScopeCoordinate;
/*****************************************************************************/ // VM stack layout
//
// A JSRuntime's stack consists of a linked list of activations. Every activation
// contains a number of scripted frames that are either running in the interpreter
// (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
// activation are contiguous: whenever C++ calls back into JS, a new activation is
// pushed.
//
// Every activation is tied to a single JSContext and JSCompartment. This means we
// can reconstruct a given context's stack by skipping activations belonging to other
// contexts. This happens whenever an embedding enters the JS engine on cx1 and
// then, from a native called by the JS engine, reenters the VM on cx2.
/* // Interpreter frames (StackFrame)
* VM stack layout //
* // Each interpreter script activation (global or function code) is given a
* SpiderMonkey uses a per-runtime stack to store the activation records, // fixed-size header (js::StackFrame). The frame contains bookkeeping information
* parameters, locals, and expression temporaries for the stack of actively // about the activation and links to the previous frame.
* executing scripts, functions and generators. //
* // The values after a StackFrame in memory are its locals followed by its
* The stack is subdivided into contiguous segments of memory which // expression stack. StackFrame::argv_ points to the frame's arguments. Missing
* have a memory layout invariant that allows fixed offsets to be used for stack // formal arguments are padded with |undefined|, so the number of arguments is
* access (by jit code) as well as fast call/return. This memory layout is // always >= the number of formals.
* encapsulated by a set of types that describe different regions of memory. //
* This encapsulation has holes: to avoid calling into C++ from generated code, // The top of an activation's current frame's expression stack is pointed to by the
* JIT compilers generate code that simulates analogous operations in C++. // activation's "current regs", which contains the stack pointer 'sp'. In the
* // interpreter, sp is adjusted as individual values are pushed and popped from
* A sample memory layout of a segment looks like: // the stack and the FrameRegs struct (pointed to by the InterpreterActivation)
* // is a local var of js::Interpret.
* regs
* .------------------------------------------------.
* | V
* | fp .--FrameRegs--. sp
* | V V
* |StackSegment| values |StackFrame| values |StackFrame| values |
* | ^ |
* ? <-----------' `------------'
* prev prev
*
* A segment starts with a fixed-size header (js::StackSegment) which logically
* describes the segment, links it to the rest of the stack, and points to the
* end of the stack.
*
* Each script activation (global or function code) is given a fixed-size header
* (js::StackFrame) which is associated with the values before and after it.
* The frame contains bookkeeping information about the activation and links to
* the previous frame.
*
* The value preceding a (function) StackFrame in memory are the arguments of
* the call. The values after a StackFrame in memory are its locals followed by
* its expression stack. There is no clean line between the arguments of a
* frame and the expression stack of the previous frame since the top values of
* the expression become the arguments of a call. There are also layout
* invariants concerning the arguments and StackFrame; see "Arguments" comment
* in StackFrame for more details.
*
* The top of a segment's current frame's expression stack is pointed to by the
* segment's "current regs", which contains the stack pointer 'sp'. In the
* interpreter, sp is adjusted as individual values are pushed and popped from
* the stack and the FrameRegs struct (pointed by the StackSegment) is a local
* var of js::Interpret. Ideally, we'd like to remove all dependence on FrameRegs
* outside the interpreter.
*
* An additional feature (perhaps not for much longer: bug 650361) is that
* multiple independent "contexts" can interleave (LIFO) on a single contiguous
* stack. "Independent" here means that each context has its own callstack.
* Note, though, that eval-in-frame allows one context's callstack to join
* another context's callstack. Thus, in general, the structure of calls in a
* StackSpace is a forest.
*
* More concretely, an embedding may enter the JS engine on cx1 and then, from
* a native called by the JS engine, reenter the VM on cx2. Changing from cx1
* to cx2 causes a new segment to be started for cx2's stack on top of cx1's
* current segment. These two segments are linked from the perspective of
* StackSpace, since they are adjacent on the thread's stack, but not from the
* perspective of cx1 and cx2. Each independent stack is encapsulated and
* managed by the js::ContextStack object stored in JSContext. ContextStack
* is the primary interface to the rest of the engine for pushing and popping
* the stack.
*/
/*****************************************************************************/
enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false }; enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
@ -281,18 +234,26 @@ class StackFrame
GENERATOR = 0x10, /* frame is associated with a generator */ GENERATOR = 0x10, /* frame is associated with a generator */
CONSTRUCTING = 0x20, /* frame is for a constructor invocation */ CONSTRUCTING = 0x20, /* frame is for a constructor invocation */
/* Temporary frame states */ /*
* Generator frame state
*
* YIELDING and SUSPENDED are similar, but there are differences. After
* a generator yields, SendToGenerator immediately clears the YIELDING
* flag, but the frame will still have the SUSPENDED flag. Also, when the
* generator returns but before it's GC'ed, YIELDING is not set but
* SUSPENDED is.
*/
YIELDING = 0x40, /* Interpret dispatched JSOP_YIELD */ YIELDING = 0x40, /* Interpret dispatched JSOP_YIELD */
SUSPENDED = 0x80, /* Generator is not running. */
/* Function prologue state */ /* Function prologue state */
HAS_CALL_OBJ = 0x80, /* CallObject created for heavyweight fun */ HAS_CALL_OBJ = 0x100, /* CallObject created for heavyweight fun */
HAS_ARGS_OBJ = 0x100, /* ArgumentsObject created for needsArgsObj script */ HAS_ARGS_OBJ = 0x200, /* ArgumentsObject created for needsArgsObj script */
/* Lazy frame initialization */ /* Lazy frame initialization */
HAS_HOOK_DATA = 0x200, /* frame has hookData_ set */ HAS_HOOK_DATA = 0x400, /* frame has hookData_ set */
HAS_RVAL = 0x400, /* frame has rval_ set */ HAS_RVAL = 0x800, /* frame has rval_ set */
HAS_SCOPECHAIN = 0x800, /* frame has scopeChain_ set */ HAS_SCOPECHAIN = 0x1000, /* frame has scopeChain_ set */
HAS_PREVPC = 0x1000, /* frame has prevpc_ and prevInline_ set */
HAS_BLOCKCHAIN = 0x2000, /* frame has blockChain_ set */ HAS_BLOCKCHAIN = 0x2000, /* frame has blockChain_ set */
/* Debugger state */ /* Debugger state */
@ -322,20 +283,28 @@ class StackFrame
JSScript *evalScript; /* the script of an eval-in-function */ JSScript *evalScript; /* the script of an eval-in-function */
} u; } u;
mutable JSObject *scopeChain_; /* if HAS_SCOPECHAIN, current scope chain */ mutable JSObject *scopeChain_; /* if HAS_SCOPECHAIN, current scope chain */
StackFrame *prev_; /* if HAS_PREVPC, previous cx->regs->fp */
Value rval_; /* if HAS_RVAL, return value of the frame */ Value rval_; /* if HAS_RVAL, return value of the frame */
StaticBlockObject *blockChain_; /* if HAS_BLOCKCHAIN, innermost let block */ StaticBlockObject *blockChain_; /* if HAS_BLOCKCHAIN, innermost let block */
ArgumentsObject *argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */ ArgumentsObject *argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */
jsbytecode *prevpc_; /* if HAS_PREVPC, pc of previous frame*/
/*
* Previous frame and its pc and sp. Always NULL for InterpreterActivation's
* entry frame, always non-NULL for inline frames.
*/
StackFrame *prev_;
jsbytecode *prevpc_;
Value *prevsp_;
void *hookData_; /* if HAS_HOOK_DATA, closure returned by call hook */ void *hookData_; /* if HAS_HOOK_DATA, closure returned by call hook */
AbstractFramePtr evalInFramePrev_; /* for an eval/debugger frame, the prev frame */ AbstractFramePtr evalInFramePrev_; /* for an eval/debugger frame, the prev frame */
Value *argv_; /* If hasArgs(), points to frame's arguments. */
LifoAlloc::Mark mark_; /* Used to release memory for this frame. */
static void staticAsserts() { static void staticAsserts() {
JS_STATIC_ASSERT(offsetof(StackFrame, rval_) % sizeof(Value) == 0); JS_STATIC_ASSERT(offsetof(StackFrame, rval_) % sizeof(Value) == 0);
JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(Value) == 0); JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(Value) == 0);
} }
inline void initPrev(JSContext *cx);
void writeBarrierPost(); void writeBarrierPost();
/* /*
@ -348,30 +317,28 @@ class StackFrame
public: public:
Value *slots() const { return (Value *)(this + 1); } Value *slots() const { return (Value *)(this + 1); }
Value *base() const { return slots() + script()->nfixed; } Value *base() const { return slots() + script()->nfixed; }
Value *argv() const { return (Value *)this - Max(numActualArgs(), numFormalArgs()); } Value *argv() const { return argv_; }
private: private:
friend class FrameRegs; friend class FrameRegs;
friend class ContextStack; friend class InterpreterStack;
friend class StackSpace;
friend class ScriptFrameIter; friend class ScriptFrameIter;
friend class CallObject; friend class CallObject;
friend class ClonedBlockObject; friend class ClonedBlockObject;
friend class ArgumentsObject; friend class ArgumentsObject;
/* /*
* Frame initialization, called by ContextStack operations after acquiring * Frame initialization, called by InterpreterStack operations after acquiring
* the raw memory for the frame: * the raw memory for the frame:
*/ */
/* Used for Invoke, Interpret, trace-jit LeaveTree, and method-jit stubs. */ /* Used for Invoke and Interpret. */
void initCallFrame(JSContext *cx, JSFunction &callee, void initCallFrame(JSContext *cx, StackFrame *prev, jsbytecode *prevpc, Value *prevsp, JSFunction &callee,
JSScript *script, uint32_t nactual, StackFrame::Flags flags); JSScript *script, Value *argv, uint32_t nactual, StackFrame::Flags flags);
/* Used for eval. */ /* Used for global and eval frames. */
void initExecuteFrame(JSScript *script, StackFrame *prevLink, AbstractFramePtr prev, void initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr prev,
FrameRegs *regs, const Value &thisv, JSObject &scopeChain, const Value &thisv, JSObject &scopeChain, ExecuteType type);
ExecuteType type);
public: public:
/* /*
@ -481,8 +448,6 @@ class StackFrame
return evalInFramePrev_; return evalInFramePrev_;
} }
inline void resetGeneratorPrev(JSContext *cx);
/* /*
* (Unaliased) locals and arguments * (Unaliased) locals and arguments
* *
@ -623,29 +588,18 @@ class StackFrame
: exec.script; : exec.script;
} }
/* /* Return the previous frame's pc. */
* Get the frame's current bytecode, assuming 'this' is in 'stack'. Beware,
* as the name implies, pcQuadratic can lead to quadratic behavior in loops
* such as:
*
* for ( ...; fp; fp = fp->prev())
* ... fp->pcQuadratic(cx->stack);
*
* This can be avoided in three ways:
* - use ScriptFrameIter, it has O(1) iteration
* - if you know the next frame (i.e., next s.t. next->prev == fp
* - pcQuadratic will only iterate maxDepth frames (before giving up and
* returning fp->script->code), making it O(1), but incorrect.
*/
jsbytecode *pcQuadratic(const ContextStack &stack, size_t maxDepth = SIZE_MAX);
/* Return the previous frame's pc. Unlike pcQuadratic, this is O(1). */
jsbytecode *prevpc() { jsbytecode *prevpc() {
JS_ASSERT(flags_ & HAS_PREVPC); JS_ASSERT(prev_);
return prevpc_; return prevpc_;
} }
/* Return the previous frame's sp. */
Value *prevsp() {
JS_ASSERT(prev_);
return prevsp_;
}
/* /*
* Function * Function
* *
@ -830,7 +784,7 @@ class StackFrame
Value *generatorArgsSnapshotEnd() const { Value *generatorArgsSnapshotEnd() const {
JS_ASSERT(isGeneratorFrame()); JS_ASSERT(isGeneratorFrame());
return (Value *)this; return argv() + js::Max(numActualArgs(), numFormalArgs());
} }
Value *generatorSlotsSnapshotBegin() const { Value *generatorSlotsSnapshotBegin() const {
@ -934,6 +888,21 @@ class StackFrame
flags_ &= ~YIELDING; flags_ &= ~YIELDING;
} }
bool isSuspended() const {
JS_ASSERT(isGeneratorFrame());
return flags_ & SUSPENDED;
}
void setSuspended() {
JS_ASSERT(isGeneratorFrame());
flags_ |= SUSPENDED;
}
void clearSuspended() {
JS_ASSERT(isGeneratorFrame());
flags_ &= ~SUSPENDED;
}
public: public:
static size_t offsetOfFlags() { static size_t offsetOfFlags() {
return offsetof(StackFrame, flags_); return offsetof(StackFrame, flags_);
@ -968,6 +937,7 @@ class StackFrame
public: public:
void mark(JSTracer *trc); void mark(JSTracer *trc);
void markValues(JSTracer *trc, Value *sp);
// Entered Baseline/Ion from the interpreter. // Entered Baseline/Ion from the interpreter.
bool runningInJit() const { bool runningInJit() const {
@ -1026,7 +996,7 @@ class FrameRegs
return fp_->base() + depth; return fp_->base() + depth;
} }
/* For generator: */ /* For generators. */
void rebaseFromTo(const FrameRegs &from, StackFrame &to) { void rebaseFromTo(const FrameRegs &from, StackFrame &to) {
fp_ = &to; fp_ = &to;
sp = to.slots() + (from.sp - from.fp_->slots()); sp = to.slots() + (from.sp - from.fp_->slots());
@ -1034,15 +1004,12 @@ class FrameRegs
JS_ASSERT(fp_); JS_ASSERT(fp_);
} }
/* For ContextStack: */ void popInlineFrame() {
void popFrame(Value *newsp) {
pc = fp_->prevpc(); pc = fp_->prevpc();
sp = newsp; sp = fp_->prevsp() - fp_->numActualArgs() - 1;
fp_ = fp_->prev(); fp_ = fp_->prev();
JS_ASSERT(fp_); JS_ASSERT(fp_);
} }
/* For stubs::CompileFunction, ContextStack: */
void prepareToRun(StackFrame &fp, JSScript *script) { void prepareToRun(StackFrame &fp, JSScript *script) {
pc = script->code; pc = script->code;
sp = fp.slots() + script->nfixed; sp = fp.slots() + script->nfixed;
@ -1059,415 +1026,104 @@ class FrameRegs
/*****************************************************************************/ /*****************************************************************************/
class StackSegment class InterpreterStack
{ {
JSContext *cx_; friend class FrameGuard;
friend class InterpreterActivation;
/* Previous segment within same context stack. */ const static size_t DEFAULT_CHUNK_SIZE = 4 * 1024;
StackSegment *const prevInContext_; LifoAlloc allocator_;
/* Previous segment sequentially in memory. */ // Number of interpreter frames on the stack, for over-recursion checks.
StackSegment *const prevInMemory_; static const size_t MAX_FRAMES = 50 * 1000;
size_t frameCount_;
/* Execution registers for most recent script in this segment (or null). */ inline uint8_t *allocateFrame(JSContext *cx, size_t size);
FrameRegs *regs_;
/* End of CallArgs pushed by pushInvokeArgs. */
Value *invokeArgsEnd_;
#if JS_BITS_PER_WORD == 32
/*
* Ensure StackSegment is Value-aligned. Protected to silence Clang warning
* about unused private fields.
*/
protected:
uint32_t padding_;
#endif
public:
StackSegment(JSContext *cx,
StackSegment *prevInContext,
StackSegment *prevInMemory,
FrameRegs *regs)
: cx_(cx),
prevInContext_(prevInContext),
prevInMemory_(prevInMemory),
regs_(regs),
invokeArgsEnd_(NULL)
{}
/* A segment is followed in memory by the arguments of the first call. */
Value *slotsBegin() const {
return (Value *)(this + 1);
}
/* Accessors. */
FrameRegs &regs() const {
JS_ASSERT(regs_);
return *regs_;
}
FrameRegs *maybeRegs() const {
return regs_;
}
StackFrame *fp() const {
return regs_->fp();
}
StackFrame *maybefp() const {
return regs_ ? regs_->fp() : NULL;
}
jsbytecode *maybepc() const {
return regs_ ? regs_->pc : NULL;
}
JSContext *cx() const {
return cx_;
}
StackSegment *prevInContext() const {
return prevInContext_;
}
StackSegment *prevInMemory() const {
return prevInMemory_;
}
void repointRegs(FrameRegs *regs) {
regs_ = regs;
}
bool isEmpty() const {
return !regs_;
}
bool contains(const StackFrame *fp) const;
bool contains(const FrameRegs *regs) const;
StackFrame *computeNextFrame(const StackFrame *fp, size_t maxDepth) const;
Value *end() const;
FrameRegs *pushRegs(FrameRegs &regs);
void popRegs(FrameRegs *regs);
Value *invokeArgsEnd() const {
return invokeArgsEnd_;
}
void pushInvokeArgsEnd(Value *end, Value **prev) {
*prev = invokeArgsEnd_;
invokeArgsEnd_ = end;
}
void popInvokeArgsEnd(Value *prev) {
invokeArgsEnd_ = prev;
}
};
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
/*****************************************************************************/
class StackSpace
{
StackSegment *seg_;
Value *base_;
mutable Value *conservativeEnd_;
#ifdef XP_WIN
mutable Value *commitEnd_;
#endif
Value *defaultEnd_;
Value *trustedEnd_;
void assertInvariants() const {
JS_ASSERT(base_ <= conservativeEnd_);
#ifdef XP_WIN
JS_ASSERT(conservativeEnd_ <= commitEnd_);
JS_ASSERT(commitEnd_ <= trustedEnd_);
#endif
JS_ASSERT(conservativeEnd_ <= defaultEnd_);
JS_ASSERT(defaultEnd_ <= trustedEnd_);
}
/* The total number of values/bytes reserved for the stack. */
static const size_t CAPACITY_VALS = 512 * 1024;
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
/* How much of the stack is initially committed. */
static const size_t COMMIT_VALS = 16 * 1024;
static const size_t COMMIT_BYTES = COMMIT_VALS * sizeof(Value);
/* How much space is reserved at the top of the stack for trusted JS. */
static const size_t BUFFER_VALS = 16 * 1024;
static const size_t BUFFER_BYTES = BUFFER_VALS * sizeof(Value);
static void staticAsserts() {
JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
}
friend class AllFramesIter;
friend class ContextStack;
friend class StackFrame;
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
StackSegment &findContainingSegment(const StackFrame *target) const;
bool containsFast(StackFrame *fp) {
return (Value *)fp >= base_ && (Value *)fp <= trustedEnd_;
}
void markFrame(JSTracer *trc, StackFrame *fp, Value *slotsEnd);
public:
StackSpace();
bool init();
~StackSpace();
/*
* Maximum supported value of arguments.length. This bounds the maximum
* number of arguments that can be supplied to Function.prototype.apply.
* This value also bounds the number of elements parsed in an array
* initialiser.
*
* Since arguments are copied onto the stack, the stack size is the
* limiting factor for this constant. Use the max stack size (available to
* untrusted code) with an extra buffer so that, after such an apply, the
* callee can do a little work without OOMing.
*/
static const unsigned ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS);
/* See stack layout comment in Stack.h. */
inline Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
StackSegment &containingSegment(const StackFrame *target) const;
/* Called during GC: mark segments, frames, and slots under firstUnused. */
void mark(JSTracer *trc);
/* Called during GC: sets active flag on compartments with active frames. */
void markActiveCompartments();
/*
* On Windows, report the committed size; on *nix, we report the resident
* size (which means that if part of the stack is swapped to disk, we say
* it's shrunk).
*/
JS_FRIEND_API(size_t) sizeOf();
#ifdef DEBUG
/* Only used in assertion of debuggers API. */
bool containsSlow(StackFrame *fp);
#endif
};
/*****************************************************************************/
class ContextStack
{
StackSegment *seg_;
StackSpace *const space_;
JSContext *cx_;
/*
* Return whether this ContextStack is at the top of the contiguous stack.
* This is a precondition for extending the current segment by pushing
* stack frames or overrides etc.
*
* NB: Just because a stack is onTop() doesn't mean there is necessarily
* a frame pushed on the stack. For this, use hasfp().
*/
bool onTop() const;
#ifdef DEBUG
void assertSpaceInSync() const;
#else
void assertSpaceInSync() const {}
#endif
/* Implementation details of push* public interface. */
StackSegment *pushSegment(JSContext *cx);
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
Value *ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
MaybeExtend extend, bool *pushedSeg);
inline StackFrame * inline StackFrame *
getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args, getCallFrame(JSContext *cx, const CallArgs &args, HandleScript script,
JSFunction *fun, HandleScript script, StackFrame::Flags *pflags) const; StackFrame::Flags *pflags, Value **pargv);
/* Make pop* functions private since only called by guard classes. */ void releaseFrame(StackFrame *fp) {
void popSegment(); frameCount_--;
friend class InvokeArgsGuard; allocator_.release(fp->mark_);
void popInvokeArgs(const InvokeArgsGuard &iag); }
friend class FrameGuard;
void popFrame(const FrameGuard &fg);
friend class GeneratorFrameGuard;
void popGeneratorFrame(const GeneratorFrameGuard &gfg);
friend class ScriptFrameIter;
public: public:
ContextStack(JSContext *cx); InterpreterStack()
~ContextStack(); : allocator_(DEFAULT_CHUNK_SIZE),
frameCount_(0)
{ }
/*** Stack accessors ***/ ~InterpreterStack() {
JS_ASSERT(frameCount_ == 0);
}
/* // For execution of eval or global code.
* A context's stack is "empty" if there are no scripts or natives StackFrame *pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv,
* executing. Note that JS_SaveFrameChain does not factor into this definition.
*/
bool empty() const { return !seg_; }
/*
* Return whether there has been at least one frame pushed since the most
* recent call to JS_SaveFrameChain. Note that natives do not have frames
* hence this query has little semantic meaning past "you can call fp()".
*/
inline bool hasfp() const { return seg_ && seg_->maybeRegs(); }
/*
* Return the most recent script activation's registers with the same
* caveat as hasfp regarding JS_SaveFrameChain.
*/
inline FrameRegs *maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; }
inline StackFrame *maybefp() const { return seg_ ? seg_->maybefp() : NULL; }
/* Faster alternatives to maybe* functions. */
inline FrameRegs &regs() const { JS_ASSERT(hasfp()); return seg_->regs(); }
inline StackFrame *fp() const { JS_ASSERT(hasfp()); return seg_->fp(); }
/* The StackSpace currently hosting this ContextStack. */
StackSpace &space() const { return *space_; }
/*** Stack manipulation ***/
/*
* pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
* the arguments to Invoke. A single allocation can be used for multiple
* Invoke calls. The InvokeArgsGuard passed to Invoke must come from
* an immediately-enclosing (stack-wise) call to pushInvokeArgs.
*/
bool pushInvokeArgs(JSContext *cx, unsigned argc, InvokeArgsGuard *ag,
MaybeReportError report = REPORT_ERROR);
StackFrame *pushInvokeFrame(JSContext *cx, MaybeReportError report,
const CallArgs &args, JSFunction *fun,
InitialFrameFlags initial, FrameGuard *fg);
/* Called by Invoke for a scripted function call. */
bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
InitialFrameFlags initial, InvokeFrameGuard *ifg);
/* Called by Execute for execution of eval or global code. */
bool pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv,
HandleObject scopeChain, ExecuteType type, HandleObject scopeChain, ExecuteType type,
AbstractFramePtr evalInFrame, ExecuteFrameGuard *efg); AbstractFramePtr evalInFrame, FrameGuard *fg);
/* // Called to invoke a function.
* Called by SendToGenerator to resume a yielded generator. In addition to StackFrame *pushInvokeFrame(JSContext *cx, const CallArgs &args, InitialFrameFlags initial,
* pushing a frame onto the VM stack, this function copies over the FrameGuard *fg);
* floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
* will copy the frame back to the floating frame.
*/
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
/* // The interpreter can push light-weight, "inline" frames without entering a
* An "inline frame" may only be pushed from within the top, active // new InterpreterActivation or recursively calling Interpret.
* segment. This is the case for calls made inside mjit code and Interpret.
* The 'stackLimit' overload updates 'stackLimit' if it changes.
*/
bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args, bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
HandleFunction callee, HandleScript script, HandleScript script, InitialFrameFlags initial);
InitialFrameFlags initial,
MaybeReportError report = REPORT_ERROR);
bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
HandleFunction callee, HandleScript script,
InitialFrameFlags initial, Value **stackLimit);
void popInlineFrame(FrameRegs &regs); void popInlineFrame(FrameRegs &regs);
/* inline void purge(JSRuntime *rt);
* Get the topmost script and optional pc on the stack. By default, this
* function only returns a JSScript in the current compartment, returning
* NULL if the current script is in a different compartment. This behavior
* can be overridden by passing ALLOW_CROSS_COMPARTMENT.
*/
enum MaybeAllowCrossCompartment {
DONT_ALLOW_CROSS_COMPARTMENT = false,
ALLOW_CROSS_COMPARTMENT = true
};
inline JSScript *currentScript(jsbytecode **pc = NULL,
MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const;
/* Get the scope chain for the topmost scripted call on the stack. */ size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
inline HandleObject currentScriptedScopeChain() const; return allocator_.sizeOfExcludingThis(mallocSizeOf);
}
bool saveFrameChain();
void restoreFrameChain();
/*
* As an optimization, the interpreter/mjit can operate on a local
* FrameRegs instance repoint the ContextStack to this local instance.
*/
inline void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
}; };
void MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc);
/*****************************************************************************/ /*****************************************************************************/
class InvokeArgsGuard : public JS::CallArgs class InvokeArgs : public JS::CallArgs
{ {
friend class ContextStack; AutoValueVector v_;
ContextStack *stack_;
Value *prevInvokeArgsEnd_;
bool pushedSeg_;
void setPushed(ContextStack &stack) { JS_ASSERT(!pushed()); stack_ = &stack; }
public: public:
InvokeArgsGuard() : CallArgs(), stack_(NULL), prevInvokeArgsEnd_(NULL), pushedSeg_(false) {} InvokeArgs(JSContext *cx) : v_(cx) {}
~InvokeArgsGuard() { if (pushed()) stack_->popInvokeArgs(*this); }
bool pushed() const { return !!stack_; } bool init(unsigned argc) {
void pop() { stack_->popInvokeArgs(*this); stack_ = NULL; } if (!v_.resize(2 + argc))
return false;
ImplicitCast<CallArgs>(*this) = CallArgsFromVp(argc, v_.begin());
return true;
}
}; };
class RunState;
class FrameGuard class FrameGuard
{ {
protected: friend class InterpreterStack;
friend class ContextStack; RunState &state_;
ContextStack *stack_; FrameRegs &regs_;
bool pushedSeg_; InterpreterStack *stack_;
FrameRegs regs_; StackFrame *fp_;
FrameRegs *prevRegs_;
void setPushed(ContextStack &stack) { stack_ = &stack; } void setPushed(InterpreterStack &stack, StackFrame *fp) {
stack_ = &stack;
fp_ = fp;
}
public: public:
FrameGuard() : stack_(NULL), pushedSeg_(false) {} FrameGuard(RunState &state, FrameRegs &regs);
~FrameGuard() { if (pushed()) stack_->popFrame(*this); } ~FrameGuard();
bool pushed() const { return !!stack_; }
void pop() { stack_->popFrame(*this); stack_ = NULL; }
StackFrame *fp() const { return regs_.fp(); } StackFrame *fp() const {
}; JS_ASSERT(fp_);
return fp_;
class InvokeFrameGuard : public FrameGuard }
{};
class ExecuteFrameGuard : public FrameGuard
{};
class DummyFrameGuard : public FrameGuard
{};
class GeneratorFrameGuard : public FrameGuard
{
friend class ContextStack;
JSGenerator *gen_;
Value *stackvp_;
public:
~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
}; };
template <> template <>
@ -1491,15 +1147,6 @@ namespace ion {
class JitActivation; class JitActivation;
}; };
// A JSRuntime's stack consists of a linked list of activations. Every activation
// contains a number of scripted frames that are either running in the interpreter
// (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
// activation are contiguous: whenever C++ calls back into JS, a new activation is
// pushed.
//
// Every activation is tied to a single JSContext and JSCompartment. This means we
// can construct a given context's stack by skipping activations belonging to other
// contexts.
class Activation class Activation
{ {
protected: protected:
@ -1579,21 +1226,18 @@ class InterpreterActivation : public Activation
StackFrame *current_; // The most recent frame. StackFrame *current_; // The most recent frame.
FrameRegs &regs_; FrameRegs &regs_;
#ifdef DEBUG
size_t oldFrameCount_;
#endif
public: public:
inline InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs); inline InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs);
inline ~InterpreterActivation(); inline ~InterpreterActivation();
void pushFrame(StackFrame *frame) { inline bool pushInlineFrame(const CallArgs &args, HandleScript script,
JS_ASSERT(frame->script()->compartment() == compartment_); InitialFrameFlags initial);
current_ = frame; inline void popInlineFrame(StackFrame *frame);
}
void popFrame(StackFrame *frame) {
JS_ASSERT(current_ == frame);
JS_ASSERT(current_ != entry_);
current_ = frame->prev();
JS_ASSERT(current_);
}
StackFrame *current() const { StackFrame *current() const {
JS_ASSERT(current_); JS_ASSERT(current_);
return current_; return current_;
@ -1688,16 +1332,19 @@ class InterpreterFrameIterator
InterpreterActivation *activation_; InterpreterActivation *activation_;
StackFrame *fp_; StackFrame *fp_;
jsbytecode *pc_; jsbytecode *pc_;
Value *sp_;
public: public:
explicit InterpreterFrameIterator(InterpreterActivation *activation) explicit InterpreterFrameIterator(InterpreterActivation *activation)
: activation_(activation), : activation_(activation),
fp_(NULL), fp_(NULL),
pc_(NULL) pc_(NULL),
sp_(NULL)
{ {
if (activation) { if (activation) {
fp_ = activation->current(); fp_ = activation->current();
pc_ = activation->regs_.pc; pc_ = activation->regs_.pc;
sp_ = activation->regs_.sp;
} }
} }
@ -1709,6 +1356,10 @@ class InterpreterFrameIterator
JS_ASSERT(!done()); JS_ASSERT(!done());
return pc_; return pc_;
} }
Value *sp() const {
JS_ASSERT(!done());
return sp_;
}
InterpreterFrameIterator &operator++(); InterpreterFrameIterator &operator++();
@ -1769,7 +1420,6 @@ class ScriptFrameIter
Data(const Data &other); Data(const Data &other);
}; };
friend class ContextStack;
friend class ::JSBrokenFrameIterator; friend class ::JSBrokenFrameIterator;
private: private:
Data data_; Data data_;

View File

@ -2029,15 +2029,9 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpData, nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpData,
"Memory used by the regexp JIT to hold data."); "Memory used by the regexp JIT to hold data.");
RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/stack"), RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.stack, nsIMemoryReporter::KIND_HEAP, rtStats.runtime.interpreterStack,
"Memory used for the JS call stack. This is the committed " "Memory used for JS interpreter frames.");
"portion of the stack on Windows; on *nix, it is the resident "
"portion of the stack. Therefore, on *nix, if part of the "
"stack is swapped out to disk, we do not count it here.\n\n"
"Note that debug builds usually have stack poisoning enabled, "
"which causes the whole stack to be committed (and likely "
"resident).");
RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc-marker"), RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc-marker"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker, nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker,