Source code

Revision control

Other Tools

/* -*- 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 "mozilla/Maybe.h"
#include <stdlib.h>
#include <utility>
#include "jspubtd.h"
#include "builtin/Array.h"
#include "builtin/Reflect.h"
#include "frontend/CompilationInfo.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "js/CharacterEncoding.h"
#include "js/StableStringChars.h"
#include "vm/BigIntType.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/JSAtom.h"
#include "vm/JSObject.h"
#include "vm/ModuleBuilder.h" // js::ModuleBuilder
#include "vm/PlainObject.h" // js::PlainObject
#include "vm/RegExpObject.h"
#include "vm/JSObject-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, method) 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_PIPELINE,
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,
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_PIPELINE */
"??", /* 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, method) str,
#include "jsast.tbl"
#undef ASTDEF
nullptr};
static const char* const callbackNames[] = {
#define ASTDEF(ast, str, method) method,
#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. See:
*
*
* Bug 569487: generalize builder interface
*/
class NodeBuilder {
using CallbackArray = RootedValueArray<AST_LIMIT>;
JSContext* cx;
frontend::Parser<frontend::FullParseHandler, char16_t>* parser;
bool saveLoc; /* save source location information? */
char const* src; /* source filename or null */
RootedValue srcval; /* source filename JS value or null */
CallbackArray callbacks; /* user-specified callbacks */
RootedValue userv; /* user-specified builder object or null */
public:
NodeBuilder(JSContext* c, bool l, char const* s)
: cx(c),
parser(nullptr),
saveLoc(l),
src(s),
srcval(c),
callbacks(cx),
userv(c) {}
MOZ_MUST_USE bool init(HandleObject userobj = nullptr) {
if (src) {
if (!atomValue(src, &srcval)) {
return false;
}
} else {
srcval.setNull();
}
if (!userobj) {
userv.setNull();
for (unsigned i = 0; i < AST_LIMIT; i++) {
callbacks[i].setNull();
}
return true;
}
userv.setObject(*userobj);
RootedValue nullVal(cx, NullValue());
RootedValue funv(cx);
for (unsigned i = 0; i < AST_LIMIT; i++) {
const char* name = callbackNames[i];
RootedAtom atom(cx, Atomize(cx, name, strlen(name)));
if (!atom) {
return false;
}
RootedId id(cx, AtomToId(atom));
if (!GetPropertyDefault(cx, userobj, id, nullVal, &funv)) {
return false;
}
if (funv.isNullOrUndefined()) {
callbacks[i].setNull();
continue;
}
if (!funv.isObject() || !funv.toObject().is<JSFunction>()) {
ReportValueError(cx, JSMSG_NOT_FUNCTION, JSDVG_SEARCH_STACK, funv,
nullptr);
return false;
}
callbacks[i].set(funv);
}
return true;
}
void setParser(frontend::Parser<frontend::FullParseHandler, char16_t>* p) {
parser = p;
}
private:
MOZ_MUST_USE bool callbackHelper(HandleValue fun, const InvokeArgs& args,
size_t i, TokenPos* pos,
MutableHandleValue dst) {
// The end of the implementation of callback(). All arguments except
// loc have already been stored in range [0, i).
if (saveLoc) {
if (!newNodeLoc(pos, args[i])) {
return false;
}
}
return js::Call(cx, fun, userv, args, dst);
}
// Helper function for callback(). Note that all Arguments must be types
// that convert to HandleValue, so this isn't as template-y as it seems,
// just variadic.
template <typename... Arguments>
MOZ_MUST_USE bool callbackHelper(HandleValue fun, const InvokeArgs& args,
size_t i, HandleValue head,
Arguments&&... tail) {
// Recursive loop to store the arguments into args. This eventually
// bottoms out in a call to the non-template callbackHelper() above.
args[i].set(head);
return callbackHelper(fun, args, i + 1, std::forward<Arguments>(tail)...);
}
// Invoke a user-defined callback. The actual signature is:
//
// bool callback(HandleValue fun, HandleValue... args, TokenPos* pos,
// MutableHandleValue dst);
template <typename... Arguments>
MOZ_MUST_USE bool callback(HandleValue fun, Arguments&&... args) {
InvokeArgs iargs(cx);
if (!iargs.init(cx, sizeof...(args) - 2 + size_t(saveLoc))) {
return false;
}
return callbackHelper(fun, iargs, 0, std::forward<Arguments>(args)...);
}
// WARNING: Returning a Handle is non-standard, but it works in this case
// because both |v| and |UndefinedHandleValue| are definitely rooted on a
// previous stack frame (i.e. we're just choosing between two
// already-rooted values).
HandleValue opt(HandleValue v) {
MOZ_ASSERT_IF(v.isMagic(), v.whyMagic() == JS_SERIALIZE_NO_NODE);
return v.isMagic(JS_SERIALIZE_NO_NODE) ? JS::UndefinedHandleValue : v;
}
MOZ_MUST_USE bool atomValue(const char* s, MutableHandleValue dst) {
/*
* Bug 575416: instead of Atomize, lookup constant atoms in tbl file
*/
RootedAtom atom(cx, Atomize(cx, s, strlen(s)));
if (!atom) {
return false;
}
dst.setString(atom);
return true;
}
MOZ_MUST_USE bool newObject(MutableHandleObject dst) {
RootedPlainObject nobj(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!nobj) {
return false;
}
dst.set(nobj);
return true;
}
MOZ_MUST_USE bool newArray(NodeVector& elts, MutableHandleValue dst);
MOZ_MUST_USE bool createNode(ASTType type, TokenPos* pos,
MutableHandleObject dst);
MOZ_MUST_USE bool newNodeHelper(HandleObject obj, MutableHandleValue dst) {
// The end of the implementation of newNode().
MOZ_ASSERT(obj);
dst.setObject(*obj);
return true;
}
template <typename... Arguments>
MOZ_MUST_USE 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>
MOZ_MUST_USE bool newNode(ASTType type, TokenPos* pos, Arguments&&... args) {
RootedObject node(cx);
return createNode(type, pos, &node) &&
newNodeHelper(node, std::forward<Arguments>(args)...);
}
MOZ_MUST_USE bool listNode(ASTType type, const char* propName,
NodeVector& elts, TokenPos* pos,
MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(elts, &array)) {
return false;
}
RootedValue cb(cx, callbacks[type]);
if (!cb.isNull()) {
return callback(cb, array, pos, dst);
}
return newNode(type, pos, propName, array, dst);
}
MOZ_MUST_USE 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
*/
RootedAtom 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);
}
MOZ_MUST_USE bool newNodeLoc(TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE 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
*/
MOZ_MUST_USE bool program(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool literal(HandleValue val, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool identifier(HandleValue name, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool function(ASTType type, TokenPos* pos, HandleValue id,
NodeVector& args, NodeVector& defaults,
HandleValue body, HandleValue rest,
GeneratorStyle generatorStyle, bool isAsync,
bool isExpression, MutableHandleValue dst);
MOZ_MUST_USE bool variableDeclarator(HandleValue id, HandleValue init,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool switchCase(HandleValue expr, NodeVector& elts,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool catchClause(HandleValue var, HandleValue body,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool prototypeMutation(HandleValue val, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool propertyInitializer(HandleValue key, HandleValue val,
PropKind kind, bool isShorthand,
bool isMethod, TokenPos* pos,
MutableHandleValue dst);
/*
* statements
*/
MOZ_MUST_USE bool blockStatement(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool expressionStatement(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool emptyStatement(TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool ifStatement(HandleValue test, HandleValue cons,
HandleValue alt, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool breakStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool continueStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool labeledStatement(HandleValue label, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool throwStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool returnStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool forStatement(HandleValue init, HandleValue test,
HandleValue update, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool forInStatement(HandleValue var, HandleValue expr,
HandleValue stmt, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool forOfStatement(HandleValue var, HandleValue expr,
HandleValue stmt, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool withStatement(HandleValue expr, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool whileStatement(HandleValue test, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool doWhileStatement(HandleValue stmt, HandleValue test,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool switchStatement(HandleValue disc, NodeVector& elts,
bool lexical, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool tryStatement(HandleValue body, HandleValue handler,
HandleValue finally, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool debuggerStatement(TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool importDeclaration(NodeVector& elts, HandleValue moduleSpec,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool importSpecifier(HandleValue importName,
HandleValue bindingName, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool exportDeclaration(HandleValue decl, NodeVector& elts,
HandleValue moduleSpec,
HandleValue isDefault, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool exportSpecifier(HandleValue bindingName,
HandleValue exportName, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool exportBatchSpecifier(TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool classDefinition(bool expr, HandleValue name,
HandleValue heritage, HandleValue block,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool classMembers(NodeVector& members, MutableHandleValue dst);
MOZ_MUST_USE bool classMethod(HandleValue name, HandleValue body,
PropKind kind, bool isStatic, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool classField(HandleValue name, HandleValue initializer,
TokenPos* pos, MutableHandleValue dst);
/*
* expressions
*/
MOZ_MUST_USE bool binaryExpression(BinaryOperator op, HandleValue left,
HandleValue right, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool unaryExpression(UnaryOperator op, HandleValue expr,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool assignmentExpression(AssignmentOperator op, HandleValue lhs,
HandleValue rhs, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool updateExpression(HandleValue expr, bool incr, bool prefix,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool logicalExpression(ParseNodeKind pnk, HandleValue left,
HandleValue right, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool conditionalExpression(HandleValue test, HandleValue cons,
HandleValue alt, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool sequenceExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool newExpression(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool callExpression(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst,
bool isOptional = false);
MOZ_MUST_USE bool memberExpression(bool computed, HandleValue expr,
HandleValue member, TokenPos* pos,
MutableHandleValue dst,
bool isOptional = false);
MOZ_MUST_USE bool arrayExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool templateLiteral(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool taggedTemplate(HandleValue callee, NodeVector& args,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool callSiteObj(NodeVector& raw, NodeVector& cooked,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool spreadExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool optionalExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool computedName(HandleValue name, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool objectExpression(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool thisExpression(TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool yieldExpression(HandleValue arg, YieldKind kind,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool metaProperty(HandleValue meta, HandleValue property,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool callImportExpression(HandleValue ident, HandleValue arg,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool super(TokenPos* pos, MutableHandleValue dst);
/*
* declarations
*/
MOZ_MUST_USE bool variableDeclaration(NodeVector& elts, VarDeclKind kind,
TokenPos* pos, MutableHandleValue dst);
/*
* patterns
*/
MOZ_MUST_USE bool arrayPattern(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool objectPattern(NodeVector& elts, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE 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);
RootedPlainObject node(cx, NewBuiltinClassInstance<PlainObject>(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(cx);
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, startColumnIndex;
uint32_t endLineNum, 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);
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);
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) {
RootedValue cb(cx, callbacks[AST_EXPR_STMT]);
if (!cb.isNull()) {
return callback(cb, expr, pos, dst);
}
return newNode(AST_EXPR_STMT, pos, "expression", expr, dst);
}
bool NodeBuilder::emptyStatement(TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_EMPTY_STMT]);
if (!cb.isNull()) {
return callback(cb, pos, dst);
}
return newNode(AST_EMPTY_STMT, pos, dst);
}
bool NodeBuilder::ifStatement(HandleValue test, HandleValue cons,
HandleValue alt, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_IF_STMT]);
if (!cb.isNull()) {
return callback(cb, test, cons, opt(alt), pos, dst);
}
return newNode(AST_IF_STMT, pos, "test", test, "consequent", cons,
"alternate", alt, dst);
}
bool NodeBuilder::breakStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_BREAK_STMT]);
if (!cb.isNull()) {
return callback(cb, opt(label), pos, dst);
}
return newNode(AST_BREAK_STMT, pos, "label", label, dst);
}
bool NodeBuilder::continueStatement(HandleValue label, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_CONTINUE_STMT]);
if (!cb.isNull()) {
return callback(cb, opt(label), pos, dst);
}
return newNode(AST_CONTINUE_STMT, pos, "label", label, dst);
}
bool NodeBuilder::labeledStatement(HandleValue label, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_LAB_STMT]);
if (!cb.isNull()) {
return callback(cb, label, stmt, pos, dst);
}
return newNode(AST_LAB_STMT, pos, "label", label, "body", stmt, dst);
}
bool NodeBuilder::throwStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_THROW_STMT]);
if (!cb.isNull()) {
return callback(cb, arg, pos, dst);
}
return newNode(AST_THROW_STMT, pos, "argument", arg, dst);
}
bool NodeBuilder::returnStatement(HandleValue arg, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_RETURN_STMT]);
if (!cb.isNull()) {
return callback(cb, opt(arg), pos, 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) {
RootedValue cb(cx, callbacks[AST_FOR_STMT]);
if (!cb.isNull()) {
return callback(cb, opt(init), opt(test), opt(update), stmt, pos, 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) {
RootedValue cb(cx, callbacks[AST_FOR_IN_STMT]);
if (!cb.isNull()) {
RootedValue isForEach(
cx, JS::FalseValue()); // obsolete E4X `for each` statement
return callback(cb, var, expr, stmt, isForEach, pos, 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) {
RootedValue cb(cx, callbacks[AST_FOR_OF_STMT]);
if (!cb.isNull()) {
return callback(cb, var, expr, stmt, pos, 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) {
RootedValue cb(cx, callbacks[AST_WITH_STMT]);
if (!cb.isNull()) {
return callback(cb, expr, stmt, pos, dst);
}
return newNode(AST_WITH_STMT, pos, "object", expr, "body", stmt, dst);
}
bool NodeBuilder::whileStatement(HandleValue test, HandleValue stmt,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_WHILE_STMT]);
if (!cb.isNull()) {
return callback(cb, test, stmt, pos, dst);
}
return newNode(AST_WHILE_STMT, pos, "test", test, "body", stmt, dst);
}
bool NodeBuilder::doWhileStatement(HandleValue stmt, HandleValue test,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_DO_STMT]);
if (!cb.isNull()) {
return callback(cb, stmt, test, pos, 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));
RootedValue cb(cx, callbacks[AST_SWITCH_STMT]);
if (!cb.isNull()) {
return callback(cb, disc, array, lexicalVal, pos, dst);
}
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) {
RootedValue cb(cx, callbacks[AST_TRY_STMT]);
if (!cb.isNull()) {
return callback(cb, body, handler, opt(finally), pos, dst);
}
return newNode(AST_TRY_STMT, pos, "block", body, "handler", handler,
"finalizer", finally, dst);
}
bool NodeBuilder::debuggerStatement(TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_DEBUGGER_STMT]);
if (!cb.isNull()) {
return callback(cb, pos, 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;
}
RootedValue cb(cx, callbacks[AST_BINARY_EXPR]);
if (!cb.isNull()) {
return callback(cb, opName, left, right, pos, dst);
}
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 cb(cx, callbacks[AST_UNARY_EXPR]);
if (!cb.isNull()) {
return callback(cb, opName, expr, pos, dst);
}
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;
}
RootedValue cb(cx, callbacks[AST_ASSIGN_EXPR]);
if (!cb.isNull()) {
return callback(cb, opName, lhs, rhs, pos, dst);
}
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));
RootedValue cb(cx, callbacks[AST_UPDATE_EXPR]);
if (!cb.isNull()) {
return callback(cb, expr, opName, prefixVal, pos, dst);
}
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`");
}
RootedValue cb(cx, callbacks[AST_LOGICAL_EXPR]);
if (!cb.isNull()) {
return callback(cb, opName, left, right, pos, dst);
}
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) {
RootedValue cb(cx, callbacks[AST_COND_EXPR]);
if (!cb.isNull()) {
return callback(cb, test, cons, alt, pos, 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;
}
RootedValue cb(cx, callbacks[AST_CALL_EXPR]);
if (!cb.isNull()) {
return callback(cb, callee, array, pos, dst);
}
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;
}
RootedValue cb(cx, callbacks[AST_NEW_EXPR]);
if (!cb.isNull()) {
return callback(cb, callee, array, pos, dst);
}
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));
RootedValue cb(cx, callbacks[AST_MEMBER_EXPR]);
if (!cb.isNull()) {
return callback(cb, computedVal, expr, member, pos, dst);
}
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::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));
RootedValue cb(cx, callbacks[AST_PROP_PATT]);
if (!cb.isNull()) {
return callback(cb, key, patt, pos, dst);
}
return newNode(AST_PROP_PATT, pos, "key", key, "value", patt, "kind",
kindName, "shorthand", isShorthandVal, dst);
}
bool NodeBuilder::prototypeMutation(HandleValue val, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_PROTOTYPEMUTATION]);
if (!cb.isNull()) {
return callback(cb, val, pos, 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));
RootedValue cb(cx, callbacks[AST_PROPERTY]);
if (!cb.isNull()) {
return callback(cb, kindName, key, val, pos, dst);
}
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);
}
bool NodeBuilder::thisExpression(TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_THIS_EXPR]);
if (!cb.isNull()) {
return callback(cb, pos, dst);
}
return newNode(AST_THIS_EXPR, pos, dst);
}
bool NodeBuilder::yieldExpression(HandleValue arg, YieldKind kind,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_YIELD_EXPR]);
RootedValue delegateVal(cx);
switch (kind) {
case Delegating:
delegateVal = BooleanValue(true);
break;
case NotDelegating:
delegateVal = BooleanValue(false);
break;
}
if (!cb.isNull()) {
return callback(cb, opt(arg), delegateVal, pos, dst);
}
return newNode(AST_YIELD_EXPR, pos, "argument", arg, "delegate", delegateVal,
dst);
}
bool NodeBuilder::importDeclaration(NodeVector& elts, HandleValue moduleSpec,
TokenPos* pos, MutableHandleValue dst) {
RootedValue array(cx);
if (!newArray(elts, &array)) {
return false;
}
RootedValue cb(cx, callbacks[AST_IMPORT_DECL]);
if (!cb.isNull()) {
return callback(cb, array, moduleSpec, pos, dst);
}
return newNode(AST_IMPORT_DECL, pos, "specifiers", array, "source",
moduleSpec, dst);
}
bool NodeBuilder::importSpecifier(HandleValue importName,
HandleValue bindingName, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_IMPORT_SPEC]);
if (!cb.isNull()) {
return callback(cb, importName, bindingName, pos, dst);
}
return newNode(AST_IMPORT_SPEC, pos, "id", importName, "name", bindingName,
dst);
}
bool NodeBuilder::exportDeclaration(HandleValue decl, NodeVector& elts,
HandleValue moduleSpec,
HandleValue isDefault, TokenPos* pos,
MutableHandleValue dst) {
RootedValue array(cx, NullValue());
if (decl.isNull() && !newArray(elts, &array)) {
return false;
}
RootedValue cb(cx, callbacks[AST_EXPORT_DECL]);
if (!cb.isNull()) {
return callback(cb, decl, array, moduleSpec, pos, dst);
}
return newNode(AST_EXPORT_DECL, pos, "declaration", decl, "specifiers", array,
"source", moduleSpec, "isDefault", isDefault, dst);
}
bool NodeBuilder::exportSpecifier(HandleValue bindingName,
HandleValue exportName, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_EXPORT_SPEC]);
if (!cb.isNull()) {
return callback(cb, bindingName, exportName, pos, dst);
}
return newNode(AST_EXPORT_SPEC, pos, "id", bindingName, "name", exportName,
dst);
}
bool NodeBuilder::exportBatchSpecifier(TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_EXPORT_BATCH_SPEC]);
if (!cb.isNull()) {
return callback(cb, pos, 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);
if (!newArray(elts, &array) ||
!atomValue(
kind == VARDECL_CONST ? "const" : kind == VARDECL_LET ? "let" : "var",
&kindName)) {
return false;
}
RootedValue cb(cx, callbacks[AST_VAR_DECL]);
if (!cb.isNull()) {
return callback(cb, kindName, array, pos, dst);
}
return newNode(AST_VAR_DECL, pos, "kind", kindName, "declarations", array,
dst);
}
bool NodeBuilder::variableDeclarator(HandleValue id, HandleValue init,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_VAR_DTOR]);
if (!cb.isNull()) {
return callback(cb, id, opt(init), pos, 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;
}
RootedValue cb(cx, callbacks[AST_CASE]);
if (!cb.isNull()) {
return callback(cb, opt(expr), array, pos, dst);
}
return newNode(AST_CASE, pos, "test", expr, "consequent", array, dst);
}
bool NodeBuilder::catchClause(HandleValue var, HandleValue body, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_CATCH]);
if (!cb.isNull()) {
return callback(cb, opt(var), body, pos, dst);
}
return newNode(AST_CATCH, pos, "param", var, "body", body, dst);
}
bool NodeBuilder::literal(HandleValue val, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_LITERAL]);
if (!cb.isNull()) {
return callback(cb, val, pos, dst);
}
return newNode(AST_LITERAL, pos, "value", val, dst);
}
bool NodeBuilder::identifier(HandleValue name, TokenPos* pos,
MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_IDENTIFIER]);
if (!cb.isNull()) {
return callback(cb, name, pos, 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));
RootedValue cb(cx, callbacks[type]);
if (!cb.isNull()) {
return callback(cb, opt(id), array, body, isGeneratorVal, isExpressionVal,
pos, dst);
}
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, 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));
RootedValue cb(cx, callbacks[AST_CLASS_METHOD]);
if (!cb.isNull()) {
return callback(cb, kindName, name, body, isStaticVal, pos, dst);
}
return newNode(AST_CLASS_METHOD, pos, "name", name, "body", body, "kind",
kindName, "static", isStaticVal, dst);
}
bool NodeBuilder::classField(HandleValue name, HandleValue initializer,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_CLASS_FIELD]);
if (!cb.isNull()) {
return callback(cb, name, initializer, pos, dst);
}
return newNode(AST_CLASS_FIELD, pos, "name", name, "init", initializer, dst);
}
bool NodeBuilder::classMembers(NodeVector& members, MutableHandleValue dst) {
return newArray(members, dst);
}
bool NodeBuilder::classDefinition(bool expr, HandleValue name,
HandleValue heritage, HandleValue block,
TokenPos* pos, MutableHandleValue dst) {
ASTType type = expr ? AST_CLASS_EXPR : AST_CLASS_STMT;
RootedValue cb(cx, callbacks[type]);
if (!cb.isNull()) {
return callback(cb, name, heritage, block, pos, dst);
}
return newNode(type, pos, "id", name, "superClass", heritage, "body", block,
dst);
}
bool NodeBuilder::metaProperty(HandleValue meta, HandleValue property,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_METAPROPERTY]);
if (!cb.isNull()) {
return callback(cb, meta, property, pos, dst);
}
return newNode(AST_METAPROPERTY, pos, "meta", meta, "property", property,
dst);
}
bool NodeBuilder::callImportExpression(HandleValue ident, HandleValue arg,
TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_CALL_IMPORT]);
if (!cb.isNull()) {
return callback(cb, arg, pos, dst);
}
return newNode(AST_CALL_IMPORT, pos, "ident", ident, "arg", arg, dst);
}
bool NodeBuilder::super(TokenPos* pos, MutableHandleValue dst) {
RootedValue cb(cx, callbacks[AST_SUPER]);
if (!cb.isNull()) {
return callback(cb, pos, 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;
Parser<FullParseHandler, char16_t>* parser;
NodeBuilder builder;
DebugOnly<uint32_t> lineno;
Value unrootedAtomContents(JSAtom* atom) {
return StringValue(atom ? atom : cx->names().empty);
}