зеркало из 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)
|
BinTokenReaderTester::raiseError(const char* description)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||||
TokenPos pos;
|
TokenPos pos = this->pos();
|
||||||
latestTokenPos(pos);
|
|
||||||
JS_ReportErrorASCII(cx_, "BinAST parsing error: %s at offsets %u => %u",
|
JS_ReportErrorASCII(cx_, "BinAST parsing error: %s at offsets %u => %u",
|
||||||
description, pos.begin, pos.end);
|
description, pos.begin, pos.end);
|
||||||
return false;
|
return false;
|
||||||
|
@ -416,12 +415,20 @@ BinTokenReaderTester::offset() const
|
||||||
return latestKnownGoodPos_;
|
return latestKnownGoodPos_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
TokenPos
|
||||||
BinTokenReaderTester::latestTokenPos(TokenPos& pos)
|
BinTokenReaderTester::pos()
|
||||||
{
|
{
|
||||||
pos.begin = latestKnownGoodPos_;
|
return pos(latestKnownGoodPos_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenPos
|
||||||
|
BinTokenReaderTester::pos(size_t start)
|
||||||
|
{
|
||||||
|
TokenPos pos;
|
||||||
|
pos.begin = start;
|
||||||
pos.end = current_ - start_;
|
pos.end = current_ - start_;
|
||||||
MOZ_ASSERT(pos.end >= pos.begin);
|
MOZ_ASSERT(pos.end >= pos.begin);
|
||||||
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -181,7 +181,8 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
||||||
/**
|
/**
|
||||||
* Return the position of the latest token.
|
* Return the position of the latest token.
|
||||||
*/
|
*/
|
||||||
void latestTokenPos(TokenPos& out);
|
TokenPos pos();
|
||||||
|
TokenPos pos(size_t startOffset);
|
||||||
size_t offset() const;
|
size_t offset() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -124,10 +124,13 @@ IsKeyword(JSLinearString* str);
|
||||||
void
|
void
|
||||||
TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
|
TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
|
||||||
|
|
||||||
|
#if defined(JS_BUILD_BINAST)
|
||||||
|
|
||||||
/* Trace all GC things reachable from binjs parser. Defined in BinSource.cpp. */
|
/* Trace all GC things reachable from binjs parser. Defined in BinSource.cpp. */
|
||||||
void
|
void
|
||||||
TraceBinParser(JSTracer* trc, JS::AutoGCRooter* parser);
|
TraceBinParser(JSTracer* trc, JS::AutoGCRooter* parser);
|
||||||
|
|
||||||
|
#endif // defined(JS_BUILD_BINAST)
|
||||||
|
|
||||||
class MOZ_STACK_CLASS AutoFrontendTraceLog
|
class MOZ_STACK_CLASS AutoFrontendTraceLog
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,13 @@ class RegExpObject;
|
||||||
|
|
||||||
namespace frontend {
|
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
|
// Parse handler used when generating a full parse tree for all code which the
|
||||||
// parser encounters.
|
// parser encounters.
|
||||||
class FullParseHandler
|
class FullParseHandler
|
||||||
|
@ -41,6 +48,8 @@ class FullParseHandler
|
||||||
size_t lazyInnerFunctionIndex;
|
size_t lazyInnerFunctionIndex;
|
||||||
size_t lazyClosedOverBindingIndex;
|
size_t lazyClosedOverBindingIndex;
|
||||||
|
|
||||||
|
const SourceKind sourceKind_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* new_ methods for creating parse nodes. These report OOM on context. */
|
/* new_ methods for creating parse nodes. These report OOM on context. */
|
||||||
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
|
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
|
||||||
|
@ -68,15 +77,24 @@ class FullParseHandler
|
||||||
return node->isInParens() && (node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY));
|
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),
|
: allocator(cx, alloc),
|
||||||
lazyOuterFunction_(cx, lazyOuterFunction),
|
lazyOuterFunction_(cx, lazyOuterFunction),
|
||||||
lazyInnerFunctionIndex(0),
|
lazyInnerFunctionIndex(0),
|
||||||
lazyClosedOverBindingIndex(0)
|
lazyClosedOverBindingIndex(0),
|
||||||
|
sourceKind_(SourceKind::Text)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
static ParseNode* null() { return nullptr; }
|
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); }
|
ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
|
||||||
void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); }
|
void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); }
|
||||||
|
|
||||||
|
@ -230,8 +248,7 @@ class FullParseHandler
|
||||||
ParseNode* elision = new_<NullaryNode>(PNK_ELISION, pos);
|
ParseNode* elision = new_<NullaryNode>(PNK_ELISION, pos);
|
||||||
if (!elision)
|
if (!elision)
|
||||||
return false;
|
return false;
|
||||||
|
addList(/* list = */ literal, /* child = */ elision);
|
||||||
literal->append(elision);
|
|
||||||
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
|
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -243,8 +260,7 @@ class FullParseHandler
|
||||||
ParseNode* spread = newSpread(begin, inner);
|
ParseNode* spread = newSpread(begin, inner);
|
||||||
if (!spread)
|
if (!spread)
|
||||||
return false;
|
return false;
|
||||||
|
addList(/* list = */ literal, /* child = */ spread);
|
||||||
literal->append(spread);
|
|
||||||
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
|
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -254,8 +270,7 @@ class FullParseHandler
|
||||||
|
|
||||||
if (!element->isConstant())
|
if (!element->isConstant())
|
||||||
literal->pn_xflags |= PNX_NONCONST;
|
literal->pn_xflags |= PNX_NONCONST;
|
||||||
|
addList(/* list = */ literal, /* child = */ element);
|
||||||
literal->append(element);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newCall(const TokenPos& pos) {
|
ParseNode* newCall(const TokenPos& pos) {
|
||||||
|
@ -294,7 +309,9 @@ class FullParseHandler
|
||||||
ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
|
ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
|
||||||
return new_<UnaryNode>(PNK_SUPERBASE, pos, thisName);
|
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_MUST_USE bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) {
|
||||||
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
||||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||||
|
@ -306,22 +323,19 @@ class FullParseHandler
|
||||||
ParseNode* mutation = newUnary(PNK_MUTATEPROTO, begin, expr);
|
ParseNode* mutation = newUnary(PNK_MUTATEPROTO, begin, expr);
|
||||||
if (!mutation)
|
if (!mutation)
|
||||||
return false;
|
return false;
|
||||||
literal->append(mutation);
|
addList(/* list = */ literal, /* child = */ mutation);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_MUST_USE bool addPropertyDefinition(ParseNode* literal, ParseNode* key, ParseNode* val) {
|
MOZ_MUST_USE bool addPropertyDefinition(ParseNode* literal, ParseNode* key, ParseNode* val) {
|
||||||
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
||||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
MOZ_ASSERT(isUsableAsObjectPropertyName(key));
|
||||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
|
||||||
key->isKind(PNK_STRING) ||
|
|
||||||
key->isKind(PNK_COMPUTED_NAME));
|
|
||||||
|
|
||||||
ParseNode* propdef = newBinary(PNK_COLON, key, val, JSOP_INITPROP);
|
ParseNode* propdef = newBinary(PNK_COLON, key, val, JSOP_INITPROP);
|
||||||
if (!propdef)
|
if (!propdef)
|
||||||
return false;
|
return false;
|
||||||
literal->append(propdef);
|
addList(/* list = */ literal, /* child = */ propdef);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +350,7 @@ class FullParseHandler
|
||||||
ParseNode* propdef = newBinary(PNK_SHORTHAND, name, expr, JSOP_INITPROP);
|
ParseNode* propdef = newBinary(PNK_SHORTHAND, name, expr, JSOP_INITPROP);
|
||||||
if (!propdef)
|
if (!propdef)
|
||||||
return false;
|
return false;
|
||||||
literal->append(propdef);
|
addList(/* list = */ literal, /* child = */ propdef);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,26 +362,21 @@ class FullParseHandler
|
||||||
ParseNode* spread = newSpread(begin, inner);
|
ParseNode* spread = newSpread(begin, inner);
|
||||||
if (!spread)
|
if (!spread)
|
||||||
return false;
|
return false;
|
||||||
literal->append(spread);
|
addList(/* list = */ literal, /* child = */ spread);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
|
MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
|
||||||
AccessorType atype)
|
AccessorType atype)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
|
||||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
literal->pn_xflags |= PNX_NONCONST;
|
||||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
|
||||||
key->isKind(PNK_STRING) ||
|
|
||||||
key->isKind(PNK_COMPUTED_NAME));
|
|
||||||
|
|
||||||
ParseNode* propdef = newBinary(PNK_COLON, key, fn, AccessorTypeToJSOp(atype));
|
ParseNode* propdef = newObjectMethodOrPropertyDefinition(key, fn, atype);
|
||||||
if (!propdef)
|
if (!propdef)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
literal->append(propdef);
|
addList(/* list = */ literal, /* child = */ propdef);
|
||||||
literal->pn_xflags |= PNX_NONCONST;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,15 +384,12 @@ class FullParseHandler
|
||||||
AccessorType atype, bool isStatic)
|
AccessorType atype, bool isStatic)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
|
MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
|
||||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
MOZ_ASSERT(isUsableAsObjectPropertyName(key));
|
||||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
|
||||||
key->isKind(PNK_STRING) ||
|
|
||||||
key->isKind(PNK_COMPUTED_NAME));
|
|
||||||
|
|
||||||
ParseNode* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
|
ParseNode* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
|
||||||
if (!classMethod)
|
if (!classMethod)
|
||||||
return false;
|
return false;
|
||||||
methodList->append(classMethod);
|
addList(/* list = */ methodList, /* child = */ classMethod);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +428,7 @@ class FullParseHandler
|
||||||
void addStatementToList(ParseNode* list, ParseNode* stmt) {
|
void addStatementToList(ParseNode* list, ParseNode* stmt) {
|
||||||
MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST));
|
MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST));
|
||||||
|
|
||||||
list->append(stmt);
|
addList(/* list = */ list, /* child = */ stmt);
|
||||||
|
|
||||||
if (isFunctionStmt(stmt)) {
|
if (isFunctionStmt(stmt)) {
|
||||||
// PNX_FUNCDEFS notifies the emitter that the block contains
|
// PNX_FUNCDEFS notifies the emitter that the block contains
|
||||||
|
@ -442,12 +448,16 @@ class FullParseHandler
|
||||||
MOZ_ASSERT(casepn->isKind(PNK_CASE));
|
MOZ_ASSERT(casepn->isKind(PNK_CASE));
|
||||||
MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST));
|
MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST));
|
||||||
|
|
||||||
list->append(casepn);
|
addList(/* list = */ list, /* child = */ casepn);
|
||||||
|
|
||||||
if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS)
|
if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS)
|
||||||
list->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_MUST_USE bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) {
|
||||||
MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
|
MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
|
||||||
MOZ_ASSERT(stmtList->isArity(PN_LIST));
|
MOZ_ASSERT(stmtList->isArity(PN_LIST));
|
||||||
|
@ -459,8 +469,7 @@ class FullParseHandler
|
||||||
|
|
||||||
MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
|
MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
|
||||||
genName->setOp(JSOP_SETNAME);
|
genName->setOp(JSOP_SETNAME);
|
||||||
|
ParseNode* genInit = newAssignment(PNK_ASSIGN, /* lhs = */ genName, /* rhs = */ makeGen);
|
||||||
ParseNode* genInit = newBinary(PNK_ASSIGN, genName, makeGen);
|
|
||||||
if (!genInit)
|
if (!genInit)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -671,6 +680,20 @@ class FullParseHandler
|
||||||
!node->pn_funbox->isArrow();
|
!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) {
|
void setFunctionFormalParametersAndBody(ParseNode* funcNode, ParseNode* kid) {
|
||||||
MOZ_ASSERT_IF(kid, kid->isKind(PNK_PARAMSBODY));
|
MOZ_ASSERT_IF(kid, kid->isKind(PNK_PARAMSBODY));
|
||||||
funcNode->pn_body = kid;
|
funcNode->pn_body = kid;
|
||||||
|
@ -681,11 +704,11 @@ class FullParseHandler
|
||||||
funbox->functionNode = pn;
|
funbox->functionNode = pn;
|
||||||
}
|
}
|
||||||
void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) {
|
void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) {
|
||||||
pn->pn_body->append(argpn);
|
addList(/* list = */ pn->pn_body, /* child = */ argpn);
|
||||||
}
|
}
|
||||||
void setFunctionBody(ParseNode* fn, ParseNode* body) {
|
void setFunctionBody(ParseNode* fn, ParseNode* body) {
|
||||||
MOZ_ASSERT(fn->pn_body->isKind(PNK_PARAMSBODY));
|
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) {
|
ParseNode* newModule(const TokenPos& pos) {
|
||||||
|
@ -701,7 +724,7 @@ class FullParseHandler
|
||||||
if (!newExpr)
|
if (!newExpr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
addList(newExpr, ctor);
|
addList(/* list = */ newExpr, /* child = */ ctor);
|
||||||
return newExpr;
|
return newExpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,6 +767,13 @@ class FullParseHandler
|
||||||
return node->isKind(PNK_SUPERBASE);
|
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);
|
inline MOZ_MUST_USE bool finishInitializerAssignment(ParseNode* pn, ParseNode* init);
|
||||||
|
|
||||||
void setBeginPosition(ParseNode* pn, ParseNode* oth) {
|
void setBeginPosition(ParseNode* pn, ParseNode* oth) {
|
||||||
|
@ -805,7 +835,10 @@ class FullParseHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
void addList(ParseNode* list, ParseNode* kid) {
|
void addList(ParseNode* list, ParseNode* kid) {
|
||||||
|
if (sourceKind_ == SourceKind::Text)
|
||||||
list->append(kid);
|
list->append(kid);
|
||||||
|
else
|
||||||
|
list->appendWithoutOrderAssumption(kid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOp(ParseNode* pn, JSOp op) {
|
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
|
inline bool
|
||||||
FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
|
FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
|
||||||
ParseNode* defaultValue)
|
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
|
#ifndef frontend_ParseContext_h
|
||||||
#define frontend_ParseContext_h
|
#define frontend_ParseContext_h
|
||||||
|
|
||||||
|
#include "ds/Nestable.h"
|
||||||
|
|
||||||
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "frontend/ErrorReporter.h"
|
#include "frontend/ErrorReporter.h"
|
||||||
|
#include "frontend/SharedContext.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
|
@ -289,6 +293,10 @@ class ParseContext : public Nestable<ParseContext>
|
||||||
return declared_.acquire(pc->sc()->context);
|
return declared_.acquire(pc->sc()->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isEmpty() const {
|
||||||
|
return declared_->all().empty();
|
||||||
|
}
|
||||||
|
|
||||||
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
|
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
|
||||||
return declared_->lookup(name);
|
return declared_->lookup(name);
|
||||||
}
|
}
|
||||||
|
@ -402,6 +410,7 @@ class ParseContext : public Nestable<ParseContext>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit inline VarScope(ParserBase* parser);
|
explicit inline VarScope(ParserBase* parser);
|
||||||
|
explicit inline VarScope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames);
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -517,6 +526,7 @@ class ParseContext : public Nestable<ParseContext>
|
||||||
return sc_;
|
return sc_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `true` if we are in the body of a function definition.
|
||||||
bool isFunctionBox() const {
|
bool isFunctionBox() const {
|
||||||
return sc_->isFunctionBox();
|
return sc_->isFunctionBox();
|
||||||
}
|
}
|
||||||
|
@ -578,6 +588,22 @@ class ParseContext : public Nestable<ParseContext>
|
||||||
return *closedOverBindingsForLazy_;
|
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.
|
// 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
|
// 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:
|
// body level, but we would not encounter f3 or f4 at body level:
|
||||||
|
|
|
@ -13,6 +13,27 @@
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
#include "vm/Printer.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 js {
|
||||||
namespace frontend {
|
namespace frontend {
|
||||||
|
|
||||||
|
@ -598,6 +619,7 @@ class ParseNode
|
||||||
appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
|
appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
|
||||||
FullParseHandler* handler, ParseContext* pc);
|
FullParseHandler* handler, ParseContext* pc);
|
||||||
|
|
||||||
|
// include "ParseNode-inl.h" for these methods.
|
||||||
inline PropertyName* name() const;
|
inline PropertyName* name() const;
|
||||||
inline JSAtom* atom() const;
|
inline JSAtom* atom() const;
|
||||||
|
|
||||||
|
@ -731,8 +753,12 @@ class ParseNode
|
||||||
}
|
}
|
||||||
|
|
||||||
void append(ParseNode* pn) {
|
void append(ParseNode* pn) {
|
||||||
MOZ_ASSERT(pn_arity == PN_LIST);
|
|
||||||
MOZ_ASSERT(pn->pn_pos.begin >= pn_pos.begin);
|
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_pos.end = pn->pn_pos.end;
|
||||||
*pn_tail = pn;
|
*pn_tail = pn;
|
||||||
pn_tail = &pn->pn_next;
|
pn_tail = &pn->pn_next;
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
||||||
|
#include "frontend/ParseContext-inl.h"
|
||||||
#include "frontend/ParseNode-inl.h"
|
#include "frontend/ParseNode-inl.h"
|
||||||
#include "vm/EnvironmentObject-inl.h"
|
#include "vm/EnvironmentObject-inl.h"
|
||||||
|
|
||||||
|
@ -1714,7 +1715,7 @@ Parser<FullParseHandler, CharT>::checkStatementsEOF()
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Scope>
|
template <typename Scope>
|
||||||
static typename Scope::Data*
|
typename Scope::Data*
|
||||||
NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
|
NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
|
||||||
{
|
{
|
||||||
size_t allocSize = Scope::sizeOfData(numBindings);
|
size_t allocSize = Scope::sizeOfData(numBindings);
|
||||||
|
@ -1728,8 +1729,9 @@ NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<GlobalScope::Data*>
|
Maybe<GlobalScope::Data*>
|
||||||
ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
|
NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||||
{
|
{
|
||||||
|
|
||||||
Vector<BindingName> funs(context);
|
Vector<BindingName> funs(context);
|
||||||
Vector<BindingName> vars(context);
|
Vector<BindingName> vars(context);
|
||||||
Vector<BindingName> lets(context);
|
Vector<BindingName> lets(context);
|
||||||
|
@ -1792,8 +1794,14 @@ ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
|
||||||
return Some(bindings);
|
return Some(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<GlobalScope::Data*>
|
||||||
|
ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
|
||||||
|
{
|
||||||
|
return NewGlobalScopeData(context, scope, alloc, pc);
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<ModuleScope::Data*>
|
Maybe<ModuleScope::Data*>
|
||||||
ParserBase::newModuleScopeData(ParseContext::Scope& scope)
|
NewModuleScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||||
{
|
{
|
||||||
Vector<BindingName> imports(context);
|
Vector<BindingName> imports(context);
|
||||||
Vector<BindingName> vars(context);
|
Vector<BindingName> vars(context);
|
||||||
|
@ -1858,8 +1866,14 @@ ParserBase::newModuleScopeData(ParseContext::Scope& scope)
|
||||||
return Some(bindings);
|
return Some(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<ModuleScope::Data*>
|
||||||
|
ParserBase::newModuleScopeData(ParseContext::Scope& scope)
|
||||||
|
{
|
||||||
|
return NewModuleScopeData(context, scope, alloc, pc);
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<EvalScope::Data*>
|
Maybe<EvalScope::Data*>
|
||||||
ParserBase::newEvalScopeData(ParseContext::Scope& scope)
|
NewEvalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||||
{
|
{
|
||||||
Vector<BindingName> funs(context);
|
Vector<BindingName> funs(context);
|
||||||
Vector<BindingName> vars(context);
|
Vector<BindingName> vars(context);
|
||||||
|
@ -1902,8 +1916,14 @@ ParserBase::newEvalScopeData(ParseContext::Scope& scope)
|
||||||
return Some(bindings);
|
return Some(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<EvalScope::Data*>
|
||||||
|
ParserBase::newEvalScopeData(ParseContext::Scope& scope)
|
||||||
|
{
|
||||||
|
return NewEvalScopeData(context, scope, alloc, pc);
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<FunctionScope::Data*>
|
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> positionalFormals(context);
|
||||||
Vector<BindingName> formals(context);
|
Vector<BindingName> formals(context);
|
||||||
|
@ -1996,8 +2016,14 @@ ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterEx
|
||||||
return Some(bindings);
|
return Some(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<FunctionScope::Data*>
|
||||||
|
ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
|
||||||
|
{
|
||||||
|
return NewFunctionScopeData(context, scope, hasParameterExprs, alloc, pc);
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<VarScope::Data*>
|
Maybe<VarScope::Data*>
|
||||||
ParserBase::newVarScopeData(ParseContext::Scope& scope)
|
NewVarScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||||
{
|
{
|
||||||
Vector<BindingName> vars(context);
|
Vector<BindingName> vars(context);
|
||||||
|
|
||||||
|
@ -2028,8 +2054,14 @@ ParserBase::newVarScopeData(ParseContext::Scope& scope)
|
||||||
return Some(bindings);
|
return Some(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<VarScope::Data*>
|
||||||
|
ParserBase::newVarScopeData(ParseContext::Scope& scope)
|
||||||
|
{
|
||||||
|
return NewVarScopeData(context, scope, alloc, pc);
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<LexicalScope::Data*>
|
Maybe<LexicalScope::Data*>
|
||||||
ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
|
NewLexicalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
|
||||||
{
|
{
|
||||||
Vector<BindingName> lets(context);
|
Vector<BindingName> lets(context);
|
||||||
Vector<BindingName> consts(context);
|
Vector<BindingName> consts(context);
|
||||||
|
@ -2079,6 +2111,12 @@ ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
|
||||||
return Some(bindings);
|
return Some(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<LexicalScope::Data*>
|
||||||
|
ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
|
||||||
|
{
|
||||||
|
return NewLexicalScopeData(context, scope, alloc, pc);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
SyntaxParseHandler::Node
|
SyntaxParseHandler::Node
|
||||||
PerHandlerParser<SyntaxParseHandler>::finishLexicalScope(ParseContext::Scope& scope, Node body)
|
PerHandlerParser<SyntaxParseHandler>::finishLexicalScope(ParseContext::Scope& scope, Node body)
|
||||||
|
@ -6513,44 +6551,16 @@ GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandlin
|
||||||
if (!matchLabel(yieldHandling, &label))
|
if (!matchLabel(yieldHandling, &label))
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
// Labeled 'continue' statements target the nearest labeled loop
|
auto validity = pc->checkContinueStatement(label);
|
||||||
// statements with the same label. Unlabeled 'continue' statements target
|
if (validity.isErr()) {
|
||||||
// the innermost loop statement.
|
switch (validity.unwrapErr()) {
|
||||||
auto isLoop = [](ParseContext::Statement* stmt) {
|
case ParseContext::ContinueStatementError::NotInALoop:
|
||||||
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);
|
errorAt(begin, JSMSG_BAD_CONTINUE);
|
||||||
return null();
|
break;
|
||||||
}
|
case ParseContext::ContinueStatementError::LabelNotFound:
|
||||||
|
error(JSMSG_LABEL_NOT_FOUND);
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
stmt = stmt->enclosing();
|
|
||||||
}
|
|
||||||
if (foundTarget)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (!pc->findInnermostStatement(isLoop)) {
|
|
||||||
error(JSMSG_BAD_CONTINUE);
|
|
||||||
return null();
|
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>
|
template <typename T>
|
||||||
inline T&
|
inline T&
|
||||||
ParseContext::Statement::as()
|
ParseContext::Statement::as()
|
||||||
|
@ -439,6 +425,13 @@ ParseContext::VarScope::VarScope(ParserBase* parser)
|
||||||
useAsVarScope(parser->pc);
|
useAsVarScope(parser->pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ParseContext::VarScope::VarScope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames)
|
||||||
|
: Scope(cx, pc, usedNames)
|
||||||
|
{
|
||||||
|
useAsVarScope(pc);
|
||||||
|
}
|
||||||
|
|
||||||
enum FunctionCallBehavior {
|
enum FunctionCallBehavior {
|
||||||
PermitAssignmentToFunctionCalls,
|
PermitAssignmentToFunctionCalls,
|
||||||
ForbidAssignmentToFunctionCalls
|
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 frontend */
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,14 @@
|
||||||
|
|
||||||
#include "builtin/ModuleObject.h"
|
#include "builtin/ModuleObject.h"
|
||||||
#include "ds/InlineTable.h"
|
#include "ds/InlineTable.h"
|
||||||
|
#include "frontend/ParseNode.h"
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
#include "vm/EnvironmentObject.h"
|
#include "vm/EnvironmentObject.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace frontend {
|
namespace frontend {
|
||||||
|
|
||||||
|
class ParseContext;
|
||||||
class ParseNode;
|
class ParseNode;
|
||||||
|
|
||||||
enum class StatementKind : uint8_t
|
enum class StatementKind : uint8_t
|
||||||
|
|
|
@ -86,17 +86,6 @@ JS::RootingContext::traceStackRoots(JSTracer* trc)
|
||||||
TraceStackRoots(trc, stackRoots_);
|
TraceStackRoots(trc, stackRoots_);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace js {
|
|
||||||
namespace frontend {
|
|
||||||
|
|
||||||
// Placeholder implementation.
|
|
||||||
void
|
|
||||||
TraceBinParser(JSTracer*, JS::AutoGCRooter*)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
TraceExactStackRoots(const CooperatingContext& target, JSTracer* trc)
|
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);
|
rt->heapRoots.ref()[kind].insertBack(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -648,7 +648,10 @@ if CONFIG['JS_BUILD_BINAST']:
|
||||||
# specification.
|
# specification.
|
||||||
SOURCES += ['frontend/BinTokenReaderTester.cpp']
|
SOURCES += ['frontend/BinTokenReaderTester.cpp']
|
||||||
# These parts of BinAST should eventually move to release.
|
# 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
|
# 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
|
# so that it is easy to use the huge-mapping optimization for other
|
||||||
|
|
Загрузка…
Ссылка в новой задаче