author | Jan de Mooij <jdemooij@mozilla.com> |
Fri, 18 Jan 2019 13:14:58 +0000 | |
changeset 454437 | 1d260750a87e00de63750365d7c0c6fb34241c58 |
parent 454436 | db7342a96476113aa1428e345e704d64c02ab893 |
child 454438 | 83d6478d261167ec129ffc5286a60bfbb39d0c31 |
push id | 35397 |
push user | opoprus@mozilla.com |
push date | Sat, 19 Jan 2019 03:35:41 +0000 |
treeherder | mozilla-central@57dc8bbbc38f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | arai |
bugs | 1284719 |
milestone | 66.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -93,17 +93,17 @@ BytecodeEmitter::BytecodeEmitter(Bytecod uint32_t lineNum, EmitterMode emitterMode) : sc(sc), cx(sc->context), parent(parent), script(cx, script), lazyScript(cx, lazyScript), prologue(cx, lineNum), main(cx, lineNum), - current(&main), + current(&prologue), parser(nullptr), atomIndices(cx->frontendCollectionPool()), firstLine(lineNum), maxFixedSlots(0), maxStackDepth(0), stackDepth(0), emitLevel(0), bodyScopeIndex(UINT32_MAX), @@ -437,16 +437,21 @@ bool BytecodeEmitter::emitCheckIsCallabl } static inline unsigned LengthOfSetLine(unsigned line) { return 1 /* SRC_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1); } /* Updates line number notes, not column notes. */ bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) { + // Don't emit line/column number notes in the prologue. + if (inPrologue()) { + return true; + } + ErrorReporter* er = &parser->errorReporter(); bool onThisLine; if (!er->isOnThisLine(offset, currentLine(), &onThisLine)) { er->errorNoOffset(JSMSG_OUT_OF_MEMORY); return false; } if (!onThisLine) { @@ -482,16 +487,21 @@ bool BytecodeEmitter::updateLineNumberNo } /* Updates the line number and column number information in the source notes. */ bool BytecodeEmitter::updateSourceCoordNotes(uint32_t offset) { if (!updateLineNumberNotes(offset)) { return false; } + // Don't emit line/column number notes in the prologue. + if (inPrologue()) { + return true; + } + uint32_t columnIndex = parser->errorReporter().columnAt(offset); ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(current->lastColumn); if (colspan != 0) { // If the column span is so large that we can't store it, then just // discard this information. This can happen with minimized or otherwise // machine-generated code. Even gigantic column numbers are still // valuable if you have a source map to relate them to something real; // but it's better to fail soft here. @@ -2260,69 +2270,97 @@ bool BytecodeEmitter::emitSetThis(Binary if (!noe.emitAssignment()) { // [stack] NEWTHIS return false; } return true; } +bool BytecodeEmitter::defineHoistedTopLevelFunctions(ParseNode* body) { + MOZ_ASSERT(inPrologue()); + MOZ_ASSERT(sc->isGlobalContext() || (sc->isEvalContext() && !sc->strict())); + MOZ_ASSERT(body->is<LexicalScopeNode>() || body->is<ListNode>()); + + if (body->is<LexicalScopeNode>()) { + body = body->as<LexicalScopeNode>().scopeBody(); + MOZ_ASSERT(body->is<ListNode>()); + } + + if (!body->as<ListNode>().hasTopLevelFunctionDeclarations()) { + return true; + } + + return emitHoistedFunctionsInList(&body->as<ListNode>()); +} + bool BytecodeEmitter::emitScript(ParseNode* body) { AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser->errorReporter(), body); setScriptStartOffsetIfUnset(body->pn_pos); + MOZ_ASSERT(inPrologue()); + TDZCheckCache tdzCache(this); EmitterScope emitterScope(this); if (sc->isGlobalContext()) { - switchToPrologue(); if (!emitterScope.enterGlobal(this, sc->asGlobalContext())) { return false; } - switchToMain(); } else if (sc->isEvalContext()) { - switchToPrologue(); if (!emitterScope.enterEval(this, sc->asEvalContext())) { return false; } - switchToMain(); } else { MOZ_ASSERT(sc->isModuleContext()); if (!emitterScope.enterModule(this, sc->asModuleContext())) { return false; } } setFunctionBodyEndPos(body->pn_pos); - if (sc->isEvalContext() && !sc->strict() && body->is<LexicalScopeNode>() && + bool isSloppyEval = sc->isEvalContext() && !sc->strict(); + if (isSloppyEval && body->is<LexicalScopeNode>() && !body->as<LexicalScopeNode>().isEmptyScope()) { // Sloppy eval scripts may need to emit DEFFUNs in the prologue. If there is // an immediately enclosed lexical scope, we need to enter the lexical // scope in the prologue for the DEFFUNs to pick up the right // environment chain. EmitterScope lexicalEmitterScope(this); LexicalScopeNode* scope = &body->as<LexicalScopeNode>(); - switchToPrologue(); if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical, scope->scopeBindings())) { return false; } + + if (!defineHoistedTopLevelFunctions(scope->scopeBody())) { + return false; + } + switchToMain(); if (!emitLexicalScopeBody(scope->scopeBody())) { return false; } if (!lexicalEmitterScope.leave(this)) { return false; } } else { + if (sc->isGlobalContext() || isSloppyEval) { + if (!defineHoistedTopLevelFunctions(body)) { + return false; + } + } + + switchToMain(); + if (!emitTree(body)) { return false; } } if (!updateSourceCoordNotes(body->pn_pos.end)) { return false; } @@ -2345,16 +2383,17 @@ bool BytecodeEmitter::emitScript(ParseNo tellDebuggerAboutCompiledScript(cx); return true; } bool BytecodeEmitter::emitFunctionScript(CodeNode* funNode, TopLevelFunction isTopLevel) { + MOZ_ASSERT(inPrologue()); MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function)); ParseNode* body = funNode->body(); MOZ_ASSERT(body->isKind(ParseNodeKind::ParamsBody)); FunctionBox* funbox = sc->asFunctionBox(); AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser->errorReporter(), funbox); setScriptStartOffsetIfUnset(body->pn_pos); @@ -2378,21 +2417,19 @@ bool BytecodeEmitter::emitFunctionScript * * Also mark the script so that initializers created within it may be * given more precise types. */ if (isRunOnceLambda()) { script->setTreatAsRunOnce(); MOZ_ASSERT(!script->hasRunOnce()); - switchToPrologue(); if (!emit1(JSOP_RUNONCE)) { return false; } - switchToMain(); } setFunctionBodyEndPos(body->pn_pos); if (!emitTree(body)) { return false; } if (!updateSourceCoordNotes(body->pn_pos.end)) { @@ -4641,16 +4678,23 @@ if_again: } return true; } bool BytecodeEmitter::emitHoistedFunctionsInList(ListNode* stmtList) { MOZ_ASSERT(stmtList->hasTopLevelFunctionDeclarations()); + // We can call this multiple times for sloppy eval scopes. + if (stmtList->emittedTopLevelFunctionDeclarations()) { + return true; + } + + stmtList->setEmittedTopLevelFunctionDeclarations(); + for (ParseNode* stmt : stmtList->contents()) { ParseNode* maybeFun = stmt; if (!sc->strict()) { while (maybeFun->isKind(ParseNodeKind::LabelStmt)) { maybeFun = maybeFun->as<LabeledStatement>().statement(); } } @@ -5638,31 +5682,30 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e RootedModuleObject module(cx, sc->asModuleContext()->module()); if (!module->noteFunctionDeclaration(cx, name, fun)) { return false; } } else { MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext()); MOZ_ASSERT(funNode->getOp() == JSOP_NOP); - switchToPrologue(); + MOZ_ASSERT(inPrologue()); if (funbox->isAsync()) { if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(), fun->isGenerator())) { return false; } } else { if (!emitIndex32(JSOP_LAMBDA, index)) { return false; } } if (!emit1(JSOP_DEFFUN)) { return false; } - switchToMain(); } } else { // For functions nested within functions and blocks, make a lambda and // initialize the binding name of the function in the current scope. NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize); if (!noe.prepareForRhs()) { return false; @@ -8048,23 +8091,26 @@ bool BytecodeEmitter::emitTypeof(UnaryNo } return emit1(op); } bool BytecodeEmitter::emitFunctionFormalParametersAndBody( ListNode* paramsBody) { MOZ_ASSERT(paramsBody->isKind(ParseNodeKind::ParamsBody)); + MOZ_ASSERT(inPrologue()); ParseNode* funBody = paramsBody->last(); FunctionBox* funbox = sc->asFunctionBox(); TDZCheckCache tdzCache(this); if (funbox->hasParameterExprs) { + switchToMain(); + EmitterScope funEmitterScope(this); if (!funEmitterScope.enterFunction(this, funbox)) { return false; } if (!emitInitializeFunctionSpecialNames()) { return false; } @@ -8143,17 +8189,16 @@ bool BytecodeEmitter::emitFunctionFormal // // One caveat is that Debugger considers ops in the prologue to be // unreachable (i.e. cannot set a breakpoint on it). If there are no // parameter exprs, any unobservable environment ops (like pushing the // call object, setting '.this', etc) need to go in the prologue, else it // messes up breakpoint tests. EmitterScope emitterScope(this); - switchToPrologue(); if (!emitterScope.enterFunction(this, funbox)) { return false; } if (!emitInitializeFunctionSpecialNames()) { return false; } switchToMain();
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -395,18 +395,20 @@ struct MOZ_STACK_CLASS BytecodeEmitter { return current->code.begin() + offset; } ptrdiff_t offset() const { return current->code.end() - current->code.begin(); } ptrdiff_t prologueOffset() const { return prologue.code.end() - prologue.code.begin(); } - void switchToMain() { current = &main; } - void switchToPrologue() { current = &prologue; } + void switchToMain() { + MOZ_ASSERT(inPrologue()); + current = &main; + } bool inPrologue() const { return current == &prologue; } SrcNotesVector& notes() const { // Prologue shouldn't have source notes. MOZ_ASSERT(!inPrologue()); return current->notes; } ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; } @@ -769,16 +771,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter { return emitIteratorCloseInScope(*innermostEmitterScope(), iterKind, completionKind, allowSelfHosted); } template <typename InnerEmitter> MOZ_MUST_USE bool wrapWithDestructuringTryNote(int32_t iterDepth, InnerEmitter emitter); + MOZ_MUST_USE bool defineHoistedTopLevelFunctions(ParseNode* body); + // Check if the value on top of the stack is "undefined". If so, replace // that value on the stack with the value defined by |defaultExpr|. // |pattern| is a lhs node of the default expression. If it's an // identifier and |defaultExpr| is an anonymous function, |SetFunctionName| // is called at compile time. MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern); MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name);
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -1149,16 +1149,19 @@ class ListNode : public ParseNode { // * object/class has __proto__ // * object/class has property which is known not to be constant // * object/class shorthand property // * object/class spread property // * object/class has method // * object/class has computed property static constexpr uint32_t hasNonConstInitializerBit = 0x04; + // Flag set by the emitter after emitting top-level function statements. + static constexpr uint32_t emittedTopLevelFunctionDeclarationsBit = 0x08; + void checkConsistency() const #ifndef DEBUG { } #endif ; public: @@ -1222,32 +1225,44 @@ class ListNode : public ParseNode { bool empty() const { return count() == 0; } MOZ_MUST_USE bool hasTopLevelFunctionDeclarations() const { MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); return pn_u.list.xflags & hasTopLevelFunctionDeclarationsBit; } + MOZ_MUST_USE bool emittedTopLevelFunctionDeclarations() const { + MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); + MOZ_ASSERT(hasTopLevelFunctionDeclarations()); + return pn_u.list.xflags & emittedTopLevelFunctionDeclarationsBit; + } + MOZ_MUST_USE bool hasArrayHoleOrSpread() const { MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr)); return pn_u.list.xflags & hasArrayHoleOrSpreadBit; } MOZ_MUST_USE bool hasNonConstInitializer() const { MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) || isKind(ParseNodeKind::ObjectExpr)); return pn_u.list.xflags & hasNonConstInitializerBit; } void setHasTopLevelFunctionDeclarations() { MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); pn_u.list.xflags |= hasTopLevelFunctionDeclarationsBit; } + void setEmittedTopLevelFunctionDeclarations() { + MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); + MOZ_ASSERT(hasTopLevelFunctionDeclarations()); + pn_u.list.xflags |= emittedTopLevelFunctionDeclarationsBit; + } + void setHasArrayHoleOrSpread() { MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr)); pn_u.list.xflags |= hasArrayHoleOrSpreadBit; } void setHasNonConstInitializer() { MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) || isKind(ParseNodeKind::ObjectExpr));