blob: db6fc229a38be84d6885ecc59252d2c1a1ac9ba6 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_Stack_inl_h
#define vm_Stack_inl_h
#include "mozilla/PodOperations.h"
#include "jscntxt.h"
#include "jscompartment.h"
#include "vm/Stack.h"
#include "jit/BaselineFrame.h"
#include "jit/BaselineFrame-inl.h"
#include "jit/IonFrameIterator-inl.h"
#include "jsfuninlines.h"
#include "jsscriptinlines.h"
#include "ArgumentsObject-inl.h"
#include "ScopeObject-inl.h"
namespace js {
/*
* We cache name lookup results only for the global object or for native
* non-global objects without prototype or with prototype that never mutates,
* see bug 462734 and bug 487039.
*/
static inline bool
IsCacheableNonGlobalScope(JSObject *obj)
{
bool cacheable = (obj->is<CallObject>() || obj->is<BlockObject>() || obj->is<DeclEnvObject>());
JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
return cacheable;
}
inline HandleObject
StackFrame::scopeChain() const
{
JS_ASSERT_IF(!(flags_ & HAS_SCOPECHAIN), isFunctionFrame());
if (!(flags_ & HAS_SCOPECHAIN)) {
scopeChain_ = callee().environment();
flags_ |= HAS_SCOPECHAIN;
}
return HandleObject::fromMarkedLocation(&scopeChain_);
}
inline GlobalObject &
StackFrame::global() const
{
return scopeChain()->global();
}
inline JSObject &
StackFrame::varObj()
{
JSObject *obj = scopeChain();
while (!obj->isVarObj())
obj = obj->enclosingScope();
return *obj;
}
inline JSCompartment *
StackFrame::compartment() const
{
JS_ASSERT(scopeChain()->compartment() == script()->compartment());
return scopeChain()->compartment();
}
inline void
StackFrame::initCallFrame(JSContext *cx, StackFrame *prev, jsbytecode *prevpc, Value *prevsp, JSFunction &callee,
JSScript *script, Value *argv, uint32_t nactual, StackFrame::Flags flagsArg)
{
JS_ASSERT((flagsArg & ~CONSTRUCTING) == 0);
JS_ASSERT(callee.nonLazyScript() == script);
/* Initialize stack frame members. */
flags_ = FUNCTION | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
argv_ = argv;
exec.fun = &callee;
u.nactual = nactual;
scopeChain_ = callee.environment();
prev_ = prev;
prevpc_ = prevpc;
prevsp_ = prevsp;
blockChain_= NULL;
JS_ASSERT(!hasBlockChain());
JS_ASSERT(!hasHookData());
initVarsToUndefined();
}
inline void
StackFrame::initVarsToUndefined()
{
SetValueRangeToUndefined(slots(), script()->nfixed);
}
inline Value &
StackFrame::unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing)
{
JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
JS_ASSERT(i < script()->nfixed);
return slots()[i];
}
inline Value &
StackFrame::unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing)
{
#ifdef DEBUG
CheckLocalUnaliased(checkAliasing, script(), maybeBlockChain(), i);
#endif
return slots()[i];
}
inline Value &
StackFrame::unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing)
{
JS_ASSERT(i < numFormalArgs());
JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
JS_ASSERT_IF(checkAliasing, !script()->formalIsAliased(i));
return argv()[i];
}
inline Value &
StackFrame::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing)
{
JS_ASSERT(i < numActualArgs());
JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i));
return argv()[i];
}
template <class Op>
inline void
StackFrame::forEachUnaliasedActual(Op op)
{
JS_ASSERT(!script()->funHasAnyAliasedFormal);
JS_ASSERT(!script()->needsArgsObj());
const Value *argsEnd = argv() + numActualArgs();
for (const Value *p = argv(); p < argsEnd; ++p)
op(*p);
}
struct CopyTo
{
Value *dst;
CopyTo(Value *dst) : dst(dst) {}
void operator()(const Value &src) { *dst++ = src; }
};
struct CopyToHeap
{
HeapValue *dst;
CopyToHeap(HeapValue *dst) : dst(dst) {}
void operator()(const Value &src) { dst->init(src); ++dst; }
};
inline ArgumentsObject &
StackFrame::argsObj() const
{
JS_ASSERT(script()->needsArgsObj());
JS_ASSERT(flags_ & HAS_ARGS_OBJ);
return *argsObj_;
}
inline void
StackFrame::initArgsObj(ArgumentsObject &argsobj)
{
JS_ASSERT(script()->needsArgsObj());
flags_ |= HAS_ARGS_OBJ;
argsObj_ = &argsobj;
}
inline ScopeObject &
StackFrame::aliasedVarScope(ScopeCoordinate sc) const
{
JSObject *scope = &scopeChain()->as<ScopeObject>();
for (unsigned i = sc.hops; i; i--)
scope = &scope->as<ScopeObject>().enclosingScope();
return scope->as<ScopeObject>();
}
inline void
StackFrame::pushOnScopeChain(ScopeObject &scope)
{
JS_ASSERT(*scopeChain() == scope.enclosingScope() ||
*scopeChain() == scope.as<CallObject>().enclosingScope().as<DeclEnvObject>().enclosingScope());
scopeChain_ = &scope;
flags_ |= HAS_SCOPECHAIN;
}
inline void
StackFrame::popOffScopeChain()
{
JS_ASSERT(flags_ & HAS_SCOPECHAIN);
scopeChain_ = &scopeChain_->as<ScopeObject>().enclosingScope();
}
bool
StackFrame::hasCallObj() const
{
JS_ASSERT(isStrictEvalFrame() || fun()->isHeavyweight());
return flags_ & HAS_CALL_OBJ;
}
inline CallObject &
StackFrame::callObj() const
{
JS_ASSERT(fun()->isHeavyweight());
JSObject *pobj = scopeChain();
while (JS_UNLIKELY(!pobj->is<CallObject>()))
pobj = pobj->enclosingScope();
return pobj->as<CallObject>();
}
/*****************************************************************************/
inline void
InterpreterStack::purge(JSRuntime *rt)
{
rt->freeLifoAlloc.transferUnusedFrom(&allocator_);
}
uint8_t *
InterpreterStack::allocateFrame(JSContext *cx, size_t size)
{
size_t maxFrames;
if (cx->compartment()->principals == cx->runtime()->trustedPrincipals())
maxFrames = MAX_FRAMES_TRUSTED;
else
maxFrames = MAX_FRAMES;
if (JS_UNLIKELY(frameCount_ >= maxFrames)) {
js_ReportOverRecursed(cx);
return NULL;
}
uint8_t *buffer = reinterpret_cast<uint8_t *>(allocator_.alloc(size));
if (!buffer)
return NULL;
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
InterpreterStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
HandleScript script, InitialFrameFlags initial)
{
JSFunction *callee = &args.callee().as<JSFunction>();
JS_ASSERT(regs.sp == args.end());
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);
Value *argv;
StackFrame *fp = getCallFrame(cx, args, script, &flags, &argv);
if (!fp)
return false;
fp->mark_ = mark;
/* Initialize frame, locals, regs. */
fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, args.length(), flags);
regs.prepareToRun(*fp, script);
return true;
}
JS_ALWAYS_INLINE void
InterpreterStack::popInlineFrame(FrameRegs &regs)
{
StackFrame *fp = regs.fp();
regs.popInlineFrame();
regs.sp[-1] = fp->returnValue();
releaseFrame(fp);
JS_ASSERT(regs.fp());
}
template <class Op>
inline void
ScriptFrameIter::ionForEachCanonicalActualArg(JSContext *cx, Op op)
{
JS_ASSERT(isJit());
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS()) {
ionInlineFrames_.forEachCanonicalActualArg(cx, op, 0, -1);
} else {
JS_ASSERT(data_.ionFrames_.isBaselineJS());
data_.ionFrames_.forEachCanonicalActualArg(op, 0, -1);
}
#endif
}
inline void *
AbstractFramePtr::maybeHookData() const
{
if (isStackFrame())
return asStackFrame()->maybeHookData();
#ifdef JS_ION
return asBaselineFrame()->maybeHookData();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline void
AbstractFramePtr::setHookData(void *data) const
{
if (isStackFrame()) {
asStackFrame()->setHookData(data);
return;
}
#ifdef JS_ION
asBaselineFrame()->setHookData(data);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value
AbstractFramePtr::returnValue() const
{
if (isStackFrame())
return asStackFrame()->returnValue();
#ifdef JS_ION
return *asBaselineFrame()->returnValue();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline void
AbstractFramePtr::setReturnValue(const Value &rval) const
{
if (isStackFrame()) {
asStackFrame()->setReturnValue(rval);
return;
}
#ifdef JS_ION
asBaselineFrame()->setReturnValue(rval);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline JSObject *
AbstractFramePtr::scopeChain() const
{
if (isStackFrame())
return asStackFrame()->scopeChain();
#ifdef JS_ION
return asBaselineFrame()->scopeChain();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline void
AbstractFramePtr::pushOnScopeChain(ScopeObject &scope)
{
if (isStackFrame()) {
asStackFrame()->pushOnScopeChain(scope);
return;
}
#ifdef JS_ION
asBaselineFrame()->pushOnScopeChain(scope);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline CallObject &
AbstractFramePtr::callObj() const
{
if (isStackFrame())
return asStackFrame()->callObj();
#ifdef JS_ION
return asBaselineFrame()->callObj();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::initFunctionScopeObjects(JSContext *cx)
{
if (isStackFrame())
return asStackFrame()->initFunctionScopeObjects(cx);
#ifdef JS_ION
return asBaselineFrame()->initFunctionScopeObjects(cx);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline JSCompartment *
AbstractFramePtr::compartment() const
{
return scopeChain()->compartment();
}
inline unsigned
AbstractFramePtr::numActualArgs() const
{
if (isStackFrame())
return asStackFrame()->numActualArgs();
#ifdef JS_ION
return asBaselineFrame()->numActualArgs();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline unsigned
AbstractFramePtr::numFormalArgs() const
{
if (isStackFrame())
return asStackFrame()->numFormalArgs();
#ifdef JS_ION
return asBaselineFrame()->numFormalArgs();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value &
AbstractFramePtr::unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing)
{
if (isStackFrame())
return asStackFrame()->unaliasedVar(i, checkAliasing);
#ifdef JS_ION
return asBaselineFrame()->unaliasedVar(i, checkAliasing);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value &
AbstractFramePtr::unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing)
{
if (isStackFrame())
return asStackFrame()->unaliasedLocal(i, checkAliasing);
#ifdef JS_ION
return asBaselineFrame()->unaliasedLocal(i, checkAliasing);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value &
AbstractFramePtr::unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing)
{
if (isStackFrame())
return asStackFrame()->unaliasedFormal(i, checkAliasing);
#ifdef JS_ION
return asBaselineFrame()->unaliasedFormal(i, checkAliasing);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value &
AbstractFramePtr::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing)
{
if (isStackFrame())
return asStackFrame()->unaliasedActual(i, checkAliasing);
#ifdef JS_ION
return asBaselineFrame()->unaliasedActual(i, checkAliasing);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline JSGenerator *
AbstractFramePtr::maybeSuspendedGenerator(JSRuntime *rt) const
{
if (isStackFrame())
return asStackFrame()->maybeSuspendedGenerator(rt);
return NULL;
}
inline StaticBlockObject *
AbstractFramePtr::maybeBlockChain() const
{
if (isStackFrame())
return asStackFrame()->maybeBlockChain();
#ifdef JS_ION
return asBaselineFrame()->maybeBlockChain();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::hasCallObj() const
{
if (isStackFrame())
return asStackFrame()->hasCallObj();
#ifdef JS_ION
return asBaselineFrame()->hasCallObj();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::useNewType() const
{
if (isStackFrame())
return asStackFrame()->useNewType();
return false;
}
inline bool
AbstractFramePtr::isGeneratorFrame() const
{
if (isStackFrame())
return asStackFrame()->isGeneratorFrame();
return false;
}
inline bool
AbstractFramePtr::isYielding() const
{
if (isStackFrame())
return asStackFrame()->isYielding();
return false;
}
inline bool
AbstractFramePtr::isFunctionFrame() const
{
if (isStackFrame())
return asStackFrame()->isFunctionFrame();
#ifdef JS_ION
return asBaselineFrame()->isFunctionFrame();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::isGlobalFrame() const
{
if (isStackFrame())
return asStackFrame()->isGlobalFrame();
#ifdef JS_ION
return asBaselineFrame()->isGlobalFrame();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::isEvalFrame() const
{
if (isStackFrame())
return asStackFrame()->isEvalFrame();
#ifdef JS_ION
return asBaselineFrame()->isEvalFrame();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::isFramePushedByExecute() const
{
return isGlobalFrame() || isEvalFrame();
}
inline bool
AbstractFramePtr::isDebuggerFrame() const
{
if (isStackFrame())
return asStackFrame()->isDebuggerFrame();
#ifdef JS_ION
return asBaselineFrame()->isDebuggerFrame();
#else
JS_NOT_REACHED("Invalid frame");
#endif
return false;
}
inline JSScript *
AbstractFramePtr::script() const
{
if (isStackFrame())
return asStackFrame()->script();
#ifdef JS_ION
return asBaselineFrame()->script();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline JSFunction *
AbstractFramePtr::fun() const
{
if (isStackFrame())
return asStackFrame()->fun();
#ifdef JS_ION
return asBaselineFrame()->fun();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline JSFunction *
AbstractFramePtr::maybeFun() const
{
if (isStackFrame())
return asStackFrame()->maybeFun();
#ifdef JS_ION
return asBaselineFrame()->maybeFun();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline JSFunction *
AbstractFramePtr::callee() const
{
if (isStackFrame())
return &asStackFrame()->callee();
#ifdef JS_ION
return asBaselineFrame()->callee();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value
AbstractFramePtr::calleev() const
{
if (isStackFrame())
return asStackFrame()->calleev();
#ifdef JS_ION
return asBaselineFrame()->calleev();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::isNonEvalFunctionFrame() const
{
if (isStackFrame())
return asStackFrame()->isNonEvalFunctionFrame();
#ifdef JS_ION
return asBaselineFrame()->isNonEvalFunctionFrame();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::isNonStrictDirectEvalFrame() const
{
if (isStackFrame())
return asStackFrame()->isNonStrictDirectEvalFrame();
#ifdef JS_ION
return asBaselineFrame()->isNonStrictDirectEvalFrame();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::isStrictEvalFrame() const
{
if (isStackFrame())
return asStackFrame()->isStrictEvalFrame();
#ifdef JS_ION
return asBaselineFrame()->isStrictEvalFrame();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value *
AbstractFramePtr::argv() const
{
if (isStackFrame())
return asStackFrame()->argv();
#ifdef JS_ION
return asBaselineFrame()->argv();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::hasArgsObj() const
{
if (isStackFrame())
return asStackFrame()->hasArgsObj();
#ifdef JS_ION
return asBaselineFrame()->hasArgsObj();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline ArgumentsObject &
AbstractFramePtr::argsObj() const
{
if (isStackFrame())
return asStackFrame()->argsObj();
#ifdef JS_ION
return asBaselineFrame()->argsObj();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline void
AbstractFramePtr::initArgsObj(ArgumentsObject &argsobj) const
{
if (isStackFrame()) {
asStackFrame()->initArgsObj(argsobj);
return;
}
#ifdef JS_ION
asBaselineFrame()->initArgsObj(argsobj);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::copyRawFrameSlots(AutoValueVector *vec) const
{
if (isStackFrame())
return asStackFrame()->copyRawFrameSlots(vec);
#ifdef JS_ION
return asBaselineFrame()->copyRawFrameSlots(vec);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline bool
AbstractFramePtr::prevUpToDate() const
{
if (isStackFrame())
return asStackFrame()->prevUpToDate();
#ifdef JS_ION
return asBaselineFrame()->prevUpToDate();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline void
AbstractFramePtr::setPrevUpToDate() const
{
if (isStackFrame()) {
asStackFrame()->setPrevUpToDate();
return;
}
#ifdef JS_ION
asBaselineFrame()->setPrevUpToDate();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline Value &
AbstractFramePtr::thisValue() const
{
if (isStackFrame())
return asStackFrame()->thisValue();
#ifdef JS_ION
return asBaselineFrame()->thisValue();
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline void
AbstractFramePtr::popBlock(JSContext *cx) const
{
if (isStackFrame()) {
asStackFrame()->popBlock(cx);
return;
}
#ifdef JS_ION
asBaselineFrame()->popBlock(cx);
#else
JS_NOT_REACHED("Invalid frame");
#endif
}
inline void
AbstractFramePtr::popWith(JSContext *cx) const
{
if (isStackFrame())
asStackFrame()->popWith(cx);
else
JS_NOT_REACHED("Invalid frame");
}
Activation::Activation(JSContext *cx, Kind kind)
: cx_(cx),
compartment_(cx->compartment()),
prev_(cx->mainThread().activation_),
savedFrameChain_(0),
kind_(kind)
{
cx->mainThread().activation_ = this;
}
Activation::~Activation()
{
JS_ASSERT(cx_->mainThread().activation_ == this);
cx_->mainThread().activation_ = prev_;
}
InterpreterActivation::InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs)
: Activation(cx, Interpreter),
entry_(entry),
current_(entry),
regs_(regs)
#ifdef DEBUG
, oldFrameCount_(cx_->runtime()->interpreterStack().frameCount_)
#endif
{}
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 */
#endif /* vm_Stack_inl_h */