mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 730497 - rm flat closures (r=bhackett,waldo)
--HG-- extra : rebase_source : cf704765ad227abced9e8804aaeb1dc8d12fc5a8
This commit is contained in:
parent
d5c7764bc8
commit
1843d7c72e
@ -110,8 +110,6 @@ BytecodeEmitter::BytecodeEmitter(Parser *parser, unsigned lineno)
|
||||
emitLevel(0),
|
||||
constMap(parser->context),
|
||||
constList(parser->context),
|
||||
upvarIndices(parser->context),
|
||||
upvarMap(parser->context),
|
||||
globalScope(NULL),
|
||||
globalUses(parser->context),
|
||||
globalMap(parser->context),
|
||||
@ -1367,79 +1365,8 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
JS_ASSERT(bce->staticLevel >= level);
|
||||
|
||||
const unsigned skip = bce->staticLevel - level;
|
||||
if (skip != 0) {
|
||||
JS_ASSERT(bce->inFunction());
|
||||
JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, bce->roLexdeps->lookup(atom));
|
||||
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
|
||||
|
||||
/*
|
||||
* If op is a mutating opcode, this upvar's lookup skips too many levels,
|
||||
* or the function is heavyweight, we fall back on JSOP_*NAME*.
|
||||
*/
|
||||
if (op != JSOP_NAME)
|
||||
return JS_TRUE;
|
||||
if (skip >= UpvarCookie::UPVAR_LEVEL_LIMIT)
|
||||
return JS_TRUE;
|
||||
if (bce->flags & TCF_FUN_HEAVYWEIGHT)
|
||||
return JS_TRUE;
|
||||
|
||||
if (!bce->fun()->isFlatClosure())
|
||||
return JS_TRUE;
|
||||
|
||||
if (!bce->upvarIndices.ensureMap(cx))
|
||||
return JS_FALSE;
|
||||
|
||||
AtomIndexAddPtr p = bce->upvarIndices->lookupForAdd(atom);
|
||||
jsatomid index;
|
||||
if (p) {
|
||||
index = p.value();
|
||||
} else {
|
||||
if (!bce->bindings.addUpvar(cx, atom))
|
||||
return JS_FALSE;
|
||||
|
||||
index = bce->upvarIndices->count();
|
||||
if (!bce->upvarIndices->add(p, atom, index))
|
||||
return JS_FALSE;
|
||||
|
||||
UpvarCookies &upvarMap = bce->upvarMap;
|
||||
/* upvarMap should have the same number of UpvarCookies as there are lexdeps. */
|
||||
size_t lexdepCount = bce->roLexdeps->count();
|
||||
|
||||
JS_ASSERT_IF(!upvarMap.empty(), lexdepCount == upvarMap.length());
|
||||
if (upvarMap.empty()) {
|
||||
/* Lazily initialize the upvar map with exactly the necessary capacity. */
|
||||
if (lexdepCount <= upvarMap.sMaxInlineStorage) {
|
||||
JS_ALWAYS_TRUE(upvarMap.growByUninitialized(lexdepCount));
|
||||
} else {
|
||||
void *buf = upvarMap.allocPolicy().malloc_(lexdepCount * sizeof(UpvarCookie));
|
||||
if (!buf)
|
||||
return JS_FALSE;
|
||||
upvarMap.replaceRawBuffer(static_cast<UpvarCookie *>(buf), lexdepCount);
|
||||
}
|
||||
for (size_t i = 0; i < lexdepCount; ++i)
|
||||
upvarMap[i] = UpvarCookie();
|
||||
}
|
||||
|
||||
unsigned slot = cookie.slot();
|
||||
if (slot != UpvarCookie::CALLEE_SLOT && dn_kind != Definition::ARG) {
|
||||
TreeContext *tc = bce;
|
||||
do {
|
||||
tc = tc->parent;
|
||||
} while (tc->staticLevel != level);
|
||||
if (tc->inFunction())
|
||||
slot += tc->fun()->nargs;
|
||||
}
|
||||
|
||||
JS_ASSERT(index < upvarMap.length());
|
||||
upvarMap[index].set(skip, slot);
|
||||
}
|
||||
|
||||
pn->setOp(JSOP_GETFCSLOT);
|
||||
JS_ASSERT((index & JS_BITMASK(16)) == index);
|
||||
pn->pn_cookie.set(0, index);
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
if (skip != 0)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are compiling a function body and may be able to optimize name
|
||||
@ -1834,9 +1761,6 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContex
|
||||
case JSOP_GETLOCAL:
|
||||
op = JSOP_CALLLOCAL;
|
||||
break;
|
||||
case JSOP_GETFCSLOT:
|
||||
op = JSOP_CALLFCSLOT;
|
||||
break;
|
||||
default:
|
||||
JS_ASSERT(op == JSOP_ARGUMENTS || op == JSOP_CALLEE);
|
||||
break;
|
||||
@ -4048,7 +3972,7 @@ EmitCatch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
case PNK_NAME:
|
||||
/* Inline and specialize BindNameToSlot for pn2. */
|
||||
JS_ASSERT(!pn2->pn_cookie.isFree());
|
||||
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_cookie.asInteger());
|
||||
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_cookie.slot());
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -5122,8 +5046,6 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return false;
|
||||
if (pn->pn_cookie.isFree()) {
|
||||
bce->switchToProlog();
|
||||
MOZ_ASSERT(!fun->isFlatClosure(),
|
||||
"global functions can't have upvars, so they are never flat");
|
||||
if (!EmitFunctionOp(cx, JSOP_DEFFUN, index, bce))
|
||||
return false;
|
||||
if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
|
||||
@ -5140,14 +5062,13 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
||||
JS_ASSERT(index < JS_BIT(20));
|
||||
pn->pn_index = index;
|
||||
JSOp op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
|
||||
if (pn->isClosed() &&
|
||||
!bce->callsEval() &&
|
||||
!bce->closedVars.append(pn->pn_cookie.slot()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return EmitSlotObjectOp(cx, op, slot, index, bce);
|
||||
return EmitSlotObjectOp(cx, JSOP_DEFLOCALFUN, slot, index, bce);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -5794,8 +5715,7 @@ EmitIncOrDec(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
if (Emit1(cx, bce, op) < 0)
|
||||
return false;
|
||||
} else if (!pn2->pn_cookie.isFree()) {
|
||||
jsatomid atomIndex = pn2->pn_cookie.asInteger();
|
||||
EMIT_UINT16_IMM_OP(op, atomIndex);
|
||||
EMIT_UINT16_IMM_OP(op, pn2->pn_cookie.slot());
|
||||
} else {
|
||||
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
|
||||
if (js_CodeSpec[op].format & (JOF_INC | JOF_DEC)) {
|
||||
|
@ -589,10 +589,6 @@ struct BytecodeEmitter : public TreeContext
|
||||
CGObjectList regexpList; /* list of emitted regexp that will be
|
||||
cloned during execution */
|
||||
|
||||
OwnedAtomIndexMapPtr upvarIndices; /* map of atoms to upvar indexes */
|
||||
|
||||
UpvarCookies upvarMap; /* indexed upvar slot locations */
|
||||
|
||||
GlobalScope *globalScope; /* frontend::CompileScript global scope, or null */
|
||||
|
||||
typedef Vector<GlobalSlotArray::Entry, 16> GlobalUseVector;
|
||||
@ -638,10 +634,6 @@ struct BytecodeEmitter : public TreeContext
|
||||
*/
|
||||
bool addGlobalUse(JSAtom *atom, uint32_t slot, UpvarCookie *cookie);
|
||||
|
||||
bool hasUpvarIndices() const {
|
||||
return upvarIndices.hasMap() && !upvarIndices->empty();
|
||||
}
|
||||
|
||||
bool compilingForEval() const { return !!(flags & TCF_COMPILE_FOR_EVAL); }
|
||||
JSVersion version() const { return parser->versionWithFlags(); }
|
||||
|
||||
|
@ -50,6 +50,50 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Indicates a location in the stack that an upvar value can be retrieved from
|
||||
* as a two tuple of (level, slot).
|
||||
*
|
||||
* Some existing client code uses the level value as a delta, or level "skip"
|
||||
* quantity. We could probably document that through use of more types at some
|
||||
* point in the future.
|
||||
*/
|
||||
class UpvarCookie
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
static const uint32_t FREE_VALUE = 0xfffffffful;
|
||||
|
||||
void checkInvariants() {
|
||||
JS_STATIC_ASSERT(sizeof(UpvarCookie) == sizeof(uint32_t));
|
||||
JS_STATIC_ASSERT(UPVAR_LEVEL_LIMIT < FREE_LEVEL);
|
||||
}
|
||||
|
||||
public:
|
||||
/*
|
||||
* All levels above-and-including FREE_LEVEL are reserved so that
|
||||
* FREE_VALUE can be used as a special value.
|
||||
*/
|
||||
static const uint16_t FREE_LEVEL = 0x3fff;
|
||||
|
||||
/*
|
||||
* If a function has a higher static level than this limit, we will not
|
||||
* optimize it using UPVAR opcodes.
|
||||
*/
|
||||
static const uint16_t UPVAR_LEVEL_LIMIT = 16;
|
||||
static const uint16_t CALLEE_SLOT = 0xffff;
|
||||
static bool isLevelReserved(uint16_t level) { return level >= FREE_LEVEL; }
|
||||
|
||||
bool isFree() const { return value == FREE_VALUE; }
|
||||
/* isFree check should be performed before using these accessors. */
|
||||
uint16_t level() const { JS_ASSERT(!isFree()); return uint16_t(value >> 16); }
|
||||
uint16_t slot() const { JS_ASSERT(!isFree()); return uint16_t(value); }
|
||||
|
||||
void set(const UpvarCookie &other) { set(other.level(), other.slot()); }
|
||||
void set(uint16_t newLevel, uint16_t newSlot) { value = (uint32_t(newLevel) << 16) | newSlot; }
|
||||
void makeFree() { set(0xffff, 0xffff); JS_ASSERT(isFree()); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Parsing builds a tree of nodes that directs code generation. This tree is
|
||||
* not a concrete syntax tree in all respects (for example, || and && are left
|
||||
|
@ -331,161 +331,6 @@ MarkFunArgs(JSContext *cx, FunctionBox *funbox, uint32_t functionCount)
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
MinBlockId(ParseNode *fn, uint32_t id)
|
||||
{
|
||||
if (fn->pn_blockid < id)
|
||||
return false;
|
||||
if (fn->isDefn()) {
|
||||
for (ParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
|
||||
if (pn->pn_blockid < id)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
CanFlattenUpvar(Definition *dn, FunctionBox *funbox, uint32_t tcflags)
|
||||
{
|
||||
/*
|
||||
* Consider the current function (the lambda, innermost below) using a var
|
||||
* x defined two static levels up:
|
||||
*
|
||||
* function f() {
|
||||
* // z = g();
|
||||
* var x = 42;
|
||||
* function g() {
|
||||
* return function () { return x; };
|
||||
* }
|
||||
* return g();
|
||||
* }
|
||||
*
|
||||
* So long as (1) the initialization in 'var x = 42' dominates all uses of
|
||||
* g and (2) x is not reassigned, it is safe to optimize the lambda to a
|
||||
* flat closure. Uncommenting the early call to g makes this optimization
|
||||
* unsafe (z could name a global setter that calls its argument).
|
||||
*/
|
||||
FunctionBox *afunbox = funbox;
|
||||
unsigned dnLevel = dn->frameLevel();
|
||||
|
||||
JS_ASSERT(dnLevel <= funbox->level);
|
||||
while (afunbox->level != dnLevel) {
|
||||
afunbox = afunbox->parent;
|
||||
|
||||
/*
|
||||
* NB: afunbox can't be null because we are sure to find a function box
|
||||
* whose level == dnLevel before we would try to walk above the root of
|
||||
* the funbox tree. See bug 493260 comments 16-18.
|
||||
*
|
||||
* Assert but check anyway, to protect future changes that bind eval
|
||||
* upvars in the parser.
|
||||
*/
|
||||
JS_ASSERT(afunbox);
|
||||
|
||||
/*
|
||||
* If this function is reaching up across an enclosing funarg, then we
|
||||
* cannot copy dn's value into a flat closure slot. The flat closure
|
||||
* code assumes the upvars to be copied are in frames still on the
|
||||
* stack.
|
||||
*/
|
||||
if (!afunbox || afunbox->node->isFunArg())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Reaching up for dn across a generator also means we can't flatten,
|
||||
* since the generator iterator does not run until later, in general.
|
||||
* See bug 563034.
|
||||
*/
|
||||
if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If afunbox's function (which is at the same level as dn) is in a loop,
|
||||
* pessimistically assume the variable initializer may be in the same loop.
|
||||
* A flat closure would then be unsafe, as the captured variable could be
|
||||
* assigned after the closure is created. See bug 493232.
|
||||
*/
|
||||
if (afunbox->inLoop)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* |with| and eval used as an operator defeat lexical scoping: they can be
|
||||
* used to assign to any in-scope variable. Therefore they must disable
|
||||
* flat closures that use such upvars. The parser detects these as special
|
||||
* forms and marks the function heavyweight.
|
||||
*/
|
||||
if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If afunbox's function is not a lambda, it will be hoisted, so it could
|
||||
* capture the undefined value that by default initializes var, let, and
|
||||
* const bindings. And if dn is a function that comes at (meaning a
|
||||
* function refers to its own name) or strictly after afunbox, we also
|
||||
* defeat the flat closure optimization for this dn.
|
||||
*/
|
||||
JSFunction *afun = afunbox->function();
|
||||
if (!(afun->flags & JSFUN_LAMBDA)) {
|
||||
if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dn->isInitialized())
|
||||
return false;
|
||||
|
||||
Definition::Kind dnKind = dn->kind();
|
||||
if (dnKind != Definition::CONST) {
|
||||
if (dn->isAssigned())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Any formal could be mutated behind our back via the arguments
|
||||
* object, so deoptimize if the outer function uses arguments.
|
||||
*
|
||||
* In a Function constructor call where the final argument -- the body
|
||||
* source for the function to create -- contains a nested function
|
||||
* definition or expression, afunbox->parent will be null. The body
|
||||
* source might use |arguments| outside of any nested functions it may
|
||||
* contain, so we have to check the tcflags parameter that was passed
|
||||
* in from js::frontend::CompileFunctionBody.
|
||||
*/
|
||||
if (dnKind == Definition::ARG &&
|
||||
((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check quick-and-dirty dominance relation. Function definitions dominate
|
||||
* their uses thanks to hoisting. Other binding forms hoist as undefined,
|
||||
* of course, so check forward-reference and blockid relations.
|
||||
*/
|
||||
if (dnKind != Definition::FUNCTION) {
|
||||
/*
|
||||
* Watch out for code such as
|
||||
*
|
||||
* (function () {
|
||||
* ...
|
||||
* var jQuery = ... = function (...) {
|
||||
* return new jQuery.foo.bar(baz);
|
||||
* }
|
||||
* ...
|
||||
* })();
|
||||
*
|
||||
* where the jQuery variable is not reassigned, but of course is not
|
||||
* initialized at the time that the would-be-flat closure containing
|
||||
* the jQuery upvar is formed.
|
||||
*/
|
||||
if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
|
||||
return false;
|
||||
if (!MinBlockId(afunbox->node, dn->pn_blockid))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags)
|
||||
{
|
||||
@ -539,70 +384,31 @@ SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool isDirectEval)
|
||||
JS_ASSERT(!fun->isNullClosure());
|
||||
} else {
|
||||
bool hasUpvars = false;
|
||||
bool canFlatten = true;
|
||||
|
||||
if (pn->isKind(PNK_UPVARS)) {
|
||||
AtomDefnMapPtr upvars = pn->pn_names;
|
||||
JS_ASSERT(!upvars->empty());
|
||||
|
||||
/*
|
||||
* For each lexical dependency from this closure to an outer
|
||||
* binding, analyze whether it is safe to copy the binding's
|
||||
* value into a flat closure slot when the closure is formed.
|
||||
*/
|
||||
/* Determine whether the this function contains upvars. */
|
||||
for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
|
||||
Definition *defn = r.front().value();
|
||||
Definition *lexdep = defn->resolve();
|
||||
|
||||
if (!lexdep->isFreeVar()) {
|
||||
if (!r.front().value()->resolve()->isFreeVar()) {
|
||||
hasUpvars = true;
|
||||
if (!CanFlattenUpvar(lexdep, funbox, *tcflags)) {
|
||||
/*
|
||||
* Can't flatten. Enclosing functions holding
|
||||
* variables used by this function will be flagged
|
||||
* heavyweight below. FIXME bug 545759: re-enable
|
||||
* partial flat closures.
|
||||
*/
|
||||
canFlatten = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Top-level functions, and (extension) functions not at top level
|
||||
* which are also not directly within other functions, aren't
|
||||
* flattened.
|
||||
*/
|
||||
if (fn->isOp(JSOP_DEFFUN))
|
||||
canFlatten = false;
|
||||
|
||||
if (!hasUpvars) {
|
||||
/* No lexical dependencies => null closure, for best performance. */
|
||||
fun->setKind(JSFUN_NULL_CLOSURE);
|
||||
} else if (canFlatten) {
|
||||
fun->setKind(JSFUN_FLAT_CLOSURE);
|
||||
switch (fn->getOp()) {
|
||||
case JSOP_DEFLOCALFUN:
|
||||
fn->setOp(JSOP_DEFLOCALFUN_FC);
|
||||
break;
|
||||
case JSOP_LAMBDA:
|
||||
fn->setOp(JSOP_LAMBDA_FC);
|
||||
break;
|
||||
default:
|
||||
/* js::frontend::EmitTree's PNK_FUNCTION case sets op. */
|
||||
JS_ASSERT(fn->isOp(JSOP_NOP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fun->kind() == JSFUN_INTERPRETED && pn->isKind(PNK_UPVARS)) {
|
||||
/*
|
||||
* One or more upvars cannot be safely snapshot into a flat
|
||||
* closure's non-reserved slot (see JSOP_GETFCSLOT), so we loop
|
||||
* again over all upvars, and for each non-free upvar, ensure that
|
||||
* its containing function has been flagged as heavyweight.
|
||||
* We loop again over all upvars, and for each non-free upvar,
|
||||
* ensure that its containing function has been flagged as
|
||||
* heavyweight.
|
||||
*
|
||||
* The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
|
||||
* generating any code for a tree of nested functions.
|
||||
|
50
js/src/jit-test/tests/basic/testReplaceWithLambda.js
Normal file
50
js/src/jit-test/tests/basic/testReplaceWithLambda.js
Normal file
@ -0,0 +1,50 @@
|
||||
// optimized
|
||||
(function(b) {
|
||||
assertEq("abc".replace(/a|b/g, function(a) { return b[a] }), 'ABc');
|
||||
})({a:'A', b:'B' });
|
||||
(function() {
|
||||
var b = {a:'A', b:'B' };
|
||||
assertEq("abc".replace(/a|b/g, function(a) { return b[a] }), 'ABc');
|
||||
})();
|
||||
(function() {
|
||||
let (b = {a:'A', b:'B' }) {
|
||||
assertEq("abc".replace(/a|b/g, function(a) { return b[a] }), 'ABc');
|
||||
}
|
||||
})();
|
||||
(function() {
|
||||
var b = {a:'A', b:'B' };
|
||||
(function () {
|
||||
assertEq("abc".replace(/a|b/g, function(a) { return b[a] }), 'ABc');
|
||||
})();
|
||||
})();
|
||||
(function() {
|
||||
let (b = {a:'A', b:'B' }) {
|
||||
(function () {
|
||||
assertEq("abc".replace(/a|b/g, function(a) { return b[a] }), 'ABc');
|
||||
})();
|
||||
}
|
||||
})();
|
||||
(function() {
|
||||
var b = {a:'A', b:'B' };
|
||||
(function () {
|
||||
(function () {
|
||||
assertEq("abc".replace(/a|b/g, function(a) { return b[a] }), 'ABc');
|
||||
})();
|
||||
})();
|
||||
})();
|
||||
|
||||
// not optimized:
|
||||
(function() {
|
||||
var b = {a:'A', b:'B' };
|
||||
with ({}) {
|
||||
(function () {
|
||||
assertEq("abc".replace(/a|b/g, function(a) { return b[a] }), 'ABc');
|
||||
})();
|
||||
}
|
||||
})();
|
||||
(function() {
|
||||
var b = {a:'A', b:'B' };
|
||||
var bad = function() { b = {a:1, b:2}; return 'X' }
|
||||
Object.defineProperty(b, 'x', {get:bad});
|
||||
assertEq("xabc".replace(/x|a|b/g, function(a) { return b[a] }), 'X12c');
|
||||
})();
|
@ -510,11 +510,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
||||
case JSOP_THROW:
|
||||
case JSOP_EXCEPTION:
|
||||
case JSOP_DEFLOCALFUN:
|
||||
case JSOP_DEFLOCALFUN_FC:
|
||||
case JSOP_LAMBDA:
|
||||
case JSOP_LAMBDA_FC:
|
||||
case JSOP_GETFCSLOT:
|
||||
case JSOP_CALLFCSLOT:
|
||||
case JSOP_DEBUGGER:
|
||||
case JSOP_FUNCALL:
|
||||
case JSOP_FUNAPPLY:
|
||||
@ -790,8 +786,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx)
|
||||
case JSOP_SETARG:
|
||||
case JSOP_SETLOCAL:
|
||||
case JSOP_SETLOCALPOP:
|
||||
case JSOP_DEFLOCALFUN:
|
||||
case JSOP_DEFLOCALFUN_FC: {
|
||||
case JSOP_DEFLOCALFUN: {
|
||||
uint32_t slot = GetBytecodeSlot(script, pc);
|
||||
if (!slotEscapes(slot))
|
||||
killVariable(cx, lifetimes[slot], offset, saved, savedCount);
|
||||
|
@ -255,7 +255,6 @@ ExtendedDef(jsbytecode *pc)
|
||||
case JSOP_SETLOCAL:
|
||||
case JSOP_SETLOCALPOP:
|
||||
case JSOP_DEFLOCALFUN:
|
||||
case JSOP_DEFLOCALFUN_FC:
|
||||
case JSOP_INCLOCAL:
|
||||
case JSOP_DECLOCAL:
|
||||
case JSOP_LOCALINC:
|
||||
@ -385,7 +384,6 @@ static inline uint32_t GetBytecodeSlot(JSScript *script, jsbytecode *pc)
|
||||
case JSOP_SETLOCAL:
|
||||
case JSOP_SETLOCALPOP:
|
||||
case JSOP_DEFLOCALFUN:
|
||||
case JSOP_DEFLOCALFUN_FC:
|
||||
case JSOP_INCLOCAL:
|
||||
case JSOP_DECLOCAL:
|
||||
case JSOP_LOCALINC:
|
||||
@ -410,7 +408,6 @@ BytecodeUpdatesSlot(JSOp op)
|
||||
case JSOP_SETLOCAL:
|
||||
case JSOP_SETLOCALPOP:
|
||||
case JSOP_DEFLOCALFUN:
|
||||
case JSOP_DEFLOCALFUN_FC:
|
||||
case JSOP_INCARG:
|
||||
case JSOP_DECARG:
|
||||
case JSOP_ARGINC:
|
||||
|
@ -4557,57 +4557,14 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
||||
}
|
||||
|
||||
JSFunction *fun = funobj->toFunction();
|
||||
if (!fun->isInterpreted())
|
||||
return CloneFunctionObject(cx, fun, parent, fun->getAllocKind());
|
||||
|
||||
if (fun->script()->compileAndGo) {
|
||||
if (fun->isInterpreted() && fun->script()->compileAndGo) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!fun->isFlatClosure())
|
||||
return CloneFunctionObject(cx, fun, parent, fun->getAllocKind());
|
||||
|
||||
/*
|
||||
* A flat closure carries its own environment, so why clone it? In case
|
||||
* someone wants to mutate its fixed slots or add ad-hoc properties. API
|
||||
* compatibility suggests we not return funobj and let callers mutate the
|
||||
* returned object at will.
|
||||
*
|
||||
* But it's worse than that: API compatibility according to the test for
|
||||
* bug 300079 requires we get "upvars" from parent and its ancestors! So
|
||||
* we do that (grudgingly!). The scope chain ancestors are searched as if
|
||||
* they were activations, respecting the skip field in each upvar's cookie
|
||||
* but looking up the property by name instead of frame slot.
|
||||
*/
|
||||
JSObject *clone = js_AllocFlatClosure(cx, fun, parent);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
JSUpvarArray *uva = fun->script()->upvars();
|
||||
uint32_t i = uva->length;
|
||||
JS_ASSERT(i != 0);
|
||||
|
||||
for (Shape::Range r(fun->script()->bindings.lastUpvar()); i-- != 0; r.popFront()) {
|
||||
JSObject *obj = parent;
|
||||
int skip = uva->vector[i].level();
|
||||
while (--skip > 0) {
|
||||
if (!obj) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
|
||||
return NULL;
|
||||
}
|
||||
obj = obj->enclosingScope();
|
||||
}
|
||||
|
||||
Value v;
|
||||
if (!obj->getGeneric(cx, r.front().propid(), &v))
|
||||
return NULL;
|
||||
clone->toFunction()->setFlatClosureUpvar(i, v);
|
||||
}
|
||||
|
||||
return clone;
|
||||
return CloneFunctionObject(cx, fun, parent, fun->getAllocKind());
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
|
@ -1086,11 +1086,6 @@ fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
|
||||
inline void
|
||||
JSFunction::trace(JSTracer *trc)
|
||||
{
|
||||
if (isFlatClosure() && hasFlatClosureUpvars()) {
|
||||
if (HeapValue *upvars = getFlatClosureUpvars())
|
||||
MarkValueRange(trc, script()->bindings.countUpvars(), upvars, "upvars");
|
||||
}
|
||||
|
||||
if (isExtended()) {
|
||||
MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots),
|
||||
toExtended()->extendedSlots, "nativeReserved");
|
||||
@ -1113,21 +1108,6 @@ fun_trace(JSTracer *trc, JSObject *obj)
|
||||
obj->toFunction()->trace(trc);
|
||||
}
|
||||
|
||||
static void
|
||||
fun_finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
if (obj->toFunction()->isFlatClosure())
|
||||
obj->toFunction()->finalizeUpvars();
|
||||
}
|
||||
|
||||
size_t
|
||||
JSFunction::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const
|
||||
{
|
||||
return (isFlatClosure() && hasFlatClosureUpvars()) ?
|
||||
mallocSizeOf(getFlatClosureUpvars()) :
|
||||
0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reserve two slots in all function objects for XPConnect. Note that this
|
||||
* does not bloat every instance, only those on which reserved slots are set,
|
||||
@ -1144,7 +1124,7 @@ JS_FRIEND_DATA(Class) js::FunctionClass = {
|
||||
fun_enumerate,
|
||||
(JSResolveOp)fun_resolve,
|
||||
JS_ConvertStub,
|
||||
fun_finalize,
|
||||
NULL, /* finalize */
|
||||
NULL, /* checkAccess */
|
||||
NULL, /* call */
|
||||
NULL, /* construct */
|
||||
@ -1881,59 +1861,6 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
|
||||
return clone;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new flat closure, but don't initialize the imported upvar
|
||||
* values. The tracer calls this function and then initializes the upvar
|
||||
* slots on trace.
|
||||
*/
|
||||
JSFunction * JS_FASTCALL
|
||||
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
|
||||
{
|
||||
JS_ASSERT(fun->isFlatClosure());
|
||||
JS_ASSERT(JSScript::isValidOffset(fun->script()->upvarsOffset) ==
|
||||
fun->script()->bindings.hasUpvars());
|
||||
JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset),
|
||||
fun->script()->upvars()->length == fun->script()->bindings.countUpvars());
|
||||
|
||||
JSFunction *closure = CloneFunctionObject(cx, fun, scopeChain, JSFunction::ExtendedFinalizeKind);
|
||||
if (!closure)
|
||||
return closure;
|
||||
|
||||
uint32_t nslots = fun->script()->bindings.countUpvars();
|
||||
if (nslots == 0)
|
||||
return closure;
|
||||
|
||||
HeapValue *data = (HeapValue *) cx->malloc_(nslots * sizeof(HeapValue));
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
closure->setExtendedSlot(JSFunction::FLAT_CLOSURE_UPVARS_SLOT, PrivateValue(data));
|
||||
return closure;
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
/*
|
||||
* Flat closures cannot yet be partial, that is, all upvars must be copied,
|
||||
* or the closure won't be flattened. Therefore they do not need to search
|
||||
* enclosing scope objects via JSOP_NAME, etc.
|
||||
*/
|
||||
JSObject *scopeChain = &cx->fp()->scopeChain();
|
||||
|
||||
JSFunction *closure = js_AllocFlatClosure(cx, fun, scopeChain);
|
||||
if (!closure || !fun->script()->bindings.hasUpvars())
|
||||
return closure;
|
||||
|
||||
unsigned level = fun->script()->staticLevel;
|
||||
JSUpvarArray *uva = fun->script()->upvars();
|
||||
|
||||
for (uint32_t i = 0, n = uva->length; i < n; i++)
|
||||
closure->initFlatClosureUpvar(i, GetUpvar(cx, level, uva->vector[i]));
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
js_DefineFunction(JSContext *cx, HandleObject obj, jsid id, Native native,
|
||||
unsigned nargs, unsigned attrs, AllocKind kind)
|
||||
|
@ -57,27 +57,9 @@
|
||||
* any) it might be.
|
||||
*
|
||||
* 00 not interpreted
|
||||
* 01 interpreted, neither flat nor null closure
|
||||
* 10 interpreted, flat closure
|
||||
* 01 interpreted, not null closure
|
||||
* 11 interpreted, null closure
|
||||
*
|
||||
* isFlatClosure() implies isInterpreted() and u.i.script->upvarsOffset != 0.
|
||||
* isNullClosure() implies isInterpreted() and u.i.script->upvarsOffset == 0.
|
||||
*
|
||||
* isInterpreted() but not isFlatClosure() and u.i.script->upvarsOffset != 0
|
||||
* is an Algol-like function expression or nested function, i.e., a function
|
||||
* that never escapes upward or downward (heapward), and is only ever called.
|
||||
*
|
||||
* Finally, isInterpreted() and u.i.script->upvarsOffset == 0 could be either
|
||||
* a non-closure (a global function definition, or any function that uses no
|
||||
* outer names), or a closure of an escaping function that uses outer names
|
||||
* whose values can't be snapshot (because the outer names could be reassigned
|
||||
* after the closure is formed, or because assignments could not be analyzed
|
||||
* due to with or eval).
|
||||
*
|
||||
* Such a hard-case function must use JSOP_NAME, etc., and reify outer function
|
||||
* activations' call objects, etc. if it's not a global function.
|
||||
*
|
||||
* NB: JSFUN_EXPR_CLOSURE reuses JSFUN_STUB_GSOPS, which is an API request flag
|
||||
* bit only, never stored in fun->flags.
|
||||
*
|
||||
@ -95,8 +77,7 @@
|
||||
#define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */
|
||||
#define JSFUN_EXTENDED 0x2000 /* structure is FunctionExtended */
|
||||
#define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */
|
||||
#define JSFUN_FLAT_CLOSURE 0x8000 /* flat (aka "display") closure */
|
||||
#define JSFUN_NULL_CLOSURE 0xc000 /* null closure entrains no scope chain */
|
||||
#define JSFUN_NULL_CLOSURE 0x8000 /* null closure entrains no scope chain */
|
||||
#define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure
|
||||
optimization level -- see above */
|
||||
|
||||
@ -129,7 +110,6 @@ struct JSFunction : public JSObject
|
||||
bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
|
||||
bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
|
||||
bool isNullClosure() const { return kind() == JSFUN_NULL_CLOSURE; }
|
||||
bool isFlatClosure() const { return kind() == JSFUN_FLAT_CLOSURE; }
|
||||
bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; }
|
||||
bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype(); }
|
||||
|
||||
@ -154,7 +134,7 @@ struct JSFunction : public JSObject
|
||||
#define JS_LOCAL_NAME_IS_CONST(nameWord) ((((nameWord) & uintptr_t(1))) != 0)
|
||||
|
||||
bool mightEscape() const {
|
||||
return isInterpreted() && (isFlatClosure() || !script()->bindings.hasUpvars());
|
||||
return isInterpreted() && isNullClosure();
|
||||
}
|
||||
|
||||
bool joinable() const {
|
||||
@ -211,11 +191,21 @@ struct JSFunction : public JSObject
|
||||
}
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
# ifdef JS_THREADSAFE
|
||||
static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2_BACKGROUND;
|
||||
static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
|
||||
# else
|
||||
static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2;
|
||||
static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4;
|
||||
# endif
|
||||
#else
|
||||
# ifdef JS_THREADSAFE
|
||||
static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
|
||||
static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8_BACKGROUND;
|
||||
# else
|
||||
static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4;
|
||||
static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
inline void trace(JSTracer *trc);
|
||||
@ -248,27 +238,6 @@ struct JSFunction : public JSObject
|
||||
inline void setExtendedSlot(size_t which, const js::Value &val);
|
||||
inline const js::Value &getExtendedSlot(size_t which) const;
|
||||
|
||||
/*
|
||||
* Flat closures with one or more upvars snapshot the upvars' values
|
||||
* into a vector of js::Values referenced from here. This is a private
|
||||
* pointer but is set only at creation and does not need to be barriered.
|
||||
*/
|
||||
static const uint32_t FLAT_CLOSURE_UPVARS_SLOT = 0;
|
||||
|
||||
static inline size_t getFlatClosureUpvarsOffset();
|
||||
|
||||
inline js::Value getFlatClosureUpvar(uint32_t i) const;
|
||||
inline void setFlatClosureUpvar(uint32_t i, const js::Value &v);
|
||||
inline void initFlatClosureUpvar(uint32_t i, const js::Value &v);
|
||||
|
||||
private:
|
||||
inline bool hasFlatClosureUpvars() const;
|
||||
inline js::HeapValue *getFlatClosureUpvars() const;
|
||||
public:
|
||||
|
||||
/* See comments in fun_finalize. */
|
||||
inline void finalizeUpvars();
|
||||
|
||||
/* Slot holding associated method property, needed for foo.caller handling. */
|
||||
static const uint32_t METHOD_PROPERTY_SLOT = 0;
|
||||
|
||||
@ -284,18 +253,12 @@ struct JSFunction : public JSObject
|
||||
|
||||
/*
|
||||
* Method name imputed from property uniquely assigned to or initialized,
|
||||
* where the function does not need to be cloned to carry a scope chain or
|
||||
* flattened upvars. This is set on both the original and cloned function.
|
||||
* where the function does not need to be cloned to carry a scope chain.
|
||||
* This is set on both the original and cloned function.
|
||||
*/
|
||||
inline JSAtom *methodAtom() const;
|
||||
inline void setMethodAtom(JSAtom *atom);
|
||||
|
||||
/*
|
||||
* Measures things hanging off this JSFunction that are counted by the
|
||||
* |miscSize| argument in JSObject::sizeOfExcludingThis().
|
||||
*/
|
||||
size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const;
|
||||
|
||||
private:
|
||||
/*
|
||||
* These member functions are inherited from JSObject, but should never be applied to
|
||||
@ -331,12 +294,6 @@ extern JSFunction * JS_FASTCALL
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
|
||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||
|
||||
extern JSFunction * JS_FASTCALL
|
||||
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
|
||||
|
||||
extern JSFunction *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSFunction *
|
||||
js_DefineFunction(JSContext *cx, js::HandleObject obj, jsid id, JSNative native,
|
||||
unsigned nargs, unsigned flags,
|
||||
|
@ -140,74 +140,6 @@ JSFunction::getExtendedSlot(size_t which) const
|
||||
return toExtended()->extendedSlots[which];
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSFunction::hasFlatClosureUpvars() const
|
||||
{
|
||||
JS_ASSERT(isFlatClosure());
|
||||
return isExtended() && !getExtendedSlot(FLAT_CLOSURE_UPVARS_SLOT).isUndefined();
|
||||
}
|
||||
|
||||
inline js::HeapValue *
|
||||
JSFunction::getFlatClosureUpvars() const
|
||||
{
|
||||
JS_ASSERT(hasFlatClosureUpvars());
|
||||
return (js::HeapValue *) getExtendedSlot(FLAT_CLOSURE_UPVARS_SLOT).toPrivate();
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::finalizeUpvars()
|
||||
{
|
||||
/*
|
||||
* Cloned function objects may be flat closures with upvars to free.
|
||||
*
|
||||
* We must not access JSScript here that is stored in JSFunction. The
|
||||
* script can be finalized before the function or closure instances. So we
|
||||
* just check if JSSLOT_FLAT_CLOSURE_UPVARS holds a private value encoded
|
||||
* as a double. We must also ignore newborn closures that do not have the
|
||||
* private pointer set.
|
||||
*
|
||||
* FIXME bug 648320 - allocate upvars on the GC heap to avoid doing it
|
||||
* here explicitly.
|
||||
*/
|
||||
if (hasFlatClosureUpvars()) {
|
||||
js::HeapValue *upvars = getFlatClosureUpvars();
|
||||
js::Foreground::free_(upvars);
|
||||
}
|
||||
}
|
||||
|
||||
inline js::Value
|
||||
JSFunction::getFlatClosureUpvar(uint32_t i) const
|
||||
{
|
||||
JS_ASSERT(hasFlatClosureUpvars());
|
||||
JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length);
|
||||
JS_ASSERT(i < script()->bindings.countUpvars());
|
||||
return getFlatClosureUpvars()[i];
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::setFlatClosureUpvar(uint32_t i, const js::Value &v)
|
||||
{
|
||||
JS_ASSERT(isFlatClosure());
|
||||
JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length);
|
||||
JS_ASSERT(i < script()->bindings.countUpvars());
|
||||
getFlatClosureUpvars()[i] = v;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::initFlatClosureUpvar(uint32_t i, const js::Value &v)
|
||||
{
|
||||
JS_ASSERT(isFlatClosure());
|
||||
JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length);
|
||||
JS_ASSERT(i < script()->bindings.countUpvars());
|
||||
getFlatClosureUpvars()[i].init(v);
|
||||
}
|
||||
|
||||
/* static */ inline size_t
|
||||
JSFunction::getFlatClosureUpvarsOffset()
|
||||
{
|
||||
return offsetof(js::FunctionExtended, extendedSlots[FLAT_CLOSURE_UPVARS_SLOT]);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
|
@ -3629,14 +3629,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
|
||||
break;
|
||||
|
||||
case JSOP_GETXPROP:
|
||||
case JSOP_GETFCSLOT:
|
||||
case JSOP_CALLFCSLOT: {
|
||||
case JSOP_GETXPROP: {
|
||||
TypeSet *seen = bytecodeTypes(pc);
|
||||
addTypeBarrier(cx, pc, seen, Type::UnknownType());
|
||||
seen->addSubset(cx, &pushed[0]);
|
||||
if (op == JSOP_CALLFCSLOT)
|
||||
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3803,17 +3799,15 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
break;
|
||||
|
||||
case JSOP_LAMBDA:
|
||||
case JSOP_LAMBDA_FC:
|
||||
case JSOP_DEFFUN:
|
||||
case JSOP_DEFLOCALFUN:
|
||||
case JSOP_DEFLOCALFUN_FC: {
|
||||
unsigned off = (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) ? SLOTNO_LEN : 0;
|
||||
case JSOP_DEFLOCALFUN: {
|
||||
unsigned off = op == JSOP_DEFLOCALFUN ? SLOTNO_LEN : 0;
|
||||
JSObject *obj = script->getObject(GET_UINT32_INDEX(pc + off));
|
||||
|
||||
TypeSet *res = NULL;
|
||||
if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC) {
|
||||
if (op == JSOP_LAMBDA) {
|
||||
res = &pushed[0];
|
||||
} else if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) {
|
||||
} else if (op == JSOP_DEFLOCALFUN) {
|
||||
uint32_t slot = GetBytecodeSlot(script, pc);
|
||||
if (trackSlot(slot)) {
|
||||
res = &pushed[0];
|
||||
|
@ -1093,45 +1093,6 @@ DoIncDec(JSContext *cx, JSScript *script, jsbytecode *pc, const Value &v, Value
|
||||
return true;
|
||||
}
|
||||
|
||||
const Value &
|
||||
js::GetUpvar(JSContext *cx, unsigned closureLevel, UpvarCookie cookie)
|
||||
{
|
||||
JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
|
||||
const unsigned targetLevel = closureLevel - cookie.level();
|
||||
|
||||
StackFrame *fp = FindUpvarFrame(cx, targetLevel);
|
||||
unsigned slot = cookie.slot();
|
||||
const Value *vp;
|
||||
|
||||
if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
|
||||
vp = fp->slots() + fp->numFixed();
|
||||
} else if (slot < fp->numFormalArgs()) {
|
||||
vp = fp->formalArgs();
|
||||
} else if (slot == UpvarCookie::CALLEE_SLOT) {
|
||||
vp = &fp->calleev();
|
||||
slot = 0;
|
||||
} else {
|
||||
slot -= fp->numFormalArgs();
|
||||
JS_ASSERT(slot < fp->numSlots());
|
||||
vp = fp->slots();
|
||||
}
|
||||
|
||||
return vp[slot];
|
||||
}
|
||||
|
||||
extern StackFrame *
|
||||
js::FindUpvarFrame(JSContext *cx, unsigned targetLevel)
|
||||
{
|
||||
StackFrame *fp = cx->fp();
|
||||
while (true) {
|
||||
JS_ASSERT(fp && fp->isScriptFrame());
|
||||
if (fp->script()->staticLevel == targetLevel)
|
||||
break;
|
||||
fp = fp->prev();
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
#define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
|
||||
#define PUSH_COPY_SKIP_CHECK(v) *regs.sp++ = v
|
||||
#define PUSH_NULL() regs.sp++->setNull()
|
||||
@ -1249,7 +1210,6 @@ js::AssertValidPropertyCacheHit(JSContext *cx,
|
||||
* same way as non-call bytecodes.
|
||||
*/
|
||||
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
|
||||
@ -1750,6 +1710,10 @@ ADD_EMPTY_CASE(JSOP_UNUSED20)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED21)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED22)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED23)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED24)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED25)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED26)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED27)
|
||||
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
|
||||
ADD_EMPTY_CASE(JSOP_TRY)
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
@ -3056,18 +3020,6 @@ BEGIN_CASE(JSOP_SETLOCAL)
|
||||
regs.fp()->localSlot(GET_SLOTNO(regs.pc)) = regs.sp[-1];
|
||||
END_CASE(JSOP_SETLOCAL)
|
||||
|
||||
BEGIN_CASE(JSOP_GETFCSLOT)
|
||||
BEGIN_CASE(JSOP_CALLFCSLOT)
|
||||
{
|
||||
JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
|
||||
unsigned index = GET_UINT16(regs.pc);
|
||||
JSObject *obj = &argv[-2].toObject();
|
||||
|
||||
PUSH_COPY(obj->toFunction()->getFlatClosureUpvar(index));
|
||||
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
|
||||
}
|
||||
END_CASE(JSOP_GETFCSLOT)
|
||||
|
||||
BEGIN_CASE(JSOP_DEFCONST)
|
||||
BEGIN_CASE(JSOP_DEFVAR)
|
||||
{
|
||||
@ -3108,8 +3060,6 @@ BEGIN_CASE(JSOP_DEFFUN)
|
||||
*/
|
||||
obj2 = ®s.fp()->scopeChain();
|
||||
} else {
|
||||
JS_ASSERT(!fun->isFlatClosure());
|
||||
|
||||
obj2 = GetScopeChain(cx, regs.fp());
|
||||
if (!obj2)
|
||||
goto error;
|
||||
@ -3214,7 +3164,6 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
|
||||
*/
|
||||
JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN));
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->isFlatClosure());
|
||||
|
||||
JSObject *parent;
|
||||
if (fun->isNullClosure()) {
|
||||
@ -3234,18 +3183,6 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
|
||||
}
|
||||
END_CASE(JSOP_DEFLOCALFUN)
|
||||
|
||||
BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
|
||||
{
|
||||
JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN));
|
||||
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
regs.fp()->varSlot(GET_SLOTNO(regs.pc)) = ObjectValue(*obj);
|
||||
}
|
||||
END_CASE(JSOP_DEFLOCALFUN_FC)
|
||||
|
||||
BEGIN_CASE(JSOP_LAMBDA)
|
||||
{
|
||||
/* Load the specified function object literal. */
|
||||
@ -3342,19 +3279,6 @@ BEGIN_CASE(JSOP_LAMBDA)
|
||||
}
|
||||
END_CASE(JSOP_LAMBDA)
|
||||
|
||||
BEGIN_CASE(JSOP_LAMBDA_FC)
|
||||
{
|
||||
JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc));
|
||||
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
|
||||
|
||||
PUSH_OBJECT(*obj);
|
||||
}
|
||||
END_CASE(JSOP_LAMBDA_FC)
|
||||
|
||||
BEGIN_CASE(JSOP_CALLEE)
|
||||
JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
|
||||
PUSH_COPY(argv[-2]);
|
||||
|
@ -263,20 +263,6 @@ HasInstance(JSContext *cx, JSObject *obj, const js::Value *v, JSBool *bp);
|
||||
extern bool
|
||||
ValueToId(JSContext *cx, const Value &v, jsid *idp);
|
||||
|
||||
/*
|
||||
* @param closureLevel The static level of the closure that the cookie
|
||||
* pertains to.
|
||||
* @param cookie Level amount is a "skip" (delta) value from the
|
||||
* closure level.
|
||||
* @return The value of the upvar.
|
||||
*/
|
||||
extern const Value &
|
||||
GetUpvar(JSContext *cx, unsigned level, UpvarCookie cookie);
|
||||
|
||||
/* Search the call stack for the nearest frame with static level targetLevel. */
|
||||
extern StackFrame *
|
||||
FindUpvarFrame(JSContext *cx, unsigned targetLevel);
|
||||
|
||||
/*
|
||||
* A linked list of the |FrameRegs regs;| variables belonging to all
|
||||
* js::Interpret C++ frames on this thread's stack.
|
||||
|
@ -5751,9 +5751,10 @@ js_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBoo
|
||||
namespace js {
|
||||
|
||||
bool
|
||||
HasDataProperty(JSContext *cx, JSObject *obj, jsid methodid, Value *vp)
|
||||
HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
{
|
||||
if (const Shape *shape = obj->nativeLookup(cx, methodid)) {
|
||||
JS_ASSERT(id == js_CheckForStringIndex(id));
|
||||
if (const Shape *shape = obj->nativeLookup(cx, id)) {
|
||||
if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
|
||||
*vp = obj->nativeGetSlot(shape->slot());
|
||||
return true;
|
||||
|
@ -1665,11 +1665,18 @@ js_SetNativeAttributes(JSContext *cx, JSObject *obj, js::Shape *shape,
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* If obj has an already-resolved data property for methodid, return true and
|
||||
* store the property value in *vp.
|
||||
* If obj has an already-resolved data property for id, return true and
|
||||
* store the property value in *vp. This helper assumes the caller has already
|
||||
* called js_CheckForStringIndex.
|
||||
*/
|
||||
extern bool
|
||||
HasDataProperty(JSContext *cx, JSObject *obj, jsid methodid, js::Value *vp);
|
||||
HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp);
|
||||
|
||||
inline bool
|
||||
HasDataProperty(JSContext *cx, JSObject *obj, JSAtom *atom, Value *vp)
|
||||
{
|
||||
return HasDataProperty(cx, obj, js_CheckForStringIndex(ATOM_TO_JSID(atom)), vp);
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
|
||||
|
@ -1242,9 +1242,7 @@ JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf,
|
||||
|
||||
/* Other things may be measured in the future if DMD indicates it is worthwhile. */
|
||||
*miscSize = 0;
|
||||
if (isFunction()) {
|
||||
*miscSize += toFunction()->sizeOfMisc(mallocSizeOf);
|
||||
} else if (isArguments()) {
|
||||
if (isArguments()) {
|
||||
*miscSize += asArguments().sizeOfMisc(mallocSizeOf);
|
||||
} else if (isRegExpStatics()) {
|
||||
*miscSize += js::SizeOfRegExpStaticsData(this, mallocSizeOf);
|
||||
|
@ -3513,55 +3513,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, int nb)
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_GETFCSLOT:
|
||||
case JSOP_CALLFCSLOT:
|
||||
{
|
||||
if (!jp->fun)
|
||||
jp->fun = jp->script->getCallerFunction();
|
||||
|
||||
if (!jp->localNames) {
|
||||
JS_ASSERT(fun == jp->fun);
|
||||
jp->localNames = cx->new_<Vector<JSAtom *> >(cx);
|
||||
if (!jp->localNames ||
|
||||
!jp->fun->script()->bindings.getLocalNameArray(cx, jp->localNames))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned index = GET_UINT16(pc);
|
||||
if (index < jp->fun->script()->bindings.countUpvars()) {
|
||||
index += jp->fun->script()->bindings.countArgsAndVars();
|
||||
} else {
|
||||
JSUpvarArray *uva;
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* We must be in an eval called from jp->fun, where
|
||||
* jp->script is the eval-compiled script.
|
||||
*
|
||||
* However, it's possible that a js_Invoke already
|
||||
* pushed a frame trying to call Construct on an
|
||||
* object that's not a constructor, causing us to be
|
||||
* called with an intervening frame on the stack.
|
||||
*/
|
||||
StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
|
||||
if (fp) {
|
||||
while (!fp->isEvalFrame())
|
||||
fp = fp->prev();
|
||||
JS_ASSERT(fp->script() == jp->script);
|
||||
JS_ASSERT(fp->prev()->fun() == jp->fun);
|
||||
JS_ASSERT(jp->fun->isInterpreted());
|
||||
JS_ASSERT(jp->script != jp->fun->script());
|
||||
JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
|
||||
}
|
||||
#endif
|
||||
uva = jp->script->upvars();
|
||||
index = uva->vector[index].slot();
|
||||
}
|
||||
atom = GetArgOrVarAtom(jp, index);
|
||||
goto do_name;
|
||||
}
|
||||
|
||||
case JSOP_CALLLOCAL:
|
||||
case JSOP_GETLOCAL:
|
||||
if (IsVarSlot(jp, pc, &i)) {
|
||||
@ -4701,7 +4652,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, int nb)
|
||||
break;
|
||||
|
||||
case JSOP_LAMBDA:
|
||||
case JSOP_LAMBDA_FC:
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_GENEXP) {
|
||||
|
@ -364,13 +364,8 @@ OPDEF(JSOP_PICK, 133, "pick", NULL, 2, 0, 0, 0, JOF_UINT8|
|
||||
OPDEF(JSOP_TRY, 134,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Get a slot from a flat closure function object that contains a snapshot of
|
||||
* the closure-invariant upvar values. The immediate operand indexes the upvar
|
||||
* in the function's u.i.script->upvars() array.
|
||||
*/
|
||||
OPDEF(JSOP_GETFCSLOT, 136,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET)
|
||||
OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET)
|
||||
OPDEF(JSOP_UNUSED26, 136,"unused20", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED27, 137,"unused21", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Define a local function object as a local variable.
|
||||
@ -543,12 +538,8 @@ OPDEF(JSOP_LENGTH, 217, "length", NULL, 5, 1, 1, 18, JOF_ATOM|J
|
||||
OPDEF(JSOP_HOLE, 218, "hole", NULL, 1, 0, 1, 0, JOF_BYTE)
|
||||
|
||||
OPDEF(JSOP_UNUSED17, 219,"unused17", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Variants of JSOP_{DEFLOCALFUN,LAMBDA} optimized for the flat closure case.
|
||||
*/
|
||||
OPDEF(JSOP_DEFLOCALFUN_FC,220,"deflocalfun_fc",NULL, 7, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
|
||||
OPDEF(JSOP_LAMBDA_FC, 221,"lambda_fc", NULL, 5, 0, 1, 19, JOF_OBJECT)
|
||||
OPDEF(JSOP_UNUSED24, 220,"unused18", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED25, 221,"unused19", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Joined function object as method optimization support.
|
||||
|
@ -100,8 +100,6 @@ Bindings::lookup(JSContext *cx, JSAtom *name, unsigned *indexp) const
|
||||
|
||||
if (shape->getter() == CallObject::getArgOp)
|
||||
return ARGUMENT;
|
||||
if (shape->getter() == CallObject::getUpvarOp)
|
||||
return UPVAR;
|
||||
|
||||
return shape->writable() ? VARIABLE : CONSTANT;
|
||||
}
|
||||
@ -126,20 +124,12 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
|
||||
|
||||
if (kind == ARGUMENT) {
|
||||
JS_ASSERT(nvars == 0);
|
||||
JS_ASSERT(nupvars == 0);
|
||||
indexp = &nargs;
|
||||
getter = CallObject::getArgOp;
|
||||
setter = CallObject::setArgOp;
|
||||
slot += nargs;
|
||||
} else if (kind == UPVAR) {
|
||||
indexp = &nupvars;
|
||||
getter = CallObject::getUpvarOp;
|
||||
setter = CallObject::setUpvarOp;
|
||||
slot = lastBinding->maybeSlot();
|
||||
attrs |= JSPROP_SHARED;
|
||||
} else {
|
||||
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
||||
JS_ASSERT(nupvars == 0);
|
||||
|
||||
indexp = &nvars;
|
||||
getter = CallObject::getVarOp;
|
||||
@ -250,9 +240,6 @@ Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
|
||||
|
||||
if (shape.getter() == CallObject::getArgOp) {
|
||||
JS_ASSERT(index < nargs);
|
||||
} else if (shape.getter() == CallObject::getUpvarOp) {
|
||||
JS_ASSERT(index < nupvars);
|
||||
index += nargs + nvars;
|
||||
} else {
|
||||
JS_ASSERT(index < nvars);
|
||||
index += nargs;
|
||||
@ -290,19 +277,6 @@ Bindings::lastArgument() const
|
||||
|
||||
const Shape *
|
||||
Bindings::lastVariable() const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
|
||||
const js::Shape *shape = lastUpvar();
|
||||
if (nupvars > 0) {
|
||||
while (shape->getter() == CallObject::getUpvarOp)
|
||||
shape = shape->previous();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Bindings::lastUpvar() const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
return lastBinding;
|
||||
@ -445,12 +419,12 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
jssrcnote *notes = NULL;
|
||||
|
||||
/* XDR arguments, local vars, and upvars. */
|
||||
uint16_t nargs, nvars, nupvars;
|
||||
uint16_t nargs, nvars;
|
||||
#if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
|
||||
script = NULL;
|
||||
nargs = nvars = nupvars = Bindings::BINDING_COUNT_LIMIT;
|
||||
nargs = nvars = Bindings::BINDING_COUNT_LIMIT;
|
||||
#endif
|
||||
uint32_t argsVars, paddingUpvars;
|
||||
uint32_t argsVars;
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
script = *scriptp;
|
||||
|
||||
@ -459,24 +433,19 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
|
||||
nargs = script->bindings.countArgs();
|
||||
nvars = script->bindings.countVars();
|
||||
nupvars = script->bindings.countUpvars();
|
||||
argsVars = (nargs << 16) | nvars;
|
||||
paddingUpvars = nupvars;
|
||||
}
|
||||
if (!JS_XDRUint32(xdr, &argsVars) || !JS_XDRUint32(xdr, &paddingUpvars))
|
||||
if (!JS_XDRUint32(xdr, &argsVars))
|
||||
return false;
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
nargs = argsVars >> 16;
|
||||
nvars = argsVars & 0xFFFF;
|
||||
JS_ASSERT((paddingUpvars >> 16) == 0);
|
||||
nupvars = paddingUpvars & 0xFFFF;
|
||||
}
|
||||
JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
|
||||
JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
|
||||
JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT);
|
||||
|
||||
Bindings bindings(cx);
|
||||
uint32_t nameCount = nargs + nvars + nupvars;
|
||||
uint32_t nameCount = nargs + nvars;
|
||||
if (nameCount > 0) {
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
|
||||
@ -532,12 +501,10 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
BindingKind kind = (i < nargs)
|
||||
? ARGUMENT
|
||||
: (i < unsigned(nargs + nvars))
|
||||
? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
|
||||
: (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
|
||||
? CONSTANT
|
||||
: VARIABLE)
|
||||
: UPVAR;
|
||||
? CONSTANT
|
||||
: VARIABLE);
|
||||
if (!bindings.add(cx, name, kind))
|
||||
return false;
|
||||
}
|
||||
@ -569,8 +536,6 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
|
||||
if (JSScript::isValidOffset(script->objectsOffset))
|
||||
nobjects = script->objects()->length;
|
||||
if (JSScript::isValidOffset(script->upvarsOffset))
|
||||
JS_ASSERT(script->bindings.countUpvars() == script->upvars()->length);
|
||||
if (JSScript::isValidOffset(script->regexpsOffset))
|
||||
nregexps = script->regexps()->length;
|
||||
if (JSScript::isValidOffset(script->trynotesOffset))
|
||||
@ -639,7 +604,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
/* Note: version is packed into the 32b space with another 16b value. */
|
||||
JSVersion version_ = JSVersion(version & JS_BITMASK(16));
|
||||
JS_ASSERT((version_ & VersionFlags::FULL_MASK) == unsigned(version_));
|
||||
script = JSScript::NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
|
||||
script = JSScript::NewScript(cx, length, nsrcnotes, natoms, nobjects,
|
||||
nregexps, ntrynotes, nconsts, 0, nClosedArgs,
|
||||
nClosedVars, nTypeSets, version_);
|
||||
if (!script)
|
||||
@ -751,10 +716,6 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
*objp = tmp;
|
||||
}
|
||||
}
|
||||
for (i = 0; i != nupvars; ++i) {
|
||||
if (!JS_XDRUint32(xdr, reinterpret_cast<uint32_t *>(&script->upvars()->vector[i])))
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i != nregexps; ++i) {
|
||||
if (!XDRScriptRegExpObject(xdr, &script->regexps()->vector[i]))
|
||||
return false;
|
||||
@ -989,7 +950,6 @@ JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0);
|
||||
* coincide with INVALID_OFFSET.
|
||||
*/
|
||||
JS_STATIC_ASSERT(sizeof(JSObjectArray) +
|
||||
sizeof(JSUpvarArray) +
|
||||
sizeof(JSObjectArray) +
|
||||
sizeof(JSTryNoteArray) +
|
||||
sizeof(js::GlobalSlotArray)
|
||||
@ -998,15 +958,13 @@ JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255);
|
||||
|
||||
JSScript *
|
||||
JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
|
||||
uint32_t nobjects, uint32_t nupvars, uint32_t nregexps,
|
||||
uint32_t nobjects, uint32_t nregexps,
|
||||
uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals,
|
||||
uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets, JSVersion version)
|
||||
{
|
||||
size_t size = sizeof(JSAtom *) * natoms;
|
||||
if (nobjects != 0)
|
||||
size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
|
||||
if (nupvars != 0)
|
||||
size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32_t);
|
||||
if (nregexps != 0)
|
||||
size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
|
||||
if (ntrynotes != 0)
|
||||
@ -1024,7 +982,6 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t
|
||||
* alignment which we ensure below.
|
||||
*/
|
||||
JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(jsval) == 0);
|
||||
JS_STATIC_ASSERT(sizeof(JSUpvarArray) % sizeof(jsval) == 0);
|
||||
JS_STATIC_ASSERT(sizeof(JSTryNoteArray) % sizeof(jsval) == 0);
|
||||
JS_STATIC_ASSERT(sizeof(GlobalSlotArray) % sizeof(jsval) == 0);
|
||||
JS_STATIC_ASSERT(sizeof(JSConstArray) % sizeof(jsval) == 0);
|
||||
@ -1083,12 +1040,6 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t
|
||||
} else {
|
||||
script->objectsOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
if (nupvars != 0) {
|
||||
script->upvarsOffset = uint8_t(cursor - data);
|
||||
cursor += sizeof(JSUpvarArray);
|
||||
} else {
|
||||
script->upvarsOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
if (nregexps != 0) {
|
||||
script->regexpsOffset = uint8_t(cursor - data);
|
||||
cursor += sizeof(JSObjectArray);
|
||||
@ -1116,7 +1067,6 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(JSObjectArray) +
|
||||
sizeof(JSUpvarArray) +
|
||||
sizeof(JSObjectArray) +
|
||||
sizeof(JSTryNoteArray) +
|
||||
sizeof(GlobalSlotArray) < 0xFF);
|
||||
@ -1173,16 +1123,6 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t
|
||||
JS_ASSERT(nTypeSets <= UINT16_MAX);
|
||||
script->nTypeSets = uint16_t(nTypeSets);
|
||||
|
||||
/*
|
||||
* NB: We allocate the vector of uint32_t upvar cookies after all vectors of
|
||||
* pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
|
||||
*/
|
||||
if (nupvars != 0) {
|
||||
script->upvars()->length = nupvars;
|
||||
script->upvars()->vector = reinterpret_cast<UpvarCookie *>(cursor);
|
||||
cursor += nupvars * sizeof(script->upvars()->vector[0]);
|
||||
}
|
||||
|
||||
script->code = (jsbytecode *)cursor;
|
||||
JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size);
|
||||
|
||||
@ -1218,11 +1158,9 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
JS_ASSERT(nClosedArgs == bce->closedArgs.length());
|
||||
uint16_t nClosedVars = uint16_t(bce->closedVars.length());
|
||||
JS_ASSERT(nClosedVars == bce->closedVars.length());
|
||||
size_t upvarIndexCount = bce->upvarIndices.hasMap() ? bce->upvarIndices->count() : 0;
|
||||
script = NewScript(cx, prologLength + mainLength, nsrcnotes,
|
||||
bce->atomIndices->count(), bce->objectList.length,
|
||||
upvarIndexCount, bce->regexpList.length,
|
||||
bce->ntrynotes, bce->constList.length(),
|
||||
bce->regexpList.length, bce->ntrynotes, bce->constList.length(),
|
||||
bce->globalUses.length(), nClosedArgs, nClosedVars,
|
||||
bce->typesetCount, bce->version());
|
||||
if (!script)
|
||||
@ -1294,14 +1232,6 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
if (bce->flags & TCF_HAS_SINGLETONS)
|
||||
script->hasSingletons = true;
|
||||
|
||||
if (bce->hasUpvarIndices()) {
|
||||
JS_ASSERT(bce->upvarIndices->count() <= bce->upvarMap.length());
|
||||
PodCopy<UpvarCookie>(script->upvars()->vector, bce->upvarMap.begin(),
|
||||
bce->upvarIndices->count());
|
||||
bce->upvarIndices->clear();
|
||||
bce->upvarMap.clear();
|
||||
}
|
||||
|
||||
if (bce->globalUses.length()) {
|
||||
PodCopy<GlobalSlotArray::Entry>(script->globals()->vector, &bce->globalUses[0],
|
||||
bce->globalUses.length());
|
||||
@ -1325,25 +1255,17 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
fun = bce->fun();
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->script());
|
||||
#ifdef DEBUG
|
||||
if (JSScript::isValidOffset(script->upvarsOffset))
|
||||
JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
|
||||
else
|
||||
JS_ASSERT(script->bindings.countUpvars() == 0);
|
||||
#endif
|
||||
if (bce->flags & TCF_FUN_HEAVYWEIGHT)
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
|
||||
/*
|
||||
* Mark functions which will only be executed once as singletons.
|
||||
* Skip this for flat closures, which must be copied on executing.
|
||||
*/
|
||||
bool singleton =
|
||||
cx->typeInferenceEnabled() &&
|
||||
bce->parent &&
|
||||
bce->parent->compiling() &&
|
||||
bce->parent->asBytecodeEmitter()->checkSingletonContext() &&
|
||||
!fun->isFlatClosure();
|
||||
bce->parent->asBytecodeEmitter()->checkSingletonContext();
|
||||
|
||||
if (!script->typeSetFunction(cx, fun, singleton))
|
||||
return NULL;
|
||||
|
@ -63,61 +63,6 @@ typedef enum JSTryNoteKind {
|
||||
JSTRY_ITER
|
||||
} JSTryNoteKind;
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Indicates a location in the stack that an upvar value can be retrieved from
|
||||
* as a two tuple of (level, slot).
|
||||
*
|
||||
* Some existing client code uses the level value as a delta, or level "skip"
|
||||
* quantity. We could probably document that through use of more types at some
|
||||
* point in the future.
|
||||
*
|
||||
* Existing XDR code wants this to be backed by a 32b integer for serialization,
|
||||
* so we oblige.
|
||||
*
|
||||
* TODO: consider giving more bits to the slot value and takings ome from the level.
|
||||
*/
|
||||
class UpvarCookie
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
static const uint32_t FREE_VALUE = 0xfffffffful;
|
||||
|
||||
void checkInvariants() {
|
||||
JS_STATIC_ASSERT(sizeof(UpvarCookie) == sizeof(uint32_t));
|
||||
JS_STATIC_ASSERT(UPVAR_LEVEL_LIMIT < FREE_LEVEL);
|
||||
}
|
||||
|
||||
public:
|
||||
/*
|
||||
* All levels above-and-including FREE_LEVEL are reserved so that
|
||||
* FREE_VALUE can be used as a special value.
|
||||
*/
|
||||
static const uint16_t FREE_LEVEL = 0x3fff;
|
||||
|
||||
/*
|
||||
* If a function has a higher static level than this limit, we will not
|
||||
* optimize it using UPVAR opcodes.
|
||||
*/
|
||||
static const uint16_t UPVAR_LEVEL_LIMIT = 16;
|
||||
static const uint16_t CALLEE_SLOT = 0xffff;
|
||||
static bool isLevelReserved(uint16_t level) { return level >= FREE_LEVEL; }
|
||||
|
||||
bool isFree() const { return value == FREE_VALUE; }
|
||||
uint32_t asInteger() const { return value; }
|
||||
/* isFree check should be performed before using these accessors. */
|
||||
uint16_t level() const { JS_ASSERT(!isFree()); return uint16_t(value >> 16); }
|
||||
uint16_t slot() const { JS_ASSERT(!isFree()); return uint16_t(value); }
|
||||
|
||||
void set(const UpvarCookie &other) { set(other.level(), other.slot()); }
|
||||
void set(uint16_t newLevel, uint16_t newSlot) { value = (uint32_t(newLevel) << 16) | newSlot; }
|
||||
void makeFree() { set(0xffff, 0xffff); JS_ASSERT(isFree()); }
|
||||
void fromInteger(uint32_t u32) { value = u32; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Exception handling record.
|
||||
*/
|
||||
@ -140,11 +85,6 @@ typedef struct JSObjectArray {
|
||||
uint32_t length; /* count of indexed objects */
|
||||
} JSObjectArray;
|
||||
|
||||
typedef struct JSUpvarArray {
|
||||
js::UpvarCookie *vector; /* array of indexed upvar cookies */
|
||||
uint32_t length; /* count of indexed upvar cookies */
|
||||
} JSUpvarArray;
|
||||
|
||||
typedef struct JSConstArray {
|
||||
js::HeapValue *vector; /* array of indexed constant values */
|
||||
uint32_t length;
|
||||
@ -163,19 +103,19 @@ struct GlobalSlotArray {
|
||||
|
||||
struct Shape;
|
||||
|
||||
enum BindingKind { NONE, ARGUMENT, VARIABLE, CONSTANT, UPVAR };
|
||||
enum BindingKind { NONE, ARGUMENT, VARIABLE, CONSTANT };
|
||||
|
||||
/*
|
||||
* Formal parameters, local variables, and upvars are stored in a shape tree
|
||||
* Formal parameters and local variables are stored in a shape tree
|
||||
* path encapsulated within this class. This class represents bindings for
|
||||
* both function and top-level scripts (the latter is needed to track names in
|
||||
* strict mode eval code, to give such code its own lexical environment).
|
||||
*/
|
||||
class Bindings {
|
||||
class Bindings
|
||||
{
|
||||
HeapPtr<Shape> lastBinding;
|
||||
uint16_t nargs;
|
||||
uint16_t nvars;
|
||||
uint16_t nupvars;
|
||||
bool hasDup_:1; // true if there are duplicate argument names
|
||||
|
||||
inline Shape *initialShape(JSContext *cx) const;
|
||||
@ -198,13 +138,9 @@ class Bindings {
|
||||
|
||||
uint16_t countArgs() const { return nargs; }
|
||||
uint16_t countVars() const { return nvars; }
|
||||
uint16_t countUpvars() const { return nupvars; }
|
||||
|
||||
unsigned countArgsAndVars() const { return nargs + nvars; }
|
||||
unsigned countLocalNames() const { return nargs + nvars; }
|
||||
|
||||
unsigned countLocalNames() const { return nargs + nvars + nupvars; }
|
||||
|
||||
bool hasUpvars() const { return nupvars > 0; }
|
||||
bool hasLocalNames() const { return countLocalNames() > 0; }
|
||||
|
||||
/* Ensure these bindings have a shape lineage. */
|
||||
@ -226,10 +162,7 @@ class Bindings {
|
||||
bool setParent(JSContext *cx, JSObject *obj);
|
||||
|
||||
enum {
|
||||
/*
|
||||
* A script may have no more than this many arguments, variables, or
|
||||
* upvars.
|
||||
*/
|
||||
/* A script may have no more than this many arguments or variables. */
|
||||
BINDING_COUNT_LIMIT = 0xFFFF
|
||||
};
|
||||
|
||||
@ -246,8 +179,7 @@ class Bindings {
|
||||
*
|
||||
* The parser builds shape paths for functions, usable by Call objects at
|
||||
* runtime, by calling an "add" method. All ARGUMENT bindings must be added
|
||||
* before before any VARIABLE or CONSTANT bindings, which themselves must
|
||||
* be added before all UPVAR bindings.
|
||||
* before before any VARIABLE or CONSTANT bindings.
|
||||
*/
|
||||
bool add(JSContext *cx, JSAtom *name, BindingKind kind);
|
||||
|
||||
@ -258,9 +190,6 @@ class Bindings {
|
||||
bool addConstant(JSContext *cx, JSAtom *name) {
|
||||
return add(cx, name, CONSTANT);
|
||||
}
|
||||
bool addUpvar(JSContext *cx, JSAtom *name) {
|
||||
return add(cx, name, UPVAR);
|
||||
}
|
||||
bool addArgument(JSContext *cx, JSAtom *name, uint16_t *slotp) {
|
||||
JS_ASSERT(name != NULL); /* not destructuring */
|
||||
*slotp = nargs;
|
||||
@ -316,7 +245,6 @@ class Bindings {
|
||||
*/
|
||||
const js::Shape *lastArgument() const;
|
||||
const js::Shape *lastVariable() const;
|
||||
const js::Shape *lastUpvar() const;
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
@ -412,7 +340,7 @@ struct JSScript : public js::gc::Cell {
|
||||
* kind (function or other) of new JSScript.
|
||||
*/
|
||||
static JSScript *NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
|
||||
uint32_t nobjects, uint32_t nupvars, uint32_t nregexps,
|
||||
uint32_t nobjects, uint32_t nregexps,
|
||||
uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals,
|
||||
uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets,
|
||||
JSVersion version);
|
||||
@ -443,8 +371,6 @@ struct JSScript : public js::gc::Cell {
|
||||
uint8_t objectsOffset; /* offset to the array of nested function,
|
||||
block, scope, xml and one-time regexps
|
||||
objects */
|
||||
uint8_t upvarsOffset; /* offset of the array of display ("up")
|
||||
closure vars */
|
||||
uint8_t regexpsOffset; /* offset to the array of to-be-cloned
|
||||
regexps */
|
||||
uint8_t trynotesOffset; /* offset to the array of try notes */
|
||||
@ -696,11 +622,6 @@ struct JSScript : public js::gc::Cell {
|
||||
return reinterpret_cast<JSObjectArray *>(data + objectsOffset);
|
||||
}
|
||||
|
||||
JSUpvarArray *upvars() {
|
||||
JS_ASSERT(isValidOffset(upvarsOffset));
|
||||
return reinterpret_cast<JSUpvarArray *>(data + upvarsOffset);
|
||||
}
|
||||
|
||||
JSObjectArray *regexps() {
|
||||
JS_ASSERT(isValidOffset(regexpsOffset));
|
||||
return reinterpret_cast<JSObjectArray *>(data + regexpsOffset);
|
||||
|
@ -58,7 +58,7 @@ namespace js {
|
||||
|
||||
inline
|
||||
Bindings::Bindings(JSContext *cx)
|
||||
: lastBinding(NULL), nargs(0), nvars(0), nupvars(0), hasDup_(false)
|
||||
: lastBinding(NULL), nargs(0), nvars(0), hasDup_(false)
|
||||
{}
|
||||
|
||||
inline void
|
||||
|
139
js/src/jsstr.cpp
139
js/src/jsstr.cpp
@ -1793,8 +1793,7 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jscha
|
||||
static bool
|
||||
FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
|
||||
{
|
||||
JSObject *base = rdata.elembase;
|
||||
if (base) {
|
||||
if (JSObject *base = rdata.elembase) {
|
||||
/*
|
||||
* The base object is used when replace was passed a lambda which looks like
|
||||
* 'function(a) { return b[a]; }' for the base object b. b will not change
|
||||
@ -1818,26 +1817,14 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
|
||||
if (!atom)
|
||||
return false;
|
||||
}
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
|
||||
JSObject *holder;
|
||||
JSProperty *prop = NULL;
|
||||
if (!LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop))
|
||||
return false;
|
||||
|
||||
/* Only handle the case where the property exists and is on this object. */
|
||||
if (prop && holder == base) {
|
||||
Shape *shape = (Shape *) prop;
|
||||
if (shape->hasSlot() && shape->hasDefaultGetter()) {
|
||||
Value value = base->getSlot(shape->slot());
|
||||
if (value.isString()) {
|
||||
rdata.repstr = value.toString()->ensureLinear(cx);
|
||||
if (!rdata.repstr)
|
||||
return false;
|
||||
*sizep = rdata.repstr->length();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Value v;
|
||||
if (HasDataProperty(cx, base, atom, &v) && v.isString()) {
|
||||
rdata.repstr = v.toString()->ensureLinear(cx);
|
||||
if (!rdata.repstr)
|
||||
return false;
|
||||
*sizep = rdata.repstr->length();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1847,8 +1834,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
|
||||
rdata.elembase = NULL;
|
||||
}
|
||||
|
||||
JSObject *lambda = rdata.lambda;
|
||||
if (lambda) {
|
||||
if (JSObject *lambda = rdata.lambda) {
|
||||
PreserveRegExpStatics staticsGuard(res);
|
||||
if (!staticsGuard.init(cx))
|
||||
return false;
|
||||
@ -2222,6 +2208,78 @@ str_replace_flat_lambda(JSContext *cx, CallArgs outerArgs, ReplaceData &rdata, c
|
||||
|
||||
static const uint32_t ReplaceOptArg = 2;
|
||||
|
||||
/*
|
||||
* Pattern match the script to check if it is is indexing into a particular
|
||||
* object, e.g. 'function(a) { return b[a]; }'. Avoid calling the script in
|
||||
* such cases, which are used by javascript packers (particularly the popular
|
||||
* Dean Edwards packer) to efficiently encode large scripts. We only handle the
|
||||
* code patterns generated by such packers here.
|
||||
*/
|
||||
static JSObject *
|
||||
LambdaIsGetElem(JSObject &lambda, JSContext *cx)
|
||||
{
|
||||
if (!lambda.isFunction())
|
||||
return NULL;
|
||||
|
||||
JSFunction *fun = lambda.toFunction();
|
||||
if (!fun->isInterpreted())
|
||||
return NULL;
|
||||
|
||||
JSScript *script = fun->script();
|
||||
jsbytecode *pc = script->code;
|
||||
|
||||
/* Look for an access to 'b' in the enclosing scope. */
|
||||
if (JSOp(*pc) != JSOP_NAME)
|
||||
return NULL;
|
||||
PropertyName *bname;
|
||||
GET_NAME_FROM_BYTECODE(script, pc, 0, bname);
|
||||
pc += JSOP_NAME_LENGTH;
|
||||
|
||||
/*
|
||||
* Do a conservative search for 'b' in the enclosing scope. Avoid using a
|
||||
* real name lookup since this can trigger observable effects.
|
||||
*/
|
||||
Value b;
|
||||
JSObject *scope = cx->stack.currentScriptedScopeChain();
|
||||
while (true) {
|
||||
if (scope->isCall()) {
|
||||
if (scope->asCall().containsVarOrArg(bname, &b, cx))
|
||||
break;
|
||||
} else if (scope->isBlock()) {
|
||||
if (scope->asClonedBlock().containsVar(bname, &b, cx))
|
||||
break;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
scope = &scope->asScope().enclosingScope();
|
||||
}
|
||||
|
||||
/* Look for 'a' to be the lambda's first argument. */
|
||||
if (JSOp(*pc) != JSOP_GETARG || GET_SLOTNO(pc) != 0)
|
||||
return NULL;
|
||||
pc += JSOP_GETARG_LENGTH;
|
||||
|
||||
/* 'b[a]' */
|
||||
if (JSOp(*pc) != JSOP_GETELEM)
|
||||
return NULL;
|
||||
pc += JSOP_GETELEM_LENGTH;
|
||||
|
||||
/* 'return b[a]' */
|
||||
if (JSOp(*pc) != JSOP_RETURN)
|
||||
return NULL;
|
||||
|
||||
/* 'b' must behave like a normal object. */
|
||||
if (!b.isObject())
|
||||
return NULL;
|
||||
|
||||
JSObject &bobj = b.toObject();
|
||||
Class *clasp = bobj.getClass();
|
||||
if (!clasp->isNative() || clasp->ops.lookupProperty || clasp->ops.getProperty)
|
||||
return NULL;
|
||||
|
||||
return &bobj;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js::str_replace(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@ -2242,39 +2300,8 @@ js::str_replace(JSContext *cx, unsigned argc, Value *vp)
|
||||
rdata.repstr = NULL;
|
||||
rdata.dollar = rdata.dollarEnd = NULL;
|
||||
|
||||
if (rdata.lambda->isFunction()) {
|
||||
JSFunction *fun = rdata.lambda->toFunction();
|
||||
if (fun->isInterpreted()) {
|
||||
/*
|
||||
* Pattern match the script to check if it is is indexing into a
|
||||
* particular object, e.g. 'function(a) { return b[a]; }'. Avoid
|
||||
* calling the script in such cases, which are used by javascript
|
||||
* packers (particularly the popular Dean Edwards packer) to efficiently
|
||||
* encode large scripts. We only handle the code patterns generated
|
||||
* by such packers here.
|
||||
*/
|
||||
JSScript *script = fun->script();
|
||||
jsbytecode *pc = script->code;
|
||||
|
||||
Value table = UndefinedValue();
|
||||
if (JSOp(*pc) == JSOP_GETFCSLOT) {
|
||||
table = fun->getFlatClosureUpvar(GET_UINT16(pc));
|
||||
pc += JSOP_GETFCSLOT_LENGTH;
|
||||
}
|
||||
|
||||
if (table.isObject() &&
|
||||
JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 &&
|
||||
JSOp(pc[JSOP_GETARG_LENGTH]) == JSOP_GETELEM &&
|
||||
JSOp(pc[JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH]) == JSOP_RETURN) {
|
||||
Class *clasp = table.toObject().getClass();
|
||||
if (clasp->isNative() &&
|
||||
!clasp->ops.lookupProperty &&
|
||||
!clasp->ops.getProperty) {
|
||||
rdata.elembase = &table.toObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (JSObject *base = LambdaIsGetElem(*rdata.lambda, cx))
|
||||
rdata.elembase = base;
|
||||
} else {
|
||||
rdata.lambda = NULL;
|
||||
rdata.elembase = NULL;
|
||||
|
@ -192,7 +192,7 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp);
|
||||
* and saved versions. If deserialization fails, the data should be
|
||||
* invalidated if possible.
|
||||
*/
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 109)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 110)
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
|
@ -3054,25 +3054,6 @@ mjit::Compiler::generateMethod()
|
||||
}
|
||||
END_CASE(JSOP_SETCONST)
|
||||
|
||||
BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
|
||||
{
|
||||
uint32_t slot = GET_SLOTNO(PC);
|
||||
JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC + SLOTNO_LEN));
|
||||
|
||||
/* See JSOP_DEFLOCALFUN. */
|
||||
markUndefinedLocal(PC - script->code, slot);
|
||||
|
||||
prepareStubCall(Uses(frame.frameSlots()));
|
||||
masm.move(ImmPtr(fun), Registers::ArgReg1);
|
||||
INLINE_STUBCALL(stubs::DefLocalFun_FC, REJOIN_DEFLOCALFUN);
|
||||
frame.takeReg(Registers::ReturnReg);
|
||||
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
|
||||
frame.storeLocal(slot, true);
|
||||
frame.pop();
|
||||
updateVarType();
|
||||
}
|
||||
END_CASE(JSOP_DEFLOCALFUN_FC)
|
||||
|
||||
BEGIN_CASE(JSOP_LAMBDA)
|
||||
{
|
||||
JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC));
|
||||
@ -3117,27 +3098,6 @@ mjit::Compiler::generateMethod()
|
||||
frame.syncAndForgetEverything();
|
||||
END_CASE(JSOP_TRY)
|
||||
|
||||
BEGIN_CASE(JSOP_GETFCSLOT)
|
||||
BEGIN_CASE(JSOP_CALLFCSLOT)
|
||||
{
|
||||
unsigned index = GET_UINT16(PC);
|
||||
|
||||
// Load the callee's payload into a register.
|
||||
frame.pushCallee();
|
||||
RegisterID reg = frame.copyDataIntoReg(frame.peek(-1));
|
||||
frame.pop();
|
||||
|
||||
// obj->getFlatClosureUpvars()
|
||||
Address upvarAddress(reg, JSFunction::getFlatClosureUpvarsOffset());
|
||||
masm.loadPrivate(upvarAddress, reg);
|
||||
// push ((Value *) reg)[index]
|
||||
|
||||
BarrierState barrier = pushAddressMaybeBarrier(Address(reg, index * sizeof(Value)),
|
||||
knownPushedType(0), true);
|
||||
finishBarrier(barrier, REJOIN_GETTER, 0);
|
||||
}
|
||||
END_CASE(JSOP_CALLFCSLOT)
|
||||
|
||||
BEGIN_CASE(JSOP_DEFLOCALFUN)
|
||||
{
|
||||
uint32_t slot = GET_SLOTNO(PC);
|
||||
@ -3237,17 +3197,6 @@ mjit::Compiler::generateMethod()
|
||||
frame.push(MagicValue(JS_ARRAY_HOLE));
|
||||
END_CASE(JSOP_HOLE)
|
||||
|
||||
BEGIN_CASE(JSOP_LAMBDA_FC)
|
||||
{
|
||||
JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC));
|
||||
prepareStubCall(Uses(frame.frameSlots()));
|
||||
masm.move(ImmPtr(fun), Registers::ArgReg1);
|
||||
INLINE_STUBCALL(stubs::FlatLambda, REJOIN_PUSH_OBJECT);
|
||||
frame.takeReg(Registers::ReturnReg);
|
||||
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
|
||||
}
|
||||
END_CASE(JSOP_LAMBDA_FC)
|
||||
|
||||
BEGIN_CASE(JSOP_LOOPHEAD)
|
||||
{
|
||||
if (analysis->jumpTarget(PC)) {
|
||||
|
@ -339,8 +339,6 @@ stubs::DefFun(VMFrame &f, JSFunction *fun)
|
||||
*/
|
||||
obj2 = &fp->scopeChain();
|
||||
} else {
|
||||
JS_ASSERT(!fun->isFlatClosure());
|
||||
|
||||
obj2 = GetScopeChain(cx, fp);
|
||||
if (!obj2)
|
||||
THROW();
|
||||
@ -976,16 +974,6 @@ stubs::InitElem(VMFrame &f, uint32_t last)
|
||||
}
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::GetUpvar(VMFrame &f, uint32_t ck)
|
||||
{
|
||||
/* :FIXME: We can do better, this stub isn't needed. */
|
||||
uint32_t staticLevel = f.script()->staticLevel;
|
||||
UpvarCookie cookie;
|
||||
cookie.fromInteger(ck);
|
||||
f.regs.sp[0] = GetUpvar(f.cx, staticLevel, cookie);
|
||||
}
|
||||
|
||||
JSObject * JS_FASTCALL
|
||||
stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
@ -997,7 +985,6 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
|
||||
* activation.
|
||||
*/
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->isFlatClosure());
|
||||
|
||||
JSObject *parent;
|
||||
if (fun->isNullClosure()) {
|
||||
@ -1016,15 +1003,6 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSObject * JS_FASTCALL
|
||||
stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
JSObject *obj = js_NewFlatClosure(f.cx, fun);
|
||||
if (!obj)
|
||||
THROWV(NULL);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::RegExp(VMFrame &f, JSObject *regex)
|
||||
{
|
||||
@ -1283,15 +1261,6 @@ stubs::Throw(VMFrame &f)
|
||||
THROW();
|
||||
}
|
||||
|
||||
JSObject * JS_FASTCALL
|
||||
stubs::FlatLambda(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
JSObject *obj = js_NewFlatClosure(f.cx, fun);
|
||||
if (!obj)
|
||||
THROWV(NULL);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::Arguments(VMFrame &f)
|
||||
{
|
||||
|
@ -126,7 +126,6 @@ void JS_FASTCALL GetElem(VMFrame &f);
|
||||
template<JSBool strict> void JS_FASTCALL SetElem(VMFrame &f);
|
||||
void JS_FASTCALL ToId(VMFrame &f);
|
||||
void JS_FASTCALL ImplicitThis(VMFrame &f, PropertyName *name);
|
||||
void JS_FASTCALL GetUpvar(VMFrame &f, uint32_t index);
|
||||
|
||||
template <JSBool strict> void JS_FASTCALL DelProp(VMFrame &f, PropertyName *name);
|
||||
template <JSBool strict> void JS_FASTCALL DelElem(VMFrame &f);
|
||||
|
@ -1717,32 +1717,7 @@ DisassembleScript(JSContext *cx, JSScript *script, JSFunction *fun, bool lines,
|
||||
|
||||
if (fun->isNullClosure())
|
||||
Sprint(sp, " NULL_CLOSURE");
|
||||
else if (fun->isFlatClosure())
|
||||
Sprint(sp, " FLAT_CLOSURE");
|
||||
|
||||
JSScript *script = fun->script();
|
||||
if (script->bindings.hasUpvars()) {
|
||||
Sprint(sp, "\nupvars: {\n");
|
||||
|
||||
Vector<JSAtom *> localNames(cx);
|
||||
if (!script->bindings.getLocalNameArray(cx, &localNames))
|
||||
return false;
|
||||
|
||||
JSUpvarArray *uva = script->upvars();
|
||||
unsigned upvar_base = script->bindings.countArgsAndVars();
|
||||
|
||||
for (uint32_t i = 0, n = uva->length; i < n; i++) {
|
||||
JSAtom *atom = localNames[upvar_base + i];
|
||||
UpvarCookie cookie = uva->vector[i];
|
||||
JSAutoByteString printable;
|
||||
if (js_AtomToPrintableString(cx, atom, &printable)) {
|
||||
Sprint(sp, " %s: {skip:%u, slot:%u},\n",
|
||||
printable.ptr(), cookie.level(), cookie.slot());
|
||||
}
|
||||
}
|
||||
|
||||
Sprint(sp, "}");
|
||||
}
|
||||
Sprint(sp, "\n");
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,6 @@ if (typeof findReferences == "function") {
|
||||
|
||||
var o = ({});
|
||||
|
||||
function returnFlat(x) { return function flat() { return x; }; }
|
||||
assertEq(referencesVia(returnFlat(o), 'upvars[0]', o), true);
|
||||
|
||||
function returnHeavy(y) { eval(''); return function heavy() { return y; }; }
|
||||
assertEq(referencesVia(returnHeavy(o), 'fun_callscope; y', o), true);
|
||||
assertEq(referencesVia(returnHeavy(o), 'fun_callscope; shape; base; parent', this), true);
|
||||
|
@ -144,7 +144,7 @@ GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
|
||||
functionProto->flags |= JSFUN_PROTOTYPE;
|
||||
|
||||
JSScript *script =
|
||||
JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
|
||||
JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
|
||||
if (!script)
|
||||
return NULL;
|
||||
script->noScriptRval = true;
|
||||
|
@ -86,7 +86,7 @@ js_PutCallObject(StackFrame *fp)
|
||||
JS_ASSERT(script == callobj.getCalleeFunction()->script());
|
||||
JS_ASSERT(script == fun->script());
|
||||
|
||||
unsigned n = bindings.countArgsAndVars();
|
||||
unsigned n = bindings.countLocalNames();
|
||||
if (n > 0) {
|
||||
uint32_t nvars = bindings.countVars();
|
||||
uint32_t nargs = bindings.countArgs();
|
||||
@ -332,28 +332,6 @@ CallObject::setArgOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
CallObject::getUpvarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
{
|
||||
CallObject &callobj = obj->asCall();
|
||||
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||
unsigned i = (uint16_t) JSID_TO_INT(id);
|
||||
|
||||
*vp = callobj.getCallee()->toFunction()->getFlatClosureUpvar(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
CallObject::setUpvarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
|
||||
{
|
||||
CallObject &callobj = obj->asCall();
|
||||
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||
unsigned i = (uint16_t) JSID_TO_INT(id);
|
||||
|
||||
callobj.getCallee()->toFunction()->setFlatClosureUpvar(i, *vp);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
CallObject::getVarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
{
|
||||
@ -391,6 +369,22 @@ CallObject::setVarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CallObject::containsVarOrArg(PropertyName *name, Value *vp, JSContext *cx)
|
||||
{
|
||||
jsid id = ATOM_TO_JSID(name);
|
||||
const Shape *shape = nativeLookup(cx, id);
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
PropertyOp op = shape->getterOp();
|
||||
if (op != getVarOp && op != getArgOp)
|
||||
return false;
|
||||
|
||||
JS_ALWAYS_TRUE(op(cx, this, INT_TO_JSID(shape->shortid()), vp));
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
call_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
|
||||
{
|
||||
@ -872,6 +866,19 @@ block_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *v
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ClonedBlockObject::containsVar(PropertyName *name, Value *vp, JSContext *cx)
|
||||
{
|
||||
jsid id = ATOM_TO_JSID(name);
|
||||
const Shape *shape = nativeLookup(cx, id);
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(shape->getterOp() == block_getProperty);
|
||||
JS_ALWAYS_TRUE(block_getProperty(cx, this, INT_TO_JSID(shape->shortid()), vp));
|
||||
return true;
|
||||
}
|
||||
|
||||
StaticBlockObject *
|
||||
StaticBlockObject::create(JSContext *cx)
|
||||
{
|
||||
|
@ -168,10 +168,11 @@ class CallObject : public ScopeObject
|
||||
static JSBool setArgumentsOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
|
||||
static JSBool getArgOp(JSContext *cx, JSObject *obj, jsid id, Value *vp);
|
||||
static JSBool getVarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp);
|
||||
static JSBool getUpvarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp);
|
||||
static JSBool setArgOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
|
||||
static JSBool setVarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
|
||||
static JSBool setUpvarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
|
||||
|
||||
/* Return whether this environment contains 'name' and, if so, its value. */
|
||||
bool containsVarOrArg(PropertyName *name, Value *vp, JSContext *cx);
|
||||
};
|
||||
|
||||
class DeclEnvObject : public ScopeObject
|
||||
@ -270,6 +271,9 @@ class ClonedBlockObject : public BlockObject
|
||||
|
||||
/* Assuming 'put' has been called, return the value of the ith let var. */
|
||||
const Value &closedSlot(unsigned i);
|
||||
|
||||
/* Return whether this environment contains 'name' and, if so, its value. */
|
||||
bool containsVar(PropertyName *name, Value *vp, JSContext *cx);
|
||||
};
|
||||
|
||||
extern bool
|
||||
|
Loading…
Reference in New Issue
Block a user