Backout 6bbf3f22bb5d (bug 753158), 38a703b244c2 (bug 753145), c9a5dfa1b07d (bug 767750), cd782fd66995 & 6cf7aa93994c (bug 765956), 0253f34f6bc2 & 41d5c8529748 (bug 771039),94f6bf99a4aa (bug 766447),fad7d06d7dd5 (bug 772303) for winxp pgo-only jsreftest failures (caused by fad7d06d7dd5) and the rest for conflicts, on a CLOSED TREE

This commit is contained in:
Ed Morley 2012-07-12 13:04:51 +01:00
parent 50e0b02bed
commit 4dfabffcbe
66 changed files with 1861 additions and 1088 deletions

View File

@ -21,7 +21,8 @@
#define DOM_PROTOTYPE_SLOT (JSCLASS_GLOBAL_SLOT_COUNT + 1)
// We use these flag bits for the new bindings.
#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
#define JSCLASS_IS_DOMJSCLASS JSCLASS_USERBIT1
#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT2
namespace mozilla {
namespace dom {

View File

@ -10,7 +10,6 @@
#include "builtin/Eval.h"
#include "frontend/BytecodeCompiler.h"
#include "mozilla/HashFunctions.h"
#include "vm/GlobalObject.h"
#include "jsinterpinlines.h"
@ -31,42 +30,108 @@ AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
#endif
}
static bool
IsEvalCacheCandidate(JSScript *script)
void
EvalCache::purge()
{
// Make sure there are no inner objects which might use the wrong parent
// and/or call scope by reusing the previous eval's script. Skip the
// script's first object, which entrains the eval's scope.
return script->savedCallerFun &&
!script->hasSingletons &&
script->objects()->length == 1 &&
!script->hasRegexps();
// Purge all scripts from the eval cache. In addition to removing them from
// table_, null out the evalHashLink field of any script removed. Since
// evalHashLink is in a union with globalObject, this allows the GC to
// indiscriminately use the union as a nullable globalObject pointer.
for (size_t i = 0; i < ArrayLength(table_); ++i) {
for (JSScript **listHeadp = &table_[i]; *listHeadp; ) {
JSScript *script = *listHeadp;
JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
*listHeadp = script->evalHashLink();
script->evalHashLink() = NULL;
}
}
}
/* static */ HashNumber
EvalCacheHashPolicy::hash(const EvalCacheLookup &l)
JSScript **
EvalCache::bucket(JSLinearString *str)
{
return AddToHash(HashString(l.str->chars(), l.str->length()),
l.caller,
l.staticLevel,
l.version,
l.compartment);
const jschar *s = str->chars();
size_t n = str->length();
if (n > 100)
n = 100;
uint32_t h;
for (h = 0; n; s++, n--)
h = JS_ROTATE_LEFT32(h, 4) ^ *s;
h *= JS_GOLDEN_RATIO;
h >>= 32 - SHIFT;
JS_ASSERT(h < ArrayLength(table_));
return &table_[h];
}
/* static */ bool
EvalCacheHashPolicy::match(JSScript *script, const EvalCacheLookup &l)
static JSScript *
EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, unsigned staticLevel,
JSPrincipals *principals, JSObject &scopeobj, JSScript **bucket)
{
JS_ASSERT(IsEvalCacheCandidate(script));
// Cache local eval scripts indexed by source qualified by scope.
//
// An eval cache entry should never be considered a hit unless its
// strictness matches that of the new eval code. The existing code takes
// care of this, because hits are qualified by the function from which
// eval was called, whose strictness doesn't change. (We don't cache evals
// in eval code, so the calling function corresponds to the calling script,
// and its strictness never varies.) Scripts produced by calls to eval from
// global code aren't cached.
//
// FIXME bug 620141: Qualify hits by calling script rather than function.
// Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
// to avoid caching nested evals in functions (thus potentially mismatching
// on strict mode), and we could cache evals in global code if desired.
unsigned count = 0;
JSScript **scriptp = bucket;
// Get the source string passed for safekeeping in the atom map
// by the prior eval to frontend::CompileScript.
JSAtom *keyStr = script->atoms[0];
JSVersion version = cx->findVersion();
JSScript *script;
JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals;
while ((script = *scriptp) != NULL) {
if (script->savedCallerFun &&
script->staticLevel == staticLevel &&
script->getVersion() == version &&
!script->hasSingletons &&
(!subsume || script->principals == principals ||
(subsume(principals, script->principals) &&
subsume(script->principals, principals))))
{
// Get the prior (cache-filling) eval's saved caller function.
// See frontend::CompileScript.
JSFunction *fun = script->getCallerFunction();
return EqualStrings(keyStr, l.str) &&
script->getCallerFunction() == l.caller &&
script->staticLevel == l.staticLevel &&
script->getVersion() == l.version &&
script->compartment() == l.compartment;
if (fun == caller->fun()) {
/*
* Get the source string passed for safekeeping in the atom map
* by the prior eval to frontend::CompileScript.
*/
JSAtom *src = script->atoms[0];
if (src == str || EqualStrings(src, str)) {
// Source matches. Make sure there are no inner objects
// which might use the wrong parent and/or call scope by
// reusing the previous eval's script. Skip the script's
// first object, which entrains the eval's scope.
JS_ASSERT(script->objects()->length >= 1);
if (script->objects()->length == 1 &&
!script->hasRegexps()) {
JS_ASSERT(staticLevel == script->staticLevel);
*scriptp = script->evalHashLink();
script->evalHashLink() = NULL;
return script;
}
}
}
}
static const unsigned EVAL_CACHE_CHAIN_LIMIT = 4;
if (++count == EVAL_CACHE_CHAIN_LIMIT)
return NULL;
scriptp = &script->evalHashLink();
}
return NULL;
}
// There are two things we want to do with each script executed in EvalKernel:
@ -76,21 +141,21 @@ EvalCacheHashPolicy::match(JSScript *script, const EvalCacheLookup &l)
// NB: Although the eval cache keeps a script alive wrt to the JS engine, from
// a jsdbgapi user's perspective, we want each eval() to create and destroy a
// script. This hides implementation details and means we don't have to deal
// with calls to JS_GetScriptObject for scripts in the eval cache.
// with calls to JS_GetScriptObject for scripts in the eval cache (currently,
// script->object aliases script->evalHashLink()).
class EvalScriptGuard
{
JSContext *cx_;
JSLinearString *str_;
JSScript **bucket_;
Rooted<JSScript*> script_;
/* These fields are only valid if lookup_.str is non-NULL. */
EvalCacheLookup lookup_;
EvalCache::AddPtr p_;
public:
EvalScriptGuard(JSContext *cx)
: cx_(cx), script_(cx)
{
lookup_.str = NULL;
EvalScriptGuard(JSContext *cx, JSLinearString *str)
: cx_(cx),
str_(str),
script_(cx) {
bucket_ = cx->runtime->evalCache.bucket(str);
}
~EvalScriptGuard() {
@ -98,23 +163,17 @@ class EvalScriptGuard
CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_);
script_->isActiveEval = false;
script_->isCachedEval = true;
if (lookup_.str && IsEvalCacheCandidate(script_))
cx_->runtime->evalCache.relookupOrAdd(p_, lookup_, script_);
script_->evalHashLink() = *bucket_;
*bucket_ = script_;
}
}
void lookupInEvalCache(JSLinearString *str, JSFunction *caller, unsigned staticLevel)
{
lookup_.str = str;
lookup_.caller = caller;
lookup_.staticLevel = staticLevel;
lookup_.version = cx_->findVersion();
lookup_.compartment = cx_->compartment;
p_ = cx_->runtime->evalCache.lookupForAdd(lookup_);
if (p_) {
script_ = *p_;
cx_->runtime->evalCache.remove(p_);
js_CallNewScriptHook(cx_, script_, NULL);
void lookupInEvalCache(StackFrame *caller, unsigned staticLevel,
JSPrincipals *principals, JSObject &scopeobj) {
if (JSScript *found = EvalCacheLookup(cx_, str_, caller, staticLevel,
principals, scopeobj, bucket_)) {
js_CallNewScriptHook(cx_, found, NULL);
script_ = found;
script_->isCachedEval = false;
script_->isActiveEval = true;
}
@ -188,6 +247,11 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
if (!ComputeThis(cx, caller))
return false;
thisv = caller->thisValue();
#ifdef DEBUG
jsbytecode *callerPC = caller->pcQuadratic(cx);
JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL);
#endif
} else {
JS_ASSERT(args.callee().global() == *scopeobj);
staticLevel = 0;
@ -246,12 +310,12 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
}
}
EvalScriptGuard esg(cx);
EvalScriptGuard esg(cx, linearStr);
JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
esg.lookupInEvalCache(linearStr, caller->fun(), staticLevel);
esg.lookupInEvalCache(caller, staticLevel, principals, *scopeobj);
if (!esg.foundScript()) {
unsigned lineno;
@ -263,9 +327,10 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
bool compileAndGo = true;
bool noScriptRval = false;
bool needScriptGlobal = false;
JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller,
principals, originPrincipals,
compileAndGo, noScriptRval,
compileAndGo, noScriptRval, needScriptGlobal,
chars, length, filename,
lineno, cx->findVersion(), linearStr,
staticLevel);

View File

@ -576,7 +576,7 @@ ExecuteRegExp(JSContext *cx, Native native, unsigned argc, Value *vp)
return false;
/* Step 4. */
Value lastIndex = reobj->getLastIndex();
const Value &lastIndex = reobj->getLastIndex();
/* Step 5. */
double i;
@ -588,7 +588,7 @@ ExecuteRegExp(JSContext *cx, Native native, unsigned argc, Value *vp)
i = 0;
const jschar *chars = linearInput->chars();
size_t length = linearInput->length();
size_t length = input->length();
/* Step 9a. */
if (i < 0 || i > length) {

View File

@ -21,10 +21,54 @@
using namespace js;
using namespace js::frontend;
bool
MarkInnerAndOuterFunctions(JSContext *cx, JSScript* script)
{
AssertRootingUnnecessary safe(cx);
Vector<JSScript *, 16> worklist(cx);
if (!worklist.append(script))
return false;
while (worklist.length()) {
JSScript *outer = worklist.back();
worklist.popBack();
if (outer->hasObjects()) {
ObjectArray *arr = outer->objects();
/*
* If this is an eval script, don't treat the saved caller function
* stored in the first object slot as an inner function.
*/
size_t start = outer->savedCallerFun ? 1 : 0;
for (size_t i = start; i < arr->length; i++) {
JSObject *obj = arr->vector[i];
if (!obj->isFunction())
continue;
JSFunction *fun = obj->toFunction();
JS_ASSERT(fun->isInterpreted());
JSScript *inner = fun->script();
if (outer->function() && outer->function()->isHeavyweight()) {
outer->isOuterFunction = true;
inner->isInnerFunction = true;
}
if (!inner->hasObjects())
continue;
if (!worklist.append(inner))
return false;
}
}
}
return true;
}
JSScript *
frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
JSPrincipals *principals, JSPrincipals *originPrincipals,
bool compileAndGo, bool noScriptRval,
bool compileAndGo, bool noScriptRval, bool needScriptGlobal,
const jschar *chars, size_t length,
const char *filename, unsigned lineno, JSVersion version,
JSString *source_ /* = NULL */,
@ -64,13 +108,14 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
return NULL;
bool savedCallerFun = compileAndGo && callerFrame && callerFrame->isFunctionFrame();
GlobalObject *globalObject = needScriptGlobal ? GetCurrentGlobal(cx) : NULL;
Rooted<JSScript*> script(cx, JSScript::Create(cx,
/* enclosingScope = */ NullPtr(),
savedCallerFun,
principals,
originPrincipals,
compileAndGo,
noScriptRval,
globalObject,
version,
staticLevel));
if (!script)
@ -204,6 +249,9 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
bce.tellDebuggerAboutCompiledScript(cx);
if (!MarkInnerAndOuterFunctions(cx, script))
return NULL;
return script;
}
@ -231,13 +279,14 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun,
if (!funtc.init())
return false;
GlobalObject *globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
Rooted<JSScript*> script(cx, JSScript::Create(cx,
/* enclosingScope = */ NullPtr(),
/* savedCallerFun = */ false,
principals,
originPrincipals,
/* compileAndGo = */ false,
/* noScriptRval = */ false,
globalObject,
version,
staticLevel));
if (!script)

View File

@ -16,7 +16,7 @@ namespace frontend {
JSScript *
CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
JSPrincipals *principals, JSPrincipals *originPrincipals,
bool compileAndGo, bool noScriptRval,
bool compileAndGo, bool noScriptRval, bool needScriptGlobal,
const jschar *chars, size_t length,
const char *filename, unsigned lineno, JSVersion version,
JSString *source_ = NULL, unsigned staticLevel = 0);

View File

@ -673,31 +673,12 @@ PushStatementBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff
PushStatement(bce, stmt, type);
}
/*
* Return the enclosing lexical scope, which is the innermost enclosing static
* block object or compiler created function.
*/
static JSObject *
EnclosingStaticScope(BytecodeEmitter *bce)
{
if (bce->blockChain)
return bce->blockChain;
if (!bce->sc->inFunction()) {
JS_ASSERT(!bce->parent);
return NULL;
}
return bce->sc->fun();
}
// Push a block scope statement and link blockObj into bce->blockChain.
static void
PushBlockScopeBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StaticBlockObject &blockObj,
ptrdiff_t top)
{
PushStatementBCE(bce, stmt, STMT_BLOCK, top);
blockObj.initEnclosingStaticScope(EnclosingStaticScope(bce));
FinishPushBlockScope(bce, stmt, blockObj);
}
@ -873,7 +854,6 @@ 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;
}
@ -892,48 +872,35 @@ ClonedBlockDepth(BytecodeEmitter *bce)
static bool
EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
{
unsigned skippedScopes = 0;
BytecodeEmitter *bceOfDef = bce;
if (pn->isUsed()) {
/*
* 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);
}
/*
* 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.
*/
ScopeCoordinate sc;
if (JOF_OPTYPE(pn->getOp()) == JOF_QARG) {
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
sc.slot = bceOfDef->sc->bindings.formalIndexToSlot(pn->pn_cookie.slot());
sc.hops = ClonedBlockDepth(bce);
sc.slot = bce->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 < bceOfDef->sc->bindings.numVars()) {
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
sc.slot = bceOfDef->sc->bindings.varIndexToSlot(local);
if (local < bce->sc->bindings.numVars()) {
sc.hops = ClonedBlockDepth(bce);
sc.slot = bce->sc->bindings.varIndexToSlot(local);
} else {
unsigned depth = local - bceOfDef->sc->bindings.numVars();
StaticBlockObject *b = bceOfDef->blockChain;
unsigned depth = local - bce->sc->bindings.numVars();
unsigned hops = 0;
StaticBlockObject *b = bce->blockChain;
while (!b->containsVarAtDepth(depth)) {
if (b->needsClone())
skippedScopes++;
hops++;
b = b->enclosingBlock();
}
sc.hops = skippedScopes;
sc.slot = b->localIndexToSlot(bceOfDef->sc->bindings, local);
sc.hops = hops;
sc.slot = b->localIndexToSlot(bce->sc->bindings, local);
}
}
@ -947,12 +914,8 @@ EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
JS_ASSERT_IF(pn->isKind(PNK_NAME), JOF_OPTYPE(op) == JOF_QARG || JOF_OPTYPE(op) == JOF_LOCAL);
JS_ASSERT(!pn->pn_cookie.isFree());
if (!bce->isAliasedName(pn)) {
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);
if (!bce->isAliasedName(pn))
return EmitUnaliasedVarOp(cx, op, pn->pn_cookie.slot(), bce);
}
switch (op) {
case JSOP_GETARG: case JSOP_GETLOCAL: op = JSOP_GETALIASEDVAR; break;
@ -1208,34 +1171,44 @@ TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
static bool
BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
{
Definition *dn;
JSOp op;
Definition::Kind dn_kind;
JS_ASSERT(pn->isKind(PNK_NAME));
/* Don't attempt if 'pn' is already bound, deoptimized, or a nop. */
if ((pn->pn_dflags & PND_BOUND) || pn->isDeoptimized() || pn->getOp() == JSOP_NOP)
/* Idempotency tests come first, since we may be called more than once. */
if (pn->pn_dflags & PND_BOUND)
return true;
/* JSOP_CALLEE is pre-bound by definition. */
/* No cookie initialized for callee; it is pre-bound by definition. */
JS_ASSERT(!pn->isOp(JSOP_CALLEE));
/*
* The parser already linked name uses to definitions when (where not
* prevented by non-lexical constructs like 'with' and 'eval').
* The parser linked all uses (including forward references) to their
* definitions, unless a with statement or direct eval intervened.
*/
Definition *dn;
if (pn->isUsed()) {
JS_ASSERT(pn->pn_cookie.isFree());
dn = pn->pn_lexdef;
JS_ASSERT(dn->isDefn());
if (pn->isDeoptimized())
return true;
pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
} else if (pn->isDefn()) {
dn = (Definition *) pn;
} else {
return true;
if (!pn->isDefn())
return true;
dn = (Definition *) pn;
}
JSOp op = pn->getOp();
op = pn->getOp();
if (op == JSOP_NOP)
return true;
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
JS_ASSERT_IF(dn->kind() == Definition::CONST, pn->pn_dflags & PND_CONST);
RootedAtom atom(cx, pn->pn_atom);
UpvarCookie cookie = dn->pn_cookie;
dn_kind = dn->kind();
/*
* Turn attempts to mutate const-declared bindings into get ops (for
@ -1251,7 +1224,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
case JSOP_SETCONST:
break;
case JSOP_DELNAME:
if (dn->kind() != Definition::UNKNOWN) {
if (dn_kind != Definition::UNKNOWN) {
if (bce->callerFrame && dn->isTopLevel())
JS_ASSERT(bce->script->compileAndGo);
else
@ -1264,7 +1237,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (pn->isConst()) {
if (bce->sc->needStrictChecks()) {
JSAutoByteString name;
if (!js_AtomToPrintableString(cx, pn->pn_atom, &name) ||
if (!js_AtomToPrintableString(cx, atom, &name) ||
!bce->reportStrictModeError(pn, JSMSG_READ_ONLY, name.ptr()))
{
return false;
@ -1274,7 +1247,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
}
if (dn->pn_cookie.isFree()) {
if (cookie.isFree()) {
StackFrame *caller = bce->callerFrame;
if (caller) {
JS_ASSERT(bce->script->compileAndGo);
@ -1315,34 +1288,34 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return true;
}
/*
* At this point, we are only dealing with uses that have already been
* bound to definitions via pn_lexdef. The rest of this routine converts
* the parse node of the use from its initial JSOP_*NAME* op to a LOCAL/ARG
* op. This requires setting the node's pn_cookie with a pair (level, slot)
* where 'level' is the number of function scopes between the use and the
* def and 'slot' is the index to emit as the immediate of the ARG/LOCAL
* op. For example, in this code:
*
* function(a,b,x) { return x }
* function(y) { function() { return y } }
*
* x will get (level = 0, slot = 2) and y will get (level = 1, slot = 0).
*/
JS_ASSERT(!pn->isDefn());
JS_ASSERT(pn->isUsed());
JS_ASSERT(pn->pn_lexdef);
JS_ASSERT(pn->pn_cookie.isFree());
uint16_t level = cookie.level();
JS_ASSERT(bce->script->staticLevel >= level);
const unsigned skip = bce->script->staticLevel - level;
if (skip != 0)
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
* rewrite pn_op and update pn accordingly.
*/
switch (dn->kind()) {
switch (dn_kind) {
case Definition::UNKNOWN:
return true;
case Definition::LET:
switch (op) {
case JSOP_NAME: op = JSOP_GETLOCAL; break;
case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
default: JS_NOT_REACHED("let");
}
break;
case Definition::ARG:
switch (op) {
case JSOP_NAME: op = JSOP_GETARG; break;
@ -1359,16 +1332,7 @@ 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);
JS_ASSERT((bce->sc->fun()->flags & JSFUN_LAMBDA) && atom == bce->sc->fun()->atom);
/*
* Leave pn->isOp(JSOP_NAME) if bce->fun is heavyweight to
@ -1406,9 +1370,10 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
/* FALL THROUGH */
case Definition::FUNCTION:
case Definition::CONST:
case Definition::LET:
default:
JS_ASSERT_IF(dn_kind != Definition::FUNCTION,
dn_kind == Definition::VAR ||
dn_kind == Definition::CONST);
switch (op) {
case JSOP_NAME: op = JSOP_GETLOCAL; break;
case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
@ -1419,42 +1384,14 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
default: JS_NOT_REACHED("local");
}
JS_ASSERT_IF(dn_kind == Definition::CONST, pn->pn_dflags & PND_CONST);
break;
default:
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, skip, dn->pn_cookie.slot()))
if (!pn->pn_cookie.set(bce->sc->context, 0, cookie.slot()))
return false;
pn->pn_dflags |= PND_BOUND;
return true;
}
@ -1705,8 +1642,11 @@ BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext *cx)
js_CallNewScriptHook(cx, script, script->function());
if (!parent) {
GlobalObject *compileAndGoGlobal = NULL;
if (script->compileAndGo)
compileAndGoGlobal = &script->global();
if (script->compileAndGo) {
compileAndGoGlobal = script->globalObject;
if (!compileAndGoGlobal)
compileAndGoGlobal = &sc->scopeChain()->global();
}
Debugger::onNewScript(cx, script, compileAndGoGlobal);
}
}
@ -3492,14 +3432,17 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
* Specialize to avoid ECMA "reference type" values on the operand
* stack, which impose pervasive runtime "GetValue" costs.
*/
jsatomid atomIndex = (jsatomid) -1;
jsatomid atomIndex = (jsatomid) -1; /* quell GCC overwarning */
jsbytecode offset = 1;
switch (lhs->getKind()) {
case PNK_NAME:
if (!BindNameToSlot(cx, bce, lhs))
return false;
if (lhs->pn_cookie.isFree()) {
if (!lhs->pn_cookie.isFree()) {
JS_ASSERT(lhs->pn_cookie.level() == 0);
atomIndex = lhs->pn_cookie.slot();
} else {
if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
return false;
if (!lhs->isConst()) {
@ -4274,6 +4217,7 @@ EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
* destructure y
* pick 1
* dup +1 SRC_DESTRUCTLET + offset to enterlet0
* pick
* destructure z
* pick 1
* pop -1
@ -4880,15 +4824,15 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
JS_ASSERT_IF(bce->sc->inStrictMode(), sc.inStrictMode());
// Inherit most things (principals, version, etc) from the parent.
GlobalObject *globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
Rooted<JSScript*> parent(cx, bce->script);
Rooted<JSObject*> enclosingScope(cx, EnclosingStaticScope(bce));
Rooted<JSScript*> script(cx, JSScript::Create(cx,
enclosingScope,
/* savedCallerFun = */ false,
parent->principals,
parent->originPrincipals,
parent->compileAndGo,
/* noScriptRval = */ false,
globalObject,
parent->getVersion(),
parent->staticLevel + 1));
if (!script)

View File

@ -736,10 +736,6 @@ struct ParseNode {
optimizable via an upvar opcode */
#define PND_CLOSED 0x200 /* variable is closed over */
#define PND_DEFAULT 0x400 /* definition is an arg with a default */
#define PND_IMPLICITARGUMENTS 0x800 /* the definition is a placeholder for
'arguments' that has been converted
into a definition after the function
body has been parsed. */
/* Flags to propagate from uses to definition. */
#define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
@ -786,7 +782,6 @@ struct ParseNode {
bool isDeoptimized() const { return test(PND_DEOPTIMIZED); }
bool isAssigned() const { return test(PND_ASSIGNED); }
bool isClosed() const { return test(PND_CLOSED); }
bool isImplicitArguments() const { return test(PND_IMPLICITARGUMENTS); }
/*
* True iff this definition creates a top-level binding in the overall

View File

@ -102,6 +102,14 @@ PushStatementTC(TreeContext *tc, StmtInfoTC *stmt, StmtType type)
stmt->isFunctionBodyBlock = false;
}
// Push a block scope statement and link blockObj into tc->blockChain.
static void
PushBlockScopeTC(TreeContext *tc, StmtInfoTC *stmt, StaticBlockObject &blockObj)
{
PushStatementTC(tc, stmt, STMT_BLOCK);
FinishPushBlockScope(tc, stmt, blockObj);
}
Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
const jschar *chars, size_t length, const char *fn, unsigned ln, JSVersion v,
bool foldConstants, bool compileAndGo)
@ -516,10 +524,7 @@ CheckStrictParameters(JSContext *cx, Parser *parser)
return false;
// Start with lastVariable(), not the last argument, for destructuring.
Shape::Range r = sc->bindings.lastVariable();
Shape::Range::AutoRooter root(cx, &r);
for (; !r.empty(); r.popFront()) {
for (Shape::Range r = sc->bindings.lastVariable(); !r.empty(); r.popFront()) {
jsid id = r.front().propid();
if (!JSID_IS_ATOM(id))
continue;
@ -665,15 +670,12 @@ Parser::functionBody(FunctionBodyType type)
if (atom == arguments) {
/*
* Turn 'dn' into a proper definition so uses will be bound as
* GETLOCAL in the emitter. The PND_IMPLICITARGUMENTS flag informs
* CompExprTransplanter (and anyone else) that this definition node
* has no proper declaration in the parse tree.
* GETLOCAL in the emitter.
*/
if (!BindLocalVariable(context, tc, dn, VARIABLE))
return NULL;
dn->setOp(JSOP_GETLOCAL);
dn->pn_dflags &= ~PND_PLACEHOLDER;
dn->pn_dflags |= PND_IMPLICITARGUMENTS;
/* NB: this leaves r invalid so we must break immediately. */
tc->lexdeps->remove(arguments);
@ -2139,16 +2141,12 @@ struct RemoveDecl {
static void
PopStatementTC(TreeContext *tc)
{
StaticBlockObject *blockObj = tc->topStmt->blockObj;
JS_ASSERT(!!blockObj == (tc->topStmt->isBlockScope));
FinishPopStatement(tc);
if (blockObj) {
JS_ASSERT(!blockObj->inDictionaryMode());
ForEachLetDef(tc, *blockObj, RemoveDecl());
blockObj->resetPrevBlockChainFromParser();
if (tc->topStmt->isBlockScope) {
StaticBlockObject &blockObj = *tc->topStmt->blockObj;
JS_ASSERT(!blockObj.inDictionaryMode());
ForEachLetDef(tc, blockObj, RemoveDecl());
}
FinishPopStatement(tc);
}
static inline bool
@ -2757,27 +2755,22 @@ Parser::returnOrYield(bool useAssignExpr)
}
static ParseNode *
PushLexicalScope(JSContext *cx, Parser *parser, StaticBlockObject &blockObj, StmtInfoTC *stmt)
PushLexicalScope(JSContext *cx, Parser *parser, StaticBlockObject &obj, StmtInfoTC *stmt)
{
ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, parser);
if (!pn)
return NULL;
ObjectBox *blockbox = parser->newObjectBox(&blockObj);
ObjectBox *blockbox = parser->newObjectBox(&obj);
if (!blockbox)
return NULL;
TreeContext *tc = parser->tc;
PushStatementTC(tc, stmt, STMT_BLOCK);
blockObj.initPrevBlockChainFromParser(tc->blockChain);
FinishPushBlockScope(tc, stmt, blockObj);
PushBlockScopeTC(parser->tc, stmt, obj);
pn->setOp(JSOP_LEAVEBLOCK);
pn->pn_objbox = blockbox;
pn->pn_cookie.makeFree();
pn->pn_dflags = 0;
if (!GenerateBlockId(tc, stmt->blockid))
if (!GenerateBlockId(parser->tc, stmt->blockid))
return NULL;
pn->pn_blockid = stmt->blockid;
return pn;
@ -3779,7 +3772,7 @@ Parser::letStatement()
stmt->downScope = tc->topScopeStmt;
tc->topScopeStmt = stmt;
blockObj->initPrevBlockChainFromParser(tc->blockChain);
blockObj->setEnclosingBlock(tc->blockChain);
tc->blockChain = blockObj;
stmt->blockObj = blockObj;
@ -4908,16 +4901,11 @@ class CompExprTransplanter {
bool genexp;
unsigned adjust;
unsigned funcLevel;
HashSet<Definition *> visitedImplicitArguments;
public:
CompExprTransplanter(ParseNode *pn, Parser *parser, bool ge, unsigned adj)
: root(pn), parser(parser), genexp(ge), adjust(adj), funcLevel(0),
visitedImplicitArguments(parser->context)
{}
bool init() {
return visitedImplicitArguments.init();
: root(pn), parser(parser), genexp(ge), adjust(adj), funcLevel(0)
{
}
bool transplant(ParseNode *pn);
@ -5192,21 +5180,6 @@ CompExprTransplanter::transplant(ParseNode *pn)
tc->parent->lexdeps->remove(atom);
if (!tc->lexdeps->put(atom, dn))
return false;
} else if (dn->isImplicitArguments()) {
/*
* Implicit 'arguments' Definition nodes (see
* PND_IMPLICITARGUMENTS in Parser::functionBody) are only
* reachable via the lexdefs of their uses. Unfortunately,
* there may be multiple uses, so we need to maintain a set
* to only bump the definition once.
*/
if (genexp && !visitedImplicitArguments.has(dn)) {
if (!BumpStaticLevel(dn, tc))
return false;
AdjustBlockId(dn, adjust, tc);
if (!visitedImplicitArguments.put(dn))
return false;
}
}
}
}
@ -5289,9 +5262,6 @@ Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
pnp = &pn->pn_expr;
CompExprTransplanter transplanter(kid, this, kind == PNK_SEMI, adjust);
if (!transplanter.init())
return NULL;
transplanter.transplant(kid);
JS_ASSERT(tc->blockChain && tc->blockChain == pn->pn_objbox->object);

View File

@ -145,6 +145,7 @@ frontend::FinishPushBlockScope(ContextT *ct, typename ContextT::StmtInfo *stmt,
StaticBlockObject &blockObj)
{
stmt->isBlockScope = true;
blockObj.setEnclosingBlock(ct->blockChain);
stmt->downScope = ct->topScopeStmt;
ct->topScopeStmt = stmt;
ct->blockChain = &blockObj;

View File

@ -1,5 +0,0 @@
assertEq(((function() arguments) for (x in [1])).next()(42)[0], 42);
assertEq(((function() {return arguments}) for (x in [1])).next()(42)[0], 42);
assertEq(((function() {return arguments[0] + arguments[1]}) for (x in [1])).next()(41,1), 42);
assertEq(((function() {return arguments[0] + (function() { return arguments[0]})(arguments[1])}) for (x in [1])).next()(41,1), 42);
assertEq(((function() { var arguments = 3; return arguments}) for (x in [1])).next()(42), 3);

View File

@ -314,25 +314,33 @@ 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:
case JSOP_SETCONST:
extendsScope_ = true;
isInlineable = canTrackVars = false;
break;
case JSOP_EVAL:
extendsScope_ = true;
isInlineable = canTrackVars = false;
break;
case JSOP_ENTERWITH:
addsScopeObjects_ = true;
isJaegerCompileable = isInlineable = canTrackVars = false;
break;
@ -340,6 +348,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_ENTERLET1:
case JSOP_ENTERBLOCK:
case JSOP_LEAVEBLOCK:
addsScopeObjects_ = true;
isInlineable = false;
break;

View File

@ -359,6 +359,16 @@ 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();
@ -833,6 +843,8 @@ class ScriptAnalysis
bool usesThisValue_:1;
bool hasFunctionCalls_:1;
bool modifiesArguments_:1;
bool extendsScope_:1;
bool addsScopeObjects_:1;
bool localsAliasStack_:1;
bool isInlineable:1;
bool isJaegerCompileable:1;
@ -888,6 +900,15 @@ class ScriptAnalysis
*/
bool modifiesArguments() { return modifiesArguments_; }
/*
* True if the script may extend declarations in its top level scope with
* dynamic fun/var declarations or through eval.
*/
bool extendsScope() { return extendsScope_; }
/* True if the script may add block or with objects to its scope chain. */
bool addsScopeObjects() { return addsScopeObjects_; }
/*
* True if there are any LOCAL opcodes aliasing values on the stack (above
* script->nfixed).
@ -1096,6 +1117,25 @@ class ScriptAnalysis
return lifetimes[slot];
}
/*
* If a NAME or similar opcode is definitely accessing a particular slot
* of a script this one is nested in, get that script/slot.
*/
struct NameAccess {
JSScript *script;
types::TypeScriptNesting *nesting;
uint32_t slot;
/* Decompose the slot above. */
bool arg;
uint32_t index;
const Value **basePointer() const {
return arg ? &nesting->argArray : &nesting->varArray;
}
};
NameAccess resolveNameAccess(JSContext *cx, jsid id, bool addDependency = false);
void printSSA(JSContext *cx);
void printTypes(JSContext *cx);

View File

@ -876,9 +876,6 @@ JSRuntime::init(uint32_t maxbytes)
if (!scriptFilenameTable.init())
return false;
if (!evalCache.init())
return false;
debugScopes = this->new_<DebugScopes>(this);
if (!debugScopes || !debugScopes->init()) {
Foreground::delete_(debugScopes);
@ -3369,11 +3366,15 @@ JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto_, JSObject *parent
JS_ASSERT(clasp != &FunctionClass);
JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
if (proto && !proto->setNewTypeUnknown(cx))
return NULL;
JSObject *obj = NewObjectWithClassProto(cx, clasp, proto, parent);
AssertRootingUnnecessary safe(cx);
if (obj) {
if (clasp->ext.equality)
MarkTypeObjectFlags(cx, obj, OBJECT_FLAG_SPECIAL_EQUALITY);
MarkTypeObjectUnknownProperties(cx, obj->type());
}
JS_ASSERT_IF(obj, obj->getParent());
@ -4662,15 +4663,8 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent_)
return NULL;
}
/*
* If a function was compiled as compile-and-go or was compiled to be
* lexically nested inside some other script, we cannot clone it without
* breaking the compiler's assumptions.
*/
RootedFunction fun(cx, funobj->toFunction());
if (fun->isInterpreted() &&
(fun->script()->compileAndGo || fun->script()->enclosingStaticScope()))
{
if (fun->isInterpreted() && fun->script()->compileAndGo) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
return NULL;
@ -4908,9 +4902,10 @@ CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj_,
bool compileAndGo = cx->hasRunOption(JSOPTION_COMPILE_N_GO);
bool noScriptRval = cx->hasRunOption(JSOPTION_NO_SCRIPT_RVAL);
bool needScriptGlobal = true;
return frontend::CompileScript(cx, obj, NULL, principals, originPrincipals,
compileAndGo, noScriptRval, chars, length,
filename, lineno, version);
compileAndGo, noScriptRval, needScriptGlobal,
chars, length, filename, lineno, version);
}
extern JS_PUBLIC_API(JSScript *)
@ -5113,9 +5108,10 @@ CompileUTF8FileHelper(JSContext *cx, JSObject *obj_, JSPrincipals *principals,
if (JS_DecodeUTF8(cx, buf, len, decodebuf, &decodelen)) {
bool compileAndGo = cx->hasRunOption(JSOPTION_COMPILE_N_GO);
bool noScriptRval = cx->hasRunOption(JSOPTION_NO_SCRIPT_RVAL);
bool needScriptGlobal = true;
script = frontend::CompileScript(cx, obj, NULL, principals, NULL,
compileAndGo, noScriptRval, decodebuf, decodelen,
filename, 1, cx->findVersion());
compileAndGo, noScriptRval, needScriptGlobal,
decodebuf, decodelen, filename, 1, cx->findVersion());
} else {
script = NULL;
}
@ -5185,7 +5181,9 @@ JS_PUBLIC_API(JSObject *)
JS_GetGlobalFromScript(JSScript *script)
{
JS_ASSERT(!script->isCachedEval);
return &script->global();
JS_ASSERT(script->globalObject);
return script->globalObject;
}
static JSFunction *
@ -5380,7 +5378,7 @@ JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *scriptArg_, jsval *rval
* mozilla, but there doesn't seem to be one, so we handle it here.
*/
if (scriptArg->compartment() != obj->compartment()) {
script = CloneScript(cx, NullPtr(), NullPtr(), scriptArg);
script = CloneScript(cx, scriptArg);
if (!script.get())
return false;
} else {
@ -5411,12 +5409,13 @@ EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj_,
bool compileAndGo = true;
bool noScriptRval = !rval;
bool needScriptGlobal = true;
CHECK_REQUEST(cx);
AutoLastFrameCheck lfc(cx);
JSScript *script = frontend::CompileScript(cx, obj, NULL, principals, originPrincipals,
compileAndGo, noScriptRval, chars, length,
filename, lineno, compileVersion);
compileAndGo, noScriptRval, needScriptGlobal,
chars, length, filename, lineno, compileVersion);
if (!script)
return false;

View File

@ -3840,7 +3840,7 @@ struct JSClass {
#define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */
#define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */
#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */
#define JSCLASS_IS_DOMJSCLASS (1<<4) /* objects are DOM */
/* (1<<4) is unused */
#define JSCLASS_IMPLEMENTS_BARRIERS (1<<5) /* Correctly implements GC read
and write barriers */
#define JSCLASS_DOCUMENT_OBSERVER (1<<6) /* DOM document observer */
@ -4645,10 +4645,6 @@ extern JS_PUBLIC_API(JSFunction *)
JS_DefineFunctionById(JSContext *cx, JSObject *obj, jsid id, JSNative call,
unsigned nargs, unsigned attrs);
/*
* Clone a top-level function into a new scope. This function will dynamically
* fail if funobj was lexically nested inside some other function.
*/
extern JS_PUBLIC_API(JSObject *)
JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);

View File

@ -173,25 +173,18 @@ class ToSourceCache
void purge();
};
struct EvalCacheLookup
class EvalCache
{
JSLinearString *str;
JSFunction *caller;
unsigned staticLevel;
JSVersion version;
JSCompartment *compartment;
static const unsigned SHIFT = 6;
static const unsigned LENGTH = 1 << SHIFT;
JSScript *table_[LENGTH];
public:
EvalCache() { PodArrayZero(table_); }
JSScript **bucket(JSLinearString *str);
void purge();
};
struct EvalCacheHashPolicy
{
typedef EvalCacheLookup Lookup;
static HashNumber hash(const Lookup &l);
static bool match(JSScript *script, const EvalCacheLookup &l);
};
typedef HashSet<JSScript *, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
class NativeIterCache
{
static const size_t SIZE = size_t(1) << 8;

View File

@ -293,8 +293,11 @@ class CompartmentChecker
}
void check(JSScript *script) {
if (script)
if (script) {
check(script->compartment());
if (!script->isCachedEval && script->globalObject)
check(script->globalObject);
}
}
void check(StackFrame *fp) {

View File

@ -535,6 +535,7 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
if (releaseTypes) {
script->types->destroy();
script->types = NULL;
script->typesPurged = true;
}
}
}

View File

@ -128,7 +128,7 @@ struct JSCompartment
//
// In contrast, JSObject::global() is infallible because marking a JSObject
// always marks its global as well.
// TODO: add infallible JSScript::global()
// TODO: add infallible JSScript::global() and JSContext::global()
//
js::GlobalObject *maybeGlobal() const {
JS_ASSERT_IF(global_, global_->compartment() == this);

View File

@ -185,7 +185,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handle
if (!CheckDebugMode(cx))
return false;
BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc);
BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc, NULL);
if (!site)
return false;
site->setTrap(cx->runtime->defaultFreeOp(), handler, closure);

View File

@ -336,8 +336,7 @@ fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
template<XDRMode mode>
bool
js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
JSObject **objp)
js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript)
{
/* NB: Keep this in sync with CloneInterpretedFunction. */
JSAtom *atom;
@ -383,7 +382,7 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, Han
if (!xdr->codeUint32(&flagsword))
return false;
if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script))
if (!XDRScript(xdr, &script, parentScript))
return false;
if (mode == XDR_DECODE) {
@ -404,13 +403,13 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, Han
}
template bool
js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, JSObject **);
js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *xdr, JSObject **objp, JSScript *parentScript);
template bool
js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, JSObject **);
js::XDRInterpretedFunction(XDRState<XDR_DECODE> *xdr, JSObject **objp, JSScript *parentScript);
JSObject *
js::CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
js::CloneInterpretedFunction(JSContext *cx, HandleFunction srcFun)
{
/* NB: Keep this in sync with XDRInterpretedFunction. */
@ -424,7 +423,7 @@ js::CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleF
return NULL;
Rooted<JSScript*> srcScript(cx, srcFun->script());
JSScript *clonedScript = CloneScript(cx, enclosingScope, clone, srcScript);
JSScript *clonedScript = CloneScript(cx, srcScript);
if (!clonedScript)
return NULL;
@ -1282,23 +1281,20 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
} else {
/*
* Across compartments we have to clone the script for interpreted
* functions. Cross-compartment cloning only happens via JSAPI
* (JS_CloneFunctionObject) which dynamically ensures that 'script' has
* no enclosing lexical scope (only the global scope).
* functions.
*/
if (clone->isInterpreted()) {
RootedScript script(cx, clone->script());
JS_ASSERT(script);
JS_ASSERT(script->compartment() == fun->compartment());
JS_ASSERT(script->compartment() != cx->compartment);
JS_ASSERT(!script->enclosingStaticScope());
clone->mutableScript().init(NULL);
JSScript *cscript = CloneScript(cx, NullPtr(), clone, script);
JSScript *cscript = CloneScript(cx, script);
if (!cscript)
return NULL;
cscript->globalObject = &clone->global();
clone->setScript(cscript);
cscript->setFunction(clone);
if (!clone->setTypeForScriptedFunction(cx))

View File

@ -72,7 +72,6 @@ struct JSFunction : public JSObject
bool isNullClosure() const { return kind() == JSFUN_NULL_CLOSURE; }
bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; }
bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype(); }
bool isNamedLambda() const { return (flags & JSFUN_LAMBDA) && atom; }
uint16_t kind() const { return flags & JSFUN_KINDMASK; }
void setKind(uint16_t k) {
@ -254,15 +253,17 @@ JSFunction::toExtended() const
return static_cast<const js::FunctionExtended *>(this);
}
inline bool
js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->atom; }
namespace js {
template<XDRMode mode>
bool
XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
HandleScript enclosingScript, JSObject **objp);
XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript);
extern JSObject *
CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
CloneInterpretedFunction(JSContext *cx, HandleFunction fun);
} /* namespace js */

View File

@ -3056,7 +3056,7 @@ PurgeRuntime(JSTracer *trc)
rt->newObjectCache.purge();
rt->nativeIterCache.purge();
rt->toSourceCache.purge();
rt->evalCache.clear();
rt->evalCache.purge();
for (ContextIter acx(rt); !acx.done(); acx.next())
acx->purge();
@ -4327,18 +4327,6 @@ JS::CheckStackRoots(JSContext *cx)
// could happen.)
JS_ASSERT(!cx->rootingUnnecessary);
// GCs can't happen when analysis/inference/compilation are active.
if (cx->compartment->activeAnalysis)
return;
// Can switch to the atoms compartment during analysis.
if (IsAtomsCompartment(cx->compartment)) {
for (CompartmentsIter c(rt); !c.done(); c.next()) {
if (c.get()->activeAnalysis)
return;
}
}
AutoCopyFreeListToArenas copy(rt);
JSTracer checker;

View File

@ -1830,8 +1830,7 @@ TypeCompartment::init(JSContext *cx)
TypeObject *
TypeCompartment::newTypeObject(JSContext *cx, JSScript *script,
JSProtoKey key, JSObject *proto_, bool unknown,
bool isDOM)
JSProtoKey key, JSObject *proto_, bool unknown)
{
RootedObject proto(cx, proto_);
TypeObject *object = gc::NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
@ -1839,17 +1838,10 @@ TypeCompartment::newTypeObject(JSContext *cx, JSScript *script,
return NULL;
new(object) TypeObject(proto, key == JSProto_Function, unknown);
if (!cx->typeInferenceEnabled()) {
if (!cx->typeInferenceEnabled())
object->flags |= OBJECT_FLAG_UNKNOWN_MASK;
} else {
if (isDOM) {
object->setFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY
| OBJECT_FLAG_NON_TYPED_ARRAY
| OBJECT_FLAG_NON_PACKED_ARRAY);
} else {
object->setFlagsFromKey(cx, key);
}
}
else
object->setFlagsFromKey(cx, key);
return object;
}
@ -1871,7 +1863,7 @@ TypeCompartment::newAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
JS_ASSERT(!p);
RootedObject proto(cx);
RootedObject global(cx, &key.script->global());
RootedObject global(cx, key.script->global());
if (!js_GetClassPrototype(cx, global, key.kind, &proto, NULL))
return NULL;
@ -1963,7 +1955,7 @@ types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc,
AutoEnterTypeInference enter(cx);
if (!script->ensureRanAnalysis(cx))
if (!script->ensureRanAnalysis(cx, NULL))
return false;
return !script->analysis()->getCode(pc).inLoop;
@ -1975,7 +1967,7 @@ types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script)
if (!cx->typeInferenceEnabled() || !script->hasGlobal())
return true;
JSObject *proto = script->global().getOrCreateArrayPrototype(cx);
JSObject *proto = script->global()->getOrCreateArrayPrototype(cx);
if (!proto)
return true;
@ -2897,6 +2889,8 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
/* Make sure flags are consistent with persistent object state. */
JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE,
interpretedFunction->script()->uninlineable);
JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION,
interpretedFunction->script()->reentrantOuterFunction);
JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
}
@ -3148,6 +3142,105 @@ GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
return TypeScript::InitObject(cx, script, pc, key);
}
/*
* Detach nesting state for script from its parent, removing it entirely if it
* has no children of its own. This happens when walking type information while
* initially resolving NAME accesses, thus will not invalidate any compiler
* dependencies.
*/
static void
DetachNestingParent(JSScript *script)
{
TypeScriptNesting *nesting = script->nesting();
if (!nesting || !nesting->parent)
return;
/* Remove from parent's list of children. */
JSScript **pscript = &nesting->parent->nesting()->children;
while ((*pscript)->nesting() != nesting)
pscript = &(*pscript)->nesting()->next;
*pscript = nesting->next;
nesting->parent = NULL;
/* If this nesting can have no children of its own, destroy it. */
if (!script->isOuterFunction)
script->clearNesting();
}
ScriptAnalysis::NameAccess
ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency)
{
JS_ASSERT(cx->typeInferenceEnabled());
NameAccess access;
PodZero(&access);
if (!JSID_IS_ATOM(id))
return access;
JSAtom *atom = JSID_TO_ATOM(id);
JSScript *script = this->script;
while (script->function() && script->nesting()) {
if (!script->ensureRanInference(cx))
return access;
/*
* Don't resolve names in scripts which use 'let' or 'with'. New names
* bound here can mask variables of the script itself.
*
* Also, don't resolve names in scripts which are generators. Frame
* balancing works differently for generators and we do not maintain
* active frame counts for such scripts.
*/
if (script->analysis()->addsScopeObjects() || script->isGenerator)
return access;
/* Check if the script definitely binds the identifier. */
unsigned index;
BindingKind kind = script->bindings.lookup(cx, atom, &index);
if (kind == ARGUMENT || kind == VARIABLE) {
TypeObject *obj = script->function()->getType(cx);
if (addDependency) {
/*
* Record the dependency which compiled code has on the outer
* function being non-reentrant.
*/
if (TypeSet::HasObjectFlags(cx, obj, OBJECT_FLAG_REENTRANT_FUNCTION))
return access;
}
if (!script->isOuterFunction)
return access;
access.script = script;
access.nesting = script->nesting();
access.slot = (kind == ARGUMENT) ? ArgSlot(index) : LocalSlot(script, index);
access.arg = (kind == ARGUMENT);
access.index = index;
return access;
} else if (kind != NONE) {
return access;
}
/*
* The script's bindings do not contain a name for the function itself,
* don't resolve name accesses on lambdas in DeclEnv objects on the
* scope chain.
*/
if (atom == CallObjectLambdaName(*script->function()))
return access;
if (!script->nesting()->parent)
return access;
script = script->nesting()->parent;
}
return access;
}
/* Analyze type information for a single bytecode. */
bool
ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
@ -3373,7 +3466,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
seen->addType(cx, Type::DoubleType());
/* Handle as a property access. */
PropertyAccess(cx, script, pc, script->global().getType(cx), false, seen, id);
PropertyAccess(cx, script, pc, script->global()->getType(cx), false, seen, id);
if (op == JSOP_CALLGNAME)
pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
@ -3386,8 +3479,24 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
case JSOP_NAME:
case JSOP_CALLNAME: {
TypeSet *seen = bytecodeTypes(pc);
addTypeBarrier(cx, pc, seen, Type::UnknownType());
seen->addSubset(cx, &pushed[0]);
/*
* Try to resolve this name by walking the function's scope nesting.
* If we succeed but the accessed script has had its TypeScript purged
* in the past, we still must use a type barrier: the name access can
* be on a call object which predated the purge, and whose types might
* not be reflected in the reconstructed information.
*/
jsid id = GetAtomId(cx, script, pc, 0);
NameAccess access = resolveNameAccess(cx, id);
if (access.script && !access.script->typesPurged) {
TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
types->addSubsetBarrier(cx, script, pc, seen);
} else {
addTypeBarrier(cx, pc, seen, Type::UnknownType());
}
if (op == JSOP_CALLNAME)
pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
break;
@ -3399,13 +3508,25 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
case JSOP_SETGNAME: {
jsid id = GetAtomId(cx, script, pc, 0);
PropertyAccess(cx, script, pc, script->global().getType(cx),
PropertyAccess(cx, script, pc, script->global()->getType(cx),
true, poppedTypes(pc, 0), id);
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
break;
}
case JSOP_SETNAME:
case JSOP_SETNAME: {
jsid id = GetAtomId(cx, script, pc, 0);
NameAccess access = resolveNameAccess(cx, id);
if (access.script) {
TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
poppedTypes(pc, 0)->addSubset(cx, types);
} else {
cx->compartment->types.monitorBytecode(cx, script, offset);
}
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
break;
}
case JSOP_SETCONST:
cx->compartment->types.monitorBytecode(cx, script, offset);
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
@ -3418,6 +3539,8 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
break;
}
case JSOP_GETALIASEDVAR:
case JSOP_CALLALIASEDVAR:
case JSOP_GETARG:
case JSOP_CALLARG:
case JSOP_GETLOCAL:
@ -3437,11 +3560,12 @@ 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)
if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL || op == JSOP_CALLALIASEDVAR)
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
break;
}
case JSOP_SETALIASEDVAR:
case JSOP_SETARG:
case JSOP_SETLOCAL: {
uint32_t slot = GetBytecodeSlot(script, pc);
@ -3459,24 +3583,6 @@ 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:
@ -3866,7 +3972,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
case JSOP_GENERATOR:
if (script->function()) {
if (script->hasGlobal()) {
JSObject *proto = script->global().getOrCreateGeneratorPrototype(cx);
JSObject *proto = script->global()->getOrCreateGeneratorPrototype(cx);
if (!proto)
return false;
TypeObject *object = proto->getNewType(cx);
@ -3969,6 +4075,45 @@ ScriptAnalysis::analyzeTypes(JSContext *cx)
for (unsigned i = 0; i < script->nfixed; i++)
TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType());
TypeScriptNesting *nesting = script->function() ? script->nesting() : NULL;
if (nesting && nesting->parent) {
/*
* Check whether NAME accesses can be resolved in parent scopes, and
* detach from the parent if so. Even if outdated activations of this
* function are live when the parent is called again, we do not need to
* consider this reentrance as no state in the parent will be used.
*/
if (!nesting->parent->ensureRanInference(cx))
return;
bool detached = false;
/* Don't track for leaf scripts which have no free variables. */
if (!usesScopeChain() && !script->isOuterFunction) {
DetachNestingParent(script);
detached = true;
}
/*
* If the names bound by the script are extensible (DEFFUN, EVAL, ...),
* don't resolve NAME accesses into the parent.
*/
if (!detached && extendsScope()) {
DetachNestingParent(script);
detached = true;
}
if (!detached) {
/*
* Don't track for parents which add call objects or are generators,
* don't resolve NAME accesses into the parent.
*/
if (nesting->parent->analysis()->addsScopeObjects() || nesting->parent->isGenerator)
DetachNestingParent(script);
}
}
TypeInferenceState state(cx);
unsigned offset = 0;
@ -4132,7 +4277,9 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO
}
JSScript *script = fun->script();
if (!script->ensureRanAnalysis(cx) || !script->ensureRanInference(cx)) {
JS_ASSERT(!script->isInnerFunction);
if (!script->ensureRanAnalysis(cx, fun) || !script->ensureRanInference(cx)) {
*pbaseobj = NULL;
cx->compartment->types.setPendingNukeTypes(cx);
return false;
@ -4368,6 +4515,7 @@ AnalyzePoppedThis(JSContext *cx, Vector<SSAUseChain *> *pendingPoppedThis,
}
JSFunction *function = scriptObj->toFunction();
JS_ASSERT(!function->script()->isInnerFunction);
/*
* Generate constraints to clear definite properties from the type
@ -4418,7 +4566,7 @@ AnalyzePoppedThis(JSContext *cx, Vector<SSAUseChain *> *pendingPoppedThis,
static void
CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, JSFunction *fun)
{
if (type->unknownProperties())
if (type->unknownProperties() || fun->script()->isInnerFunction)
return;
/* Strawman object to add properties to and watch for duplicates. */
@ -4460,8 +4608,8 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, JSFunction *fun)
* than we will use for subsequent new objects. Generate an object with the
* appropriate final shape.
*/
RootedShape shape(cx, baseobj->lastProperty());
baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind, shape);
baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind,
baseobj->lastProperty());
if (!baseobj ||
!type->addDefiniteProperties(cx, baseobj) ||
!initializerList.append(done)) {
@ -4734,7 +4882,7 @@ TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
/* Directly update associated type sets for applicable bytecodes. */
if (js_CodeSpec[*pc].format & JOF_TYPESET) {
if (!script->ensureRanAnalysis(cx)) {
if (!script->ensureRanAnalysis(cx, NULL)) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
@ -4840,7 +4988,7 @@ TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Val
AutoEnterTypeInference enter(cx);
if (!script->ensureRanAnalysis(cx)) {
if (!script->ensureRanAnalysis(cx, NULL)) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
@ -4855,6 +5003,255 @@ TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Val
types->addType(cx, type);
}
bool
TypeScript::SetScope(JSContext *cx, JSScript *script_, JSObject *scope_)
{
Rooted<JSScript*> script(cx, script_);
RootedObject scope(cx, scope_);
JS_ASSERT(script->types && !script->types->hasScope());
JSFunction *fun = script->function();
bool nullClosure = fun && fun->isNullClosure();
JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
/*
* The scope object must be the initial one for the script, before any call
* object has been created in the heavyweight case.
*/
JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(),
&scope->asCall().callee() != fun);
if (!script->compileAndGo) {
script->types->global = NULL;
return true;
}
JS_ASSERT_IF(fun && scope, fun->global() == scope->global());
script->types->global = fun ? &fun->global() : &scope->global();
/*
* Update the parent in the script's bindings. The bindings are created
* with a NULL parent, and fixing the parent now avoids the need to reshape
* every time a call object is created from the bindings.
*/
if (!script->bindings.setParent(cx, script->types->global))
return false;
if (!cx->typeInferenceEnabled())
return true;
if (!script->isInnerFunction || nullClosure) {
/*
* Outermost functions need nesting information if there are inner
* functions directly nested in them.
*/
if (script->isOuterFunction) {
script->types->nesting = cx->new_<TypeScriptNesting>();
if (!script->types->nesting)
return false;
}
return true;
}
/*
* Walk the scope chain to the next call object, which will be the function
* the script is nested inside.
*/
while (!scope->isCall())
scope = &scope->asScope().enclosingScope();
CallObject &call = scope->asCall();
/* The isInnerFunction test ensures there is no intervening strict eval call object. */
JS_ASSERT(!call.isForEval());
/* Don't track non-heavyweight parents, NAME ops won't reach into them. */
JSFunction *parentFun = &call.callee();
if (!parentFun || !parentFun->isHeavyweight())
return true;
JSScript *parent = parentFun->script();
JS_ASSERT(parent->isOuterFunction);
/*
* We only need the nesting in the child if it has NAME accesses going
* into the parent. We won't know for sure whether this is the case until
* analyzing the script's types, which we don't want to do yet. The nesting
* info we make here may get pruned if/when we eventually do such analysis.
*/
/*
* Scopes are set when scripts first execute, and the parent script must
* have executed first. It is still possible for the parent script to not
* have a scope, however, as we occasionally purge all TypeScripts from the
* compartment and there may be inner function objects parented to an
* activation of the outer function sticking around. In such cases, treat
* the parent's call object as the most recent one, so that it is not
* marked as reentrant.
*/
if (!parent->ensureHasTypes(cx))
return false;
if (!parent->types->hasScope()) {
if (!SetScope(cx, parent, &call.enclosingScope()))
return false;
parent->nesting()->activeCall = &call;
parent->nesting()->argArray = Valueify(call.argArray());
parent->nesting()->varArray = Valueify(call.varArray());
}
JS_ASSERT(!script->types->nesting);
/* Construct and link nesting information for the two functions. */
script->types->nesting = cx->new_<TypeScriptNesting>();
if (!script->types->nesting)
return false;
script->nesting()->parent = parent;
script->nesting()->next = parent->nesting()->children;
parent->nesting()->children = script;
return true;
}
TypeScriptNesting::~TypeScriptNesting()
{
/*
* Unlink from any parent/child. Nesting info on a script does not keep
* either the parent or children live during GC.
*/
if (parent) {
JSScript **pscript = &parent->nesting()->children;
while ((*pscript)->nesting() != this)
pscript = &(*pscript)->nesting()->next;
*pscript = next;
}
while (children) {
TypeScriptNesting *child = children->nesting();
children = child->next;
child->parent = NULL;
child->next = NULL;
}
}
bool
ClearActiveNesting(JSScript *start)
{
/*
* Clear active call information for script and any outer functions
* inner to it. Return false if an inner function has frames on the stack.
*/
/* Traverse children, then parent, avoiding recursion. */
JSScript *script = start;
bool traverseChildren = true;
while (true) {
TypeScriptNesting *nesting = script->nesting();
if (nesting->children && traverseChildren) {
script = nesting->children;
continue;
}
if (nesting->activeFrames)
return false;
if (script->isOuterFunction) {
nesting->activeCall = NULL;
nesting->argArray = NULL;
nesting->varArray = NULL;
}
if (script == start)
break;
if (nesting->next) {
script = nesting->next;
traverseChildren = true;
} else {
script = nesting->parent;
traverseChildren = false;
}
}
return true;
}
/*
* For the specified scope and script with an outer function, check if the
* scope represents a reentrant activation on an inner function of the parent
* or any of its transitive parents.
*/
static void
CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
{
restart:
JSScript *parent = script->nesting()->parent;
JS_ASSERT(parent);
while (!scope->isCall() || scope->asCall().callee().script() != parent)
scope = &scope->asScope().enclosingScope();
if (scope != parent->nesting()->activeCall) {
parent->reentrantOuterFunction = true;
MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
/*
* Continue checking parents to see if this is reentrant for them too.
* We don't need to check this in for non-reentrant calls on the outer
* function: when we entered any outer function to the immediate parent
* we cleared the active call for its transitive children, so a
* non-reentrant call on a child is also a non-reentrant call on the
* parent.
*/
if (parent->nesting()->parent) {
scope = &scope->asScope().enclosingScope();
script = parent;
goto restart;
}
}
}
void
NestingPrologue(JSContext *cx, StackFrame *fp)
{
JSScript *script = fp->fun()->script();
TypeScriptNesting *nesting = script->nesting();
if (nesting->parent)
CheckNestingParent(cx, fp->scopeChain(), script);
if (script->isOuterFunction) {
/*
* Check the stack has no frames for this activation, any of its inner
* functions or any of their transitive inner functions.
*
* Also, if the script has an extensible scope, then the arg/var array
* can be moved unexpectedly, so abort the optimization.
*/
if (!ClearActiveNesting(script) || script->funHasExtensibleScope) {
script->reentrantOuterFunction = true;
MarkTypeObjectFlags(cx, fp->fun(), OBJECT_FLAG_REENTRANT_FUNCTION);
}
nesting->activeCall = &fp->callObj();
nesting->argArray = Valueify(nesting->activeCall->argArray());
nesting->varArray = Valueify(nesting->activeCall->varArray());
}
/* Maintain stack frame count for the function. */
nesting->activeFrames++;
}
void
NestingEpilogue(StackFrame *fp)
{
JSScript *script = fp->fun()->script();
TypeScriptNesting *nesting = script->nesting();
JS_ASSERT(nesting->activeFrames != 0);
nesting->activeFrames--;
}
} } /* namespace js::types */
/////////////////////////////////////////////////////////////////////
@ -5031,15 +5428,13 @@ JSFunction::setTypeForScriptedFunction(JSContext *cx, bool singleton)
if (!setSingletonType(cx))
return false;
} else {
RootedFunction self(cx, this);
TypeObject *type = cx->compartment->types.newTypeObject(cx, script(),
JSProto_Function, getProto());
if (!type)
return false;
self->setType(type);
type->interpretedFunction = self;
setType(type);
type->interpretedFunction = this;
}
return true;
@ -5191,6 +5586,8 @@ JSObject::makeLazyType(JSContext *cx)
JSScript *script = type->interpretedFunction->script();
if (script->uninlineable)
type->flags |= OBJECT_FLAG_UNINLINEABLE;
if (script->reentrantOuterFunction)
type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION;
}
if (self->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
@ -5213,10 +5610,10 @@ JSObject::makeLazyType(JSContext *cx)
* looking at the class prototype key.
*/
if (self->isSlowArray())
if (isSlowArray())
type->flags |= OBJECT_FLAG_NON_DENSE_ARRAY | OBJECT_FLAG_NON_PACKED_ARRAY;
if (IsTypedArrayProto(self))
if (IsTypedArrayProto(this))
type->flags |= OBJECT_FLAG_NON_TYPED_ARRAY;
self->type_ = type;
@ -5269,7 +5666,7 @@ JSObject::setNewTypeUnknown(JSContext *cx)
}
TypeObject *
JSObject::getNewType(JSContext *cx, JSFunction *fun_, bool isDOM)
JSObject::getNewType(JSContext *cx, JSFunction *fun)
{
TypeObjectSet &table = cx->compartment->newTypeObjects;
@ -5291,17 +5688,13 @@ JSObject::getNewType(JSContext *cx, JSFunction *fun_, bool isDOM)
* Object.create is called with a prototype object that is also the
* 'prototype' property of some scripted function.
*/
if (type->newScript && type->newScript->fun != fun_)
if (type->newScript && type->newScript->fun != fun)
type->clearNewScript(cx);
if (!isDOM && !type->hasAnyFlags(OBJECT_FLAG_NON_DOM))
type->setFlags(cx, OBJECT_FLAG_NON_DOM);
return type;
}
RootedObject self(cx, this);
RootedFunction fun(cx, fun_);
if (!setDelegate(cx))
return NULL;
@ -5309,8 +5702,7 @@ JSObject::getNewType(JSContext *cx, JSFunction *fun_, bool isDOM)
bool markUnknown = self->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN);
RootedTypeObject type(cx);
type = cx->compartment->types.newTypeObject(cx, NULL, JSProto_Object, self,
markUnknown, isDOM);
type = cx->compartment->types.newTypeObject(cx, NULL, JSProto_Object, self, markUnknown);
if (!type)
return NULL;
@ -5696,6 +6088,15 @@ TypeScript::Sweep(FreeOp *fop, JSScript *script)
presult = &result->next;
}
}
/*
* If the script has nesting state with a most recent activation, we do not
* need either to mark the call object or clear it if not live. Even with
* a dead pointer in the nesting, we can't get a spurious match while
* testing for reentrancy: if previous activations are still live, they
* cannot alias the most recent one, and future activations will overwrite
* activeCall on creation.
*/
}
void
@ -5707,6 +6108,9 @@ TypeScript::destroy()
dynamicList = next;
}
if (nesting)
Foreground::delete_(nesting);
Foreground::free_(this);
}
@ -5762,6 +6166,8 @@ SizeOfScriptTypeInferenceData(JSScript *script, TypeInferenceSizes *sizes,
return;
}
sizes->scripts += mallocSizeOf(typeScript->nesting);
unsigned count = TypeScript::NumTypeSets(script);
sizes->scripts += mallocSizeOf(typeScript);

View File

@ -277,17 +277,17 @@ enum {
/* Whether any objects this represents are not typed arrays. */
OBJECT_FLAG_NON_TYPED_ARRAY = 0x00040000,
/* Whether any objects this represents are not DOM objects */
OBJECT_FLAG_NON_DOM = 0x00080000,
/* Whether any represented script is considered uninlineable. */
OBJECT_FLAG_UNINLINEABLE = 0x00100000,
OBJECT_FLAG_UNINLINEABLE = 0x00080000,
/* Whether any objects have an equality hook. */
OBJECT_FLAG_SPECIAL_EQUALITY = 0x00200000,
OBJECT_FLAG_SPECIAL_EQUALITY = 0x00100000,
/* Whether any objects have been iterated over. */
OBJECT_FLAG_ITERATED = 0x00400000,
OBJECT_FLAG_ITERATED = 0x00200000,
/* Outer function which has been marked reentrant. */
OBJECT_FLAG_REENTRANT_FUNCTION = 0x00400000,
/* For a global object, whether flags were set on the RegExpStatics. */
OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00800000,
@ -802,7 +802,7 @@ struct TypeObject : gc::Cell
* Get the global object which all objects of this type are parented to,
* or NULL if there is none known.
*/
//inline JSObject *getGlobal();
inline JSObject *getGlobal();
/* Helpers */
@ -907,6 +907,89 @@ struct TypeCallsite
bool isNew, unsigned argumentCount);
};
/*
* Information attached to outer and inner function scripts nested in one
* another for tracking the reentrance state for outer functions. This state is
* used to generate fast accesses to the args and vars of the outer function.
*
* A function is non-reentrant if, at any point in time, only the most recent
* activation (i.e. call object) is live. An activation is live if either the
* activation is on the stack, or a transitive inner function parented to the
* activation is on the stack.
*
* Because inner functions can be (and, quite often, are) stored in object
* properties and it is difficult to build a fast and robust escape analysis
* to cope with such flow, we detect reentrance dynamically. For the outer
* function, we keep track of the call object for the most recent activation,
* and the number of frames for the function and its inner functions which are
* on the stack.
*
* If the outer function is called while frames associated with a previous
* activation are on the stack, the outer function is reentrant. If an inner
* function is called whose scope does not match the most recent activation,
* the outer function is reentrant.
*
* The situation gets trickier when there are several levels of nesting.
*
* function foo() {
* var a;
* function bar() {
* var b;
* function baz() { return a + b; }
* }
* }
*
* At calls to 'baz', we don't want to do the scope check for the activations
* of both 'foo' and 'bar', but rather 'bar' only. For this to work, a call to
* 'baz' which is a reentrant call on 'foo' must also be a reentrant call on
* 'bar'. When 'foo' is called, we clear the most recent call object for 'bar'.
*/
struct TypeScriptNesting
{
/*
* If this is an inner function, the outer function. If non-NULL, this will
* be the immediate nested parent of the script (even if that parent has
* been marked reentrant). May be NULL even if the script has a nested
* parent, if NAME accesses cannot be tracked into the parent (either the
* script extends its scope with eval() etc., or the parent can make new
* scope chain objects with 'let' or 'with').
*/
JSScript *parent;
/* If this is an outer function, list of inner functions. */
JSScript *children;
/* Link for children list of parent. */
JSScript *next;
/* If this is an outer function, the most recent activation. */
CallObject *activeCall;
/*
* If this is an outer function, pointers to the most recent activation's
* arguments and variables arrays. These could be referring either to stack
* values in activeCall's frame (if it has not finished yet) or to the
* internal slots of activeCall (if the frame has finished). Pointers to
* these fields can be embedded directly in JIT code (though remember to
* use 'addDependency == true' when calling resolveNameAccess).
*/
const Value *argArray;
const Value *varArray;
/* Number of frames for this function on the stack. */
uint32_t activeFrames;
TypeScriptNesting() { PodZero(this); }
~TypeScriptNesting();
};
/* Construct nesting information for script wrt its parent. */
bool CheckScriptNesting(JSContext *cx, JSScript *script);
/* Track nesting state when calling or finishing an outer/inner function. */
void NestingPrologue(JSContext *cx, StackFrame *fp);
void NestingEpilogue(StackFrame *fp);
/* Persistent type information for a script, retained across GCs. */
class TypeScript
{
@ -915,10 +998,28 @@ class TypeScript
/* Analysis information for the script, cleared on each GC. */
analyze::ScriptAnalysis *analysis;
/*
* Information about the scope in which a script executes. This information
* is not set until the script has executed at least once and SetScope
* called, before that 'global' will be poisoned per GLOBAL_MISSING_SCOPE.
*/
static const size_t GLOBAL_MISSING_SCOPE = 0x1;
/* Global object for the script, if compileAndGo. */
HeapPtr<GlobalObject> global;
public:
/* Nesting state for outer or inner function scripts. */
TypeScriptNesting *nesting;
/* Dynamic types generated at points within this script. */
TypeResult *dynamicList;
inline TypeScript();
bool hasScope() { return size_t(global.get()) != GLOBAL_MISSING_SCOPE; }
/* Array of type type sets for variables and JOF_TYPESET ops. */
TypeSet *typeArray() { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
@ -981,6 +1082,7 @@ class TypeScript
static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
static void Sweep(FreeOp *fop, JSScript *script);
inline void trace(JSTracer *trc);
void destroy();
};
@ -1101,8 +1203,7 @@ struct TypeCompartment
* js_ObjectClass).
*/
TypeObject *newTypeObject(JSContext *cx, JSScript *script,
JSProtoKey kind, JSObject *proto,
bool unknown = false, bool isDOM = false);
JSProtoKey kind, JSObject *proto, bool unknown = false);
/* Make an object for an allocation site. */
TypeObject *newAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);

View File

@ -12,6 +12,7 @@
#include "jsinfer.h"
#include "jsprf.h"
#include "gc/Marking.h"
#include "gc/Root.h"
#include "vm/GlobalObject.h"
@ -311,7 +312,7 @@ TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
JSFunction *fun = callee->toFunction();
if (fun->isInterpreted()) {
RootedScript script(cx, fun->script());
if (!script->ensureRanAnalysis(cx))
if (!script->ensureRanAnalysis(cx, fun->environment()))
return false;
if (cx->typeInferenceEnabled())
TypeMonitorCallSlow(cx, callee, args, constructing);
@ -448,6 +449,12 @@ UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
// Script interface functions
/////////////////////////////////////////////////////////////////////
inline
TypeScript::TypeScript()
{
this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
}
/* static */ inline unsigned
TypeScript::NumTypeSets(JSScript *script)
{
@ -497,7 +504,7 @@ TypeScript::SlotTypes(JSScript *script, unsigned slot)
TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
{
RootedObject proto(cx);
RootedObject global(cx, &script->global());
RootedObject global(cx, script->global());
if (!js_GetClassPrototype(cx, global, key, &proto, NULL))
return NULL;
return proto->getNewType(cx);
@ -687,7 +694,7 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
script->id(), TypeString(type));
ThisTypes(script)->addType(cx, type);
if (analyze)
if (analyze && script->types->hasScope())
script->ensureRanInference(cx);
}
}
@ -749,6 +756,15 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js:
}
}
void
TypeScript::trace(JSTracer *trc)
{
if (hasScope() && global)
gc::MarkObject(trc, &global, "script_global");
/* Note: nesting does not keep anything alive. */
}
/////////////////////////////////////////////////////////////////////
// TypeCompartment
/////////////////////////////////////////////////////////////////////
@ -1323,8 +1339,7 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
switch (key) {
case JSProto_Array:
flags = OBJECT_FLAG_NON_TYPED_ARRAY
| OBJECT_FLAG_NON_DOM;
flags = OBJECT_FLAG_NON_TYPED_ARRAY;
break;
case JSProto_Int8Array:
@ -1338,15 +1353,13 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
case JSProto_Uint8ClampedArray:
case JSProto_DataView:
flags = OBJECT_FLAG_NON_DENSE_ARRAY
| OBJECT_FLAG_NON_PACKED_ARRAY
| OBJECT_FLAG_NON_DOM;
| OBJECT_FLAG_NON_PACKED_ARRAY;
break;
default:
flags = OBJECT_FLAG_NON_DENSE_ARRAY
| OBJECT_FLAG_NON_PACKED_ARRAY
| OBJECT_FLAG_NON_TYPED_ARRAY
| OBJECT_FLAG_NON_DOM;
| OBJECT_FLAG_NON_TYPED_ARRAY;
break;
}
@ -1354,6 +1367,16 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
setFlags(cx, flags);
}
inline JSObject *
TypeObject::getGlobal()
{
if (singleton)
return &singleton->global();
if (interpretedFunction && interpretedFunction->script()->compileAndGo)
return &interpretedFunction->global();
return NULL;
}
inline void
TypeObject::writeBarrierPre(TypeObject *type)
{
@ -1429,7 +1452,7 @@ JSScript::ensureHasTypes(JSContext *cx)
}
inline bool
JSScript::ensureRanAnalysis(JSContext *cx)
JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope)
{
js::analyze::AutoEnterAnalysis aea(cx->compartment);
JSScript *self = this;
@ -1437,6 +1460,12 @@ JSScript::ensureRanAnalysis(JSContext *cx)
if (!self->ensureHasTypes(cx))
return false;
if (!self->types->hasScope()) {
js::RootedObject scopeRoot(cx, scope);
if (!js::types::TypeScript::SetScope(cx, self, scope))
return false;
scope = scopeRoot;
}
if (!self->hasAnalysis() && !self->makeAnalysis(cx))
return false;
JS_ASSERT(self->analysis()->ranBytecode());
@ -1447,7 +1476,7 @@ inline bool
JSScript::ensureRanInference(JSContext *cx)
{
JS::RootedScript self(cx, this);
if (!ensureRanAnalysis(cx))
if (!ensureRanAnalysis(cx, NULL))
return false;
if (!self->analysis()->ranInference()) {
js::types::AutoEnterTypeInference enter(cx);

View File

@ -479,7 +479,7 @@ js::ExecuteKernel(JSContext *cx, JSScript *script_, JSObject &scopeChain, const
if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
return false;
if (!script->ensureRanAnalysis(cx))
if (!script->ensureRanAnalysis(cx, &scopeChain))
return false;
TypeScript::SetThis(cx, script, efg.fp()->thisValue());
@ -839,7 +839,7 @@ TryNoteIter::settle()
* in *expr.
*/
static bool
DoIncDec(JSContext *cx, HandleScript script, jsbytecode *pc, const Value &v, Value *slot, Value *expr)
DoIncDec(JSContext *cx, JSScript *script, jsbytecode *pc, const Value &v, Value *slot, Value *expr)
{
const JSCodeSpec &cs = js_CodeSpec[*pc];
@ -2765,7 +2765,6 @@ 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)

View File

@ -2183,16 +2183,16 @@ JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
return true;
}
/* static */ bool
JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp)
bool
JSObject::isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp)
{
if (obj->isExtensible()) {
if (isExtensible()) {
*resultp = false;
return true;
}
AutoIdVector props(cx);
if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
return false;
RootedId id(cx);
@ -2200,7 +2200,7 @@ JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it,
id = props[i];
unsigned attrs;
if (!obj->getGenericAttributes(cx, id, &attrs))
if (!getGenericAttributes(cx, id, &attrs))
return false;
/*
@ -2241,7 +2241,7 @@ obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
return false;
bool frozen;
if (!JSObject::isFrozen(cx, obj, &frozen))
if (!obj->isFrozen(cx, &frozen))
return false;
vp->setBoolean(frozen);
return true;
@ -2267,7 +2267,7 @@ obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
return false;
bool sealed;
if (!JSObject::isSealed(cx, obj, &sealed))
if (!obj->isSealed(cx, &sealed))
return false;
vp->setBoolean(sealed);
return true;
@ -2400,9 +2400,7 @@ js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto_, J
}
}
bool isDOM = (clasp->flags & JSCLASS_IS_DOMJSCLASS);
types::TypeObject *type = proto ? proto->getNewType(cx, NULL, isDOM)
: cx->compartment->getEmptyType(cx);
types::TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
if (!type)
return NULL;
@ -2511,7 +2509,7 @@ js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc
JSObject *
js::NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
gc::AllocKind kind, HandleShape shape)
gc::AllocKind kind, Shape *shape)
{
RootedObject res(cx, NewObjectWithType(cx, type, parent, kind));
if (!res)
@ -2614,20 +2612,16 @@ js_CreateThisForFunction(JSContext *cx, HandleObject callee, bool newType)
JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
if (obj && newType) {
RootedObject nobj(cx, obj);
/*
* Reshape the object and give it a (lazily instantiated) singleton
* type before passing it as the 'this' value for the call.
*/
nobj->clear(cx);
if (!nobj->setSingletonType(cx))
obj->clear(cx);
if (!obj->setSingletonType(cx))
return NULL;
JSScript *calleeScript = callee->toFunction()->script();
TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
return nobj;
TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(obj));
}
return obj;
@ -3533,12 +3527,10 @@ JSObject::growSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount)
gc::AllocKind kind = type()->newScript->allocKind;
unsigned newScriptSlots = gc::GetGCKindSlots(kind);
if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) {
AutoEnterTypeInference enter(cx);
Rooted<TypeObject*> typeObj(cx, type());
RootedShape shape(cx, typeObj->newScript->shape);
JSObject *obj = NewReshapedObject(cx, typeObj,
getParent(), kind, shape);
getParent(), kind,
typeObj->newScript->shape);
if (!obj)
return false;

View File

@ -442,8 +442,7 @@ struct JSObject : public js::ObjectImpl
inline void setType(js::types::TypeObject *newType);
js::types::TypeObject *getNewType(JSContext *cx, JSFunction *fun = NULL,
bool isDOM = false);
js::types::TypeObject *getNewType(JSContext *cx, JSFunction *fun = NULL);
#ifdef DEBUG
bool hasNewType(js::types::TypeObject *newType);
@ -532,7 +531,7 @@ struct JSObject : public js::ObjectImpl
*/
bool sealOrFreeze(JSContext *cx, ImmutabilityType it);
static bool isSealedOrFrozen(JSContext *cx, js::HandleObject obj, ImmutabilityType it, bool *resultp);
bool isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp);
static inline unsigned getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it);
@ -544,12 +543,8 @@ struct JSObject : public js::ObjectImpl
/* ES5 15.2.3.9: non-extensible, all properties non-configurable, all data props read-only */
bool freeze(JSContext *cx) { return sealOrFreeze(cx, FREEZE); }
static inline bool isSealed(JSContext *cx, js::HandleObject obj, bool *resultp) {
return isSealedOrFrozen(cx, obj, SEAL, resultp);
}
static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) {
return isSealedOrFrozen(cx, obj, FREEZE, resultp);
}
bool isSealed(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, SEAL, resultp); }
bool isFrozen(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, FREEZE, resultp); }
/* Accessors for elements. */

View File

@ -1552,7 +1552,7 @@ CopyInitializerObject(JSContext *cx, HandleObject baseobj)
JSObject *
NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
gc::AllocKind kind, HandleShape shape);
gc::AllocKind kind, Shape *shape);
/*
* As for gc::GetGCObjectKind, where numSlots is a guess at the final size of

View File

@ -6379,7 +6379,7 @@ GetPCCountScriptContents(JSContext *cx, size_t index)
{
JSAutoEnterCompartment ac;
if (!ac.enter(cx, &script->global()))
if (!ac.enter(cx, script->function() ? (JSObject *) script->function() : script->global()))
return NULL;
if (!GetPCCountJSON(cx, sac, buf))

View File

@ -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|JOF_TYPESET)
OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL, 9, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET)
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_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)

View File

@ -367,7 +367,7 @@ IndirectProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy,
}
static bool
GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, jsid id, unsigned flags,
GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
JSPropertyDescriptor *desc)
{
// If obj is a proxy, we can do better than just guessing. This is
@ -389,8 +389,7 @@ IndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
jsid id, bool set,
PropertyDescriptor *desc)
{
RootedObject target(cx, GetProxyTargetObject(proxy));
return GetOwnPropertyDescriptor(cx, target, id,
return GetOwnPropertyDescriptor(cx, GetProxyTargetObject(proxy), id,
JSRESOLVE_QUALIFIED, desc);
}
@ -1768,10 +1767,6 @@ js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_,
/* Don't track types of properties of proxies. */
MarkTypeObjectUnknownProperties(cx, obj->type());
/* Mark the new proxy as having singleton type. */
if (clasp == &OuterWindowProxyClass && !obj->setSingletonType(cx))
return NULL;
return obj;
}

View File

@ -278,12 +278,35 @@ Shape::getChildBinding(JSContext *cx, const StackShape &child)
{
JS_ASSERT(!inDictionary());
/* Try to allocate all slots inline. */
uint32_t slots = child.slotSpan();
gc::AllocKind kind = gc::GetGCObjectKind(slots);
uint32_t nfixed = gc::GetGCKindSlots(kind);
Shape *shape = cx->propertyTree().getChild(cx, this, numFixedSlots(), child);
if (shape) {
//JS_ASSERT(shape->parent == this); // XXX 'this' is not rooted here
return cx->propertyTree().getChild(cx, this, nfixed, child);
/*
* Update the number of fixed slots which bindings of this shape will
* have. Bindings are constructed as new properties come in, so the
* call object allocation class is not known ahead of time. Compute
* the fixed slot count here, which will feed into call objects created
* off of the bindings.
*/
uint32_t slots = child.slotSpan();
gc::AllocKind kind = gc::GetGCObjectKind(slots);
/*
* Make sure that the arguments and variables in the call object all
* end up in a contiguous range of slots. We need this to be able to
* embed the args/vars arrays in the TypeScriptNesting for the function
* after the call object's frame has finished.
*/
uint32_t nfixed = gc::GetGCKindSlots(kind);
if (nfixed < slots) {
nfixed = CallObject::RESERVED_SLOTS;
JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS);
}
shape->setNumFixedSlots(nfixed);
}
return shape;
}
/* static */ Shape *
@ -1184,6 +1207,31 @@ Bindings::setExtensibleParents(JSContext *cx)
return true;
}
bool
Bindings::setParent(JSContext *cx, JSObject *obj_)
{
RootedObject obj(cx, obj_);
/*
* This may be invoked on GC heap allocated bindings, in which case this
* is pointing to an internal value of a JSScript that can't itself be
* relocated. The script itself will be rooted, and will not be moved, so
* mark the stack value as non-relocatable for the stack root analysis.
*/
Bindings *self = this;
SkipRoot root(cx, &self);
if (!ensureShape(cx))
return false;
/* This is only used for Block objects, which have a NULL proto. */
Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding);
if (!newShape)
return false;
self->lastBinding = newShape;
return true;
}
inline
InitialShapeEntry::InitialShapeEntry() : shape(NULL), proto(NULL)
{

View File

@ -51,6 +51,8 @@ using namespace js;
using namespace js::gc;
using namespace js::frontend;
namespace js {
BindingKind
Bindings::lookup(JSContext *cx, JSAtom *name, unsigned *indexp) const
{
@ -122,7 +124,7 @@ Bindings::add(JSContext *cx, HandleAtom name, BindingKind kind)
id = AtomToId(name);
}
StackBaseShape base(&CallClass, cx->global(), BaseShape::VAROBJ);
StackBaseShape base(&CallClass, NULL, BaseShape::VAROBJ);
base.updateGetterSetter(attrs, getter, setter);
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
@ -245,6 +247,8 @@ Bindings::trace(JSTracer *trc)
MarkShape(trc, &lastBinding, "shape");
}
} /* namespace js */
template<XDRMode mode>
static bool
XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
@ -338,25 +342,9 @@ XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
return true;
}
static inline uint32_t
FindBlockIndex(JSScript *script, StaticBlockObject &block)
{
ObjectArray *objects = script->objects();
HeapPtrObject *vector = objects->vector;
unsigned length = objects->length;
for (unsigned i = 0; i < length; ++i) {
if (vector[i] == &block)
return i;
}
JS_NOT_REACHED("Block not found");
return UINT32_MAX;
}
template<XDRMode mode>
bool
js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
HandleFunction fun, JSScript **scriptp)
js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
{
/* NB: Keep this in sync with CloneScript. */
@ -384,12 +372,16 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = nClosedArgs = nClosedVars = 0;
jssrcnote *notes = NULL;
/* XDR arguments and vars. */
uint16_t nargs = 0, nvars = 0;
uint32_t argsVars = 0;
/* XDR arguments, var vars, and upvars. */
uint16_t nargs, nvars;
#if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
script = NULL;
nargs = nvars = Bindings::BINDING_COUNT_LIMIT;
#endif
uint32_t argsVars;
if (mode == XDR_ENCODE) {
script = *scriptp;
JS_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
JS_ASSERT_IF(parentScript, parentScript->compartment() == script->compartment());
nargs = script->bindings.numArgs();
nvars = script->bindings.numVars();
@ -523,7 +515,7 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
if (script->analyzedArgsUsage() && script->needsArgsObj())
scriptBits |= (1 << NeedsArgsObj);
if (script->filename) {
scriptBits |= (enclosingScript && enclosingScript->filename == script->filename)
scriptBits |= (parentScript && parentScript->filename == script->filename)
? (1 << ParentFilename)
: (1 << OwnFilename);
}
@ -572,12 +564,12 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
// principals and originPrincipals are set with xdr->initScriptPrincipals(script) below.
// staticLevel is set below.
script = JSScript::Create(cx,
enclosingScope,
!!(scriptBits & (1 << SavedCallerFun)),
/* principals = */ NULL,
/* originPrincipals = */ NULL,
/* compileAndGo = */ false,
!!(scriptBits & (1 << NoScriptRval)),
/* globalObject = */ NULL,
version_,
/* staticLevel = */ 0);
if (!script || !JSScript::partiallyInit(cx, script,
@ -630,9 +622,9 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
return false;
}
} else if (scriptBits & (1 << ParentFilename)) {
JS_ASSERT(enclosingScript);
JS_ASSERT(parentScript);
if (mode == XDR_DECODE)
script->filename = enclosingScript->filename;
script->filename = parentScript->filename;
}
if (mode == XDR_DECODE) {
@ -656,9 +648,10 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
}
/*
* Here looping from 0-to-length to xdr objects is essential to ensure that
* all references to enclosing blocks (via FindBlockIndex below) happen
* after the enclosing block has been XDR'd.
* Here looping from 0-to-length to xdr objects is essential. It ensures
* that block objects from the script->objects array will be written and
* restored in the outer-to-inner order. js_XDRBlockObject relies on this
* to restore the parent chain.
*/
for (i = 0; i != nobjects; ++i) {
HeapPtr<JSObject> *objp = &script->objects()->vector[i];
@ -671,58 +664,14 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
if (!xdr->codeUint32(&isBlock))
return false;
if (isBlock == 0) {
/* Code the nested function's enclosing scope. */
uint32_t funEnclosingScopeIndex = 0;
if (mode == XDR_ENCODE) {
StaticScopeIter ssi((*objp)->toFunction()->script()->enclosingStaticScope());
if (ssi.done() || ssi.type() == StaticScopeIter::FUNCTION) {
JS_ASSERT(ssi.done() == !fun);
funEnclosingScopeIndex = UINT32_MAX;
} else {
funEnclosingScopeIndex = FindBlockIndex(script, ssi.block());
JS_ASSERT(funEnclosingScopeIndex < i);
}
}
if (!xdr->codeUint32(&funEnclosingScopeIndex))
return false;
Rooted<JSObject*> funEnclosingScope(cx);
if (mode == XDR_DECODE) {
if (funEnclosingScopeIndex == UINT32_MAX) {
funEnclosingScope = fun;
} else {
JS_ASSERT(funEnclosingScopeIndex < i);
funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
}
}
JSObject *tmp = *objp;
if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
if (!XDRInterpretedFunction(xdr, &tmp, parentScript))
return false;
*objp = tmp;
} else {
/* Code the nested block's enclosing scope. */
JS_ASSERT(isBlock == 1);
uint32_t blockEnclosingScopeIndex = 0;
if (mode == XDR_ENCODE) {
if (StaticBlockObject *block = (*objp)->asStaticBlock().enclosingBlock())
blockEnclosingScopeIndex = FindBlockIndex(script, *block);
else
blockEnclosingScopeIndex = UINT32_MAX;
}
if (!xdr->codeUint32(&blockEnclosingScopeIndex))
return false;
Rooted<JSObject*> blockEnclosingScope(cx);
if (mode == XDR_DECODE) {
if (blockEnclosingScopeIndex != UINT32_MAX) {
JS_ASSERT(blockEnclosingScopeIndex < i);
blockEnclosingScope = script->objects()->vector[blockEnclosingScopeIndex];
} else {
blockEnclosingScope = fun;
}
}
StaticBlockObject *tmp = static_cast<StaticBlockObject *>(objp->get());
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, script, &tmp))
if (!XDRStaticBlockObject(xdr, script, &tmp))
return false;
*objp = tmp;
}
@ -789,10 +738,10 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
}
template bool
js::XDRScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, HandleFunction, JSScript **);
js::XDRScript(XDRState<XDR_ENCODE> *xdr, JSScript **scriptp, JSScript *parentScript);
template bool
js::XDRScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, HandleFunction, JSScript **);
js::XDRScript(XDRState<XDR_DECODE> *xdr, JSScript **scriptp, JSScript *parentScript);
bool
JSScript::initScriptCounts(JSContext *cx)
@ -1125,17 +1074,17 @@ ScriptDataSize(uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
}
JSScript *
JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun,
JSPrincipals *principals, JSPrincipals *originPrincipals,
bool compileAndGo, bool noScriptRval, JSVersion version, unsigned staticLevel)
JSScript::Create(JSContext *cx, bool savedCallerFun, JSPrincipals *principals,
JSPrincipals *originPrincipals, bool compileAndGo, bool noScriptRval,
GlobalObject *globalObject_, JSVersion version, unsigned staticLevel)
{
Rooted<GlobalObject*> globalObject(cx, globalObject_);
JSScript *script = js_NewGCScript(cx);
if (!script)
return NULL;
PodZero(script);
script->enclosingScope_ = enclosingScope;
script->savedCallerFun = savedCallerFun;
/* Establish invariant: principals implies originPrincipals. */
@ -1151,6 +1100,8 @@ JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun
script->compileAndGo = compileAndGo;
script->noScriptRval = noScriptRval;
script->globalObject = globalObject;
script->version = version;
JS_ASSERT(script->getVersion() == version); // assert that no overflow occurred
@ -1367,7 +1318,7 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
if (bce->sc->funArgumentsHasLocalBinding()) {
// This must precede the script->bindings.transfer() call below
script->setArgumentsHasVarBinding();
if (bce->sc->funDefinitelyNeedsArgsObj())
if (bce->sc->funDefinitelyNeedsArgsObj())
script->setNeedsArgsObj(true);
} else {
JS_ASSERT(!bce->sc->funDefinitelyNeedsArgsObj());
@ -1699,7 +1650,7 @@ Rebase(JSScript *dst, JSScript *src, T *srcp)
}
JSScript *
js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src)
js::CloneScript(JSContext *cx, HandleScript src)
{
/* NB: Keep this in sync with XDRScript. */
@ -1748,29 +1699,13 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
if (nobjects != 0) {
HeapPtrObject *vector = src->objects()->vector;
for (unsigned i = 0; i < nobjects; i++) {
JSObject &obj = *vector[i];
JSObject *clone;
if (obj.isStaticBlock()) {
Rooted<StaticBlockObject*> innerBlock(cx, &obj.asStaticBlock());
Rooted<JSObject*> enclosingScope(cx);
if (StaticBlockObject *enclosingBlock = innerBlock->enclosingBlock())
enclosingScope = objects[FindBlockIndex(src, *enclosingBlock)];
else
enclosingScope = fun;
clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
if (vector[i]->isStaticBlock()) {
Rooted<StaticBlockObject*> block(cx, &vector[i]->asStaticBlock());
clone = CloneStaticBlockObject(cx, block, objects, src);
} else {
Rooted<JSFunction*> innerFun(cx, obj.toFunction());
StaticScopeIter ssi(innerFun->script()->enclosingStaticScope());
Rooted<JSObject*> enclosingScope(cx);
if (!ssi.done() && ssi.type() == StaticScopeIter::BLOCK)
enclosingScope = objects[FindBlockIndex(src, ssi.block())];
else
enclosingScope = fun;
clone = CloneInterpretedFunction(cx, enclosingScope, innerFun);
RootedFunction fun(cx, vector[i]->toFunction());
clone = CloneInterpretedFunction(cx, fun);
}
if (!clone || !objects.append(clone))
return NULL;
@ -1791,10 +1726,11 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
/* Now that all fallible allocation is complete, create the GC thing. */
JSScript *dst = JSScript::Create(cx, enclosingScope, src->savedCallerFun,
JSScript *dst = JSScript::Create(cx, src->savedCallerFun,
cx->compartment->principals, src->originPrincipals,
src->compileAndGo, src->noScriptRval,
src->getVersion(), src->staticLevel);
/* globalObject = */ NULL, src->getVersion(),
src->staticLevel);
if (!dst) {
Foreground::free_(data);
return NULL;
@ -2013,7 +1949,8 @@ JSScript::changeStepModeCount(JSContext *cx, int delta)
}
BreakpointSite *
JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc)
JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
GlobalObject *scriptGlobal)
{
JS_ASSERT(size_t(pc - code) < length);
@ -2032,6 +1969,11 @@ JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc)
debug->numSites++;
}
if (site->scriptGlobal)
JS_ASSERT_IF(scriptGlobal, site->scriptGlobal == scriptGlobal);
else
site->scriptGlobal = scriptGlobal;
return site;
}
@ -2118,14 +2060,17 @@ JSScript::markChildren(JSTracer *trc)
if (function())
MarkObject(trc, &function_, "function");
if (enclosingScope_)
MarkObject(trc, &enclosingScope_, "enclosing");
if (!isCachedEval && globalObject)
MarkObject(trc, &globalObject, "object");
if (IS_GC_MARKING_TRACER(trc) && filename)
MarkScriptFilename(trc->runtime, filename);
bindings.trace(trc);
if (types)
types->trace(trc);
#ifdef JS_METHODJIT
for (int constructing = 0; constructing <= 1; constructing++) {
for (int barriers = 0; barriers <= 1; barriers++) {

View File

@ -103,9 +103,6 @@ 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
@ -131,6 +128,8 @@ class Bindings
inline bool extensibleParents();
bool setExtensibleParents(JSContext *cx);
bool setParent(JSContext *cx, JSObject *obj);
enum {
/* A script may have no more than this many arguments or variables. */
BINDING_COUNT_LIMIT = 0xFFFF
@ -410,6 +409,19 @@ struct JSScript : public js::gc::Cell
JSPrincipals *principals;/* principals for this script */
JSPrincipals *originPrincipals; /* see jsapi.h 'originPrincipals' comment */
/*
* A global object for the script.
* - All scripts returned by JSAPI functions (JS_CompileScript,
* JS_CompileUTF8File, etc.) have a non-null globalObject.
* - A function script has a globalObject if the function comes from a
* compile-and-go script.
* - Temporary scripts created by obj_eval, JS_EvaluateScript, and
* similar functions never have the globalObject field set; for such
* scripts the global should be extracted from the JS frame that
* execute scripts.
*/
js::HeapPtr<js::GlobalObject, JSScript*> globalObject;
/* Persistent type information retained across GCs. */
js::types::TypeScript *types;
@ -417,8 +429,8 @@ struct JSScript : public js::gc::Cell
#ifdef JS_METHODJIT
JITScriptSet *jitInfo;
#endif
js::HeapPtrFunction function_;
js::HeapPtrObject enclosingScope_;
// 32-bit fields.
@ -498,9 +510,14 @@ struct JSScript : public js::gc::Cell
undefined properties in this
script */
bool hasSingletons:1; /* script has singleton objects */
bool isOuterFunction:1; /* function is heavyweight, with inner functions */
bool isInnerFunction:1; /* function is directly nested in a heavyweight
* outer function */
bool isActiveEval:1; /* script came from eval(), and is still active */
bool isCachedEval:1; /* script came from eval(), and is in eval cache */
bool uninlineable:1; /* script is considered uninlineable by analysis */
bool reentrantOuterFunction:1; /* outer function marked reentrant */
bool typesPurged:1; /* TypeScript has been purged at some point */
#ifdef JS_METHODJIT
bool debugMode:1; /* script was compiled in debug mode */
bool failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
@ -525,10 +542,11 @@ struct JSScript : public js::gc::Cell
//
public:
static JSScript *Create(JSContext *cx, js::HandleObject enclosingScope, bool savedCallerFun,
static JSScript *Create(JSContext *cx, bool savedCallerFun,
JSPrincipals *principals, JSPrincipals *originPrincipals,
bool compileAndGo, bool noScriptRval,
JSVersion version, unsigned staticLevel);
js::GlobalObject *globalObject, JSVersion version,
unsigned staticLevel);
// Three ways ways to initialize a JSScript. Callers of partiallyInit()
// and fullyInitTrivial() are responsible for notifying the debugger after
@ -576,6 +594,9 @@ struct JSScript : public js::gc::Cell
return needsArgsObj() && !strictModeCode;
}
/* Hash table chaining for JSCompartment::evalCache. */
JSScript *&evalHashLink() { return *globalObject.unsafeGetUnioned(); }
/*
* Original compiled function for the script, if it has a function.
* NULL for global and eval scripts.
@ -583,9 +604,6 @@ struct JSScript : public js::gc::Cell
JSFunction *function() const { return function_; }
void setFunction(JSFunction *fun);
/* Return whether this script was compiled for 'eval' */
bool isForEval() { return isCachedEval || isActiveEval; }
#ifdef DEBUG
unsigned id();
#else
@ -596,10 +614,12 @@ struct JSScript : public js::gc::Cell
inline bool ensureHasTypes(JSContext *cx);
/*
* Ensure the script has bytecode analysis information. Performed when the
* script first runs, or first runs after a TypeScript GC purge.
* Ensure the script has scope and bytecode analysis information.
* Performed when the script first runs, or first runs after a TypeScript
* GC purge. If scope is NULL then the script must already have types with
* scope information.
*/
inline bool ensureRanAnalysis(JSContext *cx);
inline bool ensureRanAnalysis(JSContext *cx, JSObject *scope);
/* Ensure the script has type inference analysis information. */
inline bool ensureRanInference(JSContext *cx);
@ -611,10 +631,15 @@ struct JSScript : public js::gc::Cell
inline bool hasGlobal() const;
inline bool hasClearedGlobal() const;
inline js::GlobalObject &global() const;
inline js::GlobalObject * global() const;
inline js::types::TypeScriptNesting *nesting() const;
/* See StaticScopeIter comment. */
JSObject *enclosingStaticScope() const { return enclosingScope_; }
inline void clearNesting();
/* Return creation time global or null. */
js::GlobalObject *getGlobalObjectOrNull() const {
return (isCachedEval || isActiveEval) ? NULL : globalObject.get();
}
private:
bool makeTypes(JSContext *cx);
@ -850,7 +875,8 @@ struct JSScript : public js::gc::Cell
return hasDebugScript ? debugScript()->breakpoints[pc - code] : NULL;
}
js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc);
js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
js::GlobalObject *scriptGlobal);
void destroyBreakpointSite(js::FreeOp *fop, jsbytecode *pc);
@ -1009,7 +1035,7 @@ inline void
CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_CALLED_FROM_JSOP_EVAL);
extern JSScript *
CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script);
CloneScript(JSContext *cx, HandleScript script);
/*
* NB: after a successful XDR_DECODE, XDRScript callers must do any required
@ -1018,8 +1044,7 @@ CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, Hand
*/
template<XDRMode mode>
bool
XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
HandleFunction fun, JSScript **scriptp);
XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript);
} /* namespace js */

View File

@ -15,6 +15,7 @@
#include "jsscript.h"
#include "jsscope.h"
#include "vm/ScopeObject.h"
#include "vm/GlobalObject.h"
#include "vm/RegExpObject.h"
@ -27,20 +28,6 @@ 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)
{
@ -68,8 +55,8 @@ Bindings::initialShape(JSContext *cx) const
gc::AllocKind kind = gc::FINALIZE_OBJECT2_BACKGROUND;
JS_ASSERT(gc::GetGCKindSlots(kind) == CallObject::RESERVED_SLOTS);
return EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(),
kind, BaseShape::VAROBJ);
return EmptyShape::getInitialShape(cx, &CallClass, NULL, NULL, kind,
BaseShape::VAROBJ);
}
bool
@ -204,24 +191,41 @@ JSScript::hasGlobal() const
* which have had their scopes cleared. compileAndGo code should not run
* anymore against such globals.
*/
return compileAndGo && !global().isCleared();
JS_ASSERT(types && types->hasScope());
js::GlobalObject *obj = types->global;
return obj && !obj->isCleared();
}
inline js::GlobalObject &
inline js::GlobalObject *
JSScript::global() const
{
/*
* A JSScript always marks its compartment's global (via bindings) so we
* can assert that maybeGlobal is non-null here.
*/
return *compartment()->maybeGlobal();
JS_ASSERT(hasGlobal());
return types->global;
}
inline bool
JSScript::hasClearedGlobal() const
{
JS_ASSERT(types);
return global().isCleared();
JS_ASSERT(types && types->hasScope());
js::GlobalObject *obj = types->global;
return obj && obj->isCleared();
}
inline js::types::TypeScriptNesting *
JSScript::nesting() const
{
JS_ASSERT(function() && types && types->hasScope());
return types->nesting;
}
inline void
JSScript::clearNesting()
{
js::types::TypeScriptNesting *nesting = this->nesting();
if (nesting) {
js::Foreground::delete_(nesting);
types->nesting = NULL;
}
}
#ifdef JS_METHODJIT

View File

@ -1717,7 +1717,7 @@ GetCurrentScopeChain(JSContext *cx)
}
static JSXML *
ParseXMLSource(JSContext *cx, HandleString src)
ParseXMLSource(JSContext *cx, JSString *src)
{
jsval nsval;
JSLinearString *uri;
@ -1856,7 +1856,7 @@ ToXML(JSContext *cx, jsval v)
JSObject *obj;
JSXML *xml;
Class *clasp;
RootedString str(cx);
JSString *str;
uint32_t length;
if (JSVAL_IS_PRIMITIVE(v)) {
@ -1937,7 +1937,7 @@ ToXMLList(JSContext *cx, jsval v)
JSObject *obj, *listobj;
JSXML *xml, *list, *kid;
Class *clasp;
RootedString str(cx);
JSString *str;
uint32_t i, length;
if (JSVAL_IS_PRIMITIVE(v)) {

View File

@ -1375,7 +1375,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::MIPSRegiste
};
/* Return f<true> if the script is strict mode code, f<false> otherwise. */
#define STRICT_VARIANT(script, f) \
#define STRICT_VARIANT(f) \
(FunctionTemplateConditional(script->strictModeCode, \
f<true>, f<false>))

View File

@ -59,7 +59,7 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
isConstructing(isConstructing),
outerChunk(outerJIT()->chunkDescriptor(chunkIndex)),
ssa(cx, outerScript),
globalObj(cx, outerScript->hasGlobal() ? &outerScript->global() : NULL),
globalObj(cx, outerScript->hasGlobal() ? outerScript->global() : NULL),
globalSlots(globalObj ? globalObj->getRawSlots() : NULL),
frame(cx, *thisFromCtor(), masm, stubcc),
a(NULL), outer(NULL), script(NULL), PC(NULL), loop(NULL),
@ -135,7 +135,7 @@ mjit::Compiler::checkAnalysis(HandleScript script)
return Compile_Abort;
}
if (!script->ensureRanAnalysis(cx))
if (!script->ensureRanAnalysis(cx, NULL))
return Compile_Error;
if (!script->analysis()->jaegerCompileable()) {
@ -190,7 +190,7 @@ mjit::Compiler::scanInlineCalls(uint32_t index, uint32_t depth)
/* Don't inline from functions which could have a non-global scope object. */
if (!script->hasGlobal() ||
&script->global() != globalObj ||
script->global() != globalObj ||
(script->function() && script->function()->getParent() != globalObj) ||
(script->function() && script->function()->isHeavyweight()) ||
script->isActiveEval) {
@ -316,7 +316,7 @@ mjit::Compiler::scanInlineCalls(uint32_t index, uint32_t depth)
break;
}
if (!script->types) {
if (!script->types || !script->types->hasScope()) {
okay = false;
break;
}
@ -633,7 +633,7 @@ mjit::SetChunkLimit(uint32_t limit)
JITScript *
MakeJITScript(JSContext *cx, JSScript *script)
{
if (!script->ensureRanAnalysis(cx))
if (!script->ensureRanAnalysis(cx, NULL))
return NULL;
ScriptAnalysis *analysis = script->analysis();
@ -1097,7 +1097,9 @@ mjit::Compiler::generatePrologue()
* set for global and eval frames, and will have been set by
* HeavyweightFunctionPrologue for heavyweight function frames.
*/
if (!script->function()->isHeavyweight() && analysis->usesScopeChain()) {
if (!script->function()->isHeavyweight() &&
(analysis->usesScopeChain() || script->nesting()))
{
RegisterID t0 = Registers::ReturnReg;
Jump hasScope = masm.branchTest32(Assembler::NonZero,
FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN));
@ -1143,10 +1145,42 @@ mjit::Compiler::generatePrologue()
if (script->function()->isHeavyweight()) {
prepareStubCall(Uses(0));
INLINE_STUBCALL(stubs::HeavyweightFunctionPrologue, REJOIN_FUNCTION_PROLOGUE);
} else if (types::TypeScriptNesting *nesting = script->nesting()) {
/*
* Inline the common case for the nesting prologue: the
* function is a non-heavyweight inner function with no
* children of its own. We ensure during inference that the
* outer function does not add scope objects for 'let' or
* 'with', so that the frame's scope chain will be
* the parent's call object, and if it differs from the
* parent's current activation then the parent is reentrant.
*/
JSScript *parent = nesting->parent;
JS_ASSERT(parent);
JS_ASSERT_IF(parent->hasAnalysis() && parent->analysis()->ranBytecode(),
!parent->analysis()->addsScopeObjects());
RegisterID t0 = Registers::ReturnReg;
masm.move(ImmPtr(&parent->nesting()->activeCall), t0);
masm.loadPtr(Address(t0), t0);
Address scopeChain(JSFrameReg, StackFrame::offsetOfScopeChain());
Jump mismatch = masm.branchPtr(Assembler::NotEqual, t0, scopeChain);
masm.add32(Imm32(1), AbsoluteAddress(&nesting->activeFrames));
masm.load32(FrameFlagsAddress(), t0);
masm.or32(Imm32(StackFrame::HAS_NESTING), t0);
masm.store32(t0, FrameFlagsAddress());
stubcc.linkExitDirect(mismatch, stubcc.masm.label());
OOL_STUBCALL(stubs::TypeNestingPrologue, REJOIN_FUNCTION_PROLOGUE);
stubcc.crossJump(stubcc.masm.jump(), masm.label());
}
if (isConstructing && !constructThis())
return Compile_Error;
if (isConstructing) {
if (!constructThis())
return Compile_Error;
}
}
CompileStatus status = methodEntryHelper();
@ -2523,7 +2557,7 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(1));
masm.move(ImmPtr(name), Registers::ArgReg1);
INLINE_STUBCALL(STRICT_VARIANT(script, stubs::DelProp), REJOIN_FALLTHROUGH);
INLINE_STUBCALL(STRICT_VARIANT(stubs::DelProp), REJOIN_FALLTHROUGH);
frame.pop();
pushSyncedEntry(0);
}
@ -2532,7 +2566,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_DELELEM)
{
prepareStubCall(Uses(2));
INLINE_STUBCALL(STRICT_VARIANT(script, stubs::DelElem), REJOIN_FALLTHROUGH);
INLINE_STUBCALL(STRICT_VARIANT(stubs::DelElem), REJOIN_FALLTHROUGH);
frame.popn(2);
pushSyncedEntry(0);
}
@ -2826,6 +2860,8 @@ 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.
@ -2837,27 +2873,26 @@ 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();
}
updateVarType();
if (pop) {
frame.pop();
@ -3032,7 +3067,7 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(0));
masm.move(ImmPtr(innerFun), Registers::ArgReg1);
INLINE_STUBCALL(STRICT_VARIANT(script, stubs::DefFun), REJOIN_FALLTHROUGH);
INLINE_STUBCALL(STRICT_VARIANT(stubs::DefFun), REJOIN_FALLTHROUGH);
}
END_CASE(JSOP_DEFFUN)
@ -3773,6 +3808,10 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
INLINE_STUBCALL(stubs::Epilogue, REJOIN_NONE);
} else {
profilingPopHelper();
if (script->function() && script->nesting()) {
masm.sub32(Imm32(1), AbsoluteAddress(&script->nesting()->activeFrames));
}
}
emitReturnValue(&masm, fe);
@ -4200,8 +4239,18 @@ mjit::Compiler::inlineCallHelper(uint32_t argc, bool callingNew, FrameSize &call
if (icCalleeType.isSet())
notObjectJump = masm.testObject(Assembler::NotEqual, icCalleeType.reg());
/*
* For an optimized apply, keep icCalleeData in a callee-saved register for
* the subsequent ic::SplatApplyArgs call.
*/
Registers tempRegs(Registers::AvailRegs);
tempRegs.takeReg(icCalleeData);
if (callIC.frameSize.isDynamic() && !Registers::isSaved(icCalleeData)) {
RegisterID x = tempRegs.takeAnyReg(Registers::SavedRegs).reg();
masm.move(icCalleeData, x);
icCalleeData = x;
} else {
tempRegs.takeReg(icCalleeData);
}
/* Reserve space just before initialization of funGuard. */
RESERVE_IC_SPACE(masm);
@ -4242,16 +4291,9 @@ mjit::Compiler::inlineCallHelper(uint32_t argc, bool callingNew, FrameSize &call
* Check after the function is known not to be a native so that the
* catch-all/native path has a static depth.
*/
if (callIC.frameSize.isDynamic()) {
if (callIC.frameSize.isDynamic())
OOL_STUBCALL(ic::SplatApplyArgs, REJOIN_CALL_SPLAT);
/*
* Restore identity of callee after SplatApplyArgs, which may
* have been clobbered (not callee save reg or changed by moving GC).
*/
stubcc.masm.loadPayload(frame.addressOf(origThis), icCalleeData);
}
/*
* No-op jump that gets patched by ic::New/Call to the stub generated
* by generateFullCallStub.
@ -4694,7 +4736,7 @@ mjit::Compiler::jsop_setprop_slow(PropertyName *name)
{
prepareStubCall(Uses(2));
masm.move(ImmPtr(name), Registers::ArgReg1);
INLINE_STUBCALL(STRICT_VARIANT(script, stubs::SetName), REJOIN_FALLTHROUGH);
INLINE_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH);
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
frame.shimmy(1);
if (script->hasScriptCounts)
@ -5355,6 +5397,42 @@ mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed)
return true;
}
/*
* If this is a SETNAME to a variable of a non-reentrant outer function,
* set the variable's slot directly for the active call object.
*/
if (cx->typeInferenceEnabled() && js_CodeSpec[*PC].format & JOF_NAME) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, NameToId(name), true);
if (access.nesting) {
/* Use a SavedReg so it isn't clobbered by the stub call. */
RegisterID nameReg = frame.allocReg(Registers::SavedRegs).reg();
Address address = frame.loadNameAddress(access, nameReg);
#ifdef JSGC_INCREMENTAL_MJ
/* Write barrier. */
if (cx->compartment->needsBarrier()) {
stubcc.linkExit(masm.jump(), Uses(0));
stubcc.leave();
/* sync() may have overwritten nameReg, so we reload its data. */
JS_ASSERT(address.base == nameReg);
stubcc.masm.move(ImmPtr(access.basePointer()), nameReg);
stubcc.masm.loadPtr(Address(nameReg), nameReg);
stubcc.masm.addPtr(Imm32(address.offset), nameReg, Registers::ArgReg1);
OOL_STUBCALL(stubs::WriteBarrier, REJOIN_NONE);
stubcc.rejoin(Changes(0));
}
#endif
frame.storeTo(rhs, address, popGuaranteed);
frame.shimmy(1);
frame.freeReg(address.base);
return true;
}
}
/*
* Set the property directly if we are accessing a known object which
* always has the property in a particular inline slot.
@ -5396,7 +5474,7 @@ mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed)
stubcc.linkExit(notObject.get(), Uses(2));
stubcc.leave();
stubcc.masm.move(ImmPtr(name), Registers::ArgReg1);
OOL_STUBCALL(STRICT_VARIANT(script, stubs::SetName), REJOIN_FALLTHROUGH);
OOL_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH);
}
frame.storeTo(rhs, Address(reg, JSObject::getFixedSlotOffset(slot)), popGuaranteed);
frame.unpinReg(reg);
@ -5460,7 +5538,7 @@ mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed)
stubcc.leave();
stubcc.masm.move(ImmPtr(name), Registers::ArgReg1);
OOL_STUBCALL(STRICT_VARIANT(script, stubs::SetName), REJOIN_FALLTHROUGH);
OOL_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH);
typeCheck = stubcc.masm.jump();
pic.hasTypeCheck = true;
@ -5541,6 +5619,24 @@ mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed)
void
mjit::Compiler::jsop_name(PropertyName *name, JSValueType type)
{
/*
* If this is a NAME for a variable of a non-reentrant outer function, get
* the variable's slot directly for the active call object. We always need
* to check for undefined, however.
*/
if (cx->typeInferenceEnabled()) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, NameToId(name), true);
if (access.nesting) {
Address address = frame.loadNameAddress(access);
JSValueType type = knownPushedType(0);
BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
/* testUndefined = */ true);
finishBarrier(barrier, REJOIN_GETTER, 0);
return;
}
}
PICGenInfo pic(ic::PICInfo::NAME, JSOp(*PC));
RESERVE_IC_SPACE(masm);
@ -5597,6 +5693,24 @@ mjit::Compiler::jsop_name(PropertyName *name, JSValueType type)
bool
mjit::Compiler::jsop_xname(PropertyName *name)
{
/*
* If this is a GETXPROP for a variable of a non-reentrant outer function,
* treat in the same way as a NAME.
*/
if (cx->typeInferenceEnabled()) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, NameToId(name), true);
if (access.nesting) {
frame.pop();
Address address = frame.loadNameAddress(access);
JSValueType type = knownPushedType(0);
BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
/* testUndefined = */ true);
finishBarrier(barrier, REJOIN_GETTER, 0);
return true;
}
}
PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC));
FrameEntry *fe = frame.peek(-1);
@ -5659,6 +5773,23 @@ mjit::Compiler::jsop_xname(PropertyName *name)
void
mjit::Compiler::jsop_bindname(PropertyName *name)
{
/*
* If this is a BINDNAME for a variable of a non-reentrant outer function,
* the object is definitely the outer function's active call object.
*/
if (cx->typeInferenceEnabled()) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, NameToId(name), true);
if (access.nesting) {
RegisterID reg = frame.allocReg();
CallObject **pobj = &access.nesting->activeCall;
masm.move(ImmPtr(pobj), reg);
masm.loadPtr(Address(reg), reg);
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
return;
}
}
PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC));
// This code does not check the frame flags to see if scopeChain has been
@ -5799,25 +5930,47 @@ 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 = ScopeCoordinateToStaticScope(script, PC).scopeShape();
/*
* TODO bug 753158: Call and Block objects should use the same layout
* strategy: up to the maximum numFixedSlots and overflow (if any) in
* dynamic slots. For now, we special case for different layouts:
*/
Address addr;
if (shape->numFixedSlots() <= sc.slot) {
masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
addr = Address(reg, (sc.slot - shape->numFixedSlots()) * sizeof(Value));
StaticBlockObject *block = ScopeCoordinateBlockChain(script, PC);
if (block) {
/*
* Block objects use a fixed AllocKind which means an invariant number
* of fixed slots. Any slot below the fixed slot count is inline, any
* slot over is in the dynamic slots.
*/
uint32_t nfixed = gc::GetGCKindSlots(BlockObject::FINALIZE_KIND);
if (nfixed <= sc.slot) {
masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
addr = Address(reg, (sc.slot - nfixed) * sizeof(Value));
} else {
addr = Address(reg, JSObject::getFixedSlotOffset(sc.slot));
}
} else {
addr = Address(reg, JSObject::getFixedSlotOffset(sc.slot));
/*
* Using special-case hackery in Shape::getChildBinding, CallObject
* slots are either altogether in fixed slots or altogether in dynamic
* slots (by having numFixed == RESERVED_SLOTS).
*/
if (script->bindings.lastShape()->numFixedSlots() <= sc.slot) {
masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
addr = Address(reg, (sc.slot - CallObject::RESERVED_SLOTS) * sizeof(Value));
} else {
addr = Address(reg, JSObject::getFixedSlotOffset(sc.slot));
}
}
if (get) {
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);
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 */);
} else {
#ifdef JSGC_INCREMENTAL_MJ
if (cx->compartment->needsBarrier()) {
@ -6313,7 +6466,7 @@ mjit::Compiler::jsop_setgname_slow(PropertyName *name)
{
prepareStubCall(Uses(2));
masm.move(ImmPtr(name), Registers::ArgReg1);
INLINE_STUBCALL(STRICT_VARIANT(script, stubs::SetGlobalName), REJOIN_FALLTHROUGH);
INLINE_STUBCALL(STRICT_VARIANT(stubs::SetGlobalName), REJOIN_FALLTHROUGH);
frame.popn(2);
pushSyncedEntry(0);
}
@ -6450,7 +6603,7 @@ void
mjit::Compiler::jsop_setelem_slow()
{
prepareStubCall(Uses(3));
INLINE_STUBCALL(STRICT_VARIANT(script, stubs::SetElem), REJOIN_FALLTHROUGH);
INLINE_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
frame.popn(3);
frame.pushSynced(JSVAL_TYPE_UNKNOWN);
}

View File

@ -1212,7 +1212,7 @@ mjit::Compiler::jsop_setelem_dense()
masm.storeValue(vr, BaseIndex(slotsReg, key.reg(), masm.JSVAL_SCALE));
stubcc.leave();
OOL_STUBCALL(STRICT_VARIANT(script, stubs::SetElem), REJOIN_FALLTHROUGH);
OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
if (!hoisted)
frame.freeReg(slotsReg);
@ -1486,7 +1486,7 @@ mjit::Compiler::jsop_setelem_typed(int atype)
frame.freeReg(objReg);
stubcc.leave();
OOL_STUBCALL(STRICT_VARIANT(script, stubs::SetElem), REJOIN_FALLTHROUGH);
OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
frame.shimmy(2);
stubcc.rejoin(Changes(2));
@ -1687,9 +1687,9 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed)
stubcc.leave();
#if defined JS_POLYIC
passICAddress(&ic);
ic.slowPathCall = OOL_STUBCALL(STRICT_VARIANT(script, ic::SetElement), REJOIN_FALLTHROUGH);
ic.slowPathCall = OOL_STUBCALL(STRICT_VARIANT(ic::SetElement), REJOIN_FALLTHROUGH);
#else
OOL_STUBCALL(STRICT_VARIANT(script, stubs::SetElem), REJOIN_FALLTHROUGH);
OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
#endif
ic.fastPathRejoin = masm.label();

View File

@ -882,6 +882,24 @@ FrameState::syncAndForgetFe(FrameEntry *fe, bool markSynced)
fe->data.setMemory();
}
inline JSC::MacroAssembler::Address
FrameState::loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access, RegisterID reg)
{
JS_ASSERT(access.script && access.nesting);
masm.move(ImmPtr(access.basePointer()), reg);
masm.loadPtr(Address(reg), reg);
return Address(reg, access.index * sizeof(Value));
}
inline JSC::MacroAssembler::Address
FrameState::loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access)
{
RegisterID reg = allocReg();
return loadNameAddress(access, reg);
}
inline void
FrameState::forgetLoopReg(FrameEntry *fe)
{

View File

@ -929,6 +929,14 @@ class FrameState
inline void syncAndForgetFe(FrameEntry *fe, bool markSynced = false);
inline void forgetLoopReg(FrameEntry *fe);
/*
* Get an address for the specified name access in another script.
* The compiler owns the result's base register.
*/
inline Address loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access);
inline Address loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access,
RegisterID reg);
private:
inline AnyRegisterID allocAndLoadReg(FrameEntry *fe, bool fp, RematInfo::RematType type);
inline void forgetReg(AnyRegisterID reg);

View File

@ -562,7 +562,7 @@ js_InternalThrow(VMFrame &f)
*/
cx->jaegerRuntime().setLastUnfinished(Jaeger_Unfinished);
if (!script->ensureRanAnalysis(cx)) {
if (!script->ensureRanAnalysis(cx, NULL)) {
js_ReportOutOfMemory(cx);
return NULL;
}
@ -736,7 +736,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
JSOp op = JSOp(*pc);
const JSCodeSpec *cs = &js_CodeSpec[op];
if (!script->ensureRanAnalysis(cx)) {
if (!script->ensureRanAnalysis(cx, NULL)) {
js_ReportOutOfMemory(cx);
return js_InternalThrow(f);
}

View File

@ -58,12 +58,12 @@ PatchGetFallback(VMFrame &f, ic::GetGlobalNameIC *ic)
void JS_FASTCALL
ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
{
RootedObject obj(f.cx, &f.fp()->global());
JSObject &obj = f.fp()->global();
PropertyName *name = f.script()->getName(GET_UINT32_INDEX(f.pc()));
RecompilationMonitor monitor(f.cx);
Shape *shape = obj->nativeLookup(f.cx, NameToId(name));
Shape *shape = obj.nativeLookup(f.cx, NameToId(name));
if (monitor.recompiled()) {
stubs::Name(f);
@ -83,10 +83,10 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
/* Patch shape guard. */
Repatcher repatcher(f.chunk());
repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj->lastProperty());
repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj.lastProperty());
/* Patch loads. */
uint32_t index = obj->dynamicSlotIndex(slot);
uint32_t index = obj.dynamicSlotIndex(slot);
JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value));
@ -107,8 +107,9 @@ template void JS_FASTCALL DisabledSetGlobal<false>(VMFrame &f, ic::SetGlobalName
static void
PatchSetFallback(VMFrame &f, ic::SetGlobalNameIC *ic)
{
JSScript *script = f.script();
Repatcher repatch(f.chunk());
VoidStubSetGlobal stub = STRICT_VARIANT(f.script(), DisabledSetGlobal);
VoidStubSetGlobal stub = STRICT_VARIANT(DisabledSetGlobal);
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub));
repatch.relink(ic->slowPathCall, fptr);
}
@ -152,20 +153,21 @@ UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, Shape *s
void JS_FASTCALL
ic::SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic)
{
RootedObject obj(f.cx, &f.fp()->global());
RootedPropertyName name(f.cx, f.script()->getName(GET_UINT32_INDEX(f.pc())));
JSObject &obj = f.fp()->global();
JSScript *script = f.script();
PropertyName *name = script->getName(GET_UINT32_INDEX(f.pc()));
RecompilationMonitor monitor(f.cx);
Shape *shape = obj->nativeLookup(f.cx, NameToId(name));
Shape *shape = obj.nativeLookup(f.cx, NameToId(name));
if (!monitor.recompiled()) {
LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape);
LookupStatus status = UpdateSetGlobalName(f, ic, &obj, shape);
if (status == Lookup_Error)
THROW();
}
STRICT_VARIANT(f.script(), stubs::SetGlobalName)(f, name);
STRICT_VARIANT(stubs::SetGlobalName)(f, name);
}
class EqualityICLinker : public LinkerHelper
@ -762,8 +764,8 @@ class CallCompiler : public BaseCompiler
args = CallArgsFromSp(f.u.call.dynamicArgc, f.regs.sp);
}
RootedFunction fun(cx);
if (!IsFunctionObject(args.calleev(), fun.address()))
JSFunction *fun;
if (!IsFunctionObject(args.calleev(), &fun))
return false;
if ((!callingNew && !fun->isNative()) || (callingNew && !fun->isNativeConstructor()))

View File

@ -68,6 +68,7 @@ class PICStubCompiler : public BaseCompiler
protected:
const char *type;
VMFrame &f;
JSScript *script;
ic::PICInfo &pic;
void *stub;
uint64_t gcNumber;
@ -75,8 +76,8 @@ class PICStubCompiler : public BaseCompiler
public:
bool canCallHook;
PICStubCompiler(const char *type, VMFrame &f, ic::PICInfo &pic, void *stub)
: BaseCompiler(f.cx), type(type), f(f), pic(pic), stub(stub),
PICStubCompiler(const char *type, VMFrame &f, JSScript *script, ic::PICInfo &pic, void *stub)
: BaseCompiler(f.cx), type(type), f(f), script(script), pic(pic), stub(stub),
gcNumber(f.cx->runtime->gcNumber), canCallHook(pic.canCallHook)
{ }
@ -112,7 +113,7 @@ class PICStubCompiler : public BaseCompiler
void spew(const char *event, const char *op) {
#ifdef JS_METHODJIT_SPEW
JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
type, event, op, f.script()->filename, CurrentLine(cx));
type, event, op, script->filename, CurrentLine(cx));
#endif
}
};
@ -164,15 +165,15 @@ GeneratePrototypeGuards(JSContext *cx, Vector<JSC::MacroAssembler::Jump,8> &mism
class SetPropCompiler : public PICStubCompiler
{
RootedObject obj;
RootedPropertyName name;
JSObject *obj;
PropertyName *name;
int lastStubSecondShapeGuard;
public:
SetPropCompiler(VMFrame &f, JSObject *obj, ic::PICInfo &pic, PropertyName *name,
SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, PropertyName *name,
VoidStubPIC stub)
: PICStubCompiler("setprop", f, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
obj(f.cx, obj), name(f.cx, name), lastStubSecondShapeGuard(pic.secondShapeGuard)
: PICStubCompiler("setprop", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
obj(obj), name(name), lastStubSecondShapeGuard(pic.secondShapeGuard)
{ }
static void reset(Repatcher &repatcher, ic::PICInfo &pic)
@ -750,17 +751,17 @@ namespace mjit {
class GetPropCompiler : public PICStubCompiler
{
RootedObject obj;
RootedPropertyName name;
int lastStubSecondShapeGuard;
JSObject *obj;
PropertyName *name;
int lastStubSecondShapeGuard;
public:
GetPropCompiler(VMFrame &f, JSObject *obj, ic::PICInfo &pic, PropertyName *name,
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, PropertyName *name,
VoidStubPIC stub)
: PICStubCompiler("getprop", f, pic,
: PICStubCompiler("getprop", f, script, pic,
JS_FUNC_TO_DATA_PTR(void *, stub)),
obj(f.cx, obj),
name(f.cx, name),
obj(obj),
name(name),
lastStubSecondShapeGuard(pic.secondShapeGuard)
{ }
@ -871,7 +872,7 @@ class GetPropCompiler : public PICStubCompiler
RecompilationMonitor monitor(f.cx);
RootedObject obj(f.cx, f.fp()->global().getOrCreateStringPrototype(f.cx));
JSObject *obj = f.fp()->global().getOrCreateStringPrototype(f.cx);
if (!obj)
return error();
@ -1216,15 +1217,12 @@ class GetPropCompiler : public PICStubCompiler
linkerEpilogue(linker, start, shapeMismatches);
}
LookupStatus generateStub(JSObject *holder, HandleShape shape)
LookupStatus generateStub(JSObject *holder, Shape *shape)
{
Vector<Jump, 8> shapeMismatches(cx);
Assembler masm;
// Ignore GC pointers baked into assembly visible on the stack.
SkipRoot skip(cx, &masm);
Label start;
Jump shapeGuardJump;
Jump argsLenGuard;
@ -1447,9 +1445,9 @@ class ScopeNameCompiler : public PICStubCompiler
}
public:
ScopeNameCompiler(VMFrame &f, JSObject *scopeChain, ic::PICInfo &pic,
ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic,
PropertyName *name, VoidStubPIC stub)
: PICStubCompiler("name", f, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
: PICStubCompiler("name", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
scopeChain(f.cx, scopeChain), name(f.cx, name),
getprop(f.cx, NULL, name, *thisFromCtor(), f)
{ }
@ -1715,9 +1713,9 @@ class BindNameCompiler : public PICStubCompiler
RootedPropertyName name;
public:
BindNameCompiler(VMFrame &f, JSObject *scopeChain, ic::PICInfo &pic,
BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic,
PropertyName *name, VoidStubPIC stub)
: PICStubCompiler("bind", f, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
: PICStubCompiler("bind", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
scopeChain(f.cx, scopeChain), name(f.cx, name)
{ }
@ -1862,7 +1860,9 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
bool cached = pic->cached;
VoidStubPIC stub = cached ? DisabledGetPropIC : DisabledGetPropNoCacheIC;
RootedPropertyName name(f.cx, pic->name);
JSScript *script = f.fp()->script();
PropertyName *name = pic->name;
if (name == f.cx->runtime->atomState.lengthAtom) {
if (IsOptimizedArguments(f.fp(), &f.regs.sp[-1])) {
f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs());
@ -1871,7 +1871,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
if (!f.regs.sp[-1].isPrimitive()) {
JSObject *obj = &f.regs.sp[-1].toObject();
if (obj->isArray() || obj->isString()) {
GetPropCompiler cc(f, obj, *pic, NULL, stub);
GetPropCompiler cc(f, script, obj, *pic, NULL, stub);
if (obj->isArray()) {
LookupStatus status = cc.generateArrayLengthStub();
if (status == Lookup_Error)
@ -1890,7 +1890,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
}
if (f.regs.sp[-1].isString()) {
GetPropCompiler cc(f, NULL, *pic, name, stub);
GetPropCompiler cc(f, script, NULL, *pic, name, stub);
if (name == f.cx->runtime->atomState.lengthAtom) {
LookupStatus status = cc.generateStringLengthStub();
if (status == Lookup_Error)
@ -1912,22 +1912,22 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
RecompilationMonitor monitor(f.cx);
RootedObject obj(f.cx, ValueToObject(f.cx, f.regs.sp[-1]));
JSObject *obj = ValueToObject(f.cx, f.regs.sp[-1]);
if (!obj)
THROW();
if (!monitor.recompiled() && pic->shouldUpdate(f.cx)) {
GetPropCompiler cc(f, obj, *pic, name, stub);
GetPropCompiler cc(f, script, obj, *pic, name, stub);
if (!cc.update())
THROW();
}
RootedValue v(f.cx);
Value v;
if (cached) {
if (!GetPropertyOperation(f.cx, f.script(), f.pc(), f.regs.sp[-1], v.address()))
if (!GetPropertyOperation(f.cx, f.script(), f.pc(), f.regs.sp[-1], &v))
THROW();
} else {
if (!obj->getProperty(f.cx, name, v.address()))
if (!obj->getProperty(f.cx, name, &v))
THROW();
}
@ -1944,13 +1944,14 @@ DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic)
void JS_FASTCALL
ic::SetProp(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
JS_ASSERT(pic->isSet());
VoidStubPIC stub = STRICT_VARIANT(f.script(), DisabledSetPropIC);
VoidStubPIC stub = STRICT_VARIANT(DisabledSetPropIC);
// Save this in case the compiler triggers a recompilation of this script.
RootedPropertyName name(f.cx, pic->name);
VoidStubName nstub = STRICT_VARIANT(f.script(), stubs::SetName);
PropertyName *name = pic->name;
VoidStubName nstub = STRICT_VARIANT(stubs::SetName);
RecompilationMonitor monitor(f.cx);
@ -1961,7 +1962,7 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic)
// Note, we can't use SetName for PROPINC PICs because the property
// cache can't handle a GET and SET from the same scripted PC.
if (!monitor.recompiled() && pic->shouldUpdate(f.cx)) {
SetPropCompiler cc(f, obj, *pic, name, stub);
SetPropCompiler cc(f, script, obj, *pic, name, stub);
LookupStatus status = cc.update();
if (status == Lookup_Error)
THROW();
@ -1985,10 +1986,12 @@ DisabledXNameIC(VMFrame &f, ic::PICInfo *pic)
void JS_FASTCALL
ic::XName(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
/* GETXPROP is guaranteed to have an object. */
JSObject *obj = &f.regs.sp[-1].toObject();
ScopeNameCompiler cc(f, obj, *pic, pic->name, DisabledXNameIC);
ScopeNameCompiler cc(f, script, obj, *pic, pic->name, DisabledXNameIC);
LookupStatus status = cc.updateForXName();
if (status == Lookup_Error)
@ -2003,14 +2006,16 @@ ic::XName(VMFrame &f, ic::PICInfo *pic)
void JS_FASTCALL
ic::Name(VMFrame &f, ic::PICInfo *pic)
{
ScopeNameCompiler cc(f, f.fp()->scopeChain(), *pic, pic->name, DisabledNameIC);
JSScript *script = f.fp()->script();
ScopeNameCompiler cc(f, script, f.fp()->scopeChain(), *pic, pic->name, DisabledNameIC);
LookupStatus status = cc.updateForName();
if (status == Lookup_Error)
THROW();
RootedValue rval(f.cx);
if (!cc.retrieve(rval.address(), PICInfo::NAME))
Value rval;
if (!cc.retrieve(&rval, PICInfo::NAME))
THROW();
f.regs.sp[0] = rval;
}
@ -2024,8 +2029,10 @@ DisabledBindNameIC(VMFrame &f, ic::PICInfo *pic)
void JS_FASTCALL
ic::BindName(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
VoidStubPIC stub = DisabledBindNameIC;
BindNameCompiler cc(f, f.fp()->scopeChain(), *pic, pic->name, stub);
BindNameCompiler cc(f, script, f.fp()->scopeChain(), *pic, pic->name, stub);
JSObject *obj = cc.update();
if (!obj)
@ -2174,7 +2181,7 @@ GetElementIC::purge(Repatcher &repatcher)
}
LookupStatus
GetElementIC::attachGetProp(VMFrame &f, HandleObject obj, HandleValue v, HandlePropertyName name,
GetElementIC::attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name,
Value *vp)
{
JS_ASSERT(v.isString());
@ -2355,7 +2362,7 @@ GetElementIC::attachGetProp(VMFrame &f, HandleObject obj, HandleValue v, HandleP
#if defined JS_METHODJIT_TYPED_ARRAY
LookupStatus
GetElementIC::attachTypedArray(VMFrame &f, HandleObject obj, HandleValue v, HandleId id, Value *vp)
GetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp)
{
JSContext *cx = f.cx;
@ -2448,18 +2455,16 @@ GetElementIC::attachTypedArray(VMFrame &f, HandleObject obj, HandleValue v, Hand
#endif /* JS_METHODJIT_TYPED_ARRAY */
LookupStatus
GetElementIC::update(VMFrame &f, HandleObject obj, HandleValue v, HandleId id, Value *vp)
GetElementIC::update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp)
{
/*JSObject *obj, const Value &v, jsid id, Value *vp)
/*
* Only treat this as a GETPROP for non-numeric string identifiers. The
* GETPROP IC assumes the id has already gone through filtering for string
* indexes in the emitter.
*/
uint32_t dummy;
if (v.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy)) {
RootedPropertyName name(f.cx, JSID_TO_ATOM(id)->asPropertyName());
return attachGetProp(f, obj, v, name, vp);
}
if (v.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy))
return attachGetProp(f, obj, v, JSID_TO_ATOM(id)->asPropertyName(), vp);
#if defined JS_METHODJIT_TYPED_ARRAY
/*
@ -2491,8 +2496,7 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
return;
}
RootedValue idval_(cx, f.regs.sp[-1]);
Value &idval = idval_.get();
Value idval = f.regs.sp[-1];
RecompilationMonitor monitor(cx);
@ -2522,7 +2526,7 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
#ifdef DEBUG
f.regs.sp[-2] = MagicValue(JS_GENERIC_MAGIC);
#endif
LookupStatus status = ic->update(f, obj, idval_, id, &f.regs.sp[-2]);
LookupStatus status = ic->update(f, obj, idval, id, &f.regs.sp[-2]);
if (status != Lookup_Uncacheable) {
if (status == Lookup_Error)
THROW();

View File

@ -243,10 +243,10 @@ struct GetElementIC : public BasePolyIC {
}
void purge(Repatcher &repatcher);
LookupStatus update(VMFrame &f, HandleObject obj, HandleValue v, HandleId id, Value *vp);
LookupStatus attachGetProp(VMFrame &f, HandleObject obj, HandleValue v, HandlePropertyName name,
LookupStatus update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
LookupStatus attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name,
Value *vp);
LookupStatus attachTypedArray(VMFrame &f, HandleObject obj, HandleValue v, HandleId id, Value *vp);
LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
LookupStatus disable(VMFrame &f, const char *reason);
LookupStatus error(JSContext *cx);
bool shouldUpdate(JSContext *cx);

View File

@ -123,7 +123,7 @@ stubs::SetElem(VMFrame &f)
Value &objval = regs.sp[-3];
Value &idval = regs.sp[-2];
RootedValue rval(cx, regs.sp[-1]);
Value rval = regs.sp[-1];
RootedId id(cx);
@ -155,7 +155,7 @@ stubs::SetElem(VMFrame &f)
}
}
} while (0);
if (!obj->setGeneric(cx, obj, id, rval.address(), strict))
if (!obj->setGeneric(cx, obj, id, &rval, strict))
THROW();
end_setelem:
/* :FIXME: Moving the assigned object into the lowest stack slot
@ -337,13 +337,13 @@ stubs::DefFun(VMFrame &f, JSFunction *fun_)
Rooted<JSObject*> parent(cx, &fp->varObj());
/* ES5 10.5 (NB: with subsequent errata). */
RootedPropertyName name(cx, fun->atom->asPropertyName());
PropertyName *name = fun->atom->asPropertyName();
RootedShape shape(cx);
RootedObject pobj(cx);
if (!parent->lookupProperty(cx, name, &pobj, &shape))
THROW();
RootedValue rval(cx, ObjectValue(*fun));
Value rval = ObjectValue(*fun);
do {
/* Steps 5d, 5f. */
@ -386,7 +386,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun_)
*/
/* Step 5f. */
if (!parent->setProperty(cx, parent, name, rval.address(), strict))
if (!parent->setProperty(cx, parent, name, &rval, strict))
THROW();
} while (false);
}
@ -465,9 +465,8 @@ StubEqualityOp(VMFrame &f)
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
RootedValue rval_(cx, regs.sp[-1]);
RootedValue lval_(cx, regs.sp[-2]);
Value &rval = rval_.get(), &lval = lval_.get();
Value rval = regs.sp[-1];
Value lval = regs.sp[-2];
bool cond;
@ -576,9 +575,8 @@ stubs::Add(VMFrame &f)
{
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
RootedValue rval_(cx, regs.sp[-1]);
RootedValue lval_(cx, regs.sp[-2]);
Value &rval = rval_.get(), &lval = lval_.get();
Value rval = regs.sp[-1];
Value lval = regs.sp[-2];
/* The string + string case is easily the hottest; try it first. */
bool lIsString = lval.isString();
@ -863,11 +861,11 @@ stubs::Neg(VMFrame &f)
void JS_FASTCALL
stubs::NewInitArray(VMFrame &f, uint32_t count)
{
Rooted<TypeObject*> type(f.cx, (TypeObject *) f.scratch);
RootedObject obj(f.cx, NewDenseAllocatedArray(f.cx, count));
if (!obj)
THROW();
TypeObject *type = (TypeObject *) f.scratch;
if (type) {
obj->setType(type);
} else {
@ -883,7 +881,7 @@ void JS_FASTCALL
stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
{
JSContext *cx = f.cx;
Rooted<TypeObject*> type(f.cx, (TypeObject *) f.scratch);
TypeObject *type = (TypeObject *) f.scratch;
RootedObject obj(cx);
if (baseobj) {
@ -921,7 +919,7 @@ stubs::InitElem(VMFrame &f, uint32_t last)
/* Find the object being initialized at top of stack. */
const Value &lref = regs.sp[-3];
JS_ASSERT(lref.isObject());
RootedObject obj(cx, &lref.toObject());
JSObject *obj = &lref.toObject();
/* Fetch id now that we have obj. */
RootedId id(cx);
@ -980,8 +978,8 @@ stubs::GetProp(VMFrame &f, PropertyName *name)
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
RootedValue rval(cx);
if (!GetPropertyOperation(cx, f.script(), f.pc(), f.regs.sp[-1], rval.address()))
Value rval;
if (!GetPropertyOperation(cx, f.script(), f.pc(), f.regs.sp[-1], &rval))
THROW();
regs.sp[-1] = rval;
@ -1024,7 +1022,8 @@ InitPropOrMethod(VMFrame &f, PropertyName *name, JSOp op)
/* Load the property's initial value into rval. */
JS_ASSERT(regs.stackDepth() >= 2);
RootedValue rval(f.cx, regs.sp[-1]);
Value rval;
rval = regs.sp[-1];
/* Load the object being initialized into lval/obj. */
RootedObject obj(cx, &regs.sp[-2].toObject());
@ -1034,7 +1033,7 @@ InitPropOrMethod(VMFrame &f, PropertyName *name, JSOp op)
RootedId id(cx, NameToId(name));
if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
? !baseops::SetPropertyHelper(cx, obj, obj, id, 0, rval.address(), false)
? !baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, false)
: !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, 0)) {
THROW();
@ -1435,7 +1434,7 @@ stubs::In(VMFrame &f)
THROWV(JS_FALSE);
}
RootedObject obj(cx, &rref.toObject());
JSObject *obj = &rref.toObject();
RootedId id(cx);
if (!FetchElementId(f.cx, obj, f.regs.sp[-2], id.address(), &f.regs.sp[-2]))
THROWV(JS_FALSE);
@ -1619,6 +1618,12 @@ stubs::HeavyweightFunctionPrologue(VMFrame &f)
THROW();
}
void JS_FASTCALL
stubs::TypeNestingPrologue(VMFrame &f)
{
f.fp()->jitTypeNestingPrologue(f.cx);
}
void JS_FASTCALL
stubs::Epilogue(VMFrame &f)
{

View File

@ -170,6 +170,7 @@ void JS_FASTCALL Exception(VMFrame &f);
void JS_FASTCALL StrictEvalPrologue(VMFrame &f);
void JS_FASTCALL HeavyweightFunctionPrologue(VMFrame &f);
void JS_FASTCALL TypeNestingPrologue(VMFrame &f);
void JS_FASTCALL AnyFrameEpilogue(VMFrame &f);
void JS_FASTCALL Epilogue(VMFrame &f);

View File

@ -1472,7 +1472,7 @@ SetThrowHook(JSContext *cx, unsigned argc, jsval *vp)
static JSBool
LineToPC(JSContext *cx, unsigned argc, jsval *vp)
{
RootedScript script(cx);
JSScript *script;
int32_t lineArg = 0;
uint32_t lineno;
jsbytecode *pc;

View File

@ -27,7 +27,7 @@ function test()
else {
expect = 'PASSED';
f = Function("return a * a;");
f = Function("a", "return (function () { return a * a;});")();
g = clone(f, {a: 3});
f = null;
gc();

View File

@ -0,0 +1,33 @@
/* -*- 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');
}

View File

@ -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().formal(i);
return getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall().arg(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().setFormal(i, v);
getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall().setArg(i, v);
else
lhs = v;
}

View File

@ -221,7 +221,7 @@ class Debugger::FrameRange
/*** Breakpoints *********************************************************************************/
BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc)
: script(script), pc(pc), enabledCount(0),
: script(script), pc(pc), scriptGlobal(NULL), enabledCount(0),
trapHandler(NULL), trapClosure(UndefinedValue())
{
JS_ASSERT(!script->hasBreakpointsAt(pc));
@ -2105,10 +2105,9 @@ class Debugger::ScriptQuery {
for (CompartmentSet::Range r = compartments.all(); !r.empty(); r.popFront()) {
for (gc::CellIter i(r.front(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasGlobal() && !script->isForEval()) {
if (!consider(script, &script->global(), vector))
return false;
}
GlobalObject *global = script->getGlobalObjectOrNull();
if (global && !consider(script, global, vector))
return false;
}
}
@ -2121,10 +2120,11 @@ class Debugger::ScriptQuery {
JSScript *script = fri.script();
/*
* Eval scripts were not considered above so we don't need to
* check the existing script vector for duplicates.
* If eval scripts never have global objects set, then we don't need
* to check the existing script vector for duplicates, since we only
* include scripts with globals above.
*/
JS_ASSERT(script->isForEval());
JS_ASSERT(!script->getGlobalObjectOrNull());
GlobalObject *global = &fri.fp()->global();
if (!consider(script, global, vector))
@ -2861,7 +2861,19 @@ Debugger::observesScript(JSScript *script) const
{
if (!enabled)
return false;
return observesGlobal(&script->global());
/* Does the script have a global stored in it? */
if (GlobalObject *global = script->getGlobalObjectOrNull())
return observesGlobal(global);
/* Is the script in a compartment this Debugger is debugging? */
JSCompartment *comp = script->compartment();
for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
if (r.front()->compartment() == comp)
return true;
}
return false;
}
static JSBool
@ -2885,7 +2897,8 @@ DebuggerScript_setBreakpoint(JSContext *cx, unsigned argc, Value *vp)
return false;
jsbytecode *pc = script->code + offset;
BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc);
Rooted<GlobalObject *> scriptGlobal(cx, script->getGlobalObjectOrNull());
BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc, scriptGlobal);
if (!site)
return false;
site->inc(cx->runtime->defaultFreeOp());
@ -3177,7 +3190,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().formal(i);
arg = fp->callObj().arg(i);
else if (fp->script()->argsObjAliasesFormals() && fp->hasArgsObj())
arg = fp->argsObj().arg(i);
else
@ -3409,8 +3422,9 @@ js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, StackFrame *fp, const jschar
JSPrincipals *prin = fp->scopeChain()->principals(cx);
bool compileAndGo = true;
bool noScriptRval = false;
bool needScriptGlobal = true;
JSScript *script = frontend::CompileScript(cx, env, fp, prin, prin,
compileAndGo, noScriptRval,
compileAndGo, noScriptRval, needScriptGlobal,
chars, length, filename, lineno,
cx->findVersion(), NULL, /* staticLimit = */ 1);
if (!script)
@ -4026,10 +4040,10 @@ DebuggerObject_isSealedHelper(JSContext *cx, unsigned argc, Value *vp, SealHelpe
ErrorCopier ec(ac, dbg->toJSObject());
bool r;
if (op == Seal) {
if (!JSObject::isSealed(cx, obj, &r))
if (!obj->isSealed(cx, &r))
return false;
} else if (op == Freeze) {
if (!JSObject::isFrozen(cx, obj, &r))
if (!obj->isFrozen(cx, &r))
return false;
} else {
r = obj->isExtensible();

View File

@ -355,6 +355,13 @@ class BreakpointSite {
jsbytecode * const pc;
private:
/*
* The holder object for script, if known, else NULL. This is NULL for
* cached eval scripts and for JSD1 traps. It is always non-null for JSD2
* breakpoints in held scripts.
*/
GlobalObject *scriptGlobal;
JSCList breakpoints; /* cyclic list of all js::Breakpoints at this instruction */
size_t enabledCount; /* number of breakpoints in the list that are enabled */
JSTrapHandler trapHandler; /* jsdbgapi trap state */
@ -367,6 +374,7 @@ class BreakpointSite {
Breakpoint *firstBreakpoint() const;
bool hasBreakpoint(Breakpoint *bp);
bool hasTrap() const { return !!trapHandler; }
GlobalObject *getScriptGlobal() const { return scriptGlobal; }
void inc(FreeOp *fop);
void dec(FreeOp *fop);

View File

@ -114,12 +114,12 @@ GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
functionProto->flags |= JSFUN_PROTOTYPE;
Rooted<JSScript*> script(cx, JSScript::Create(cx,
/* enclosingScope = */ NullPtr(),
/* savedCallerFun = */ false,
/* principals = */ NULL,
/* originPrincipals = */ NULL,
/* compileAndGo = */ false,
/* noScriptRval = */ true,
/* globalObject = */ NULL,
JSVERSION_DEFAULT,
/* staticLevel = */ 0));
if (!script || !JSScript::fullyInitTrivial(cx, script))

View File

@ -150,6 +150,17 @@ js::ObjectImpl::getSlotRange(uint32_t start, uint32_t length,
getSlotRangeUnchecked(start, length, fixedStart, fixedEnd, slotsStart, slotsEnd);
}
inline bool
js::ObjectImpl::hasContiguousSlots(uint32_t start, uint32_t count) const
{
/*
* Check that the range [start, start+count) is either all inline or all
* out of line.
*/
MOZ_ASSERT(slotInRange(start + count, SENTINEL_ALLOWED));
return start + count <= numFixedSlots() || start >= numFixedSlots();
}
inline void
js::ObjectImpl::invalidateSlotRange(uint32_t start, uint32_t length)
{

View File

@ -1043,6 +1043,8 @@ class ObjectImpl : public gc::Cell
friend struct Shape;
friend class NewObjectCache;
inline bool hasContiguousSlots(uint32_t start, uint32_t count) const;
inline void invalidateSlotRange(uint32_t start, uint32_t count);
inline void initializeSlotRange(uint32_t start, uint32_t count);

View File

@ -145,7 +145,7 @@ class SPSProfiler
class SPSEntryMarker
{
SPSProfiler *profiler;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
JS_DECL_USE_GUARD_OBJECT_NOTIFIER;
public:
SPSEntryMarker(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM);
~SPSEntryMarker();

View File

@ -10,8 +10,6 @@
#include "ScopeObject.h"
#include "jsscriptinlines.h"
namespace js {
inline
@ -74,14 +72,14 @@ CallObject::callee() const
}
inline const Value &
CallObject::formal(unsigned i, MaybeCheckAliasing checkAliasing) const
CallObject::arg(unsigned i, MaybeCheckAliasing checkAliasing) const
{
JS_ASSERT_IF(checkAliasing, callee().script()->formalLivesInCallObject(i));
return getSlot(RESERVED_SLOTS + i);
}
inline void
CallObject::setFormal(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
CallObject::setArg(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
{
JS_ASSERT_IF(checkAliasing, callee().script()->formalLivesInCallObject(i));
setSlot(RESERVED_SLOTS + i, v);
@ -103,6 +101,25 @@ CallObject::setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
setSlot(RESERVED_SLOTS + fun.nargs + i, v);
}
inline HeapSlotArray
CallObject::argArray()
{
#ifdef DEBUG
JSFunction &fun = callee();
JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS, fun.nargs));
#endif
return HeapSlotArray(getSlotAddress(RESERVED_SLOTS));
}
inline HeapSlotArray
CallObject::varArray()
{
JSFunction &fun = callee();
JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS + fun.nargs,
fun.script()->bindings.numVars()));
return HeapSlotArray(getSlotAddress(RESERVED_SLOTS + fun.nargs));
}
inline uint32_t
NestedScopeObject::stackDepth() const
{
@ -154,36 +171,17 @@ BlockObject::setSlotValue(unsigned i, const Value &v)
setSlot(RESERVED_SLOTS + i, v);
}
inline void
StaticBlockObject::initPrevBlockChainFromParser(StaticBlockObject *prev)
{
setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
}
inline void
StaticBlockObject::resetPrevBlockChainFromParser()
{
setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
}
inline void
StaticBlockObject::initEnclosingStaticScope(JSObject *obj)
{
JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
}
inline StaticBlockObject *
StaticBlockObject::enclosingBlock() const
{
JSObject *obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
return obj && obj->isStaticBlock() ? &obj->asStaticBlock() : NULL;
return obj ? &obj->asStaticBlock() : NULL;
}
inline JSObject *
StaticBlockObject::enclosingStaticScope() const
inline void
StaticBlockObject::setEnclosingBlock(StaticBlockObject *blockObj)
{
return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
setFixedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(blockObj));
}
inline void

View File

@ -23,114 +23,63 @@ using namespace js::types;
/*****************************************************************************/
StaticScopeIter::StaticScopeIter(JSObject *obj)
: obj(obj), onNamedLambda(false)
StaticBlockObject *
js::ScopeCoordinateBlockChain(JSScript *script, jsbytecode *pc)
{
JS_ASSERT_IF(obj, obj->isStaticBlock() || obj->isFunction());
}
bool
StaticScopeIter::done() const
{
return obj == NULL;
}
void
StaticScopeIter::operator++(int)
{
if (obj->isStaticBlock()) {
obj = obj->asStaticBlock().enclosingStaticScope();
} else if (onNamedLambda || !obj->toFunction()->isNamedLambda()) {
onNamedLambda = false;
obj = obj->toFunction()->script()->enclosingStaticScope();
} else {
onNamedLambda = true;
}
JS_ASSERT_IF(obj, obj->isStaticBlock() || obj->isFunction());
JS_ASSERT_IF(onNamedLambda, obj->isFunction());
}
bool
StaticScopeIter::hasDynamicScopeObject() const
{
return obj->isStaticBlock()
? obj->asStaticBlock().needsClone()
: 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
{
if (onNamedLambda)
return NAMED_LAMBDA;
return obj->isStaticBlock() ? BLOCK : FUNCTION;
}
StaticBlockObject &
StaticScopeIter::block() const
{
JS_ASSERT(type() == BLOCK);
return obj->asStaticBlock();
}
JSScript *
StaticScopeIter::funScript() const
{
JS_ASSERT(type() == FUNCTION);
return obj->toFunction()->script();
}
/*****************************************************************************/
StaticScopeIter
js::ScopeCoordinateToStaticScope(JSScript *script, jsbytecode *pc)
{
JS_ASSERT(pc >= script->code && pc < script->code + script->length);
JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
ScopeCoordinate sc(pc);
uint32_t blockIndex = GET_UINT32_INDEX(pc + 2 * sizeof(uint16_t));
JSObject *innermostStaticScope;
if (blockIndex == UINT32_MAX)
innermostStaticScope = script->function();
else
innermostStaticScope = &script->getObject(blockIndex)->asStaticBlock();
return NULL;
StaticScopeIter ssi(innermostStaticScope);
ScopeCoordinate sc(pc);
StaticBlockObject *block = &script->getObject(blockIndex)->asStaticBlock();
unsigned i = 0;
while (true) {
if (ssi.hasDynamicScopeObject()) {
if (!sc.hops)
break;
sc.hops--;
}
ssi++;
while (block && !block->needsClone())
block = block->enclosingBlock();
if (i++ == sc.hops)
break;
block = block->enclosingBlock();
}
return ssi;
return block;
}
PropertyName *
js::ScopeCoordinateName(JSRuntime *rt, JSScript *script, jsbytecode *pc)
{
Shape::Range r = ScopeCoordinateToStaticScope(script, pc).scopeShape()->all();
StaticBlockObject *maybeBlock = ScopeCoordinateBlockChain(script, pc);
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;
}
/*****************************************************************************/
/*
@ -162,7 +111,18 @@ CallObject::create(JSContext *cx, JSScript *script, HandleObject enclosing, Hand
if (!obj)
return NULL;
JS_ASSERT(enclosing->global() == obj->global());
/*
* Update the parent for bindings associated with non-compileAndGo scripts,
* whose call objects do not have a consistent global variable and need
* to be updated dynamically.
*/
if (&enclosing->global() != obj->getParent()) {
JS_ASSERT(obj->getParent() == NULL);
Rooted<GlobalObject*> global(cx, &enclosing->global());
if (!JSObject::setParent(cx, obj, global))
return NULL;
}
if (!obj->asScope().setEnclosingScope(cx, enclosing))
return NULL;
@ -191,7 +151,7 @@ CallObject::createForFunction(JSContext *cx, StackFrame *fp)
* For a named function expression Call's parent points to an environment
* object holding function's name.
*/
if (fp->fun()->isNamedLambda()) {
if (js_IsNamedLambda(fp->fun())) {
scopeChain = DeclEnvObject::create(cx, fp);
if (!scopeChain)
return NULL;
@ -207,12 +167,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->setFormal(slot, formals[slot]);
callobj->setArg(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->setFormal(slot, formals[slot]);
callobj->setArg(slot, formals[slot]);
}
}
@ -233,9 +193,9 @@ CallObject::copyUnaliasedValues(StackFrame *fp)
for (unsigned i = 0; i < script->bindings.numArgs(); ++i) {
if (!script->formalLivesInCallObject(i)) {
if (script->argsObjAliasesFormals() && fp->hasArgsObj())
setFormal(i, fp->argsObj().arg(i), DONT_CHECK_ALIASING);
setArg(i, fp->argsObj().arg(i), DONT_CHECK_ALIASING);
else
setFormal(i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING);
setArg(i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING);
}
}
@ -260,20 +220,40 @@ CallObject::createForStrictEval(JSContext *cx, StackFrame *fp)
JSBool
CallObject::setArgOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, Value *vp)
{
/* TODO: this can totally be a data property now. */
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
unsigned i = (uint16_t) JSID_TO_INT(id);
obj->asCall().setFormal(i, *vp);
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);
return true;
}
JSBool
CallObject::setVarOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, Value *vp)
{
/* TODO: this can totally be a data property now. */
CallObject &callobj = obj->asCall();
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
unsigned i = (uint16_t) JSID_TO_INT(id);
obj->asCall().setVar(i, *vp);
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);
return true;
}
@ -712,21 +692,45 @@ Class js::BlockClass = {
JS_ConvertStub
};
#define NO_PARENT_INDEX UINT32_MAX
/*
* If there's a parent id, then get the parent out of our script's object
* array. We know that we clone block objects in outer-to-inner order, which
* means that getting the parent now will work.
*/
static uint32_t
FindObjectIndex(JSScript *script, StaticBlockObject *maybeBlock)
{
if (!maybeBlock || !script->hasObjects())
return NO_PARENT_INDEX;
ObjectArray *objects = script->objects();
HeapPtrObject *vector = objects->vector;
unsigned length = objects->length;
for (unsigned i = 0; i < length; ++i) {
if (vector[i] == maybeBlock)
return i;
}
return NO_PARENT_INDEX;
}
template<XDRMode mode>
bool
js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
StaticBlockObject **objp)
js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObject **objp)
{
/* NB: Keep this in sync with CloneStaticBlockObject. */
JSContext *cx = xdr->cx();
Rooted<StaticBlockObject*> obj(cx);
uint32_t parentId = 0;
uint32_t count = 0;
uint32_t depthAndCount = 0;
if (mode == XDR_ENCODE) {
obj = *objp;
parentId = FindObjectIndex(script, obj->enclosingBlock());
uint32_t depth = obj->stackDepth();
JS_ASSERT(depth <= UINT16_MAX);
count = obj->slotCount();
@ -734,14 +738,23 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, Handl
depthAndCount = (depth << 16) | uint16_t(count);
}
/* First, XDR the parent atomid. */
if (!xdr->codeUint32(&parentId))
return false;
if (mode == XDR_DECODE) {
obj = StaticBlockObject::create(cx);
if (!obj)
return false;
obj->initEnclosingStaticScope(enclosingScope);
*objp = obj;
obj->setEnclosingBlock(parentId == NO_PARENT_INDEX
? NULL
: &script->getObject(parentId)->asStaticBlock());
}
AutoObjectRooter tvr(cx, obj);
if (!xdr->codeUint32(&depthAndCount))
return false;
@ -816,13 +829,14 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, Handl
}
template bool
js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, StaticBlockObject **);
js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *xdr, JSScript *script, StaticBlockObject **objp);
template bool
js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, HandleScript, StaticBlockObject **);
js::XDRStaticBlockObject(XDRState<XDR_DECODE> *xdr, JSScript *script, StaticBlockObject **objp);
JSObject *
js::CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> srcBlock)
js::CloneStaticBlockObject(JSContext *cx, Handle<StaticBlockObject*> srcBlock,
const AutoObjectVector &objects, JSScript *src)
{
/* NB: Keep this in sync with XDRStaticBlockObject. */
@ -830,7 +844,11 @@ js::CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<St
if (!clone)
return NULL;
clone->initEnclosingStaticScope(enclosingScope);
uint32_t parentId = FindObjectIndex(src, srcBlock->enclosingBlock());
clone->setEnclosingBlock(parentId == NO_PARENT_INDEX
? NULL
: &objects[parentId]->asStaticBlock());
clone->setStackDepth(srcBlock->stackDepth());
/* Shape::Range is reverse order, so build a list in forward order. */
@ -1165,9 +1183,9 @@ class DebugScopeProxy : public BaseProxyHandler
}
} else {
if (action == GET)
*vp = callobj.formal(i, DONT_CHECK_ALIASING);
*vp = callobj.arg(i, DONT_CHECK_ALIASING);
else
callobj.setFormal(i, *vp, DONT_CHECK_ALIASING);
callobj.setArg(i, *vp, DONT_CHECK_ALIASING);
}
if (action == SET)

View File

@ -18,72 +18,14 @@ namespace js {
/*****************************************************************************/
/*
* All function scripts have an "enclosing static scope" that refers to the
* innermost enclosing let or function in the program text. This allows full
* reconstruction of the lexical scope for debugging or compiling efficient
* access to variables in enclosing scopes. The static scope is represented at
* runtime by a tree of compiler-created objects representing each scope:
* - a StaticBlockObject is created for 'let' and 'catch' scopes
* - a JSFunction+JSScript+Bindings trio is created for function scopes
* (These objects are primarily used to clone objects scopes for the
* dynamic scope chain.)
*
* There is an additional scope for named lambdas. E.g., in:
*
* (function f() { var x; function g() { } })
*
* g's innermost enclosing scope will first be the function scope containing
* 'x', enclosed by a scope containing only the name 'f'. (This separate scope
* is necessary due to the fact that declarations in the function scope shadow
* (dynamically, in the case of 'eval') the lambda name.)
*
* There are two limitations to the current lexical nesting information:
*
* - 'with' is completely absent; this isn't a problem for the current use
* cases since 'with' causes every static scope to be on the dynamic scope
* chain (so the debugger can find everything) and inhibits all upvar
* optimization.
*
* - The "enclosing static scope" chain stops at 'eval'. For example in:
* let (x) { eval("function f() {}") }
* f does not have an enclosing static scope. This is fine for current uses
* for the same reason as 'with'.
*
* (See also AssertDynamicScopeMatchesStaticScope.)
*/
class StaticScopeIter
{
JSObject *obj;
bool onNamedLambda;
public:
explicit StaticScopeIter(JSObject *obj);
bool done() const;
void operator++(int);
/* 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;
StaticBlockObject &block() const;
JSScript *funScript() const;
};
/*****************************************************************************/
/*
* A "scope coordinate" describes how to get from head of the scope chain to a
* given lexically-enclosing variable. A scope coordinate has two dimensions:
* - hops: the number of scope objects on the scope chain to skip
* - slot: the slot on the scope object holding the variable's value
* - binding: which binding on the scope object
* Additionally (as described in jsopcode.tbl) there is a 'block' index, but
* this is only needed for decompilation/inference so it is not included in the
* main ScopeCoordinate struct: use ScopeCoordinate{BlockChain,Name} instead.
* main ScopeCoordinate struct: use ScopeCoordinate{BlockChain,Atom} instead.
*/
struct ScopeCoordinate
{
@ -94,17 +36,25 @@ struct ScopeCoordinate
inline ScopeCoordinate() {}
};
/*
* 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 static block chain (or null) accessed by *pc. */
extern StaticBlockObject *
ScopeCoordinateBlockChain(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);
/*****************************************************************************/
/*
@ -194,15 +144,22 @@ class CallObject : public ScopeObject
*/
inline JSFunction &callee() const;
/* 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 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 variable at the given index. */
/* Returns 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. */
/*
* Get the actual arrays of arguments and variables. Only call if type
* inference is enabled, where we ensure that call object variables are in
* contiguous slots (see NewCallObject).
*/
inline HeapSlotArray argArray();
inline HeapSlotArray varArray();
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);
@ -279,39 +236,11 @@ class StaticBlockObject : public BlockObject
public:
static StaticBlockObject *create(JSContext *cx);
/* See StaticScopeIter comment. */
inline JSObject *enclosingStaticScope() const;
/*
* A refinement of enclosingStaticScope that returns NULL if the enclosing
* static scope is a JSFunction.
*/
inline StaticBlockObject *enclosingBlock() const;
inline void setEnclosingBlock(StaticBlockObject *blockObj);
/*
* Return whether this StaticBlockObject contains a variable stored at
* the given stack depth (i.e., fp->base()[depth]).
*/
bool containsVarAtDepth(uint32_t depth);
/*
* A let binding is aliased if accessed lexically by nested functions or
* dynamically through dynamic name lookup (eval, with, function::, etc).
*/
bool isAliased(unsigned i);
/*
* A static block object is cloned (when entering the block) iff some
* variable of the block isAliased.
*/
bool needsClone();
/* Frontend-only functions ***********************************************/
/* Initialization functions for above fields. */
void setAliased(unsigned i, bool aliased);
void setStackDepth(uint32_t depth);
void initEnclosingStaticScope(JSObject *obj);
bool containsVarAtDepth(uint32_t depth);
/*
* Frontend compilation temporarily uses the object's slots to link
@ -321,13 +250,17 @@ class StaticBlockObject : public BlockObject
Definition *maybeDefinitionParseNode(unsigned i);
/*
* The parser uses 'enclosingBlock' as the prev-link in the tc->blockChain
* stack. Note: in the case of hoisting, this prev-link will not ultimately
* be the same as enclosingBlock, initEnclosingStaticScope must be called
* separately in the emitter. 'reset' is just for asserting stackiness.
* A let binding is aliased is accessed lexically by nested functions or
* dynamically through dynamic name lookup (eval, with, function::, etc).
*/
void initPrevBlockChainFromParser(StaticBlockObject *prev);
void resetPrevBlockChainFromParser();
void setAliased(unsigned i, bool aliased);
bool isAliased(unsigned i);
/*
* A static block object is cloned (when entering the block) iff some
* variable of the block isAliased.
*/
bool needsClone();
static Shape *addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id,
int index, bool *redeclared);
@ -352,11 +285,11 @@ class ClonedBlockObject : public BlockObject
template<XDRMode mode>
bool
XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
StaticBlockObject **objp);
XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObject **objp);
extern JSObject *
CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> src);
CloneStaticBlockObject(JSContext *cx, Handle<StaticBlockObject*> srcBlock,
const AutoObjectVector &objects, JSScript *src);
/*****************************************************************************/

View File

@ -186,9 +186,21 @@ StackFrame::jitHeavyweightFunctionPrologue(JSContext *cx)
pushOnScopeChain(*callobj);
flags_ |= HAS_CALL_OBJ;
if (script()->nesting()) {
types::NestingPrologue(cx, this);
flags_ |= HAS_NESTING;
}
return true;
}
inline void
StackFrame::jitTypeNestingPrologue(JSContext *cx)
{
types::NestingPrologue(cx, this);
flags_ |= HAS_NESTING;
}
inline void
StackFrame::initVarsToUndefined()
{

View File

@ -222,42 +222,6 @@ StackFrame::pcQuadratic(const ContextStack &stack, size_t maxDepth)
return regs.fp()->script()->code;
}
static inline void
AssertDynamicScopeMatchesStaticScope(JSScript *script, JSObject *scope)
{
#ifdef DEBUG
for (StaticScopeIter i(script->enclosingStaticScope()); !i.done(); i++) {
if (i.hasDynamicScopeObject()) {
/*
* 'with' does not participate in the static scope of the script,
* but it does in the dynamic scope, so skip them here.
*/
while (scope->isWith())
scope = &scope->asWith().enclosingScope();
switch (i.type()) {
case StaticScopeIter::BLOCK:
JS_ASSERT(i.block() == scope->asClonedBlock().staticBlock());
scope = &scope->asClonedBlock().enclosingScope();
break;
case StaticScopeIter::FUNCTION:
JS_ASSERT(i.funScript() == scope->asCall().callee().script());
scope = &scope->asCall().enclosingScope();
break;
case StaticScopeIter::NAMED_LAMBDA:
scope = &scope->asDeclEnv().enclosingScope();
break;
}
}
}
/*
* Ideally, we'd JS_ASSERT(!scope->isScope()) but the enclosing lexical
* scope chain stops at eval() boundaries. See StaticScopeIter comment.
*/
#endif
}
bool
StackFrame::prologue(JSContext *cx, bool newType)
{
@ -283,7 +247,6 @@ StackFrame::prologue(JSContext *cx, bool newType)
}
JS_ASSERT(isNonEvalFunctionFrame());
AssertDynamicScopeMatchesStaticScope(script(), scopeChain());
if (fun()->isHeavyweight()) {
CallObject *callobj = CallObject::createForFunction(cx, this);
@ -293,6 +256,11 @@ StackFrame::prologue(JSContext *cx, bool newType)
flags_ |= HAS_CALL_OBJ;
}
if (script()->nesting()) {
types::NestingPrologue(cx, this);
flags_ |= HAS_NESTING;
}
if (isConstructing()) {
RootedObject callee(cx, &this->callee());
JSObject *obj = js_CreateThisForFunction(cx, callee, newType);
@ -336,15 +304,18 @@ StackFrame::epilogue(JSContext *cx)
}
JS_ASSERT(isNonEvalFunctionFrame());
if (fun()->isHeavyweight())
if (fun()->isHeavyweight()) {
JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().callee().script() == script());
else
AssertDynamicScopeMatchesStaticScope(script(), scopeChain());
} else {
JS_ASSERT(!scopeChain()->isCall() || scopeChain()->asCall().isForEval() ||
scopeChain()->asCall().callee().script() != script());
}
if (cx->compartment->debugMode())
cx->runtime->debugScopes->onPopCall(this, cx);
if (script()->nesting() && (flags_ & HAS_NESTING))
types::NestingEpilogue(this);
if (isConstructing() && returnValue().isPrimitive())
setReturnValue(ObjectValue(constructorThis()));

View File

@ -207,7 +207,7 @@ enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
enum InitialFrameFlags {
INITIAL_NONE = 0,
INITIAL_CONSTRUCT = 0x40, /* == StackFrame::CONSTRUCTING, asserted below */
INITIAL_LOWERED = 0x100000 /* == StackFrame::LOWERED_CALL_APPLY, asserted below */
INITIAL_LOWERED = 0x200000 /* == StackFrame::LOWERED_CALL_APPLY, asserted below */
};
enum ExecuteType {
@ -245,24 +245,25 @@ class StackFrame
/* Function prologue state */
HAS_CALL_OBJ = 0x800, /* CallObject created for heavyweight fun */
HAS_ARGS_OBJ = 0x1000, /* ArgumentsObject created for needsArgsObj script */
HAS_NESTING = 0x2000, /* NestingPrologue called for frame */
/* Lazy frame initialization */
HAS_HOOK_DATA = 0x2000, /* frame has hookData_ set */
HAS_ANNOTATION = 0x4000, /* frame has annotation_ set */
HAS_RVAL = 0x8000, /* frame has rval_ set */
HAS_SCOPECHAIN = 0x10000, /* frame has scopeChain_ set */
HAS_PREVPC = 0x20000, /* frame has prevpc_ and prevInline_ set */
HAS_BLOCKCHAIN = 0x40000, /* frame has blockChain_ set */
HAS_HOOK_DATA = 0x4000, /* frame has hookData_ set */
HAS_ANNOTATION = 0x8000, /* frame has annotation_ set */
HAS_RVAL = 0x10000, /* frame has rval_ set */
HAS_SCOPECHAIN = 0x20000, /* frame has scopeChain_ set */
HAS_PREVPC = 0x40000, /* frame has prevpc_ and prevInline_ set */
HAS_BLOCKCHAIN = 0x80000, /* frame has blockChain_ set */
/* Method JIT state */
DOWN_FRAMES_EXPANDED = 0x80000, /* inlining in down frames has been expanded */
LOWERED_CALL_APPLY = 0x100000, /* Pushed by a lowered call/apply */
DOWN_FRAMES_EXPANDED = 0x100000, /* inlining in down frames has been expanded */
LOWERED_CALL_APPLY = 0x200000, /* Pushed by a lowered call/apply */
/* Debugger state */
PREV_UP_TO_DATE = 0x200000, /* see DebugScopes::updateLiveScopes */
PREV_UP_TO_DATE = 0x400000, /* see DebugScopes::updateLiveScopes */
/* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */
HAS_PUSHED_SPS_FRAME = 0x400000 /* SPS was notified of enty */
HAS_PUSHED_SPS_FRAME = 0x800000 /* SPS was notified of enty */
};
private:
@ -369,6 +370,7 @@ class StackFrame
/* Subsets of 'prologue' called from jit code. */
inline bool jitHeavyweightFunctionPrologue(JSContext *cx);
inline void jitTypeNestingPrologue(JSContext *cx);
bool jitStrictEvalPrologue(JSContext *cx);
/* Initialize local variables of newly-pushed frame. */

View File

@ -123,10 +123,7 @@ XDRState<mode>::codeFunction(JSObject **objp)
if (mode == XDR_DECODE)
*objp = NULL;
if (!VersionCheck(this))
return false;
return XDRInterpretedFunction(this, NullPtr(), NullPtr(), objp);
return VersionCheck(this) && XDRInterpretedFunction(this, objp, NULL);
}
template<XDRMode mode>
@ -141,14 +138,12 @@ XDRState<mode>::codeScript(JSScript **scriptp)
script = *scriptp;
}
if (!VersionCheck(this))
return false;
if (!XDRScript(this, NullPtr(), NullPtr(), NullPtr(), &script))
if (!VersionCheck(this) || !XDRScript(this, &script, NULL))
return false;
if (mode == XDR_DECODE) {
JS_ASSERT(!script->compileAndGo);
script->globalObject = GetCurrentGlobal(cx());
js_CallNewScriptHook(cx(), script, NULL);
Debugger::onNewScript(cx(), script, NULL);
*scriptp = script;

View File

@ -25,7 +25,7 @@ namespace js {
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 120);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 119);
class XDRBuffer {
public: