diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 261d14480e..0deed9131e 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4141,8 +4141,15 @@ Parser::statementList(YieldHandling yieldHandling) return null(); bool canHaveDirectives = pc->atBodyLevel(); - if (canHaveDirectives) + if (canHaveDirectives) { tokenStream.clearSawOctalEscape(); + } + + bool canHaveHashbangComment = pc->atTopLevel(); + if (canHaveHashbangComment) { + tokenStream.consumeOptionalHashbangComment(); + } + bool afterReturn = false; bool warnedAboutStatementsAfterReturn = false; uint32_t statementBegin = 0; diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 4a8e038d6e..b1a9bf83c5 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -470,6 +470,12 @@ class ParseContext : public Nestable return atBodyLevel() && sc_->isModuleContext(); } + // True if we are at the topmost level of an entire script or module. For + // example, in the comment on |atBodyLevel()| above, we would encounter |f1| + // and the outermost |if (cond)| at top level, and everything else would not + // be at top level. + bool atTopLevel() { return atBodyLevel() && sc_->isTopLevelContext(); } + void setIsStandaloneFunctionBody() { isStandaloneFunctionBody_ = true; } diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index bc0212054b..d938bfab7e 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -289,6 +289,16 @@ class SharedContext bool isEvalContext() { return kind_ == Kind::Eval; } inline EvalSharedContext* asEvalContext(); + bool isTopLevelContext() const { + switch (kind_) { + case Kind::Module: + case Kind::Global: + case Kind::Eval: + return true; + } + return false; + } + ThisBinding thisBinding() const { return thisBinding_; } bool hasModuleGoal() const { return hasModuleGoal_; } diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index b11c7df584..cd0e1b29e4 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1300,6 +1300,21 @@ TokenStream::putIdentInTokenbuf(const char16_t* identStart) return true; } +void +TokenStream::consumeOptionalHashbangComment() { + int c = userbuf.getRawChar(); + if (c == '#') { + if (matchChar('!')) { + // Hashbang; ignore rest of line as comment. + while ((c = getChar()) != EOF && c != '\n') + continue; + } + } + ungetChar(c); + cursor = (cursor - 1) & ntokensMask; +} + + enum FirstCharKind { // A char16_t has the 'OneChar' kind if it, by itself, constitutes a valid // token that cannot also be a prefix of a longer token. E.g. ';' has the diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index e7a3f7b808..3d4695fdea 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -428,6 +428,13 @@ class MOZ_STACK_CLASS TokenStream // asm.js reporter void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...); + /** + * Consume any hashbang comment at the start of a Script or Module, if one is + * present. Stops consuming just before any terminating LineTerminator or + * before an encoding error is encountered. + */ + void consumeOptionalHashbangComment(); + JSAtom* getRawTemplateStringAtom() { MOZ_ASSERT(currentToken().type == TOK_TEMPLATE_HEAD || currentToken().type == TOK_NO_SUBS_TEMPLATE);