зеркало из https://github.com/mozilla/gecko-dev.git
Bug 753158 - emit ALIASEDVAR ops for upvars (r=bhackett)
--HG-- extra : rebase_source : 1e868b37de87f94799500019cc13e7b8abe3c0f3
This commit is contained in:
Родитель
1299e77cea
Коммит
dce55acfc8
|
@ -873,6 +873,7 @@ EmitAliasedVarOp(JSContext *cx, JSOp op, ScopeCoordinate sc, BytecodeEmitter *bc
|
|||
SET_UINT16(pc, sc.slot);
|
||||
pc += sizeof(uint16_t);
|
||||
SET_UINT32_INDEX(pc, maybeBlockIndex);
|
||||
CheckTypeSet(cx, bce, op);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -891,40 +892,48 @@ ClonedBlockDepth(BytecodeEmitter *bce)
|
|||
static bool
|
||||
EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
|
||||
{
|
||||
/* This restriction will be removed shortly by bug 753158. */
|
||||
JS_ASSERT(pn->isUsed() || pn->isDefn());
|
||||
JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
|
||||
JS_ASSERT_IF(pn->isDefn(), pn->pn_cookie.level() == bce->script->staticLevel);
|
||||
|
||||
unsigned skippedScopes = 0;
|
||||
BytecodeEmitter *bceOfDef = bce;
|
||||
if (pn->isUsed()) {
|
||||
/*
|
||||
* The contents of the dynamic scope chain (fp->scopeChain) exactly reflect
|
||||
* the needsClone-subset of the block chain. Use this to determine the
|
||||
* number of ClonedBlockObjects on fp->scopeChain to skip to find the scope
|
||||
* object containing the var to which pn is bound. ALIASEDVAR ops cannot
|
||||
* reach across with scopes so ClonedBlockObjects is the only NestedScope
|
||||
* on the scope chain.
|
||||
* As explained in BindNameToSlot, the 'level' of a use indicates how
|
||||
* many function scopes (i.e., BytecodeEmitters) to skip to find the
|
||||
* enclosing function scope of the definition being accessed.
|
||||
*/
|
||||
for (unsigned i = pn->pn_cookie.level(); i; i--) {
|
||||
skippedScopes += ClonedBlockDepth(bceOfDef);
|
||||
if (bceOfDef->sc->funIsHeavyweight()) {
|
||||
skippedScopes++;
|
||||
if (bceOfDef->sc->fun()->isNamedLambda())
|
||||
skippedScopes++;
|
||||
}
|
||||
bceOfDef = bceOfDef->parent;
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(pn->isDefn());
|
||||
JS_ASSERT(pn->pn_cookie.level() == bce->script->staticLevel);
|
||||
}
|
||||
|
||||
ScopeCoordinate sc;
|
||||
if (JOF_OPTYPE(pn->getOp()) == JOF_QARG) {
|
||||
sc.hops = ClonedBlockDepth(bce);
|
||||
sc.slot = bce->sc->bindings.formalIndexToSlot(pn->pn_cookie.slot());
|
||||
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
|
||||
sc.slot = bceOfDef->sc->bindings.formalIndexToSlot(pn->pn_cookie.slot());
|
||||
} else {
|
||||
JS_ASSERT(JOF_OPTYPE(pn->getOp()) == JOF_LOCAL || pn->isKind(PNK_FUNCTION));
|
||||
unsigned local = pn->pn_cookie.slot();
|
||||
if (local < bce->sc->bindings.numVars()) {
|
||||
sc.hops = ClonedBlockDepth(bce);
|
||||
sc.slot = bce->sc->bindings.varIndexToSlot(local);
|
||||
if (local < bceOfDef->sc->bindings.numVars()) {
|
||||
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
|
||||
sc.slot = bceOfDef->sc->bindings.varIndexToSlot(local);
|
||||
} else {
|
||||
unsigned depth = local - bce->sc->bindings.numVars();
|
||||
unsigned hops = 0;
|
||||
StaticBlockObject *b = bce->blockChain;
|
||||
unsigned depth = local - bceOfDef->sc->bindings.numVars();
|
||||
StaticBlockObject *b = bceOfDef->blockChain;
|
||||
while (!b->containsVarAtDepth(depth)) {
|
||||
if (b->needsClone())
|
||||
hops++;
|
||||
skippedScopes++;
|
||||
b = b->enclosingBlock();
|
||||
}
|
||||
sc.hops = hops;
|
||||
sc.slot = b->localIndexToSlot(bce->sc->bindings, local);
|
||||
sc.hops = skippedScopes;
|
||||
sc.slot = b->localIndexToSlot(bceOfDef->sc->bindings, local);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1325,12 +1334,6 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
JS_ASSERT(pn->pn_lexdef);
|
||||
JS_ASSERT(pn->pn_cookie.isFree());
|
||||
|
||||
/* TODO: actually the comment lies; until bug 753158, y will stay a NAME. */
|
||||
if (dn->pn_cookie.level() != bce->script->staticLevel) {
|
||||
JS_ASSERT(dn->isClosed());
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are compiling a function body and may be able to optimize name
|
||||
* to stack slot. Look for an argument or variable in the function and
|
||||
|
@ -1356,6 +1359,14 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
case Definition::VAR:
|
||||
if (dn->isOp(JSOP_CALLEE)) {
|
||||
JS_ASSERT(op != JSOP_CALLEE);
|
||||
|
||||
/*
|
||||
* Currently, the ALIASEDVAR ops do not support accessing the
|
||||
* callee of a DeclEnvObject, so use NAME.
|
||||
*/
|
||||
if (dn->pn_cookie.level() != bce->script->staticLevel)
|
||||
return true;
|
||||
|
||||
JS_ASSERT(bce->sc->fun()->flags & JSFUN_LAMBDA);
|
||||
JS_ASSERT(pn->pn_atom == bce->sc->fun()->atom);
|
||||
|
||||
|
@ -1414,9 +1425,34 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
JS_NOT_REACHED("unexpected dn->kind()");
|
||||
}
|
||||
|
||||
/*
|
||||
* The difference between the current static level and the static level of
|
||||
* the definition is the number of function scopes between the current
|
||||
* scope and dn's scope.
|
||||
*/
|
||||
unsigned skip = bce->script->staticLevel - dn->pn_cookie.level();
|
||||
JS_ASSERT_IF(skip, dn->isClosed());
|
||||
|
||||
/*
|
||||
* Explicitly disallow accessing var/let bindings in global scope from
|
||||
* nested functions. The reason for this limitation is that, since the
|
||||
* global script is not included in the static scope chain (1. because it
|
||||
* has no object to stand in the static scope chain, 2. to minimize memory
|
||||
* bloat where a single live function keeps its whole global script
|
||||
* alive.), ScopeCoordinateToTypeSet is not able to find the var/let's
|
||||
* associated types::TypeSet.
|
||||
*/
|
||||
if (skip) {
|
||||
BytecodeEmitter *bceSkipped = bce;
|
||||
for (unsigned i = 0; i < skip; i++)
|
||||
bceSkipped = bceSkipped->parent;
|
||||
if (!bceSkipped->sc->inFunction())
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(!pn->isOp(op));
|
||||
pn->setOp(op);
|
||||
if (!pn->pn_cookie.set(bce->sc->context, 0, dn->pn_cookie.slot()))
|
||||
if (!pn->pn_cookie.set(bce->sc->context, skip, dn->pn_cookie.slot()))
|
||||
return false;
|
||||
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
|
|
|
@ -314,18 +314,13 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
|||
case JSOP_BINDNAME:
|
||||
case JSOP_SETNAME:
|
||||
case JSOP_DELNAME:
|
||||
case JSOP_GETALIASEDVAR:
|
||||
case JSOP_CALLALIASEDVAR:
|
||||
case JSOP_SETALIASEDVAR:
|
||||
usesScopeChain_ = true;
|
||||
isInlineable = false;
|
||||
break;
|
||||
|
||||
case JSOP_GETALIASEDVAR:
|
||||
case JSOP_CALLALIASEDVAR:
|
||||
case JSOP_SETALIASEDVAR: {
|
||||
JS_ASSERT(!isInlineable);
|
||||
usesScopeChain_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_DEFFUN:
|
||||
case JSOP_DEFVAR:
|
||||
case JSOP_DEFCONST:
|
||||
|
|
|
@ -359,16 +359,6 @@ static inline uint32_t GetBytecodeSlot(JSScript *script, jsbytecode *pc)
|
|||
case JSOP_LOCALDEC:
|
||||
return LocalSlot(script, GET_SLOTNO(pc));
|
||||
|
||||
case JSOP_GETALIASEDVAR:
|
||||
case JSOP_CALLALIASEDVAR:
|
||||
case JSOP_SETALIASEDVAR:
|
||||
{
|
||||
unsigned index;
|
||||
return ScopeCoordinateToFrameIndex(script, pc, &index) == FrameIndex_Local
|
||||
? LocalSlot(script, index)
|
||||
: ArgSlot(index);
|
||||
}
|
||||
|
||||
case JSOP_THIS:
|
||||
return ThisSlot();
|
||||
|
||||
|
|
|
@ -3418,8 +3418,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
break;
|
||||
}
|
||||
|
||||
case JSOP_GETALIASEDVAR:
|
||||
case JSOP_CALLALIASEDVAR:
|
||||
case JSOP_GETARG:
|
||||
case JSOP_CALLARG:
|
||||
case JSOP_GETLOCAL:
|
||||
|
@ -3439,12 +3437,11 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
/* Local 'let' variable. Punt on types for these, for now. */
|
||||
pushed[0].addType(cx, Type::UnknownType());
|
||||
}
|
||||
if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL || op == JSOP_CALLALIASEDVAR)
|
||||
if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
|
||||
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_SETALIASEDVAR:
|
||||
case JSOP_SETARG:
|
||||
case JSOP_SETLOCAL: {
|
||||
uint32_t slot = GetBytecodeSlot(script, pc);
|
||||
|
@ -3462,6 +3459,24 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
break;
|
||||
}
|
||||
|
||||
case JSOP_GETALIASEDVAR:
|
||||
case JSOP_CALLALIASEDVAR:
|
||||
/*
|
||||
* Every aliased variable will contain 'undefined' in addition to the
|
||||
* type of whatever value is written to it. Thus, a dynamic barrier is
|
||||
* necessary. Since we don't expect the to observe more than 1 type,
|
||||
* there is little benefit to maintaining a TypeSet for the aliased
|
||||
* variable. Instead, we monitor/barrier all reads unconditionally.
|
||||
*/
|
||||
bytecodeTypes(pc)->addSubset(cx, &pushed[0]);
|
||||
if (op == JSOP_CALLALIASEDVAR)
|
||||
pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
|
||||
break;
|
||||
|
||||
case JSOP_SETALIASEDVAR:
|
||||
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
|
||||
break;
|
||||
|
||||
case JSOP_INCARG:
|
||||
case JSOP_DECARG:
|
||||
case JSOP_ARGINC:
|
||||
|
|
|
@ -2765,6 +2765,7 @@ BEGIN_CASE(JSOP_GETALIASEDVAR)
|
|||
{
|
||||
ScopeCoordinate sc = ScopeCoordinate(regs.pc);
|
||||
PUSH_COPY(regs.fp()->aliasedVarScope(sc).aliasedVar(sc));
|
||||
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
|
||||
}
|
||||
END_CASE(JSOP_GETALIASEDVAR)
|
||||
|
||||
|
|
|
@ -342,8 +342,8 @@ OPDEF(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
|
|||
* uint32 block: the index (into the script object table) of the block chain
|
||||
* at the point of the variable access.
|
||||
*/
|
||||
OPDEF(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL, 9, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME)
|
||||
OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL, 9, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME)
|
||||
OPDEF(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL, 9, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET)
|
||||
OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL, 9, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET)
|
||||
OPDEF(JSOP_SETALIASEDVAR, 138,"setaliasedvar",NULL, 9, 1, 1, 3, JOF_SCOPECOORD|JOF_NAME|JOF_SET|JOF_DETECTING)
|
||||
OPDEF(JSOP_INCALIASEDVAR, 139,"incaliasedvar",NULL, 10, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
|
||||
OPDEF(JSOP_DECALIASEDVAR, 140,"decaliasedvar",NULL, 10, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
|
||||
|
|
|
@ -103,6 +103,9 @@ class Bindings
|
|||
uint16_t numVars() const { return nvars; }
|
||||
unsigned count() const { return nargs + nvars; }
|
||||
|
||||
/* Convert a CallObject slot to either a formal or local variable index. */
|
||||
inline BindingKind slotToFrameIndex(unsigned slot, unsigned *index);
|
||||
|
||||
/*
|
||||
* The VM's StackFrame allocates a Value for each formal and variable.
|
||||
* A (formal|var)Index is the index passed to fp->unaliasedFormal/Var to
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "jsscript.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
#include "vm/ScopeObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
|
||||
|
@ -28,6 +27,20 @@ Bindings::Bindings()
|
|||
: lastBinding(NULL), nargs(0), nvars(0), hasDup_(false)
|
||||
{}
|
||||
|
||||
inline BindingKind
|
||||
Bindings::slotToFrameIndex(unsigned slot, unsigned *index)
|
||||
{
|
||||
slot -= CallObject::RESERVED_SLOTS;
|
||||
if (slot < numArgs()) {
|
||||
*index = slot;
|
||||
return ARGUMENT;
|
||||
}
|
||||
|
||||
*index = slot - numArgs();
|
||||
JS_ASSERT(*index < numVars());
|
||||
return VARIABLE;
|
||||
}
|
||||
|
||||
inline void
|
||||
Bindings::transfer(Bindings *bindings)
|
||||
{
|
||||
|
|
|
@ -2832,8 +2832,6 @@ mjit::Compiler::generateMethod()
|
|||
|
||||
BEGIN_CASE(JSOP_GETLOCAL)
|
||||
BEGIN_CASE(JSOP_CALLLOCAL)
|
||||
BEGIN_CASE(JSOP_GETALIASEDVAR)
|
||||
BEGIN_CASE(JSOP_CALLALIASEDVAR)
|
||||
{
|
||||
/*
|
||||
* Update the var type unless we are about to pop the variable.
|
||||
|
@ -2845,26 +2843,27 @@ mjit::Compiler::generateMethod()
|
|||
restoreVarType();
|
||||
if (JSObject *singleton = pushedSingleton(0))
|
||||
frame.push(ObjectValue(*singleton));
|
||||
else if (JOF_OPTYPE(*PC) == JOF_SCOPECOORD)
|
||||
jsop_aliasedVar(ScopeCoordinate(PC), /* get = */ true);
|
||||
else
|
||||
frame.pushLocal(GET_SLOTNO(PC));
|
||||
|
||||
PC += GetBytecodeLength(PC);
|
||||
break;
|
||||
}
|
||||
END_CASE(JSOP_GETLOCAL)
|
||||
|
||||
BEGIN_CASE(JSOP_GETALIASEDVAR)
|
||||
BEGIN_CASE(JSOP_CALLALIASEDVAR)
|
||||
jsop_aliasedVar(ScopeCoordinate(PC), /* get = */ true);
|
||||
END_CASE(JSOP_GETALIASEDVAR);
|
||||
|
||||
BEGIN_CASE(JSOP_SETLOCAL)
|
||||
BEGIN_CASE(JSOP_SETALIASEDVAR)
|
||||
{
|
||||
jsbytecode *next = &PC[GetBytecodeLength(PC)];
|
||||
bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
|
||||
if (JOF_OPTYPE(*PC) == JOF_SCOPECOORD)
|
||||
if (JOF_OPTYPE(*PC) == JOF_SCOPECOORD) {
|
||||
jsop_aliasedVar(ScopeCoordinate(PC), /* get = */ false, pop);
|
||||
else
|
||||
} else {
|
||||
frame.storeLocal(GET_SLOTNO(PC), pop);
|
||||
updateVarType();
|
||||
}
|
||||
|
||||
if (pop) {
|
||||
frame.pop();
|
||||
|
@ -5804,12 +5803,7 @@ mjit::Compiler::jsop_aliasedVar(ScopeCoordinate sc, bool get, bool poppedAfter)
|
|||
for (unsigned i = 0; i < sc.hops; i++)
|
||||
masm.loadPayload(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg);
|
||||
|
||||
Shape *shape;
|
||||
if (StaticBlockObject *block = ScopeCoordinateBlockChain(script, PC))
|
||||
shape = block->lastProperty();
|
||||
else
|
||||
shape = script->bindings.lastShape();
|
||||
|
||||
Shape *shape = ScopeCoordinateToStaticScope(script, PC).scopeShape();
|
||||
Address addr;
|
||||
if (shape->numFixedSlots() <= sc.slot) {
|
||||
masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
|
||||
|
@ -5819,12 +5813,15 @@ mjit::Compiler::jsop_aliasedVar(ScopeCoordinate sc, bool get, bool poppedAfter)
|
|||
}
|
||||
|
||||
if (get) {
|
||||
unsigned index;
|
||||
FrameEntry *fe = ScopeCoordinateToFrameIndex(script, PC, &index) == FrameIndex_Local
|
||||
? frame.getLocal(index)
|
||||
: frame.getArg(index);
|
||||
JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN;
|
||||
frame.push(addr, type, true /* = reuseBase */);
|
||||
JSValueType type = knownPushedType(0);
|
||||
RegisterID typeReg, dataReg;
|
||||
frame.loadIntoRegisters(addr, /* reuseBase = */ true, &typeReg, &dataReg);
|
||||
frame.pushRegs(typeReg, dataReg, type);
|
||||
BarrierState barrier = testBarrier(typeReg, dataReg,
|
||||
/* testUndefined = */ false,
|
||||
/* testReturn */ false,
|
||||
/* force */ true);
|
||||
finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
|
||||
} else {
|
||||
#ifdef JSGC_INCREMENTAL_MJ
|
||||
if (cx->compartment->needsBarrier()) {
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 452498;
|
||||
var summary = 'TM: upvar2 regression tests';
|
||||
var actual = '';
|
||||
var expect = '';
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test();
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test()
|
||||
{
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
// ------- Comment #108 From Jesse Ruderman
|
||||
|
||||
function p(){p}
|
||||
|
||||
expect = 'function p(){p;}';
|
||||
actual = p + '';
|
||||
|
||||
compareSource(expect, actual, summary);
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
|
@ -72,7 +72,7 @@ ArgumentsObject::element(uint32_t i) const
|
|||
JS_ASSERT(!isElementDeleted(i));
|
||||
const Value &v = data()->args[i];
|
||||
if (v.isMagic(JS_FORWARD_TO_CALL_OBJECT))
|
||||
return getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall().arg(i);
|
||||
return getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall().formal(i);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ ArgumentsObject::setElement(uint32_t i, const Value &v)
|
|||
JS_ASSERT(!isElementDeleted(i));
|
||||
HeapValue &lhs = data()->args[i];
|
||||
if (lhs.isMagic(JS_FORWARD_TO_CALL_OBJECT))
|
||||
getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall().setArg(i, v);
|
||||
getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall().setFormal(i, v);
|
||||
else
|
||||
lhs = v;
|
||||
}
|
||||
|
|
|
@ -3177,7 +3177,7 @@ DebuggerArguments_getArg(JSContext *cx, unsigned argc, Value *vp)
|
|||
Value arg;
|
||||
if (unsigned(i) < fp->numActualArgs()) {
|
||||
if (unsigned(i) < fp->numFormalArgs() && fp->script()->formalLivesInCallObject(i))
|
||||
arg = fp->callObj().arg(i);
|
||||
arg = fp->callObj().formal(i);
|
||||
else if (fp->script()->argsObjAliasesFormals() && fp->hasArgsObj())
|
||||
arg = fp->argsObj().arg(i);
|
||||
else
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include "ScopeObject.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
inline
|
||||
|
@ -72,14 +74,14 @@ CallObject::callee() const
|
|||
}
|
||||
|
||||
inline const Value &
|
||||
CallObject::arg(unsigned i, MaybeCheckAliasing checkAliasing) const
|
||||
CallObject::formal(unsigned i, MaybeCheckAliasing checkAliasing) const
|
||||
{
|
||||
JS_ASSERT_IF(checkAliasing, callee().script()->formalLivesInCallObject(i));
|
||||
return getSlot(RESERVED_SLOTS + i);
|
||||
}
|
||||
|
||||
inline void
|
||||
CallObject::setArg(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
|
||||
CallObject::setFormal(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
|
||||
{
|
||||
JS_ASSERT_IF(checkAliasing, callee().script()->formalLivesInCallObject(i));
|
||||
setSlot(RESERVED_SLOTS + i, v);
|
||||
|
|
|
@ -58,6 +58,14 @@ StaticScopeIter::hasDynamicScopeObject() const
|
|||
: obj->toFunction()->isHeavyweight();
|
||||
}
|
||||
|
||||
Shape *
|
||||
StaticScopeIter::scopeShape() const
|
||||
{
|
||||
JS_ASSERT(hasDynamicScopeObject());
|
||||
JS_ASSERT(type() != NAMED_LAMBDA);
|
||||
return type() == BLOCK ? block().lastProperty() : funScript()->bindings.lastShape();
|
||||
}
|
||||
|
||||
StaticScopeIter::Type
|
||||
StaticScopeIter::type() const
|
||||
{
|
||||
|
@ -82,63 +90,47 @@ StaticScopeIter::funScript() const
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
StaticBlockObject *
|
||||
js::ScopeCoordinateBlockChain(JSScript *script, jsbytecode *pc)
|
||||
StaticScopeIter
|
||||
js::ScopeCoordinateToStaticScope(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
ScopeCoordinate sc(pc);
|
||||
JS_ASSERT(pc >= script->code && pc < script->code + script->length);
|
||||
JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
|
||||
|
||||
uint32_t blockIndex = GET_UINT32_INDEX(pc + 2 * sizeof(uint16_t));
|
||||
JSObject *innermostStaticScope;
|
||||
if (blockIndex == UINT32_MAX)
|
||||
return NULL;
|
||||
innermostStaticScope = script->function();
|
||||
else
|
||||
innermostStaticScope = &script->getObject(blockIndex)->asStaticBlock();
|
||||
|
||||
StaticBlockObject *block = &script->getObject(blockIndex)->asStaticBlock();
|
||||
unsigned i = 0;
|
||||
StaticScopeIter ssi(innermostStaticScope);
|
||||
ScopeCoordinate sc(pc);
|
||||
while (true) {
|
||||
while (block && !block->needsClone())
|
||||
block = block->enclosingBlock();
|
||||
if (i++ == sc.hops)
|
||||
if (ssi.hasDynamicScopeObject()) {
|
||||
if (!sc.hops)
|
||||
break;
|
||||
block = block->enclosingBlock();
|
||||
sc.hops--;
|
||||
}
|
||||
return block;
|
||||
ssi++;
|
||||
}
|
||||
return ssi;
|
||||
}
|
||||
|
||||
PropertyName *
|
||||
js::ScopeCoordinateName(JSRuntime *rt, JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
StaticBlockObject *maybeBlock = ScopeCoordinateBlockChain(script, pc);
|
||||
Shape::Range r = ScopeCoordinateToStaticScope(script, pc).scopeShape()->all();
|
||||
ScopeCoordinate sc(pc);
|
||||
Shape *shape = maybeBlock ? maybeBlock->lastProperty() : script->bindings.lastShape();
|
||||
Shape::Range r = shape->all();
|
||||
while (r.front().slot() != sc.slot)
|
||||
r.popFront();
|
||||
jsid id = r.front().propid();
|
||||
|
||||
/* Beware nameless destructuring formal. */
|
||||
if (!JSID_IS_ATOM(id))
|
||||
return rt->atomState.emptyAtom;
|
||||
return JSID_TO_ATOM(id)->asPropertyName();
|
||||
}
|
||||
|
||||
FrameIndexType
|
||||
js::ScopeCoordinateToFrameIndex(JSScript *script, jsbytecode *pc, unsigned *index)
|
||||
{
|
||||
ScopeCoordinate sc(pc);
|
||||
if (StaticBlockObject *block = ScopeCoordinateBlockChain(script, pc)) {
|
||||
*index = block->slotToLocalIndex(script->bindings, sc.slot);
|
||||
return FrameIndex_Local;
|
||||
}
|
||||
|
||||
unsigned i = sc.slot - CallObject::RESERVED_SLOTS;
|
||||
if (i < script->bindings.numArgs()) {
|
||||
*index = i;
|
||||
return FrameIndex_Arg;
|
||||
}
|
||||
|
||||
*index = i - script->bindings.numArgs();
|
||||
JS_ASSERT(*index < script->bindings.numVars());
|
||||
return FrameIndex_Local;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -215,12 +207,12 @@ CallObject::createForFunction(JSContext *cx, StackFrame *fp)
|
|||
if (script->bindingsAccessedDynamically) {
|
||||
Value *formals = fp->formals();
|
||||
for (unsigned slot = 0, n = fp->fun()->nargs; slot < n; ++slot)
|
||||
callobj->setArg(slot, formals[slot]);
|
||||
callobj->setFormal(slot, formals[slot]);
|
||||
} else if (unsigned n = script->numClosedArgs()) {
|
||||
Value *formals = fp->formals();
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
uint32_t slot = script->getClosedArg(i);
|
||||
callobj->setArg(slot, formals[slot]);
|
||||
callobj->setFormal(slot, formals[slot]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,9 +233,9 @@ CallObject::copyUnaliasedValues(StackFrame *fp)
|
|||
for (unsigned i = 0; i < script->bindings.numArgs(); ++i) {
|
||||
if (!script->formalLivesInCallObject(i)) {
|
||||
if (script->argsObjAliasesFormals() && fp->hasArgsObj())
|
||||
setArg(i, fp->argsObj().arg(i), DONT_CHECK_ALIASING);
|
||||
setFormal(i, fp->argsObj().arg(i), DONT_CHECK_ALIASING);
|
||||
else
|
||||
setArg(i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING);
|
||||
setFormal(i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,40 +260,20 @@ CallObject::createForStrictEval(JSContext *cx, StackFrame *fp)
|
|||
JSBool
|
||||
CallObject::setArgOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, Value *vp)
|
||||
{
|
||||
CallObject &callobj = obj->asCall();
|
||||
|
||||
/* TODO: this can totally be a data property now. */
|
||||
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||
unsigned i = (uint16_t) JSID_TO_INT(id);
|
||||
|
||||
JSScript *script = callobj.callee().script();
|
||||
JS_ASSERT(script->formalLivesInCallObject(i));
|
||||
|
||||
callobj.setArg(i, *vp);
|
||||
|
||||
if (!script->ensureHasTypes(cx))
|
||||
return false;
|
||||
|
||||
TypeScript::SetArgument(cx, script, i, *vp);
|
||||
obj->asCall().setFormal(i, *vp);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
CallObject::setVarOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, Value *vp)
|
||||
{
|
||||
CallObject &callobj = obj->asCall();
|
||||
|
||||
/* TODO: this can totally be a data property now. */
|
||||
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||
unsigned i = (uint16_t) JSID_TO_INT(id);
|
||||
|
||||
JSScript *script = callobj.callee().script();
|
||||
JS_ASSERT(script->varIsAliased(i));
|
||||
|
||||
callobj.setVar(i, *vp);
|
||||
|
||||
if (!script->ensureHasTypes(cx))
|
||||
return false;
|
||||
|
||||
TypeScript::SetLocal(cx, script, i, *vp);
|
||||
obj->asCall().setVar(i, *vp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1193,9 +1165,9 @@ class DebugScopeProxy : public BaseProxyHandler
|
|||
}
|
||||
} else {
|
||||
if (action == GET)
|
||||
*vp = callobj.arg(i, DONT_CHECK_ALIASING);
|
||||
*vp = callobj.formal(i, DONT_CHECK_ALIASING);
|
||||
else
|
||||
callobj.setArg(i, *vp, DONT_CHECK_ALIASING);
|
||||
callobj.setFormal(i, *vp, DONT_CHECK_ALIASING);
|
||||
}
|
||||
|
||||
if (action == SET)
|
||||
|
|
|
@ -65,6 +65,7 @@ class StaticScopeIter
|
|||
|
||||
/* Return whether this static scope will be on the dynamic scope chain. */
|
||||
bool hasDynamicScopeObject() const;
|
||||
Shape *scopeShape() const;
|
||||
|
||||
enum Type { BLOCK, FUNCTION, NAMED_LAMBDA };
|
||||
Type type() const;
|
||||
|
@ -93,25 +94,17 @@ struct ScopeCoordinate
|
|||
inline ScopeCoordinate() {}
|
||||
};
|
||||
|
||||
/* Return the static block chain (or null) accessed by *pc. */
|
||||
extern StaticBlockObject *
|
||||
ScopeCoordinateBlockChain(JSScript *script, jsbytecode *pc);
|
||||
/*
|
||||
* Return a scope iterator pointing at the static scope containing the variable
|
||||
* accessed by the ALIASEDVAR op at 'pc'.
|
||||
*/
|
||||
extern StaticScopeIter
|
||||
ScopeCoordinateToStaticScope(JSScript *script, jsbytecode *pc);
|
||||
|
||||
/* Return the name being accessed by the given ALIASEDVAR op. */
|
||||
extern PropertyName *
|
||||
ScopeCoordinateName(JSRuntime *rt, JSScript *script, jsbytecode *pc);
|
||||
|
||||
/*
|
||||
* The 'slot' of a ScopeCoordinate is relative to the scope object. Type
|
||||
* inference and jit compilation are instead relative to frame values (even if
|
||||
* these values are aliased and thus never accessed, the the index of the
|
||||
* variable is used to refer to the jit/inference information). This function
|
||||
* maps from the ScopeCoordinate space to the StackFrame variable space.
|
||||
*/
|
||||
enum FrameIndexType { FrameIndex_Local, FrameIndex_Arg };
|
||||
extern FrameIndexType
|
||||
ScopeCoordinateToFrameIndex(JSScript *script, jsbytecode *pc, unsigned *index);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -201,14 +194,15 @@ class CallObject : public ScopeObject
|
|||
*/
|
||||
inline JSFunction &callee() const;
|
||||
|
||||
/* Returns the formal argument at the given index. */
|
||||
inline const Value &arg(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
|
||||
inline void setArg(unsigned i, const Value &v, MaybeCheckAliasing = CHECK_ALIASING);
|
||||
/* Get/set the formal argument at the given index. */
|
||||
inline const Value &formal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
|
||||
inline void setFormal(unsigned i, const Value &v, MaybeCheckAliasing = CHECK_ALIASING);
|
||||
|
||||
/* Returns the variable at the given index. */
|
||||
/* Get/set the variable at the given index. */
|
||||
inline const Value &var(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
|
||||
inline void setVar(unsigned i, const Value &v, MaybeCheckAliasing = CHECK_ALIASING);
|
||||
|
||||
/* Internal property ops for CallObject dynamic access via scope chain. */
|
||||
static JSBool setArgOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, Value *vp);
|
||||
static JSBool setVarOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, Value *vp);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче