fune/js/src/frontend/BinASTParserPerTokenizer.cpp

821 lines
27 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "frontend/BinASTParserPerTokenizer.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/Maybe.h"
#include "mozilla/Move.h"
#include "mozilla/PodOperations.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Vector.h"
#include "frontend/BinAST-macros.h"
#include "frontend/BinASTParser.h"
#include "frontend/BinASTTokenReaderContext.h"
#include "frontend/BinASTTokenReaderMultipart.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#include "js/Result.h"
#include "vm/RegExpObject.h"
#include "frontend/ParseContext-inl.h"
#include "frontend/SharedContext-inl.h"
#include "vm/JSContext-inl.h"
// # About compliance with EcmaScript
//
// For the moment, this parser implements ES5. Future versions will be extended
// to ES6 and further on.
//
// By design, it does NOT implement Annex B.3.3. If possible, we would like
// to avoid going down that rabbit hole.
//
//
// # About the AST
//
// At this stage of experimentation, the AST specifications change often. This
// version of the parser attempts to implement
// https://gist.github.com/Yoric/2390f0367515c079172be2526349b294
//
//
// # About validating the AST
//
// Normally, this implementation validates all properties of the AST *except*
// the order of fields, which is partially constrained by the AST spec (e.g. in
// a block, field `scope` must appear before field `body`, etc.).
//
//
// # About names and scopes
//
// One of the key objectives of the BinAST syntax is to be able to entirely skip
// parsing inner functions until they are needed. With a purely syntactic AST,
// this is generally impossible, as we would need to walk the AST to find
// lexically-bound/var-bound variables, instances of direct eval, etc.
//
// To achieve this, BinAST files contain scope data, as instances of
// `BinJS:Scope` nodes. Rather than walking the AST to assign bindings
// to scopes, we extract data from the `BinJS:Scope` and check it lazily,
// once we actually need to walk the AST.
//
// WARNING: The current implementation DOES NOT perform the check yet. It
// is therefore unsafe.
//
// # About directives
//
// Currently, directives are ignored and treated as regular strings.
//
// They should be treated lazily (whenever we open a subscope), like bindings.
namespace js {
namespace frontend {
using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
// ------------- Toplevel constructions
template <typename Tok>
BinASTParserPerTokenizer<Tok>::BinASTParserPerTokenizer(
JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames,
const JS::ReadOnlyCompileOptions& options,
HandleScriptSourceObject sourceObject,
Handle<LazyScript*> lazyScript /* = nullptr */)
: BinASTParserBase(cx, alloc, usedNames, sourceObject),
options_(options),
lazyScript_(cx, lazyScript),
handler_(cx, alloc, nullptr, SourceKind::Binary),
variableDeclarationKind_(VariableDeclarationKind::Var) {
MOZ_ASSERT_IF(lazyScript_, lazyScript_->isBinAST());
}
template <typename Tok>
JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parse(
GlobalSharedContext* globalsc, const Vector<uint8_t>& data,
BinASTSourceMetadata** metadataPtr) {
return parse(globalsc, data.begin(), data.length(), metadataPtr);
}
template <typename Tok>
JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parse(
GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
BinASTSourceMetadata** metadataPtr) {
auto result = parseAux(globalsc, start, length, metadataPtr);
poison(); // Make sure that the parser is never used again accidentally.
return result;
}
template <typename Tok>
JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parseAux(
GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
BinASTSourceMetadata** metadataPtr) {
MOZ_ASSERT(globalsc);
tokenizer_.emplace(cx_, this, start, length);
BinASTParseContext globalpc(cx_, this, globalsc,
/* newDirectives = */ nullptr);
if (!globalpc.init()) {
return cx_->alreadyReportedError();
}
ParseContext::VarScope varScope(cx_, &globalpc, usedNames_);
if (!varScope.init(&globalpc)) {
return cx_->alreadyReportedError();
}
MOZ_TRY(tokenizer_->readHeader());
ParseNode* result(nullptr);
const Context topContext(Context::topLevel());
MOZ_TRY_VAR(result, asFinalParser()->parseProgram(topContext));
mozilla::Maybe<GlobalScope::Data*> bindings =
NewGlobalScopeData(cx_, varScope, alloc_, pc_);
if (!bindings) {
return cx_->alreadyReportedError();
}
globalsc->bindings = *bindings;
if (metadataPtr) {
*metadataPtr = tokenizer_->takeMetadata();
}
return result; // Magic conversion to Ok.
}
template <typename Tok>
JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::parseLazyFunction(
ScriptSource* scriptSource, const size_t firstOffset) {
MOZ_ASSERT(lazyScript_);
MOZ_ASSERT(scriptSource->length() > firstOffset);
tokenizer_.emplace(cx_, this, scriptSource->binASTSource(),
scriptSource->length());
MOZ_TRY(tokenizer_->initFromScriptSource(scriptSource));
tokenizer_->seek(firstOffset);
// For now, only function declarations and function expression are supported.
RootedFunction func(cx_, lazyScript_->functionNonDelazifying());
bool isExpr = func->isLambda();
MOZ_ASSERT(func->kind() == JSFunction::FunctionKind::NormalFunction);
// Poison the tokenizer when we leave to ensure that it's not used again by
// accident.
auto onExit = mozilla::MakeScopeExit([&]() { poison(); });
// TODO: This should be actually shared with the auto-generated version.
auto syntaxKind =
isExpr ? FunctionSyntaxKind::Expression : FunctionSyntaxKind::Statement;
BINJS_MOZ_TRY_DECL(
funbox, buildFunctionBox(lazyScript_->generatorKind(),
lazyScript_->asyncKind(), syntaxKind, nullptr));
// Push a new ParseContext. It will be used to parse `scope`, the arguments,
// the function.
BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
BINJS_TRY(funpc.init());
pc_->functionScope().useAsVarScope(pc_);
MOZ_ASSERT(pc_->isFunctionBox());
ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
BINJS_TRY(lexicalScope.init(pc_));
ListNode* params;
ListNode* tmpBody;
auto parseFunc = isExpr ? &FinalParser::parseFunctionExpressionContents
: &FinalParser::parseFunctionOrMethodContents;
// Inject a toplevel context (i.e. no parent) to parse the lazy content.
// In the future, we may move this to a more specific context.
const Context context(Context::topLevel());
MOZ_TRY(
(asFinalParser()->*parseFunc)(func->nargs(), &params, &tmpBody, context));
BINJS_TRY_DECL(lexicalScopeData,
NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
BINJS_TRY_DECL(body, handler_.newLexicalScope(*lexicalScopeData, tmpBody));
auto binASTKind = isExpr ? BinASTKind::LazyFunctionExpression
: BinASTKind::LazyFunctionDeclaration;
return buildFunction(firstOffset, binASTKind, nullptr, params, body);
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::forceStrictIfNecessary(
SharedContext* sc, ListNode* directives) {
JSAtom* useStrict = cx_->names().useStrict;
for (const ParseNode* directive : directives->contents()) {
if (directive->as<NameNode>().atom() == useStrict) {
sc->strictScript = true;
break;
}
}
}
template <typename Tok>
JS::Result<FunctionBox*> BinASTParserPerTokenizer<Tok>::buildFunctionBox(
GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind,
FunctionSyntaxKind syntax, ParseNode* name) {
MOZ_ASSERT_IF(!pc_, lazyScript_);
RootedAtom atom(cx_);
// Name might be any of {Identifier,ComputedPropertyName,LiteralPropertyName}
if (name && name->is<NameNode>()) {
atom = name->as<NameNode>().atom();
}
if (pc_ && syntax == FunctionSyntaxKind::Statement) {
auto ptr = pc_->varScope().lookupDeclaredName(atom);
if (!ptr) {
return raiseError(
"FunctionDeclaration without corresponding AssertedDeclaredName.");
}
DeclarationKind declaredKind = ptr->value()->kind();
if (DeclarationKindIsVar(declaredKind)) {
if (!pc_->atBodyLevel()) {
return raiseError(
"body-level FunctionDeclaration inside non-body-level context.");
}
RedeclareVar(ptr, DeclarationKind::BodyLevelFunction);
}
}
// Allocate the function before walking down the tree.
RootedFunction fun(cx_);
BINJS_TRY_VAR(fun, !pc_ ? lazyScript_->functionNonDelazifying()
: AllocNewFunction(cx_, atom, syntax, generatorKind,
functionAsyncKind, nullptr));
MOZ_ASSERT_IF(pc_, fun->explicitName() == atom);
mozilla::Maybe<Directives> directives;
if (pc_) {
directives.emplace(pc_);
} else {
directives.emplace(lazyScript_->strict());
}
auto* funbox = alloc_.new_<FunctionBox>(
cx_, traceListHead_, fun, /* toStringStart = */ 0, *directives,
/* extraWarning = */ false, generatorKind, functionAsyncKind);
if (!funbox) {
return raiseOOM();
}
traceListHead_ = funbox;
if (pc_) {
funbox->initWithEnclosingParseContext(pc_, fun,syntax);
} else {
funbox->initFromLazyFunction(fun);
}
return funbox;
}
FunctionSyntaxKind BinASTKindToFunctionSyntaxKind(const BinASTKind kind) {
// FIXME: this doesn't cover FunctionSyntaxKind::ClassConstructor and
// FunctionSyntaxKind::DerivedClassConstructor.
switch (kind) {
case BinASTKind::EagerFunctionDeclaration:
case BinASTKind::LazyFunctionDeclaration:
return FunctionSyntaxKind::Statement;
case BinASTKind::EagerFunctionExpression:
case BinASTKind::LazyFunctionExpression:
return FunctionSyntaxKind::Expression;
case BinASTKind::EagerArrowExpressionWithFunctionBody:
case BinASTKind::LazyArrowExpressionWithFunctionBody:
case BinASTKind::EagerArrowExpressionWithExpression:
case BinASTKind::LazyArrowExpressionWithExpression:
return FunctionSyntaxKind::Arrow;
case BinASTKind::EagerMethod:
case BinASTKind::LazyMethod:
return FunctionSyntaxKind::Method;
case BinASTKind::EagerGetter:
case BinASTKind::LazyGetter:
return FunctionSyntaxKind::Getter;
case BinASTKind::EagerSetter:
case BinASTKind::LazySetter:
return FunctionSyntaxKind::Setter;
default:
MOZ_CRASH("Invalid/ kind");
}
}
template <typename Tok>
JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::makeEmptyFunctionNode(
const size_t start, const BinASTKind kind, FunctionBox* funbox) {
// LazyScript compilation requires basically none of the fields filled out.
TokenPos pos = tokenizer_->pos(start);
FunctionSyntaxKind syntaxKind = BinASTKindToFunctionSyntaxKind(kind);
BINJS_TRY_DECL(result, handler_.newFunction(syntaxKind, pos));
handler_.setFunctionBox(result, funbox);
return result;
}
template <typename Tok>
JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::buildFunction(
const size_t start, const BinASTKind kind, ParseNode* name,
ListNode* params, ParseNode* body) {
FunctionBox* funbox = pc_->functionBox();
// Set the argument count for building argument packets. Function.length is
// handled by setting the appropriate funbox field during argument parsing.
if (!lazyScript_ ||
lazyScript_->functionNonDelazifying() != funbox->function()) {
funbox->function()->setArgCount(params ? uint16_t(params->count()) : 0);
}
// ParseNode represents the body as concatenated after the params.
params->appendWithoutOrderAssumption(body);
BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(start, kind, funbox));
handler_.setFunctionFormalParametersAndBody(result, params);
if (funbox->needsDotGeneratorName()) {
BINJS_TRY(pc_->declareDotGeneratorName());
HandlePropertyName dotGenerator = cx_->names().dotGenerator;
BINJS_TRY(usedNames_.noteUse(cx_, dotGenerator, pc_->scriptId(),
pc_->innermostScope()->id()));
if (funbox->isGenerator()) {
BINJS_TRY_DECL(
dotGen, handler_.newName(dotGenerator,
tokenizer_->pos(tokenizer_->offset()), cx_));
ListNode* stmtList =
&body->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
BINJS_TRY(handler_.prependInitialYield(stmtList, dotGen));
}
}
const bool canSkipLazyClosedOverBindings = false;
BINJS_TRY(pc_->declareFunctionArgumentsObject(usedNames_,
canSkipLazyClosedOverBindings));
BINJS_TRY(
pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings));
// Check all our bindings after maybe adding function metavars.
MOZ_TRY(checkFunctionClosedVars());
BINJS_TRY_DECL(bindings,
NewFunctionScopeData(cx_, pc_->functionScope(),
/* hasParameterExprs = */ false,
IsFieldInitializer::No, alloc_, pc_));
funbox->functionScopeBindings().set(*bindings);
if (funbox->isNamedLambda()) {
BINJS_TRY_DECL(
recursiveBinding,
NewLexicalScopeData(cx_, pc_->namedLambdaScope(), alloc_, pc_));
funbox->namedLambdaBindings().set(*recursiveBinding);
}
return result;
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::addScopeName(
AssertedScopeKind scopeKind, HandleAtom name, ParseContext::Scope* scope,
DeclarationKind declKind, bool isCaptured, bool allowDuplicateName) {
auto ptr = scope->lookupDeclaredNameForAdd(name);
if (ptr) {
if (allowDuplicateName) {
return Ok();
}
return raiseError("Variable redeclaration");
}
BINJS_TRY(scope->addDeclaredName(pc_, ptr, name.get(), declKind,
tokenizer_->offset()));
if (isCaptured) {
auto declaredPtr = scope->lookupDeclaredName(name);
MOZ_ASSERT(declaredPtr);
declaredPtr->value()->setClosedOver();
}
return Ok();
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::captureFunctionName() {
MOZ_ASSERT(pc_->isFunctionBox());
MOZ_ASSERT(pc_->functionBox()->isNamedLambda());
RootedAtom funName(cx_, pc_->functionBox()->explicitName());
MOZ_ASSERT(funName);
auto ptr = pc_->namedLambdaScope().lookupDeclaredName(funName);
MOZ_ASSERT(ptr);
ptr->value()->setClosedOver();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::getDeclaredScope(
AssertedScopeKind scopeKind, AssertedDeclaredKind kind,
ParseContext::Scope*& scope, DeclarationKind& declKind) {
MOZ_ASSERT(scopeKind == AssertedScopeKind::Block ||
scopeKind == AssertedScopeKind::Global ||
scopeKind == AssertedScopeKind::Var);
switch (kind) {
case AssertedDeclaredKind::Var:
if (scopeKind == AssertedScopeKind::Block) {
return raiseError("AssertedBlockScope cannot contain 'var' binding");
}
declKind = DeclarationKind::Var;
scope = &pc_->varScope();
break;
case AssertedDeclaredKind::NonConstLexical:
declKind = DeclarationKind::Let;
scope = pc_->innermostScope();
break;
case AssertedDeclaredKind::ConstLexical:
declKind = DeclarationKind::Const;
scope = pc_->innermostScope();
break;
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::getBoundScope(
AssertedScopeKind scopeKind, ParseContext::Scope*& scope,
DeclarationKind& declKind) {
MOZ_ASSERT(scopeKind == AssertedScopeKind::Catch ||
scopeKind == AssertedScopeKind::Parameter);
switch (scopeKind) {
case AssertedScopeKind::Catch:
declKind = DeclarationKind::CatchParameter;
scope = pc_->innermostScope();
break;
case AssertedScopeKind::Parameter:
MOZ_ASSERT(pc_->isFunctionBox());
declKind = DeclarationKind::PositionalFormalParameter;
scope = &pc_->functionScope();
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected AssertedScopeKind");
break;
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkBinding(JSAtom* name) {
// Check that the variable appears in the corresponding scope.
ParseContext::Scope& scope =
variableDeclarationKind_ == VariableDeclarationKind::Var
? pc_->varScope()
: *pc_->innermostScope();
auto ptr = scope.lookupDeclaredName(name->asPropertyName());
if (!ptr) {
return raiseMissingVariableInAssertedScope(name);
}
return Ok();
}
// Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
// 3.1.5 CheckPositionalParameterIndices.
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkPositionalParameterIndices(
Handle<GCVector<JSAtom*>> positionalParams, ListNode* params) {
// positionalParams should have the corresponding entry up to the last
// positional parameter.
// `positionalParams` corresponds to `expectedParams` parameter in the spec.
// `params` corresponds to `parseTree` in 3.1.9 CheckAssertedScope, and
// `positionalParamNames` parameter
// Steps 1-3.
// PositionalParameterNames (3.1.9 CheckAssertedScope step 5.d) and
// CreatePositionalParameterIndices (3.1.5 CheckPositionalParameterIndices
// step 1) are done implicitly.
uint32_t i = 0;
const bool hasRest = pc_->functionBox()->hasRest();
for (ParseNode* param : params->contents()) {
if (param->isKind(ParseNodeKind::AssignExpr)) {
param = param->as<AssignmentNode>().left();
}
// At this point, function body is not part of params list.
const bool isRest = hasRest && !param->pn_next;
if (isRest) {
// Rest parameter
// Step 3.
if (i >= positionalParams.get().length()) {
continue;
}
if (positionalParams.get()[i]) {
return raiseError(
"Expected positional parameter per "
"AssertedParameterScope.paramNames, got rest parameter");
}
} else if (param->isKind(ParseNodeKind::Name)) {
// Simple or default parameter.
// Step 2.a.
if (i >= positionalParams.get().length()) {
return raiseError(
"AssertedParameterScope.paramNames doesn't have corresponding "
"entry to positional parameter");
}
JSAtom* name = positionalParams.get()[i];
if (!name) {
// Step 2.a.ii.1.
return raiseError(
"Expected destructuring/rest parameter per "
"AssertedParameterScope.paramNames, got positional parameter");
}
// Step 2.a.i.
if (param->as<NameNode>().name() != name) {
// Step 2.a.ii.1.
return raiseError(
"Name mismatch between AssertedPositionalParameterName in "
"AssertedParameterScope.paramNames and actual parameter");
}
// Step 2.a.i.1.
// Implicitly done.
} else {
// Destructuring parameter.
MOZ_ASSERT(param->isKind(ParseNodeKind::ObjectExpr) ||
param->isKind(ParseNodeKind::ArrayExpr));
// Step 3.
if (i >= positionalParams.get().length()) {
continue;
}
if (positionalParams.get()[i]) {
return raiseError(
"Expected positional parameter per "
"AssertedParameterScope.paramNames, got destructuring parameter");
}
}
i++;
}
// Step 3.
if (positionalParams.get().length() > params->count()) {
// `positionalParams` has unchecked entries.
return raiseError(
"AssertedParameterScope.paramNames has unmatching items than the "
"actual parameters");
}
return Ok();
}
// Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
// 3.1.13 CheckFunctionLength.
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkFunctionLength(
uint32_t expectedLength) {
if (pc_->functionBox()->length != expectedLength) {
return raiseError("Function length does't match");
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkClosedVars(
ParseContext::Scope& scope) {
for (ParseContext::Scope::BindingIter bi = scope.bindings(pc_); bi; bi++) {
if (UsedNamePtr p = usedNames_.lookup(bi.name())) {
bool closedOver;
p->value().noteBoundInScope(pc_->scriptId(), scope.id(), &closedOver);
if (closedOver && !bi.closedOver()) {
return raiseInvalidClosedVar(bi.name());
}
}
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkFunctionClosedVars() {
MOZ_ASSERT(pc_->isFunctionBox());
MOZ_TRY(checkClosedVars(*pc_->innermostScope()));
MOZ_TRY(checkClosedVars(pc_->functionScope()));
if (pc_->functionBox()->isNamedLambda()) {
MOZ_TRY(checkClosedVars(pc_->namedLambdaScope()));
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::prependDirectivesToBody(
ListNode* body, ListNode* directives) {
if (!directives) {
return Ok();
}
if (directives->empty()) {
return Ok();
}
MOZ_TRY(prependDirectivesImpl(body, directives->head()));
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::prependDirectivesImpl(
ListNode* body, ParseNode* directive) {
BINJS_TRY(CheckRecursionLimit(cx_));
// Prepend in the reverse order by using stack, so that the directives are
// prepended in the original order.
if (ParseNode* next = directive->pn_next) {
MOZ_TRY(prependDirectivesImpl(body, next));
}
BINJS_TRY_DECL(statement,
handler_.newExprStatement(directive, directive->pn_pos.end));
body->prependAndUpdatePos(statement);
return Ok();
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseInvalidClosedVar(JSAtom* name) {
return raiseError("Captured variable was not declared as captured");
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseMissingVariableInAssertedScope(
JSAtom* name) {
// For the moment, we don't trust inputs sufficiently to put the name
// in an error message.
return raiseError("Missing variable in AssertedScope");
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseMissingDirectEvalInAssertedScope() {
return raiseError("Direct call to `eval` was not declared in AssertedScope");
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseInvalidKind(const char* superKind,
const BinASTKind kind) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, invalid kind %s", superKind,
describeBinASTKind(kind)));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseInvalidVariant(const char* kind,
const BinASTVariant value) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, invalid variant '%s'", kind,
describeBinASTVariant(value)));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseMissingField(const char* kind,
const BinASTField field) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, missing field '%s'", kind,
describeBinASTField(field)));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseEmpty(const char* description) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("Empty %s", description));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseOOM() {
return tokenizer_->raiseOOM();
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseError(BinASTKind kind,
const char* description) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, %s", describeBinASTKind(kind), description));
return tokenizer_->raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseError(const char* description) {
return tokenizer_->raiseError(description);
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::poison() {
tokenizer_.reset();
}
template <typename Tok>
bool BinASTParserPerTokenizer<Tok>::computeErrorMetadata(
ErrorMetadata* err, const ErrorOffset& errorOffset) {
err->filename = getFilename();
err->lineNumber = 0;
if (errorOffset.is<uint32_t>()) {
err->columnNumber = errorOffset.as<uint32_t>();
} else if (errorOffset.is<Current>()) {
err->columnNumber = offset();
} else {
errorOffset.is<NoOffset>();
err->columnNumber = 0;
}
err->isMuted = options().mutedErrors();
return true;
}
void TraceBinASTParser(JSTracer* trc, JS::AutoGCRooter* parser) {
static_cast<BinASTParserBase*>(parser)->trace(trc);
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::doTrace(JSTracer* trc) {
if (tokenizer_) {
tokenizer_->traceMetadata(trc);
}
}
template <typename Tok>
inline typename BinASTParserPerTokenizer<Tok>::FinalParser*
BinASTParserPerTokenizer<Tok>::asFinalParser() {
// Same as GeneralParser::asFinalParser, verify the inheritance to
// make sure the static downcast works.
static_assert(
mozilla::IsBaseOf<BinASTParserPerTokenizer<Tok>, FinalParser>::value,
"inheritance relationship required by the static_cast<> below");
return static_cast<FinalParser*>(this);
}
template <typename Tok>
inline const typename BinASTParserPerTokenizer<Tok>::FinalParser*
BinASTParserPerTokenizer<Tok>::asFinalParser() const {
static_assert(
mozilla::IsBaseOf<BinASTParserPerTokenizer<Tok>, FinalParser>::value,
"inheritance relationship required by the static_cast<> below");
return static_cast<const FinalParser*>(this);
}
// Force class instantiation.
// This ensures that the symbols are built, without having to export all our
// code (and its baggage of #include and macros) in the header.
template class BinASTParserPerTokenizer<BinASTTokenReaderContext>;
template class BinASTParserPerTokenizer<BinASTTokenReaderMultipart>;
} // namespace frontend
} // namespace js