/* -*- 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/. */
/* JS reflection package. */
#include "mozilla/DebugOnly.h"
#include <stdlib.h>
#include <utility>
#include "jspubtd.h"
#include "builtin/Array.h"
#include "frontend/CompilationStencil.h"
#include "frontend/FrontendContext.h" // AutoReportFrontendContext
#include "frontend/ModuleSharedContext.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
#include "js/PropertyAndElement.h" // JS_DefineFunction
#include "js/StableStringChars.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/Interpreter.h"
#include "vm/JSAtomUtils.h" // Atomize, AtomizeUTF8Chars
#include "vm/JSObject.h"
#include "vm/ModuleBuilder.h" // js::ModuleBuilder
#include "vm/PlainObject.h" // js::PlainObject
#include "vm/RegExpObject.h"
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/ObjectOperations-inl.h"
using namespace js;
using namespace js::frontend;
using JS::AutoStableStringChars;
using JS::CompileOptions;
using JS::RootedValueArray;
using mozilla::DebugOnly;
enum ASTType {
AST_ERROR = -1,
#define ASTDEF(ast, str) ast,
#include "jsast.tbl"
#undef ASTDEF
AST_LIMIT
};
enum AssignmentOperator {
AOP_ERR = -1,
/* assign */
AOP_ASSIGN = 0,
/* operator-assign */
AOP_PLUS,
AOP_MINUS,
AOP_STAR,
AOP_DIV,
AOP_MOD,
AOP_POW,
/* shift-assign */
AOP_LSH,
AOP_RSH,
AOP_URSH,
/* binary */
AOP_BITOR,
AOP_BITXOR,
AOP_BITAND,
/* short-circuit */
AOP_COALESCE,
AOP_OR,
AOP_AND,
AOP_LIMIT
};
enum BinaryOperator {
BINOP_ERR = -1,
/* eq */
BINOP_EQ = 0,
BINOP_NE,
BINOP_STRICTEQ,
BINOP_STRICTNE,
/* rel */
BINOP_LT,
BINOP_LE,
BINOP_GT,
BINOP_GE,
/* shift */
BINOP_LSH,
BINOP_RSH,
BINOP_URSH,
/* arithmetic */
BINOP_ADD,
BINOP_SUB,
BINOP_STAR,
BINOP_DIV,
BINOP_MOD,
BINOP_POW,
/* binary */
BINOP_BITOR,
BINOP_BITXOR,
BINOP_BITAND,
/* misc */
BINOP_IN,
BINOP_INSTANCEOF,
BINOP_COALESCE,
BINOP_LIMIT
};
enum UnaryOperator {
UNOP_ERR = -1,
UNOP_DELETE = 0,
UNOP_NEG,
UNOP_POS,
UNOP_NOT,
UNOP_BITNOT,
UNOP_TYPEOF,
UNOP_VOID,
UNOP_AWAIT,
UNOP_LIMIT
};
enum VarDeclKind {
VARDECL_ERR = -1,
VARDECL_VAR = 0,
VARDECL_CONST,
VARDECL_LET,
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
VARDECL_USING,
VARDECL_AWAIT_USING,
#endif
VARDECL_LIMIT
};
enum PropKind {
PROP_ERR = -1,
PROP_INIT = 0,
PROP_GETTER,
PROP_SETTER,
PROP_MUTATEPROTO,
PROP_LIMIT
};
static const char*
const aopNames[] = {
"=",
/* AOP_ASSIGN */
"+=",
/* AOP_PLUS */
"-=",
/* AOP_MINUS */
"*=",
/* AOP_STAR */
"/=",
/* AOP_DIV */
"%=",
/* AOP_MOD */
"**=",
/* AOP_POW */
"<<=",
/* AOP_LSH */
">>=",
/* AOP_RSH */
">>>=",
/* AOP_URSH */
"|=",
/* AOP_BITOR */
"^=",
/* AOP_BITXOR */
"&=",
/* AOP_BITAND */
"\?\?=",
/* AOP_COALESCE */
"||=",
/* AOP_OR */
"&&=",
/* AOP_AND */
};
static const char*
const binopNames[] = {
"==",
/* BINOP_EQ */
"!=",
/* BINOP_NE */
"===",
/* BINOP_STRICTEQ */
"!==",
/* BINOP_STRICTNE */
"<",
/* BINOP_LT */
"<=",
/* BINOP_LE */
">",
/* BINOP_GT */
">=",
/* BINOP_GE */
"<<",
/* BINOP_LSH */
">>",
/* BINOP_RSH */
">>>",
/* BINOP_URSH */
"+",
/* BINOP_PLUS */
"-",
/* BINOP_MINUS */
"*",
/* BINOP_STAR */
"/",
/* BINOP_DIV */
"%",
/* BINOP_MOD */
"**",
/* BINOP_POW */
"|",
/* BINOP_BITOR */
"^",
/* BINOP_BITXOR */
"&",
/* BINOP_BITAND */
"in",
/* BINOP_IN */
"instanceof",
/* BINOP_INSTANCEOF */
"??",
/* BINOP_COALESCE */
};
static const char*
const unopNames[] = {
"delete",
/* UNOP_DELETE */
"-",
/* UNOP_NEG */
"+",
/* UNOP_POS */
"!",
/* UNOP_NOT */
"~",
/* UNOP_BITNOT */
"typeof",
/* UNOP_TYPEOF */
"void",
/* UNOP_VOID */
"await",
/* UNOP_AWAIT */
};
static const char*
const nodeTypeNames[] = {
#define ASTDEF(ast, str) str,
#include "jsast.tbl"
#undef ASTDEF
nullptr};
enum YieldKind { Delegating, NotDelegating };
using NodeVector = RootedValueVector;
/*
* ParseNode is a somewhat intricate data structure, and its invariants have
* evolved, making it more likely that there could be a disconnect between the
* parser and the AST serializer. We use these macros to check invariants on a
* parse node and raise a dynamic error on failure.
*/
#define LOCAL_ASSERT(expr) \
JS_BEGIN_MACRO \
MOZ_ASSERT(expr); \
if (!(expr)) { \
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, \
JSMSG_BAD_PARSE_NODE); \
return false; \
} \
JS_END_MACRO
#define LOCAL_NOT_REACHED(expr) \
JS_BEGIN_MACRO \
MOZ_ASSERT(
false); \
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, \
JSMSG_BAD_PARSE_NODE); \
return false; \
JS_END_MACRO
namespace {
/* Set 'result' to obj[id] if any such property exists, else defaultValue. */
static bool GetPropertyDefault(JSContext* cx, HandleObject obj, HandleId id,
HandleValue defaultValue,
MutableHandleValue result) {
bool found;
if (!HasProperty(cx, obj, id, &found)) {
return false;
}
if (!found) {
result.set(defaultValue);
return true;
}
return GetProperty(cx, obj, obj, id, result);
}
enum class GeneratorStyle { None, ES6 };
/*
* Builder class that constructs JavaScript AST node objects.
*/
class NodeBuilder {
using CallbackArray = RootedValueArray<AST_LIMIT>;
JSContext* cx;
FrontendContext* fc;
frontend::Parser<frontend::FullParseHandler, char16_t>* parser;
bool saveLoc;
/* save source location information? */
char const* src;
/* UTF-8 encoded source filename or null */
RootedValue srcval;
/* source filename JS value or null */
public:
NodeBuilder(JSContext* c, FrontendContext* f,
bool l,
char const* s)
: cx(c), fc(f), parser(nullptr), saveLoc(l), src(s), srcval(c) {}
[[nodiscard]]
bool init() {
if (src) {
if (!atomValueUtf8(src, &srcval)) {
return false;
}
}
else {
srcval.setNull();
}
return true;
}
void setParser(frontend::Parser<frontend::FullParseHandler, char16_t>* p) {
parser = p;
}
private:
[[nodiscard]]
bool atomValue(
const char* s, MutableHandleValue dst) {
MOZ_ASSERT(JS::StringIsASCII(s));
/*
* Bug 575416: instead of Atomize, lookup constant atoms in tbl file
*/
Rooted<JSAtom*> atom(cx, Atomize(cx, s, strlen(s)));
if (!atom) {
return false;
}
dst.setString(atom);
return true;
}
[[nodiscard]]
bool atomValueUtf8(
const char* s, MutableHandleValue dst) {
Rooted<JSAtom*> atom(cx, AtomizeUTF8Chars(cx, s, strlen(s)));
if (!atom) {
return false;
}
dst.setString(atom);
return true;
}
[[nodiscard]]
bool newObject(MutableHandleObject dst) {
Rooted<PlainObject*> nobj(cx, NewPlainObject(cx));
if (!nobj) {
return false;
}
dst.set(nobj);
return true;
}
[[nodiscard]]
bool newArray(NodeVector& elts, MutableHandleValue dst);
[[nodiscard]]
bool createNode(ASTType type, TokenPos* pos,
MutableHandleObject dst);
[[nodiscard]]
bool newNodeHelper(HandleObject obj, MutableHandleValue dst) {
// The end of the implementation of newNode().
MOZ_ASSERT(obj);
dst.setObject(*obj);
return true;
}
template <
typename... Arguments>
[[nodiscard]]
bool newNodeHelper(HandleObject obj,
const char* name,
HandleValue value, Arguments&&... rest) {
// Recursive loop to define properties. Note that the newNodeHelper()
// call below passes two fewer arguments than we received, as we omit
// `name` and `value`. This eventually bottoms out in a call to the
// non-template newNodeHelper() above.
return defineProperty(obj, name, value) &&
newNodeHelper(obj, std::forward<Arguments>(rest)...);
}
// Create a node object with "type" and "loc" properties, as well as zero
// or more properties passed in as arguments. The signature is really more
// like:
//
// bool newNode(ASTType type, TokenPos* pos,
// {const char *name0, HandleValue value0,}...
// MutableHandleValue dst);
template <
typename... Arguments>
[[nodiscard]]
bool newNode(ASTType type, TokenPos* pos, Arguments&&... args) {
RootedObject node(cx);
return createNode(type, pos, &node) &&
newNodeHelper(node, std::forward<Arguments>(args)...);
}
[[nodiscard]]
bool listNode(ASTType type,
const char* propName,
NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(elts, &array)) {
return false;
}
return newNode(type, pos, propName, array, dst);
}
[[nodiscard]]
bool defineProperty(HandleObject obj,
const char* name,
HandleValue val) {
MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
/*
* Bug 575416: instead of Atomize, lookup constant atoms in tbl file
*/
Rooted<JSAtom*> atom(cx, Atomize(cx, name, strlen(name)));
if (!atom) {
return false;
}
// Represent "no node" as null and ensure users are not exposed to magic
// values.
RootedValue optVal(cx,
val.isMagic(JS_SERIALIZE_NO_NODE) ? NullValue() : val);
return DefineDataProperty(cx, obj, atom->asPropertyName(), optVal);
}
[[nodiscard]]
bool newNodeLoc(TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool setNodeLoc(HandleObject node, TokenPos* pos);
public:
/*
* All of the public builder methods take as their last two
* arguments a nullable token position and a non-nullable, rooted
* outparam.
*
* Any Value arguments representing optional subnodes may be a
* JS_SERIALIZE_NO_NODE magic value.
*/
/*
* misc nodes
*/
[[nodiscard]]
bool program(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool literal(HandleValue val, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool identifier(HandleValue name, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool function(ASTType type, TokenPos* pos, HandleValue id,
NodeVector& args, NodeVector& defaults,
HandleValue body, HandleValue rest,
GeneratorStyle generatorStyle,
bool isAsync,
bool isExpression, MutableHandleValue dst);
[[nodiscard]]
bool variableDeclarator(HandleValue id, HandleValue init,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool switchCase(HandleValue expr, NodeVector& elts,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool catchClause(HandleValue var, HandleValue body,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool prototypeMutation(HandleValue val, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool propertyInitializer(HandleValue key, HandleValue val,
PropKind kind,
bool isShorthand,
bool isMethod, TokenPos* pos,
MutableHandleValue dst);
/*
* statements
*/
[[nodiscard]]
bool blockStatement(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool expressionStatement(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool emptyStatement(TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool ifStatement(HandleValue test, HandleValue cons,
HandleValue alt, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool breakStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool continueStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool labeledStatement(HandleValue label, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool throwStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool returnStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool forStatement(HandleValue init, HandleValue test,
HandleValue update, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool forInStatement(HandleValue var, HandleValue expr,
HandleValue stmt, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool forOfStatement(HandleValue var, HandleValue expr,
HandleValue stmt, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool withStatement(HandleValue expr, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool whileStatement(HandleValue test, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool doWhileStatement(HandleValue stmt, HandleValue test,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool switchStatement(HandleValue disc, NodeVector& elts,
bool lexical, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool tryStatement(HandleValue body, HandleValue handler,
HandleValue finally, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool debuggerStatement(TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool moduleRequest(HandleValue moduleSpec,
NodeVector& attributes, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool importAttribute(HandleValue key, HandleValue value,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool importDeclaration(NodeVector& elts, HandleValue moduleSpec,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool importSpecifier(HandleValue importName,
HandleValue bindingName, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool importNamespaceSpecifier(HandleValue bindingName,
TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool exportDeclaration(HandleValue decl, NodeVector& elts,
HandleValue moduleSpec,
HandleValue isDefault, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool exportSpecifier(HandleValue bindingName,
HandleValue exportName, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool exportNamespaceSpecifier(HandleValue exportName,
TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool exportBatchSpecifier(TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool classDefinition(
bool expr, HandleValue name,
HandleValue heritage, HandleValue block,
#ifdef ENABLE_DECORATORS
HandleValue decorators,
#endif
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool classMembers(NodeVector& members, MutableHandleValue dst);
[[nodiscard]]
bool classMethod(HandleValue name, HandleValue body,
#ifdef ENABLE_DECORATORS
HandleValue decorators,
#endif
PropKind kind,
bool isStatic, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool classField(HandleValue name, HandleValue initializer,
#ifdef ENABLE_DECORATORS
HandleValue decorators,
#endif
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool staticClassBlock(HandleValue body, TokenPos* pos,
MutableHandleValue dst);
/*
* expressions
*/
[[nodiscard]]
bool binaryExpression(BinaryOperator op, HandleValue left,
HandleValue right, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool unaryExpression(UnaryOperator op, HandleValue expr,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool assignmentExpression(AssignmentOperator op,
HandleValue lhs, HandleValue rhs,
TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool updateExpression(HandleValue expr,
bool incr,
bool prefix,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool logicalExpression(ParseNodeKind pnk, HandleValue left,
HandleValue right, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool conditionalExpression(HandleValue test, HandleValue cons,
HandleValue alt, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool sequenceExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool newExpression(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool callExpression(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst,
bool isOptional =
false);
[[nodiscard]]
bool memberExpression(
bool computed, HandleValue expr,
HandleValue member, TokenPos* pos,
MutableHandleValue dst,
bool isOptional =
false);
[[nodiscard]]
bool arrayExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool templateLiteral(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool taggedTemplate(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool callSiteObj(NodeVector& raw, NodeVector& cooked,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool spreadExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool optionalExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool deleteOptionalExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool computedName(HandleValue name, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool objectExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool thisExpression(TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool yieldExpression(HandleValue arg, YieldKind kind,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool metaProperty(HandleValue meta, HandleValue property,
TokenPos* pos, MutableHandleValue dst);
[[nodiscard]]
bool callImportExpression(HandleValue ident, NodeVector& args,
TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool super(TokenPos* pos, MutableHandleValue dst);
#ifdef ENABLE_RECORD_TUPLE
[[nodiscard]]
bool recordExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool tupleExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
#endif
/*
* declarations
*/
[[nodiscard]]
bool variableDeclaration(NodeVector& elts, VarDeclKind kind,
TokenPos* pos, MutableHandleValue dst);
/*
* patterns
*/
[[nodiscard]]
bool arrayPattern(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool objectPattern(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
[[nodiscard]]
bool propertyPattern(HandleValue key, HandleValue patt,
bool isShorthand, TokenPos* pos,
MutableHandleValue dst);
};
}
/* anonymous namespace */
bool NodeBuilder::createNode(ASTType type, TokenPos* pos,
MutableHandleObject dst) {
MOZ_ASSERT(type > AST_ERROR && type < AST_LIMIT);
RootedValue tv(cx);
Rooted<PlainObject*> node(cx, NewPlainObject(cx));
if (!node || !setNodeLoc(node, pos) || !atomValue(nodeTypeNames[type], &tv) ||
!defineProperty(node,
"type", tv)) {
return false;
}
dst.set(node);
return true;
}
bool NodeBuilder::newArray(NodeVector& elts, MutableHandleValue dst) {
const size_t len = elts.length();
if (len > UINT32_MAX) {
ReportAllocationOverflow(fc);
return false;
}
RootedObject array(cx, NewDenseFullyAllocatedArray(cx, uint32_t(len)));
if (!array) {
return false;
}
for (size_t i = 0; i < len; i++) {
RootedValue val(cx, elts[i]);
MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
/* Represent "no node" as an array hole by not adding the value. */
if (val.isMagic(JS_SERIALIZE_NO_NODE)) {
continue;
}
if (!DefineDataElement(cx, array, i, val)) {
return false;
}
}
dst.setObject(*array);
return true;
}
bool NodeBuilder::newNodeLoc(TokenPos* pos, MutableHandleValue dst) {
if (!pos) {
dst.setNull();
return true;
}
RootedObject loc(cx);
RootedObject to(cx);
RootedValue val(cx);
if (!newObject(&loc)) {
return false;
}
dst.setObject(*loc);
uint32_t startLineNum, endLineNum;
JS::LimitedColumnNumberOneOrigin startColumnIndex, endColumnIndex;
parser->tokenStream.computeLineAndColumn(pos->begin, &startLineNum,
&startColumnIndex);
parser->tokenStream.computeLineAndColumn(pos->end, &endLineNum,
&endColumnIndex);
if (!newObject(&to)) {
return false;
}
val.setObject(*to);
if (!defineProperty(loc,
"start", val)) {
return false;
}
val.setNumber(startLineNum);
if (!defineProperty(to,
"line", val)) {
return false;
}
val.setNumber(startColumnIndex.oneOriginValue());
if (!defineProperty(to,
"column", val)) {
return false;
}
if (!newObject(&to)) {
return false;
}
val.setObject(*to);
if (!defineProperty(loc,
"end", val)) {
return false;
}
val.setNumber(endLineNum);
if (!defineProperty(to,
"line", val)) {
return false;
}
val.setNumber(endColumnIndex.oneOriginValue());
if (!defineProperty(to,
"column", val)) {
return false;
}
if (!defineProperty(loc,
"source", srcval)) {
return false;
}
return true;
}
bool NodeBuilder::setNodeLoc(HandleObject node, TokenPos* pos) {
if (!saveLoc) {
return true;
}
RootedValue loc(cx);
return newNodeLoc(pos, &loc) && defineProperty(node,
"loc", loc);
}
bool NodeBuilder::program(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_PROGRAM,
"body", elts, pos, dst);
}
bool NodeBuilder::blockStatement(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_BLOCK_STMT,
"body", elts, pos, dst);
}
bool NodeBuilder::expressionStatement(HandleValue expr, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_EXPR_STMT, pos,
"expression", expr, dst);
}
bool NodeBuilder::emptyStatement(TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_EMPTY_STMT, pos, dst);
}
bool NodeBuilder::ifStatement(HandleValue test, HandleValue cons,
HandleValue alt, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_IF_STMT, pos,
"test", test,
"consequent", cons,
"alternate", alt, dst);
}
bool NodeBuilder::breakStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_BREAK_STMT, pos,
"label", label, dst);
}
bool NodeBuilder::continueStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_CONTINUE_STMT, pos,
"label", label, dst);
}
bool NodeBuilder::labeledStatement(HandleValue label, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_LAB_STMT, pos,
"label", label,
"body", stmt, dst);
}
bool NodeBuilder::throwStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_THROW_STMT, pos,
"argument", arg, dst);
}
bool NodeBuilder::returnStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_RETURN_STMT, pos,
"argument", arg, dst);
}
bool NodeBuilder::forStatement(HandleValue init, HandleValue test,
HandleValue update, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_FOR_STMT, pos,
"init", init,
"test", test,
"update",
update,
"body", stmt, dst);
}
bool NodeBuilder::forInStatement(HandleValue var, HandleValue expr,
HandleValue stmt, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_FOR_IN_STMT, pos,
"left", var,
"right", expr,
"body", stmt,
dst);
}
bool NodeBuilder::forOfStatement(HandleValue var, HandleValue expr,
HandleValue stmt, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_FOR_OF_STMT, pos,
"left", var,
"right", expr,
"body", stmt,
dst);
}
bool NodeBuilder::withStatement(HandleValue expr, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_WITH_STMT, pos,
"object", expr,
"body", stmt, dst);
}
bool NodeBuilder::whileStatement(HandleValue test, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_WHILE_STMT, pos,
"test", test,
"body", stmt, dst);
}
bool NodeBuilder::doWhileStatement(HandleValue stmt, HandleValue test,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_DO_STMT, pos,
"body", stmt,
"test", test, dst);
}
bool NodeBuilder::switchStatement(HandleValue disc, NodeVector& elts,
bool lexical, TokenPos* pos,
MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(elts, &array)) {
return false;
}
RootedValue lexicalVal(cx, BooleanValue(lexical));
return newNode(AST_SWITCH_STMT, pos,
"discriminant", disc,
"cases", array,
"lexical", lexicalVal, dst);
}
bool NodeBuilder::tryStatement(HandleValue body, HandleValue handler,
HandleValue finally, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_TRY_STMT, pos,
"block", body,
"handler", handler,
"finalizer", finally, dst);
}
bool NodeBuilder::debuggerStatement(TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_DEBUGGER_STMT, pos, dst);
}
bool NodeBuilder::binaryExpression(BinaryOperator op, HandleValue left,
HandleValue right, TokenPos* pos,
MutableHandleValue dst) {
MOZ_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
RootedValue opName(cx);
if (!atomValue(binopNames[op], &opName)) {
return false;
}
return newNode(AST_BINARY_EXPR, pos,
"operator", opName,
"left", left,
"right", right, dst);
}
bool NodeBuilder::unaryExpression(UnaryOperator unop, HandleValue expr,
TokenPos* pos, MutableHandleValue dst) {
MOZ_ASSERT(unop > UNOP_ERR && unop < UNOP_LIMIT);
RootedValue opName(cx);
if (!atomValue(unopNames[unop], &opName)) {
return false;
}
RootedValue trueVal(cx, BooleanValue(
true));
return newNode(AST_UNARY_EXPR, pos,
"operator", opName,
"argument", expr,
"prefix", trueVal, dst);
}
bool NodeBuilder::assignmentExpression(AssignmentOperator aop, HandleValue lhs,
HandleValue rhs, TokenPos* pos,
MutableHandleValue dst) {
MOZ_ASSERT(aop > AOP_ERR && aop < AOP_LIMIT);
RootedValue opName(cx);
if (!atomValue(aopNames[aop], &opName)) {
return false;
}
return newNode(AST_ASSIGN_EXPR, pos,
"operator", opName,
"left", lhs,
"right",
rhs, dst);
}
bool NodeBuilder::updateExpression(HandleValue expr,
bool incr,
bool prefix,
TokenPos* pos, MutableHandleValue dst) {
RootedValue opName(cx);
if (!atomValue(incr ?
"++" :
"--", &opName)) {
return false;
}
RootedValue prefixVal(cx, BooleanValue(prefix));
return newNode(AST_UPDATE_EXPR, pos,
"operator", opName,
"argument", expr,
"prefix", prefixVal, dst);
}
bool NodeBuilder::logicalExpression(ParseNodeKind pnk, HandleValue left,
HandleValue right, TokenPos* pos,
MutableHandleValue dst) {
RootedValue opName(cx);
switch (pnk) {
case ParseNodeKind::OrExpr:
if (!atomValue(
"||", &opName)) {
return false;
}
break;
case ParseNodeKind::CoalesceExpr:
if (!atomValue(
"??", &opName)) {
return false;
}
break;
case ParseNodeKind::AndExpr:
if (!atomValue(
"&&", &opName)) {
return false;
}
break;
default:
MOZ_CRASH(
"Unexpected ParseNodeKind: Must be `Or`, `And`, or `Coalesce`");
}
return newNode(AST_LOGICAL_EXPR, pos,
"operator", opName,
"left", left,
"right", right, dst);
}
bool NodeBuilder::conditionalExpression(HandleValue test, HandleValue cons,
HandleValue alt, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_COND_EXPR, pos,
"test", test,
"consequent", cons,
"alternate", alt, dst);
}
bool NodeBuilder::sequenceExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_LIST_EXPR,
"expressions", elts, pos, dst);
}
bool NodeBuilder::callExpression(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst,
bool isOptional) {
RootedValue array(cx);
if (!newArray(args, &array)) {
return false;
}
return newNode(isOptional ? AST_OPT_CALL_EXPR : AST_CALL_EXPR, pos,
"callee",
callee,
"arguments", array, dst);
}
bool NodeBuilder::newExpression(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(args, &array)) {
return false;
}
return newNode(AST_NEW_EXPR, pos,
"callee", callee,
"arguments", array, dst);
}
bool NodeBuilder::memberExpression(
bool computed, HandleValue expr,
HandleValue member, TokenPos* pos,
MutableHandleValue dst,
bool isOptional
/* = false */) {
RootedValue computedVal(cx, BooleanValue(computed));
return newNode(isOptional ? AST_OPT_MEMBER_EXPR : AST_MEMBER_EXPR, pos,
"object", expr,
"property", member,
"computed", computedVal,
dst);
}
bool NodeBuilder::arrayExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_ARRAY_EXPR,
"elements", elts, pos, dst);
}
bool NodeBuilder::callSiteObj(NodeVector& raw, NodeVector& cooked,
TokenPos* pos, MutableHandleValue dst) {
RootedValue rawVal(cx);
if (!newArray(raw, &rawVal)) {
return false;
}
RootedValue cookedVal(cx);
if (!newArray(cooked, &cookedVal)) {
return false;
}
return newNode(AST_CALL_SITE_OBJ, pos,
"raw", rawVal,
"cooked", cookedVal,
dst);
}
bool NodeBuilder::taggedTemplate(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(args, &array)) {
return false;
}
return newNode(AST_TAGGED_TEMPLATE, pos,
"callee", callee,
"arguments", array,
dst);
}
bool NodeBuilder::templateLiteral(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_TEMPLATE_LITERAL,
"elements", elts, pos, dst);
}
bool NodeBuilder::computedName(HandleValue name, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_COMPUTED_NAME, pos,
"name", name, dst);
}
bool NodeBuilder::spreadExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_SPREAD_EXPR, pos,
"expression", expr, dst);
}
bool NodeBuilder::optionalExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_OPTIONAL_EXPR, pos,
"expression", expr, dst);
}
bool NodeBuilder::deleteOptionalExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_DELETE_OPTIONAL_EXPR, pos,
"expression", expr, dst);
}
bool NodeBuilder::propertyPattern(HandleValue key, HandleValue patt,
bool isShorthand, TokenPos* pos,
MutableHandleValue dst) {
RootedValue kindName(cx);
if (!atomValue(
"init", &kindName)) {
return false;
}
RootedValue isShorthandVal(cx, BooleanValue(isShorthand));
return newNode(AST_PROP_PATT, pos,
"key", key,
"value", patt,
"kind",
kindName,
"shorthand", isShorthandVal, dst);
}
bool NodeBuilder::prototypeMutation(HandleValue val, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_PROTOTYPEMUTATION, pos,
"value", val, dst);
}
bool NodeBuilder::propertyInitializer(HandleValue key, HandleValue val,
PropKind kind,
bool isShorthand,
bool isMethod, TokenPos* pos,
MutableHandleValue dst) {
RootedValue kindName(cx);
if (!atomValue(kind == PROP_INIT ?
"init"
: kind == PROP_GETTER ?
"get"
:
"set",
&kindName)) {
return false;
}
RootedValue isShorthandVal(cx, BooleanValue(isShorthand));
RootedValue isMethodVal(cx, BooleanValue(isMethod));
return newNode(AST_PROPERTY, pos,
"key", key,
"value", val,
"kind", kindName,
"method", isMethodVal,
"shorthand", isShorthandVal, dst);
}
bool NodeBuilder::objectExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_OBJECT_EXPR,
"properties", elts, pos, dst);
}
#ifdef ENABLE_RECORD_TUPLE
bool NodeBuilder::recordExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_RECORD_EXPR,
"properties", elts, pos, dst);
}
bool NodeBuilder::tupleExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_TUPLE_EXPR,
"elements", elts, pos, dst);
}
#endif
bool NodeBuilder::thisExpression(TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_THIS_EXPR, pos, dst);
}
bool NodeBuilder::yieldExpression(HandleValue arg, YieldKind kind,
TokenPos* pos, MutableHandleValue dst) {
RootedValue delegateVal(cx);
switch (kind) {
case Delegating:
delegateVal = BooleanValue(
true);
break;
case NotDelegating:
delegateVal = BooleanValue(
false);
break;
}
return newNode(AST_YIELD_EXPR, pos,
"argument", arg,
"delegate", delegateVal,
dst);
}
bool NodeBuilder::moduleRequest(HandleValue moduleSpec, NodeVector& attributes,
TokenPos* pos, MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(attributes, &array)) {
return false;
}
return newNode(AST_MODULE_REQUEST, pos,
"source", moduleSpec,
"attributes",
array, dst);
}
bool NodeBuilder::importAttribute(HandleValue key, HandleValue value,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_IMPORT_ATTRIBUTE, pos,
"key", key,
"value", value, dst);
}
bool NodeBuilder::importDeclaration(NodeVector& elts, HandleValue moduleRequest,
TokenPos* pos, MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(elts, &array)) {
return false;
}
return newNode(AST_IMPORT_DECL, pos,
"specifiers", array,
"moduleRequest",
moduleRequest, dst);
}
bool NodeBuilder::importSpecifier(HandleValue importName,
HandleValue bindingName, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_IMPORT_SPEC, pos,
"id", importName,
"name", bindingName,
dst);
}
bool NodeBuilder::importNamespaceSpecifier(HandleValue bindingName,
TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_IMPORT_NAMESPACE_SPEC, pos,
"name", bindingName, dst);
}
bool NodeBuilder::exportDeclaration(HandleValue decl, NodeVector& elts,
HandleValue moduleRequest,
HandleValue isDefault, TokenPos* pos,
MutableHandleValue dst) {
RootedValue array(cx, NullValue());
if (decl.isNull() && !newArray(elts, &array)) {
return false;
}
return newNode(AST_EXPORT_DECL, pos,
"declaration", decl,
"specifiers", array,
"moduleRequest", moduleRequest,
"isDefault", isDefault, dst);
}
bool NodeBuilder::exportSpecifier(HandleValue bindingName,
HandleValue exportName, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_EXPORT_SPEC, pos,
"id", bindingName,
"name", exportName,
dst);
}
bool NodeBuilder::exportNamespaceSpecifier(HandleValue exportName,
TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_EXPORT_NAMESPACE_SPEC, pos,
"name", exportName, dst);
}
bool NodeBuilder::exportBatchSpecifier(TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_EXPORT_BATCH_SPEC, pos, dst);
}
bool NodeBuilder::variableDeclaration(NodeVector& elts, VarDeclKind kind,
TokenPos* pos, MutableHandleValue dst) {
MOZ_ASSERT(kind > VARDECL_ERR && kind < VARDECL_LIMIT);
RootedValue array(cx), kindName(cx);
const char* s;
switch (kind) {
case VARDECL_CONST:
s =
"const";
break;
case VARDECL_LET:
s =
"let";
break;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case VARDECL_USING:
s =
"using";
break;
case VARDECL_AWAIT_USING:
s =
"await using";
break;
#endif
default:
s =
"var";
}
if (!newArray(elts, &array) || !atomValue(s, &kindName)) {
return false;
}
return newNode(AST_VAR_DECL, pos,
"kind", kindName,
"declarations", array,
dst);
}
bool NodeBuilder::variableDeclarator(HandleValue id, HandleValue init,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_VAR_DTOR, pos,
"id", id,
"init", init, dst);
}
bool NodeBuilder::switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(elts, &array)) {
return false;
}
return newNode(AST_CASE, pos,
"test", expr,
"consequent", array, dst);
}
bool NodeBuilder::catchClause(HandleValue var, HandleValue body, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_CATCH, pos,
"param", var,
"body", body, dst);
}
bool NodeBuilder::literal(HandleValue val, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_LITERAL, pos,
"value", val, dst);
}
bool NodeBuilder::identifier(HandleValue name, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_IDENTIFIER, pos,
"name", name, dst);
}
bool NodeBuilder::objectPattern(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_OBJECT_PATT,
"properties", elts, pos, dst);
}
bool NodeBuilder::arrayPattern(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
return listNode(AST_ARRAY_PATT,
"elements", elts, pos, dst);
}
bool NodeBuilder::function(ASTType type, TokenPos* pos, HandleValue id,
NodeVector& args, NodeVector& defaults,
HandleValue body, HandleValue rest,
GeneratorStyle generatorStyle,
bool isAsync,
bool isExpression, MutableHandleValue dst) {
RootedValue array(cx), defarray(cx);
if (!newArray(args, &array)) {
return false;
}
if (!newArray(defaults, &defarray)) {
return false;
}
bool isGenerator = generatorStyle != GeneratorStyle::None;
RootedValue isGeneratorVal(cx, BooleanValue(isGenerator));
RootedValue isAsyncVal(cx, BooleanValue(isAsync));
RootedValue isExpressionVal(cx, BooleanValue(isExpression));
if (isGenerator) {
MOZ_ASSERT(generatorStyle == GeneratorStyle::ES6);
JSAtom* styleStr = Atomize(cx,
"es6", 3);
if (!styleStr) {
return false;
}
RootedValue styleVal(cx, StringValue(styleStr));
return newNode(type, pos,
"id", id,
"params", array,
"defaults", defarray,
"body", body,
"rest", rest,
"generator", isGeneratorVal,
"async", isAsyncVal,
"style", styleVal,
"expression",
isExpressionVal, dst);
}
return newNode(type, pos,
"id", id,
"params", array,
"defaults", defarray,
"body", body,
"rest", rest,
"generator", isGeneratorVal,
"async", isAsyncVal,
"expression", isExpressionVal, dst);
}
bool NodeBuilder::classMethod(HandleValue name, HandleValue body,
#ifdef ENABLE_DECORATORS
HandleValue decorators,
#endif
PropKind kind,
bool isStatic, TokenPos* pos,
MutableHandleValue dst) {
RootedValue kindName(cx);
if (!atomValue(kind == PROP_INIT ?
"method"
: kind == PROP_GETTER ?
"get"
:
"set",
&kindName)) {
return false;
}
RootedValue isStaticVal(cx, BooleanValue(isStatic));
return newNode(AST_CLASS_METHOD, pos,
"name", name,
"body", body,
"kind",
kindName,
"static", isStaticVal,
#ifdef ENABLE_DECORATORS
"decorators", decorators,
#endif
dst);
}
bool NodeBuilder::classField(HandleValue name, HandleValue initializer,
#ifdef ENABLE_DECORATORS
HandleValue decorators,
#endif
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_CLASS_FIELD, pos,
"name", name,
"init", initializer,
#ifdef ENABLE_DECORATORS
"decorators", decorators,
#endif
dst);
}
bool NodeBuilder::staticClassBlock(HandleValue body, TokenPos* pos,
MutableHandleValue dst) {
return newNode(AST_STATIC_CLASS_BLOCK, pos,
"body", body, dst);
}
bool NodeBuilder::classMembers(NodeVector& members, MutableHandleValue dst) {
return newArray(members, dst);
}
bool NodeBuilder::classDefinition(
bool expr, HandleValue name,
HandleValue heritage, HandleValue block,
#ifdef ENABLE_DECORATORS
HandleValue decorators,
#endif
TokenPos* pos, MutableHandleValue dst) {
ASTType type = expr ? AST_CLASS_EXPR : AST_CLASS_STMT;
return newNode(type, pos,
"id", name,
"superClass", heritage,
"body", block,
#ifdef ENABLE_DECORATORS
"decorators", decorators,
#endif
dst);
}
bool NodeBuilder::metaProperty(HandleValue meta, HandleValue property,
TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_METAPROPERTY, pos,
"meta", meta,
"property", property,
dst);
}
bool NodeBuilder::callImportExpression(HandleValue ident, NodeVector& args,
TokenPos* pos, MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(args, &array)) {
return false;
}
return newNode(AST_CALL_IMPORT, pos,
"ident", ident,
"arguments", array, dst);
}
bool NodeBuilder::super(TokenPos* pos, MutableHandleValue dst) {
return newNode(AST_SUPER, pos, dst);
}
namespace {
/*
* Serialization of parse nodes to JavaScript objects.
*
* All serialization methods take a non-nullable ParseNode pointer.
*/
class ASTSerializer {
JSContext* cx;
FrontendContext* fc;
Parser<FullParseHandler, char16_t>* parser;
NodeBuilder builder;
DebugOnly<uint32_t> lineno;
Value unrootedAtomContents(JSAtom* atom) {
return StringValue(atom ? atom : cx->names().empty_);
}
BinaryOperator binop(ParseNodeKind kind);
UnaryOperator unop(ParseNodeKind kind);
AssignmentOperator aop(ParseNodeKind kind);
bool statements(ListNode* stmtList, NodeVector& elts);
bool expressions(ListNode* exprList, NodeVector& elts);
bool leftAssociate(ListNode* node, MutableHandleValue dst);
bool rightAssociate(ListNode* node, MutableHandleValue dst);
bool functionArgs(ParamsBodyNode* pn, NodeVector& args, NodeVector& defaults,
MutableHandleValue rest);
bool sourceElement(ParseNode* pn, MutableHandleValue dst);
bool declaration(ParseNode* pn, MutableHandleValue dst);
bool variableDeclaration(ListNode* declList,
bool lexical,
MutableHandleValue dst);
bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
bool importDeclaration(BinaryNode* importNode, MutableHandleValue dst);
bool importSpecifier(BinaryNode* importSpec, MutableHandleValue dst);
bool importNamespaceSpecifier(UnaryNode* importSpec, MutableHandleValue dst);
bool exportDeclaration(ParseNode* exportNode, MutableHandleValue dst);
bool exportSpecifier(BinaryNode* exportSpec, MutableHandleValue dst);
bool exportNamespaceSpecifier(UnaryNode* exportSpec, MutableHandleValue dst);
bool classDefinition(ClassNode* pn,
bool expr, MutableHandleValue dst);
bool importAttributes(ListNode* attributeList, NodeVector& attributes);
bool optStatement(ParseNode* pn, MutableHandleValue dst) {
if (!pn) {
dst.setMagic(JS_SERIALIZE_NO_NODE);
return true;
}
return statement(pn, dst);
}
bool forInit(ParseNode* pn, MutableHandleValue dst);
bool forIn(ForNode* loop, ParseNode* iterExpr, HandleValue var,
HandleValue stmt, MutableHandleValue dst);
bool forOf(ForNode* loop, ParseNode* iterExpr, HandleValue var,
HandleValue stmt, MutableHandleValue dst);
bool statement(ParseNode* pn, MutableHandleValue dst);
bool blockStatement(ListNode* node, MutableHandleValue dst);
bool switchStatement(SwitchStatement* switchStmt, MutableHandleValue dst);
bool switchCase(CaseClause* caseClause, MutableHandleValue dst);
bool tryStatement(TryNode* tryNode, MutableHandleValue dst);
bool catchClause(BinaryNode* catchClause, MutableHandleValue dst);
bool optExpression(ParseNode* pn, MutableHandleValue dst) {
if (!pn) {
dst.setMagic(JS_SERIALIZE_NO_NODE);
return true;
}
return expression(pn, dst);
}
bool expression(ParseNode* pn, MutableHandleValue dst);
bool propertyName(ParseNode* key, MutableHandleValue dst);
bool property(ParseNode* pn, MutableHandleValue dst);
bool classMethod(ClassMethod* classMethod, MutableHandleValue dst);
bool classField(ClassField* classField, MutableHandleValue dst);
bool staticClassBlock(StaticClassBlock* staticClassBlock,
MutableHandleValue dst);
bool optIdentifier(Handle<JSAtom*> atom, TokenPos* pos,
MutableHandleValue dst) {
if (!atom) {
dst.setMagic(JS_SERIALIZE_NO_NODE);
return true;
}
return identifier(atom, pos, dst);
}
bool identifier(Handle<JSAtom*> atom, TokenPos* pos, MutableHandleValue dst);
bool identifier(NameNode* id, MutableHandleValue dst);
bool identifierOrLiteral(ParseNode* id, MutableHandleValue dst);
bool literal(ParseNode* pn, MutableHandleValue dst);
bool optPattern(ParseNode* pn, MutableHandleValue dst) {
if (!pn) {
dst.setMagic(JS_SERIALIZE_NO_NODE);
return true;
}
return pattern(pn, dst);
}
bool pattern(ParseNode* pn, MutableHandleValue dst);
bool arrayPattern(ListNode* array, MutableHandleValue dst);
bool objectPattern(ListNode* obj, MutableHandleValue dst);
bool function(FunctionNode* funNode, ASTType type, MutableHandleValue dst);
bool functionArgsAndBody(ParamsBodyNode* pn, NodeVector& args,
NodeVector& defaults,
bool isAsync,
bool isExpression, MutableHandleValue body,
MutableHandleValue rest);
bool functionBody(ParseNode* pn, TokenPos* pos, MutableHandleValue dst);
public:
ASTSerializer(JSContext* c, FrontendContext* f,
bool l,
char const* src,
uint32_t ln)
: cx(c),
fc(f),
parser(nullptr),
builder(c, f, l, src)
#ifdef DEBUG
,
lineno(ln)
#endif
{
}
bool init() {
return builder.init(); }
void setParser(frontend::Parser<frontend::FullParseHandler, char16_t>* p) {
parser = p;
builder.setParser(p);
}
bool program(ListNode* node, MutableHandleValue dst);
};
}
/* anonymous namespace */
AssignmentOperator ASTSerializer::aop(ParseNodeKind kind) {
switch (kind) {
case ParseNodeKind::AssignExpr:
return AOP_ASSIGN;
case ParseNodeKind::AddAssignExpr:
return AOP_PLUS;
case ParseNodeKind::SubAssignExpr:
return AOP_MINUS;
case ParseNodeKind::MulAssignExpr:
return AOP_STAR;
case ParseNodeKind::DivAssignExpr:
return AOP_DIV;
case ParseNodeKind::ModAssignExpr:
return AOP_MOD;
case ParseNodeKind::PowAssignExpr:
return AOP_POW;
case ParseNodeKind::LshAssignExpr:
return AOP_LSH;
case ParseNodeKind::RshAssignExpr:
return AOP_RSH;
case ParseNodeKind::UrshAssignExpr:
return AOP_URSH;
case ParseNodeKind::BitOrAssignExpr:
return AOP_BITOR;
case ParseNodeKind::BitXorAssignExpr:
return AOP_BITXOR;
case ParseNodeKind::BitAndAssignExpr:
return AOP_BITAND;
case ParseNodeKind::CoalesceAssignExpr:
return AOP_COALESCE;
case ParseNodeKind::OrAssignExpr:
return AOP_OR;
case ParseNodeKind::AndAssignExpr:
return AOP_AND;
default:
return AOP_ERR;
}
}
UnaryOperator ASTSerializer::unop(ParseNodeKind kind) {
if (IsDeleteKind(kind)) {
return UNOP_DELETE;
}
if (IsTypeofKind(kind)) {
return UNOP_TYPEOF;
}
switch (kind) {
case ParseNodeKind::AwaitExpr:
return UNOP_AWAIT;
case ParseNodeKind::NegExpr:
return UNOP_NEG;
case ParseNodeKind::PosExpr:
return UNOP_POS;
case ParseNodeKind::NotExpr:
return UNOP_NOT;
case ParseNodeKind::BitNotExpr:
return UNOP_BITNOT;
case ParseNodeKind::VoidExpr:
return UNOP_VOID;
default:
return UNOP_ERR;
}
}
BinaryOperator ASTSerializer::binop(ParseNodeKind kind) {
switch (kind) {
case ParseNodeKind::LshExpr:
return BINOP_LSH;
case ParseNodeKind::RshExpr:
return BINOP_RSH;
case ParseNodeKind::UrshExpr:
return BINOP_URSH;
case ParseNodeKind::LtExpr:
return BINOP_LT;
case ParseNodeKind::LeExpr:
return BINOP_LE;
case ParseNodeKind::GtExpr:
return BINOP_GT;
case ParseNodeKind::GeExpr:
return BINOP_GE;
case ParseNodeKind::EqExpr:
return BINOP_EQ;
case ParseNodeKind::NeExpr:
return BINOP_NE;
case ParseNodeKind::StrictEqExpr:
return BINOP_STRICTEQ;
case ParseNodeKind::StrictNeExpr:
return BINOP_STRICTNE;
case ParseNodeKind::AddExpr:
return BINOP_ADD;
case ParseNodeKind::SubExpr:
return BINOP_SUB;
case ParseNodeKind::MulExpr:
return BINOP_STAR;
case ParseNodeKind::DivExpr:
return BINOP_DIV;
case ParseNodeKind::ModExpr:
return BINOP_MOD;
case ParseNodeKind::PowExpr:
return BINOP_POW;
case ParseNodeKind::BitOrExpr:
return BINOP_BITOR;
case ParseNodeKind::BitXorExpr:
return BINOP_BITXOR;
case ParseNodeKind::BitAndExpr:
return BINOP_BITAND;
case ParseNodeKind::InExpr:
case ParseNodeKind::PrivateInExpr:
return BINOP_IN;
case ParseNodeKind::InstanceOfExpr:
return BINOP_INSTANCEOF;
case ParseNodeKind::CoalesceExpr:
return BINOP_COALESCE;
default:
return BINOP_ERR;
}
}
bool ASTSerializer::statements(ListNode* stmtList, NodeVector& elts) {
MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
if (!elts.reserve(stmtList->count())) {
return false;
}
for (ParseNode* stmt : stmtList->contents()) {
MOZ_ASSERT(stmtList->pn_pos.encloses(stmt->pn_pos));
RootedValue elt(cx);
if (!sourceElement(stmt, &elt)) {
return false;
}
elts.infallibleAppend(elt);
}
return true;
}
bool ASTSerializer::expressions(ListNode* exprList, NodeVector& elts) {
if (!elts.reserve(exprList->count())) {
return false;
}
for (ParseNode* expr : exprList->contents()) {
MOZ_ASSERT(exprList->pn_pos.encloses(expr->pn_pos));
RootedValue elt(cx);
if (!expression(expr, &elt)) {
return false;
}
elts.infallibleAppend(elt);
}
return true;
}
bool ASTSerializer::blockStatement(ListNode* node, MutableHandleValue dst) {
MOZ_ASSERT(node->isKind(ParseNodeKind::StatementList));
NodeVector stmts(cx);
return statements(node, stmts) &&
builder.blockStatement(stmts, &node->pn_pos, dst);
}
bool ASTSerializer::program(ListNode* node, MutableHandleValue dst) {
#ifdef DEBUG
{
const TokenStreamAnyChars& anyChars = parser->anyChars;
auto lineToken = anyChars.lineToken(node->pn_pos.begin);
MOZ_ASSERT(anyChars.lineNumber(lineToken) == lineno);
}
#endif
NodeVector stmts(cx);
return statements(node, stmts) && builder.program(stmts, &node->pn_pos, dst);
}
bool ASTSerializer::sourceElement(ParseNode* pn, MutableHandleValue dst) {
/* SpiderMonkey allows declarations even in pure statement contexts. */
return statement(pn, dst);
}
bool ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst) {
MOZ_ASSERT(pn->isKind(ParseNodeKind::Function) ||
pn->isKind(ParseNodeKind::VarStmt) ||
pn->isKind(ParseNodeKind::LetDecl) ||
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
pn->isKind(ParseNodeKind::UsingDecl) ||
pn->isKind(ParseNodeKind::AwaitUsingDecl) ||
#endif
pn->isKind(ParseNodeKind::ConstDecl));
switch (pn->getKind()) {
case ParseNodeKind::Function:
return function(&pn->as<FunctionNode>(), AST_FUNC_DECL, dst);
case ParseNodeKind::VarStmt:
return variableDeclaration(&pn->as<ListNode>(),
false, dst);
default:
MOZ_ASSERT(pn->isKind(ParseNodeKind::LetDecl) ||
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
pn->isKind(ParseNodeKind::UsingDecl) ||
pn->isKind(ParseNodeKind::AwaitUsingDecl) ||
#endif
pn->isKind(ParseNodeKind::ConstDecl));
return variableDeclaration(&pn->as<ListNode>(),
true, dst);
}
}
bool ASTSerializer::variableDeclaration(ListNode* declList,
bool lexical,
MutableHandleValue dst) {
MOZ_ASSERT_IF(lexical, declList->isKind(ParseNodeKind::LetDecl) ||
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
declList->isKind(ParseNodeKind::UsingDecl) ||
declList->isKind(ParseNodeKind::AwaitUsingDecl) ||
#endif
declList->isKind(ParseNodeKind::ConstDecl));
MOZ_ASSERT_IF(!lexical, declList->isKind(ParseNodeKind::VarStmt));
VarDeclKind kind = VARDECL_ERR;
// Treat both the toplevel const binding (secretly var-like) and the lexical
// const the same way
if (lexical) {
if (declList->isKind(ParseNodeKind::LetDecl)) {
kind = VARDECL_LET;
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
else if (declList->isKind(ParseNodeKind::UsingDecl)) {
kind = VARDECL_USING;
}
else if (declList->isKind(ParseNodeKind::AwaitUsingDecl)) {
kind = VARDECL_AWAIT_USING;
}
#endif
else {
kind = VARDECL_CONST;
}
}
else {
kind =
declList->isKind(ParseNodeKind::VarStmt) ? VARDECL_VAR : VARDECL_CONST;
}
NodeVector dtors(cx);
if (!dtors.reserve(declList->count())) {
return false;
}
for (ParseNode* decl : declList->contents()) {
RootedValue child(cx);
if (!variableDeclarator(decl, &child)) {
return false;
}
dtors.infallibleAppend(child);
}
return builder.variableDeclaration(dtors, kind, &declList->pn_pos, dst);
}
bool ASTSerializer::variableDeclarator(ParseNode* pn, MutableHandleValue dst) {
ParseNode* patternNode;
ParseNode* initNode;
if (pn->isKind(ParseNodeKind::Name)) {
patternNode = pn;
initNode = nullptr;
}
else if (pn->isKind(ParseNodeKind::AssignExpr)) {
AssignmentNode* assignNode = &pn->as<AssignmentNode>();
patternNode = assignNode->left();
initNode = assignNode->right();
MOZ_ASSERT(pn->pn_pos.encloses(patternNode->pn_pos));
MOZ_ASSERT(pn->pn_pos.encloses(initNode->pn_pos));
}
else {
/* This happens for a destructuring declarator in a for-in/of loop. */
patternNode = pn;
initNode = nullptr;
}
RootedValue patternVal(cx), init(cx);
return pattern(patternNode, &patternVal) && optExpression(initNode, &init) &&
builder.variableDeclarator(patternVal, init, &pn->pn_pos, dst);
}
bool ASTSerializer::importDeclaration(BinaryNode* importNode,
MutableHandleValue dst) {
MOZ_ASSERT(importNode->isKind(ParseNodeKind::ImportDecl));
ListNode* specList = &importNode->left()->as<ListNode>();
MOZ_ASSERT(specList->isKind(ParseNodeKind::ImportSpecList));
auto* moduleRequest = &importNode->right()->as<BinaryNode>();
MOZ_ASSERT(moduleRequest->isKind(ParseNodeKind::ImportModuleRequest));
ParseNode* moduleSpecNode = moduleRequest->left();
MOZ_ASSERT(moduleSpecNode->isKind(ParseNodeKind::StringExpr));
auto* attributeList = &moduleRequest->right()->as<ListNode>();
MOZ_ASSERT(attributeList->isKind(ParseNodeKind::ImportAttributeList));
NodeVector elts(cx);
if (!elts.reserve(specList->count())) {
return false;
}
for (ParseNode* item : specList->contents()) {
RootedValue elt(cx);
if (item->is<UnaryNode>()) {
auto* spec = &item->as<UnaryNode>();
if (!importNamespaceSpecifier(spec, &elt)) {
return false;
}
}
else {
auto* spec = &item->as<BinaryNode>();
if (!importSpecifier(spec, &elt)) {
return false;
}
}
elts.infallibleAppend(elt);
}
RootedValue moduleSpec(cx);
if (!literal(moduleSpecNode, &moduleSpec)) {
return false;
}
NodeVector attributes(cx);
if (!importAttributes(attributeList, attributes)) {
return false;
}
RootedValue moduleRequestValue(cx);
if (!builder.moduleRequest(moduleSpec, attributes, &importNode->pn_pos,
&moduleRequestValue)) {
return false;
}
return builder.importDeclaration(elts, moduleRequestValue,
&importNode->pn_pos, dst);
}
bool ASTSerializer::importSpecifier(BinaryNode* importSpec,
MutableHandleValue dst) {
MOZ_ASSERT(importSpec->isKind(ParseNodeKind::ImportSpec));
NameNode* importNameNode = &importSpec->left()->as<NameNode>();
NameNode* bindingNameNode = &importSpec->right()->as<NameNode>();
RootedValue importName(cx);
RootedValue bindingName(cx);
return identifierOrLiteral(importNameNode, &importName) &&
identifier(bindingNameNode, &bindingName) &&
builder.importSpecifier(importName, bindingName, &importSpec->pn_pos,
dst);
}
bool ASTSerializer::importNamespaceSpecifier(UnaryNode* importSpec,
MutableHandleValue dst) {
MOZ_ASSERT(importSpec->isKind(ParseNodeKind::ImportNamespaceSpec));
NameNode* bindingNameNode = &importSpec->kid()->as<NameNode>();
RootedValue bindingName(cx);
return identifier(bindingNameNode, &bindingName) &&
builder.importNamespaceSpecifier(bindingName, &importSpec->pn_pos,
dst);
}
bool ASTSerializer::exportDeclaration(ParseNode* exportNode,
MutableHandleValue dst) {
MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportStmt) ||
exportNode->isKind(ParseNodeKind::ExportFromStmt) ||
exportNode->isKind(ParseNodeKind::ExportDefaultStmt));
MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::ExportStmt),
exportNode->is<UnaryNode>());
MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::ExportFromStmt),
exportNode->as<BinaryNode>().right()->isKind(
ParseNodeKind::ImportModuleRequest));
RootedValue decl(cx, NullValue());
--> --------------------
--> maximum size reached
--> --------------------