Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


SSL ReflectParse.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/* 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 charconst 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 charconst 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 charconst unopNames[] = {
    "delete"/* UNOP_DELETE */
    "-",      /* UNOP_NEG */
    "+",      /* UNOP_POS */
    "!",      /* UNOP_NOT */
    "~",      /* UNOP_BITNOT */
    "typeof"/* UNOP_TYPEOF */
    "void",   /* UNOP_VOID */
    "await",  /* UNOP_AWAIT */
};

static const charconst 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

--> --------------------

Messung V0.5
C=95 H=99 G=96

¤ Dauer der Verarbeitung: 0.71 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge