--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -478,17 +478,18 @@ bool frontend::SourceAwareCompiler<Unit>
// 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 (parser->anyChars.hadError() || info.directives == newDirectives) {
return false;
}
- parser->tokenStream.seek(startPosition);
+ // Rewind to starting position to retry.
+ parser->tokenStream.rewind(startPosition);
// Assignment must be monotonic to prevent reparsing iloops
MOZ_ASSERT_IF(info.directives.strict(), newDirectives.strict());
MOZ_ASSERT_IF(info.directives.asmJS(), newDirectives.asmJS());
info.directives = newDirectives;
return true;
}
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2619,27 +2619,44 @@ GeneralParser<ParseHandler, Unit>::funct
}
// Assignment must be monotonic to prevent infinitely attempting to
// reparse.
MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
directives = newDirectives;
- tokenStream.seek(start);
+ // Rewind to retry parsing with new directives applied.
+ tokenStream.rewind(start);
// functionFormalParametersAndBody may have already set body before
// failing.
handler_.setFunctionFormalParametersAndBody(funNode, null());
}
return funNode;
}
template <typename Unit>
+bool Parser<FullParseHandler, Unit>::advancePastSyntaxParsedFunction(
+ AutoKeepAtoms& keepAtoms, SyntaxParser* syntaxParser) {
+ MOZ_ASSERT(getSyntaxParser() == syntaxParser);
+
+ // Advance this parser over tokens processed by the syntax parser.
+ Position currentSyntaxPosition(keepAtoms_, syntaxParser->tokenStream);
+ if (!tokenStream.fastForward(currentSyntaxPosition, syntaxParser->anyChars)) {
+ return false;
+ }
+
+ anyChars.adoptState(syntaxParser->anyChars);
+ tokenStream.adoptState(syntaxParser->tokenStream);
+ return true;
+}
+
+template <typename Unit>
bool Parser<FullParseHandler, Unit>::trySyntaxParseInnerFunction(
FunctionNode** funNode, HandleFunction fun, uint32_t toStringStart,
InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives) {
// Try a syntax parse for this inner function.
do {
// If we're assuming this function is an IIFE, always perform a full
@@ -2654,19 +2671,27 @@ bool Parser<FullParseHandler, Unit>::try
SyntaxParser* syntaxParser = getSyntaxParser();
if (!syntaxParser) {
break;
}
UsedNameTracker::RewindToken token = usedNames_.getRewindToken();
- // Move the syntax parser to the current position in the stream.
+ // Move the syntax parser to the current position in the stream. In the
+ // common case this seeks forward, but it'll also seek backward *at least*
+ // when arrow functions appear inside arrow function argument defaults
+ // (because we rewind to reparse arrow functions once we're certain they're
+ // arrow functions):
+ //
+ // var x = (y = z => 2) => q;
+ // // ^ we first seek to here to syntax-parse this function
+ // // ^ then we seek back to here to syntax-parse the outer function
Position currentPosition(keepAtoms_, tokenStream);
- if (!syntaxParser->tokenStream.seek(currentPosition, anyChars)) {
+ if (!syntaxParser->tokenStream.seekTo(currentPosition, anyChars)) {
return false;
}
// Make a FunctionBox before we enter the syntax parser, because |pn|
// still expects a FunctionBox to be attached to it during BCE, and
// the syntax parser cannot attach one to it.
FunctionBox* funbox =
newFunctionBox(*funNode, fun, toStringStart, inheritedDirectives,
@@ -2689,19 +2714,17 @@ bool Parser<FullParseHandler, Unit>::try
usedNames_.rewind(token);
MOZ_ASSERT_IF(!syntaxParser->cx_->helperThread(),
!syntaxParser->cx_->isExceptionPending());
break;
}
return false;
}
- // Advance this parser over tokens processed by the syntax parser.
- Position currentSyntaxPosition(keepAtoms_, syntaxParser->tokenStream);
- if (!tokenStream.seek(currentSyntaxPosition, syntaxParser->anyChars)) {
+ if (!advancePastSyntaxParsedFunction(keepAtoms_, syntaxParser)) {
return false;
}
// Update the end position of the parse node.
(*funNode)->pn_pos.end = anyChars.currentToken().pos.end;
// Append possible Annex B function box only upon successfully parsing.
if (tryAnnexB) {
@@ -8377,17 +8400,18 @@ typename ParseHandler::Node GeneralParse
TokenStream::SlashIsRegExp)) {
return null();
}
isArrow = tokenAfterLHS == TokenKind::Arrow;
}
if (isArrow) {
- tokenStream.seek(start);
+ // Rewind to reparse as an arrow function.
+ tokenStream.rewind(start);
TokenKind next;
if (!tokenStream.getToken(&next, TokenStream::SlashIsRegExp)) {
return null();
}
TokenPos startPos = pos();
uint32_t toStringStart = startPos.begin;
anyChars.ungetToken();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1737,16 +1737,19 @@ class MOZ_STACK_CLASS Parser<FullParseHa
bool trySyntaxParseInnerFunction(
FunctionNodeType* funNode, HandleFunction fun, uint32_t toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives);
+ MOZ_MUST_USE bool advancePastSyntaxParsedFunction(AutoKeepAtoms& keepAtoms,
+ SyntaxParser* syntaxParser);
+
bool skipLazyInnerFunction(FunctionNodeType funNode, uint32_t toStringStart,
FunctionSyntaxKind kind, bool tryAnnexB);
// Functions present only in Parser<FullParseHandler, Unit>.
// Parse the body of an eval.
//
// Eval scripts are distinguished from global scripts in that in ES6, per
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1553,17 +1553,17 @@ bool TokenStreamSpecific<Unit, AnyCharsA
cur->pos.begin = this->sourceUnits.offset();
cur->pos.end = cur->pos.begin;
MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type));
anyChars.lookahead = 0;
return true;
}
template <typename Unit, class AnyCharsAccess>
-void TokenStreamSpecific<Unit, AnyCharsAccess>::seek(const Position& pos) {
+void TokenStreamSpecific<Unit, AnyCharsAccess>::seekTo(const Position& pos) {
TokenStreamAnyChars& anyChars = anyCharsAccess();
this->sourceUnits.setAddressOfNextCodeUnit(pos.buf,
/* allowPoisoned = */ true);
anyChars.flags = pos.flags;
anyChars.lineno = pos.lineno;
anyChars.linebase = pos.linebase;
anyChars.prevLinebase = pos.prevLinebase;
@@ -1571,23 +1571,23 @@ void TokenStreamSpecific<Unit, AnyCharsA
anyChars.tokens[anyChars.cursor()] = pos.currentToken;
for (unsigned i = 0; i < anyChars.lookahead; i++) {
anyChars.tokens[anyChars.aheadCursor(1 + i)] = pos.lookaheadTokens[i];
}
}
template <typename Unit, class AnyCharsAccess>
-bool TokenStreamSpecific<Unit, AnyCharsAccess>::seek(
+bool TokenStreamSpecific<Unit, AnyCharsAccess>::seekTo(
const Position& pos, const TokenStreamAnyChars& other) {
if (!anyCharsAccess().srcCoords.fill(other.srcCoords)) {
return false;
}
- seek(pos);
+ seekTo(pos);
return true;
}
void TokenStreamAnyChars::computeErrorMetadataNoOffset(ErrorMetadata* err) {
err->isMuted = mutedErrors;
err->filename = filename_;
err->lineNumber = 0;
err->columnNumber = 0;
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -730,16 +730,29 @@ class TokenStreamAnyChars : public Token
// Push the last scanned token back into the stream.
void ungetToken() {
MOZ_ASSERT(lookahead < maxLookahead);
lookahead++;
retractCursor();
}
public:
+ void adoptState(TokenStreamAnyChars& other) {
+ // If |other| has fresh information from directives, overwrite any
+ // previously recorded directives. (There is no specification directing
+ // that last-in-source-order directive controls, sadly. We behave this way
+ // in the ordinary case, so we ought do so here too.)
+ if (auto& url = other.displayURL_) {
+ displayURL_ = std::move(url);
+ }
+ if (auto& url = other.sourceMapURL_) {
+ sourceMapURL_ = std::move(url);
+ }
+ }
+
// Compute error metadata for an error at no offset.
void computeErrorMetadataNoOffset(ErrorMetadata* err);
// ErrorReporter API Helpers
// Provide minimal set of error reporting API given we cannot use
// ErrorReportMixin here. "report" prefix is added to avoid conflict with
// ErrorReportMixin methods in TokenStream class.
@@ -1422,16 +1435,24 @@ class TokenStreamCharsShared {
}
JSAtom* drainCharBufferIntoAtom(JSContext* cx) {
JSAtom* atom = AtomizeChars(cx, charBuffer.begin(), charBuffer.length());
charBuffer.clear();
return atom;
}
+ protected:
+ void adoptState(TokenStreamCharsShared& other) {
+ // The other stream's buffer may contain information for a
+ // gotten-then-ungotten token, that we must transfer into this stream so
+ // that token's final get behaves as desired.
+ charBuffer = std::move(other.charBuffer);
+ }
+
public:
CharBuffer& getCharBuffer() { return charBuffer; }
};
inline mozilla::Span<const char> ToCharSpan(
mozilla::Span<const mozilla::Utf8Unit> codeUnits) {
static_assert(alignof(char) == alignof(mozilla::Utf8Unit),
"must have equal alignment to reinterpret_cast<>");
@@ -1999,16 +2020,20 @@ class GeneralTokenStreamChars : public S
end =
this->sourceUnits.codeUnitPtrAt(anyChars.currentToken().pos.end - 2);
} else {
// NO_SUBS_TEMPLATE is of the form |`...`| or |}...`|
end =
this->sourceUnits.codeUnitPtrAt(anyChars.currentToken().pos.end - 1);
}
+ // |charBuffer| should be empty here, but we may as well code defensively.
+ MOZ_ASSERT(this->charBuffer.length() == 0);
+ this->charBuffer.clear();
+
// Template literals normalize only '\r' and "\r\n" to '\n'; Unicode
// separators don't need special handling.
// https://tc39.github.io/ecma262/#sec-static-semantics-tv-and-trv
if (!fillCharBufferFromSourceNormalizingAsciiLineBreaks(cur, end)) {
return nullptr;
}
return drainCharBufferIntoAtom(anyChars.cx);
@@ -2281,16 +2306,17 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
//
// As an alternative, we directly add every one of these functions to this
// class, using explicit qualification to address the dependent-name
// problem. |this| or other qualification is no longer necessary -- at
// cost of this ever-changing laundry list of |using|s. So it goes.
public:
using GeneralCharsBase::anyCharsAccess;
using GeneralCharsBase::computeLineAndColumn;
+ using TokenStreamCharsShared::adoptState;
private:
using typename CharsBase::SourceUnits;
private:
using CharsBase::atomizeSourceChars;
using GeneralCharsBase::badToken;
using TokenStreamCharsShared::appendCodePointToCharBuffer;
@@ -2641,18 +2667,45 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
// with SlashIsRegExp, so we have to do it manually here.
anyCharsAccess().allowGettingNextTokenWithSlashIsRegExp();
}
return true;
}
MOZ_MUST_USE bool advance(size_t position);
- void seek(const Position& pos);
- MOZ_MUST_USE bool seek(const Position& pos, const TokenStreamAnyChars& other);
+ void seekTo(const Position& pos);
+ MOZ_MUST_USE bool seekTo(const Position& pos,
+ const TokenStreamAnyChars& other);
+
+ void rewind(const Position& pos) {
+ MOZ_ASSERT(pos.buf <= this->sourceUnits.addressOfNextCodeUnit(),
+ "should be rewinding here");
+ seekTo(pos);
+ }
+
+ MOZ_MUST_USE bool rewind(const Position& pos,
+ const TokenStreamAnyChars& other) {
+ MOZ_ASSERT(pos.buf <= this->sourceUnits.addressOfNextCodeUnit(),
+ "should be rewinding here");
+ return seekTo(pos, other);
+ }
+
+ void fastForward(const Position& pos) {
+ MOZ_ASSERT(this->sourceUnits.addressOfNextCodeUnit() <= pos.buf,
+ "should be moving forward here");
+ seekTo(pos);
+ }
+
+ MOZ_MUST_USE bool fastForward(const Position& pos,
+ const TokenStreamAnyChars& other) {
+ MOZ_ASSERT(this->sourceUnits.addressOfNextCodeUnit() <= pos.buf,
+ "should be moving forward here");
+ return seekTo(pos, other);
+ }
const Unit* codeUnitPtrAt(size_t offset) const {
return this->sourceUnits.codeUnitPtrAt(offset);
}
const Unit* rawLimit() const { return this->sourceUnits.limit(); }
MOZ_MUST_USE bool identifierName(TokenStart start, const Unit* identStart,