michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* JS reflection package. */ michael@0: michael@0: #include "jsreflect.h" michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include michael@0: michael@0: #include "jsarray.h" michael@0: #include "jsatom.h" michael@0: #include "jsobj.h" michael@0: #include "jspubtd.h" michael@0: michael@0: #include "frontend/Parser.h" michael@0: #include "frontend/TokenStream.h" michael@0: #include "js/CharacterEncoding.h" michael@0: #include "vm/RegExpObject.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "frontend/ParseNode-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::frontend; michael@0: michael@0: using JS::AutoValueArray; michael@0: using mozilla::ArrayLength; michael@0: using mozilla::DebugOnly; michael@0: michael@0: char const * const js::aopNames[] = { michael@0: "=", /* AOP_ASSIGN */ michael@0: "+=", /* AOP_PLUS */ michael@0: "-=", /* AOP_MINUS */ michael@0: "*=", /* AOP_STAR */ michael@0: "/=", /* AOP_DIV */ michael@0: "%=", /* AOP_MOD */ michael@0: "<<=", /* AOP_LSH */ michael@0: ">>=", /* AOP_RSH */ michael@0: ">>>=", /* AOP_URSH */ michael@0: "|=", /* AOP_BITOR */ michael@0: "^=", /* AOP_BITXOR */ michael@0: "&=" /* AOP_BITAND */ michael@0: }; michael@0: michael@0: char const * const js::binopNames[] = { michael@0: "==", /* BINOP_EQ */ michael@0: "!=", /* BINOP_NE */ michael@0: "===", /* BINOP_STRICTEQ */ michael@0: "!==", /* BINOP_STRICTNE */ michael@0: "<", /* BINOP_LT */ michael@0: "<=", /* BINOP_LE */ michael@0: ">", /* BINOP_GT */ michael@0: ">=", /* BINOP_GE */ michael@0: "<<", /* BINOP_LSH */ michael@0: ">>", /* BINOP_RSH */ michael@0: ">>>", /* BINOP_URSH */ michael@0: "+", /* BINOP_PLUS */ michael@0: "-", /* BINOP_MINUS */ michael@0: "*", /* BINOP_STAR */ michael@0: "/", /* BINOP_DIV */ michael@0: "%", /* BINOP_MOD */ michael@0: "|", /* BINOP_BITOR */ michael@0: "^", /* BINOP_BITXOR */ michael@0: "&", /* BINOP_BITAND */ michael@0: "in", /* BINOP_IN */ michael@0: "instanceof", /* BINOP_INSTANCEOF */ michael@0: }; michael@0: michael@0: char const * const js::unopNames[] = { michael@0: "delete", /* UNOP_DELETE */ michael@0: "-", /* UNOP_NEG */ michael@0: "+", /* UNOP_POS */ michael@0: "!", /* UNOP_NOT */ michael@0: "~", /* UNOP_BITNOT */ michael@0: "typeof", /* UNOP_TYPEOF */ michael@0: "void" /* UNOP_VOID */ michael@0: }; michael@0: michael@0: char const * const js::nodeTypeNames[] = { michael@0: #define ASTDEF(ast, str, method) str, michael@0: #include "jsast.tbl" michael@0: #undef ASTDEF michael@0: nullptr michael@0: }; michael@0: michael@0: static char const * const callbackNames[] = { michael@0: #define ASTDEF(ast, str, method) method, michael@0: #include "jsast.tbl" michael@0: #undef ASTDEF michael@0: nullptr michael@0: }; michael@0: michael@0: enum YieldKind { Delegating, NotDelegating }; michael@0: michael@0: typedef AutoValueVector NodeVector; michael@0: michael@0: /* michael@0: * ParseNode is a somewhat intricate data structure, and its invariants have michael@0: * evolved, making it more likely that there could be a disconnect between the michael@0: * parser and the AST serializer. We use these macros to check invariants on a michael@0: * parse node and raise a dynamic error on failure. michael@0: */ michael@0: #define LOCAL_ASSERT(expr) \ michael@0: JS_BEGIN_MACRO \ michael@0: JS_ASSERT(expr); \ michael@0: if (!(expr)) { \ michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PARSE_NODE); \ michael@0: return false; \ michael@0: } \ michael@0: JS_END_MACRO michael@0: michael@0: #define LOCAL_NOT_REACHED(expr) \ michael@0: JS_BEGIN_MACRO \ michael@0: MOZ_ASSERT(false); \ michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PARSE_NODE); \ michael@0: return false; \ michael@0: JS_END_MACRO michael@0: michael@0: namespace { michael@0: michael@0: /* Set 'result' to obj[id] if any such property exists, else defaultValue. */ michael@0: static bool michael@0: GetPropertyDefault(JSContext *cx, HandleObject obj, HandleId id, HandleValue defaultValue, michael@0: MutableHandleValue result) michael@0: { michael@0: bool found; michael@0: if (!JSObject::hasProperty(cx, obj, id, &found)) michael@0: return false; michael@0: if (!found) { michael@0: result.set(defaultValue); michael@0: return true; michael@0: } michael@0: return JSObject::getGeneric(cx, obj, obj, id, result); michael@0: } michael@0: michael@0: /* michael@0: * Builder class that constructs JavaScript AST node objects. See: michael@0: * michael@0: * https://developer.mozilla.org/en/SpiderMonkey/Parser_API michael@0: * michael@0: * Bug 569487: generalize builder interface michael@0: */ michael@0: class NodeBuilder michael@0: { michael@0: typedef AutoValueArray CallbackArray; michael@0: michael@0: JSContext *cx; michael@0: TokenStream *tokenStream; michael@0: bool saveLoc; /* save source location information? */ michael@0: char const *src; /* source filename or null */ michael@0: RootedValue srcval; /* source filename JS value or null */ michael@0: CallbackArray callbacks; /* user-specified callbacks */ michael@0: RootedValue userv; /* user-specified builder object or null */ michael@0: michael@0: public: michael@0: NodeBuilder(JSContext *c, bool l, char const *s) michael@0: : cx(c), tokenStream(nullptr), saveLoc(l), src(s), srcval(c), callbacks(cx), michael@0: userv(c) michael@0: {} michael@0: michael@0: bool init(HandleObject userobj = js::NullPtr()) { michael@0: if (src) { michael@0: if (!atomValue(src, &srcval)) michael@0: return false; michael@0: } else { michael@0: srcval.setNull(); michael@0: } michael@0: michael@0: if (!userobj) { michael@0: userv.setNull(); michael@0: for (unsigned i = 0; i < AST_LIMIT; i++) { michael@0: callbacks[i].setNull(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: userv.setObject(*userobj); michael@0: michael@0: RootedValue nullVal(cx, NullValue()); michael@0: RootedValue funv(cx); michael@0: for (unsigned i = 0; i < AST_LIMIT; i++) { michael@0: const char *name = callbackNames[i]; michael@0: RootedAtom atom(cx, Atomize(cx, name, strlen(name))); michael@0: if (!atom) michael@0: return false; michael@0: RootedId id(cx, AtomToId(atom)); michael@0: if (!GetPropertyDefault(cx, userobj, id, nullVal, &funv)) michael@0: return false; michael@0: michael@0: if (funv.isNullOrUndefined()) { michael@0: callbacks[i].setNull(); michael@0: continue; michael@0: } michael@0: michael@0: if (!funv.isObject() || !funv.toObject().is()) { michael@0: js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_FUNCTION, michael@0: JSDVG_SEARCH_STACK, funv, js::NullPtr(), nullptr, nullptr); michael@0: return false; michael@0: } michael@0: michael@0: callbacks[i].set(funv); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void setTokenStream(TokenStream *ts) { michael@0: tokenStream = ts; michael@0: } michael@0: michael@0: private: michael@0: bool callback(HandleValue fun, TokenPos *pos, MutableHandleValue dst) { michael@0: if (saveLoc) { michael@0: RootedValue loc(cx); michael@0: if (!newNodeLoc(pos, &loc)) michael@0: return false; michael@0: AutoValueArray<1> argv(cx); michael@0: argv[0].set(loc); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: AutoValueArray<1> argv(cx); michael@0: argv[0].setNull(); /* no zero-length arrays allowed! */ michael@0: return Invoke(cx, userv, fun, 0, argv.begin(), dst); michael@0: } michael@0: michael@0: bool callback(HandleValue fun, HandleValue v1, TokenPos *pos, MutableHandleValue dst) { michael@0: if (saveLoc) { michael@0: RootedValue loc(cx); michael@0: if (!newNodeLoc(pos, &loc)) michael@0: return false; michael@0: AutoValueArray<2> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(loc); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: AutoValueArray<1> argv(cx); michael@0: argv[0].set(v1); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: bool callback(HandleValue fun, HandleValue v1, HandleValue v2, TokenPos *pos, michael@0: MutableHandleValue dst) { michael@0: if (saveLoc) { michael@0: RootedValue loc(cx); michael@0: if (!newNodeLoc(pos, &loc)) michael@0: return false; michael@0: AutoValueArray<3> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: argv[2].set(loc); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: AutoValueArray<2> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, TokenPos *pos, michael@0: MutableHandleValue dst) { michael@0: if (saveLoc) { michael@0: RootedValue loc(cx); michael@0: if (!newNodeLoc(pos, &loc)) michael@0: return false; michael@0: AutoValueArray<4> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: argv[2].set(v3); michael@0: argv[3].set(loc); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: AutoValueArray<3> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: argv[2].set(v3); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, HandleValue v4, michael@0: TokenPos *pos, MutableHandleValue dst) { michael@0: if (saveLoc) { michael@0: RootedValue loc(cx); michael@0: if (!newNodeLoc(pos, &loc)) michael@0: return false; michael@0: AutoValueArray<5> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: argv[2].set(v3); michael@0: argv[3].set(v4); michael@0: argv[4].set(loc); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: AutoValueArray<4> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: argv[2].set(v3); michael@0: argv[3].set(v4); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, HandleValue v4, michael@0: HandleValue v5, TokenPos *pos, MutableHandleValue dst) { michael@0: if (saveLoc) { michael@0: RootedValue loc(cx); michael@0: if (!newNodeLoc(pos, &loc)) michael@0: return false; michael@0: AutoValueArray<6> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: argv[2].set(v3); michael@0: argv[3].set(v4); michael@0: argv[4].set(v5); michael@0: argv[5].set(loc); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: AutoValueArray<5> argv(cx); michael@0: argv[0].set(v1); michael@0: argv[1].set(v2); michael@0: argv[2].set(v3); michael@0: argv[3].set(v4); michael@0: argv[4].set(v5); michael@0: return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); michael@0: } michael@0: michael@0: // WARNING: Returning a Handle is non-standard, but it works in this case michael@0: // because both |v| and |UndefinedHandleValue| are definitely rooted on a michael@0: // previous stack frame (i.e. we're just choosing between two michael@0: // already-rooted values). michael@0: HandleValue opt(HandleValue v) { michael@0: JS_ASSERT_IF(v.isMagic(), v.whyMagic() == JS_SERIALIZE_NO_NODE); michael@0: return v.isMagic(JS_SERIALIZE_NO_NODE) ? JS::UndefinedHandleValue : v; michael@0: } michael@0: michael@0: bool atomValue(const char *s, MutableHandleValue dst) { michael@0: /* michael@0: * Bug 575416: instead of Atomize, lookup constant atoms in tbl file michael@0: */ michael@0: RootedAtom atom(cx, Atomize(cx, s, strlen(s))); michael@0: if (!atom) michael@0: return false; michael@0: michael@0: dst.setString(atom); michael@0: return true; michael@0: } michael@0: michael@0: bool newObject(MutableHandleObject dst) { michael@0: RootedObject nobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); michael@0: if (!nobj) michael@0: return false; michael@0: michael@0: dst.set(nobj); michael@0: return true; michael@0: } michael@0: michael@0: bool newArray(NodeVector &elts, MutableHandleValue dst); michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, MutableHandleObject dst); michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, MutableHandleValue dst) { michael@0: RootedObject node(cx); michael@0: return newNode(type, pos, &node) && michael@0: setResult(node, dst); michael@0: } michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, michael@0: const char *childName, HandleValue child, michael@0: MutableHandleValue dst) { michael@0: RootedObject node(cx); michael@0: return newNode(type, pos, &node) && michael@0: setProperty(node, childName, child) && michael@0: setResult(node, dst); michael@0: } michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, michael@0: const char *childName1, HandleValue child1, michael@0: const char *childName2, HandleValue child2, michael@0: MutableHandleValue dst) { michael@0: RootedObject node(cx); michael@0: return newNode(type, pos, &node) && michael@0: setProperty(node, childName1, child1) && michael@0: setProperty(node, childName2, child2) && michael@0: setResult(node, dst); michael@0: } michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, michael@0: const char *childName1, HandleValue child1, michael@0: const char *childName2, HandleValue child2, michael@0: const char *childName3, HandleValue child3, michael@0: MutableHandleValue dst) { michael@0: RootedObject node(cx); michael@0: return newNode(type, pos, &node) && michael@0: setProperty(node, childName1, child1) && michael@0: setProperty(node, childName2, child2) && michael@0: setProperty(node, childName3, child3) && michael@0: setResult(node, dst); michael@0: } michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, michael@0: const char *childName1, HandleValue child1, michael@0: const char *childName2, HandleValue child2, michael@0: const char *childName3, HandleValue child3, michael@0: const char *childName4, HandleValue child4, michael@0: MutableHandleValue dst) { michael@0: RootedObject node(cx); michael@0: return newNode(type, pos, &node) && michael@0: setProperty(node, childName1, child1) && michael@0: setProperty(node, childName2, child2) && michael@0: setProperty(node, childName3, child3) && michael@0: setProperty(node, childName4, child4) && michael@0: setResult(node, dst); michael@0: } michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, michael@0: const char *childName1, HandleValue child1, michael@0: const char *childName2, HandleValue child2, michael@0: const char *childName3, HandleValue child3, michael@0: const char *childName4, HandleValue child4, michael@0: const char *childName5, HandleValue child5, michael@0: MutableHandleValue dst) { michael@0: RootedObject node(cx); michael@0: return newNode(type, pos, &node) && michael@0: setProperty(node, childName1, child1) && michael@0: setProperty(node, childName2, child2) && michael@0: setProperty(node, childName3, child3) && michael@0: setProperty(node, childName4, child4) && michael@0: setProperty(node, childName5, child5) && michael@0: setResult(node, dst); michael@0: } michael@0: michael@0: bool newNode(ASTType type, TokenPos *pos, michael@0: const char *childName1, HandleValue child1, michael@0: const char *childName2, HandleValue child2, michael@0: const char *childName3, HandleValue child3, michael@0: const char *childName4, HandleValue child4, michael@0: const char *childName5, HandleValue child5, michael@0: const char *childName6, HandleValue child6, michael@0: const char *childName7, HandleValue child7, michael@0: MutableHandleValue dst) { michael@0: RootedObject node(cx); michael@0: return newNode(type, pos, &node) && michael@0: setProperty(node, childName1, child1) && michael@0: setProperty(node, childName2, child2) && michael@0: setProperty(node, childName3, child3) && michael@0: setProperty(node, childName4, child4) && michael@0: setProperty(node, childName5, child5) && michael@0: setProperty(node, childName6, child6) && michael@0: setProperty(node, childName7, child7) && michael@0: setResult(node, dst); michael@0: } michael@0: michael@0: bool listNode(ASTType type, const char *propName, NodeVector &elts, TokenPos *pos, michael@0: MutableHandleValue dst) { michael@0: RootedValue array(cx); michael@0: if (!newArray(elts, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[type]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, array, pos, dst); michael@0: michael@0: return newNode(type, pos, propName, array, dst); michael@0: } michael@0: michael@0: bool setProperty(HandleObject obj, const char *name, HandleValue val) { michael@0: JS_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE); michael@0: michael@0: /* michael@0: * Bug 575416: instead of Atomize, lookup constant atoms in tbl file michael@0: */ michael@0: RootedAtom atom(cx, Atomize(cx, name, strlen(name))); michael@0: if (!atom) michael@0: return false; michael@0: michael@0: /* Represent "no node" as null and ensure users are not exposed to magic values. */ michael@0: RootedValue optVal(cx, val.isMagic(JS_SERIALIZE_NO_NODE) ? NullValue() : val); michael@0: return JSObject::defineProperty(cx, obj, atom->asPropertyName(), optVal); michael@0: } michael@0: michael@0: bool newNodeLoc(TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool setNodeLoc(HandleObject node, TokenPos *pos); michael@0: michael@0: bool setResult(HandleObject obj, MutableHandleValue dst) { michael@0: JS_ASSERT(obj); michael@0: dst.setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: public: michael@0: /* michael@0: * All of the public builder methods take as their last two michael@0: * arguments a nullable token position and a non-nullable, rooted michael@0: * outparam. michael@0: * michael@0: * Any Value arguments representing optional subnodes may be a michael@0: * JS_SERIALIZE_NO_NODE magic value. michael@0: */ michael@0: michael@0: /* michael@0: * misc nodes michael@0: */ michael@0: michael@0: bool program(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool literal(HandleValue val, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool identifier(HandleValue name, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool function(ASTType type, TokenPos *pos, michael@0: HandleValue id, NodeVector &args, NodeVector &defaults, michael@0: HandleValue body, HandleValue rest, bool isGenerator, bool isExpression, michael@0: MutableHandleValue dst); michael@0: michael@0: bool variableDeclarator(HandleValue id, HandleValue init, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool switchCase(HandleValue expr, NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: michael@0: /* michael@0: * statements michael@0: */ michael@0: michael@0: bool blockStatement(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool expressionStatement(HandleValue expr, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool emptyStatement(TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool ifStatement(HandleValue test, HandleValue cons, HandleValue alt, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool breakStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool continueStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool labeledStatement(HandleValue label, HandleValue stmt, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool throwStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool returnStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool forStatement(HandleValue init, HandleValue test, HandleValue update, HandleValue stmt, michael@0: TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool forInStatement(HandleValue var, HandleValue expr, HandleValue stmt, michael@0: bool isForEach, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool forOfStatement(HandleValue var, HandleValue expr, HandleValue stmt, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool withStatement(HandleValue expr, HandleValue stmt, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool whileStatement(HandleValue test, HandleValue stmt, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool doWhileStatement(HandleValue stmt, HandleValue test, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool switchStatement(HandleValue disc, NodeVector &elts, bool lexical, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool tryStatement(HandleValue body, NodeVector &guarded, HandleValue unguarded, michael@0: HandleValue finally, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool debuggerStatement(TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool letStatement(NodeVector &head, HandleValue stmt, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool importDeclaration(NodeVector &elts, HandleValue moduleSpec, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool exportDeclaration(HandleValue decl, NodeVector &elts, HandleValue moduleSpec, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool exportBatchSpecifier(TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: /* michael@0: * expressions michael@0: */ michael@0: michael@0: bool binaryExpression(BinaryOperator op, HandleValue left, HandleValue right, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool unaryExpression(UnaryOperator op, HandleValue expr, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool assignmentExpression(AssignmentOperator op, HandleValue lhs, HandleValue rhs, michael@0: TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool updateExpression(HandleValue expr, bool incr, bool prefix, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool logicalExpression(bool lor, HandleValue left, HandleValue right, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool conditionalExpression(HandleValue test, HandleValue cons, HandleValue alt, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool sequenceExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool newExpression(HandleValue callee, NodeVector &args, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool callExpression(HandleValue callee, NodeVector &args, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool memberExpression(bool computed, HandleValue expr, HandleValue member, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool arrayExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool spreadExpression(HandleValue expr, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool objectExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool thisExpression(TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool yieldExpression(HandleValue arg, YieldKind kind, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach, bool isForOf, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: bool comprehensionExpression(HandleValue body, NodeVector &blocks, HandleValue filter, michael@0: TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool generatorExpression(HandleValue body, NodeVector &blocks, HandleValue filter, michael@0: TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool letExpression(NodeVector &head, HandleValue expr, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: /* michael@0: * declarations michael@0: */ michael@0: michael@0: bool variableDeclaration(NodeVector &elts, VarDeclKind kind, TokenPos *pos, michael@0: MutableHandleValue dst); michael@0: michael@0: /* michael@0: * patterns michael@0: */ michael@0: michael@0: bool arrayPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool objectPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool propertyPattern(HandleValue key, HandleValue patt, TokenPos *pos, MutableHandleValue dst); michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: bool michael@0: NodeBuilder::newNode(ASTType type, TokenPos *pos, MutableHandleObject dst) michael@0: { michael@0: JS_ASSERT(type > AST_ERROR && type < AST_LIMIT); michael@0: michael@0: RootedValue tv(cx); michael@0: RootedObject node(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); michael@0: if (!node || michael@0: !setNodeLoc(node, pos) || michael@0: !atomValue(nodeTypeNames[type], &tv) || michael@0: !setProperty(node, "type", tv)) { michael@0: return false; michael@0: } michael@0: michael@0: dst.set(node); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::newArray(NodeVector &elts, MutableHandleValue dst) michael@0: { michael@0: const size_t len = elts.length(); michael@0: if (len > UINT32_MAX) { michael@0: js_ReportAllocationOverflow(cx); michael@0: return false; michael@0: } michael@0: RootedObject array(cx, NewDenseAllocatedArray(cx, uint32_t(len))); michael@0: if (!array) michael@0: return false; michael@0: michael@0: for (size_t i = 0; i < len; i++) { michael@0: RootedValue val(cx, elts[i]); michael@0: michael@0: JS_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE); michael@0: michael@0: /* Represent "no node" as an array hole by not adding the value. */ michael@0: if (val.isMagic(JS_SERIALIZE_NO_NODE)) michael@0: continue; michael@0: michael@0: if (!JSObject::setElement(cx, array, array, i, &val, false)) michael@0: return false; michael@0: } michael@0: michael@0: dst.setObject(*array); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::newNodeLoc(TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: if (!pos) { michael@0: dst.setNull(); michael@0: return true; michael@0: } michael@0: michael@0: RootedObject loc(cx); michael@0: RootedObject to(cx); michael@0: RootedValue val(cx); michael@0: michael@0: if (!newObject(&loc)) michael@0: return false; michael@0: michael@0: dst.setObject(*loc); michael@0: michael@0: uint32_t startLineNum, startColumnIndex; michael@0: uint32_t endLineNum, endColumnIndex; michael@0: tokenStream->srcCoords.lineNumAndColumnIndex(pos->begin, &startLineNum, &startColumnIndex); michael@0: tokenStream->srcCoords.lineNumAndColumnIndex(pos->end, &endLineNum, &endColumnIndex); michael@0: michael@0: if (!newObject(&to)) michael@0: return false; michael@0: val.setObject(*to); michael@0: if (!setProperty(loc, "start", val)) michael@0: return false; michael@0: val.setNumber(startLineNum); michael@0: if (!setProperty(to, "line", val)) michael@0: return false; michael@0: val.setNumber(startColumnIndex); michael@0: if (!setProperty(to, "column", val)) michael@0: return false; michael@0: michael@0: if (!newObject(&to)) michael@0: return false; michael@0: val.setObject(*to); michael@0: if (!setProperty(loc, "end", val)) michael@0: return false; michael@0: val.setNumber(endLineNum); michael@0: if (!setProperty(to, "line", val)) michael@0: return false; michael@0: val.setNumber(endColumnIndex); michael@0: if (!setProperty(to, "column", val)) michael@0: return false; michael@0: michael@0: if (!setProperty(loc, "source", srcval)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::setNodeLoc(HandleObject node, TokenPos *pos) michael@0: { michael@0: if (!saveLoc) { michael@0: RootedValue nullVal(cx, NullValue()); michael@0: setProperty(node, "loc", nullVal); michael@0: return true; michael@0: } michael@0: michael@0: RootedValue loc(cx); michael@0: return newNodeLoc(pos, &loc) && michael@0: setProperty(node, "loc", loc); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::program(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return listNode(AST_PROGRAM, "body", elts, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::blockStatement(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return listNode(AST_BLOCK_STMT, "body", elts, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::expressionStatement(HandleValue expr, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_EXPR_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, expr, pos, dst); michael@0: michael@0: return newNode(AST_EXPR_STMT, pos, "expression", expr, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::emptyStatement(TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_EMPTY_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, pos, dst); michael@0: michael@0: return newNode(AST_EMPTY_STMT, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::ifStatement(HandleValue test, HandleValue cons, HandleValue alt, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_IF_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, test, cons, opt(alt), pos, dst); michael@0: michael@0: return newNode(AST_IF_STMT, pos, michael@0: "test", test, michael@0: "consequent", cons, michael@0: "alternate", alt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::breakStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_BREAK_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opt(label), pos, dst); michael@0: michael@0: return newNode(AST_BREAK_STMT, pos, "label", label, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::continueStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_CONTINUE_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opt(label), pos, dst); michael@0: michael@0: return newNode(AST_CONTINUE_STMT, pos, "label", label, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::labeledStatement(HandleValue label, HandleValue stmt, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_LAB_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, label, stmt, pos, dst); michael@0: michael@0: return newNode(AST_LAB_STMT, pos, michael@0: "label", label, michael@0: "body", stmt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::throwStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_THROW_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, arg, pos, dst); michael@0: michael@0: return newNode(AST_THROW_STMT, pos, "argument", arg, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::returnStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_RETURN_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opt(arg), pos, dst); michael@0: michael@0: return newNode(AST_RETURN_STMT, pos, "argument", arg, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::forStatement(HandleValue init, HandleValue test, HandleValue update, HandleValue stmt, michael@0: TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_FOR_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opt(init), opt(test), opt(update), stmt, pos, dst); michael@0: michael@0: return newNode(AST_FOR_STMT, pos, michael@0: "init", init, michael@0: "test", test, michael@0: "update", update, michael@0: "body", stmt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::forInStatement(HandleValue var, HandleValue expr, HandleValue stmt, bool isForEach, michael@0: TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue isForEachVal(cx, BooleanValue(isForEach)); michael@0: michael@0: RootedValue cb(cx, callbacks[AST_FOR_IN_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, var, expr, stmt, isForEachVal, pos, dst); michael@0: michael@0: return newNode(AST_FOR_IN_STMT, pos, michael@0: "left", var, michael@0: "right", expr, michael@0: "body", stmt, michael@0: "each", isForEachVal, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::forOfStatement(HandleValue var, HandleValue expr, HandleValue stmt, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_FOR_OF_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, var, expr, stmt, pos, dst); michael@0: michael@0: return newNode(AST_FOR_OF_STMT, pos, michael@0: "left", var, michael@0: "right", expr, michael@0: "body", stmt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::withStatement(HandleValue expr, HandleValue stmt, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_WITH_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, expr, stmt, pos, dst); michael@0: michael@0: return newNode(AST_WITH_STMT, pos, michael@0: "object", expr, michael@0: "body", stmt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::whileStatement(HandleValue test, HandleValue stmt, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_WHILE_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, test, stmt, pos, dst); michael@0: michael@0: return newNode(AST_WHILE_STMT, pos, michael@0: "test", test, michael@0: "body", stmt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::doWhileStatement(HandleValue stmt, HandleValue test, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_DO_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, stmt, test, pos, dst); michael@0: michael@0: return newNode(AST_DO_STMT, pos, michael@0: "body", stmt, michael@0: "test", test, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::switchStatement(HandleValue disc, NodeVector &elts, bool lexical, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(elts, &array)) michael@0: return false; michael@0: michael@0: RootedValue lexicalVal(cx, BooleanValue(lexical)); michael@0: michael@0: RootedValue cb(cx, callbacks[AST_SWITCH_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, disc, array, lexicalVal, pos, dst); michael@0: michael@0: return newNode(AST_SWITCH_STMT, pos, michael@0: "discriminant", disc, michael@0: "cases", array, michael@0: "lexical", lexicalVal, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::tryStatement(HandleValue body, NodeVector &guarded, HandleValue unguarded, michael@0: HandleValue finally, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue guardedHandlers(cx); michael@0: if (!newArray(guarded, &guardedHandlers)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_TRY_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, body, guardedHandlers, unguarded, opt(finally), pos, dst); michael@0: michael@0: return newNode(AST_TRY_STMT, pos, michael@0: "block", body, michael@0: "guardedHandlers", guardedHandlers, michael@0: "handler", unguarded, michael@0: "finalizer", finally, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::debuggerStatement(TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_DEBUGGER_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, pos, dst); michael@0: michael@0: return newNode(AST_DEBUGGER_STMT, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::binaryExpression(BinaryOperator op, HandleValue left, HandleValue right, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); michael@0: michael@0: RootedValue opName(cx); michael@0: if (!atomValue(binopNames[op], &opName)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_BINARY_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opName, left, right, pos, dst); michael@0: michael@0: return newNode(AST_BINARY_EXPR, pos, michael@0: "operator", opName, michael@0: "left", left, michael@0: "right", right, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::unaryExpression(UnaryOperator unop, HandleValue expr, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(unop > UNOP_ERR && unop < UNOP_LIMIT); michael@0: michael@0: RootedValue opName(cx); michael@0: if (!atomValue(unopNames[unop], &opName)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_UNARY_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opName, expr, pos, dst); michael@0: michael@0: RootedValue trueVal(cx, BooleanValue(true)); michael@0: return newNode(AST_UNARY_EXPR, pos, michael@0: "operator", opName, michael@0: "argument", expr, michael@0: "prefix", trueVal, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::assignmentExpression(AssignmentOperator aop, HandleValue lhs, HandleValue rhs, michael@0: TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(aop > AOP_ERR && aop < AOP_LIMIT); michael@0: michael@0: RootedValue opName(cx); michael@0: if (!atomValue(aopNames[aop], &opName)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_ASSIGN_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opName, lhs, rhs, pos, dst); michael@0: michael@0: return newNode(AST_ASSIGN_EXPR, pos, michael@0: "operator", opName, michael@0: "left", lhs, michael@0: "right", rhs, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::updateExpression(HandleValue expr, bool incr, bool prefix, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue opName(cx); michael@0: if (!atomValue(incr ? "++" : "--", &opName)) michael@0: return false; michael@0: michael@0: RootedValue prefixVal(cx, BooleanValue(prefix)); michael@0: michael@0: RootedValue cb(cx, callbacks[AST_UPDATE_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, expr, opName, prefixVal, pos, dst); michael@0: michael@0: return newNode(AST_UPDATE_EXPR, pos, michael@0: "operator", opName, michael@0: "argument", expr, michael@0: "prefix", prefixVal, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::logicalExpression(bool lor, HandleValue left, HandleValue right, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue opName(cx); michael@0: if (!atomValue(lor ? "||" : "&&", &opName)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_LOGICAL_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opName, left, right, pos, dst); michael@0: michael@0: return newNode(AST_LOGICAL_EXPR, pos, michael@0: "operator", opName, michael@0: "left", left, michael@0: "right", right, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::conditionalExpression(HandleValue test, HandleValue cons, HandleValue alt, michael@0: TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_COND_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, test, cons, alt, pos, dst); michael@0: michael@0: return newNode(AST_COND_EXPR, pos, michael@0: "test", test, michael@0: "consequent", cons, michael@0: "alternate", alt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::sequenceExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return listNode(AST_LIST_EXPR, "expressions", elts, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::callExpression(HandleValue callee, NodeVector &args, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(args, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_CALL_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, callee, array, pos, dst); michael@0: michael@0: return newNode(AST_CALL_EXPR, pos, michael@0: "callee", callee, michael@0: "arguments", array, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::newExpression(HandleValue callee, NodeVector &args, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(args, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_NEW_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, callee, array, pos, dst); michael@0: michael@0: return newNode(AST_NEW_EXPR, pos, michael@0: "callee", callee, michael@0: "arguments", array, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::memberExpression(bool computed, HandleValue expr, HandleValue member, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue computedVal(cx, BooleanValue(computed)); michael@0: michael@0: RootedValue cb(cx, callbacks[AST_MEMBER_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, computedVal, expr, member, pos, dst); michael@0: michael@0: return newNode(AST_MEMBER_EXPR, pos, michael@0: "object", expr, michael@0: "property", member, michael@0: "computed", computedVal, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::arrayExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return listNode(AST_ARRAY_EXPR, "elements", elts, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::spreadExpression(HandleValue expr, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return newNode(AST_SPREAD_EXPR, pos, michael@0: "expression", expr, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::propertyPattern(HandleValue key, HandleValue patt, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue kindName(cx); michael@0: if (!atomValue("init", &kindName)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_PROP_PATT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, key, patt, pos, dst); michael@0: michael@0: return newNode(AST_PROP_PATT, pos, michael@0: "key", key, michael@0: "value", patt, michael@0: "kind", kindName, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::propertyInitializer(HandleValue key, HandleValue val, PropKind kind, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue kindName(cx); michael@0: if (!atomValue(kind == PROP_INIT michael@0: ? "init" michael@0: : kind == PROP_GETTER michael@0: ? "get" michael@0: : "set", &kindName)) { michael@0: return false; michael@0: } michael@0: michael@0: RootedValue cb(cx, callbacks[AST_PROPERTY]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, kindName, key, val, pos, dst); michael@0: michael@0: return newNode(AST_PROPERTY, pos, michael@0: "key", key, michael@0: "value", val, michael@0: "kind", kindName, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::objectExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return listNode(AST_OBJECT_EXPR, "properties", elts, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::thisExpression(TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_THIS_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, pos, dst); michael@0: michael@0: return newNode(AST_THIS_EXPR, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::yieldExpression(HandleValue arg, YieldKind kind, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_YIELD_EXPR]); michael@0: RootedValue delegateVal(cx); michael@0: michael@0: switch (kind) { michael@0: case Delegating: michael@0: delegateVal = BooleanValue(true); michael@0: break; michael@0: case NotDelegating: michael@0: delegateVal = BooleanValue(false); michael@0: break; michael@0: } michael@0: michael@0: if (!cb.isNull()) michael@0: return callback(cb, opt(arg), delegateVal, pos, dst); michael@0: return newNode(AST_YIELD_EXPR, pos, "argument", arg, "delegate", delegateVal, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach, bool isForOf, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue isForEachVal(cx, BooleanValue(isForEach)); michael@0: RootedValue isForOfVal(cx, BooleanValue(isForOf)); michael@0: michael@0: RootedValue cb(cx, callbacks[AST_COMP_BLOCK]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, patt, src, isForEachVal, isForOfVal, pos, dst); michael@0: michael@0: return newNode(AST_COMP_BLOCK, pos, michael@0: "left", patt, michael@0: "right", src, michael@0: "each", isForEachVal, michael@0: "of", isForOfVal, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::comprehensionExpression(HandleValue body, NodeVector &blocks, HandleValue filter, michael@0: TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(blocks, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_COMP_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, body, array, opt(filter), pos, dst); michael@0: michael@0: return newNode(AST_COMP_EXPR, pos, michael@0: "body", body, michael@0: "blocks", array, michael@0: "filter", filter, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::generatorExpression(HandleValue body, NodeVector &blocks, HandleValue filter, michael@0: TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(blocks, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_GENERATOR_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, body, array, opt(filter), pos, dst); michael@0: michael@0: return newNode(AST_GENERATOR_EXPR, pos, michael@0: "body", body, michael@0: "blocks", array, michael@0: "filter", filter, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::letExpression(NodeVector &head, HandleValue expr, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(head, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_LET_EXPR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, array, expr, pos, dst); michael@0: michael@0: return newNode(AST_LET_EXPR, pos, michael@0: "head", array, michael@0: "body", expr, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::letStatement(NodeVector &head, HandleValue stmt, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(head, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_LET_STMT]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, array, stmt, pos, dst); michael@0: michael@0: return newNode(AST_LET_STMT, pos, michael@0: "head", array, michael@0: "body", stmt, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::importDeclaration(NodeVector &elts, HandleValue moduleSpec, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(elts, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_IMPORT_DECL]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, array, moduleSpec, pos, dst); michael@0: michael@0: return newNode(AST_IMPORT_DECL, pos, michael@0: "specifiers", array, michael@0: "source", moduleSpec, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_IMPORT_SPEC]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, importName, bindingName, pos, dst); michael@0: michael@0: return newNode(AST_IMPORT_SPEC, pos, michael@0: "id", importName, michael@0: "name", bindingName, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::exportDeclaration(HandleValue decl, NodeVector &elts, HandleValue moduleSpec, michael@0: TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx, NullValue()); michael@0: if (decl.isNull() && !newArray(elts, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_IMPORT_DECL]); michael@0: michael@0: if (!cb.isNull()) michael@0: return callback(cb, decl, array, moduleSpec, pos, dst); michael@0: michael@0: return newNode(AST_EXPORT_DECL, pos, michael@0: "declaration", decl, michael@0: "specifiers", array, michael@0: "source", moduleSpec, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_EXPORT_SPEC]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, bindingName, exportName, pos, dst); michael@0: michael@0: return newNode(AST_EXPORT_SPEC, pos, michael@0: "id", bindingName, michael@0: "name", exportName, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::exportBatchSpecifier(TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_EXPORT_BATCH_SPEC]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, pos, dst); michael@0: michael@0: return newNode(AST_EXPORT_BATCH_SPEC, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::variableDeclaration(NodeVector &elts, VarDeclKind kind, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(kind > VARDECL_ERR && kind < VARDECL_LIMIT); michael@0: michael@0: RootedValue array(cx), kindName(cx); michael@0: if (!newArray(elts, &array) || michael@0: !atomValue(kind == VARDECL_CONST michael@0: ? "const" michael@0: : kind == VARDECL_LET michael@0: ? "let" michael@0: : "var", &kindName)) { michael@0: return false; michael@0: } michael@0: michael@0: RootedValue cb(cx, callbacks[AST_VAR_DECL]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, kindName, array, pos, dst); michael@0: michael@0: return newNode(AST_VAR_DECL, pos, michael@0: "kind", kindName, michael@0: "declarations", array, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::variableDeclarator(HandleValue id, HandleValue init, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_VAR_DTOR]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, id, opt(init), pos, dst); michael@0: michael@0: return newNode(AST_VAR_DTOR, pos, "id", id, "init", init, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::switchCase(HandleValue expr, NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx); michael@0: if (!newArray(elts, &array)) michael@0: return false; michael@0: michael@0: RootedValue cb(cx, callbacks[AST_CASE]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, opt(expr), array, pos, dst); michael@0: michael@0: return newNode(AST_CASE, pos, michael@0: "test", expr, michael@0: "consequent", array, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos *pos, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_CATCH]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, var, opt(guard), body, pos, dst); michael@0: michael@0: return newNode(AST_CATCH, pos, michael@0: "param", var, michael@0: "guard", guard, michael@0: "body", body, michael@0: dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::literal(HandleValue val, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_LITERAL]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, val, pos, dst); michael@0: michael@0: return newNode(AST_LITERAL, pos, "value", val, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::identifier(HandleValue name, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue cb(cx, callbacks[AST_IDENTIFIER]); michael@0: if (!cb.isNull()) michael@0: return callback(cb, name, pos, dst); michael@0: michael@0: return newNode(AST_IDENTIFIER, pos, "name", name, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::objectPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return listNode(AST_OBJECT_PATT, "properties", elts, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::arrayPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: return listNode(AST_ARRAY_PATT, "elements", elts, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: NodeBuilder::function(ASTType type, TokenPos *pos, michael@0: HandleValue id, NodeVector &args, NodeVector &defaults, michael@0: HandleValue body, HandleValue rest, michael@0: bool isGenerator, bool isExpression, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue array(cx), defarray(cx); michael@0: if (!newArray(args, &array)) michael@0: return false; michael@0: if (!newArray(defaults, &defarray)) michael@0: return false; michael@0: michael@0: RootedValue isGeneratorVal(cx, BooleanValue(isGenerator)); michael@0: RootedValue isExpressionVal(cx, BooleanValue(isExpression)); michael@0: michael@0: RootedValue cb(cx, callbacks[type]); michael@0: if (!cb.isNull()) { michael@0: return callback(cb, opt(id), array, body, isGeneratorVal, isExpressionVal, pos, dst); michael@0: } michael@0: michael@0: return newNode(type, pos, michael@0: "id", id, michael@0: "params", array, michael@0: "defaults", defarray, michael@0: "body", body, michael@0: "rest", rest, michael@0: "generator", isGeneratorVal, michael@0: "expression", isExpressionVal, michael@0: dst); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: /* michael@0: * Serialization of parse nodes to JavaScript objects. michael@0: * michael@0: * All serialization methods take a non-nullable ParseNode pointer. michael@0: */ michael@0: class ASTSerializer michael@0: { michael@0: JSContext *cx; michael@0: Parser *parser; michael@0: NodeBuilder builder; michael@0: DebugOnly lineno; michael@0: michael@0: Value unrootedAtomContents(JSAtom *atom) { michael@0: return StringValue(atom ? atom : cx->names().empty); michael@0: } michael@0: michael@0: BinaryOperator binop(ParseNodeKind kind, JSOp op); michael@0: UnaryOperator unop(ParseNodeKind kind, JSOp op); michael@0: AssignmentOperator aop(JSOp op); michael@0: michael@0: bool statements(ParseNode *pn, NodeVector &elts); michael@0: bool expressions(ParseNode *pn, NodeVector &elts); michael@0: bool leftAssociate(ParseNode *pn, MutableHandleValue dst); michael@0: bool functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestruct, ParseNode *pnbody, michael@0: NodeVector &args, NodeVector &defaults, MutableHandleValue rest); michael@0: michael@0: bool sourceElement(ParseNode *pn, MutableHandleValue dst); michael@0: michael@0: bool declaration(ParseNode *pn, MutableHandleValue dst); michael@0: bool variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst); michael@0: bool variableDeclarator(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); michael@0: bool let(ParseNode *pn, bool expr, MutableHandleValue dst); michael@0: bool importDeclaration(ParseNode *pn, MutableHandleValue dst); michael@0: bool importSpecifier(ParseNode *pn, MutableHandleValue dst); michael@0: bool exportDeclaration(ParseNode *pn, MutableHandleValue dst); michael@0: bool exportSpecifier(ParseNode *pn, MutableHandleValue dst); michael@0: michael@0: bool optStatement(ParseNode *pn, MutableHandleValue dst) { michael@0: if (!pn) { michael@0: dst.setMagic(JS_SERIALIZE_NO_NODE); michael@0: return true; michael@0: } michael@0: return statement(pn, dst); michael@0: } michael@0: michael@0: bool forInit(ParseNode *pn, MutableHandleValue dst); michael@0: bool forIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, michael@0: MutableHandleValue dst); michael@0: bool forOf(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, michael@0: MutableHandleValue dst); michael@0: bool statement(ParseNode *pn, MutableHandleValue dst); michael@0: bool blockStatement(ParseNode *pn, MutableHandleValue dst); michael@0: bool switchStatement(ParseNode *pn, MutableHandleValue dst); michael@0: bool switchCase(ParseNode *pn, MutableHandleValue dst); michael@0: bool tryStatement(ParseNode *pn, MutableHandleValue dst); michael@0: bool catchClause(ParseNode *pn, bool *isGuarded, MutableHandleValue dst); michael@0: michael@0: bool optExpression(ParseNode *pn, MutableHandleValue dst) { michael@0: if (!pn) { michael@0: dst.setMagic(JS_SERIALIZE_NO_NODE); michael@0: return true; michael@0: } michael@0: return expression(pn, dst); michael@0: } michael@0: michael@0: bool expression(ParseNode *pn, MutableHandleValue dst); michael@0: michael@0: bool propertyName(ParseNode *pn, MutableHandleValue dst); michael@0: bool property(ParseNode *pn, MutableHandleValue dst); michael@0: michael@0: bool optIdentifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst) { michael@0: if (!atom) { michael@0: dst.setMagic(JS_SERIALIZE_NO_NODE); michael@0: return true; michael@0: } michael@0: return identifier(atom, pos, dst); michael@0: } michael@0: michael@0: bool identifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst); michael@0: bool identifier(ParseNode *pn, MutableHandleValue dst); michael@0: bool literal(ParseNode *pn, MutableHandleValue dst); michael@0: michael@0: bool pattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); michael@0: bool arrayPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); michael@0: bool objectPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); michael@0: michael@0: bool function(ParseNode *pn, ASTType type, MutableHandleValue dst); michael@0: bool functionArgsAndBody(ParseNode *pn, NodeVector &args, NodeVector &defaults, michael@0: MutableHandleValue body, MutableHandleValue rest); michael@0: bool functionBody(ParseNode *pn, TokenPos *pos, MutableHandleValue dst); michael@0: michael@0: bool comprehensionBlock(ParseNode *pn, MutableHandleValue dst); michael@0: bool comprehension(ParseNode *pn, MutableHandleValue dst); michael@0: bool generatorExpression(ParseNode *pn, MutableHandleValue dst); michael@0: michael@0: public: michael@0: ASTSerializer(JSContext *c, bool l, char const *src, uint32_t ln) michael@0: : cx(c) michael@0: , builder(c, l, src) michael@0: #ifdef DEBUG michael@0: , lineno(ln) michael@0: #endif michael@0: {} michael@0: michael@0: bool init(HandleObject userobj) { michael@0: return builder.init(userobj); michael@0: } michael@0: michael@0: void setParser(Parser *p) { michael@0: parser = p; michael@0: builder.setTokenStream(&p->tokenStream); michael@0: } michael@0: michael@0: bool program(ParseNode *pn, MutableHandleValue dst); michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: AssignmentOperator michael@0: ASTSerializer::aop(JSOp op) michael@0: { michael@0: switch (op) { michael@0: case JSOP_NOP: michael@0: return AOP_ASSIGN; michael@0: case JSOP_ADD: michael@0: return AOP_PLUS; michael@0: case JSOP_SUB: michael@0: return AOP_MINUS; michael@0: case JSOP_MUL: michael@0: return AOP_STAR; michael@0: case JSOP_DIV: michael@0: return AOP_DIV; michael@0: case JSOP_MOD: michael@0: return AOP_MOD; michael@0: case JSOP_LSH: michael@0: return AOP_LSH; michael@0: case JSOP_RSH: michael@0: return AOP_RSH; michael@0: case JSOP_URSH: michael@0: return AOP_URSH; michael@0: case JSOP_BITOR: michael@0: return AOP_BITOR; michael@0: case JSOP_BITXOR: michael@0: return AOP_BITXOR; michael@0: case JSOP_BITAND: michael@0: return AOP_BITAND; michael@0: default: michael@0: return AOP_ERR; michael@0: } michael@0: } michael@0: michael@0: UnaryOperator michael@0: ASTSerializer::unop(ParseNodeKind kind, JSOp op) michael@0: { michael@0: if (kind == PNK_DELETE) michael@0: return UNOP_DELETE; michael@0: michael@0: switch (op) { michael@0: case JSOP_NEG: michael@0: return UNOP_NEG; michael@0: case JSOP_POS: michael@0: return UNOP_POS; michael@0: case JSOP_NOT: michael@0: return UNOP_NOT; michael@0: case JSOP_BITNOT: michael@0: return UNOP_BITNOT; michael@0: case JSOP_TYPEOF: michael@0: case JSOP_TYPEOFEXPR: michael@0: return UNOP_TYPEOF; michael@0: case JSOP_VOID: michael@0: return UNOP_VOID; michael@0: default: michael@0: return UNOP_ERR; michael@0: } michael@0: } michael@0: michael@0: BinaryOperator michael@0: ASTSerializer::binop(ParseNodeKind kind, JSOp op) michael@0: { michael@0: switch (kind) { michael@0: case PNK_LSH: michael@0: return BINOP_LSH; michael@0: case PNK_RSH: michael@0: return BINOP_RSH; michael@0: case PNK_URSH: michael@0: return BINOP_URSH; michael@0: case PNK_LT: michael@0: return BINOP_LT; michael@0: case PNK_LE: michael@0: return BINOP_LE; michael@0: case PNK_GT: michael@0: return BINOP_GT; michael@0: case PNK_GE: michael@0: return BINOP_GE; michael@0: case PNK_EQ: michael@0: return BINOP_EQ; michael@0: case PNK_NE: michael@0: return BINOP_NE; michael@0: case PNK_STRICTEQ: michael@0: return BINOP_STRICTEQ; michael@0: case PNK_STRICTNE: michael@0: return BINOP_STRICTNE; michael@0: case PNK_ADD: michael@0: return BINOP_ADD; michael@0: case PNK_SUB: michael@0: return BINOP_SUB; michael@0: case PNK_STAR: michael@0: return BINOP_STAR; michael@0: case PNK_DIV: michael@0: return BINOP_DIV; michael@0: case PNK_MOD: michael@0: return BINOP_MOD; michael@0: case PNK_BITOR: michael@0: return BINOP_BITOR; michael@0: case PNK_BITXOR: michael@0: return BINOP_BITXOR; michael@0: case PNK_BITAND: michael@0: return BINOP_BITAND; michael@0: case PNK_IN: michael@0: return BINOP_IN; michael@0: case PNK_INSTANCEOF: michael@0: return BINOP_INSTANCEOF; michael@0: default: michael@0: return BINOP_ERR; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::statements(ParseNode *pn, NodeVector &elts) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_STATEMENTLIST)); michael@0: JS_ASSERT(pn->isArity(PN_LIST)); michael@0: michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { michael@0: JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); michael@0: michael@0: RootedValue elt(cx); michael@0: if (!sourceElement(next, &elt)) michael@0: return false; michael@0: elts.infallibleAppend(elt); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::expressions(ParseNode *pn, NodeVector &elts) michael@0: { michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { michael@0: JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); michael@0: michael@0: RootedValue elt(cx); michael@0: if (!expression(next, &elt)) michael@0: return false; michael@0: elts.infallibleAppend(elt); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::blockStatement(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_STATEMENTLIST)); michael@0: michael@0: NodeVector stmts(cx); michael@0: return statements(pn, stmts) && michael@0: builder.blockStatement(stmts, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::program(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin) == lineno); michael@0: michael@0: NodeVector stmts(cx); michael@0: return statements(pn, stmts) && michael@0: builder.program(stmts, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::sourceElement(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: /* SpiderMonkey allows declarations even in pure statement contexts. */ michael@0: return statement(pn, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::declaration(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_FUNCTION) || michael@0: pn->isKind(PNK_VAR) || michael@0: pn->isKind(PNK_LET) || michael@0: pn->isKind(PNK_CONST)); michael@0: michael@0: switch (pn->getKind()) { michael@0: case PNK_FUNCTION: michael@0: return function(pn, AST_FUNC_DECL, dst); michael@0: michael@0: case PNK_VAR: michael@0: case PNK_CONST: michael@0: return variableDeclaration(pn, false, dst); michael@0: michael@0: default: michael@0: JS_ASSERT(pn->isKind(PNK_LET)); michael@0: return variableDeclaration(pn, true, dst); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(let ? pn->isKind(PNK_LET) : (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))); michael@0: michael@0: /* Later updated to VARDECL_CONST if we find a PND_CONST declarator. */ michael@0: VarDeclKind kind = let ? VARDECL_LET : VARDECL_VAR; michael@0: michael@0: NodeVector dtors(cx); michael@0: if (!dtors.reserve(pn->pn_count)) michael@0: return false; michael@0: for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { michael@0: RootedValue child(cx); michael@0: if (!variableDeclarator(next, &kind, &child)) michael@0: return false; michael@0: dtors.infallibleAppend(child); michael@0: } michael@0: return builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::variableDeclarator(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) michael@0: { michael@0: ParseNode *pnleft; michael@0: ParseNode *pnright; michael@0: michael@0: if (pn->isKind(PNK_NAME)) { michael@0: pnleft = pn; michael@0: pnright = pn->isUsed() ? nullptr : pn->pn_expr; michael@0: JS_ASSERT_IF(pnright, pn->pn_pos.encloses(pnright->pn_pos)); michael@0: } else if (pn->isKind(PNK_ASSIGN)) { michael@0: pnleft = pn->pn_left; michael@0: pnright = pn->pn_right; michael@0: JS_ASSERT(pn->pn_pos.encloses(pnleft->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pnright->pn_pos)); michael@0: } else { michael@0: /* This happens for a destructuring declarator in a for-in/of loop. */ michael@0: pnleft = pn; michael@0: pnright = nullptr; michael@0: } michael@0: michael@0: RootedValue left(cx), right(cx); michael@0: return pattern(pnleft, pkind, &left) && michael@0: optExpression(pnright, &right) && michael@0: builder.variableDeclarator(left, right, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::let(ParseNode *pn, bool expr, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: ParseNode *letHead = pn->pn_left; michael@0: LOCAL_ASSERT(letHead->isArity(PN_LIST)); michael@0: michael@0: ParseNode *letBody = pn->pn_right; michael@0: LOCAL_ASSERT(letBody->isKind(PNK_LEXICALSCOPE)); michael@0: michael@0: NodeVector dtors(cx); michael@0: if (!dtors.reserve(letHead->pn_count)) michael@0: return false; michael@0: michael@0: VarDeclKind kind = VARDECL_LET_HEAD; michael@0: michael@0: for (ParseNode *next = letHead->pn_head; next; next = next->pn_next) { michael@0: RootedValue child(cx); michael@0: /* michael@0: * Unlike in |variableDeclaration|, this does not update |kind|; since let-heads do michael@0: * not contain const declarations, declarators should never have PND_CONST set. michael@0: */ michael@0: if (!variableDeclarator(next, &kind, &child)) michael@0: return false; michael@0: dtors.infallibleAppend(child); michael@0: } michael@0: michael@0: RootedValue v(cx); michael@0: return expr michael@0: ? expression(letBody->pn_expr, &v) && michael@0: builder.letExpression(dtors, v, &pn->pn_pos, dst) michael@0: : statement(letBody->pn_expr, &v) && michael@0: builder.letStatement(dtors, v, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::importDeclaration(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_IMPORT)); michael@0: JS_ASSERT(pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST)); michael@0: JS_ASSERT(pn->pn_right->isKind(PNK_STRING)); michael@0: michael@0: NodeVector elts(cx); michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_left->pn_head; next; next = next->pn_next) { michael@0: RootedValue elt(cx); michael@0: if (!importSpecifier(next, &elt)) michael@0: return false; michael@0: elts.infallibleAppend(elt); michael@0: } michael@0: michael@0: RootedValue moduleSpec(cx); michael@0: return literal(pn->pn_right, &moduleSpec) && michael@0: builder.importDeclaration(elts, moduleSpec, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::importSpecifier(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_IMPORT_SPEC)); michael@0: michael@0: RootedValue importName(cx); michael@0: RootedValue bindingName(cx); michael@0: return identifier(pn->pn_left, &importName) && michael@0: identifier(pn->pn_right, &bindingName) && michael@0: builder.importSpecifier(importName, bindingName, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::exportDeclaration(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_FROM)); michael@0: JS_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_right->isKind(PNK_STRING)); michael@0: michael@0: RootedValue decl(cx, NullValue()); michael@0: NodeVector elts(cx); michael@0: michael@0: ParseNode *kid = pn->isKind(PNK_EXPORT) ? pn->pn_kid : pn->pn_left; michael@0: switch (ParseNodeKind kind = kid->getKind()) { michael@0: case PNK_EXPORT_SPEC_LIST: michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_left->pn_head; next; next = next->pn_next) { michael@0: RootedValue elt(cx); michael@0: if (next->isKind(PNK_EXPORT_SPEC)) { michael@0: if (!exportSpecifier(next, &elt)) michael@0: return false; michael@0: } else { michael@0: if (!builder.exportBatchSpecifier(&pn->pn_pos, &elt)) michael@0: return false; michael@0: } michael@0: elts.infallibleAppend(elt); michael@0: } michael@0: break; michael@0: michael@0: case PNK_FUNCTION: michael@0: if (!function(kid, AST_FUNC_DECL, &decl)) michael@0: return false; michael@0: break; michael@0: michael@0: case PNK_VAR: michael@0: case PNK_CONST: michael@0: case PNK_LET: michael@0: if (!variableDeclaration(kid, kind == PNK_LET, &decl)) michael@0: return false; michael@0: break; michael@0: michael@0: default: michael@0: LOCAL_NOT_REACHED("unexpected statement type"); michael@0: } michael@0: michael@0: RootedValue moduleSpec(cx, NullValue()); michael@0: if (pn->isKind(PNK_EXPORT_FROM) && !literal(pn->pn_right, &moduleSpec)) michael@0: return false; michael@0: michael@0: return builder.exportDeclaration(decl, elts, moduleSpec, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::exportSpecifier(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_EXPORT_SPEC)); michael@0: michael@0: RootedValue bindingName(cx); michael@0: RootedValue exportName(cx); michael@0: return identifier(pn->pn_left, &bindingName) && michael@0: identifier(pn->pn_right, &exportName) && michael@0: builder.exportSpecifier(bindingName, exportName, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::switchCase(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: NodeVector stmts(cx); michael@0: michael@0: RootedValue expr(cx); michael@0: michael@0: return optExpression(pn->pn_left, &expr) && michael@0: statements(pn->pn_right, stmts) && michael@0: builder.switchCase(expr, stmts, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::switchStatement(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: RootedValue disc(cx); michael@0: michael@0: if (!expression(pn->pn_left, &disc)) michael@0: return false; michael@0: michael@0: ParseNode *listNode; michael@0: bool lexical; michael@0: michael@0: if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) { michael@0: listNode = pn->pn_right->pn_expr; michael@0: lexical = true; michael@0: } else { michael@0: listNode = pn->pn_right; michael@0: lexical = false; michael@0: } michael@0: michael@0: NodeVector cases(cx); michael@0: if (!cases.reserve(listNode->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = listNode->pn_head; next; next = next->pn_next) { michael@0: RootedValue child(cx); michael@0: if (!switchCase(next, &child)) michael@0: return false; michael@0: cases.infallibleAppend(child); michael@0: } michael@0: michael@0: return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::catchClause(ParseNode *pn, bool *isGuarded, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); michael@0: JS_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); michael@0: michael@0: RootedValue var(cx), guard(cx), body(cx); michael@0: michael@0: if (!pattern(pn->pn_kid1, nullptr, &var) || michael@0: !optExpression(pn->pn_kid2, &guard)) { michael@0: return false; michael@0: } michael@0: michael@0: *isGuarded = !guard.isMagic(JS_SERIALIZE_NO_NODE); michael@0: michael@0: return statement(pn->pn_kid3, &body) && michael@0: builder.catchClause(var, guard, body, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::tryStatement(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); michael@0: JS_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); michael@0: JS_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); michael@0: michael@0: RootedValue body(cx); michael@0: if (!statement(pn->pn_kid1, &body)) michael@0: return false; michael@0: michael@0: NodeVector guarded(cx); michael@0: RootedValue unguarded(cx, NullValue()); michael@0: michael@0: if (pn->pn_kid2) { michael@0: if (!guarded.reserve(pn->pn_kid2->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_kid2->pn_head; next; next = next->pn_next) { michael@0: RootedValue clause(cx); michael@0: bool isGuarded; michael@0: if (!catchClause(next->pn_expr, &isGuarded, &clause)) michael@0: return false; michael@0: if (isGuarded) michael@0: guarded.infallibleAppend(clause); michael@0: else michael@0: unguarded = clause; michael@0: } michael@0: } michael@0: michael@0: RootedValue finally(cx); michael@0: return optStatement(pn->pn_kid3, &finally) && michael@0: builder.tryStatement(body, guarded, unguarded, finally, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::forInit(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: if (!pn) { michael@0: dst.setMagic(JS_SERIALIZE_NO_NODE); michael@0: return true; michael@0: } michael@0: michael@0: return (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) michael@0: ? variableDeclaration(pn, false, dst) michael@0: : expression(pn, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::forOf(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue expr(cx); michael@0: michael@0: return expression(head->pn_kid3, &expr) && michael@0: builder.forOfStatement(var, expr, stmt, &loop->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::forIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, michael@0: MutableHandleValue dst) michael@0: { michael@0: RootedValue expr(cx); michael@0: bool isForEach = loop->pn_iflags & JSITER_FOREACH; michael@0: michael@0: return expression(head->pn_kid3, &expr) && michael@0: builder.forInStatement(var, expr, stmt, isForEach, &loop->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_CHECK_RECURSION(cx, return false); michael@0: switch (pn->getKind()) { michael@0: case PNK_FUNCTION: michael@0: case PNK_VAR: michael@0: case PNK_CONST: michael@0: return declaration(pn, dst); michael@0: michael@0: case PNK_LET: michael@0: return pn->isArity(PN_BINARY) michael@0: ? let(pn, false, dst) michael@0: : declaration(pn, dst); michael@0: michael@0: case PNK_IMPORT: michael@0: return importDeclaration(pn, dst); michael@0: michael@0: case PNK_EXPORT: michael@0: case PNK_EXPORT_FROM: michael@0: return exportDeclaration(pn, dst); michael@0: michael@0: case PNK_NAME: michael@0: LOCAL_ASSERT(pn->isUsed()); michael@0: return statement(pn->pn_lexdef, dst); michael@0: michael@0: case PNK_SEMI: michael@0: if (pn->pn_kid) { michael@0: RootedValue expr(cx); michael@0: return expression(pn->pn_kid, &expr) && michael@0: builder.expressionStatement(expr, &pn->pn_pos, dst); michael@0: } michael@0: return builder.emptyStatement(&pn->pn_pos, dst); michael@0: michael@0: case PNK_LEXICALSCOPE: michael@0: pn = pn->pn_expr; michael@0: if (!pn->isKind(PNK_STATEMENTLIST)) michael@0: return statement(pn, dst); michael@0: /* FALL THROUGH */ michael@0: michael@0: case PNK_STATEMENTLIST: michael@0: return blockStatement(pn, dst); michael@0: michael@0: case PNK_IF: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); michael@0: JS_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); michael@0: michael@0: RootedValue test(cx), cons(cx), alt(cx); michael@0: michael@0: return expression(pn->pn_kid1, &test) && michael@0: statement(pn->pn_kid2, &cons) && michael@0: optStatement(pn->pn_kid3, &alt) && michael@0: builder.ifStatement(test, cons, alt, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_SWITCH: michael@0: return switchStatement(pn, dst); michael@0: michael@0: case PNK_TRY: michael@0: return tryStatement(pn, dst); michael@0: michael@0: case PNK_WITH: michael@0: case PNK_WHILE: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: RootedValue expr(cx), stmt(cx); michael@0: michael@0: return expression(pn->pn_left, &expr) && michael@0: statement(pn->pn_right, &stmt) && michael@0: (pn->isKind(PNK_WITH) michael@0: ? builder.withStatement(expr, stmt, &pn->pn_pos, dst) michael@0: : builder.whileStatement(expr, stmt, &pn->pn_pos, dst)); michael@0: } michael@0: michael@0: case PNK_DOWHILE: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: RootedValue stmt(cx), test(cx); michael@0: michael@0: return statement(pn->pn_left, &stmt) && michael@0: expression(pn->pn_right, &test) && michael@0: builder.doWhileStatement(stmt, test, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_FOR: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: ParseNode *head = pn->pn_left; michael@0: michael@0: JS_ASSERT_IF(head->pn_kid1, head->pn_pos.encloses(head->pn_kid1->pn_pos)); michael@0: JS_ASSERT_IF(head->pn_kid2, head->pn_pos.encloses(head->pn_kid2->pn_pos)); michael@0: JS_ASSERT_IF(head->pn_kid3, head->pn_pos.encloses(head->pn_kid3->pn_pos)); michael@0: michael@0: RootedValue stmt(cx); michael@0: if (!statement(pn->pn_right, &stmt)) michael@0: return false; michael@0: michael@0: if (head->isKind(PNK_FORIN)) { michael@0: RootedValue var(cx); michael@0: return (!head->pn_kid1 michael@0: ? pattern(head->pn_kid2, nullptr, &var) michael@0: : head->pn_kid1->isKind(PNK_LEXICALSCOPE) michael@0: ? variableDeclaration(head->pn_kid1->pn_expr, true, &var) michael@0: : variableDeclaration(head->pn_kid1, false, &var)) && michael@0: forIn(pn, head, var, stmt, dst); michael@0: } michael@0: michael@0: if (head->isKind(PNK_FOROF)) { michael@0: RootedValue var(cx); michael@0: return (!head->pn_kid1 michael@0: ? pattern(head->pn_kid2, nullptr, &var) michael@0: : head->pn_kid1->isKind(PNK_LEXICALSCOPE) michael@0: ? variableDeclaration(head->pn_kid1->pn_expr, true, &var) michael@0: : variableDeclaration(head->pn_kid1, false, &var)) && michael@0: forOf(pn, head, var, stmt, dst); michael@0: } michael@0: michael@0: RootedValue init(cx), test(cx), update(cx); michael@0: michael@0: return forInit(head->pn_kid1, &init) && michael@0: optExpression(head->pn_kid2, &test) && michael@0: optExpression(head->pn_kid3, &update) && michael@0: builder.forStatement(init, test, update, stmt, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: /* Synthesized by the parser when a for-in loop contains a variable initializer. */ michael@0: case PNK_SEQ: michael@0: { michael@0: LOCAL_ASSERT(pn->pn_count == 2); michael@0: michael@0: ParseNode *prelude = pn->pn_head; michael@0: ParseNode *loop = prelude->pn_next; michael@0: michael@0: LOCAL_ASSERT(prelude->isKind(PNK_VAR) && loop->isKind(PNK_FOR)); michael@0: michael@0: RootedValue var(cx); michael@0: if (!variableDeclaration(prelude, false, &var)) michael@0: return false; michael@0: michael@0: ParseNode *head = loop->pn_left; michael@0: JS_ASSERT(head->isKind(PNK_FORIN)); michael@0: michael@0: RootedValue stmt(cx); michael@0: michael@0: return statement(loop->pn_right, &stmt) && forIn(loop, head, var, stmt, dst); michael@0: } michael@0: michael@0: case PNK_BREAK: michael@0: case PNK_CONTINUE: michael@0: { michael@0: RootedValue label(cx); michael@0: RootedAtom pnAtom(cx, pn->pn_atom); michael@0: return optIdentifier(pnAtom, nullptr, &label) && michael@0: (pn->isKind(PNK_BREAK) michael@0: ? builder.breakStatement(label, &pn->pn_pos, dst) michael@0: : builder.continueStatement(label, &pn->pn_pos, dst)); michael@0: } michael@0: michael@0: case PNK_LABEL: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos)); michael@0: michael@0: RootedValue label(cx), stmt(cx); michael@0: RootedAtom pnAtom(cx, pn->as().label()); michael@0: return identifier(pnAtom, nullptr, &label) && michael@0: statement(pn->pn_expr, &stmt) && michael@0: builder.labeledStatement(label, stmt, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_THROW: michael@0: case PNK_RETURN: michael@0: { michael@0: JS_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos)); michael@0: michael@0: RootedValue arg(cx); michael@0: michael@0: return optExpression(pn->pn_kid, &arg) && michael@0: (pn->isKind(PNK_THROW) michael@0: ? builder.throwStatement(arg, &pn->pn_pos, dst) michael@0: : builder.returnStatement(arg, &pn->pn_pos, dst)); michael@0: } michael@0: michael@0: case PNK_DEBUGGER: michael@0: return builder.debuggerStatement(&pn->pn_pos, dst); michael@0: michael@0: case PNK_NOP: michael@0: return builder.emptyStatement(&pn->pn_pos, dst); michael@0: michael@0: default: michael@0: LOCAL_NOT_REACHED("unexpected statement type"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::leftAssociate(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_LIST)); michael@0: JS_ASSERT(pn->pn_count >= 1); michael@0: michael@0: ParseNodeKind kind = pn->getKind(); michael@0: bool lor = kind == PNK_OR; michael@0: bool logop = lor || (kind == PNK_AND); michael@0: michael@0: ParseNode *head = pn->pn_head; michael@0: RootedValue left(cx); michael@0: if (!expression(head, &left)) michael@0: return false; michael@0: for (ParseNode *next = head->pn_next; next; next = next->pn_next) { michael@0: RootedValue right(cx); michael@0: if (!expression(next, &right)) michael@0: return false; michael@0: michael@0: TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end); michael@0: michael@0: if (logop) { michael@0: if (!builder.logicalExpression(lor, left, right, &subpos, &left)) michael@0: return false; michael@0: } else { michael@0: BinaryOperator op = binop(pn->getKind(), pn->getOp()); michael@0: LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); michael@0: michael@0: if (!builder.binaryExpression(op, left, right, &subpos, &left)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: dst.set(left); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::comprehensionBlock(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: LOCAL_ASSERT(pn->isArity(PN_BINARY)); michael@0: michael@0: ParseNode *in = pn->pn_left; michael@0: michael@0: LOCAL_ASSERT(in && (in->isKind(PNK_FORIN) || in->isKind(PNK_FOROF))); michael@0: michael@0: bool isForEach = pn->pn_iflags & JSITER_FOREACH; michael@0: bool isForOf = in->isKind(PNK_FOROF); michael@0: michael@0: RootedValue patt(cx), src(cx); michael@0: return pattern(in->pn_kid2, nullptr, &patt) && michael@0: expression(in->pn_kid3, &src) && michael@0: builder.comprehensionBlock(patt, src, isForEach, isForOf, &in->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::comprehension(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: LOCAL_ASSERT(pn->isKind(PNK_FOR)); michael@0: michael@0: NodeVector blocks(cx); michael@0: michael@0: ParseNode *next = pn; michael@0: while (next->isKind(PNK_FOR)) { michael@0: RootedValue block(cx); michael@0: if (!comprehensionBlock(next, &block) || !blocks.append(block)) michael@0: return false; michael@0: next = next->pn_right; michael@0: } michael@0: michael@0: RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE)); michael@0: michael@0: if (next->isKind(PNK_IF)) { michael@0: if (!optExpression(next->pn_kid1, &filter)) michael@0: return false; michael@0: next = next->pn_kid2; michael@0: } else if (next->isKind(PNK_STATEMENTLIST) && next->pn_count == 0) { michael@0: /* FoldConstants optimized away the push. */ michael@0: NodeVector empty(cx); michael@0: return builder.arrayExpression(empty, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: LOCAL_ASSERT(next->isKind(PNK_ARRAYPUSH)); michael@0: michael@0: RootedValue body(cx); michael@0: michael@0: return expression(next->pn_kid, &body) && michael@0: builder.comprehensionExpression(body, blocks, filter, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::generatorExpression(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: LOCAL_ASSERT(pn->isKind(PNK_FOR)); michael@0: michael@0: NodeVector blocks(cx); michael@0: michael@0: ParseNode *next = pn; michael@0: while (next->isKind(PNK_FOR)) { michael@0: RootedValue block(cx); michael@0: if (!comprehensionBlock(next, &block) || !blocks.append(block)) michael@0: return false; michael@0: next = next->pn_right; michael@0: } michael@0: michael@0: RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE)); michael@0: michael@0: if (next->isKind(PNK_IF)) { michael@0: if (!optExpression(next->pn_kid1, &filter)) michael@0: return false; michael@0: next = next->pn_kid2; michael@0: } michael@0: michael@0: LOCAL_ASSERT(next->isKind(PNK_SEMI) && michael@0: next->pn_kid->isKind(PNK_YIELD) && michael@0: next->pn_kid->pn_kid); michael@0: michael@0: RootedValue body(cx); michael@0: michael@0: return expression(next->pn_kid->pn_kid, &body) && michael@0: builder.generatorExpression(body, blocks, filter, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: JS_CHECK_RECURSION(cx, return false); michael@0: switch (pn->getKind()) { michael@0: case PNK_FUNCTION: michael@0: { michael@0: ASTType type = pn->pn_funbox->function()->isArrow() ? AST_ARROW_EXPR : AST_FUNC_EXPR; michael@0: return function(pn, type, dst); michael@0: } michael@0: michael@0: case PNK_COMMA: michael@0: { michael@0: NodeVector exprs(cx); michael@0: return expressions(pn, exprs) && michael@0: builder.sequenceExpression(exprs, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_CONDITIONAL: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); michael@0: michael@0: RootedValue test(cx), cons(cx), alt(cx); michael@0: michael@0: return expression(pn->pn_kid1, &test) && michael@0: expression(pn->pn_kid2, &cons) && michael@0: expression(pn->pn_kid3, &alt) && michael@0: builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_OR: michael@0: case PNK_AND: michael@0: { michael@0: if (pn->isArity(PN_BINARY)) { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: RootedValue left(cx), right(cx); michael@0: return expression(pn->pn_left, &left) && michael@0: expression(pn->pn_right, &right) && michael@0: builder.logicalExpression(pn->isKind(PNK_OR), left, right, &pn->pn_pos, dst); michael@0: } michael@0: return leftAssociate(pn, dst); michael@0: } michael@0: michael@0: case PNK_PREINCREMENT: michael@0: case PNK_PREDECREMENT: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); michael@0: michael@0: bool inc = pn->isKind(PNK_PREINCREMENT); michael@0: RootedValue expr(cx); michael@0: return expression(pn->pn_kid, &expr) && michael@0: builder.updateExpression(expr, inc, true, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_POSTINCREMENT: michael@0: case PNK_POSTDECREMENT: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); michael@0: michael@0: bool inc = pn->isKind(PNK_POSTINCREMENT); michael@0: RootedValue expr(cx); michael@0: return expression(pn->pn_kid, &expr) && michael@0: builder.updateExpression(expr, inc, false, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_ASSIGN: michael@0: case PNK_ADDASSIGN: michael@0: case PNK_SUBASSIGN: michael@0: case PNK_BITORASSIGN: michael@0: case PNK_BITXORASSIGN: michael@0: case PNK_BITANDASSIGN: michael@0: case PNK_LSHASSIGN: michael@0: case PNK_RSHASSIGN: michael@0: case PNK_URSHASSIGN: michael@0: case PNK_MULASSIGN: michael@0: case PNK_DIVASSIGN: michael@0: case PNK_MODASSIGN: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: AssignmentOperator op = aop(pn->getOp()); michael@0: LOCAL_ASSERT(op > AOP_ERR && op < AOP_LIMIT); michael@0: michael@0: RootedValue lhs(cx), rhs(cx); michael@0: return pattern(pn->pn_left, nullptr, &lhs) && michael@0: expression(pn->pn_right, &rhs) && michael@0: builder.assignmentExpression(op, lhs, rhs, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_ADD: michael@0: case PNK_SUB: michael@0: case PNK_STRICTEQ: michael@0: case PNK_EQ: michael@0: case PNK_STRICTNE: michael@0: case PNK_NE: michael@0: case PNK_LT: michael@0: case PNK_LE: michael@0: case PNK_GT: michael@0: case PNK_GE: michael@0: case PNK_LSH: michael@0: case PNK_RSH: michael@0: case PNK_URSH: michael@0: case PNK_STAR: michael@0: case PNK_DIV: michael@0: case PNK_MOD: michael@0: case PNK_BITOR: michael@0: case PNK_BITXOR: michael@0: case PNK_BITAND: michael@0: case PNK_IN: michael@0: case PNK_INSTANCEOF: michael@0: if (pn->isArity(PN_BINARY)) { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: BinaryOperator op = binop(pn->getKind(), pn->getOp()); michael@0: LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); michael@0: michael@0: RootedValue left(cx), right(cx); michael@0: return expression(pn->pn_left, &left) && michael@0: expression(pn->pn_right, &right) && michael@0: builder.binaryExpression(op, left, right, &pn->pn_pos, dst); michael@0: } michael@0: return leftAssociate(pn, dst); michael@0: michael@0: case PNK_DELETE: michael@0: case PNK_TYPEOF: michael@0: case PNK_VOID: michael@0: case PNK_NOT: michael@0: case PNK_BITNOT: michael@0: case PNK_POS: michael@0: case PNK_NEG: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); michael@0: michael@0: UnaryOperator op = unop(pn->getKind(), pn->getOp()); michael@0: LOCAL_ASSERT(op > UNOP_ERR && op < UNOP_LIMIT); michael@0: michael@0: RootedValue expr(cx); michael@0: return expression(pn->pn_kid, &expr) && michael@0: builder.unaryExpression(op, expr, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: #if JS_HAS_GENERATOR_EXPRS michael@0: case PNK_GENEXP: michael@0: return generatorExpression(pn->generatorExpr(), dst); michael@0: #endif michael@0: michael@0: case PNK_NEW: michael@0: case PNK_CALL: michael@0: { michael@0: ParseNode *next = pn->pn_head; michael@0: JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); michael@0: michael@0: RootedValue callee(cx); michael@0: if (!expression(next, &callee)) michael@0: return false; michael@0: michael@0: NodeVector args(cx); michael@0: if (!args.reserve(pn->pn_count - 1)) michael@0: return false; michael@0: michael@0: for (next = next->pn_next; next; next = next->pn_next) { michael@0: JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); michael@0: michael@0: RootedValue arg(cx); michael@0: if (!expression(next, &arg)) michael@0: return false; michael@0: args.infallibleAppend(arg); michael@0: } michael@0: michael@0: return pn->isKind(PNK_NEW) michael@0: ? builder.newExpression(callee, args, &pn->pn_pos, dst) michael@0: michael@0: : builder.callExpression(callee, args, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_DOT: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos)); michael@0: michael@0: RootedValue expr(cx), id(cx); michael@0: RootedAtom pnAtom(cx, pn->pn_atom); michael@0: return expression(pn->pn_expr, &expr) && michael@0: identifier(pnAtom, nullptr, &id) && michael@0: builder.memberExpression(false, expr, id, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_ELEM: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); michael@0: michael@0: RootedValue left(cx), right(cx); michael@0: return expression(pn->pn_left, &left) && michael@0: expression(pn->pn_right, &right) && michael@0: builder.memberExpression(true, left, right, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_ARRAY: michael@0: { michael@0: NodeVector elts(cx); michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { michael@0: JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); michael@0: michael@0: if (next->isKind(PNK_ELISION)) { michael@0: elts.infallibleAppend(NullValue()); michael@0: } else { michael@0: RootedValue expr(cx); michael@0: if (!expression(next, &expr)) michael@0: return false; michael@0: elts.infallibleAppend(expr); michael@0: } michael@0: } michael@0: michael@0: return builder.arrayExpression(elts, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_SPREAD: michael@0: { michael@0: RootedValue expr(cx); michael@0: return expression(pn->pn_kid, &expr) && michael@0: builder.spreadExpression(expr, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_OBJECT: michael@0: { michael@0: /* The parser notes any uninitialized properties by setting the PNX_DESTRUCT flag. */ michael@0: if (pn->pn_xflags & PNX_DESTRUCT) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_OBJECT_INIT); michael@0: return false; michael@0: } michael@0: NodeVector elts(cx); michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { michael@0: JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); michael@0: michael@0: RootedValue prop(cx); michael@0: if (!property(next, &prop)) michael@0: return false; michael@0: elts.infallibleAppend(prop); michael@0: } michael@0: michael@0: return builder.objectExpression(elts, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_NAME: michael@0: return identifier(pn, dst); michael@0: michael@0: case PNK_THIS: michael@0: return builder.thisExpression(&pn->pn_pos, dst); michael@0: michael@0: case PNK_STRING: michael@0: case PNK_REGEXP: michael@0: case PNK_NUMBER: michael@0: case PNK_TRUE: michael@0: case PNK_FALSE: michael@0: case PNK_NULL: michael@0: return literal(pn, dst); michael@0: michael@0: case PNK_YIELD_STAR: michael@0: { michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); michael@0: michael@0: RootedValue arg(cx); michael@0: return expression(pn->pn_kid, &arg) && michael@0: builder.yieldExpression(arg, Delegating, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_YIELD: michael@0: { michael@0: JS_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos)); michael@0: michael@0: RootedValue arg(cx); michael@0: return optExpression(pn->pn_kid, &arg) && michael@0: builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: case PNK_ARRAYCOMP: michael@0: JS_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos)); michael@0: michael@0: /* NB: it's no longer the case that pn_count could be 2. */ michael@0: LOCAL_ASSERT(pn->pn_count == 1); michael@0: LOCAL_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE)); michael@0: michael@0: return comprehension(pn->pn_head->pn_expr, dst); michael@0: michael@0: case PNK_LET: michael@0: return let(pn, true, dst); michael@0: michael@0: default: michael@0: LOCAL_NOT_REACHED("unexpected expression type"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::propertyName(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: if (pn->isKind(PNK_NAME)) michael@0: return identifier(pn, dst); michael@0: michael@0: LOCAL_ASSERT(pn->isKind(PNK_STRING) || pn->isKind(PNK_NUMBER)); michael@0: michael@0: return literal(pn, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::property(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: PropKind kind; michael@0: switch (pn->getOp()) { michael@0: case JSOP_INITPROP: michael@0: kind = PROP_INIT; michael@0: break; michael@0: michael@0: case JSOP_INITPROP_GETTER: michael@0: kind = PROP_GETTER; michael@0: break; michael@0: michael@0: case JSOP_INITPROP_SETTER: michael@0: kind = PROP_SETTER; michael@0: break; michael@0: michael@0: default: michael@0: LOCAL_NOT_REACHED("unexpected object-literal property"); michael@0: } michael@0: michael@0: RootedValue key(cx), val(cx); michael@0: return propertyName(pn->pn_left, &key) && michael@0: expression(pn->pn_right, &val) && michael@0: builder.propertyInitializer(key, val, kind, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::literal(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: RootedValue val(cx); michael@0: switch (pn->getKind()) { michael@0: case PNK_STRING: michael@0: val.setString(pn->pn_atom); michael@0: break; michael@0: michael@0: case PNK_REGEXP: michael@0: { michael@0: RootedObject re1(cx, pn->as().objbox()->object); michael@0: LOCAL_ASSERT(re1 && re1->is()); michael@0: michael@0: RootedObject re2(cx, CloneRegExpObject(cx, re1)); michael@0: if (!re2) michael@0: return false; michael@0: michael@0: val.setObject(*re2); michael@0: break; michael@0: } michael@0: michael@0: case PNK_NUMBER: michael@0: val.setNumber(pn->pn_dval); michael@0: break; michael@0: michael@0: case PNK_NULL: michael@0: val.setNull(); michael@0: break; michael@0: michael@0: case PNK_TRUE: michael@0: val.setBoolean(true); michael@0: break; michael@0: michael@0: case PNK_FALSE: michael@0: val.setBoolean(false); michael@0: break; michael@0: michael@0: default: michael@0: LOCAL_NOT_REACHED("unexpected literal type"); michael@0: } michael@0: michael@0: return builder.literal(val, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::arrayPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_ARRAY)); michael@0: michael@0: NodeVector elts(cx); michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { michael@0: if (next->isKind(PNK_ELISION)) { michael@0: elts.infallibleAppend(NullValue()); michael@0: } else { michael@0: RootedValue patt(cx); michael@0: if (!pattern(next, pkind, &patt)) michael@0: return false; michael@0: elts.infallibleAppend(patt); michael@0: } michael@0: } michael@0: michael@0: return builder.arrayPattern(elts, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::objectPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_OBJECT)); michael@0: michael@0: NodeVector elts(cx); michael@0: if (!elts.reserve(pn->pn_count)) michael@0: return false; michael@0: michael@0: for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { michael@0: LOCAL_ASSERT(next->isOp(JSOP_INITPROP)); michael@0: michael@0: RootedValue key(cx), patt(cx), prop(cx); michael@0: if (!propertyName(next->pn_left, &key) || michael@0: !pattern(next->pn_right, pkind, &patt) || michael@0: !builder.propertyPattern(key, patt, &next->pn_pos, &prop)) { michael@0: return false; michael@0: } michael@0: michael@0: elts.infallibleAppend(prop); michael@0: } michael@0: michael@0: return builder.objectPattern(elts, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::pattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) michael@0: { michael@0: JS_CHECK_RECURSION(cx, return false); michael@0: switch (pn->getKind()) { michael@0: case PNK_OBJECT: michael@0: return objectPattern(pn, pkind, dst); michael@0: michael@0: case PNK_ARRAY: michael@0: return arrayPattern(pn, pkind, dst); michael@0: michael@0: case PNK_NAME: michael@0: if (pkind && (pn->pn_dflags & PND_CONST)) michael@0: *pkind = VARDECL_CONST; michael@0: /* FALL THROUGH */ michael@0: michael@0: default: michael@0: return expression(pn, dst); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::identifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: RootedValue atomContentsVal(cx, unrootedAtomContents(atom)); michael@0: return builder.identifier(atomContentsVal, pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::identifier(ParseNode *pn, MutableHandleValue dst) michael@0: { michael@0: LOCAL_ASSERT(pn->isArity(PN_NAME) || pn->isArity(PN_NULLARY)); michael@0: LOCAL_ASSERT(pn->pn_atom); michael@0: michael@0: RootedAtom pnAtom(cx, pn->pn_atom); michael@0: return identifier(pnAtom, &pn->pn_pos, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst) michael@0: { michael@0: RootedFunction func(cx, pn->pn_funbox->function()); michael@0: michael@0: // FIXME: Provide more information (legacy generator vs star generator). michael@0: bool isGenerator = pn->pn_funbox->isGenerator(); michael@0: michael@0: bool isExpression = michael@0: #if JS_HAS_EXPR_CLOSURES michael@0: func->isExprClosure(); michael@0: #else michael@0: false; michael@0: #endif michael@0: michael@0: RootedValue id(cx); michael@0: RootedAtom funcAtom(cx, func->atom()); michael@0: if (!optIdentifier(funcAtom, nullptr, &id)) michael@0: return false; michael@0: michael@0: NodeVector args(cx); michael@0: NodeVector defaults(cx); michael@0: michael@0: RootedValue body(cx), rest(cx); michael@0: if (func->hasRest()) michael@0: rest.setUndefined(); michael@0: else michael@0: rest.setNull(); michael@0: return functionArgsAndBody(pn->pn_body, args, defaults, &body, &rest) && michael@0: builder.function(type, &pn->pn_pos, id, args, defaults, body, michael@0: rest, isGenerator, isExpression, dst); michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, NodeVector &defaults, michael@0: MutableHandleValue body, MutableHandleValue rest) michael@0: { michael@0: ParseNode *pnargs; michael@0: ParseNode *pnbody; michael@0: michael@0: /* Extract the args and body separately. */ michael@0: if (pn->isKind(PNK_ARGSBODY)) { michael@0: pnargs = pn; michael@0: pnbody = pn->last(); michael@0: } else { michael@0: pnargs = nullptr; michael@0: pnbody = pn; michael@0: } michael@0: michael@0: ParseNode *pndestruct; michael@0: michael@0: /* Extract the destructuring assignments. */ michael@0: if (pnbody->isArity(PN_LIST) && (pnbody->pn_xflags & PNX_DESTRUCT)) { michael@0: ParseNode *head = pnbody->pn_head; michael@0: LOCAL_ASSERT(head && head->isKind(PNK_SEMI)); michael@0: michael@0: pndestruct = head->pn_kid; michael@0: LOCAL_ASSERT(pndestruct); michael@0: LOCAL_ASSERT(pndestruct->isKind(PNK_VAR)); michael@0: } else { michael@0: pndestruct = nullptr; michael@0: } michael@0: michael@0: /* Serialize the arguments and body. */ michael@0: switch (pnbody->getKind()) { michael@0: case PNK_RETURN: /* expression closure, no destructured args */ michael@0: return functionArgs(pn, pnargs, nullptr, pnbody, args, defaults, rest) && michael@0: expression(pnbody->pn_kid, body); michael@0: michael@0: case PNK_SEQ: /* expression closure with destructured args */ michael@0: { michael@0: ParseNode *pnstart = pnbody->pn_head->pn_next; michael@0: LOCAL_ASSERT(pnstart && pnstart->isKind(PNK_RETURN)); michael@0: michael@0: return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) && michael@0: expression(pnstart->pn_kid, body); michael@0: } michael@0: michael@0: case PNK_STATEMENTLIST: /* statement closure */ michael@0: { michael@0: ParseNode *pnstart = (pnbody->pn_xflags & PNX_DESTRUCT) michael@0: ? pnbody->pn_head->pn_next michael@0: : pnbody->pn_head; michael@0: michael@0: return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) && michael@0: functionBody(pnstart, &pnbody->pn_pos, body); michael@0: } michael@0: michael@0: default: michael@0: LOCAL_NOT_REACHED("unexpected function contents"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestruct, michael@0: ParseNode *pnbody, NodeVector &args, NodeVector &defaults, michael@0: MutableHandleValue rest) michael@0: { michael@0: uint32_t i = 0; michael@0: ParseNode *arg = pnargs ? pnargs->pn_head : nullptr; michael@0: ParseNode *destruct = pndestruct ? pndestruct->pn_head : nullptr; michael@0: RootedValue node(cx); michael@0: michael@0: /* michael@0: * Arguments are found in potentially two different places: 1) the michael@0: * argsbody sequence (which ends with the body node), or 2) a michael@0: * destructuring initialization at the beginning of the body. Loop michael@0: * |arg| through the argsbody and |destruct| through the initial michael@0: * destructuring assignments, stopping only when we've exhausted michael@0: * both. michael@0: */ michael@0: while ((arg && arg != pnbody) || destruct) { michael@0: if (destruct && destruct->pn_right->frameSlot() == i) { michael@0: if (!pattern(destruct->pn_left, nullptr, &node) || !args.append(node)) michael@0: return false; michael@0: destruct = destruct->pn_next; michael@0: } else if (arg && arg != pnbody) { michael@0: /* michael@0: * We don't check that arg->frameSlot() == i since we michael@0: * can't call that method if the arg def has been turned michael@0: * into a use, e.g.: michael@0: * michael@0: * function(a) { function a() { } } michael@0: * michael@0: * There's no other way to ask a non-destructuring arg its michael@0: * index in the formals list, so we rely on the ability to michael@0: * ask destructuring args their index above. michael@0: */ michael@0: JS_ASSERT(arg->isKind(PNK_NAME) || arg->isKind(PNK_ASSIGN)); michael@0: ParseNode *argName = arg->isKind(PNK_NAME) ? arg : arg->pn_left; michael@0: if (!identifier(argName, &node)) michael@0: return false; michael@0: if (rest.isUndefined() && arg->pn_next == pnbody) michael@0: rest.setObject(node.toObject()); michael@0: else if (!args.append(node)) michael@0: return false; michael@0: if (arg->pn_dflags & PND_DEFAULT) { michael@0: ParseNode *expr = arg->isDefn() ? arg->expr() : arg->pn_kid->pn_right; michael@0: RootedValue def(cx); michael@0: if (!expression(expr, &def) || !defaults.append(def)) michael@0: return false; michael@0: } michael@0: arg = arg->pn_next; michael@0: } else { michael@0: LOCAL_NOT_REACHED("missing function argument"); michael@0: } michael@0: ++i; michael@0: } michael@0: JS_ASSERT(!rest.isUndefined()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ASTSerializer::functionBody(ParseNode *pn, TokenPos *pos, MutableHandleValue dst) michael@0: { michael@0: NodeVector elts(cx); michael@0: michael@0: /* We aren't sure how many elements there are up front, so we'll check each append. */ michael@0: for (ParseNode *next = pn; next; next = next->pn_next) { michael@0: RootedValue child(cx); michael@0: if (!sourceElement(next, &child) || !elts.append(child)) michael@0: return false; michael@0: } michael@0: michael@0: return builder.blockStatement(elts, pos, dst); michael@0: } michael@0: michael@0: static bool michael@0: reflect_parse(JSContext *cx, uint32_t argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() < 1) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, michael@0: "Reflect.parse", "0", "s"); michael@0: return false; michael@0: } michael@0: michael@0: RootedString src(cx, ToString(cx, args[0])); michael@0: if (!src) michael@0: return false; michael@0: michael@0: ScopedJSFreePtr filename; michael@0: uint32_t lineno = 1; michael@0: bool loc = true; michael@0: michael@0: RootedObject builder(cx); michael@0: michael@0: RootedValue arg(cx, args.get(1)); michael@0: michael@0: if (!arg.isNullOrUndefined()) { michael@0: if (!arg.isObject()) { michael@0: js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, michael@0: JSDVG_SEARCH_STACK, arg, js::NullPtr(), michael@0: "not an object", nullptr); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject config(cx, &arg.toObject()); michael@0: michael@0: RootedValue prop(cx); michael@0: michael@0: /* config.loc */ michael@0: RootedId locId(cx, NameToId(cx->names().loc)); michael@0: RootedValue trueVal(cx, BooleanValue(true)); michael@0: if (!GetPropertyDefault(cx, config, locId, trueVal, &prop)) michael@0: return false; michael@0: michael@0: loc = ToBoolean(prop); michael@0: michael@0: if (loc) { michael@0: /* config.source */ michael@0: RootedId sourceId(cx, NameToId(cx->names().source)); michael@0: RootedValue nullVal(cx, NullValue()); michael@0: if (!GetPropertyDefault(cx, config, sourceId, nullVal, &prop)) michael@0: return false; michael@0: michael@0: if (!prop.isNullOrUndefined()) { michael@0: RootedString str(cx, ToString(cx, prop)); michael@0: if (!str) michael@0: return false; michael@0: michael@0: size_t length = str->length(); michael@0: const jschar *chars = str->getChars(cx); michael@0: if (!chars) michael@0: return false; michael@0: michael@0: TwoByteChars tbchars(chars, length); michael@0: filename = LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str(); michael@0: if (!filename) michael@0: return false; michael@0: } michael@0: michael@0: /* config.line */ michael@0: RootedId lineId(cx, NameToId(cx->names().line)); michael@0: RootedValue oneValue(cx, Int32Value(1)); michael@0: if (!GetPropertyDefault(cx, config, lineId, oneValue, &prop) || michael@0: !ToUint32(cx, prop, &lineno)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /* config.builder */ michael@0: RootedId builderId(cx, NameToId(cx->names().builder)); michael@0: RootedValue nullVal(cx, NullValue()); michael@0: if (!GetPropertyDefault(cx, config, builderId, nullVal, &prop)) michael@0: return false; michael@0: michael@0: if (!prop.isNullOrUndefined()) { michael@0: if (!prop.isObject()) { michael@0: js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, michael@0: JSDVG_SEARCH_STACK, prop, js::NullPtr(), michael@0: "not an object", nullptr); michael@0: return false; michael@0: } michael@0: builder = &prop.toObject(); michael@0: } michael@0: } michael@0: michael@0: /* Extract the builder methods first to report errors before parsing. */ michael@0: ASTSerializer serialize(cx, loc, filename, lineno); michael@0: if (!serialize.init(builder)) michael@0: return false; michael@0: michael@0: JSFlatString *flat = src->ensureFlat(cx); michael@0: if (!flat) michael@0: return false; michael@0: michael@0: CompileOptions options(cx); michael@0: options.setFileAndLine(filename, lineno); michael@0: options.setCanLazilyParse(false); michael@0: Parser parser(cx, &cx->tempLifoAlloc(), options, flat->chars(), michael@0: flat->length(), /* foldConstants = */ false, nullptr, nullptr); michael@0: michael@0: serialize.setParser(&parser); michael@0: michael@0: ParseNode *pn = parser.parse(nullptr); michael@0: if (!pn) michael@0: return false; michael@0: michael@0: RootedValue val(cx); michael@0: if (!serialize.program(pn, &val)) { michael@0: args.rval().setNull(); michael@0: return false; michael@0: } michael@0: michael@0: args.rval().set(val); michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSObject *) michael@0: JS_InitReflect(JSContext *cx, HandleObject obj) michael@0: { michael@0: static const JSFunctionSpec static_methods[] = { michael@0: JS_FN("parse", reflect_parse, 1, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: RootedObject proto(cx, obj->as().getOrCreateObjectPrototype(cx)); michael@0: if (!proto) michael@0: return nullptr; michael@0: RootedObject Reflect(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, michael@0: obj, SingletonObject)); michael@0: if (!Reflect) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineProperty(cx, obj, "Reflect", Reflect, 0, michael@0: JS_PropertyStub, JS_StrictPropertyStub)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!JS_DefineFunctions(cx, Reflect, static_methods)) michael@0: return nullptr; michael@0: michael@0: return Reflect; michael@0: }