зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1377007 - Implementing basic binjs-ref parser in SpiderMonkey;r=arai,jorendorff
This patch implements a Binary AST parser matching the latest binjs-ref parser at this date. The subset of JS recognized matches ES5, with an AST based on a slightly customized Babylon AST. At this stage, the parser trusts its input, insofar as it does not check directives or bindings. Followup patch will introduce checking of these directives/bindings. MozReview-Commit-ID: 1nt230rt02R --HG-- extra : rebase_source : 4710455326bfd028f6e396426536be01f16dd649
This commit is contained in:
Родитель
4382d682b7
Коммит
5db396578d
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,278 @@
|
|||
#ifndef frontend_BinSource_h
|
||||
#define frontend_BinSource_h
|
||||
|
||||
/**
|
||||
* A Binary AST parser.
|
||||
*
|
||||
* At the time of this writing, this parser implements the grammar of ES5
|
||||
* and trusts its input (in particular, variable declarations).
|
||||
*/
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "frontend/BinTokenReaderTester.h"
|
||||
#include "frontend/FullParseHandler.h"
|
||||
#include "frontend/ParseContext.h"
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
|
||||
#include "js/GCHashTable.h"
|
||||
#include "js/GCVector.h"
|
||||
#include "js/Result.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
class BinASTParser;
|
||||
|
||||
/**
|
||||
* The parser for a Binary AST.
|
||||
*
|
||||
* By design, this parser never needs to backtrack or look ahead. Errors are not
|
||||
* recoverable.
|
||||
*/
|
||||
class BinASTParser : private JS::AutoGCRooter, public ErrorReporter
|
||||
{
|
||||
using Names = JS::GCVector<JSString*, 8>;
|
||||
using Tokenizer = BinTokenReaderTester;
|
||||
using Chars = Tokenizer::Chars;
|
||||
|
||||
public:
|
||||
BinASTParser(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames, const JS::ReadOnlyCompileOptions& options)
|
||||
: AutoGCRooter(cx, BINPARSER)
|
||||
, traceListHead_(nullptr)
|
||||
, options_(options)
|
||||
, cx_(cx)
|
||||
, alloc_(alloc)
|
||||
, nodeAlloc_(cx, alloc)
|
||||
, keepAtoms_(cx)
|
||||
, parseContext_(nullptr)
|
||||
, usedNames_(usedNames)
|
||||
, factory_(cx, alloc, nullptr, SourceKind::Binary)
|
||||
{
|
||||
cx_->frontendCollectionPool().addActiveCompilation();
|
||||
tempPoolMark_ = alloc.mark();
|
||||
}
|
||||
~BinASTParser()
|
||||
{
|
||||
alloc_.release(tempPoolMark_);
|
||||
|
||||
/*
|
||||
* The parser can allocate enormous amounts of memory for large functions.
|
||||
* Eagerly free the memory now (which otherwise won't be freed until the
|
||||
* next GC) to avoid unnecessary OOMs.
|
||||
*/
|
||||
alloc_.freeAllIfHugeAndUnused();
|
||||
|
||||
cx_->frontendCollectionPool().removeActiveCompilation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a buffer, returning a node (which may be nullptr) in case of success
|
||||
* or Nothing() in case of error.
|
||||
*
|
||||
* The instance of `ParseNode` MAY NOT survive the `BinASTParser`. Indeed,
|
||||
* destruction of the `BinASTParser` will also destroy the `ParseNode`.
|
||||
*
|
||||
* In case of error, the parser reports the JS error.
|
||||
*/
|
||||
JS::Result<ParseNode*> parse(const uint8_t* start, const size_t length);
|
||||
JS::Result<ParseNode*> parse(const Vector<uint8_t>& data);
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseAux(const uint8_t* start, const size_t length);
|
||||
|
||||
// --- Raise errors.
|
||||
//
|
||||
// These methods return a (failed) JS::Result for convenience.
|
||||
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidKind(const char* superKind, const BinKind kind);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidField(const char* kind, const BinField field);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidEnum(const char* kind, const Chars& value);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingField(const char* kind, const BinField field);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseEmpty(const char* description);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseOOM();
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(const char* description);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(BinKind kind, const char* description);
|
||||
|
||||
// Ensure that this parser will never be used again.
|
||||
void poison();
|
||||
|
||||
// --- Parse full nodes (methods are sorted by alphabetical order)
|
||||
//
|
||||
// These method may NEVER return `nullptr`. // FIXME: We can probably optimize Result<> based on this.
|
||||
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseBlockStatement();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseCatchClause();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseExpression();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseForInit();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseForInInit();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseIdentifier();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseObjectPropertyName();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseObjectMember();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parsePattern(); // Parse a *binding* pattern.
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parsePropertyName();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseProgram();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseStatement();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseSwitchCase();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseVariableDeclarator();
|
||||
|
||||
|
||||
// --- Parse lists of nodes (methods are sorted by alphabetical order)
|
||||
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseArgumentList();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseDirectiveList();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseExpressionList(bool acceptElisions);
|
||||
|
||||
// Returns a list of PNK_COLON.
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseObjectMemberList();
|
||||
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseStatementList();
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseSwitchCaseList();
|
||||
|
||||
// --- Parse the contents of a node whose kind has already been determined.
|
||||
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseArrayExpressionAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseBreakOrContinueStatementAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseBlockStatementAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseExpressionStatementAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseExpressionAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseFunctionAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseIdentifierAux(const BinKind, const Tokenizer::BinFields& fields, const bool expectObjectPropertyName = false);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseMemberExpressionAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseNumericLiteralAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseObjectExpressionAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parsePatternAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseStringLiteralAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseStatementAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseVariableDeclarationAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
|
||||
// --- Auxiliary parsing functions that may have a side-effect on the parser but do not return a node.
|
||||
|
||||
MOZ_MUST_USE JS::Result<Ok> checkEmptyTuple(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
MOZ_MUST_USE JS::Result<Ok> parseElisionAux(const BinKind kind, const Tokenizer::BinFields& fields);
|
||||
|
||||
// Parse full scope information to the current innermost scope.
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateCurrentScope();
|
||||
// Parse full scope information to a specific var scope / let scope combination.
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScope(ParseContext::Scope& varScope, ParseContext::Scope& letScope);
|
||||
// Parse a list of names and add it to a given scope.
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScopeNames(ParseContext::Scope& scope, DeclarationKind kind);
|
||||
MOZ_MUST_USE JS::Result<Ok> parseStringList(MutableHandle<Maybe<Names>> out);
|
||||
|
||||
// --- Utilities.
|
||||
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> appendDirectivesToBody(ParseNode* body, ParseNode* directives);
|
||||
|
||||
// Read a string as a `Chars`.
|
||||
MOZ_MUST_USE JS::Result<Ok> readString(Maybe<Chars>& out);
|
||||
MOZ_MUST_USE JS::Result<Ok> readString(MutableHandleAtom out);
|
||||
MOZ_MUST_USE JS::Result<bool> readBool();
|
||||
MOZ_MUST_USE JS::Result<double> readNumber();
|
||||
|
||||
const ReadOnlyCompileOptions& options() const override {
|
||||
return this->options_;
|
||||
}
|
||||
|
||||
// Names
|
||||
|
||||
|
||||
bool hasUsedName(HandlePropertyName name);
|
||||
|
||||
// --- GC.
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
ObjectBox::TraceList(trc, traceListHead_);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
ObjectBox* newObjectBox(JSObject* obj) {
|
||||
MOZ_ASSERT(obj);
|
||||
|
||||
/*
|
||||
* We use JSContext.tempLifoAlloc to allocate parsed objects and place them
|
||||
* on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
|
||||
* arenas containing the entries must be alive until we are done with
|
||||
* scanning, parsing and code generation for the whole script or top-level
|
||||
* function.
|
||||
*/
|
||||
|
||||
ObjectBox* objbox = alloc_.new_<ObjectBox>(obj, traceListHead_);
|
||||
if (!objbox) {
|
||||
ReportOutOfMemory(cx_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
traceListHead_ = objbox;
|
||||
|
||||
return objbox;
|
||||
}
|
||||
|
||||
ParseNode* allocParseNode(size_t size) {
|
||||
MOZ_ASSERT(size == sizeof(ParseNode));
|
||||
return static_cast<ParseNode*>(nodeAlloc_.allocNode());
|
||||
}
|
||||
|
||||
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
|
||||
|
||||
private: // Implement ErrorReporter
|
||||
|
||||
virtual void lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const override {
|
||||
*line = 0;
|
||||
*column = offset;
|
||||
}
|
||||
virtual void currentLineAndColumn(uint32_t* line, uint32_t* column) const override {
|
||||
*line = 0;
|
||||
*column = offset();
|
||||
}
|
||||
size_t offset() const {
|
||||
if (tokenizer_.isSome())
|
||||
return tokenizer_->offset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
virtual bool hasTokenizationStarted() const override {
|
||||
return tokenizer_.isSome();
|
||||
}
|
||||
virtual void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) override;
|
||||
virtual const char* getFilename() const override {
|
||||
return this->options_.filename();
|
||||
}
|
||||
|
||||
ObjectBox* traceListHead_;
|
||||
const ReadOnlyCompileOptions& options_;
|
||||
JSContext* cx_;
|
||||
LifoAlloc& alloc_;
|
||||
LifoAlloc::Mark tempPoolMark_;
|
||||
ParseNodeAllocator nodeAlloc_;
|
||||
|
||||
// Root atoms and objects allocated for the parse tree.
|
||||
AutoKeepAtoms keepAtoms_;
|
||||
|
||||
// The current ParseContext, holding directives, etc.
|
||||
ParseContext* parseContext_;
|
||||
UsedNameTracker& usedNames_;
|
||||
Maybe<Tokenizer> tokenizer_;
|
||||
FullParseHandler factory_;
|
||||
|
||||
friend class BinParseContext;
|
||||
|
||||
// Needs access to AutoGCRooter.
|
||||
friend void TraceBinParser(JSTracer* trc, AutoGCRooter* parser);
|
||||
};
|
||||
|
||||
class BinParseContext : public ParseContext
|
||||
{
|
||||
public:
|
||||
BinParseContext(JSContext* cx, BinASTParser* parser, SharedContext* sc, Directives* newDirectives)
|
||||
: ParseContext(cx, parser->parseContext_, sc, *parser,
|
||||
parser->usedNames_, newDirectives, /* isFull = */ true)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
#endif // frontend_BinSource_h
|
|
@ -31,8 +31,7 @@ bool
|
|||
BinTokenReaderTester::raiseError(const char* description)
|
||||
{
|
||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||
TokenPos pos;
|
||||
latestTokenPos(pos);
|
||||
TokenPos pos = this->pos();
|
||||
JS_ReportErrorASCII(cx_, "BinAST parsing error: %s at offsets %u => %u",
|
||||
description, pos.begin, pos.end);
|
||||
return false;
|
||||
|
@ -416,12 +415,20 @@ BinTokenReaderTester::offset() const
|
|||
return latestKnownGoodPos_;
|
||||
}
|
||||
|
||||
void
|
||||
BinTokenReaderTester::latestTokenPos(TokenPos& pos)
|
||||
TokenPos
|
||||
BinTokenReaderTester::pos()
|
||||
{
|
||||
pos.begin = latestKnownGoodPos_;
|
||||
return pos(latestKnownGoodPos_);
|
||||
}
|
||||
|
||||
TokenPos
|
||||
BinTokenReaderTester::pos(size_t start)
|
||||
{
|
||||
TokenPos pos;
|
||||
pos.begin = start;
|
||||
pos.end = current_ - start_;
|
||||
MOZ_ASSERT(pos.end >= pos.begin);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -181,7 +181,8 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
/**
|
||||
* Return the position of the latest token.
|
||||
*/
|
||||
void latestTokenPos(TokenPos& out);
|
||||
TokenPos pos();
|
||||
TokenPos pos(size_t startOffset);
|
||||
size_t offset() const;
|
||||
|
||||
/**
|
||||
|
|
|
@ -124,10 +124,13 @@ IsKeyword(JSLinearString* str);
|
|||
void
|
||||
TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
|
||||
|
||||
#if defined(JS_BUILD_BINAST)
|
||||
|
||||
/* Trace all GC things reachable from binjs parser. Defined in BinSource.cpp. */
|
||||
void
|
||||
TraceBinParser(JSTracer* trc, JS::AutoGCRooter* parser);
|
||||
|
||||
#endif // defined(JS_BUILD_BINAST)
|
||||
|
||||
class MOZ_STACK_CLASS AutoFrontendTraceLog
|
||||
{
|
||||
|
|
|
@ -21,6 +21,13 @@ class RegExpObject;
|
|||
|
||||
namespace frontend {
|
||||
|
||||
enum class SourceKind {
|
||||
// We are parsing from a text source (Parser.h)
|
||||
Text,
|
||||
// We are parsing from a binary source (BinSource.h)
|
||||
Binary,
|
||||
};
|
||||
|
||||
// Parse handler used when generating a full parse tree for all code which the
|
||||
// parser encounters.
|
||||
class FullParseHandler
|
||||
|
@ -41,6 +48,8 @@ class FullParseHandler
|
|||
size_t lazyInnerFunctionIndex;
|
||||
size_t lazyClosedOverBindingIndex;
|
||||
|
||||
const SourceKind sourceKind_;
|
||||
|
||||
public:
|
||||
/* new_ methods for creating parse nodes. These report OOM on context. */
|
||||
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
|
||||
|
@ -68,15 +77,24 @@ class FullParseHandler
|
|||
return node->isInParens() && (node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY));
|
||||
}
|
||||
|
||||
FullParseHandler(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
|
||||
FullParseHandler(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction,
|
||||
SourceKind kind = SourceKind::Text)
|
||||
: allocator(cx, alloc),
|
||||
lazyOuterFunction_(cx, lazyOuterFunction),
|
||||
lazyInnerFunctionIndex(0),
|
||||
lazyClosedOverBindingIndex(0)
|
||||
lazyClosedOverBindingIndex(0),
|
||||
sourceKind_(SourceKind::Text)
|
||||
{}
|
||||
|
||||
static ParseNode* null() { return nullptr; }
|
||||
|
||||
// The FullParseHandler may be used to create nodes for text sources
|
||||
// (from Parser.h) or for binary sources (from BinSource.h). In the latter
|
||||
// case, some common assumptions on offsets are incorrect, e.g. in `a + b`,
|
||||
// `a`, `b` and `+` may be stored in any order. We use `sourceKind()`
|
||||
// to determine whether we need to check these assumptions.
|
||||
SourceKind sourceKind() const { return sourceKind_; }
|
||||
|
||||
ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
|
||||
void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); }
|
||||
|
||||
|
@ -230,8 +248,7 @@ class FullParseHandler
|
|||
ParseNode* elision = new_<NullaryNode>(PNK_ELISION, pos);
|
||||
if (!elision)
|
||||
return false;
|
||||
|
||||
literal->append(elision);
|
||||
addList(/* list = */ literal, /* child = */ elision);
|
||||
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
|
||||
return true;
|
||||
}
|
||||
|
@ -243,8 +260,7 @@ class FullParseHandler
|
|||
ParseNode* spread = newSpread(begin, inner);
|
||||
if (!spread)
|
||||
return false;
|
||||
|
||||
literal->append(spread);
|
||||
addList(/* list = */ literal, /* child = */ spread);
|
||||
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
|
||||
return true;
|
||||
}
|
||||
|
@ -254,8 +270,7 @@ class FullParseHandler
|
|||
|
||||
if (!element->isConstant())
|
||||
literal->pn_xflags |= PNX_NONCONST;
|
||||
|
||||
literal->append(element);
|
||||
addList(/* list = */ literal, /* child = */ element);
|
||||
}
|
||||
|
||||
ParseNode* newCall(const TokenPos& pos) {
|
||||
|
@ -294,7 +309,9 @@ class FullParseHandler
|
|||
ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
|
||||
return new_<UnaryNode>(PNK_SUPERBASE, pos, thisName);
|
||||
}
|
||||
|
||||
ParseNode* newCatchBlock(ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody) {
|
||||
return new_<TernaryNode>(PNK_CATCH, catchName, catchGuard, catchBody);
|
||||
}
|
||||
MOZ_MUST_USE bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) {
|
||||
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||
|
@ -306,22 +323,19 @@ class FullParseHandler
|
|||
ParseNode* mutation = newUnary(PNK_MUTATEPROTO, begin, expr);
|
||||
if (!mutation)
|
||||
return false;
|
||||
literal->append(mutation);
|
||||
addList(/* list = */ literal, /* child = */ mutation);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool addPropertyDefinition(ParseNode* literal, ParseNode* key, ParseNode* val) {
|
||||
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
||||
key->isKind(PNK_STRING) ||
|
||||
key->isKind(PNK_COMPUTED_NAME));
|
||||
MOZ_ASSERT(isUsableAsObjectPropertyName(key));
|
||||
|
||||
ParseNode* propdef = newBinary(PNK_COLON, key, val, JSOP_INITPROP);
|
||||
if (!propdef)
|
||||
return false;
|
||||
literal->append(propdef);
|
||||
addList(/* list = */ literal, /* child = */ propdef);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -336,7 +350,7 @@ class FullParseHandler
|
|||
ParseNode* propdef = newBinary(PNK_SHORTHAND, name, expr, JSOP_INITPROP);
|
||||
if (!propdef)
|
||||
return false;
|
||||
literal->append(propdef);
|
||||
addList(/* list = */ literal, /* child = */ propdef);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -348,26 +362,21 @@ class FullParseHandler
|
|||
ParseNode* spread = newSpread(begin, inner);
|
||||
if (!spread)
|
||||
return false;
|
||||
literal->append(spread);
|
||||
addList(/* list = */ literal, /* child = */ spread);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
|
||||
AccessorType atype)
|
||||
{
|
||||
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
||||
key->isKind(PNK_STRING) ||
|
||||
key->isKind(PNK_COMPUTED_NAME));
|
||||
literal->pn_xflags |= PNX_NONCONST;
|
||||
|
||||
ParseNode* propdef = newBinary(PNK_COLON, key, fn, AccessorTypeToJSOp(atype));
|
||||
ParseNode* propdef = newObjectMethodOrPropertyDefinition(key, fn, atype);
|
||||
if (!propdef)
|
||||
return false;
|
||||
|
||||
literal->append(propdef);
|
||||
literal->pn_xflags |= PNX_NONCONST;
|
||||
addList(/* list = */ literal, /* child = */ propdef);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -375,15 +384,12 @@ class FullParseHandler
|
|||
AccessorType atype, bool isStatic)
|
||||
{
|
||||
MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
|
||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
||||
key->isKind(PNK_STRING) ||
|
||||
key->isKind(PNK_COMPUTED_NAME));
|
||||
MOZ_ASSERT(isUsableAsObjectPropertyName(key));
|
||||
|
||||
ParseNode* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
|
||||
if (!classMethod)
|
||||
return false;
|
||||
methodList->append(classMethod);
|
||||
addList(/* list = */ methodList, /* child = */ classMethod);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -422,7 +428,7 @@ class FullParseHandler
|
|||
void addStatementToList(ParseNode* list, ParseNode* stmt) {
|
||||
MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST));
|
||||
|
||||
list->append(stmt);
|
||||
addList(/* list = */ list, /* child = */ stmt);
|
||||
|
||||
if (isFunctionStmt(stmt)) {
|
||||
// PNX_FUNCDEFS notifies the emitter that the block contains
|
||||
|
@ -442,12 +448,16 @@ class FullParseHandler
|
|||
MOZ_ASSERT(casepn->isKind(PNK_CASE));
|
||||
MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST));
|
||||
|
||||
list->append(casepn);
|
||||
addList(/* list = */ list, /* child = */ casepn);
|
||||
|
||||
if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS)
|
||||
list->pn_xflags |= PNX_FUNCDEFS;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE inline bool addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
|
||||
ParseNode* catchName, ParseNode* catchGuard,
|
||||
ParseNode* catchBody);
|
||||
|
||||
MOZ_MUST_USE bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) {
|
||||
MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
|
||||
MOZ_ASSERT(stmtList->isArity(PN_LIST));
|
||||
|
@ -459,8 +469,7 @@ class FullParseHandler
|
|||
|
||||
MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
|
||||
genName->setOp(JSOP_SETNAME);
|
||||
|
||||
ParseNode* genInit = newBinary(PNK_ASSIGN, genName, makeGen);
|
||||
ParseNode* genInit = newAssignment(PNK_ASSIGN, /* lhs = */ genName, /* rhs = */ makeGen);
|
||||
if (!genInit)
|
||||
return false;
|
||||
|
||||
|
@ -671,6 +680,20 @@ class FullParseHandler
|
|||
!node->pn_funbox->isArrow();
|
||||
}
|
||||
|
||||
ParseNode* newObjectMethodOrPropertyDefinition(ParseNode* key, ParseNode* fn, AccessorType atype) {
|
||||
MOZ_ASSERT(isUsableAsObjectPropertyName(key));
|
||||
|
||||
return newBinary(PNK_COLON, key, fn, AccessorTypeToJSOp(atype));
|
||||
}
|
||||
|
||||
bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) {
|
||||
MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
|
||||
ParseNode* paramsBody = newList(PNK_PARAMSBODY, body);
|
||||
if (!paramsBody)
|
||||
return false;
|
||||
setFunctionFormalParametersAndBody(pn, paramsBody);
|
||||
return true;
|
||||
}
|
||||
void setFunctionFormalParametersAndBody(ParseNode* funcNode, ParseNode* kid) {
|
||||
MOZ_ASSERT_IF(kid, kid->isKind(PNK_PARAMSBODY));
|
||||
funcNode->pn_body = kid;
|
||||
|
@ -681,11 +704,11 @@ class FullParseHandler
|
|||
funbox->functionNode = pn;
|
||||
}
|
||||
void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) {
|
||||
pn->pn_body->append(argpn);
|
||||
addList(/* list = */ pn->pn_body, /* child = */ argpn);
|
||||
}
|
||||
void setFunctionBody(ParseNode* fn, ParseNode* body) {
|
||||
MOZ_ASSERT(fn->pn_body->isKind(PNK_PARAMSBODY));
|
||||
fn->pn_body->append(body);
|
||||
addList(/* list = */ fn->pn_body, /* child = */ body);
|
||||
}
|
||||
|
||||
ParseNode* newModule(const TokenPos& pos) {
|
||||
|
@ -701,7 +724,7 @@ class FullParseHandler
|
|||
if (!newExpr)
|
||||
return nullptr;
|
||||
|
||||
addList(newExpr, ctor);
|
||||
addList(/* list = */ newExpr, /* child = */ ctor);
|
||||
return newExpr;
|
||||
}
|
||||
|
||||
|
@ -744,6 +767,13 @@ class FullParseHandler
|
|||
return node->isKind(PNK_SUPERBASE);
|
||||
}
|
||||
|
||||
bool isUsableAsObjectPropertyName(ParseNode* node) {
|
||||
return node->isKind(PNK_NUMBER)
|
||||
|| node->isKind(PNK_OBJECT_PROPERTY_NAME)
|
||||
|| node->isKind(PNK_STRING)
|
||||
|| node->isKind(PNK_COMPUTED_NAME);
|
||||
}
|
||||
|
||||
inline MOZ_MUST_USE bool finishInitializerAssignment(ParseNode* pn, ParseNode* init);
|
||||
|
||||
void setBeginPosition(ParseNode* pn, ParseNode* oth) {
|
||||
|
@ -805,7 +835,10 @@ class FullParseHandler
|
|||
}
|
||||
|
||||
void addList(ParseNode* list, ParseNode* kid) {
|
||||
list->append(kid);
|
||||
if (sourceKind_ == SourceKind::Text)
|
||||
list->append(kid);
|
||||
else
|
||||
list->appendWithoutOrderAssumption(kid);
|
||||
}
|
||||
|
||||
void setOp(ParseNode* pn, JSOp op) {
|
||||
|
@ -879,6 +912,19 @@ class FullParseHandler
|
|||
}
|
||||
};
|
||||
|
||||
inline bool
|
||||
FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
|
||||
ParseNode* catchName, ParseNode* catchGuard,
|
||||
ParseNode* catchBody)
|
||||
{
|
||||
ParseNode* catchpn = newCatchBlock(catchName, catchGuard, catchBody);
|
||||
if (!catchpn)
|
||||
return false;
|
||||
addList(/* list = */ catchList, /* child = */ lexicalScope);
|
||||
lexicalScope->setScopeBody(catchpn);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
|
||||
ParseNode* defaultValue)
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef frontend_ParseContext_inl_h
|
||||
#define frontend_ParseContext_inl_h
|
||||
|
||||
#include "frontend/ParseContext.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
template <>
|
||||
inline bool
|
||||
ParseContext::Statement::is<ParseContext::LabelStatement>() const
|
||||
{
|
||||
return kind_ == StatementKind::Label;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool
|
||||
ParseContext::Statement::is<ParseContext::ClassStatement>() const
|
||||
{
|
||||
return kind_ == StatementKind::Class;
|
||||
}
|
||||
|
||||
|
||||
inline JS::Result<Ok, ParseContext::BreakStatementError>
|
||||
ParseContext::checkBreakStatement(PropertyName* label)
|
||||
{
|
||||
// Labeled 'break' statements target the nearest labeled statements (could
|
||||
// be any kind) with the same label. Unlabeled 'break' statements target
|
||||
// the innermost loop or switch statement.
|
||||
if (label) {
|
||||
auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
|
||||
MOZ_ASSERT(stmt);
|
||||
return stmt->label() == label;
|
||||
};
|
||||
|
||||
if (!findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel))
|
||||
return mozilla::Err(ParseContext::BreakStatementError::LabelNotFound);
|
||||
|
||||
} else {
|
||||
auto isBreakTarget = [](ParseContext::Statement* stmt) {
|
||||
return StatementKindIsUnlabeledBreakTarget(stmt->kind());
|
||||
};
|
||||
|
||||
if (!findInnermostStatement(isBreakTarget))
|
||||
return mozilla::Err(ParseContext::BreakStatementError::ToughBreak);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
inline JS::Result<Ok, ParseContext::ContinueStatementError>
|
||||
ParseContext::checkContinueStatement(PropertyName* label)
|
||||
{
|
||||
// Labeled 'continue' statements target the nearest labeled loop
|
||||
// statements with the same label. Unlabeled 'continue' statements target
|
||||
// the innermost loop statement.
|
||||
auto isLoop = [](ParseContext::Statement* stmt) {
|
||||
MOZ_ASSERT(stmt);
|
||||
return StatementKindIsLoop(stmt->kind());
|
||||
};
|
||||
|
||||
if (!label) {
|
||||
// Unlabeled statement: we target the innermost loop, so make sure that
|
||||
// there is an innermost loop.
|
||||
if (!findInnermostStatement(isLoop))
|
||||
return mozilla::Err(ParseContext::ContinueStatementError::NotInALoop);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Labeled statement: targest the nearest labeled loop with the same label.
|
||||
ParseContext::Statement* stmt = innermostStatement();
|
||||
bool foundLoop = false; // True if we have encountered at least one loop.
|
||||
|
||||
for (;;) {
|
||||
stmt = ParseContext::Statement::findNearest(stmt, isLoop);
|
||||
if (!stmt)
|
||||
return foundLoop ? mozilla::Err(ParseContext::ContinueStatementError::LabelNotFound)
|
||||
: mozilla::Err(ParseContext::ContinueStatementError::NotInALoop);
|
||||
|
||||
foundLoop = true;
|
||||
|
||||
// Is it labeled by our label?
|
||||
stmt = stmt->enclosing();
|
||||
while (stmt && stmt->is<ParseContext::LabelStatement>()) {
|
||||
if (stmt->as<ParseContext::LabelStatement>().label() == label)
|
||||
return Ok();
|
||||
|
||||
stmt = stmt->enclosing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // frontend_ParseContext_inl_h
|
|
@ -7,7 +7,11 @@
|
|||
#ifndef frontend_ParseContext_h
|
||||
#define frontend_ParseContext_h
|
||||
|
||||
#include "ds/Nestable.h"
|
||||
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "frontend/ErrorReporter.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -289,6 +293,10 @@ class ParseContext : public Nestable<ParseContext>
|
|||
return declared_.acquire(pc->sc()->context);
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return declared_->all().empty();
|
||||
}
|
||||
|
||||
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
|
||||
return declared_->lookup(name);
|
||||
}
|
||||
|
@ -402,6 +410,7 @@ class ParseContext : public Nestable<ParseContext>
|
|||
{
|
||||
public:
|
||||
explicit inline VarScope(ParserBase* parser);
|
||||
explicit inline VarScope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames);
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -517,6 +526,7 @@ class ParseContext : public Nestable<ParseContext>
|
|||
return sc_;
|
||||
}
|
||||
|
||||
// `true` if we are in the body of a function definition.
|
||||
bool isFunctionBox() const {
|
||||
return sc_->isFunctionBox();
|
||||
}
|
||||
|
@ -578,6 +588,22 @@ class ParseContext : public Nestable<ParseContext>
|
|||
return *closedOverBindingsForLazy_;
|
||||
}
|
||||
|
||||
enum class BreakStatementError {
|
||||
// Unlabeled break must be inside loop or switch.
|
||||
ToughBreak,
|
||||
LabelNotFound,
|
||||
};
|
||||
|
||||
// Return Err(true) if we have encountered at least one loop,
|
||||
// Err(false) otherwise.
|
||||
MOZ_MUST_USE inline JS::Result<Ok, BreakStatementError> checkBreakStatement(PropertyName* label);
|
||||
|
||||
enum class ContinueStatementError {
|
||||
NotInALoop,
|
||||
LabelNotFound,
|
||||
};
|
||||
MOZ_MUST_USE inline JS::Result<Ok, ContinueStatementError> checkContinueStatement(PropertyName* label);
|
||||
|
||||
// True if we are at the topmost level of a entire script or function body.
|
||||
// For example, while parsing this code we would encounter f1 and f2 at
|
||||
// body level, but we would not encounter f3 or f4 at body level:
|
||||
|
|
|
@ -13,6 +13,27 @@
|
|||
#include "frontend/TokenStream.h"
|
||||
#include "vm/Printer.h"
|
||||
|
||||
// A few notes on lifetime of ParseNode trees:
|
||||
//
|
||||
// - All the `ParseNode` instances MUST BE explicitly allocated in the context's `LifoAlloc`.
|
||||
// This is typically implemented by the `FullParseHandler` or it can be reimplemented with
|
||||
// a custom `new_`.
|
||||
//
|
||||
// - The tree is bulk-deallocated when the parser is deallocated. Consequently, references
|
||||
// to a subtree MUST NOT exist once the parser has been deallocated.
|
||||
//
|
||||
// - This bulk-deallocation DOES NOT run destructors.
|
||||
//
|
||||
// - Instances of `LexicalScope::Data` MUST BE allocated as instances of `ParseNode`, in the same
|
||||
// `LifoAlloc`. They are bulk-deallocated alongside the rest of the tree.
|
||||
//
|
||||
// - Instances of `JSAtom` used throughout the tree (including instances of `PropertyName`) MUST
|
||||
// be kept alive by the parser. This is done through an instance of `AutoKeepAtoms` held by
|
||||
// the parser.
|
||||
//
|
||||
// - Once the parser is deallocated, the `JSAtom` instances MAY be garbage-collected.
|
||||
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
|
@ -598,6 +619,7 @@ class ParseNode
|
|||
appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
|
||||
FullParseHandler* handler, ParseContext* pc);
|
||||
|
||||
// include "ParseNode-inl.h" for these methods.
|
||||
inline PropertyName* name() const;
|
||||
inline JSAtom* atom() const;
|
||||
|
||||
|
@ -731,8 +753,12 @@ class ParseNode
|
|||
}
|
||||
|
||||
void append(ParseNode* pn) {
|
||||
MOZ_ASSERT(pn_arity == PN_LIST);
|
||||
MOZ_ASSERT(pn->pn_pos.begin >= pn_pos.begin);
|
||||
appendWithoutOrderAssumption(pn);
|
||||
}
|
||||
|
||||
void appendWithoutOrderAssumption(ParseNode* pn) {
|
||||
MOZ_ASSERT(pn_arity == PN_LIST);
|
||||
pn_pos.end = pn->pn_pos.end;
|
||||
*pn_tail = pn;
|
||||
pn_tail = &pn->pn_next;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "jsatominlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
#include "frontend/ParseContext-inl.h"
|
||||
#include "frontend/ParseNode-inl.h"
|
||||
#include "vm/EnvironmentObject-inl.h"
|
||||
|
||||
|
@ -1714,7 +1715,7 @@ Parser<FullParseHandler, CharT>::checkStatementsEOF()
|
|||
}
|
||||
|
||||
template <typename Scope>
|
||||
static typename Scope::Data*
|
||||
typename Scope::Data*
|
||||
NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
|
||||
{
|
||||
size_t allocSize = Scope::sizeOfData(numBindings);
|
||||
|
@ -1728,8 +1729,9 @@ NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
|
|||
}
|
||||
|
||||
Maybe<GlobalScope::Data*>
|
||||
ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
|
||||
NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||
{
|
||||
|
||||
Vector<BindingName> funs(context);
|
||||
Vector<BindingName> vars(context);
|
||||
Vector<BindingName> lets(context);
|
||||
|
@ -1792,8 +1794,14 @@ ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
|
|||
return Some(bindings);
|
||||
}
|
||||
|
||||
Maybe<GlobalScope::Data*>
|
||||
ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
|
||||
{
|
||||
return NewGlobalScopeData(context, scope, alloc, pc);
|
||||
}
|
||||
|
||||
Maybe<ModuleScope::Data*>
|
||||
ParserBase::newModuleScopeData(ParseContext::Scope& scope)
|
||||
NewModuleScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||
{
|
||||
Vector<BindingName> imports(context);
|
||||
Vector<BindingName> vars(context);
|
||||
|
@ -1858,8 +1866,14 @@ ParserBase::newModuleScopeData(ParseContext::Scope& scope)
|
|||
return Some(bindings);
|
||||
}
|
||||
|
||||
Maybe<ModuleScope::Data*>
|
||||
ParserBase::newModuleScopeData(ParseContext::Scope& scope)
|
||||
{
|
||||
return NewModuleScopeData(context, scope, alloc, pc);
|
||||
}
|
||||
|
||||
Maybe<EvalScope::Data*>
|
||||
ParserBase::newEvalScopeData(ParseContext::Scope& scope)
|
||||
NewEvalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||
{
|
||||
Vector<BindingName> funs(context);
|
||||
Vector<BindingName> vars(context);
|
||||
|
@ -1902,8 +1916,14 @@ ParserBase::newEvalScopeData(ParseContext::Scope& scope)
|
|||
return Some(bindings);
|
||||
}
|
||||
|
||||
Maybe<EvalScope::Data*>
|
||||
ParserBase::newEvalScopeData(ParseContext::Scope& scope)
|
||||
{
|
||||
return NewEvalScopeData(context, scope, alloc, pc);
|
||||
}
|
||||
|
||||
Maybe<FunctionScope::Data*>
|
||||
ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
|
||||
NewFunctionScopeData(JSContext* context, ParseContext::Scope& scope, bool hasParameterExprs, LifoAlloc& alloc, ParseContext* pc)
|
||||
{
|
||||
Vector<BindingName> positionalFormals(context);
|
||||
Vector<BindingName> formals(context);
|
||||
|
@ -1996,8 +2016,14 @@ ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterEx
|
|||
return Some(bindings);
|
||||
}
|
||||
|
||||
Maybe<FunctionScope::Data*>
|
||||
ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
|
||||
{
|
||||
return NewFunctionScopeData(context, scope, hasParameterExprs, alloc, pc);
|
||||
}
|
||||
|
||||
Maybe<VarScope::Data*>
|
||||
ParserBase::newVarScopeData(ParseContext::Scope& scope)
|
||||
NewVarScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||
{
|
||||
Vector<BindingName> vars(context);
|
||||
|
||||
|
@ -2028,8 +2054,14 @@ ParserBase::newVarScopeData(ParseContext::Scope& scope)
|
|||
return Some(bindings);
|
||||
}
|
||||
|
||||
Maybe<VarScope::Data*>
|
||||
ParserBase::newVarScopeData(ParseContext::Scope& scope)
|
||||
{
|
||||
return NewVarScopeData(context, scope, alloc, pc);
|
||||
}
|
||||
|
||||
Maybe<LexicalScope::Data*>
|
||||
ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
|
||||
NewLexicalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||
{
|
||||
Vector<BindingName> lets(context);
|
||||
Vector<BindingName> consts(context);
|
||||
|
@ -2079,6 +2111,12 @@ ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
|
|||
return Some(bindings);
|
||||
}
|
||||
|
||||
Maybe<LexicalScope::Data*>
|
||||
ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
|
||||
{
|
||||
return NewLexicalScopeData(context, scope, alloc, pc);
|
||||
}
|
||||
|
||||
template <>
|
||||
SyntaxParseHandler::Node
|
||||
PerHandlerParser<SyntaxParseHandler>::finishLexicalScope(ParseContext::Scope& scope, Node body)
|
||||
|
@ -6513,44 +6551,16 @@ GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandlin
|
|||
if (!matchLabel(yieldHandling, &label))
|
||||
return null();
|
||||
|
||||
// Labeled 'continue' statements target the nearest labeled loop
|
||||
// statements with the same label. Unlabeled 'continue' statements target
|
||||
// the innermost loop statement.
|
||||
auto isLoop = [](ParseContext::Statement* stmt) {
|
||||
return StatementKindIsLoop(stmt->kind());
|
||||
};
|
||||
|
||||
if (label) {
|
||||
ParseContext::Statement* stmt = pc->innermostStatement();
|
||||
bool foundLoop = false;
|
||||
|
||||
for (;;) {
|
||||
stmt = ParseContext::Statement::findNearest(stmt, isLoop);
|
||||
if (!stmt) {
|
||||
if (foundLoop)
|
||||
error(JSMSG_LABEL_NOT_FOUND);
|
||||
else
|
||||
errorAt(begin, JSMSG_BAD_CONTINUE);
|
||||
return null();
|
||||
}
|
||||
|
||||
foundLoop = true;
|
||||
|
||||
// Is it labeled by our label?
|
||||
bool foundTarget = false;
|
||||
stmt = stmt->enclosing();
|
||||
while (stmt && stmt->is<ParseContext::LabelStatement>()) {
|
||||
if (stmt->as<ParseContext::LabelStatement>().label() == label) {
|
||||
foundTarget = true;
|
||||
break;
|
||||
}
|
||||
stmt = stmt->enclosing();
|
||||
}
|
||||
if (foundTarget)
|
||||
break;
|
||||
auto validity = pc->checkContinueStatement(label);
|
||||
if (validity.isErr()) {
|
||||
switch (validity.unwrapErr()) {
|
||||
case ParseContext::ContinueStatementError::NotInALoop:
|
||||
errorAt(begin, JSMSG_BAD_CONTINUE);
|
||||
break;
|
||||
case ParseContext::ContinueStatementError::LabelNotFound:
|
||||
error(JSMSG_LABEL_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
} else if (!pc->findInnermostStatement(isLoop)) {
|
||||
error(JSMSG_BAD_CONTINUE);
|
||||
return null();
|
||||
}
|
||||
|
||||
|
|
|
@ -204,20 +204,6 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
template <>
|
||||
inline bool
|
||||
ParseContext::Statement::is<ParseContext::LabelStatement>() const
|
||||
{
|
||||
return kind_ == StatementKind::Label;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool
|
||||
ParseContext::Statement::is<ParseContext::ClassStatement>() const
|
||||
{
|
||||
return kind_ == StatementKind::Class;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T&
|
||||
ParseContext::Statement::as()
|
||||
|
@ -439,6 +425,13 @@ ParseContext::VarScope::VarScope(ParserBase* parser)
|
|||
useAsVarScope(parser->pc);
|
||||
}
|
||||
|
||||
inline
|
||||
ParseContext::VarScope::VarScope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames)
|
||||
: Scope(cx, pc, usedNames)
|
||||
{
|
||||
useAsVarScope(pc);
|
||||
}
|
||||
|
||||
enum FunctionCallBehavior {
|
||||
PermitAssignmentToFunctionCalls,
|
||||
ForbidAssignmentToFunctionCalls
|
||||
|
@ -1588,6 +1581,21 @@ class MOZ_STACK_CLASS AutoAwaitIsKeyword
|
|||
}
|
||||
};
|
||||
|
||||
template <typename Scope>
|
||||
extern typename Scope::Data*
|
||||
NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings);
|
||||
|
||||
Maybe<GlobalScope::Data*>
|
||||
NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
|
||||
Maybe<EvalScope::Data*>
|
||||
NewEvalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
|
||||
Maybe<FunctionScope::Data*>
|
||||
NewFunctionScopeData(JSContext* context, ParseContext::Scope& scope, bool hasParameterExprs, LifoAlloc& alloc, ParseContext* pc);
|
||||
Maybe<VarScope::Data*>
|
||||
NewVarScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
|
||||
Maybe<LexicalScope::Data*>
|
||||
NewLexicalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "ds/InlineTable.h"
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "vm/EnvironmentObject.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
class ParseContext;
|
||||
class ParseNode;
|
||||
|
||||
enum class StatementKind : uint8_t
|
||||
|
|
|
@ -86,17 +86,6 @@ JS::RootingContext::traceStackRoots(JSTracer* trc)
|
|||
TraceStackRoots(trc, stackRoots_);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
// Placeholder implementation.
|
||||
void
|
||||
TraceBinParser(JSTracer*, JS::AutoGCRooter*)
|
||||
{ }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
TraceExactStackRoots(const CooperatingContext& target, JSTracer* trc)
|
||||
{
|
||||
|
@ -571,4 +560,3 @@ JS::AddPersistentRoot(JSRuntime* rt, RootKind kind, PersistentRooted<void*>* roo
|
|||
{
|
||||
rt->heapRoots.ref()[kind].insertBack(root);
|
||||
}
|
||||
|
||||
|
|
|
@ -648,7 +648,10 @@ if CONFIG['JS_BUILD_BINAST']:
|
|||
# specification.
|
||||
SOURCES += ['frontend/BinTokenReaderTester.cpp']
|
||||
# These parts of BinAST should eventually move to release.
|
||||
SOURCES += ['frontend/BinToken.cpp']
|
||||
SOURCES += [
|
||||
'frontend/BinSource.cpp',
|
||||
'frontend/BinToken.cpp'
|
||||
]
|
||||
|
||||
# Wasm code should use WASM_HUGE_MEMORY instead of JS_CODEGEN_X64
|
||||
# so that it is easy to use the huge-mapping optimization for other
|
||||
|
|
Загрузка…
Ссылка в новой задаче