mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 678037 - Add (disabled) ability to parse script bytecode lazily, r=luke.
This commit is contained in:
parent
d3b1307dba
commit
ddbb9d9519
@ -152,9 +152,11 @@ struct ZoneStats
|
||||
gcHeapUnusedGcThings(0),
|
||||
gcHeapStringsNormal(0),
|
||||
gcHeapStringsShort(0),
|
||||
gcHeapLazyScripts(0),
|
||||
gcHeapTypeObjects(0),
|
||||
gcHeapIonCodes(0),
|
||||
stringCharsNonHuge(0),
|
||||
lazyScripts(0),
|
||||
typeObjects(0),
|
||||
typePool(0),
|
||||
hugeStrings()
|
||||
@ -166,9 +168,11 @@ struct ZoneStats
|
||||
gcHeapUnusedGcThings(other.gcHeapUnusedGcThings),
|
||||
gcHeapStringsNormal(other.gcHeapStringsNormal),
|
||||
gcHeapStringsShort(other.gcHeapStringsShort),
|
||||
gcHeapLazyScripts(other.gcHeapLazyScripts),
|
||||
gcHeapTypeObjects(other.gcHeapTypeObjects),
|
||||
gcHeapIonCodes(other.gcHeapIonCodes),
|
||||
stringCharsNonHuge(other.stringCharsNonHuge),
|
||||
lazyScripts(other.lazyScripts),
|
||||
typeObjects(other.typeObjects),
|
||||
typePool(other.typePool),
|
||||
hugeStrings()
|
||||
@ -185,10 +189,12 @@ struct ZoneStats
|
||||
|
||||
ADD(gcHeapStringsNormal);
|
||||
ADD(gcHeapStringsShort);
|
||||
ADD(gcHeapLazyScripts);
|
||||
ADD(gcHeapTypeObjects);
|
||||
ADD(gcHeapIonCodes);
|
||||
|
||||
ADD(stringCharsNonHuge);
|
||||
ADD(lazyScripts);
|
||||
ADD(typeObjects);
|
||||
ADD(typePool);
|
||||
|
||||
@ -206,10 +212,12 @@ struct ZoneStats
|
||||
size_t gcHeapStringsNormal;
|
||||
size_t gcHeapStringsShort;
|
||||
|
||||
size_t gcHeapLazyScripts;
|
||||
size_t gcHeapTypeObjects;
|
||||
size_t gcHeapIonCodes;
|
||||
|
||||
size_t stringCharsNonHuge;
|
||||
size_t lazyScripts;
|
||||
size_t typeObjects;
|
||||
size_t typePool;
|
||||
|
||||
|
@ -191,6 +191,30 @@ TryEvalJSON(JSContext *cx, JSScript *callerScript,
|
||||
return EvalJSON_NotJSON;
|
||||
}
|
||||
|
||||
static void
|
||||
MarkFunctionsWithinEvalScript(JSScript *script)
|
||||
{
|
||||
// Mark top level functions in an eval script as being within an eval and,
|
||||
// if applicable, inside a with statement.
|
||||
|
||||
if (!script->hasObjects())
|
||||
return;
|
||||
|
||||
ObjectArray *objects = script->objects();
|
||||
size_t start = script->innerObjectsStart();
|
||||
|
||||
for (size_t i = start; i < objects->length; i++) {
|
||||
JSObject *obj = objects->vector[i];
|
||||
if (obj->isFunction()) {
|
||||
JSFunction *fun = obj->toFunction();
|
||||
if (fun->hasScript())
|
||||
fun->nonLazyScript()->directlyInsideEval = true;
|
||||
else if (fun->isInterpretedLazy())
|
||||
fun->lazyScript()->setDirectlyInsideEval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define subset of ExecuteType so that casting performs the injection.
|
||||
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
|
||||
|
||||
@ -295,6 +319,8 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
||||
if (!compiled)
|
||||
return false;
|
||||
|
||||
MarkFunctionsWithinEvalScript(compiled);
|
||||
|
||||
esg.setNewScript(compiled);
|
||||
}
|
||||
|
||||
@ -357,6 +383,8 @@ js::DirectEvalFromIon(JSContext *cx,
|
||||
if (!compiled)
|
||||
return false;
|
||||
|
||||
MarkFunctionsWithinEvalScript(compiled);
|
||||
|
||||
esg.setNewScript(compiled);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
using mozilla::Maybe;
|
||||
|
||||
static bool
|
||||
CheckLength(JSContext *cx, size_t length)
|
||||
@ -84,6 +85,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
SourceCompressionToken *extraSct /* = NULL */)
|
||||
{
|
||||
RootedString source(cx, source_);
|
||||
SkipRoot skip(cx, &chars);
|
||||
|
||||
/*
|
||||
* The scripted callerFrame can only be given for compile-and-go scripts
|
||||
@ -119,15 +121,28 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
break;
|
||||
}
|
||||
|
||||
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
|
||||
Maybe<Parser<SyntaxParseHandler> > syntaxParser;
|
||||
if (options.canLazilyParse) {
|
||||
syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
|
||||
(Parser<SyntaxParseHandler> *) NULL,
|
||||
(LazyScript *) NULL);
|
||||
}
|
||||
|
||||
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
|
||||
options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
|
||||
if (!parser.init())
|
||||
return NULL;
|
||||
parser.sct = sct;
|
||||
|
||||
GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
|
||||
|
||||
ParseContext<FullParseHandler> pc(&parser, NULL, &globalsc, staticLevel, /* bodyid = */ 0);
|
||||
if (!pc.init())
|
||||
// Syntax parsing may cause us to restart processing of top level
|
||||
// statements in the script. Use Maybe<> so that the parse context can be
|
||||
// reset when this occurs.
|
||||
Maybe<ParseContext<FullParseHandler> > pc;
|
||||
|
||||
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc, staticLevel, /* bodyid = */ 0);
|
||||
if (!pc.ref().init())
|
||||
return NULL;
|
||||
|
||||
bool savedCallerFun =
|
||||
@ -150,8 +165,10 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
JS_ASSERT_IF(globalScope, globalScope->isNative());
|
||||
JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
|
||||
|
||||
BytecodeEmitter::EmitterMode emitterMode =
|
||||
options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
|
||||
BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, options.forEval, evalCaller,
|
||||
!!globalScope, options.lineno, options.selfHostingMode);
|
||||
!!globalScope, options.lineno, emitterMode);
|
||||
if (!bce.init())
|
||||
return NULL;
|
||||
|
||||
@ -178,7 +195,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
* wishes to decompile it while it's running.
|
||||
*/
|
||||
JSFunction *fun = evalCaller->functionOrCallerFunction();
|
||||
ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict());
|
||||
ObjectBox *funbox = parser.newFunctionBox(fun, pc.addr(), fun->strict());
|
||||
if (!funbox)
|
||||
return NULL;
|
||||
bce.objectList.add(funbox);
|
||||
@ -195,9 +212,32 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TokenStream::Position pos(parser.keepAtoms);
|
||||
parser.tokenStream.tell(&pos);
|
||||
|
||||
ParseNode *pn = parser.statement();
|
||||
if (!pn)
|
||||
return NULL;
|
||||
if (!pn) {
|
||||
if (parser.hadAbortedSyntaxParse()) {
|
||||
// Parsing inner functions lazily may lead the parser into an
|
||||
// unrecoverable state and may require starting over on the top
|
||||
// level statement. Restart the parse; syntax parsing has
|
||||
// already been disabled for the parser and the result will not
|
||||
// be ambiguous.
|
||||
parser.clearAbortedSyntaxParse();
|
||||
parser.tokenStream.seek(pos);
|
||||
pc.destroy();
|
||||
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc,
|
||||
staticLevel, /* bodyid = */ 0);
|
||||
if (!pc.ref().init())
|
||||
return NULL;
|
||||
JS_ASSERT(parser.pc == pc.addr());
|
||||
pn = parser.statement();
|
||||
}
|
||||
if (!pn) {
|
||||
JS_ASSERT(!parser.hadAbortedSyntaxParse());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (canHaveDirectives) {
|
||||
if (!parser.maybeParseDirective(pn, &canHaveDirectives))
|
||||
@ -223,13 +263,13 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
// free variables and as variables redeclared with 'var'.
|
||||
RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
|
||||
HandlePropertyName arguments = cx->names().arguments;
|
||||
for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
|
||||
for (AtomDefnRange r = pc.ref().lexdeps->all(); !r.empty(); r.popFront()) {
|
||||
if (r.front().key() == arguments) {
|
||||
if (!CheckArgumentsWithinEval(cx, parser, fun))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) {
|
||||
for (AtomDefnListMap::Range r = pc.ref().decls().all(); !r.empty(); r.popFront()) {
|
||||
if (r.front().key() == arguments) {
|
||||
if (!CheckArgumentsWithinEval(cx, parser, fun))
|
||||
return NULL;
|
||||
@ -239,7 +279,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
// If the eval'ed script contains any debugger statement, force construction
|
||||
// of arguments objects for the caller script and any other scripts it is
|
||||
// transitively nested inside.
|
||||
if (pc.sc->hasDebuggerStatement()) {
|
||||
if (pc.ref().sc->hasDebuggerStatement()) {
|
||||
RootedObject scope(cx, scopeChain);
|
||||
while (scope->isScope() || scope->isDebugScope()) {
|
||||
if (scope->isCall() && !scope->asCall().isForEval()) {
|
||||
@ -273,43 +313,55 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::ParseScript(JSContext *cx, HandleObject scopeChain,
|
||||
const CompileOptions &options, StableCharPtr chars, size_t length)
|
||||
frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
|
||||
const jschar *chars, size_t length)
|
||||
{
|
||||
if (!CheckLength(cx, length))
|
||||
CompileOptions options(cx);
|
||||
options.setPrincipals(cx->compartment->principals)
|
||||
.setOriginPrincipals(lazy->parent()->originPrincipals)
|
||||
.setVersion(lazy->parent()->getVersion())
|
||||
.setFileAndLine(lazy->parent()->filename(), lazy->lineno())
|
||||
.setColumn(lazy->column())
|
||||
.setCompileAndGo(lazy->parent()->compileAndGo)
|
||||
.setNoScriptRval(false)
|
||||
.setSelfHostingMode(false);
|
||||
|
||||
Parser<FullParseHandler> parser(cx, options, chars, length,
|
||||
/* foldConstants = */ true, NULL, lazy);
|
||||
if (!parser.init())
|
||||
return false;
|
||||
|
||||
Parser<SyntaxParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
|
||||
if (!parser.init()) {
|
||||
cx->clearPendingException();
|
||||
RootedObject enclosingScope(cx, lazy->parent()->function());
|
||||
|
||||
ParseNode *pn = parser.standaloneLazyFunction(fun, lazy->parent()->staticLevel + 1,
|
||||
lazy->strict());
|
||||
if (!pn)
|
||||
return false;
|
||||
}
|
||||
|
||||
GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
|
||||
|
||||
ParseContext<SyntaxParseHandler> pc(&parser, NULL, &globalsc, 0, /* bodyid = */ 0);
|
||||
if (!pc.init()) {
|
||||
cx->clearPendingException();
|
||||
JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, lazy->source()));
|
||||
if (!sourceObject)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND);
|
||||
if (tt <= TOK_EOF) {
|
||||
if (tt == TOK_EOF)
|
||||
break;
|
||||
JS_ASSERT(tt == TOK_ERROR);
|
||||
cx->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false,
|
||||
options, lazy->parent()->staticLevel + 1,
|
||||
sourceObject, lazy->begin(), lazy->end()));
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
if (!parser.statement()) {
|
||||
cx->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
script->bindings = pn->pn_funbox->bindings;
|
||||
|
||||
return true;
|
||||
if (lazy->directlyInsideEval())
|
||||
script->directlyInsideEval = true;
|
||||
|
||||
bool hasGlobalScope = lazy->parent()->compileAndGo;
|
||||
|
||||
BytecodeEmitter bce(/* parent = */ NULL, &parser, pn->pn_funbox, script, options.forEval,
|
||||
/* evalCaller = */ NullPtr(), hasGlobalScope,
|
||||
options.lineno, BytecodeEmitter::LazyFunction);
|
||||
if (!bce.init())
|
||||
return false;
|
||||
|
||||
return EmitFunctionScript(cx, &bce, pn->pn_body);
|
||||
}
|
||||
|
||||
// Compile a JS function body, which might appear as the value of an event
|
||||
@ -319,6 +371,8 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length,
|
||||
bool isAsmJSRecompile)
|
||||
{
|
||||
SkipRoot skip(cx, &chars);
|
||||
|
||||
if (!CheckLength(cx, length))
|
||||
return false;
|
||||
ScriptSource *ss = cx->new_<ScriptSource>();
|
||||
@ -336,9 +390,17 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<Parser<SyntaxParseHandler> > syntaxParser;
|
||||
if (options.canLazilyParse) {
|
||||
syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
|
||||
(Parser<SyntaxParseHandler> *) NULL,
|
||||
(LazyScript *) NULL);
|
||||
}
|
||||
|
||||
JS_ASSERT(!options.forEval);
|
||||
|
||||
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
|
||||
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
|
||||
options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
|
||||
if (!parser.init())
|
||||
return false;
|
||||
parser.sct = &sct;
|
||||
@ -374,21 +436,29 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
|
||||
// directive, we backup and reparse it as strict.
|
||||
TokenStream::Position start(parser.keepAtoms);
|
||||
parser.tokenStream.tell(&start);
|
||||
bool initiallyStrict = StrictModeFromContext(cx);
|
||||
bool strict = StrictModeFromContext(cx);
|
||||
bool becameStrict;
|
||||
FunctionBox *funbox;
|
||||
ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
|
||||
initiallyStrict, &becameStrict);
|
||||
if (!pn) {
|
||||
if (initiallyStrict || !becameStrict || parser.tokenStream.hadError())
|
||||
return false;
|
||||
|
||||
// Reparse in strict mode.
|
||||
parser.tokenStream.seek(start);
|
||||
ParseNode *pn;
|
||||
while (true) {
|
||||
pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
|
||||
/* strict = */ true);
|
||||
if (!pn)
|
||||
return false;
|
||||
strict, &becameStrict);
|
||||
if (pn)
|
||||
break;
|
||||
|
||||
if (parser.hadAbortedSyntaxParse()) {
|
||||
// Hit some unrecoverable ambiguity during an inner syntax parse.
|
||||
// Syntax parsing has now been disabled in the parser, so retry
|
||||
// the parse.
|
||||
parser.clearAbortedSyntaxParse();
|
||||
} else {
|
||||
// If the function became strict, reparse in strict mode.
|
||||
if (strict || !becameStrict || parser.tokenStream.hadError())
|
||||
return false;
|
||||
strict = true;
|
||||
}
|
||||
|
||||
parser.tokenStream.seek(start);
|
||||
}
|
||||
|
||||
if (!NameFunctions(cx, pn))
|
||||
|
@ -19,8 +19,8 @@ CompileScript(JSContext *cx, HandleObject scopeChain, HandleScript evalCaller,
|
||||
SourceCompressionToken *extraSct = NULL);
|
||||
|
||||
bool
|
||||
ParseScript(JSContext *cx, HandleObject scopeChain,
|
||||
const CompileOptions &options, StableCharPtr chars, size_t length);
|
||||
CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
|
||||
const jschar *chars, size_t length);
|
||||
|
||||
bool
|
||||
CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
|
||||
|
@ -92,7 +92,7 @@ struct frontend::StmtInfoBCE : public StmtInfoBase
|
||||
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
|
||||
Parser<FullParseHandler> *parser, SharedContext *sc,
|
||||
HandleScript script, bool insideEval, HandleScript evalCaller,
|
||||
bool hasGlobalScope, uint32_t lineNum, bool selfHostingMode)
|
||||
bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode)
|
||||
: sc(sc),
|
||||
parent(parent),
|
||||
script(sc->context, script),
|
||||
@ -117,7 +117,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
|
||||
emittingRunOnceLambda(false),
|
||||
insideEval(insideEval),
|
||||
hasGlobalScope(hasGlobalScope),
|
||||
selfHostingMode(selfHostingMode)
|
||||
emitterMode(emitterMode)
|
||||
{
|
||||
JS_ASSERT_IF(evalCaller, insideEval);
|
||||
}
|
||||
@ -828,23 +828,24 @@ ClonedBlockDepth(BytecodeEmitter *bce)
|
||||
return clonedBlockDepth;
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
AliasedNameToSlot(HandleScript script, PropertyName *name)
|
||||
static bool
|
||||
LookupAliasedName(HandleScript script, PropertyName *name, uint16_t *pslot)
|
||||
{
|
||||
/*
|
||||
* Beware: BindingIter may contain more than one Binding for a given name
|
||||
* (in the case of |function f(x,x) {}|) but only one will be aliased.
|
||||
*/
|
||||
unsigned slot = CallObject::RESERVED_SLOTS;
|
||||
for (BindingIter bi(script); ; bi++) {
|
||||
for (BindingIter bi(script); !bi.done(); bi++) {
|
||||
if (bi->aliased()) {
|
||||
if (bi->name() == name)
|
||||
return slot;
|
||||
if (bi->name() == name) {
|
||||
*pslot = slot;
|
||||
return true;
|
||||
}
|
||||
slot++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -876,13 +877,13 @@ EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
|
||||
ScopeCoordinate sc;
|
||||
if (IsArgOp(pn->getOp())) {
|
||||
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
|
||||
sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name());
|
||||
JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
|
||||
} else {
|
||||
JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
|
||||
unsigned local = pn->pn_cookie.slot();
|
||||
if (local < bceOfDef->script->bindings.numVars()) {
|
||||
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
|
||||
sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name());
|
||||
JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
|
||||
} else {
|
||||
unsigned depth = local - bceOfDef->script->bindings.numVars();
|
||||
StaticBlockObject *b = bceOfDef->blockChain;
|
||||
@ -903,9 +904,17 @@ static bool
|
||||
EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_NAME));
|
||||
JS_ASSERT_IF(pn->isKind(PNK_NAME), IsArgOp(op) || IsLocalOp(op));
|
||||
JS_ASSERT(!pn->pn_cookie.isFree());
|
||||
|
||||
if (IsAliasedVarOp(op)) {
|
||||
ScopeCoordinate sc;
|
||||
sc.hops = pn->pn_cookie.level();
|
||||
sc.slot = pn->pn_cookie.slot();
|
||||
return EmitAliasedVarOp(cx, op, sc, bce);
|
||||
}
|
||||
|
||||
JS_ASSERT_IF(pn->isKind(PNK_NAME), IsArgOp(op) || IsLocalOp(op));
|
||||
|
||||
if (!bce->isAliasedName(pn)) {
|
||||
JS_ASSERT(pn->isUsed() || pn->isDefn());
|
||||
JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
|
||||
@ -936,14 +945,24 @@ static bool
|
||||
EmitVarIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
|
||||
{
|
||||
JSOp op = pn->pn_kid->getOp();
|
||||
JS_ASSERT(IsLocalOp(op) || IsArgOp(op));
|
||||
JS_ASSERT(IsArgOp(op) || IsLocalOp(op) || IsAliasedVarOp(op));
|
||||
JS_ASSERT(pn->pn_kid->isKind(PNK_NAME));
|
||||
JS_ASSERT(!pn->pn_kid->pn_cookie.isFree());
|
||||
|
||||
bool post;
|
||||
JSOp binop = GetIncDecInfo(pn->getKind(), &post);
|
||||
JSOp getOp = IsLocalOp(op) ? JSOP_GETLOCAL : JSOP_GETARG;
|
||||
JSOp setOp = IsLocalOp(op) ? JSOP_SETLOCAL : JSOP_SETARG;
|
||||
|
||||
JSOp getOp, setOp;
|
||||
if (IsLocalOp(op)) {
|
||||
getOp = JSOP_GETLOCAL;
|
||||
setOp = JSOP_SETLOCAL;
|
||||
} else if (IsArgOp(op)) {
|
||||
getOp = JSOP_GETARG;
|
||||
setOp = JSOP_SETARG;
|
||||
} else {
|
||||
getOp = JSOP_GETALIASEDVAR;
|
||||
setOp = JSOP_SETALIASEDVAR;
|
||||
}
|
||||
|
||||
if (!EmitVarOp(cx, pn->pn_kid, getOp, bce)) // V
|
||||
return false;
|
||||
@ -1077,42 +1096,101 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
|
||||
* globals. Return true if a conversion was made.
|
||||
*
|
||||
* Don't convert to *GNAME ops within strict-mode eval, since access
|
||||
* to a "global" might merely be to a binding local to that eval:
|
||||
*
|
||||
* "use strict";
|
||||
* var x = "global";
|
||||
* eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
|
||||
*
|
||||
* Outside eval code, access to an undeclared global is a strict mode error:
|
||||
*
|
||||
* "use strict";
|
||||
* function foo()
|
||||
* {
|
||||
* undeclared = 17; // throws ReferenceError
|
||||
* }
|
||||
* foo();
|
||||
*
|
||||
* In self-hosting mode, JSOP_*NAME is unconditionally converted to
|
||||
* JSOP_*INTRINSIC. This causes lookups to be redirected to the special
|
||||
* intrinsics holder in the global object, into which any missing values are
|
||||
* cloned lazily upon first access.
|
||||
* Try to convert a *NAME op with a free name to a more specialized GNAME,
|
||||
* INTRINSIC or ALIASEDVAR op, which optimize accesses on that name.
|
||||
* Return true if a conversion was made.
|
||||
*/
|
||||
static bool
|
||||
TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
|
||||
TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
if (bce->selfHostingMode) {
|
||||
switch (*op) {
|
||||
case JSOP_NAME: *op = JSOP_GETINTRINSIC; break;
|
||||
case JSOP_SETNAME: *op = JSOP_SETINTRINSIC; break;
|
||||
/*
|
||||
* In self-hosting mode, JSOP_*NAME is unconditionally converted to
|
||||
* JSOP_*INTRINSIC. This causes lookups to be redirected to the special
|
||||
* intrinsics holder in the global object, into which any missing values are
|
||||
* cloned lazily upon first access.
|
||||
*/
|
||||
if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
|
||||
JSOp op;
|
||||
switch (pn->getOp()) {
|
||||
case JSOP_NAME: op = JSOP_GETINTRINSIC; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETINTRINSIC; break;
|
||||
/* Other *NAME ops aren't (yet) supported in self-hosted code. */
|
||||
default: JS_NOT_REACHED("intrinsic");
|
||||
}
|
||||
pn->setOp(op);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* When parsing inner functions lazily, parse nodes for outer functions no
|
||||
* longer exist and only the function's scope chain is available for
|
||||
* resolving upvar accesses within the inner function.
|
||||
*/
|
||||
if (bce->emitterMode == BytecodeEmitter::LazyFunction) {
|
||||
size_t hops = 0;
|
||||
FunctionBox *funbox = bce->sc->asFunctionBox();
|
||||
if (funbox->hasExtensibleScope())
|
||||
return false;
|
||||
if (funbox->function()->atom() == pn->pn_atom)
|
||||
return false;
|
||||
if (funbox->function()->isHeavyweight()) {
|
||||
hops++;
|
||||
if (funbox->function()->isNamedLambda())
|
||||
hops++;
|
||||
}
|
||||
if (bce->script->directlyInsideEval)
|
||||
return false;
|
||||
RootedObject outerScope(bce->sc->context, bce->script->enclosingStaticScope());
|
||||
for (StaticScopeIter ssi(bce->sc->context, outerScope); !ssi.done(); ssi++) {
|
||||
if (ssi.type() != StaticScopeIter::FUNCTION) {
|
||||
if (ssi.hasDynamicScopeObject())
|
||||
hops++;
|
||||
continue;
|
||||
}
|
||||
RootedScript script(bce->sc->context, ssi.funScript());
|
||||
if (script->function()->atom() == pn->pn_atom)
|
||||
return false;
|
||||
if (ssi.hasDynamicScopeObject()) {
|
||||
uint16_t slot;
|
||||
if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) {
|
||||
JSOp op;
|
||||
switch (pn->getOp()) {
|
||||
case JSOP_NAME: op = JSOP_GETALIASEDVAR; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break;
|
||||
default: return false;
|
||||
}
|
||||
pn->setOp(op);
|
||||
JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->sc->context, hops, slot));
|
||||
return true;
|
||||
}
|
||||
hops++;
|
||||
}
|
||||
|
||||
if (script->funHasExtensibleScope || script->directlyInsideEval)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to convert free names in global scope to GNAME opcodes.
|
||||
*
|
||||
* This conversion is not made if we are in strict mode. In eval code nested
|
||||
* within (strict mode) eval code, access to an undeclared "global" might
|
||||
* merely be to a binding local to that outer eval:
|
||||
*
|
||||
* "use strict";
|
||||
* var x = "global";
|
||||
* eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
|
||||
*
|
||||
* Outside eval code, access to an undeclared global is a strict mode error:
|
||||
*
|
||||
* "use strict";
|
||||
* function foo()
|
||||
* {
|
||||
* undeclared = 17; // throws ReferenceError
|
||||
* }
|
||||
* foo();
|
||||
*/
|
||||
if (bce->script->compileAndGo &&
|
||||
bce->hasGlobalScope &&
|
||||
!(bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->mightAliasLocals()) &&
|
||||
@ -1121,16 +1199,19 @@ TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
|
||||
{
|
||||
// If you change anything here, you might also need to change
|
||||
// js::ReportIfUndeclaredVarAssignment.
|
||||
switch (*op) {
|
||||
case JSOP_NAME: *op = JSOP_GETGNAME; break;
|
||||
case JSOP_SETNAME: *op = JSOP_SETGNAME; break;
|
||||
JSOp op;
|
||||
switch (pn->getOp()) {
|
||||
case JSOP_NAME: op = JSOP_GETGNAME; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETGNAME; break;
|
||||
case JSOP_SETCONST:
|
||||
/* Not supported. */
|
||||
return false;
|
||||
default: JS_NOT_REACHED("gname");
|
||||
}
|
||||
pn->setOp(op);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1225,8 +1306,7 @@ BindNameToSlotHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
* If this is an eval in the global scope, then unbound variables
|
||||
* must be globals, so try to use GNAME ops.
|
||||
*/
|
||||
if (!caller->functionOrCallerFunction() && TryConvertToGname(bce, pn, &op)) {
|
||||
pn->setOp(op);
|
||||
if (!caller->functionOrCallerFunction() && TryConvertFreeName(bce, pn)) {
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
return true;
|
||||
}
|
||||
@ -1239,12 +1319,10 @@ BindNameToSlotHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
}
|
||||
|
||||
/* Optimize accesses to undeclared globals. */
|
||||
if (!TryConvertToGname(bce, pn, &op))
|
||||
if (!TryConvertFreeName(bce, pn))
|
||||
return true;
|
||||
|
||||
pn->setOp(op);
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1394,7 +1472,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
if (!BindNameToSlotHelper(cx, bce, pn))
|
||||
return false;
|
||||
|
||||
if (bce->selfHostingMode && !pn->isBound()) {
|
||||
if (bce->emitterMode == BytecodeEmitter::SelfHosting && !pn->isBound()) {
|
||||
bce->reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME);
|
||||
return false;
|
||||
}
|
||||
@ -1721,6 +1799,9 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
|
||||
case JSOP_GETLOCAL:
|
||||
op = JSOP_CALLLOCAL;
|
||||
break;
|
||||
case JSOP_GETALIASEDVAR:
|
||||
op = JSOP_CALLALIASEDVAR;
|
||||
break;
|
||||
default:
|
||||
JS_ASSERT(op == JSOP_CALLEE);
|
||||
break;
|
||||
@ -2459,7 +2540,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
if (bce->script->varIsAliased(varIndex)) {
|
||||
ScopeCoordinate sc;
|
||||
sc.hops = 0;
|
||||
sc.slot = AliasedNameToSlot(bce->script, cx->names().arguments);
|
||||
JS_ALWAYS_TRUE(LookupAliasedName(bce->script, cx->names().arguments, &sc.slot));
|
||||
if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, bce))
|
||||
return false;
|
||||
} else {
|
||||
@ -2498,28 +2579,10 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
if (bce->parent && bce->parent->emittingRunOnceLambda)
|
||||
bce->script->treatAsRunOnce = true;
|
||||
|
||||
/*
|
||||
* Mark as singletons any function which will only be executed once, or
|
||||
* which is inner to a lambda we only expect to run once. In the latter
|
||||
* case, if the lambda runs multiple times then CloneFunctionObject will
|
||||
* make a deep clone of its contents.
|
||||
*/
|
||||
bool singleton =
|
||||
cx->typeInferenceEnabled() &&
|
||||
bce->script->compileAndGo &&
|
||||
bce->parent &&
|
||||
(bce->parent->checkSingletonContext() ||
|
||||
(!bce->parent->isInLoop() &&
|
||||
bce->parent->parent &&
|
||||
bce->parent->parent->emittingRunOnceLambda));
|
||||
|
||||
/* Initialize fun->script() so that the debugger has a valid fun->script(). */
|
||||
RootedFunction fun(cx, bce->script->function());
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->hasScript());
|
||||
fun->setScript(bce->script);
|
||||
if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
|
||||
return false;
|
||||
|
||||
bce->tellDebuggerAboutCompiledScript(cx);
|
||||
|
||||
@ -3256,7 +3319,13 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
|
||||
if (!EmitAtomOp(cx, lhs, JSOP_GETINTRINSIC, bce))
|
||||
return false;
|
||||
} else {
|
||||
JSOp op = lhs->isOp(JSOP_SETARG) ? JSOP_GETARG : JSOP_GETLOCAL;
|
||||
JSOp op;
|
||||
switch (lhs->getOp()) {
|
||||
case JSOP_SETARG: op = JSOP_GETARG; break;
|
||||
case JSOP_SETLOCAL: op = JSOP_GETLOCAL; break;
|
||||
case JSOP_SETALIASEDVAR: op = JSOP_GETALIASEDVAR; break;
|
||||
default: JS_NOT_REACHED("Bad op");
|
||||
}
|
||||
if (!EmitVarOp(cx, lhs, op, bce))
|
||||
return false;
|
||||
}
|
||||
@ -3320,7 +3389,7 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL)) {
|
||||
if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL) || lhs->isOp(JSOP_SETALIASEDVAR)) {
|
||||
if (!EmitVarOp(cx, lhs, lhs->getOp(), bce))
|
||||
return false;
|
||||
} else {
|
||||
@ -4333,20 +4402,49 @@ EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
static JS_NEVER_INLINE bool
|
||||
EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
RootedFunction fun(cx, pn->pn_funbox->function());
|
||||
FunctionBox *funbox = pn->pn_funbox;
|
||||
RootedFunction fun(cx, funbox->function());
|
||||
if (fun->isNative()) {
|
||||
JS_ASSERT(IsAsmJSModuleNative(fun->native()));
|
||||
return true;
|
||||
}
|
||||
if (fun->hasScript()) {
|
||||
JS_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
|
||||
|
||||
/*
|
||||
* Set the EMITTEDFUNCTION flag in function definitions once they have been
|
||||
* emitted. Function definitions that need hoisting to the top of the
|
||||
* function will be seen by EmitFunc in two places.
|
||||
*/
|
||||
if (pn->pn_dflags & PND_EMITTEDFUNCTION) {
|
||||
JS_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
|
||||
JS_ASSERT(pn->functionIsHoisted());
|
||||
JS_ASSERT(bce->sc->isFunctionBox());
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
pn->pn_dflags |= PND_EMITTEDFUNCTION;
|
||||
|
||||
/*
|
||||
* Mark as singletons any function which will only be executed once, or
|
||||
* which is inner to a lambda we only expect to run once. In the latter
|
||||
* case, if the lambda runs multiple times then CloneFunctionObject will
|
||||
* make a deep clone of its contents.
|
||||
*/
|
||||
bool singleton =
|
||||
cx->typeInferenceEnabled() &&
|
||||
bce->script->compileAndGo &&
|
||||
(bce->checkSingletonContext() ||
|
||||
(!bce->isInLoop() &&
|
||||
bce->parent &&
|
||||
bce->parent->emittingRunOnceLambda));
|
||||
if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
|
||||
return false;
|
||||
|
||||
if (fun->isInterpretedLazy()) {
|
||||
if (!fun->lazyScript()->parent())
|
||||
fun->lazyScript()->initParent(bce->script);
|
||||
} else {
|
||||
SharedContext *outersc = bce->sc;
|
||||
FunctionBox *funbox = pn->pn_funbox;
|
||||
|
||||
if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
|
||||
funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent
|
||||
@ -4402,7 +4500,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
|
||||
BytecodeEmitter bce2(bce, bce->parser, funbox, script, bce->insideEval,
|
||||
bce->evalCaller, bce->hasGlobalScope, lineNum,
|
||||
bce->selfHostingMode);
|
||||
bce->emitterMode);
|
||||
if (!bce2.init())
|
||||
return false;
|
||||
|
||||
@ -4841,7 +4939,8 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
ParseNode *pn2 = pn->pn_head;
|
||||
switch (pn2->getKind()) {
|
||||
case PNK_NAME:
|
||||
if (bce->selfHostingMode && pn2->name() == cx->names().callFunction)
|
||||
if (bce->emitterMode == BytecodeEmitter::SelfHosting &&
|
||||
pn2->name() == cx->names().callFunction)
|
||||
{
|
||||
/*
|
||||
* Special-casing of callFunction to emit bytecode that directly
|
||||
@ -5065,6 +5164,7 @@ EmitIncOrDec(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
switch (op) {
|
||||
case JSOP_SETLOCAL:
|
||||
case JSOP_SETARG:
|
||||
case JSOP_SETALIASEDVAR:
|
||||
case JSOP_SETNAME:
|
||||
case JSOP_SETGNAME:
|
||||
maySet = true;
|
||||
|
@ -130,10 +130,24 @@ struct BytecodeEmitter
|
||||
const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the
|
||||
global object */
|
||||
|
||||
const bool selfHostingMode:1; /* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME
|
||||
and assert that JSOP_NAME and JSOP_*GNAME
|
||||
don't ever get emitted. See the comment for
|
||||
the field |selfHostingMode| in Parser.h for details. */
|
||||
enum EmitterMode {
|
||||
Normal,
|
||||
|
||||
/*
|
||||
* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME and assert that
|
||||
* JSOP_NAME and JSOP_*GNAME don't ever get emitted. See the comment
|
||||
* for the field |selfHostingMode| in Parser.h for details.
|
||||
*/
|
||||
SelfHosting,
|
||||
|
||||
/*
|
||||
* Check the static scope chain of the root function for resolving free
|
||||
* variable accesses in the script.
|
||||
*/
|
||||
LazyFunction
|
||||
};
|
||||
|
||||
const EmitterMode emitterMode;
|
||||
|
||||
/*
|
||||
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
|
||||
@ -143,7 +157,7 @@ struct BytecodeEmitter
|
||||
*/
|
||||
BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
|
||||
HandleScript script, bool insideEval, HandleScript evalCaller,
|
||||
bool hasGlobalScope, uint32_t lineNum, bool selfHostingMode = false);
|
||||
bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode = Normal);
|
||||
bool init();
|
||||
|
||||
bool isAliasedName(ParseNode *pn);
|
||||
|
@ -272,9 +272,12 @@ FoldConstants<FullParseHandler>(JSContext *cx, ParseNode **pnp,
|
||||
if (!FoldConstants(cx, &pn->pn_body, parser))
|
||||
return false;
|
||||
} else {
|
||||
// Note: pn_body is NULL for functions which are being lazily parsed.
|
||||
JS_ASSERT(pn->getKind() == PNK_FUNCTION);
|
||||
if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
|
||||
return false;
|
||||
if (pn->pn_body) {
|
||||
if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -15,6 +15,13 @@
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
template <typename ParseHandler>
|
||||
class Parser;
|
||||
|
||||
class SyntaxParseHandler;
|
||||
|
||||
// Parse handler used when generating a full parse tree for all code which the
|
||||
// parser encounters.
|
||||
class FullParseHandler
|
||||
{
|
||||
ParseNodeAllocator allocator;
|
||||
@ -34,17 +41,38 @@ class FullParseHandler
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a full parse to construct the bytecode for a function that
|
||||
* was previously lazily parsed, that lazy function and the current index
|
||||
* into its inner functions. We do not want to reparse the inner functions.
|
||||
*/
|
||||
LazyScript * const lazyOuterFunction_;
|
||||
size_t lazyInnerFunctionIndex;
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
* If non-NULL, points to a syntax parser which can be used for inner
|
||||
* functions. Cleared if language features not handled by the syntax parser
|
||||
* are encountered, in which case all future activity will use the full
|
||||
* parser.
|
||||
*/
|
||||
Parser<SyntaxParseHandler> *syntaxParser;
|
||||
|
||||
/* new_ methods for creating parse nodes. These report OOM on context. */
|
||||
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
|
||||
|
||||
typedef ParseNode *Node;
|
||||
typedef Definition *DefinitionNode;
|
||||
|
||||
FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
|
||||
FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
|
||||
Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
|
||||
: allocator(cx),
|
||||
tokenStream(tokenStream),
|
||||
foldConstants(foldConstants)
|
||||
foldConstants(foldConstants),
|
||||
lazyOuterFunction_(lazyOuterFunction),
|
||||
lazyInnerFunctionIndex(0),
|
||||
syntaxParser(syntaxParser)
|
||||
{}
|
||||
|
||||
static ParseNode *null() { return NULL; }
|
||||
@ -61,8 +89,8 @@ class FullParseHandler
|
||||
pn->setOp(JSOP_NAME);
|
||||
return pn;
|
||||
}
|
||||
Definition *newPlaceholder(ParseNode *pn, ParseContext<FullParseHandler> *pc) {
|
||||
Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, this, pc);
|
||||
Definition *newPlaceholder(JSAtom *atom, ParseContext<FullParseHandler> *pc) {
|
||||
Definition *dn = (Definition *) NameNode::create(PNK_NAME, atom, this, pc);
|
||||
if (!dn)
|
||||
return NULL;
|
||||
|
||||
@ -313,6 +341,17 @@ class FullParseHandler
|
||||
static Definition *nullDefinition() {
|
||||
return NULL;
|
||||
}
|
||||
void disableSyntaxParser() {
|
||||
syntaxParser = NULL;
|
||||
}
|
||||
|
||||
LazyScript *lazyOuterFunction() {
|
||||
return lazyOuterFunction_;
|
||||
}
|
||||
JSFunction *nextLazyInnerFunction() {
|
||||
JS_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions());
|
||||
return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++];
|
||||
}
|
||||
};
|
||||
|
||||
inline bool
|
||||
|
@ -632,6 +632,7 @@ struct ParseNode {
|
||||
'arguments' that has been converted
|
||||
into a definition after the function
|
||||
body has been parsed. */
|
||||
#define PND_EMITTEDFUNCTION 0x400 /* hoisted function that was emitted */
|
||||
|
||||
/* Flags to propagate from uses to definition. */
|
||||
#define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
|
||||
|
@ -62,6 +62,7 @@ ParseContext<ParseHandler>::ParseContext(Parser<ParseHandler> *prs,
|
||||
oldpc(prs->pc),
|
||||
lexdeps(prs->context),
|
||||
funcStmts(NULL),
|
||||
innerFunctions(prs->context),
|
||||
inDeclDestructuring(false),
|
||||
funBecameStrict(false)
|
||||
{
|
||||
|
@ -59,6 +59,7 @@
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using mozilla::Maybe;
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
@ -213,6 +214,10 @@ ParseContext<SyntaxParseHandler>::define(JSContext *cx, HandlePropertyName name,
|
||||
if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
|
||||
lexdeps->remove(name);
|
||||
|
||||
// Keep track of the number of arguments in args_, for fun->nargs.
|
||||
if (kind == Definition::ARG && !args_.append((Definition *) NULL))
|
||||
return false;
|
||||
|
||||
return decls_.addUnique(name, kind);
|
||||
}
|
||||
|
||||
@ -378,9 +383,27 @@ Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::abortIfSyntaxParser()
|
||||
{
|
||||
handler.disableSyntaxParser();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::abortIfSyntaxParser()
|
||||
{
|
||||
abortedSyntaxParse = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
|
||||
const jschar *chars, size_t length, bool foldConstants)
|
||||
const jschar *chars, size_t length, bool foldConstants,
|
||||
Parser<SyntaxParseHandler> *syntaxParser,
|
||||
LazyScript *lazyOuterFunction)
|
||||
: AutoGCRooter(cx, PARSER),
|
||||
context(cx),
|
||||
tokenStream(cx, options, chars, length, thisForCtor(), keepAtoms),
|
||||
@ -391,10 +414,19 @@ Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
|
||||
foldConstants(foldConstants),
|
||||
compileAndGo(options.compileAndGo),
|
||||
selfHostingMode(options.selfHostingMode),
|
||||
unknownResult(false),
|
||||
handler(cx, tokenStream, foldConstants)
|
||||
abortedSyntaxParse(false),
|
||||
handler(cx, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
|
||||
{
|
||||
// XXX bug 678037 always disable syntax parsing for now.
|
||||
handler.disableSyntaxParser();
|
||||
|
||||
cx->activeCompilations++;
|
||||
|
||||
// The Mozilla specific 'strict' option adds extra warnings which are not
|
||||
// generated if functions are parsed lazily. Note that the standard
|
||||
// "use strict" does not inhibit lazy parsing.
|
||||
if (context->hasStrictOption())
|
||||
handler.disableSyntaxParser();
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
@ -984,6 +1016,18 @@ template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::checkFunctionArguments()
|
||||
{
|
||||
if (pc->sc->asFunctionBox()->function()->hasRest()) {
|
||||
if (pc->lexdeps->lookup(context->names().arguments)) {
|
||||
report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
|
||||
return false;
|
||||
}
|
||||
DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments);
|
||||
if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) {
|
||||
report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1223,6 +1267,49 @@ MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::DefinitionNode
|
||||
Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
|
||||
{
|
||||
AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
|
||||
if (p)
|
||||
return p.value().get<ParseHandler>();
|
||||
|
||||
DefinitionNode dn = handler.newPlaceholder(atom, pc);
|
||||
if (!dn)
|
||||
return ParseHandler::nullDefinition();
|
||||
DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
|
||||
if (!pc->lexdeps->add(p, atom, def))
|
||||
return ParseHandler::nullDefinition();
|
||||
return dn;
|
||||
}
|
||||
|
||||
static bool
|
||||
ConvertDefinitionToNamedLambdaUse(JSContext *cx, ParseContext<FullParseHandler> *pc,
|
||||
FunctionBox *funbox, Definition *dn)
|
||||
{
|
||||
dn->setOp(JSOP_CALLEE);
|
||||
if (!dn->pn_cookie.set(cx, pc->staticLevel, UpvarCookie::CALLEE_SLOT))
|
||||
return false;
|
||||
dn->pn_dflags |= PND_BOUND;
|
||||
JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
|
||||
|
||||
/*
|
||||
* Since 'dn' is a placeholder, it has not been defined in the
|
||||
* ParseContext and hence we must manually flag a closed-over
|
||||
* callee name as needing a dynamic scope (this is done for all
|
||||
* definitions in the ParseContext by generateFunctionBindings).
|
||||
*
|
||||
* If 'dn' has been assigned to, then we also flag the function
|
||||
* scope has needing a dynamic scope so that dynamic scope
|
||||
* setter can either ignore the set (in non-strict mode) or
|
||||
* produce an error (in strict mode).
|
||||
*/
|
||||
if (dn->isClosed() || dn->isAssigned())
|
||||
funbox->function()->setIsHeavyweight();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Beware: this function is called for functions nested in other functions or
|
||||
* global scripts but not for functions compiled through the Function
|
||||
@ -1251,26 +1338,8 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
|
||||
JS_ASSERT(dn->isPlaceholder());
|
||||
|
||||
if (atom == funName && kind == Expression) {
|
||||
dn->setOp(JSOP_CALLEE);
|
||||
if (!dn->pn_cookie.set(context, pc->staticLevel,
|
||||
UpvarCookie::CALLEE_SLOT))
|
||||
if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
|
||||
return false;
|
||||
dn->pn_dflags |= PND_BOUND;
|
||||
JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
|
||||
|
||||
/*
|
||||
* Since 'dn' is a placeholder, it has not been defined in the
|
||||
* ParseContext and hence we must manually flag a closed-over
|
||||
* callee name as needing a dynamic scope (this is done for all
|
||||
* definitions in the ParseContext by generateFunctionBindings).
|
||||
*
|
||||
* If 'dn' has been assigned to, then we also flag the function
|
||||
* scope has needing a dynamic scope so that dynamic scope
|
||||
* setter can either ignore the set (in non-strict mode) or
|
||||
* produce an error (in strict mode).
|
||||
*/
|
||||
if (dn->isClosed() || dn->isAssigned())
|
||||
funbox->function()->setIsHeavyweight();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1293,38 +1362,30 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
|
||||
handler.deoptimizeUsesWithin(dn, fn->pn_pos);
|
||||
|
||||
if (!outer_dn) {
|
||||
AtomDefnAddPtr p = outerpc->lexdeps->lookupForAdd(atom);
|
||||
if (p) {
|
||||
outer_dn = p.value().get<FullParseHandler>();
|
||||
} else {
|
||||
/*
|
||||
* Create a new placeholder for our outer lexdep. We could
|
||||
* simply re-use the inner placeholder, but that introduces
|
||||
* subtleties in the case where we find a later definition
|
||||
* that captures an existing lexdep. For example:
|
||||
*
|
||||
* function f() { function g() { x; } let x; }
|
||||
*
|
||||
* Here, g's TOK_UPVARS node lists the placeholder for x,
|
||||
* which must be captured by the 'let' declaration later,
|
||||
* since 'let's are hoisted. Taking g's placeholder as our
|
||||
* own would work fine. But consider:
|
||||
*
|
||||
* function f() { x; { function g() { x; } let x; } }
|
||||
*
|
||||
* Here, the 'let' must not capture all the uses of f's
|
||||
* lexdep entry for x, but it must capture the x node
|
||||
* referred to from g's TOK_UPVARS node. Always turning
|
||||
* inherited lexdeps into uses of a new outer definition
|
||||
* allows us to handle both these cases in a natural way.
|
||||
*/
|
||||
outer_dn = handler.newPlaceholder(dn, outerpc);
|
||||
if (!outer_dn)
|
||||
return false;
|
||||
DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(outer_dn);
|
||||
if (!outerpc->lexdeps->add(p, atom, def))
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Create a new placeholder for our outer lexdep. We could
|
||||
* simply re-use the inner placeholder, but that introduces
|
||||
* subtleties in the case where we find a later definition
|
||||
* that captures an existing lexdep. For example:
|
||||
*
|
||||
* function f() { function g() { x; } let x; }
|
||||
*
|
||||
* Here, g's TOK_UPVARS node lists the placeholder for x,
|
||||
* which must be captured by the 'let' declaration later,
|
||||
* since 'let's are hoisted. Taking g's placeholder as our
|
||||
* own would work fine. But consider:
|
||||
*
|
||||
* function f() { x; { function g() { x; } let x; } }
|
||||
*
|
||||
* Here, the 'let' must not capture all the uses of f's
|
||||
* lexdep entry for x, but it must capture the x node
|
||||
* referred to from g's TOK_UPVARS node. Always turning
|
||||
* inherited lexdeps into uses of a new outer definition
|
||||
* allows us to handle both these cases in a natural way.
|
||||
*/
|
||||
outer_dn = getOrCreateLexicalDependency(outerpc, atom);
|
||||
if (!outer_dn)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1363,11 +1424,7 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
|
||||
|
||||
InternalHandle<Bindings*> bindings =
|
||||
InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
|
||||
if (!pc->generateFunctionBindings(context, bindings))
|
||||
return false;
|
||||
|
||||
pc->lexdeps.releaseMap(context);
|
||||
return true;
|
||||
return pc->generateFunctionBindings(context, bindings);
|
||||
}
|
||||
|
||||
template <>
|
||||
@ -1376,8 +1433,10 @@ Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
|
||||
ParseContext<SyntaxParseHandler> *outerpc,
|
||||
FunctionSyntaxKind kind)
|
||||
{
|
||||
pc->lexdeps.releaseMap(context);
|
||||
return true;
|
||||
outerpc->blockidGen = pc->blockidGen;
|
||||
|
||||
FunctionBox *funbox = pc->sc->asFunctionBox();
|
||||
return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1486,7 +1545,7 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
||||
|
||||
// Record the start of function source (for FunctionToString). If we
|
||||
// are parenFreeArrow, we will set this below, after consuming the NAME.
|
||||
funbox->bufStart = tokenStream.currentToken().pos.begin;
|
||||
funbox->setStart(tokenStream);
|
||||
}
|
||||
|
||||
hasRest = false;
|
||||
@ -1587,7 +1646,7 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
||||
case TOK_NAME:
|
||||
{
|
||||
if (parenFreeArrow)
|
||||
funbox->bufStart = tokenStream.currentToken().pos.begin;
|
||||
funbox->setStart(tokenStream);
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentToken().name());
|
||||
bool disallowDuplicateArgs = destructuringArg || hasDefaults;
|
||||
@ -1643,9 +1702,11 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
ParseNode **pn_, FunctionSyntaxKind kind)
|
||||
ParseNode **pn_, FunctionSyntaxKind kind,
|
||||
bool *pbodyProcessed)
|
||||
{
|
||||
ParseNode *&pn = *pn_;
|
||||
*pbodyProcessed = false;
|
||||
|
||||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
@ -1712,7 +1773,6 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
* statements (e.g., functions in an "if" or "while" block) are
|
||||
* dynamically bound when control flow reaches the statement. The
|
||||
* emitter normally emits functions in two passes (see PNK_ARGSBODY).
|
||||
* To distinguish
|
||||
*/
|
||||
if (bodyLevel) {
|
||||
JS_ASSERT(pn->functionIsHoisted());
|
||||
@ -1759,14 +1819,86 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
pn->setOp(JSOP_LAMBDA);
|
||||
}
|
||||
|
||||
// When a lazily-parsed function is called, we only fully parse (and emit)
|
||||
// that function, not any of its nested children. The initial syntax-only
|
||||
// parse recorded the free variables of nested functions and their extents,
|
||||
// so we can skip over them after accounting for their free variables.
|
||||
if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
|
||||
JSFunction *fun = handler.nextLazyInnerFunction();
|
||||
FunctionBox *funbox = newFunctionBox(fun, pc, /* strict = */ false);
|
||||
if (!funbox)
|
||||
return false;
|
||||
handler.setFunctionBox(pn, funbox);
|
||||
|
||||
if (!addFreeVariablesFromLazyFunction(fun, pc))
|
||||
return false;
|
||||
|
||||
// The position passed to tokenStream.advance() is relative to
|
||||
// userbuf.base() while LazyScript::{begin,end} offsets are relative to
|
||||
// the outermost script source. N.B: userbuf.base() is initialized
|
||||
// (in TokenStream()) to begin() - column() so that column numbers in
|
||||
// the lazily parsed script are correct.
|
||||
uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
|
||||
tokenStream.advance(fun->lazyScript()->end() - userbufBase);
|
||||
|
||||
*pbodyProcessed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
static inline void
|
||||
PropagateTransitiveParseFlags(const T *inner, U *outer)
|
||||
{
|
||||
if (inner->bindingsAccessedDynamically())
|
||||
outer->setBindingsAccessedDynamically();
|
||||
if (inner->hasDebuggerStatement())
|
||||
outer->setHasDebuggerStatement();
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun,
|
||||
ParseContext<ParseHandler> *pc)
|
||||
{
|
||||
// Update any definition nodes in this context according to free variables
|
||||
// in a lazily parsed inner function.
|
||||
|
||||
LazyScript *lazy = fun->lazyScript();
|
||||
HeapPtrAtom *freeVariables = lazy->freeVariables();
|
||||
for (size_t i = 0; i < lazy->numFreeVariables(); i++) {
|
||||
JSAtom *atom = freeVariables[i];
|
||||
|
||||
// 'arguments' will be implicitly bound within the inner function.
|
||||
if (atom == context->names().arguments)
|
||||
continue;
|
||||
|
||||
DefinitionNode dn = pc->decls().lookupFirst(atom);
|
||||
|
||||
if (!dn) {
|
||||
dn = getOrCreateLexicalDependency(pc, atom);
|
||||
if (!dn)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Mark the outer dn as escaping. */
|
||||
handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED);
|
||||
}
|
||||
|
||||
PropagateTransitiveParseFlags(lazy, pc->sc);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
Node *pn, FunctionSyntaxKind kind)
|
||||
Node *pn, FunctionSyntaxKind kind,
|
||||
bool *pbodyProcessed)
|
||||
{
|
||||
*pbodyProcessed = false;
|
||||
|
||||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
@ -1797,6 +1929,11 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
}
|
||||
|
||||
if (kind == Arrow) {
|
||||
/* Arrow functions cannot yet be parsed lazily. */
|
||||
return abortIfSyntaxParser();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1812,9 +1949,13 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
if (!checkFunctionDefinition(funName, &pn, kind))
|
||||
bool bodyProcessed;
|
||||
if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
|
||||
return null();
|
||||
|
||||
if (bodyProcessed)
|
||||
return pn;
|
||||
|
||||
RootedFunction fun(context, newFunction(pc, funName, kind));
|
||||
if (!fun)
|
||||
return null();
|
||||
@ -1825,8 +1966,8 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
||||
handler.setFunctionBody(pn, null());
|
||||
bool initiallyStrict = kind == Arrow || pc->sc->strict;
|
||||
bool becameStrict;
|
||||
if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, initiallyStrict,
|
||||
&becameStrict))
|
||||
if (!functionArgsAndBody(pn, fun, funName, startOffset,
|
||||
type, kind, initiallyStrict, &becameStrict))
|
||||
{
|
||||
if (initiallyStrict || !becameStrict || tokenStream.hadError())
|
||||
return null();
|
||||
@ -1846,22 +1987,10 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
|
||||
ParseNode *prelude, ParseNode *body,
|
||||
ParseContext<FullParseHandler> *outerpc)
|
||||
ParseNode *prelude, ParseNode *body)
|
||||
{
|
||||
pn->pn_pos.end = tokenStream.currentToken().pos.end;
|
||||
|
||||
/*
|
||||
* Fruit of the poisonous tree: if a closure contains a dynamic name access
|
||||
* (eval, with, etc), we consider the parent to do the same. The reason is
|
||||
* that the deoptimizing effects of dynamic name access apply equally to
|
||||
* parents: any local can be read at runtime.
|
||||
*/
|
||||
if (funbox->bindingsAccessedDynamically())
|
||||
outerpc->sc->setBindingsAccessedDynamically();
|
||||
if (funbox->hasDebuggerStatement())
|
||||
outerpc->sc->setHasDebuggerStatement();
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
* If there were destructuring formal parameters, prepend the initializing
|
||||
@ -1900,7 +2029,6 @@ Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *f
|
||||
pn->pn_funbox = funbox;
|
||||
pn->pn_body->append(body);
|
||||
pn->pn_body->pn_pos = body->pn_pos;
|
||||
pn->pn_blockid = outerpc->blockid();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1908,21 +2036,144 @@ Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *f
|
||||
template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox,
|
||||
Node prelude, Node body,
|
||||
ParseContext<SyntaxParseHandler> *outerpc)
|
||||
Node prelude, Node body)
|
||||
{
|
||||
// The LazyScript for a lazily parsed function needs to be constructed
|
||||
// while its ParseContext and associated lexdeps and inner functions are
|
||||
// still available.
|
||||
|
||||
if (funbox->inWith)
|
||||
return abortIfSyntaxParser();
|
||||
|
||||
size_t numFreeVariables = pc->lexdeps->count();
|
||||
size_t numInnerFunctions = pc->innerFunctions.length();
|
||||
|
||||
LazyScript *lazy = LazyScript::Create(context, numFreeVariables, numInnerFunctions,
|
||||
funbox->bufStart, funbox->bufEnd,
|
||||
funbox->startLine, funbox->startColumn);
|
||||
if (!lazy)
|
||||
return false;
|
||||
|
||||
HeapPtrAtom *freeVariables = lazy->freeVariables();
|
||||
size_t i = 0;
|
||||
for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
|
||||
freeVariables[i++].init(r.front().key());
|
||||
JS_ASSERT(i == numFreeVariables);
|
||||
|
||||
HeapPtrFunction *innerFunctions = lazy->innerFunctions();
|
||||
for (size_t i = 0; i < numInnerFunctions; i++)
|
||||
innerFunctions[i].init(pc->innerFunctions[i]);
|
||||
|
||||
if (pc->sc->strict)
|
||||
lazy->setStrict();
|
||||
PropagateTransitiveParseFlags(funbox, lazy);
|
||||
|
||||
funbox->object->toFunction()->initLazyScript(lazy);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
template <>
|
||||
bool
|
||||
Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
|
||||
size_t startOffset, FunctionType type,
|
||||
FunctionSyntaxKind kind, bool strict, bool *becameStrict)
|
||||
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
|
||||
HandlePropertyName funName,
|
||||
size_t startOffset, FunctionType type,
|
||||
FunctionSyntaxKind kind,
|
||||
bool strict, bool *becameStrict)
|
||||
{
|
||||
if (becameStrict)
|
||||
*becameStrict = false;
|
||||
ParseContext<ParseHandler> *outerpc = pc;
|
||||
ParseContext<FullParseHandler> *outerpc = pc;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
FunctionBox *funbox = newFunctionBox(fun, pc, strict);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
// Disable lazy parsing if any functions are defined within a scope
|
||||
// statement. Free names in the inner functions will be bound incorrectly.
|
||||
if (pc->topScopeStmt)
|
||||
handler.disableSyntaxParser();
|
||||
|
||||
// Try a syntax parse for this inner function.
|
||||
do {
|
||||
Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
|
||||
if (!parser)
|
||||
break;
|
||||
|
||||
{
|
||||
// Move the syntax parser to the current position in the stream.
|
||||
TokenStream::Position position(keepAtoms);
|
||||
tokenStream.tell(&position);
|
||||
parser->tokenStream.seek(position, tokenStream);
|
||||
|
||||
ParseContext<SyntaxParseHandler> funpc(parser, outerpc, funbox,
|
||||
outerpc->staticLevel + 1, outerpc->blockidGen);
|
||||
if (!funpc.init())
|
||||
return false;
|
||||
|
||||
if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
|
||||
fun, funName, type, kind, strict, becameStrict))
|
||||
{
|
||||
if (parser->hadAbortedSyntaxParse()) {
|
||||
// Try again with a full parse.
|
||||
parser->clearAbortedSyntaxParse();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
outerpc->blockidGen = funpc.blockidGen;
|
||||
|
||||
// Advance this parser over tokens processed by the syntax parser.
|
||||
parser->tokenStream.tell(&position);
|
||||
tokenStream.seek(position, parser->tokenStream);
|
||||
}
|
||||
|
||||
pn->pn_funbox = funbox;
|
||||
|
||||
if (!addFreeVariablesFromLazyFunction(fun, pc))
|
||||
return false;
|
||||
|
||||
pn->pn_blockid = outerpc->blockid();
|
||||
PropagateTransitiveParseFlags(funbox, outerpc->sc);
|
||||
return true;
|
||||
} while (false);
|
||||
|
||||
// Continue doing a full parse for this inner function.
|
||||
ParseContext<FullParseHandler> funpc(this, pc, funbox,
|
||||
outerpc->staticLevel + 1, outerpc->blockidGen);
|
||||
if (!funpc.init())
|
||||
return false;
|
||||
|
||||
if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
|
||||
return false;
|
||||
|
||||
if (!leaveFunction(pn, funName, outerpc, kind))
|
||||
return false;
|
||||
|
||||
pn->pn_blockid = outerpc->blockid();
|
||||
|
||||
/*
|
||||
* Fruit of the poisonous tree: if a closure contains a dynamic name access
|
||||
* (eval, with, etc), we consider the parent to do the same. The reason is
|
||||
* that the deoptimizing effects of dynamic name access apply equally to
|
||||
* parents: any local can be read at runtime.
|
||||
*/
|
||||
PropagateTransitiveParseFlags(funbox, outerpc->sc);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
HandlePropertyName funName,
|
||||
size_t startOffset, FunctionType type,
|
||||
FunctionSyntaxKind kind,
|
||||
bool strict, bool *becameStrict)
|
||||
{
|
||||
if (becameStrict)
|
||||
*becameStrict = false;
|
||||
ParseContext<SyntaxParseHandler> *outerpc = pc;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
FunctionBox *funbox = newFunctionBox(fun, pc, strict);
|
||||
@ -1930,17 +2181,82 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
|
||||
return false;
|
||||
|
||||
// Initialize early for possible flags mutation via destructuringExpr.
|
||||
ParseContext<ParseHandler> funpc(this, pc, funbox, outerpc->staticLevel + 1, outerpc->blockidGen);
|
||||
ParseContext<SyntaxParseHandler> funpc(this, pc, funbox,
|
||||
outerpc->staticLevel + 1, outerpc->blockidGen);
|
||||
if (!funpc.init())
|
||||
return false;
|
||||
|
||||
// Now parse formal argument list and compute fun->nargs.
|
||||
if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
|
||||
return false;
|
||||
|
||||
if (!leaveFunction(pn, funName, outerpc, kind))
|
||||
return false;
|
||||
|
||||
// This is a lazy function inner to another lazy function. Remember the
|
||||
// inner function so that if the outer function is eventually parsed we do
|
||||
// not need any further parsing or processing of the inner function.
|
||||
JS_ASSERT(fun->lazyScript());
|
||||
return outerpc->innerFunctions.append(fun);
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
|
||||
bool strict)
|
||||
{
|
||||
Node pn = handler.newFunctionDefinition();
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
FunctionBox *funbox = newFunctionBox(fun, /* outerpc = */ NULL, strict);
|
||||
if (!funbox)
|
||||
return null();
|
||||
handler.setFunctionBox(pn, funbox);
|
||||
|
||||
ParseContext<FullParseHandler> funpc(this, NULL, funbox, staticLevel, 0);
|
||||
if (!funpc.init())
|
||||
return null();
|
||||
|
||||
RootedPropertyName funName(context, fun->atom() ? fun->atom()->asPropertyName() : NULL);
|
||||
|
||||
if (!functionArgsAndBodyGeneric(pn, fun, funName, Normal, Statement, strict, NULL))
|
||||
return null();
|
||||
|
||||
if (fun->isNamedLambda()) {
|
||||
if (AtomDefnPtr p = pc->lexdeps->lookup(funName)) {
|
||||
Definition *dn = p.value().get<FullParseHandler>();
|
||||
if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
InternalHandle<Bindings*> bindings =
|
||||
InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
|
||||
if (!pc->generateFunctionBindings(context, bindings))
|
||||
return null();
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
|
||||
HandlePropertyName funName, FunctionType type,
|
||||
FunctionSyntaxKind kind,
|
||||
bool strict, bool *becameStrict)
|
||||
{
|
||||
// Given a properly initialized parse context, try to parse an actual
|
||||
// function without concern for conversion to strict mode, use of lazy
|
||||
// parsing and such.
|
||||
|
||||
Node prelude = null();
|
||||
bool hasRest;
|
||||
if (!functionArguments(kind, &prelude, pn, hasRest))
|
||||
return false;
|
||||
|
||||
fun->setArgCount(funpc.numArgs());
|
||||
FunctionBox *funbox = pc->sc->asFunctionBox();
|
||||
|
||||
fun->setArgCount(pc->numArgs());
|
||||
if (funbox->ndefaults)
|
||||
fun->setHasDefaults();
|
||||
if (hasRest)
|
||||
@ -1961,7 +2277,7 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
|
||||
}
|
||||
|
||||
// Parse the function body.
|
||||
mozilla::Maybe<GenexpGuard<ParseHandler> > yieldGuard;
|
||||
Maybe<GenexpGuard<ParseHandler> > yieldGuard;
|
||||
if (kind == Arrow)
|
||||
yieldGuard.construct(this);
|
||||
|
||||
@ -2004,10 +2320,7 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!finishFunctionDefinition(pn, funbox, prelude, body, outerpc))
|
||||
return false;
|
||||
|
||||
return leaveFunction(pn, funName, outerpc, kind);
|
||||
return finishFunctionDefinition(pn, funbox, prelude, body);
|
||||
}
|
||||
|
||||
template <>
|
||||
@ -2050,7 +2363,7 @@ template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::moduleDecl()
|
||||
{
|
||||
setUnknownResult();
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
@ -2179,6 +2492,8 @@ Parser<ParseHandler>::maybeParseDirective(Node pn, bool *cont)
|
||||
if (pc->sc->isFunctionBox()) {
|
||||
pc->sc->asFunctionBox()->useAsm = true;
|
||||
pc->sc->asFunctionBox()->asmStart = handler.getPosition(pn).begin;
|
||||
if (!abortIfSyntaxParser())
|
||||
return false;
|
||||
} else {
|
||||
if (!report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL))
|
||||
return false;
|
||||
@ -2586,24 +2901,17 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
|
||||
if (!defs.empty()) {
|
||||
dn = defs.front<ParseHandler>();
|
||||
} else {
|
||||
if (AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(name)) {
|
||||
dn = p.value().get<ParseHandler>();
|
||||
} else {
|
||||
/*
|
||||
* No definition before this use in any lexical scope.
|
||||
* Create a placeholder definition node to either:
|
||||
* - Be adopted when we parse the real defining
|
||||
* declaration, or
|
||||
* - Be left as a free variable definition if we never
|
||||
* see the real definition.
|
||||
*/
|
||||
dn = handler.newPlaceholder(pn, pc);
|
||||
if (!dn)
|
||||
return false;
|
||||
DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
|
||||
if (!pc->lexdeps->add(p, name, def))
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* No definition before this use in any lexical scope.
|
||||
* Create a placeholder definition node to either:
|
||||
* - Be adopted when we parse the real defining
|
||||
* declaration, or
|
||||
* - Be left as a free variable definition if we never
|
||||
* see the real definition.
|
||||
*/
|
||||
dn = getOrCreateLexicalDependency(pc, name);
|
||||
if (!dn)
|
||||
return false;
|
||||
}
|
||||
|
||||
handler.linkUseToDef(pn, dn);
|
||||
@ -2847,8 +3155,7 @@ bool
|
||||
Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
|
||||
Node left, bool toplevel)
|
||||
{
|
||||
setUnknownResult();
|
||||
return false;
|
||||
return abortIfSyntaxParser();
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
@ -2889,6 +3196,9 @@ Parser<ParseHandler>::returnOrYield(bool useAssignExpr)
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
if (tt == TOK_YIELD) {
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
/*
|
||||
* If we're within parens, we won't know if this is a generator expression until we see
|
||||
* a |for| token, so we have to delay flagging the current function.
|
||||
@ -3023,7 +3333,7 @@ template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
|
||||
{
|
||||
setUnknownResult();
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
@ -3401,6 +3711,7 @@ Parser<FullParseHandler>::forStatement()
|
||||
}
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
else if (tt == TOK_LET) {
|
||||
handler.disableSyntaxParser();
|
||||
(void) tokenStream.getToken();
|
||||
if (tokenStream.peekToken() == TOK_LP) {
|
||||
pn1 = letBlock(LetExpresion);
|
||||
@ -3695,6 +4006,12 @@ Parser<SyntaxParseHandler>::forStatement()
|
||||
StmtInfoPC forStmt(context);
|
||||
PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
|
||||
|
||||
/* Don't parse 'for each' loops. */
|
||||
if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) {
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
|
||||
/* True if we have 'for (var ...)'. */
|
||||
@ -3718,7 +4035,7 @@ Parser<SyntaxParseHandler>::forStatement()
|
||||
}
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
else if (tt == TOK_CONST || tt == TOK_LET) {
|
||||
setUnknownResult();
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
#endif
|
||||
@ -3747,12 +4064,12 @@ Parser<SyntaxParseHandler>::forStatement()
|
||||
lhsNode != SyntaxParseHandler::NodeName &&
|
||||
lhsNode != SyntaxParseHandler::NodeLValue)
|
||||
{
|
||||
setUnknownResult();
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
|
||||
if (!simpleForDecl) {
|
||||
setUnknownResult();
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
|
||||
@ -3961,6 +4278,9 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::withStatement()
|
||||
{
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
|
||||
uint32_t begin = tokenStream.currentToken().pos.begin;
|
||||
|
||||
@ -4017,6 +4337,8 @@ template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::letStatement()
|
||||
{
|
||||
handler.disableSyntaxParser();
|
||||
|
||||
ParseNode *pn;
|
||||
do {
|
||||
/* Check for a let statement or let expression. */
|
||||
@ -4134,7 +4456,7 @@ template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::letStatement()
|
||||
{
|
||||
setUnknownResult();
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
@ -4417,6 +4739,9 @@ Parser<ParseHandler>::statement()
|
||||
break;
|
||||
|
||||
case TOK_CONST:
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
pn = variables(PNK_CONST);
|
||||
if (!pn)
|
||||
return null();
|
||||
@ -4873,10 +5198,8 @@ bool
|
||||
Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, JSOp op)
|
||||
{
|
||||
/* Full syntax checking of valid assignment LHS terms requires a parse tree. */
|
||||
if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue) {
|
||||
setUnknownResult();
|
||||
return false;
|
||||
}
|
||||
if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue)
|
||||
return abortIfSyntaxParser();
|
||||
return checkStrictAssignment(pn);
|
||||
}
|
||||
|
||||
@ -4919,6 +5242,8 @@ Parser<ParseHandler>::assignExpr()
|
||||
|
||||
case TOK_ARROW: {
|
||||
tokenStream.seek(start);
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
if (tokenStream.getToken() == TOK_ERROR)
|
||||
return null();
|
||||
@ -5054,6 +5379,12 @@ Parser<SyntaxParseHandler>::checkDeleteExpression(Node *pn)
|
||||
PropertyName *name = handler.isName(*pn);
|
||||
if (name)
|
||||
return report(ParseStrictError, pc->sc->strict, *pn, JSMSG_DEPRECATED_DELETE_OPERAND);
|
||||
|
||||
// Treat deletion of non-lvalues as ambiguous, so that any error associated
|
||||
// with deleting a call expression is reported.
|
||||
if (*pn != SyntaxParseHandler::NodeLValue && strictMode())
|
||||
return abortIfSyntaxParser();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5409,7 +5740,7 @@ CompExprTransplanter::transplant(ParseNode *pn)
|
||||
* generator) a use of a new placeholder in the generator's
|
||||
* lexdeps.
|
||||
*/
|
||||
Definition *dn2 = parser->handler.newPlaceholder(pn, parser->pc);
|
||||
Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc);
|
||||
if (!dn2)
|
||||
return false;
|
||||
dn2->pn_pos = root->pn_pos;
|
||||
@ -5491,6 +5822,18 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
||||
ParseContext<FullParseHandler> *outerpc,
|
||||
ParseNodeKind kind, JSOp op)
|
||||
{
|
||||
/*
|
||||
* If we saw any inner functions while processing the generator expression
|
||||
* then they may have upvars referring to the let vars in this generator
|
||||
* which were not correctly processed. Bail out and start over without
|
||||
* allowing lazy parsing.
|
||||
*/
|
||||
if (handler.syntaxParser) {
|
||||
handler.disableSyntaxParser();
|
||||
abortedSyntaxParse = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned adjust;
|
||||
ParseNode *pn, *pn2, *pn3, **pnp;
|
||||
StmtInfoPC stmtInfo(context);
|
||||
@ -5746,8 +6089,7 @@ template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::arrayInitializerComprehensionTail(Node pn)
|
||||
{
|
||||
setUnknownResult();
|
||||
return false;
|
||||
return abortIfSyntaxParser();
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
@ -5853,7 +6195,7 @@ template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::generatorExpr(Node kid)
|
||||
{
|
||||
setUnknownResult();
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ typedef HashSet<JSAtom *> FuncStmtSet;
|
||||
class SharedContext;
|
||||
|
||||
typedef Vector<Definition *, 16> DeclVector;
|
||||
typedef Vector<JSFunction *, 4> FunctionVector;
|
||||
|
||||
struct GenericParseContext
|
||||
{
|
||||
@ -203,6 +204,9 @@ struct ParseContext : public GenericParseContext
|
||||
that will alias any top-level bindings with
|
||||
the same name. */
|
||||
|
||||
// All inner functions in this context. Only filled in when parsing syntax.
|
||||
FunctionVector innerFunctions;
|
||||
|
||||
// Set when parsing a declaration-like destructuring pattern. This flag
|
||||
// causes PrimaryExpr to create PN_NAME parse nodes for variable references
|
||||
// which are not hooked into any definition's use chain, added to any tree
|
||||
@ -254,6 +258,7 @@ class GenexpGuard;
|
||||
|
||||
enum LetContext { LetExpresion, LetStatement };
|
||||
enum VarContext { HoistVars, DontHoistVars };
|
||||
enum FunctionType { Getter, Setter, Normal };
|
||||
|
||||
template <typename ParseHandler>
|
||||
struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
@ -301,7 +306,7 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
* is not known whether the parse succeeds or fails, this bit is set and
|
||||
* the parse will return false.
|
||||
*/
|
||||
bool unknownResult;
|
||||
bool abortedSyntaxParse;
|
||||
|
||||
typedef typename ParseHandler::Node Node;
|
||||
typedef typename ParseHandler::DefinitionNode DefinitionNode;
|
||||
@ -319,7 +324,9 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
...);
|
||||
|
||||
Parser(JSContext *cx, const CompileOptions &options,
|
||||
const jschar *chars, size_t length, bool foldConstants);
|
||||
const jschar *chars, size_t length, bool foldConstants,
|
||||
Parser<SyntaxParseHandler> *syntaxParser,
|
||||
LazyScript *lazyOuterFunction);
|
||||
~Parser();
|
||||
|
||||
friend void AutoGCRooter::trace(JSTracer *trc);
|
||||
@ -356,8 +363,11 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
bool hadUnknownResult() {
|
||||
return unknownResult;
|
||||
bool hadAbortedSyntaxParse() {
|
||||
return abortedSyntaxParse;
|
||||
}
|
||||
void clearAbortedSyntaxParse() {
|
||||
abortedSyntaxParse = false;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -366,12 +376,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
/*
|
||||
* Create a parse node with the given kind and op using the current token's
|
||||
* atom.
|
||||
*/
|
||||
*/
|
||||
Node atomNode(ParseNodeKind kind, JSOp op);
|
||||
|
||||
void setUnknownResult() {
|
||||
unknownResult = true;
|
||||
}
|
||||
inline bool abortIfSyntaxParser();
|
||||
|
||||
public:
|
||||
|
||||
@ -384,6 +392,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
Node fn, FunctionBox **funbox, bool strict,
|
||||
bool *becameStrict = NULL);
|
||||
|
||||
// Parse a function, given only its arguments and body. Used for lazily
|
||||
// parsed functions.
|
||||
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
|
||||
|
||||
/*
|
||||
* Parse a function body. Pass StatementListBody if the body is a list of
|
||||
* statements; pass ExpressionBody if the body is a single expression.
|
||||
@ -391,6 +403,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
enum FunctionBodyType { StatementListBody, ExpressionBody };
|
||||
Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
|
||||
|
||||
bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
|
||||
HandlePropertyName funName, FunctionType type,
|
||||
FunctionSyntaxKind kind, bool strict, bool *becameStrict);
|
||||
|
||||
virtual bool strictMode() { return pc->sc->strict; }
|
||||
|
||||
private:
|
||||
@ -439,7 +455,6 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
/*
|
||||
* Additional JS parsers.
|
||||
*/
|
||||
enum FunctionType { Getter, Setter, Normal };
|
||||
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
|
||||
|
||||
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
|
||||
@ -478,10 +493,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
void addStatementToList(Node pn, Node kid, bool *hasFunctionStmt);
|
||||
bool checkFunctionArguments();
|
||||
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
|
||||
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind);
|
||||
bool finishFunctionDefinition(Node pn, FunctionBox *funbox,
|
||||
Node prelude, Node body,
|
||||
ParseContext<ParseHandler> *outerpc);
|
||||
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
|
||||
bool *pbodyProcessed);
|
||||
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
|
||||
bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
|
||||
|
||||
bool isValidForStatementLHS(Node pn1, JSVersion version,
|
||||
bool forDecl, bool forEach, bool forOf);
|
||||
@ -523,6 +538,7 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
||||
bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
|
||||
bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
|
||||
bool checkFinalReturn(Node pn);
|
||||
DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
|
||||
|
||||
bool leaveFunction(Node fn, HandlePropertyName funName,
|
||||
ParseContext<ParseHandler> *outerpc,
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "builtin/Module.h"
|
||||
#include "frontend/ParseMaps.h"
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "vm/ScopeObject.h"
|
||||
|
||||
namespace js {
|
||||
@ -200,6 +201,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
Bindings bindings; /* bindings for this function */
|
||||
uint32_t bufStart;
|
||||
uint32_t bufEnd;
|
||||
uint32_t startLine;
|
||||
uint32_t startColumn;
|
||||
uint32_t asmStart; /* offset of the "use asm" directive, if present */
|
||||
uint16_t ndefaults;
|
||||
bool inWith:1; /* some enclosing scope is a with-statement */
|
||||
@ -234,6 +237,12 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
bool useAsmOrInsideUseAsm() const {
|
||||
return useAsm || insideUseAsm;
|
||||
}
|
||||
|
||||
void setStart(const TokenStream &tokenStream) {
|
||||
bufStart = tokenStream.currentToken().pos.begin;
|
||||
startLine = tokenStream.getLineno();
|
||||
startColumn = tokenStream.getColumn();
|
||||
}
|
||||
};
|
||||
|
||||
inline FunctionBox *
|
||||
|
@ -10,9 +10,19 @@
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
// Parse handler used when processing the syntax in a block of code, to generate
|
||||
// the minimal information which is required to detect syntax errors and allow
|
||||
// bytecode to be emitted for outer functions.
|
||||
//
|
||||
// When parsing, we start at the top level with a full parse, and when possible
|
||||
// only check the syntax for inner functions, so that they can be lazily parsed
|
||||
// into bytecode when/if they first run. Checking the syntax of a function is
|
||||
// several times faster than doing a full parse/emit, and lazy parsing improves
|
||||
// both performance and memory usage significantly when pages contain large
|
||||
// amounts of code that never executes (which happens often).
|
||||
class SyntaxParseHandler
|
||||
{
|
||||
/* Remember the last encountered name or string literal during syntax parses. */
|
||||
// Remember the last encountered name or string literal during syntax parses.
|
||||
JSAtom *lastAtom;
|
||||
TokenPos lastStringPos;
|
||||
TokenStream &tokenStream;
|
||||
@ -28,7 +38,8 @@ class SyntaxParseHandler
|
||||
};
|
||||
typedef Definition::Kind DefinitionNode;
|
||||
|
||||
SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
|
||||
SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
|
||||
Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
|
||||
: lastAtom(NULL),
|
||||
tokenStream(tokenStream)
|
||||
{}
|
||||
@ -42,7 +53,7 @@ class SyntaxParseHandler
|
||||
lastAtom = name;
|
||||
return NodeName;
|
||||
}
|
||||
DefinitionNode newPlaceholder(Node pn, ParseContext<SyntaxParseHandler> *pc) {
|
||||
DefinitionNode newPlaceholder(JSAtom *atom, ParseContext<SyntaxParseHandler> *pc) {
|
||||
return Definition::PLACEHOLDER;
|
||||
}
|
||||
Node newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) {
|
||||
@ -182,6 +193,8 @@ class SyntaxParseHandler
|
||||
static DefinitionNode nullDefinition() {
|
||||
return Definition::MISSING;
|
||||
}
|
||||
void disableSyntaxParser() {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace frontend
|
||||
|
@ -158,6 +158,22 @@ TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset)
|
||||
}
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
TokenStream::SourceCoords::fill(const TokenStream::SourceCoords &other)
|
||||
{
|
||||
JS_ASSERT(lineStartOffsets_.back() == MAX_PTR);
|
||||
JS_ASSERT(other.lineStartOffsets_.back() == MAX_PTR);
|
||||
|
||||
if (lineStartOffsets_.length() >= other.lineStartOffsets_.length())
|
||||
return;
|
||||
|
||||
uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
|
||||
lineStartOffsets_[sentinelIndex] = other.lineStartOffsets_[sentinelIndex];
|
||||
|
||||
for (size_t i = sentinelIndex + 1; i < other.lineStartOffsets_.length(); i++)
|
||||
(void)lineStartOffsets_.append(other.lineStartOffsets_[i]);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE uint32_t
|
||||
TokenStream::SourceCoords::lineIndexOf(uint32_t offset) const
|
||||
{
|
||||
@ -251,9 +267,9 @@ TokenStream::TokenStream(JSContext *cx, const CompileOptions &options,
|
||||
lookahead(),
|
||||
lineno(options.lineno),
|
||||
flags(),
|
||||
linebase(base),
|
||||
linebase(base - options.column),
|
||||
prevLinebase(NULL),
|
||||
userbuf(cx, base, length),
|
||||
userbuf(cx, base - options.column, length + options.column), // See comment below
|
||||
filename(options.filename),
|
||||
sourceMap(NULL),
|
||||
listenerTSData(),
|
||||
@ -268,6 +284,12 @@ TokenStream::TokenStream(JSContext *cx, const CompileOptions &options,
|
||||
linebaseSkip(cx, &linebase),
|
||||
prevLinebaseSkip(cx, &prevLinebase)
|
||||
{
|
||||
// Column numbers are computed as offsets from the current line's base, so the
|
||||
// initial line's base must be included in the buffer. linebase and userbuf
|
||||
// were adjusted above, and if we are starting tokenization part way through
|
||||
// this line then adjust the next character.
|
||||
userbuf.setAddressOfNextRawChar(base);
|
||||
|
||||
if (originPrincipals)
|
||||
JS_HoldPrincipals(originPrincipals);
|
||||
|
||||
@ -501,6 +523,19 @@ TokenStream::TokenBuf::findEOLMax(const jschar *p, size_t max)
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
TokenStream::advance(size_t position)
|
||||
{
|
||||
const jschar *end = userbuf.base() + position;
|
||||
while (userbuf.addressOfNextRawChar() < end)
|
||||
getChar();
|
||||
|
||||
Token *cur = &tokens[cursor];
|
||||
cur->pos.begin = userbuf.addressOfNextRawChar() - userbuf.base();
|
||||
cur->type = TOK_ERROR;
|
||||
lookahead = 0;
|
||||
}
|
||||
|
||||
void
|
||||
TokenStream::tell(Position *pos)
|
||||
{
|
||||
@ -530,6 +565,13 @@ TokenStream::seek(const Position &pos)
|
||||
tokens[(cursor + 1 + i) & ntokensMask] = pos.lookaheadTokens[i];
|
||||
}
|
||||
|
||||
void
|
||||
TokenStream::seek(const Position &pos, const TokenStream &other)
|
||||
{
|
||||
srcCoords.fill(other.srcCoords);
|
||||
seek(pos);
|
||||
}
|
||||
|
||||
void
|
||||
TokenStream::positionAfterLastFunctionKeyword(Position &pos)
|
||||
{
|
||||
|
@ -472,6 +472,7 @@ class MOZ_STACK_CLASS TokenStream
|
||||
const CharBuffer &getTokenbuf() const { return tokenbuf; }
|
||||
const char *getFilename() const { return filename; }
|
||||
unsigned getLineno() const { return lineno; }
|
||||
unsigned getColumn() const { return userbuf.addressOfNextRawChar() - linebase - 1; }
|
||||
JSVersion versionNumber() const { return VersionNumber(version); }
|
||||
JSVersion versionWithFlags() const { return version; }
|
||||
bool hadError() const { return !!(flags & TSF_HAD_ERROR); }
|
||||
@ -660,8 +661,10 @@ class MOZ_STACK_CLASS TokenStream
|
||||
Token lookaheadTokens[maxLookahead];
|
||||
};
|
||||
|
||||
void advance(size_t position);
|
||||
void tell(Position *);
|
||||
void seek(const Position &pos);
|
||||
void seek(const Position &pos, const TokenStream &other);
|
||||
void positionAfterLastFunctionKeyword(Position &pos);
|
||||
|
||||
size_t positionToOffset(const Position &pos) const {
|
||||
@ -752,6 +755,7 @@ class MOZ_STACK_CLASS TokenStream
|
||||
SourceCoords(JSContext *cx, uint32_t ln);
|
||||
|
||||
void add(uint32_t lineNum, uint32_t lineStartOffset);
|
||||
void fill(const SourceCoords &other);
|
||||
|
||||
bool isOnThisLine(uint32_t offset, uint32_t lineNum) const {
|
||||
uint32_t lineIndex = lineNumToIndex(lineNum);
|
||||
@ -828,7 +832,7 @@ class MOZ_STACK_CLASS TokenStream
|
||||
ptr--;
|
||||
}
|
||||
|
||||
const jschar *addressOfNextRawChar() {
|
||||
const jschar *addressOfNextRawChar() const {
|
||||
JS_ASSERT(ptr); /* make sure haven't been poisoned */
|
||||
return ptr;
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ enum AllocKind {
|
||||
FINALIZE_OBJECT16_BACKGROUND,
|
||||
FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
|
||||
FINALIZE_SCRIPT,
|
||||
FINALIZE_LAZY_SCRIPT,
|
||||
FINALIZE_SHAPE,
|
||||
FINALIZE_BASE_SHAPE,
|
||||
FINALIZE_TYPE_OBJECT,
|
||||
|
@ -81,6 +81,7 @@ namespace gc {
|
||||
|
||||
static void MarkChildren(JSTracer *trc, JSString *str);
|
||||
static void MarkChildren(JSTracer *trc, JSScript *script);
|
||||
static void MarkChildren(JSTracer *trc, LazyScript *lazy);
|
||||
static void MarkChildren(JSTracer *trc, Shape *shape);
|
||||
static void MarkChildren(JSTracer *trc, BaseShape *base);
|
||||
static void MarkChildren(JSTracer *trc, types::TypeObject *type);
|
||||
@ -360,6 +361,7 @@ DeclMarkerImpl(Object, JSObject)
|
||||
DeclMarkerImpl(Object, JSFunction)
|
||||
DeclMarkerImpl(Object, ScopeObject)
|
||||
DeclMarkerImpl(Script, JSScript)
|
||||
DeclMarkerImpl(LazyScript, LazyScript)
|
||||
DeclMarkerImpl(Shape, Shape)
|
||||
DeclMarkerImpl(String, JSAtom)
|
||||
DeclMarkerImpl(String, JSString)
|
||||
@ -390,6 +392,9 @@ gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
|
||||
case JSTRACE_SCRIPT:
|
||||
MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
|
||||
break;
|
||||
case JSTRACE_LAZY_SCRIPT:
|
||||
MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
|
||||
break;
|
||||
case JSTRACE_SHAPE:
|
||||
MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
|
||||
break;
|
||||
@ -792,6 +797,20 @@ PushMarkStack(GCMarker *gcmarker, JSScript *thing)
|
||||
MarkChildren(gcmarker, thing);
|
||||
}
|
||||
|
||||
static void
|
||||
PushMarkStack(GCMarker *gcmarker, LazyScript *thing)
|
||||
{
|
||||
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
|
||||
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
|
||||
|
||||
/*
|
||||
* We mark lazy scripts directly rather than pushing on the stack as they
|
||||
* only refer to normal scripts and to strings, and cannot recurse.
|
||||
*/
|
||||
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
|
||||
MarkChildren(gcmarker, thing);
|
||||
}
|
||||
|
||||
static void
|
||||
ScanShape(GCMarker *gcmarker, Shape *shape);
|
||||
|
||||
@ -997,6 +1016,12 @@ gc::MarkChildren(JSTracer *trc, JSScript *script)
|
||||
script->markChildren(trc);
|
||||
}
|
||||
|
||||
static void
|
||||
gc::MarkChildren(JSTracer *trc, LazyScript *lazy)
|
||||
{
|
||||
lazy->markChildren(trc);
|
||||
}
|
||||
|
||||
static void
|
||||
gc::MarkChildren(JSTracer *trc, Shape *shape)
|
||||
{
|
||||
@ -1153,6 +1178,10 @@ gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
|
||||
PushArenaTyped<JSScript>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_LAZY_SCRIPT:
|
||||
PushArenaTyped<LazyScript>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_SHAPE:
|
||||
PushArenaTyped<js::Shape>(gcmarker, aheader);
|
||||
break;
|
||||
@ -1501,6 +1530,10 @@ js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
|
||||
MarkChildren(trc, static_cast<JSScript *>(thing));
|
||||
break;
|
||||
|
||||
case JSTRACE_LAZY_SCRIPT:
|
||||
MarkChildren(trc, static_cast<LazyScript *>(thing));
|
||||
break;
|
||||
|
||||
case JSTRACE_SHAPE:
|
||||
MarkChildren(trc, static_cast<Shape *>(thing));
|
||||
break;
|
||||
|
@ -101,6 +101,7 @@ DeclMarker(Object, JSObject)
|
||||
DeclMarker(Object, JSFunction)
|
||||
DeclMarker(Object, ScopeObject)
|
||||
DeclMarker(Script, JSScript)
|
||||
DeclMarker(LazyScript, LazyScript)
|
||||
DeclMarker(Shape, Shape)
|
||||
DeclMarker(String, JSAtom)
|
||||
DeclMarker(String, JSString)
|
||||
@ -396,6 +397,12 @@ TraceKind(JSScript *script)
|
||||
return JSTRACE_SCRIPT;
|
||||
}
|
||||
|
||||
inline JSGCTraceKind
|
||||
TraceKind(LazyScript *lazy)
|
||||
{
|
||||
return JSTRACE_LAZY_SCRIPT;
|
||||
}
|
||||
|
||||
} /* namespace gc */
|
||||
|
||||
void
|
||||
|
@ -1550,7 +1550,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
|
||||
masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
|
||||
|
||||
// Knowing that calleereg is a non-native function, load the JSScript.
|
||||
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
|
||||
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
|
||||
|
||||
// Load script jitcode.
|
||||
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
|
||||
@ -1685,7 +1685,7 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
|
||||
}
|
||||
|
||||
// Knowing that calleereg is a non-native function, load the JSScript.
|
||||
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
|
||||
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
|
||||
|
||||
// Load script jitcode.
|
||||
if (call->mir()->needsArgCheck())
|
||||
@ -1894,7 +1894,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
|
||||
}
|
||||
|
||||
// Knowing that calleereg is a non-native function, load the JSScript.
|
||||
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
|
||||
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
|
||||
|
||||
// Load script jitcode.
|
||||
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
|
||||
|
@ -7965,6 +7965,9 @@ IonBuilder::jsop_object(JSObject *obj)
|
||||
bool
|
||||
IonBuilder::jsop_lambda(JSFunction *fun)
|
||||
{
|
||||
if (fun->isInterpreted() && !fun->getOrCreateScript(cx))
|
||||
return false;
|
||||
|
||||
JS_ASSERT(script()->analysis()->usesScopeChain());
|
||||
if (fun->isArrow())
|
||||
return abort("bound arrow function");
|
||||
|
@ -416,7 +416,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
|
||||
// Call the target function.
|
||||
// Note that this code assumes the function is JITted.
|
||||
masm.ma_ldr(DTRAddr(r1, DtrOffImm(offsetof(JSFunction, u.i.script_))), r3);
|
||||
masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3);
|
||||
masm.loadBaselineOrIonRaw(r3, r3, mode, NULL);
|
||||
masm.ma_callIonHalfPush(r3);
|
||||
|
||||
|
@ -388,7 +388,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
|
||||
// Call the target function.
|
||||
// Note that this code assumes the function is JITted.
|
||||
masm.movq(Operand(rax, offsetof(JSFunction, u.i.script_)), rax);
|
||||
masm.movq(Operand(rax, JSFunction::offsetOfNativeOrScript()), rax);
|
||||
masm.loadBaselineOrIonRaw(rax, rax, mode, NULL);
|
||||
masm.call(rax);
|
||||
uint32_t returnOffset = masm.currentOffset();
|
||||
|
@ -379,7 +379,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
|
||||
// Call the target function.
|
||||
// Note that this assumes the function is JITted.
|
||||
masm.movl(Operand(eax, offsetof(JSFunction, u.i.script_)), eax);
|
||||
masm.movl(Operand(eax, JSFunction::offsetOfNativeOrScript()), eax);
|
||||
masm.loadBaselineOrIonRaw(eax, eax, mode, NULL);
|
||||
masm.call(eax);
|
||||
uint32_t returnOffset = masm.currentOffset();
|
||||
|
@ -2597,6 +2597,10 @@ JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
|
||||
name = "script";
|
||||
break;
|
||||
|
||||
case JSTRACE_LAZY_SCRIPT:
|
||||
name = "lazyscript";
|
||||
break;
|
||||
|
||||
case JSTRACE_IONCODE:
|
||||
name = "ioncode";
|
||||
break;
|
||||
@ -2668,6 +2672,7 @@ JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_LAZY_SCRIPT:
|
||||
case JSTRACE_IONCODE:
|
||||
case JSTRACE_SHAPE:
|
||||
case JSTRACE_BASE_SHAPE:
|
||||
@ -4919,6 +4924,11 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobjArg, JSObject *parentArg)
|
||||
* script, we cannot clone it without breaking the compiler's assumptions.
|
||||
*/
|
||||
RootedFunction fun(cx, funobj->toFunction());
|
||||
if (fun->isInterpretedLazy()) {
|
||||
AutoCompartment ac(cx, funobj);
|
||||
if (!fun->getOrCreateScript(cx))
|
||||
return NULL;
|
||||
}
|
||||
if (fun->isInterpreted() && (fun->nonLazyScript()->enclosingStaticScope() ||
|
||||
(fun->nonLazyScript()->compileAndGo && !parent->isGlobal())))
|
||||
{
|
||||
@ -5282,10 +5292,12 @@ JS::CompileOptions::CompileOptions(JSContext *cx)
|
||||
utf8(false),
|
||||
filename(NULL),
|
||||
lineno(1),
|
||||
column(0),
|
||||
compileAndGo(cx->hasOption(JSOPTION_COMPILE_N_GO)),
|
||||
forEval(false),
|
||||
noScriptRval(cx->hasOption(JSOPTION_NO_SCRIPT_RVAL)),
|
||||
selfHostingMode(false),
|
||||
canLazilyParse(true),
|
||||
sourcePolicy(SAVE_SOURCE)
|
||||
{
|
||||
}
|
||||
@ -5424,7 +5436,7 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *objArg, const char *utf8, siz
|
||||
CompileOptions options(cx);
|
||||
options.setCompileAndGo(false);
|
||||
Parser<frontend::FullParseHandler> parser(cx, options, chars, length,
|
||||
/* foldConstants = */ true);
|
||||
/* foldConstants = */ true, NULL, NULL);
|
||||
if (parser.init()) {
|
||||
older = JS_SetErrorReporter(cx, NULL);
|
||||
if (!parser.parse(obj) &&
|
||||
|
@ -3894,10 +3894,12 @@ struct JS_PUBLIC_API(CompileOptions) {
|
||||
bool utf8;
|
||||
const char *filename;
|
||||
unsigned lineno;
|
||||
unsigned column;
|
||||
bool compileAndGo;
|
||||
bool forEval;
|
||||
bool noScriptRval;
|
||||
bool selfHostingMode;
|
||||
bool canLazilyParse;
|
||||
enum SourcePolicy {
|
||||
NO_SOURCE,
|
||||
LAZY_SOURCE,
|
||||
@ -3912,10 +3914,12 @@ struct JS_PUBLIC_API(CompileOptions) {
|
||||
CompileOptions &setFileAndLine(const char *f, unsigned l) {
|
||||
filename = f; lineno = l; return *this;
|
||||
}
|
||||
CompileOptions &setColumn(unsigned c) { column = c; return *this; }
|
||||
CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
|
||||
CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
|
||||
CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
|
||||
CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
|
||||
CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
|
||||
CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
|
||||
};
|
||||
|
||||
|
@ -921,6 +921,8 @@ JS::IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind)
|
||||
JSString::writeBarrierPre(static_cast<JSString*>(cell));
|
||||
else if (kind == JSTRACE_SCRIPT)
|
||||
JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
|
||||
else if (kind == JSTRACE_LAZY_SCRIPT)
|
||||
LazyScript::writeBarrierPre(static_cast<LazyScript*>(cell));
|
||||
else if (kind == JSTRACE_SHAPE)
|
||||
Shape::writeBarrierPre(static_cast<Shape*>(cell));
|
||||
else if (kind == JSTRACE_BASE_SHAPE)
|
||||
|
@ -491,8 +491,13 @@ JSFunction::trace(JSTracer *trc)
|
||||
MarkString(trc, &atom_, "atom");
|
||||
|
||||
if (isInterpreted()) {
|
||||
if (hasScript())
|
||||
MarkScriptUnbarriered(trc, &u.i.script_, "script");
|
||||
// Functions can be be marked as interpreted despite having no script
|
||||
// yet at some points when parsing, and can be lazy with no lazy script
|
||||
// for self hosted code.
|
||||
if (hasScript() && u.i.s.script_)
|
||||
MarkScriptUnbarriered(trc, &u.i.s.script_, "script");
|
||||
else if (isInterpretedLazy() && u.i.s.lazy_)
|
||||
MarkLazyScriptUnbarriered(trc, &u.i.s.lazy_, "lazyScript");
|
||||
if (u.i.env_)
|
||||
MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
|
||||
}
|
||||
@ -584,8 +589,8 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length,
|
||||
JSString *
|
||||
js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen)
|
||||
{
|
||||
StringBuffer out(cx);
|
||||
RootedScript script(cx);
|
||||
if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
|
||||
return NULL;
|
||||
|
||||
// If the object is an automatically-bound arrow function, get the source
|
||||
// of the pre-binding target.
|
||||
@ -596,6 +601,9 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
|
||||
return FunctionToString(cx, targetFun, bodyOnly, lambdaParen);
|
||||
}
|
||||
|
||||
StringBuffer out(cx);
|
||||
RootedScript script(cx);
|
||||
|
||||
if (fun->hasScript()) {
|
||||
script = fun->nonLazyScript();
|
||||
if (script->isGeneratorExp) {
|
||||
@ -1061,17 +1069,65 @@ JSFunction::getBoundFunctionArgumentCount() const
|
||||
return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
|
||||
}
|
||||
|
||||
bool
|
||||
JSFunction::initializeLazyScript(JSContext *cx)
|
||||
/* static */ bool
|
||||
JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFunction fun)
|
||||
{
|
||||
JS_ASSERT(isInterpretedLazy());
|
||||
const JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(getExtendedSlot(0).toPrivate());
|
||||
Rooted<JSFunction*> self(cx, this);
|
||||
JS_ASSERT(fun->isInterpretedLazy());
|
||||
|
||||
if (LazyScript *lazy = fun->lazyScriptOrNull()) {
|
||||
/* Trigger a pre barrier on the lazy script being overwritten. */
|
||||
if (cx->zone()->needsBarrier())
|
||||
LazyScript::writeBarrierPre(lazy);
|
||||
|
||||
if (JSScript *script = lazy->maybeScript()) {
|
||||
fun->flags &= ~INTERPRETED_LAZY;
|
||||
fun->flags |= INTERPRETED;
|
||||
fun->initScript(script);
|
||||
|
||||
/*
|
||||
* Set some bits that are normally filled in by the Parser after
|
||||
* the full parse tree has been produced.
|
||||
*/
|
||||
if (script->function()->isHeavyweight())
|
||||
fun->setIsHeavyweight();
|
||||
fun->nargs = script->function()->nargs;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Lazily parsed script. */
|
||||
const jschar *chars = lazy->source()->chars(cx);
|
||||
if (!chars)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* GC must be suppressed for the remainder of the lazy parse, as any
|
||||
* GC activity may destroy the characters.
|
||||
*/
|
||||
AutoSuppressGC suppressGC(cx);
|
||||
|
||||
fun->flags &= ~INTERPRETED_LAZY;
|
||||
fun->flags |= INTERPRETED;
|
||||
fun->initScript(NULL);
|
||||
|
||||
const jschar *lazyStart = chars + lazy->begin();
|
||||
size_t lazyLength = lazy->end() - lazy->begin();
|
||||
|
||||
if (!frontend::CompileLazyFunction(cx, fun, lazy, lazyStart, lazyLength)) {
|
||||
fun->initLazyScript(lazy);
|
||||
return false;
|
||||
}
|
||||
|
||||
lazy->initScript(fun->nonLazyScript());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Lazily cloned self hosted script. */
|
||||
JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(fun->getExtendedSlot(0).toPrivate());
|
||||
RootedAtom funAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
|
||||
if (!funAtom)
|
||||
return false;
|
||||
Rooted<PropertyName *> funName(cx, funAtom->asPropertyName());
|
||||
return cx->runtime->cloneSelfHostedFunctionScript(cx, funName, self);
|
||||
return cx->runtime->cloneSelfHostedFunctionScript(cx, funName, fun);
|
||||
}
|
||||
|
||||
/* ES5 15.3.4.5.1 and 15.3.4.5.2. */
|
||||
@ -1504,6 +1560,10 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
bool useSameScript = cx->compartment == fun->compartment() &&
|
||||
!fun->hasSingletonType() &&
|
||||
!types::UseNewTypeForClone(fun);
|
||||
|
||||
if (!useSameScript && fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
|
||||
return NULL;
|
||||
|
||||
NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
|
||||
JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent),
|
||||
allocKind, newKind);
|
||||
@ -1513,16 +1573,13 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
|
||||
clone->nargs = fun->nargs;
|
||||
clone->flags = fun->flags & ~JSFunction::EXTENDED;
|
||||
if (fun->isInterpreted()) {
|
||||
if (fun->isInterpretedLazy()) {
|
||||
RootedFunction cloneRoot(cx, clone);
|
||||
AutoCompartment ac(cx, fun);
|
||||
if (!fun->getOrCreateScript(cx))
|
||||
return NULL;
|
||||
clone = cloneRoot;
|
||||
}
|
||||
if (fun->hasScript()) {
|
||||
clone->initScript(fun->nonLazyScript());
|
||||
clone->initEnvironment(parent);
|
||||
} else if (fun->isInterpretedLazy()) {
|
||||
LazyScript *lazy = fun->lazyScriptOrNull();
|
||||
clone->initLazyScript(lazy);
|
||||
clone->initEnvironment(parent);
|
||||
} else {
|
||||
clone->initNative(fun->native(), fun->jitInfo());
|
||||
}
|
||||
|
@ -68,8 +68,11 @@ class JSFunction : public JSObject
|
||||
use the accessor! */
|
||||
} n;
|
||||
struct Scripted {
|
||||
JSScript *script_; /* interpreted bytecode descriptor or null;
|
||||
use the accessor! */
|
||||
union {
|
||||
JSScript *script_; /* interpreted bytecode descriptor or null;
|
||||
use the accessor! */
|
||||
js::LazyScript *lazy_; /* lazily compiled script, or NULL */
|
||||
} s;
|
||||
JSObject *env_; /* environment for new activations;
|
||||
use the accessor! */
|
||||
} i;
|
||||
@ -80,6 +83,12 @@ class JSFunction : public JSObject
|
||||
|
||||
public:
|
||||
|
||||
bool isHeavyweight() {
|
||||
/* The heavyweight flag is not set until the script is parsed. */
|
||||
JS_ASSERT(!isInterpretedLazy());
|
||||
return flags & HEAVYWEIGHT;
|
||||
}
|
||||
|
||||
/* A function can be classified as either native (C++) or interpreted (JS): */
|
||||
bool isInterpreted() const { return flags & (INTERPRETED | INTERPRETED_LAZY); }
|
||||
bool isNative() const { return !isInterpreted(); }
|
||||
@ -88,10 +97,9 @@ class JSFunction : public JSObject
|
||||
bool isNativeConstructor() const { return flags & NATIVE_CTOR; }
|
||||
|
||||
/* Possible attributes of an interpreted function: */
|
||||
bool isHeavyweight() const { return flags & HEAVYWEIGHT; }
|
||||
bool isFunctionPrototype() const { return flags & IS_FUN_PROTO; }
|
||||
bool isInterpretedLazy() const { return flags & INTERPRETED_LAZY; }
|
||||
bool hasScript() const { return isInterpreted() && u.i.script_; }
|
||||
bool hasScript() const { return flags & INTERPRETED; }
|
||||
bool isExprClosure() const { return flags & EXPR_CLOSURE; }
|
||||
bool hasGuessedAtom() const { return flags & HAS_GUESSED_ATOM; }
|
||||
bool isLambda() const { return flags & LAMBDA; }
|
||||
@ -206,7 +214,7 @@ class JSFunction : public JSObject
|
||||
static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
|
||||
static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
|
||||
|
||||
bool initializeLazyScript(JSContext *cx);
|
||||
static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun);
|
||||
|
||||
JSScript *getOrCreateScript(JSContext *cx) {
|
||||
JS_ASSERT(isInterpreted());
|
||||
@ -214,12 +222,13 @@ class JSFunction : public JSObject
|
||||
if (isInterpretedLazy()) {
|
||||
JS::RootedFunction self(cx, this);
|
||||
js::MaybeCheckStackRoots(cx);
|
||||
if (!self->initializeLazyScript(cx))
|
||||
if (!createScriptForLazilyInterpretedFunction(cx, self))
|
||||
return NULL;
|
||||
return self->u.i.script_;
|
||||
JS_ASSERT(self->hasScript());
|
||||
return self->u.i.s.script_;
|
||||
}
|
||||
JS_ASSERT(hasScript());
|
||||
return u.i.script_;
|
||||
return u.i.s.script_;
|
||||
}
|
||||
|
||||
static bool maybeGetOrCreateScript(JSContext *cx, js::HandleFunction fun,
|
||||
@ -235,20 +244,35 @@ class JSFunction : public JSObject
|
||||
|
||||
JSScript *nonLazyScript() const {
|
||||
JS_ASSERT(hasScript());
|
||||
return JS::HandleScript::fromMarkedLocation(&u.i.script_);
|
||||
return JS::HandleScript::fromMarkedLocation(&u.i.s.script_);
|
||||
}
|
||||
|
||||
JSScript *maybeNonLazyScript() const {
|
||||
return isInterpreted() ? nonLazyScript() : NULL;
|
||||
return hasScript() ? nonLazyScript() : NULL;
|
||||
}
|
||||
|
||||
js::HeapPtrScript &mutableScript() {
|
||||
JS_ASSERT(isInterpreted());
|
||||
return *(js::HeapPtrScript *)&u.i.script_;
|
||||
return *(js::HeapPtrScript *)&u.i.s.script_;
|
||||
}
|
||||
|
||||
// A lazily interpreted function will have an associated LazyScript if the
|
||||
// script has not yet been parsed. For functions whose scripts are lazily
|
||||
// cloned from self hosted code, there is no LazyScript.
|
||||
|
||||
js::LazyScript *lazyScript() const {
|
||||
JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
|
||||
return u.i.s.lazy_;
|
||||
}
|
||||
|
||||
js::LazyScript *lazyScriptOrNull() const {
|
||||
JS_ASSERT(isInterpretedLazy());
|
||||
return u.i.s.lazy_;
|
||||
}
|
||||
|
||||
inline void setScript(JSScript *script_);
|
||||
inline void initScript(JSScript *script_);
|
||||
inline void initLazyScript(js::LazyScript *script);
|
||||
|
||||
JSNative native() const {
|
||||
JS_ASSERT(isNative());
|
||||
@ -264,7 +288,7 @@ class JSFunction : public JSObject
|
||||
inline void setJitInfo(const JSJitInfo *data);
|
||||
|
||||
static unsigned offsetOfNativeOrScript() {
|
||||
JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script_));
|
||||
JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.s.script_));
|
||||
JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript));
|
||||
return offsetof(JSFunction, u.nativeOrScript);
|
||||
}
|
||||
|
@ -168,13 +168,32 @@ SkipScopeParent(JSObject *parent)
|
||||
return parent;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CanReuseFunctionForClone(JSContext *cx, HandleFunction fun)
|
||||
{
|
||||
if (!fun->hasSingletonType())
|
||||
return false;
|
||||
if (fun->isInterpretedLazy()) {
|
||||
LazyScript *lazy = fun->lazyScript();
|
||||
if (lazy->hasBeenCloned())
|
||||
return false;
|
||||
lazy->setHasBeenCloned();
|
||||
} else {
|
||||
JSScript *script = fun->nonLazyScript();
|
||||
if (script->hasBeenCloned)
|
||||
return false;
|
||||
script->hasBeenCloned = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline JSFunction *
|
||||
CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
NewObjectKind newKind = GenericObject)
|
||||
{
|
||||
/*
|
||||
* For attempts to clone functions at a function definition opcode,
|
||||
* don't perform the clone if the function has singleton type. This
|
||||
* try to avoid the the clone if the function has singleton type. This
|
||||
* was called pessimistically, and we need to preserve the type's
|
||||
* property that if it is singleton there is only a single object
|
||||
* with its type in existence.
|
||||
@ -184,16 +203,12 @@ CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObjec
|
||||
* cases, fall through to CloneFunctionObject, which will deep clone
|
||||
* the function's script.
|
||||
*/
|
||||
if (fun->hasSingletonType()) {
|
||||
RootedScript script(cx, fun->getOrCreateScript(cx));
|
||||
if (!script->hasBeenCloned) {
|
||||
script->hasBeenCloned = true;
|
||||
Rooted<JSObject*> obj(cx, SkipScopeParent(parent));
|
||||
if (!JSObject::setParent(cx, fun, obj))
|
||||
return NULL;
|
||||
fun->setEnvironment(parent);
|
||||
return fun;
|
||||
}
|
||||
if (CanReuseFunctionForClone(cx, fun)) {
|
||||
RootedObject obj(cx, SkipScopeParent(parent));
|
||||
if (!JSObject::setParent(cx, fun, obj))
|
||||
return NULL;
|
||||
fun->setEnvironment(parent);
|
||||
return fun;
|
||||
}
|
||||
|
||||
// These intermediate variables are needed to avoid link errors on some
|
||||
@ -222,6 +237,17 @@ JSFunction::initScript(JSScript *script_)
|
||||
mutableScript().init(script_);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSFunction::initLazyScript(js::LazyScript *lazy)
|
||||
{
|
||||
JS_ASSERT(isInterpreted());
|
||||
|
||||
flags &= ~INTERPRETED;
|
||||
flags |= INTERPRETED_LAZY;
|
||||
|
||||
u.i.s.lazy_ = lazy;
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
JSFunction::getBoundFunctionTarget() const
|
||||
{
|
||||
|
@ -121,6 +121,7 @@ const uint32_t Arena::ThingSizes[] = {
|
||||
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16 */
|
||||
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
sizeof(JSScript), /* FINALIZE_SCRIPT */
|
||||
sizeof(LazyScript), /* FINALIZE_LAZY_SCRIPT */
|
||||
sizeof(Shape), /* FINALIZE_SHAPE */
|
||||
sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */
|
||||
sizeof(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
|
||||
@ -146,6 +147,7 @@ const uint32_t Arena::FirstThingOffsets[] = {
|
||||
OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16 */
|
||||
OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
OFFSET(JSScript), /* FINALIZE_SCRIPT */
|
||||
OFFSET(LazyScript), /* FINALIZE_LAZY_SCRIPT */
|
||||
OFFSET(Shape), /* FINALIZE_SHAPE */
|
||||
OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */
|
||||
OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
|
||||
@ -211,6 +213,7 @@ static const AllocKind BackgroundPhaseStrings[] = {
|
||||
};
|
||||
|
||||
static const AllocKind BackgroundPhaseShapes[] = {
|
||||
FINALIZE_LAZY_SCRIPT,
|
||||
FINALIZE_SHAPE,
|
||||
FINALIZE_BASE_SHAPE,
|
||||
FINALIZE_TYPE_OBJECT
|
||||
@ -427,6 +430,8 @@ FinalizeArenas(FreeOp *fop,
|
||||
return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget);
|
||||
case FINALIZE_SCRIPT:
|
||||
return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget);
|
||||
case FINALIZE_LAZY_SCRIPT:
|
||||
return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget);
|
||||
case FINALIZE_SHAPE:
|
||||
return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget);
|
||||
case FINALIZE_BASE_SHAPE:
|
||||
@ -1435,6 +1440,7 @@ ArenaLists::queueScriptsForSweep(FreeOp *fop)
|
||||
{
|
||||
gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_SCRIPT);
|
||||
queueForForegroundSweep(fop, FINALIZE_SCRIPT);
|
||||
queueForBackgroundSweep(fop, FINALIZE_LAZY_SCRIPT);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -119,6 +119,7 @@ MapAllocToTraceKind(AllocKind kind)
|
||||
JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */
|
||||
JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */
|
||||
JSTRACE_LAZY_SCRIPT,/* FINALIZE_LAZY_SCRIPT */
|
||||
JSTRACE_SHAPE, /* FINALIZE_SHAPE */
|
||||
JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
|
||||
JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
|
||||
@ -140,6 +141,7 @@ template <> struct MapTypeToTraceKind<DebugScopeObject> { const static JSGCTrace
|
||||
template <> struct MapTypeToTraceKind<GlobalObject> { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
|
||||
template <> struct MapTypeToTraceKind<ScopeObject> { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
|
||||
template <> struct MapTypeToTraceKind<JSScript> { const static JSGCTraceKind kind = JSTRACE_SCRIPT; };
|
||||
template <> struct MapTypeToTraceKind<LazyScript> { const static JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
|
||||
template <> struct MapTypeToTraceKind<Shape> { const static JSGCTraceKind kind = JSTRACE_SHAPE; };
|
||||
template <> struct MapTypeToTraceKind<BaseShape> { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
|
||||
template <> struct MapTypeToTraceKind<UnownedBaseShape> { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
|
||||
@ -170,6 +172,7 @@ IsNurseryAllocable(AllocKind kind)
|
||||
false, /* FINALIZE_OBJECT16 */
|
||||
true, /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
false, /* FINALIZE_SCRIPT */
|
||||
false, /* FINALIZE_LAZY_SCRIPT */
|
||||
false, /* FINALIZE_SHAPE */
|
||||
false, /* FINALIZE_BASE_SHAPE */
|
||||
false, /* FINALIZE_TYPE_OBJECT */
|
||||
@ -201,6 +204,7 @@ IsBackgroundFinalized(AllocKind kind)
|
||||
false, /* FINALIZE_OBJECT16 */
|
||||
true, /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
false, /* FINALIZE_SCRIPT */
|
||||
true, /* FINALIZE_LAZY_SCRIPT */
|
||||
true, /* FINALIZE_SHAPE */
|
||||
true, /* FINALIZE_BASE_SHAPE */
|
||||
true, /* FINALIZE_TYPE_OBJECT */
|
||||
|
@ -591,6 +591,13 @@ js_NewGCScript(JSContext *cx)
|
||||
sizeof(JSScript), js::gc::TenuredHeap);
|
||||
}
|
||||
|
||||
inline js::LazyScript *
|
||||
js_NewGCLazyScript(JSContext *cx)
|
||||
{
|
||||
return js::gc::NewGCThing<js::LazyScript, js::CanGC>(cx, js::gc::FINALIZE_LAZY_SCRIPT,
|
||||
sizeof(js::LazyScript), js::gc::TenuredHeap);
|
||||
}
|
||||
|
||||
inline js::Shape *
|
||||
js_NewGCShape(JSContext *cx)
|
||||
{
|
||||
|
@ -5949,20 +5949,12 @@ JSScript::makeAnalysis(JSContext *cx)
|
||||
/* static */ bool
|
||||
JSFunction::setTypeForScriptedFunction(JSContext *cx, HandleFunction fun, bool singleton /* = false */)
|
||||
{
|
||||
JS_ASSERT(fun->nonLazyScript());
|
||||
JS_ASSERT(fun->nonLazyScript()->function() == fun);
|
||||
|
||||
if (!cx->typeInferenceEnabled())
|
||||
return true;
|
||||
|
||||
if (singleton) {
|
||||
if (!setSingletonType(cx, fun))
|
||||
return false;
|
||||
} else if (UseNewTypeForClone(fun)) {
|
||||
/*
|
||||
* Leave the default unknown-properties type for the function, it
|
||||
* should not be used by scripts or appear in type sets.
|
||||
*/
|
||||
} else {
|
||||
RootedObject funProto(cx, fun->getProto());
|
||||
TypeObject *type = cx->compartment->types.newTypeObject(cx, &FunctionClass, funProto);
|
||||
|
@ -716,7 +716,7 @@ UseNewTypeForClone(JSFunction *fun)
|
||||
if (!fun->isInterpreted())
|
||||
return false;
|
||||
|
||||
if (fun->nonLazyScript()->shouldCloneAtCallsite)
|
||||
if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite)
|
||||
return true;
|
||||
|
||||
if (fun->isArrow())
|
||||
|
@ -57,6 +57,7 @@ ZoneStats::GCHeapThingsSize()
|
||||
size_t n = 0;
|
||||
n += gcHeapStringsNormal;
|
||||
n += gcHeapStringsShort;
|
||||
n += gcHeapLazyScripts;
|
||||
n += gcHeapTypeObjects;
|
||||
n += gcHeapIonCodes;
|
||||
|
||||
@ -271,6 +272,13 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_LAZY_SCRIPT: {
|
||||
LazyScript *lazy = static_cast<LazyScript *>(thing);
|
||||
zStats->gcHeapLazyScripts += thingSize;
|
||||
zStats->lazyScripts += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_IONCODE: {
|
||||
#ifdef JS_ION
|
||||
zStats->gcHeapIonCodes += thingSize;
|
||||
|
@ -487,6 +487,12 @@ IsLocalOp(JSOp op)
|
||||
return JOF_OPTYPE(op) == JOF_LOCAL;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsAliasedVarOp(JSOp op)
|
||||
{
|
||||
return JOF_OPTYPE(op) == JOF_SCOPECOORD;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsGlobalOp(JSOp op)
|
||||
{
|
||||
|
@ -83,6 +83,7 @@ class RegExpShared;
|
||||
class RegExpStatics;
|
||||
class MatchPairs;
|
||||
class PropertyName;
|
||||
class LazyScript;
|
||||
|
||||
enum RegExpFlag
|
||||
{
|
||||
|
@ -150,9 +150,10 @@ typedef enum {
|
||||
JSTRACE_SCRIPT,
|
||||
|
||||
/*
|
||||
* Trace kinds internal to the engine. The embedding can only them if it
|
||||
* implements JSTraceCallback.
|
||||
* Trace kinds internal to the engine. The embedding can only see them if
|
||||
* it implements JSTraceCallback.
|
||||
*/
|
||||
JSTRACE_LAZY_SCRIPT,
|
||||
JSTRACE_IONCODE,
|
||||
JSTRACE_SHAPE,
|
||||
JSTRACE_BASE_SHAPE,
|
||||
|
@ -3069,7 +3069,9 @@ reflect_parse(JSContext *cx, uint32_t argc, jsval *vp)
|
||||
size_t length = stable->length();
|
||||
CompileOptions options(cx);
|
||||
options.setFileAndLine(filename, lineno);
|
||||
Parser<FullParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
|
||||
options.setCanLazilyParse(false);
|
||||
Parser<FullParseHandler> parser(cx, options, chars.get(), length,
|
||||
/* foldConstants = */ false, NULL, NULL);
|
||||
if (!parser.init())
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -1213,17 +1213,15 @@ SourceDataCache::purge()
|
||||
map_ = NULL;
|
||||
}
|
||||
|
||||
JSStableString *
|
||||
ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
|
||||
const jschar *
|
||||
ScriptSource::chars(JSContext *cx)
|
||||
{
|
||||
const jschar *chars;
|
||||
#ifdef USE_ZLIB
|
||||
Rooted<JSStableString *> cached(cx, NULL);
|
||||
#endif
|
||||
#ifdef JS_THREADSAFE
|
||||
if (!ready()) {
|
||||
chars = cx->runtime->sourceCompressorThread.currentChars();
|
||||
} else
|
||||
if (!ready())
|
||||
return cx->runtime->sourceCompressorThread.currentChars();
|
||||
#endif
|
||||
#ifdef USE_ZLIB
|
||||
if (compressed()) {
|
||||
@ -1247,14 +1245,21 @@ ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
|
||||
}
|
||||
cx->runtime->sourceDataCache.put(this, cached);
|
||||
}
|
||||
chars = cached->chars().get();
|
||||
JS_ASSERT(chars);
|
||||
} else {
|
||||
chars = data.source;
|
||||
return cached->chars().get();
|
||||
}
|
||||
return data.source;
|
||||
#else
|
||||
chars = data.source;
|
||||
return data.source;
|
||||
#endif
|
||||
}
|
||||
|
||||
JSStableString *
|
||||
ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
|
||||
{
|
||||
JS_ASSERT(start <= stop);
|
||||
const jschar *chars = this->chars(cx);
|
||||
if (!chars)
|
||||
return NULL;
|
||||
JSFlatString *flatStr = js_NewStringCopyN<CanGC>(cx, chars + start, stop - start);
|
||||
if (!flatStr)
|
||||
return NULL;
|
||||
@ -1667,6 +1672,8 @@ JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun
|
||||
const CompileOptions &options, unsigned staticLevel,
|
||||
JS::HandleScriptSource sourceObject, uint32_t bufStart, uint32_t bufEnd)
|
||||
{
|
||||
JS_ASSERT(bufStart <= bufEnd);
|
||||
|
||||
RootedScript script(cx, js_NewGCScript(cx));
|
||||
if (!script)
|
||||
return NULL;
|
||||
@ -1958,7 +1965,7 @@ JSScript::enclosingScriptsCompiledSuccessfully() const
|
||||
while (enclosing) {
|
||||
if (enclosing->isFunction()) {
|
||||
JSFunction *fun = enclosing->toFunction();
|
||||
if (!fun->hasScript())
|
||||
if (!fun->hasScript() || !fun->nonLazyScript())
|
||||
return false;
|
||||
enclosing = fun->nonLazyScript()->enclosingStaticScope();
|
||||
} else {
|
||||
@ -2294,6 +2301,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
|
||||
clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
|
||||
} else if (obj->isFunction()) {
|
||||
RootedFunction innerFun(cx, obj->toFunction());
|
||||
if (!innerFun->getOrCreateScript(cx))
|
||||
return NULL;
|
||||
RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
|
||||
StaticScopeIter ssi(cx, staticScope);
|
||||
RootedObject enclosingScope(cx);
|
||||
@ -2736,6 +2745,31 @@ JSScript::markChildren(JSTracer *trc)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
LazyScript::markChildren(JSTracer *trc)
|
||||
{
|
||||
if (parent_)
|
||||
MarkScriptUnbarriered(trc, &parent_, "lazyScriptParent");
|
||||
|
||||
if (script_)
|
||||
MarkScriptUnbarriered(trc, &script_, "lazyScript");
|
||||
|
||||
HeapPtrAtom *freeVariables = this->freeVariables();
|
||||
for (size_t i = 0; i < numFreeVariables(); i++)
|
||||
MarkString(trc, &freeVariables[i], "lazyScriptFreeVariable");
|
||||
|
||||
HeapPtrFunction *innerFunctions = this->innerFunctions();
|
||||
for (size_t i = 0; i < numInnerFunctions(); i++)
|
||||
MarkObject(trc, &innerFunctions[i], "lazyScriptInnerFunction");
|
||||
}
|
||||
|
||||
void
|
||||
LazyScript::finalize(FreeOp *fop)
|
||||
{
|
||||
if (table_)
|
||||
fop->free_(table_);
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::setArgumentsHasVarBinding()
|
||||
{
|
||||
@ -2882,6 +2916,29 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
|
||||
return argsObjAliasesFormals() && !formalIsAliased(argSlot);
|
||||
}
|
||||
|
||||
/* static */ LazyScript *
|
||||
LazyScript::Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
|
||||
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
|
||||
{
|
||||
JS_ASSERT(begin <= end);
|
||||
|
||||
size_t bytes = (numFreeVariables * sizeof(HeapPtrAtom))
|
||||
+ (numInnerFunctions * sizeof(HeapPtrFunction));
|
||||
|
||||
void *table = NULL;
|
||||
if (bytes) {
|
||||
table = cx->malloc_(bytes);
|
||||
if (!table)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LazyScript *res = js_NewGCLazyScript(cx);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
return new (res) LazyScript(table, numFreeVariables, numInnerFunctions, begin, end, lineno, column);
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::updateBaselineOrIonRaw()
|
||||
{
|
||||
|
@ -336,6 +336,7 @@ class JSScript : public js::gc::Cell
|
||||
|
||||
uint32_t natoms; /* length of atoms array */
|
||||
|
||||
/* Range of characters in scriptSource which contains this script's source. */
|
||||
uint32_t sourceStart;
|
||||
uint32_t sourceEnd;
|
||||
|
||||
@ -416,6 +417,9 @@ class JSScript : public js::gc::Cell
|
||||
bool isCachedEval:1; /* script came from eval(), and is in eval cache */
|
||||
bool uninlineable:1; /* script is considered uninlineable by analysis */
|
||||
|
||||
/* Set for functions defined at the top level within an 'eval' script. */
|
||||
bool directlyInsideEval:1;
|
||||
|
||||
/* script is attempted to be cloned anew at each callsite. This is
|
||||
temporarily needed for ParallelArray selfhosted code until type
|
||||
information can be made context sensitive. See discussion in
|
||||
@ -792,6 +796,11 @@ class JSScript : public js::gc::Cell
|
||||
return arr->vector[index];
|
||||
}
|
||||
|
||||
size_t innerObjectsStart() {
|
||||
// The first object contains the caller if savedCallerFun is used.
|
||||
return savedCallerFun ? 1 : 0;
|
||||
}
|
||||
|
||||
JSObject *getObject(jsbytecode *pc) {
|
||||
JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length);
|
||||
return getObject(GET_UINT32_INDEX(pc));
|
||||
@ -1041,6 +1050,7 @@ struct ScriptSource
|
||||
JS_ASSERT(hasSourceData());
|
||||
return argumentsNotIncluded_;
|
||||
}
|
||||
const jschar *chars(JSContext *cx);
|
||||
JSStableString *substring(JSContext *cx, uint32_t start, uint32_t stop);
|
||||
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
|
||||
|
||||
@ -1103,6 +1113,161 @@ class ScriptSourceObject : public JSObject {
|
||||
static const uint32_t SOURCE_SLOT = 0;
|
||||
};
|
||||
|
||||
// Information about a script which may be (or has been) lazily compiled to
|
||||
// bytecode from its source.
|
||||
class LazyScript : public js::gc::Cell
|
||||
{
|
||||
// Immediate parent in which the script is nested, or NULL if the parent
|
||||
// has not been compiled yet. Lazy scripts are always functions within a
|
||||
// global or eval script so there will be a parent.
|
||||
JSScript *parent_;
|
||||
|
||||
// If non-NULL, the script has been compiled and this is a forwarding
|
||||
// pointer to the result.
|
||||
JSScript *script_;
|
||||
|
||||
// Heap allocated table with any free variables or inner functions.
|
||||
void *table_;
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
uint32_t padding;
|
||||
#endif
|
||||
|
||||
uint32_t numFreeVariables_;
|
||||
uint32_t numInnerFunctions_ : 26;
|
||||
|
||||
bool strict_ : 1;
|
||||
bool bindingsAccessedDynamically_ : 1;
|
||||
bool hasDebuggerStatement_ : 1;
|
||||
bool directlyInsideEval_:1;
|
||||
bool hasBeenCloned_:1;
|
||||
|
||||
// Source location for the script.
|
||||
uint32_t begin_;
|
||||
uint32_t end_;
|
||||
uint32_t lineno_;
|
||||
uint32_t column_;
|
||||
|
||||
LazyScript(void *table, uint32_t numFreeVariables, uint32_t numInnerFunctions,
|
||||
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
|
||||
: parent_(NULL),
|
||||
script_(NULL),
|
||||
table_(table),
|
||||
numFreeVariables_(numFreeVariables),
|
||||
numInnerFunctions_(numInnerFunctions),
|
||||
strict_(false),
|
||||
bindingsAccessedDynamically_(false),
|
||||
hasDebuggerStatement_(false),
|
||||
directlyInsideEval_(false),
|
||||
hasBeenCloned_(false),
|
||||
begin_(begin),
|
||||
end_(end),
|
||||
lineno_(lineno),
|
||||
column_(column)
|
||||
{
|
||||
JS_ASSERT(begin <= end);
|
||||
}
|
||||
|
||||
public:
|
||||
static LazyScript *Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
|
||||
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
|
||||
|
||||
void initParent(JSScript *parent) {
|
||||
JS_ASSERT(parent && !parent_);
|
||||
parent_ = parent;
|
||||
}
|
||||
JSScript *parent() const {
|
||||
return parent_;
|
||||
}
|
||||
|
||||
void initScript(JSScript *script) {
|
||||
JS_ASSERT(script && !script_);
|
||||
script_ = script;
|
||||
}
|
||||
JSScript *maybeScript() {
|
||||
return script_;
|
||||
}
|
||||
|
||||
uint32_t numFreeVariables() const {
|
||||
return numFreeVariables_;
|
||||
}
|
||||
HeapPtrAtom *freeVariables() {
|
||||
return (HeapPtrAtom *)table_;
|
||||
}
|
||||
|
||||
uint32_t numInnerFunctions() const {
|
||||
return numInnerFunctions_;
|
||||
}
|
||||
HeapPtrFunction *innerFunctions() {
|
||||
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
|
||||
}
|
||||
|
||||
bool strict() const {
|
||||
return strict_;
|
||||
}
|
||||
void setStrict() {
|
||||
strict_ = true;
|
||||
}
|
||||
|
||||
bool bindingsAccessedDynamically() const {
|
||||
return bindingsAccessedDynamically_;
|
||||
}
|
||||
void setBindingsAccessedDynamically() {
|
||||
bindingsAccessedDynamically_ = true;
|
||||
}
|
||||
|
||||
bool hasDebuggerStatement() const {
|
||||
return hasDebuggerStatement_;
|
||||
}
|
||||
void setHasDebuggerStatement() {
|
||||
hasDebuggerStatement_ = true;
|
||||
}
|
||||
|
||||
bool directlyInsideEval() const {
|
||||
return directlyInsideEval_;
|
||||
}
|
||||
void setDirectlyInsideEval() {
|
||||
directlyInsideEval_ = true;
|
||||
}
|
||||
|
||||
bool hasBeenCloned() const {
|
||||
return hasBeenCloned_;
|
||||
}
|
||||
void setHasBeenCloned() {
|
||||
hasBeenCloned_ = true;
|
||||
}
|
||||
|
||||
ScriptSource *source() const {
|
||||
return parent()->scriptSource();
|
||||
}
|
||||
uint32_t begin() const {
|
||||
return begin_;
|
||||
}
|
||||
uint32_t end() const {
|
||||
return end_;
|
||||
}
|
||||
uint32_t lineno() const {
|
||||
return lineno_;
|
||||
}
|
||||
uint32_t column() const {
|
||||
return column_;
|
||||
}
|
||||
|
||||
Zone *zone() const {
|
||||
return Cell::tenuredZone();
|
||||
}
|
||||
|
||||
void markChildren(JSTracer *trc);
|
||||
void finalize(js::FreeOp *fop);
|
||||
|
||||
size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf)
|
||||
{
|
||||
return mallocSizeOf(table_);
|
||||
}
|
||||
|
||||
static inline void writeBarrierPre(LazyScript *lazy);
|
||||
};
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* Background thread to compress JS source code. This happens only while parsing
|
||||
|
@ -184,6 +184,23 @@ JSScript::writeBarrierPost(JSScript *script, void *addr)
|
||||
{
|
||||
}
|
||||
|
||||
/* static */ inline void
|
||||
js::LazyScript::writeBarrierPre(js::LazyScript *lazy)
|
||||
{
|
||||
#ifdef JSGC_INCREMENTAL
|
||||
if (!lazy)
|
||||
return;
|
||||
|
||||
JS::Zone *zone = lazy->zone();
|
||||
if (zone->needsBarrier()) {
|
||||
JS_ASSERT(!zone->rt->isHeapBusy());
|
||||
js::LazyScript *tmp = lazy;
|
||||
MarkLazyScriptUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
|
||||
JS_ASSERT(tmp == lazy);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline JSPrincipals *
|
||||
JSScript::principals()
|
||||
{
|
||||
|
@ -3168,7 +3168,7 @@ Parse(JSContext *cx, unsigned argc, jsval *vp)
|
||||
Parser<FullParseHandler> parser(cx, options,
|
||||
JS_GetStringCharsZ(cx, scriptContents),
|
||||
JS_GetStringLength(scriptContents),
|
||||
/* foldConstants = */ true);
|
||||
/* foldConstants = */ true, NULL, NULL);
|
||||
if (!parser.init())
|
||||
return false;
|
||||
|
||||
@ -3208,8 +3208,7 @@ SyntaxParse(JSContext *cx, unsigned argc, jsval *vp)
|
||||
|
||||
const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
|
||||
size_t length = JS_GetStringLength(scriptContents);
|
||||
Parser<frontend::SyntaxParseHandler> parser(cx, options, chars, length,
|
||||
/* foldConstants = */ false);
|
||||
Parser<frontend::SyntaxParseHandler> parser(cx, options, chars, length, false, NULL, NULL);
|
||||
if (!parser.init())
|
||||
return false;
|
||||
|
||||
@ -3217,7 +3216,7 @@ SyntaxParse(JSContext *cx, unsigned argc, jsval *vp)
|
||||
if (cx->isExceptionPending())
|
||||
return false;
|
||||
|
||||
if (!succeeded && !parser.hadUnknownResult()) {
|
||||
if (!succeeded && !parser.hadAbortedSyntaxParse()) {
|
||||
// If no exception is posted, either there was an OOM or a language
|
||||
// feature unhandled by the syntax parser was encountered.
|
||||
JS_ASSERT(cx->runtime->hadOutOfMemory);
|
||||
|
@ -2892,6 +2892,16 @@ DebuggerScript_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EnsureFunctionHasScript(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
if (fun->isInterpretedLazy()) {
|
||||
AutoCompartment ac(cx, fun);
|
||||
return fun->getOrCreateScript(cx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@ -2905,16 +2915,21 @@ DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
|
||||
/*
|
||||
* script->savedCallerFun indicates that this is a direct eval script
|
||||
* and the calling function is stored as script->objects()->vector[0].
|
||||
* It is not really a child script of this script, so skip it.
|
||||
* It is not really a child script of this script, so skip it using
|
||||
* innerObjectsStart().
|
||||
*/
|
||||
ObjectArray *objects = script->objects();
|
||||
RootedFunction fun(cx);
|
||||
RootedScript funScript(cx);
|
||||
RootedObject obj(cx), s(cx);
|
||||
for (uint32_t i = script->savedCallerFun ? 1 : 0; i < objects->length; i++) {
|
||||
for (uint32_t i = script->innerObjectsStart(); i < objects->length; i++) {
|
||||
obj = objects->vector[i];
|
||||
if (obj->isFunction()) {
|
||||
fun = static_cast<JSFunction *>(obj.get());
|
||||
|
||||
if (!EnsureFunctionHasScript(cx, fun))
|
||||
return false;
|
||||
|
||||
funScript = fun->nonLazyScript();
|
||||
s = dbg->wrapScript(cx, funScript);
|
||||
if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
|
||||
@ -4122,7 +4137,8 @@ js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFr
|
||||
.setCompileAndGo(true)
|
||||
.setForEval(true)
|
||||
.setNoScriptRval(false)
|
||||
.setFileAndLine(filename, lineno);
|
||||
.setFileAndLine(filename, lineno)
|
||||
.setCanLazilyParse(false);
|
||||
RootedScript callerScript(cx, frame ? frame.script() : NULL);
|
||||
RootedScript script(cx, frontend::CompileScript(cx, env, callerScript,
|
||||
options, chars.get(), length,
|
||||
@ -4455,6 +4471,9 @@ DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
|
||||
result->ensureDenseInitializedLength(cx, 0, fun->nargs);
|
||||
|
||||
if (fun->isInterpreted()) {
|
||||
if (!EnsureFunctionHasScript(cx, fun))
|
||||
return false;
|
||||
|
||||
JS_ASSERT(fun->nargs == fun->nonLazyScript()->bindings.numArgs());
|
||||
|
||||
if (fun->nargs > 0) {
|
||||
@ -4496,6 +4515,9 @@ DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!EnsureFunctionHasScript(cx, fun))
|
||||
return false;
|
||||
|
||||
RootedScript script(cx, fun->nonLazyScript());
|
||||
RootedObject scriptObject(cx, dbg->wrapScript(cx, script));
|
||||
if (!scriptObject)
|
||||
|
@ -615,12 +615,11 @@ js::ParallelDo::enqueueInitialScript(ExecutionStatus *status)
|
||||
if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
|
||||
return sequentialExecution(true, status);
|
||||
|
||||
if (callee->isInterpretedLazy() && !callee->initializeLazyScript(cx_))
|
||||
return sequentialExecution(true, status);
|
||||
|
||||
// If this function has not been run enough to enable parallel
|
||||
// execution, perform a warmup.
|
||||
RootedScript script(cx_, callee->nonLazyScript());
|
||||
RootedScript script(cx_, callee->getOrCreateScript(cx_));
|
||||
if (!script)
|
||||
return RedLight;
|
||||
if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
|
||||
if (warmupExecution(status) == RedLight)
|
||||
return RedLight;
|
||||
|
@ -517,6 +517,7 @@ JSRuntime::initSelfHosting(JSContext *cx)
|
||||
CompileOptions options(cx);
|
||||
options.setFileAndLine("self-hosted", 1);
|
||||
options.setSelfHostingMode(true);
|
||||
options.setCanLazilyParse(false);
|
||||
options.setSourcePolicy(CompileOptions::NO_SOURCE);
|
||||
options.setVersion(JSVERSION_LATEST);
|
||||
|
||||
|
@ -1662,6 +1662,11 @@ ReportZoneStats(const JS::ZoneStats &zStats,
|
||||
"heap that holds over-sized string headers, in which "
|
||||
"string characters are stored inline.");
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/lazy-scripts"),
|
||||
zStats.gcHeapLazyScripts,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that represents scripts which haven't executed yet.");
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/type-objects"),
|
||||
zStats.gcHeapTypeObjects,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
@ -1673,6 +1678,11 @@ ReportZoneStats(const JS::ZoneStats &zStats,
|
||||
"heap that holds references to executable code pools "
|
||||
"used by the IonMonkey JIT.");
|
||||
|
||||
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts"),
|
||||
zStats.lazyScripts,
|
||||
"Memory holding miscellaneous additional information associated with lazy "
|
||||
"scripts.");
|
||||
|
||||
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects"),
|
||||
zStats.typeObjects,
|
||||
"Memory holding miscellaneous additional information associated with type "
|
||||
|
@ -722,6 +722,7 @@ DescribeGCThing(bool isMarked, void *p, JSGCTraceKind traceKind,
|
||||
"Object",
|
||||
"String",
|
||||
"Script",
|
||||
"LazyScript",
|
||||
"IonCode",
|
||||
"Shape",
|
||||
"BaseShape",
|
||||
|
52
mfbt/Util.h
52
mfbt/Util.h
@ -196,6 +196,58 @@ class Maybe
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
template<class T1, class T2, class T3, class T4, class T5>
|
||||
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) {
|
||||
MOZ_ASSERT(!constructed);
|
||||
::new (storage.addr()) T(t1, t2, t3, t4, t5);
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
template<class T1, class T2, class T3, class T4, class T5,
|
||||
class T6>
|
||||
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
|
||||
const T6& t6) {
|
||||
MOZ_ASSERT(!constructed);
|
||||
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6);
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
template<class T1, class T2, class T3, class T4, class T5,
|
||||
class T6, class T7>
|
||||
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
|
||||
const T6& t6, const T7& t7) {
|
||||
MOZ_ASSERT(!constructed);
|
||||
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7);
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
template<class T1, class T2, class T3, class T4, class T5,
|
||||
class T6, class T7, class T8>
|
||||
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
|
||||
const T6& t6, const T7& t7, const T8& t8) {
|
||||
MOZ_ASSERT(!constructed);
|
||||
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
template<class T1, class T2, class T3, class T4, class T5,
|
||||
class T6, class T7, class T8, class T9>
|
||||
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
|
||||
const T6& t6, const T7& t7, const T8& t8, const T9& t9) {
|
||||
MOZ_ASSERT(!constructed);
|
||||
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8, t9);
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
template<class T1, class T2, class T3, class T4, class T5,
|
||||
class T6, class T7, class T8, class T9, class T10>
|
||||
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
|
||||
const T6& t6, const T7& t7, const T8& t8, const T9& t9, const T10& t10) {
|
||||
MOZ_ASSERT(!constructed);
|
||||
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10);
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
T* addr() {
|
||||
MOZ_ASSERT(constructed);
|
||||
return &asT();
|
||||
|
Loading…
Reference in New Issue
Block a user