Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* JS reflection package. */ |
michael@0 | 8 | |
michael@0 | 9 | #include "jsreflect.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "mozilla/ArrayUtils.h" |
michael@0 | 12 | #include "mozilla/DebugOnly.h" |
michael@0 | 13 | |
michael@0 | 14 | #include <stdlib.h> |
michael@0 | 15 | |
michael@0 | 16 | #include "jsarray.h" |
michael@0 | 17 | #include "jsatom.h" |
michael@0 | 18 | #include "jsobj.h" |
michael@0 | 19 | #include "jspubtd.h" |
michael@0 | 20 | |
michael@0 | 21 | #include "frontend/Parser.h" |
michael@0 | 22 | #include "frontend/TokenStream.h" |
michael@0 | 23 | #include "js/CharacterEncoding.h" |
michael@0 | 24 | #include "vm/RegExpObject.h" |
michael@0 | 25 | |
michael@0 | 26 | #include "jsobjinlines.h" |
michael@0 | 27 | |
michael@0 | 28 | #include "frontend/ParseNode-inl.h" |
michael@0 | 29 | |
michael@0 | 30 | using namespace js; |
michael@0 | 31 | using namespace js::frontend; |
michael@0 | 32 | |
michael@0 | 33 | using JS::AutoValueArray; |
michael@0 | 34 | using mozilla::ArrayLength; |
michael@0 | 35 | using mozilla::DebugOnly; |
michael@0 | 36 | |
michael@0 | 37 | char const * const js::aopNames[] = { |
michael@0 | 38 | "=", /* AOP_ASSIGN */ |
michael@0 | 39 | "+=", /* AOP_PLUS */ |
michael@0 | 40 | "-=", /* AOP_MINUS */ |
michael@0 | 41 | "*=", /* AOP_STAR */ |
michael@0 | 42 | "/=", /* AOP_DIV */ |
michael@0 | 43 | "%=", /* AOP_MOD */ |
michael@0 | 44 | "<<=", /* AOP_LSH */ |
michael@0 | 45 | ">>=", /* AOP_RSH */ |
michael@0 | 46 | ">>>=", /* AOP_URSH */ |
michael@0 | 47 | "|=", /* AOP_BITOR */ |
michael@0 | 48 | "^=", /* AOP_BITXOR */ |
michael@0 | 49 | "&=" /* AOP_BITAND */ |
michael@0 | 50 | }; |
michael@0 | 51 | |
michael@0 | 52 | char const * const js::binopNames[] = { |
michael@0 | 53 | "==", /* BINOP_EQ */ |
michael@0 | 54 | "!=", /* BINOP_NE */ |
michael@0 | 55 | "===", /* BINOP_STRICTEQ */ |
michael@0 | 56 | "!==", /* BINOP_STRICTNE */ |
michael@0 | 57 | "<", /* BINOP_LT */ |
michael@0 | 58 | "<=", /* BINOP_LE */ |
michael@0 | 59 | ">", /* BINOP_GT */ |
michael@0 | 60 | ">=", /* BINOP_GE */ |
michael@0 | 61 | "<<", /* BINOP_LSH */ |
michael@0 | 62 | ">>", /* BINOP_RSH */ |
michael@0 | 63 | ">>>", /* BINOP_URSH */ |
michael@0 | 64 | "+", /* BINOP_PLUS */ |
michael@0 | 65 | "-", /* BINOP_MINUS */ |
michael@0 | 66 | "*", /* BINOP_STAR */ |
michael@0 | 67 | "/", /* BINOP_DIV */ |
michael@0 | 68 | "%", /* BINOP_MOD */ |
michael@0 | 69 | "|", /* BINOP_BITOR */ |
michael@0 | 70 | "^", /* BINOP_BITXOR */ |
michael@0 | 71 | "&", /* BINOP_BITAND */ |
michael@0 | 72 | "in", /* BINOP_IN */ |
michael@0 | 73 | "instanceof", /* BINOP_INSTANCEOF */ |
michael@0 | 74 | }; |
michael@0 | 75 | |
michael@0 | 76 | char const * const js::unopNames[] = { |
michael@0 | 77 | "delete", /* UNOP_DELETE */ |
michael@0 | 78 | "-", /* UNOP_NEG */ |
michael@0 | 79 | "+", /* UNOP_POS */ |
michael@0 | 80 | "!", /* UNOP_NOT */ |
michael@0 | 81 | "~", /* UNOP_BITNOT */ |
michael@0 | 82 | "typeof", /* UNOP_TYPEOF */ |
michael@0 | 83 | "void" /* UNOP_VOID */ |
michael@0 | 84 | }; |
michael@0 | 85 | |
michael@0 | 86 | char const * const js::nodeTypeNames[] = { |
michael@0 | 87 | #define ASTDEF(ast, str, method) str, |
michael@0 | 88 | #include "jsast.tbl" |
michael@0 | 89 | #undef ASTDEF |
michael@0 | 90 | nullptr |
michael@0 | 91 | }; |
michael@0 | 92 | |
michael@0 | 93 | static char const * const callbackNames[] = { |
michael@0 | 94 | #define ASTDEF(ast, str, method) method, |
michael@0 | 95 | #include "jsast.tbl" |
michael@0 | 96 | #undef ASTDEF |
michael@0 | 97 | nullptr |
michael@0 | 98 | }; |
michael@0 | 99 | |
michael@0 | 100 | enum YieldKind { Delegating, NotDelegating }; |
michael@0 | 101 | |
michael@0 | 102 | typedef AutoValueVector NodeVector; |
michael@0 | 103 | |
michael@0 | 104 | /* |
michael@0 | 105 | * ParseNode is a somewhat intricate data structure, and its invariants have |
michael@0 | 106 | * evolved, making it more likely that there could be a disconnect between the |
michael@0 | 107 | * parser and the AST serializer. We use these macros to check invariants on a |
michael@0 | 108 | * parse node and raise a dynamic error on failure. |
michael@0 | 109 | */ |
michael@0 | 110 | #define LOCAL_ASSERT(expr) \ |
michael@0 | 111 | JS_BEGIN_MACRO \ |
michael@0 | 112 | JS_ASSERT(expr); \ |
michael@0 | 113 | if (!(expr)) { \ |
michael@0 | 114 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PARSE_NODE); \ |
michael@0 | 115 | return false; \ |
michael@0 | 116 | } \ |
michael@0 | 117 | JS_END_MACRO |
michael@0 | 118 | |
michael@0 | 119 | #define LOCAL_NOT_REACHED(expr) \ |
michael@0 | 120 | JS_BEGIN_MACRO \ |
michael@0 | 121 | MOZ_ASSERT(false); \ |
michael@0 | 122 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PARSE_NODE); \ |
michael@0 | 123 | return false; \ |
michael@0 | 124 | JS_END_MACRO |
michael@0 | 125 | |
michael@0 | 126 | namespace { |
michael@0 | 127 | |
michael@0 | 128 | /* Set 'result' to obj[id] if any such property exists, else defaultValue. */ |
michael@0 | 129 | static bool |
michael@0 | 130 | GetPropertyDefault(JSContext *cx, HandleObject obj, HandleId id, HandleValue defaultValue, |
michael@0 | 131 | MutableHandleValue result) |
michael@0 | 132 | { |
michael@0 | 133 | bool found; |
michael@0 | 134 | if (!JSObject::hasProperty(cx, obj, id, &found)) |
michael@0 | 135 | return false; |
michael@0 | 136 | if (!found) { |
michael@0 | 137 | result.set(defaultValue); |
michael@0 | 138 | return true; |
michael@0 | 139 | } |
michael@0 | 140 | return JSObject::getGeneric(cx, obj, obj, id, result); |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | /* |
michael@0 | 144 | * Builder class that constructs JavaScript AST node objects. See: |
michael@0 | 145 | * |
michael@0 | 146 | * https://developer.mozilla.org/en/SpiderMonkey/Parser_API |
michael@0 | 147 | * |
michael@0 | 148 | * Bug 569487: generalize builder interface |
michael@0 | 149 | */ |
michael@0 | 150 | class NodeBuilder |
michael@0 | 151 | { |
michael@0 | 152 | typedef AutoValueArray<AST_LIMIT> CallbackArray; |
michael@0 | 153 | |
michael@0 | 154 | JSContext *cx; |
michael@0 | 155 | TokenStream *tokenStream; |
michael@0 | 156 | bool saveLoc; /* save source location information? */ |
michael@0 | 157 | char const *src; /* source filename or null */ |
michael@0 | 158 | RootedValue srcval; /* source filename JS value or null */ |
michael@0 | 159 | CallbackArray callbacks; /* user-specified callbacks */ |
michael@0 | 160 | RootedValue userv; /* user-specified builder object or null */ |
michael@0 | 161 | |
michael@0 | 162 | public: |
michael@0 | 163 | NodeBuilder(JSContext *c, bool l, char const *s) |
michael@0 | 164 | : cx(c), tokenStream(nullptr), saveLoc(l), src(s), srcval(c), callbacks(cx), |
michael@0 | 165 | userv(c) |
michael@0 | 166 | {} |
michael@0 | 167 | |
michael@0 | 168 | bool init(HandleObject userobj = js::NullPtr()) { |
michael@0 | 169 | if (src) { |
michael@0 | 170 | if (!atomValue(src, &srcval)) |
michael@0 | 171 | return false; |
michael@0 | 172 | } else { |
michael@0 | 173 | srcval.setNull(); |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | if (!userobj) { |
michael@0 | 177 | userv.setNull(); |
michael@0 | 178 | for (unsigned i = 0; i < AST_LIMIT; i++) { |
michael@0 | 179 | callbacks[i].setNull(); |
michael@0 | 180 | } |
michael@0 | 181 | return true; |
michael@0 | 182 | } |
michael@0 | 183 | |
michael@0 | 184 | userv.setObject(*userobj); |
michael@0 | 185 | |
michael@0 | 186 | RootedValue nullVal(cx, NullValue()); |
michael@0 | 187 | RootedValue funv(cx); |
michael@0 | 188 | for (unsigned i = 0; i < AST_LIMIT; i++) { |
michael@0 | 189 | const char *name = callbackNames[i]; |
michael@0 | 190 | RootedAtom atom(cx, Atomize(cx, name, strlen(name))); |
michael@0 | 191 | if (!atom) |
michael@0 | 192 | return false; |
michael@0 | 193 | RootedId id(cx, AtomToId(atom)); |
michael@0 | 194 | if (!GetPropertyDefault(cx, userobj, id, nullVal, &funv)) |
michael@0 | 195 | return false; |
michael@0 | 196 | |
michael@0 | 197 | if (funv.isNullOrUndefined()) { |
michael@0 | 198 | callbacks[i].setNull(); |
michael@0 | 199 | continue; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | if (!funv.isObject() || !funv.toObject().is<JSFunction>()) { |
michael@0 | 203 | js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_FUNCTION, |
michael@0 | 204 | JSDVG_SEARCH_STACK, funv, js::NullPtr(), nullptr, nullptr); |
michael@0 | 205 | return false; |
michael@0 | 206 | } |
michael@0 | 207 | |
michael@0 | 208 | callbacks[i].set(funv); |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | return true; |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | void setTokenStream(TokenStream *ts) { |
michael@0 | 215 | tokenStream = ts; |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | private: |
michael@0 | 219 | bool callback(HandleValue fun, TokenPos *pos, MutableHandleValue dst) { |
michael@0 | 220 | if (saveLoc) { |
michael@0 | 221 | RootedValue loc(cx); |
michael@0 | 222 | if (!newNodeLoc(pos, &loc)) |
michael@0 | 223 | return false; |
michael@0 | 224 | AutoValueArray<1> argv(cx); |
michael@0 | 225 | argv[0].set(loc); |
michael@0 | 226 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | AutoValueArray<1> argv(cx); |
michael@0 | 230 | argv[0].setNull(); /* no zero-length arrays allowed! */ |
michael@0 | 231 | return Invoke(cx, userv, fun, 0, argv.begin(), dst); |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | bool callback(HandleValue fun, HandleValue v1, TokenPos *pos, MutableHandleValue dst) { |
michael@0 | 235 | if (saveLoc) { |
michael@0 | 236 | RootedValue loc(cx); |
michael@0 | 237 | if (!newNodeLoc(pos, &loc)) |
michael@0 | 238 | return false; |
michael@0 | 239 | AutoValueArray<2> argv(cx); |
michael@0 | 240 | argv[0].set(v1); |
michael@0 | 241 | argv[1].set(loc); |
michael@0 | 242 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | AutoValueArray<1> argv(cx); |
michael@0 | 246 | argv[0].set(v1); |
michael@0 | 247 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | bool callback(HandleValue fun, HandleValue v1, HandleValue v2, TokenPos *pos, |
michael@0 | 251 | MutableHandleValue dst) { |
michael@0 | 252 | if (saveLoc) { |
michael@0 | 253 | RootedValue loc(cx); |
michael@0 | 254 | if (!newNodeLoc(pos, &loc)) |
michael@0 | 255 | return false; |
michael@0 | 256 | AutoValueArray<3> argv(cx); |
michael@0 | 257 | argv[0].set(v1); |
michael@0 | 258 | argv[1].set(v2); |
michael@0 | 259 | argv[2].set(loc); |
michael@0 | 260 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | AutoValueArray<2> argv(cx); |
michael@0 | 264 | argv[0].set(v1); |
michael@0 | 265 | argv[1].set(v2); |
michael@0 | 266 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, TokenPos *pos, |
michael@0 | 270 | MutableHandleValue dst) { |
michael@0 | 271 | if (saveLoc) { |
michael@0 | 272 | RootedValue loc(cx); |
michael@0 | 273 | if (!newNodeLoc(pos, &loc)) |
michael@0 | 274 | return false; |
michael@0 | 275 | AutoValueArray<4> argv(cx); |
michael@0 | 276 | argv[0].set(v1); |
michael@0 | 277 | argv[1].set(v2); |
michael@0 | 278 | argv[2].set(v3); |
michael@0 | 279 | argv[3].set(loc); |
michael@0 | 280 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | AutoValueArray<3> argv(cx); |
michael@0 | 284 | argv[0].set(v1); |
michael@0 | 285 | argv[1].set(v2); |
michael@0 | 286 | argv[2].set(v3); |
michael@0 | 287 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, HandleValue v4, |
michael@0 | 291 | TokenPos *pos, MutableHandleValue dst) { |
michael@0 | 292 | if (saveLoc) { |
michael@0 | 293 | RootedValue loc(cx); |
michael@0 | 294 | if (!newNodeLoc(pos, &loc)) |
michael@0 | 295 | return false; |
michael@0 | 296 | AutoValueArray<5> argv(cx); |
michael@0 | 297 | argv[0].set(v1); |
michael@0 | 298 | argv[1].set(v2); |
michael@0 | 299 | argv[2].set(v3); |
michael@0 | 300 | argv[3].set(v4); |
michael@0 | 301 | argv[4].set(loc); |
michael@0 | 302 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 303 | } |
michael@0 | 304 | |
michael@0 | 305 | AutoValueArray<4> argv(cx); |
michael@0 | 306 | argv[0].set(v1); |
michael@0 | 307 | argv[1].set(v2); |
michael@0 | 308 | argv[2].set(v3); |
michael@0 | 309 | argv[3].set(v4); |
michael@0 | 310 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, HandleValue v4, |
michael@0 | 314 | HandleValue v5, TokenPos *pos, MutableHandleValue dst) { |
michael@0 | 315 | if (saveLoc) { |
michael@0 | 316 | RootedValue loc(cx); |
michael@0 | 317 | if (!newNodeLoc(pos, &loc)) |
michael@0 | 318 | return false; |
michael@0 | 319 | AutoValueArray<6> argv(cx); |
michael@0 | 320 | argv[0].set(v1); |
michael@0 | 321 | argv[1].set(v2); |
michael@0 | 322 | argv[2].set(v3); |
michael@0 | 323 | argv[3].set(v4); |
michael@0 | 324 | argv[4].set(v5); |
michael@0 | 325 | argv[5].set(loc); |
michael@0 | 326 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | AutoValueArray<5> argv(cx); |
michael@0 | 330 | argv[0].set(v1); |
michael@0 | 331 | argv[1].set(v2); |
michael@0 | 332 | argv[2].set(v3); |
michael@0 | 333 | argv[3].set(v4); |
michael@0 | 334 | argv[4].set(v5); |
michael@0 | 335 | return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst); |
michael@0 | 336 | } |
michael@0 | 337 | |
michael@0 | 338 | // WARNING: Returning a Handle is non-standard, but it works in this case |
michael@0 | 339 | // because both |v| and |UndefinedHandleValue| are definitely rooted on a |
michael@0 | 340 | // previous stack frame (i.e. we're just choosing between two |
michael@0 | 341 | // already-rooted values). |
michael@0 | 342 | HandleValue opt(HandleValue v) { |
michael@0 | 343 | JS_ASSERT_IF(v.isMagic(), v.whyMagic() == JS_SERIALIZE_NO_NODE); |
michael@0 | 344 | return v.isMagic(JS_SERIALIZE_NO_NODE) ? JS::UndefinedHandleValue : v; |
michael@0 | 345 | } |
michael@0 | 346 | |
michael@0 | 347 | bool atomValue(const char *s, MutableHandleValue dst) { |
michael@0 | 348 | /* |
michael@0 | 349 | * Bug 575416: instead of Atomize, lookup constant atoms in tbl file |
michael@0 | 350 | */ |
michael@0 | 351 | RootedAtom atom(cx, Atomize(cx, s, strlen(s))); |
michael@0 | 352 | if (!atom) |
michael@0 | 353 | return false; |
michael@0 | 354 | |
michael@0 | 355 | dst.setString(atom); |
michael@0 | 356 | return true; |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | bool newObject(MutableHandleObject dst) { |
michael@0 | 360 | RootedObject nobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); |
michael@0 | 361 | if (!nobj) |
michael@0 | 362 | return false; |
michael@0 | 363 | |
michael@0 | 364 | dst.set(nobj); |
michael@0 | 365 | return true; |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | bool newArray(NodeVector &elts, MutableHandleValue dst); |
michael@0 | 369 | |
michael@0 | 370 | bool newNode(ASTType type, TokenPos *pos, MutableHandleObject dst); |
michael@0 | 371 | |
michael@0 | 372 | bool newNode(ASTType type, TokenPos *pos, MutableHandleValue dst) { |
michael@0 | 373 | RootedObject node(cx); |
michael@0 | 374 | return newNode(type, pos, &node) && |
michael@0 | 375 | setResult(node, dst); |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | bool newNode(ASTType type, TokenPos *pos, |
michael@0 | 379 | const char *childName, HandleValue child, |
michael@0 | 380 | MutableHandleValue dst) { |
michael@0 | 381 | RootedObject node(cx); |
michael@0 | 382 | return newNode(type, pos, &node) && |
michael@0 | 383 | setProperty(node, childName, child) && |
michael@0 | 384 | setResult(node, dst); |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | bool newNode(ASTType type, TokenPos *pos, |
michael@0 | 388 | const char *childName1, HandleValue child1, |
michael@0 | 389 | const char *childName2, HandleValue child2, |
michael@0 | 390 | MutableHandleValue dst) { |
michael@0 | 391 | RootedObject node(cx); |
michael@0 | 392 | return newNode(type, pos, &node) && |
michael@0 | 393 | setProperty(node, childName1, child1) && |
michael@0 | 394 | setProperty(node, childName2, child2) && |
michael@0 | 395 | setResult(node, dst); |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | bool newNode(ASTType type, TokenPos *pos, |
michael@0 | 399 | const char *childName1, HandleValue child1, |
michael@0 | 400 | const char *childName2, HandleValue child2, |
michael@0 | 401 | const char *childName3, HandleValue child3, |
michael@0 | 402 | MutableHandleValue dst) { |
michael@0 | 403 | RootedObject node(cx); |
michael@0 | 404 | return newNode(type, pos, &node) && |
michael@0 | 405 | setProperty(node, childName1, child1) && |
michael@0 | 406 | setProperty(node, childName2, child2) && |
michael@0 | 407 | setProperty(node, childName3, child3) && |
michael@0 | 408 | setResult(node, dst); |
michael@0 | 409 | } |
michael@0 | 410 | |
michael@0 | 411 | bool newNode(ASTType type, TokenPos *pos, |
michael@0 | 412 | const char *childName1, HandleValue child1, |
michael@0 | 413 | const char *childName2, HandleValue child2, |
michael@0 | 414 | const char *childName3, HandleValue child3, |
michael@0 | 415 | const char *childName4, HandleValue child4, |
michael@0 | 416 | MutableHandleValue dst) { |
michael@0 | 417 | RootedObject node(cx); |
michael@0 | 418 | return newNode(type, pos, &node) && |
michael@0 | 419 | setProperty(node, childName1, child1) && |
michael@0 | 420 | setProperty(node, childName2, child2) && |
michael@0 | 421 | setProperty(node, childName3, child3) && |
michael@0 | 422 | setProperty(node, childName4, child4) && |
michael@0 | 423 | setResult(node, dst); |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | bool newNode(ASTType type, TokenPos *pos, |
michael@0 | 427 | const char *childName1, HandleValue child1, |
michael@0 | 428 | const char *childName2, HandleValue child2, |
michael@0 | 429 | const char *childName3, HandleValue child3, |
michael@0 | 430 | const char *childName4, HandleValue child4, |
michael@0 | 431 | const char *childName5, HandleValue child5, |
michael@0 | 432 | MutableHandleValue dst) { |
michael@0 | 433 | RootedObject node(cx); |
michael@0 | 434 | return newNode(type, pos, &node) && |
michael@0 | 435 | setProperty(node, childName1, child1) && |
michael@0 | 436 | setProperty(node, childName2, child2) && |
michael@0 | 437 | setProperty(node, childName3, child3) && |
michael@0 | 438 | setProperty(node, childName4, child4) && |
michael@0 | 439 | setProperty(node, childName5, child5) && |
michael@0 | 440 | setResult(node, dst); |
michael@0 | 441 | } |
michael@0 | 442 | |
michael@0 | 443 | bool newNode(ASTType type, TokenPos *pos, |
michael@0 | 444 | const char *childName1, HandleValue child1, |
michael@0 | 445 | const char *childName2, HandleValue child2, |
michael@0 | 446 | const char *childName3, HandleValue child3, |
michael@0 | 447 | const char *childName4, HandleValue child4, |
michael@0 | 448 | const char *childName5, HandleValue child5, |
michael@0 | 449 | const char *childName6, HandleValue child6, |
michael@0 | 450 | const char *childName7, HandleValue child7, |
michael@0 | 451 | MutableHandleValue dst) { |
michael@0 | 452 | RootedObject node(cx); |
michael@0 | 453 | return newNode(type, pos, &node) && |
michael@0 | 454 | setProperty(node, childName1, child1) && |
michael@0 | 455 | setProperty(node, childName2, child2) && |
michael@0 | 456 | setProperty(node, childName3, child3) && |
michael@0 | 457 | setProperty(node, childName4, child4) && |
michael@0 | 458 | setProperty(node, childName5, child5) && |
michael@0 | 459 | setProperty(node, childName6, child6) && |
michael@0 | 460 | setProperty(node, childName7, child7) && |
michael@0 | 461 | setResult(node, dst); |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | bool listNode(ASTType type, const char *propName, NodeVector &elts, TokenPos *pos, |
michael@0 | 465 | MutableHandleValue dst) { |
michael@0 | 466 | RootedValue array(cx); |
michael@0 | 467 | if (!newArray(elts, &array)) |
michael@0 | 468 | return false; |
michael@0 | 469 | |
michael@0 | 470 | RootedValue cb(cx, callbacks[type]); |
michael@0 | 471 | if (!cb.isNull()) |
michael@0 | 472 | return callback(cb, array, pos, dst); |
michael@0 | 473 | |
michael@0 | 474 | return newNode(type, pos, propName, array, dst); |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | bool setProperty(HandleObject obj, const char *name, HandleValue val) { |
michael@0 | 478 | JS_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE); |
michael@0 | 479 | |
michael@0 | 480 | /* |
michael@0 | 481 | * Bug 575416: instead of Atomize, lookup constant atoms in tbl file |
michael@0 | 482 | */ |
michael@0 | 483 | RootedAtom atom(cx, Atomize(cx, name, strlen(name))); |
michael@0 | 484 | if (!atom) |
michael@0 | 485 | return false; |
michael@0 | 486 | |
michael@0 | 487 | /* Represent "no node" as null and ensure users are not exposed to magic values. */ |
michael@0 | 488 | RootedValue optVal(cx, val.isMagic(JS_SERIALIZE_NO_NODE) ? NullValue() : val); |
michael@0 | 489 | return JSObject::defineProperty(cx, obj, atom->asPropertyName(), optVal); |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | bool newNodeLoc(TokenPos *pos, MutableHandleValue dst); |
michael@0 | 493 | |
michael@0 | 494 | bool setNodeLoc(HandleObject node, TokenPos *pos); |
michael@0 | 495 | |
michael@0 | 496 | bool setResult(HandleObject obj, MutableHandleValue dst) { |
michael@0 | 497 | JS_ASSERT(obj); |
michael@0 | 498 | dst.setObject(*obj); |
michael@0 | 499 | return true; |
michael@0 | 500 | } |
michael@0 | 501 | |
michael@0 | 502 | public: |
michael@0 | 503 | /* |
michael@0 | 504 | * All of the public builder methods take as their last two |
michael@0 | 505 | * arguments a nullable token position and a non-nullable, rooted |
michael@0 | 506 | * outparam. |
michael@0 | 507 | * |
michael@0 | 508 | * Any Value arguments representing optional subnodes may be a |
michael@0 | 509 | * JS_SERIALIZE_NO_NODE magic value. |
michael@0 | 510 | */ |
michael@0 | 511 | |
michael@0 | 512 | /* |
michael@0 | 513 | * misc nodes |
michael@0 | 514 | */ |
michael@0 | 515 | |
michael@0 | 516 | bool program(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 517 | |
michael@0 | 518 | bool literal(HandleValue val, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 519 | |
michael@0 | 520 | bool identifier(HandleValue name, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 521 | |
michael@0 | 522 | bool function(ASTType type, TokenPos *pos, |
michael@0 | 523 | HandleValue id, NodeVector &args, NodeVector &defaults, |
michael@0 | 524 | HandleValue body, HandleValue rest, bool isGenerator, bool isExpression, |
michael@0 | 525 | MutableHandleValue dst); |
michael@0 | 526 | |
michael@0 | 527 | bool variableDeclarator(HandleValue id, HandleValue init, TokenPos *pos, |
michael@0 | 528 | MutableHandleValue dst); |
michael@0 | 529 | |
michael@0 | 530 | bool switchCase(HandleValue expr, NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 531 | |
michael@0 | 532 | bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos *pos, |
michael@0 | 533 | MutableHandleValue dst); |
michael@0 | 534 | |
michael@0 | 535 | bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind, TokenPos *pos, |
michael@0 | 536 | MutableHandleValue dst); |
michael@0 | 537 | |
michael@0 | 538 | |
michael@0 | 539 | /* |
michael@0 | 540 | * statements |
michael@0 | 541 | */ |
michael@0 | 542 | |
michael@0 | 543 | bool blockStatement(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 544 | |
michael@0 | 545 | bool expressionStatement(HandleValue expr, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 546 | |
michael@0 | 547 | bool emptyStatement(TokenPos *pos, MutableHandleValue dst); |
michael@0 | 548 | |
michael@0 | 549 | bool ifStatement(HandleValue test, HandleValue cons, HandleValue alt, TokenPos *pos, |
michael@0 | 550 | MutableHandleValue dst); |
michael@0 | 551 | |
michael@0 | 552 | bool breakStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 553 | |
michael@0 | 554 | bool continueStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 555 | |
michael@0 | 556 | bool labeledStatement(HandleValue label, HandleValue stmt, TokenPos *pos, |
michael@0 | 557 | MutableHandleValue dst); |
michael@0 | 558 | |
michael@0 | 559 | bool throwStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 560 | |
michael@0 | 561 | bool returnStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 562 | |
michael@0 | 563 | bool forStatement(HandleValue init, HandleValue test, HandleValue update, HandleValue stmt, |
michael@0 | 564 | TokenPos *pos, MutableHandleValue dst); |
michael@0 | 565 | |
michael@0 | 566 | bool forInStatement(HandleValue var, HandleValue expr, HandleValue stmt, |
michael@0 | 567 | bool isForEach, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 568 | |
michael@0 | 569 | bool forOfStatement(HandleValue var, HandleValue expr, HandleValue stmt, TokenPos *pos, |
michael@0 | 570 | MutableHandleValue dst); |
michael@0 | 571 | |
michael@0 | 572 | bool withStatement(HandleValue expr, HandleValue stmt, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 573 | |
michael@0 | 574 | bool whileStatement(HandleValue test, HandleValue stmt, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 575 | |
michael@0 | 576 | bool doWhileStatement(HandleValue stmt, HandleValue test, TokenPos *pos, |
michael@0 | 577 | MutableHandleValue dst); |
michael@0 | 578 | |
michael@0 | 579 | bool switchStatement(HandleValue disc, NodeVector &elts, bool lexical, TokenPos *pos, |
michael@0 | 580 | MutableHandleValue dst); |
michael@0 | 581 | |
michael@0 | 582 | bool tryStatement(HandleValue body, NodeVector &guarded, HandleValue unguarded, |
michael@0 | 583 | HandleValue finally, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 584 | |
michael@0 | 585 | bool debuggerStatement(TokenPos *pos, MutableHandleValue dst); |
michael@0 | 586 | |
michael@0 | 587 | bool letStatement(NodeVector &head, HandleValue stmt, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 588 | |
michael@0 | 589 | bool importDeclaration(NodeVector &elts, HandleValue moduleSpec, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 590 | |
michael@0 | 591 | bool importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 592 | |
michael@0 | 593 | bool exportDeclaration(HandleValue decl, NodeVector &elts, HandleValue moduleSpec, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 594 | |
michael@0 | 595 | bool exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 596 | |
michael@0 | 597 | bool exportBatchSpecifier(TokenPos *pos, MutableHandleValue dst); |
michael@0 | 598 | |
michael@0 | 599 | /* |
michael@0 | 600 | * expressions |
michael@0 | 601 | */ |
michael@0 | 602 | |
michael@0 | 603 | bool binaryExpression(BinaryOperator op, HandleValue left, HandleValue right, TokenPos *pos, |
michael@0 | 604 | MutableHandleValue dst); |
michael@0 | 605 | |
michael@0 | 606 | bool unaryExpression(UnaryOperator op, HandleValue expr, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 607 | |
michael@0 | 608 | bool assignmentExpression(AssignmentOperator op, HandleValue lhs, HandleValue rhs, |
michael@0 | 609 | TokenPos *pos, MutableHandleValue dst); |
michael@0 | 610 | |
michael@0 | 611 | bool updateExpression(HandleValue expr, bool incr, bool prefix, TokenPos *pos, |
michael@0 | 612 | MutableHandleValue dst); |
michael@0 | 613 | |
michael@0 | 614 | bool logicalExpression(bool lor, HandleValue left, HandleValue right, TokenPos *pos, |
michael@0 | 615 | MutableHandleValue dst); |
michael@0 | 616 | |
michael@0 | 617 | bool conditionalExpression(HandleValue test, HandleValue cons, HandleValue alt, TokenPos *pos, |
michael@0 | 618 | MutableHandleValue dst); |
michael@0 | 619 | |
michael@0 | 620 | bool sequenceExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 621 | |
michael@0 | 622 | bool newExpression(HandleValue callee, NodeVector &args, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 623 | |
michael@0 | 624 | bool callExpression(HandleValue callee, NodeVector &args, TokenPos *pos, |
michael@0 | 625 | MutableHandleValue dst); |
michael@0 | 626 | |
michael@0 | 627 | bool memberExpression(bool computed, HandleValue expr, HandleValue member, TokenPos *pos, |
michael@0 | 628 | MutableHandleValue dst); |
michael@0 | 629 | |
michael@0 | 630 | bool arrayExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 631 | |
michael@0 | 632 | bool spreadExpression(HandleValue expr, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 633 | |
michael@0 | 634 | bool objectExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 635 | |
michael@0 | 636 | bool thisExpression(TokenPos *pos, MutableHandleValue dst); |
michael@0 | 637 | |
michael@0 | 638 | bool yieldExpression(HandleValue arg, YieldKind kind, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 639 | |
michael@0 | 640 | bool comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach, bool isForOf, TokenPos *pos, |
michael@0 | 641 | MutableHandleValue dst); |
michael@0 | 642 | |
michael@0 | 643 | bool comprehensionExpression(HandleValue body, NodeVector &blocks, HandleValue filter, |
michael@0 | 644 | TokenPos *pos, MutableHandleValue dst); |
michael@0 | 645 | |
michael@0 | 646 | bool generatorExpression(HandleValue body, NodeVector &blocks, HandleValue filter, |
michael@0 | 647 | TokenPos *pos, MutableHandleValue dst); |
michael@0 | 648 | |
michael@0 | 649 | bool letExpression(NodeVector &head, HandleValue expr, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 650 | |
michael@0 | 651 | /* |
michael@0 | 652 | * declarations |
michael@0 | 653 | */ |
michael@0 | 654 | |
michael@0 | 655 | bool variableDeclaration(NodeVector &elts, VarDeclKind kind, TokenPos *pos, |
michael@0 | 656 | MutableHandleValue dst); |
michael@0 | 657 | |
michael@0 | 658 | /* |
michael@0 | 659 | * patterns |
michael@0 | 660 | */ |
michael@0 | 661 | |
michael@0 | 662 | bool arrayPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 663 | |
michael@0 | 664 | bool objectPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 665 | |
michael@0 | 666 | bool propertyPattern(HandleValue key, HandleValue patt, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 667 | }; |
michael@0 | 668 | |
michael@0 | 669 | } /* anonymous namespace */ |
michael@0 | 670 | |
michael@0 | 671 | bool |
michael@0 | 672 | NodeBuilder::newNode(ASTType type, TokenPos *pos, MutableHandleObject dst) |
michael@0 | 673 | { |
michael@0 | 674 | JS_ASSERT(type > AST_ERROR && type < AST_LIMIT); |
michael@0 | 675 | |
michael@0 | 676 | RootedValue tv(cx); |
michael@0 | 677 | RootedObject node(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); |
michael@0 | 678 | if (!node || |
michael@0 | 679 | !setNodeLoc(node, pos) || |
michael@0 | 680 | !atomValue(nodeTypeNames[type], &tv) || |
michael@0 | 681 | !setProperty(node, "type", tv)) { |
michael@0 | 682 | return false; |
michael@0 | 683 | } |
michael@0 | 684 | |
michael@0 | 685 | dst.set(node); |
michael@0 | 686 | return true; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | bool |
michael@0 | 690 | NodeBuilder::newArray(NodeVector &elts, MutableHandleValue dst) |
michael@0 | 691 | { |
michael@0 | 692 | const size_t len = elts.length(); |
michael@0 | 693 | if (len > UINT32_MAX) { |
michael@0 | 694 | js_ReportAllocationOverflow(cx); |
michael@0 | 695 | return false; |
michael@0 | 696 | } |
michael@0 | 697 | RootedObject array(cx, NewDenseAllocatedArray(cx, uint32_t(len))); |
michael@0 | 698 | if (!array) |
michael@0 | 699 | return false; |
michael@0 | 700 | |
michael@0 | 701 | for (size_t i = 0; i < len; i++) { |
michael@0 | 702 | RootedValue val(cx, elts[i]); |
michael@0 | 703 | |
michael@0 | 704 | JS_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE); |
michael@0 | 705 | |
michael@0 | 706 | /* Represent "no node" as an array hole by not adding the value. */ |
michael@0 | 707 | if (val.isMagic(JS_SERIALIZE_NO_NODE)) |
michael@0 | 708 | continue; |
michael@0 | 709 | |
michael@0 | 710 | if (!JSObject::setElement(cx, array, array, i, &val, false)) |
michael@0 | 711 | return false; |
michael@0 | 712 | } |
michael@0 | 713 | |
michael@0 | 714 | dst.setObject(*array); |
michael@0 | 715 | return true; |
michael@0 | 716 | } |
michael@0 | 717 | |
michael@0 | 718 | bool |
michael@0 | 719 | NodeBuilder::newNodeLoc(TokenPos *pos, MutableHandleValue dst) |
michael@0 | 720 | { |
michael@0 | 721 | if (!pos) { |
michael@0 | 722 | dst.setNull(); |
michael@0 | 723 | return true; |
michael@0 | 724 | } |
michael@0 | 725 | |
michael@0 | 726 | RootedObject loc(cx); |
michael@0 | 727 | RootedObject to(cx); |
michael@0 | 728 | RootedValue val(cx); |
michael@0 | 729 | |
michael@0 | 730 | if (!newObject(&loc)) |
michael@0 | 731 | return false; |
michael@0 | 732 | |
michael@0 | 733 | dst.setObject(*loc); |
michael@0 | 734 | |
michael@0 | 735 | uint32_t startLineNum, startColumnIndex; |
michael@0 | 736 | uint32_t endLineNum, endColumnIndex; |
michael@0 | 737 | tokenStream->srcCoords.lineNumAndColumnIndex(pos->begin, &startLineNum, &startColumnIndex); |
michael@0 | 738 | tokenStream->srcCoords.lineNumAndColumnIndex(pos->end, &endLineNum, &endColumnIndex); |
michael@0 | 739 | |
michael@0 | 740 | if (!newObject(&to)) |
michael@0 | 741 | return false; |
michael@0 | 742 | val.setObject(*to); |
michael@0 | 743 | if (!setProperty(loc, "start", val)) |
michael@0 | 744 | return false; |
michael@0 | 745 | val.setNumber(startLineNum); |
michael@0 | 746 | if (!setProperty(to, "line", val)) |
michael@0 | 747 | return false; |
michael@0 | 748 | val.setNumber(startColumnIndex); |
michael@0 | 749 | if (!setProperty(to, "column", val)) |
michael@0 | 750 | return false; |
michael@0 | 751 | |
michael@0 | 752 | if (!newObject(&to)) |
michael@0 | 753 | return false; |
michael@0 | 754 | val.setObject(*to); |
michael@0 | 755 | if (!setProperty(loc, "end", val)) |
michael@0 | 756 | return false; |
michael@0 | 757 | val.setNumber(endLineNum); |
michael@0 | 758 | if (!setProperty(to, "line", val)) |
michael@0 | 759 | return false; |
michael@0 | 760 | val.setNumber(endColumnIndex); |
michael@0 | 761 | if (!setProperty(to, "column", val)) |
michael@0 | 762 | return false; |
michael@0 | 763 | |
michael@0 | 764 | if (!setProperty(loc, "source", srcval)) |
michael@0 | 765 | return false; |
michael@0 | 766 | |
michael@0 | 767 | return true; |
michael@0 | 768 | } |
michael@0 | 769 | |
michael@0 | 770 | bool |
michael@0 | 771 | NodeBuilder::setNodeLoc(HandleObject node, TokenPos *pos) |
michael@0 | 772 | { |
michael@0 | 773 | if (!saveLoc) { |
michael@0 | 774 | RootedValue nullVal(cx, NullValue()); |
michael@0 | 775 | setProperty(node, "loc", nullVal); |
michael@0 | 776 | return true; |
michael@0 | 777 | } |
michael@0 | 778 | |
michael@0 | 779 | RootedValue loc(cx); |
michael@0 | 780 | return newNodeLoc(pos, &loc) && |
michael@0 | 781 | setProperty(node, "loc", loc); |
michael@0 | 782 | } |
michael@0 | 783 | |
michael@0 | 784 | bool |
michael@0 | 785 | NodeBuilder::program(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 786 | { |
michael@0 | 787 | return listNode(AST_PROGRAM, "body", elts, pos, dst); |
michael@0 | 788 | } |
michael@0 | 789 | |
michael@0 | 790 | bool |
michael@0 | 791 | NodeBuilder::blockStatement(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 792 | { |
michael@0 | 793 | return listNode(AST_BLOCK_STMT, "body", elts, pos, dst); |
michael@0 | 794 | } |
michael@0 | 795 | |
michael@0 | 796 | bool |
michael@0 | 797 | NodeBuilder::expressionStatement(HandleValue expr, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 798 | { |
michael@0 | 799 | RootedValue cb(cx, callbacks[AST_EXPR_STMT]); |
michael@0 | 800 | if (!cb.isNull()) |
michael@0 | 801 | return callback(cb, expr, pos, dst); |
michael@0 | 802 | |
michael@0 | 803 | return newNode(AST_EXPR_STMT, pos, "expression", expr, dst); |
michael@0 | 804 | } |
michael@0 | 805 | |
michael@0 | 806 | bool |
michael@0 | 807 | NodeBuilder::emptyStatement(TokenPos *pos, MutableHandleValue dst) |
michael@0 | 808 | { |
michael@0 | 809 | RootedValue cb(cx, callbacks[AST_EMPTY_STMT]); |
michael@0 | 810 | if (!cb.isNull()) |
michael@0 | 811 | return callback(cb, pos, dst); |
michael@0 | 812 | |
michael@0 | 813 | return newNode(AST_EMPTY_STMT, pos, dst); |
michael@0 | 814 | } |
michael@0 | 815 | |
michael@0 | 816 | bool |
michael@0 | 817 | NodeBuilder::ifStatement(HandleValue test, HandleValue cons, HandleValue alt, TokenPos *pos, |
michael@0 | 818 | MutableHandleValue dst) |
michael@0 | 819 | { |
michael@0 | 820 | RootedValue cb(cx, callbacks[AST_IF_STMT]); |
michael@0 | 821 | if (!cb.isNull()) |
michael@0 | 822 | return callback(cb, test, cons, opt(alt), pos, dst); |
michael@0 | 823 | |
michael@0 | 824 | return newNode(AST_IF_STMT, pos, |
michael@0 | 825 | "test", test, |
michael@0 | 826 | "consequent", cons, |
michael@0 | 827 | "alternate", alt, |
michael@0 | 828 | dst); |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | bool |
michael@0 | 832 | NodeBuilder::breakStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 833 | { |
michael@0 | 834 | RootedValue cb(cx, callbacks[AST_BREAK_STMT]); |
michael@0 | 835 | if (!cb.isNull()) |
michael@0 | 836 | return callback(cb, opt(label), pos, dst); |
michael@0 | 837 | |
michael@0 | 838 | return newNode(AST_BREAK_STMT, pos, "label", label, dst); |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | bool |
michael@0 | 842 | NodeBuilder::continueStatement(HandleValue label, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 843 | { |
michael@0 | 844 | RootedValue cb(cx, callbacks[AST_CONTINUE_STMT]); |
michael@0 | 845 | if (!cb.isNull()) |
michael@0 | 846 | return callback(cb, opt(label), pos, dst); |
michael@0 | 847 | |
michael@0 | 848 | return newNode(AST_CONTINUE_STMT, pos, "label", label, dst); |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | bool |
michael@0 | 852 | NodeBuilder::labeledStatement(HandleValue label, HandleValue stmt, TokenPos *pos, |
michael@0 | 853 | MutableHandleValue dst) |
michael@0 | 854 | { |
michael@0 | 855 | RootedValue cb(cx, callbacks[AST_LAB_STMT]); |
michael@0 | 856 | if (!cb.isNull()) |
michael@0 | 857 | return callback(cb, label, stmt, pos, dst); |
michael@0 | 858 | |
michael@0 | 859 | return newNode(AST_LAB_STMT, pos, |
michael@0 | 860 | "label", label, |
michael@0 | 861 | "body", stmt, |
michael@0 | 862 | dst); |
michael@0 | 863 | } |
michael@0 | 864 | |
michael@0 | 865 | bool |
michael@0 | 866 | NodeBuilder::throwStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 867 | { |
michael@0 | 868 | RootedValue cb(cx, callbacks[AST_THROW_STMT]); |
michael@0 | 869 | if (!cb.isNull()) |
michael@0 | 870 | return callback(cb, arg, pos, dst); |
michael@0 | 871 | |
michael@0 | 872 | return newNode(AST_THROW_STMT, pos, "argument", arg, dst); |
michael@0 | 873 | } |
michael@0 | 874 | |
michael@0 | 875 | bool |
michael@0 | 876 | NodeBuilder::returnStatement(HandleValue arg, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 877 | { |
michael@0 | 878 | RootedValue cb(cx, callbacks[AST_RETURN_STMT]); |
michael@0 | 879 | if (!cb.isNull()) |
michael@0 | 880 | return callback(cb, opt(arg), pos, dst); |
michael@0 | 881 | |
michael@0 | 882 | return newNode(AST_RETURN_STMT, pos, "argument", arg, dst); |
michael@0 | 883 | } |
michael@0 | 884 | |
michael@0 | 885 | bool |
michael@0 | 886 | NodeBuilder::forStatement(HandleValue init, HandleValue test, HandleValue update, HandleValue stmt, |
michael@0 | 887 | TokenPos *pos, MutableHandleValue dst) |
michael@0 | 888 | { |
michael@0 | 889 | RootedValue cb(cx, callbacks[AST_FOR_STMT]); |
michael@0 | 890 | if (!cb.isNull()) |
michael@0 | 891 | return callback(cb, opt(init), opt(test), opt(update), stmt, pos, dst); |
michael@0 | 892 | |
michael@0 | 893 | return newNode(AST_FOR_STMT, pos, |
michael@0 | 894 | "init", init, |
michael@0 | 895 | "test", test, |
michael@0 | 896 | "update", update, |
michael@0 | 897 | "body", stmt, |
michael@0 | 898 | dst); |
michael@0 | 899 | } |
michael@0 | 900 | |
michael@0 | 901 | bool |
michael@0 | 902 | NodeBuilder::forInStatement(HandleValue var, HandleValue expr, HandleValue stmt, bool isForEach, |
michael@0 | 903 | TokenPos *pos, MutableHandleValue dst) |
michael@0 | 904 | { |
michael@0 | 905 | RootedValue isForEachVal(cx, BooleanValue(isForEach)); |
michael@0 | 906 | |
michael@0 | 907 | RootedValue cb(cx, callbacks[AST_FOR_IN_STMT]); |
michael@0 | 908 | if (!cb.isNull()) |
michael@0 | 909 | return callback(cb, var, expr, stmt, isForEachVal, pos, dst); |
michael@0 | 910 | |
michael@0 | 911 | return newNode(AST_FOR_IN_STMT, pos, |
michael@0 | 912 | "left", var, |
michael@0 | 913 | "right", expr, |
michael@0 | 914 | "body", stmt, |
michael@0 | 915 | "each", isForEachVal, |
michael@0 | 916 | dst); |
michael@0 | 917 | } |
michael@0 | 918 | |
michael@0 | 919 | bool |
michael@0 | 920 | NodeBuilder::forOfStatement(HandleValue var, HandleValue expr, HandleValue stmt, TokenPos *pos, |
michael@0 | 921 | MutableHandleValue dst) |
michael@0 | 922 | { |
michael@0 | 923 | RootedValue cb(cx, callbacks[AST_FOR_OF_STMT]); |
michael@0 | 924 | if (!cb.isNull()) |
michael@0 | 925 | return callback(cb, var, expr, stmt, pos, dst); |
michael@0 | 926 | |
michael@0 | 927 | return newNode(AST_FOR_OF_STMT, pos, |
michael@0 | 928 | "left", var, |
michael@0 | 929 | "right", expr, |
michael@0 | 930 | "body", stmt, |
michael@0 | 931 | dst); |
michael@0 | 932 | } |
michael@0 | 933 | |
michael@0 | 934 | bool |
michael@0 | 935 | NodeBuilder::withStatement(HandleValue expr, HandleValue stmt, TokenPos *pos, |
michael@0 | 936 | MutableHandleValue dst) |
michael@0 | 937 | { |
michael@0 | 938 | RootedValue cb(cx, callbacks[AST_WITH_STMT]); |
michael@0 | 939 | if (!cb.isNull()) |
michael@0 | 940 | return callback(cb, expr, stmt, pos, dst); |
michael@0 | 941 | |
michael@0 | 942 | return newNode(AST_WITH_STMT, pos, |
michael@0 | 943 | "object", expr, |
michael@0 | 944 | "body", stmt, |
michael@0 | 945 | dst); |
michael@0 | 946 | } |
michael@0 | 947 | |
michael@0 | 948 | bool |
michael@0 | 949 | NodeBuilder::whileStatement(HandleValue test, HandleValue stmt, TokenPos *pos, |
michael@0 | 950 | MutableHandleValue dst) |
michael@0 | 951 | { |
michael@0 | 952 | RootedValue cb(cx, callbacks[AST_WHILE_STMT]); |
michael@0 | 953 | if (!cb.isNull()) |
michael@0 | 954 | return callback(cb, test, stmt, pos, dst); |
michael@0 | 955 | |
michael@0 | 956 | return newNode(AST_WHILE_STMT, pos, |
michael@0 | 957 | "test", test, |
michael@0 | 958 | "body", stmt, |
michael@0 | 959 | dst); |
michael@0 | 960 | } |
michael@0 | 961 | |
michael@0 | 962 | bool |
michael@0 | 963 | NodeBuilder::doWhileStatement(HandleValue stmt, HandleValue test, TokenPos *pos, |
michael@0 | 964 | MutableHandleValue dst) |
michael@0 | 965 | { |
michael@0 | 966 | RootedValue cb(cx, callbacks[AST_DO_STMT]); |
michael@0 | 967 | if (!cb.isNull()) |
michael@0 | 968 | return callback(cb, stmt, test, pos, dst); |
michael@0 | 969 | |
michael@0 | 970 | return newNode(AST_DO_STMT, pos, |
michael@0 | 971 | "body", stmt, |
michael@0 | 972 | "test", test, |
michael@0 | 973 | dst); |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | bool |
michael@0 | 977 | NodeBuilder::switchStatement(HandleValue disc, NodeVector &elts, bool lexical, TokenPos *pos, |
michael@0 | 978 | MutableHandleValue dst) |
michael@0 | 979 | { |
michael@0 | 980 | RootedValue array(cx); |
michael@0 | 981 | if (!newArray(elts, &array)) |
michael@0 | 982 | return false; |
michael@0 | 983 | |
michael@0 | 984 | RootedValue lexicalVal(cx, BooleanValue(lexical)); |
michael@0 | 985 | |
michael@0 | 986 | RootedValue cb(cx, callbacks[AST_SWITCH_STMT]); |
michael@0 | 987 | if (!cb.isNull()) |
michael@0 | 988 | return callback(cb, disc, array, lexicalVal, pos, dst); |
michael@0 | 989 | |
michael@0 | 990 | return newNode(AST_SWITCH_STMT, pos, |
michael@0 | 991 | "discriminant", disc, |
michael@0 | 992 | "cases", array, |
michael@0 | 993 | "lexical", lexicalVal, |
michael@0 | 994 | dst); |
michael@0 | 995 | } |
michael@0 | 996 | |
michael@0 | 997 | bool |
michael@0 | 998 | NodeBuilder::tryStatement(HandleValue body, NodeVector &guarded, HandleValue unguarded, |
michael@0 | 999 | HandleValue finally, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1000 | { |
michael@0 | 1001 | RootedValue guardedHandlers(cx); |
michael@0 | 1002 | if (!newArray(guarded, &guardedHandlers)) |
michael@0 | 1003 | return false; |
michael@0 | 1004 | |
michael@0 | 1005 | RootedValue cb(cx, callbacks[AST_TRY_STMT]); |
michael@0 | 1006 | if (!cb.isNull()) |
michael@0 | 1007 | return callback(cb, body, guardedHandlers, unguarded, opt(finally), pos, dst); |
michael@0 | 1008 | |
michael@0 | 1009 | return newNode(AST_TRY_STMT, pos, |
michael@0 | 1010 | "block", body, |
michael@0 | 1011 | "guardedHandlers", guardedHandlers, |
michael@0 | 1012 | "handler", unguarded, |
michael@0 | 1013 | "finalizer", finally, |
michael@0 | 1014 | dst); |
michael@0 | 1015 | } |
michael@0 | 1016 | |
michael@0 | 1017 | bool |
michael@0 | 1018 | NodeBuilder::debuggerStatement(TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1019 | { |
michael@0 | 1020 | RootedValue cb(cx, callbacks[AST_DEBUGGER_STMT]); |
michael@0 | 1021 | if (!cb.isNull()) |
michael@0 | 1022 | return callback(cb, pos, dst); |
michael@0 | 1023 | |
michael@0 | 1024 | return newNode(AST_DEBUGGER_STMT, pos, dst); |
michael@0 | 1025 | } |
michael@0 | 1026 | |
michael@0 | 1027 | bool |
michael@0 | 1028 | NodeBuilder::binaryExpression(BinaryOperator op, HandleValue left, HandleValue right, TokenPos *pos, |
michael@0 | 1029 | MutableHandleValue dst) |
michael@0 | 1030 | { |
michael@0 | 1031 | JS_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); |
michael@0 | 1032 | |
michael@0 | 1033 | RootedValue opName(cx); |
michael@0 | 1034 | if (!atomValue(binopNames[op], &opName)) |
michael@0 | 1035 | return false; |
michael@0 | 1036 | |
michael@0 | 1037 | RootedValue cb(cx, callbacks[AST_BINARY_EXPR]); |
michael@0 | 1038 | if (!cb.isNull()) |
michael@0 | 1039 | return callback(cb, opName, left, right, pos, dst); |
michael@0 | 1040 | |
michael@0 | 1041 | return newNode(AST_BINARY_EXPR, pos, |
michael@0 | 1042 | "operator", opName, |
michael@0 | 1043 | "left", left, |
michael@0 | 1044 | "right", right, |
michael@0 | 1045 | dst); |
michael@0 | 1046 | } |
michael@0 | 1047 | |
michael@0 | 1048 | bool |
michael@0 | 1049 | NodeBuilder::unaryExpression(UnaryOperator unop, HandleValue expr, TokenPos *pos, |
michael@0 | 1050 | MutableHandleValue dst) |
michael@0 | 1051 | { |
michael@0 | 1052 | JS_ASSERT(unop > UNOP_ERR && unop < UNOP_LIMIT); |
michael@0 | 1053 | |
michael@0 | 1054 | RootedValue opName(cx); |
michael@0 | 1055 | if (!atomValue(unopNames[unop], &opName)) |
michael@0 | 1056 | return false; |
michael@0 | 1057 | |
michael@0 | 1058 | RootedValue cb(cx, callbacks[AST_UNARY_EXPR]); |
michael@0 | 1059 | if (!cb.isNull()) |
michael@0 | 1060 | return callback(cb, opName, expr, pos, dst); |
michael@0 | 1061 | |
michael@0 | 1062 | RootedValue trueVal(cx, BooleanValue(true)); |
michael@0 | 1063 | return newNode(AST_UNARY_EXPR, pos, |
michael@0 | 1064 | "operator", opName, |
michael@0 | 1065 | "argument", expr, |
michael@0 | 1066 | "prefix", trueVal, |
michael@0 | 1067 | dst); |
michael@0 | 1068 | } |
michael@0 | 1069 | |
michael@0 | 1070 | bool |
michael@0 | 1071 | NodeBuilder::assignmentExpression(AssignmentOperator aop, HandleValue lhs, HandleValue rhs, |
michael@0 | 1072 | TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1073 | { |
michael@0 | 1074 | JS_ASSERT(aop > AOP_ERR && aop < AOP_LIMIT); |
michael@0 | 1075 | |
michael@0 | 1076 | RootedValue opName(cx); |
michael@0 | 1077 | if (!atomValue(aopNames[aop], &opName)) |
michael@0 | 1078 | return false; |
michael@0 | 1079 | |
michael@0 | 1080 | RootedValue cb(cx, callbacks[AST_ASSIGN_EXPR]); |
michael@0 | 1081 | if (!cb.isNull()) |
michael@0 | 1082 | return callback(cb, opName, lhs, rhs, pos, dst); |
michael@0 | 1083 | |
michael@0 | 1084 | return newNode(AST_ASSIGN_EXPR, pos, |
michael@0 | 1085 | "operator", opName, |
michael@0 | 1086 | "left", lhs, |
michael@0 | 1087 | "right", rhs, |
michael@0 | 1088 | dst); |
michael@0 | 1089 | } |
michael@0 | 1090 | |
michael@0 | 1091 | bool |
michael@0 | 1092 | NodeBuilder::updateExpression(HandleValue expr, bool incr, bool prefix, TokenPos *pos, |
michael@0 | 1093 | MutableHandleValue dst) |
michael@0 | 1094 | { |
michael@0 | 1095 | RootedValue opName(cx); |
michael@0 | 1096 | if (!atomValue(incr ? "++" : "--", &opName)) |
michael@0 | 1097 | return false; |
michael@0 | 1098 | |
michael@0 | 1099 | RootedValue prefixVal(cx, BooleanValue(prefix)); |
michael@0 | 1100 | |
michael@0 | 1101 | RootedValue cb(cx, callbacks[AST_UPDATE_EXPR]); |
michael@0 | 1102 | if (!cb.isNull()) |
michael@0 | 1103 | return callback(cb, expr, opName, prefixVal, pos, dst); |
michael@0 | 1104 | |
michael@0 | 1105 | return newNode(AST_UPDATE_EXPR, pos, |
michael@0 | 1106 | "operator", opName, |
michael@0 | 1107 | "argument", expr, |
michael@0 | 1108 | "prefix", prefixVal, |
michael@0 | 1109 | dst); |
michael@0 | 1110 | } |
michael@0 | 1111 | |
michael@0 | 1112 | bool |
michael@0 | 1113 | NodeBuilder::logicalExpression(bool lor, HandleValue left, HandleValue right, TokenPos *pos, |
michael@0 | 1114 | MutableHandleValue dst) |
michael@0 | 1115 | { |
michael@0 | 1116 | RootedValue opName(cx); |
michael@0 | 1117 | if (!atomValue(lor ? "||" : "&&", &opName)) |
michael@0 | 1118 | return false; |
michael@0 | 1119 | |
michael@0 | 1120 | RootedValue cb(cx, callbacks[AST_LOGICAL_EXPR]); |
michael@0 | 1121 | if (!cb.isNull()) |
michael@0 | 1122 | return callback(cb, opName, left, right, pos, dst); |
michael@0 | 1123 | |
michael@0 | 1124 | return newNode(AST_LOGICAL_EXPR, pos, |
michael@0 | 1125 | "operator", opName, |
michael@0 | 1126 | "left", left, |
michael@0 | 1127 | "right", right, |
michael@0 | 1128 | dst); |
michael@0 | 1129 | } |
michael@0 | 1130 | |
michael@0 | 1131 | bool |
michael@0 | 1132 | NodeBuilder::conditionalExpression(HandleValue test, HandleValue cons, HandleValue alt, |
michael@0 | 1133 | TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1134 | { |
michael@0 | 1135 | RootedValue cb(cx, callbacks[AST_COND_EXPR]); |
michael@0 | 1136 | if (!cb.isNull()) |
michael@0 | 1137 | return callback(cb, test, cons, alt, pos, dst); |
michael@0 | 1138 | |
michael@0 | 1139 | return newNode(AST_COND_EXPR, pos, |
michael@0 | 1140 | "test", test, |
michael@0 | 1141 | "consequent", cons, |
michael@0 | 1142 | "alternate", alt, |
michael@0 | 1143 | dst); |
michael@0 | 1144 | } |
michael@0 | 1145 | |
michael@0 | 1146 | bool |
michael@0 | 1147 | NodeBuilder::sequenceExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1148 | { |
michael@0 | 1149 | return listNode(AST_LIST_EXPR, "expressions", elts, pos, dst); |
michael@0 | 1150 | } |
michael@0 | 1151 | |
michael@0 | 1152 | bool |
michael@0 | 1153 | NodeBuilder::callExpression(HandleValue callee, NodeVector &args, TokenPos *pos, |
michael@0 | 1154 | MutableHandleValue dst) |
michael@0 | 1155 | { |
michael@0 | 1156 | RootedValue array(cx); |
michael@0 | 1157 | if (!newArray(args, &array)) |
michael@0 | 1158 | return false; |
michael@0 | 1159 | |
michael@0 | 1160 | RootedValue cb(cx, callbacks[AST_CALL_EXPR]); |
michael@0 | 1161 | if (!cb.isNull()) |
michael@0 | 1162 | return callback(cb, callee, array, pos, dst); |
michael@0 | 1163 | |
michael@0 | 1164 | return newNode(AST_CALL_EXPR, pos, |
michael@0 | 1165 | "callee", callee, |
michael@0 | 1166 | "arguments", array, |
michael@0 | 1167 | dst); |
michael@0 | 1168 | } |
michael@0 | 1169 | |
michael@0 | 1170 | bool |
michael@0 | 1171 | NodeBuilder::newExpression(HandleValue callee, NodeVector &args, TokenPos *pos, |
michael@0 | 1172 | MutableHandleValue dst) |
michael@0 | 1173 | { |
michael@0 | 1174 | RootedValue array(cx); |
michael@0 | 1175 | if (!newArray(args, &array)) |
michael@0 | 1176 | return false; |
michael@0 | 1177 | |
michael@0 | 1178 | RootedValue cb(cx, callbacks[AST_NEW_EXPR]); |
michael@0 | 1179 | if (!cb.isNull()) |
michael@0 | 1180 | return callback(cb, callee, array, pos, dst); |
michael@0 | 1181 | |
michael@0 | 1182 | return newNode(AST_NEW_EXPR, pos, |
michael@0 | 1183 | "callee", callee, |
michael@0 | 1184 | "arguments", array, |
michael@0 | 1185 | dst); |
michael@0 | 1186 | } |
michael@0 | 1187 | |
michael@0 | 1188 | bool |
michael@0 | 1189 | NodeBuilder::memberExpression(bool computed, HandleValue expr, HandleValue member, TokenPos *pos, |
michael@0 | 1190 | MutableHandleValue dst) |
michael@0 | 1191 | { |
michael@0 | 1192 | RootedValue computedVal(cx, BooleanValue(computed)); |
michael@0 | 1193 | |
michael@0 | 1194 | RootedValue cb(cx, callbacks[AST_MEMBER_EXPR]); |
michael@0 | 1195 | if (!cb.isNull()) |
michael@0 | 1196 | return callback(cb, computedVal, expr, member, pos, dst); |
michael@0 | 1197 | |
michael@0 | 1198 | return newNode(AST_MEMBER_EXPR, pos, |
michael@0 | 1199 | "object", expr, |
michael@0 | 1200 | "property", member, |
michael@0 | 1201 | "computed", computedVal, |
michael@0 | 1202 | dst); |
michael@0 | 1203 | } |
michael@0 | 1204 | |
michael@0 | 1205 | bool |
michael@0 | 1206 | NodeBuilder::arrayExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1207 | { |
michael@0 | 1208 | return listNode(AST_ARRAY_EXPR, "elements", elts, pos, dst); |
michael@0 | 1209 | } |
michael@0 | 1210 | |
michael@0 | 1211 | bool |
michael@0 | 1212 | NodeBuilder::spreadExpression(HandleValue expr, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1213 | { |
michael@0 | 1214 | return newNode(AST_SPREAD_EXPR, pos, |
michael@0 | 1215 | "expression", expr, |
michael@0 | 1216 | dst); |
michael@0 | 1217 | } |
michael@0 | 1218 | |
michael@0 | 1219 | bool |
michael@0 | 1220 | NodeBuilder::propertyPattern(HandleValue key, HandleValue patt, TokenPos *pos, |
michael@0 | 1221 | MutableHandleValue dst) |
michael@0 | 1222 | { |
michael@0 | 1223 | RootedValue kindName(cx); |
michael@0 | 1224 | if (!atomValue("init", &kindName)) |
michael@0 | 1225 | return false; |
michael@0 | 1226 | |
michael@0 | 1227 | RootedValue cb(cx, callbacks[AST_PROP_PATT]); |
michael@0 | 1228 | if (!cb.isNull()) |
michael@0 | 1229 | return callback(cb, key, patt, pos, dst); |
michael@0 | 1230 | |
michael@0 | 1231 | return newNode(AST_PROP_PATT, pos, |
michael@0 | 1232 | "key", key, |
michael@0 | 1233 | "value", patt, |
michael@0 | 1234 | "kind", kindName, |
michael@0 | 1235 | dst); |
michael@0 | 1236 | } |
michael@0 | 1237 | |
michael@0 | 1238 | bool |
michael@0 | 1239 | NodeBuilder::propertyInitializer(HandleValue key, HandleValue val, PropKind kind, TokenPos *pos, |
michael@0 | 1240 | MutableHandleValue dst) |
michael@0 | 1241 | { |
michael@0 | 1242 | RootedValue kindName(cx); |
michael@0 | 1243 | if (!atomValue(kind == PROP_INIT |
michael@0 | 1244 | ? "init" |
michael@0 | 1245 | : kind == PROP_GETTER |
michael@0 | 1246 | ? "get" |
michael@0 | 1247 | : "set", &kindName)) { |
michael@0 | 1248 | return false; |
michael@0 | 1249 | } |
michael@0 | 1250 | |
michael@0 | 1251 | RootedValue cb(cx, callbacks[AST_PROPERTY]); |
michael@0 | 1252 | if (!cb.isNull()) |
michael@0 | 1253 | return callback(cb, kindName, key, val, pos, dst); |
michael@0 | 1254 | |
michael@0 | 1255 | return newNode(AST_PROPERTY, pos, |
michael@0 | 1256 | "key", key, |
michael@0 | 1257 | "value", val, |
michael@0 | 1258 | "kind", kindName, |
michael@0 | 1259 | dst); |
michael@0 | 1260 | } |
michael@0 | 1261 | |
michael@0 | 1262 | bool |
michael@0 | 1263 | NodeBuilder::objectExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1264 | { |
michael@0 | 1265 | return listNode(AST_OBJECT_EXPR, "properties", elts, pos, dst); |
michael@0 | 1266 | } |
michael@0 | 1267 | |
michael@0 | 1268 | bool |
michael@0 | 1269 | NodeBuilder::thisExpression(TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1270 | { |
michael@0 | 1271 | RootedValue cb(cx, callbacks[AST_THIS_EXPR]); |
michael@0 | 1272 | if (!cb.isNull()) |
michael@0 | 1273 | return callback(cb, pos, dst); |
michael@0 | 1274 | |
michael@0 | 1275 | return newNode(AST_THIS_EXPR, pos, dst); |
michael@0 | 1276 | } |
michael@0 | 1277 | |
michael@0 | 1278 | bool |
michael@0 | 1279 | NodeBuilder::yieldExpression(HandleValue arg, YieldKind kind, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1280 | { |
michael@0 | 1281 | RootedValue cb(cx, callbacks[AST_YIELD_EXPR]); |
michael@0 | 1282 | RootedValue delegateVal(cx); |
michael@0 | 1283 | |
michael@0 | 1284 | switch (kind) { |
michael@0 | 1285 | case Delegating: |
michael@0 | 1286 | delegateVal = BooleanValue(true); |
michael@0 | 1287 | break; |
michael@0 | 1288 | case NotDelegating: |
michael@0 | 1289 | delegateVal = BooleanValue(false); |
michael@0 | 1290 | break; |
michael@0 | 1291 | } |
michael@0 | 1292 | |
michael@0 | 1293 | if (!cb.isNull()) |
michael@0 | 1294 | return callback(cb, opt(arg), delegateVal, pos, dst); |
michael@0 | 1295 | return newNode(AST_YIELD_EXPR, pos, "argument", arg, "delegate", delegateVal, dst); |
michael@0 | 1296 | } |
michael@0 | 1297 | |
michael@0 | 1298 | bool |
michael@0 | 1299 | NodeBuilder::comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach, bool isForOf, TokenPos *pos, |
michael@0 | 1300 | MutableHandleValue dst) |
michael@0 | 1301 | { |
michael@0 | 1302 | RootedValue isForEachVal(cx, BooleanValue(isForEach)); |
michael@0 | 1303 | RootedValue isForOfVal(cx, BooleanValue(isForOf)); |
michael@0 | 1304 | |
michael@0 | 1305 | RootedValue cb(cx, callbacks[AST_COMP_BLOCK]); |
michael@0 | 1306 | if (!cb.isNull()) |
michael@0 | 1307 | return callback(cb, patt, src, isForEachVal, isForOfVal, pos, dst); |
michael@0 | 1308 | |
michael@0 | 1309 | return newNode(AST_COMP_BLOCK, pos, |
michael@0 | 1310 | "left", patt, |
michael@0 | 1311 | "right", src, |
michael@0 | 1312 | "each", isForEachVal, |
michael@0 | 1313 | "of", isForOfVal, |
michael@0 | 1314 | dst); |
michael@0 | 1315 | } |
michael@0 | 1316 | |
michael@0 | 1317 | bool |
michael@0 | 1318 | NodeBuilder::comprehensionExpression(HandleValue body, NodeVector &blocks, HandleValue filter, |
michael@0 | 1319 | TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1320 | { |
michael@0 | 1321 | RootedValue array(cx); |
michael@0 | 1322 | if (!newArray(blocks, &array)) |
michael@0 | 1323 | return false; |
michael@0 | 1324 | |
michael@0 | 1325 | RootedValue cb(cx, callbacks[AST_COMP_EXPR]); |
michael@0 | 1326 | if (!cb.isNull()) |
michael@0 | 1327 | return callback(cb, body, array, opt(filter), pos, dst); |
michael@0 | 1328 | |
michael@0 | 1329 | return newNode(AST_COMP_EXPR, pos, |
michael@0 | 1330 | "body", body, |
michael@0 | 1331 | "blocks", array, |
michael@0 | 1332 | "filter", filter, |
michael@0 | 1333 | dst); |
michael@0 | 1334 | } |
michael@0 | 1335 | |
michael@0 | 1336 | bool |
michael@0 | 1337 | NodeBuilder::generatorExpression(HandleValue body, NodeVector &blocks, HandleValue filter, |
michael@0 | 1338 | TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1339 | { |
michael@0 | 1340 | RootedValue array(cx); |
michael@0 | 1341 | if (!newArray(blocks, &array)) |
michael@0 | 1342 | return false; |
michael@0 | 1343 | |
michael@0 | 1344 | RootedValue cb(cx, callbacks[AST_GENERATOR_EXPR]); |
michael@0 | 1345 | if (!cb.isNull()) |
michael@0 | 1346 | return callback(cb, body, array, opt(filter), pos, dst); |
michael@0 | 1347 | |
michael@0 | 1348 | return newNode(AST_GENERATOR_EXPR, pos, |
michael@0 | 1349 | "body", body, |
michael@0 | 1350 | "blocks", array, |
michael@0 | 1351 | "filter", filter, |
michael@0 | 1352 | dst); |
michael@0 | 1353 | } |
michael@0 | 1354 | |
michael@0 | 1355 | bool |
michael@0 | 1356 | NodeBuilder::letExpression(NodeVector &head, HandleValue expr, TokenPos *pos, |
michael@0 | 1357 | MutableHandleValue dst) |
michael@0 | 1358 | { |
michael@0 | 1359 | RootedValue array(cx); |
michael@0 | 1360 | if (!newArray(head, &array)) |
michael@0 | 1361 | return false; |
michael@0 | 1362 | |
michael@0 | 1363 | RootedValue cb(cx, callbacks[AST_LET_EXPR]); |
michael@0 | 1364 | if (!cb.isNull()) |
michael@0 | 1365 | return callback(cb, array, expr, pos, dst); |
michael@0 | 1366 | |
michael@0 | 1367 | return newNode(AST_LET_EXPR, pos, |
michael@0 | 1368 | "head", array, |
michael@0 | 1369 | "body", expr, |
michael@0 | 1370 | dst); |
michael@0 | 1371 | } |
michael@0 | 1372 | |
michael@0 | 1373 | bool |
michael@0 | 1374 | NodeBuilder::letStatement(NodeVector &head, HandleValue stmt, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1375 | { |
michael@0 | 1376 | RootedValue array(cx); |
michael@0 | 1377 | if (!newArray(head, &array)) |
michael@0 | 1378 | return false; |
michael@0 | 1379 | |
michael@0 | 1380 | RootedValue cb(cx, callbacks[AST_LET_STMT]); |
michael@0 | 1381 | if (!cb.isNull()) |
michael@0 | 1382 | return callback(cb, array, stmt, pos, dst); |
michael@0 | 1383 | |
michael@0 | 1384 | return newNode(AST_LET_STMT, pos, |
michael@0 | 1385 | "head", array, |
michael@0 | 1386 | "body", stmt, |
michael@0 | 1387 | dst); |
michael@0 | 1388 | } |
michael@0 | 1389 | |
michael@0 | 1390 | bool |
michael@0 | 1391 | NodeBuilder::importDeclaration(NodeVector &elts, HandleValue moduleSpec, TokenPos *pos, |
michael@0 | 1392 | MutableHandleValue dst) |
michael@0 | 1393 | { |
michael@0 | 1394 | RootedValue array(cx); |
michael@0 | 1395 | if (!newArray(elts, &array)) |
michael@0 | 1396 | return false; |
michael@0 | 1397 | |
michael@0 | 1398 | RootedValue cb(cx, callbacks[AST_IMPORT_DECL]); |
michael@0 | 1399 | if (!cb.isNull()) |
michael@0 | 1400 | return callback(cb, array, moduleSpec, pos, dst); |
michael@0 | 1401 | |
michael@0 | 1402 | return newNode(AST_IMPORT_DECL, pos, |
michael@0 | 1403 | "specifiers", array, |
michael@0 | 1404 | "source", moduleSpec, |
michael@0 | 1405 | dst); |
michael@0 | 1406 | } |
michael@0 | 1407 | |
michael@0 | 1408 | bool |
michael@0 | 1409 | NodeBuilder::importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos *pos, |
michael@0 | 1410 | MutableHandleValue dst) |
michael@0 | 1411 | { |
michael@0 | 1412 | RootedValue cb(cx, callbacks[AST_IMPORT_SPEC]); |
michael@0 | 1413 | if (!cb.isNull()) |
michael@0 | 1414 | return callback(cb, importName, bindingName, pos, dst); |
michael@0 | 1415 | |
michael@0 | 1416 | return newNode(AST_IMPORT_SPEC, pos, |
michael@0 | 1417 | "id", importName, |
michael@0 | 1418 | "name", bindingName, |
michael@0 | 1419 | dst); |
michael@0 | 1420 | } |
michael@0 | 1421 | |
michael@0 | 1422 | bool |
michael@0 | 1423 | NodeBuilder::exportDeclaration(HandleValue decl, NodeVector &elts, HandleValue moduleSpec, |
michael@0 | 1424 | TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1425 | { |
michael@0 | 1426 | RootedValue array(cx, NullValue()); |
michael@0 | 1427 | if (decl.isNull() && !newArray(elts, &array)) |
michael@0 | 1428 | return false; |
michael@0 | 1429 | |
michael@0 | 1430 | RootedValue cb(cx, callbacks[AST_IMPORT_DECL]); |
michael@0 | 1431 | |
michael@0 | 1432 | if (!cb.isNull()) |
michael@0 | 1433 | return callback(cb, decl, array, moduleSpec, pos, dst); |
michael@0 | 1434 | |
michael@0 | 1435 | return newNode(AST_EXPORT_DECL, pos, |
michael@0 | 1436 | "declaration", decl, |
michael@0 | 1437 | "specifiers", array, |
michael@0 | 1438 | "source", moduleSpec, |
michael@0 | 1439 | dst); |
michael@0 | 1440 | } |
michael@0 | 1441 | |
michael@0 | 1442 | bool |
michael@0 | 1443 | NodeBuilder::exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos *pos, |
michael@0 | 1444 | MutableHandleValue dst) |
michael@0 | 1445 | { |
michael@0 | 1446 | RootedValue cb(cx, callbacks[AST_EXPORT_SPEC]); |
michael@0 | 1447 | if (!cb.isNull()) |
michael@0 | 1448 | return callback(cb, bindingName, exportName, pos, dst); |
michael@0 | 1449 | |
michael@0 | 1450 | return newNode(AST_EXPORT_SPEC, pos, |
michael@0 | 1451 | "id", bindingName, |
michael@0 | 1452 | "name", exportName, |
michael@0 | 1453 | dst); |
michael@0 | 1454 | } |
michael@0 | 1455 | |
michael@0 | 1456 | bool |
michael@0 | 1457 | NodeBuilder::exportBatchSpecifier(TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1458 | { |
michael@0 | 1459 | RootedValue cb(cx, callbacks[AST_EXPORT_BATCH_SPEC]); |
michael@0 | 1460 | if (!cb.isNull()) |
michael@0 | 1461 | return callback(cb, pos, dst); |
michael@0 | 1462 | |
michael@0 | 1463 | return newNode(AST_EXPORT_BATCH_SPEC, pos, dst); |
michael@0 | 1464 | } |
michael@0 | 1465 | |
michael@0 | 1466 | bool |
michael@0 | 1467 | NodeBuilder::variableDeclaration(NodeVector &elts, VarDeclKind kind, TokenPos *pos, |
michael@0 | 1468 | MutableHandleValue dst) |
michael@0 | 1469 | { |
michael@0 | 1470 | JS_ASSERT(kind > VARDECL_ERR && kind < VARDECL_LIMIT); |
michael@0 | 1471 | |
michael@0 | 1472 | RootedValue array(cx), kindName(cx); |
michael@0 | 1473 | if (!newArray(elts, &array) || |
michael@0 | 1474 | !atomValue(kind == VARDECL_CONST |
michael@0 | 1475 | ? "const" |
michael@0 | 1476 | : kind == VARDECL_LET |
michael@0 | 1477 | ? "let" |
michael@0 | 1478 | : "var", &kindName)) { |
michael@0 | 1479 | return false; |
michael@0 | 1480 | } |
michael@0 | 1481 | |
michael@0 | 1482 | RootedValue cb(cx, callbacks[AST_VAR_DECL]); |
michael@0 | 1483 | if (!cb.isNull()) |
michael@0 | 1484 | return callback(cb, kindName, array, pos, dst); |
michael@0 | 1485 | |
michael@0 | 1486 | return newNode(AST_VAR_DECL, pos, |
michael@0 | 1487 | "kind", kindName, |
michael@0 | 1488 | "declarations", array, |
michael@0 | 1489 | dst); |
michael@0 | 1490 | } |
michael@0 | 1491 | |
michael@0 | 1492 | bool |
michael@0 | 1493 | NodeBuilder::variableDeclarator(HandleValue id, HandleValue init, TokenPos *pos, |
michael@0 | 1494 | MutableHandleValue dst) |
michael@0 | 1495 | { |
michael@0 | 1496 | RootedValue cb(cx, callbacks[AST_VAR_DTOR]); |
michael@0 | 1497 | if (!cb.isNull()) |
michael@0 | 1498 | return callback(cb, id, opt(init), pos, dst); |
michael@0 | 1499 | |
michael@0 | 1500 | return newNode(AST_VAR_DTOR, pos, "id", id, "init", init, dst); |
michael@0 | 1501 | } |
michael@0 | 1502 | |
michael@0 | 1503 | bool |
michael@0 | 1504 | NodeBuilder::switchCase(HandleValue expr, NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1505 | { |
michael@0 | 1506 | RootedValue array(cx); |
michael@0 | 1507 | if (!newArray(elts, &array)) |
michael@0 | 1508 | return false; |
michael@0 | 1509 | |
michael@0 | 1510 | RootedValue cb(cx, callbacks[AST_CASE]); |
michael@0 | 1511 | if (!cb.isNull()) |
michael@0 | 1512 | return callback(cb, opt(expr), array, pos, dst); |
michael@0 | 1513 | |
michael@0 | 1514 | return newNode(AST_CASE, pos, |
michael@0 | 1515 | "test", expr, |
michael@0 | 1516 | "consequent", array, |
michael@0 | 1517 | dst); |
michael@0 | 1518 | } |
michael@0 | 1519 | |
michael@0 | 1520 | bool |
michael@0 | 1521 | NodeBuilder::catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos *pos, |
michael@0 | 1522 | MutableHandleValue dst) |
michael@0 | 1523 | { |
michael@0 | 1524 | RootedValue cb(cx, callbacks[AST_CATCH]); |
michael@0 | 1525 | if (!cb.isNull()) |
michael@0 | 1526 | return callback(cb, var, opt(guard), body, pos, dst); |
michael@0 | 1527 | |
michael@0 | 1528 | return newNode(AST_CATCH, pos, |
michael@0 | 1529 | "param", var, |
michael@0 | 1530 | "guard", guard, |
michael@0 | 1531 | "body", body, |
michael@0 | 1532 | dst); |
michael@0 | 1533 | } |
michael@0 | 1534 | |
michael@0 | 1535 | bool |
michael@0 | 1536 | NodeBuilder::literal(HandleValue val, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1537 | { |
michael@0 | 1538 | RootedValue cb(cx, callbacks[AST_LITERAL]); |
michael@0 | 1539 | if (!cb.isNull()) |
michael@0 | 1540 | return callback(cb, val, pos, dst); |
michael@0 | 1541 | |
michael@0 | 1542 | return newNode(AST_LITERAL, pos, "value", val, dst); |
michael@0 | 1543 | } |
michael@0 | 1544 | |
michael@0 | 1545 | bool |
michael@0 | 1546 | NodeBuilder::identifier(HandleValue name, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1547 | { |
michael@0 | 1548 | RootedValue cb(cx, callbacks[AST_IDENTIFIER]); |
michael@0 | 1549 | if (!cb.isNull()) |
michael@0 | 1550 | return callback(cb, name, pos, dst); |
michael@0 | 1551 | |
michael@0 | 1552 | return newNode(AST_IDENTIFIER, pos, "name", name, dst); |
michael@0 | 1553 | } |
michael@0 | 1554 | |
michael@0 | 1555 | bool |
michael@0 | 1556 | NodeBuilder::objectPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1557 | { |
michael@0 | 1558 | return listNode(AST_OBJECT_PATT, "properties", elts, pos, dst); |
michael@0 | 1559 | } |
michael@0 | 1560 | |
michael@0 | 1561 | bool |
michael@0 | 1562 | NodeBuilder::arrayPattern(NodeVector &elts, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 1563 | { |
michael@0 | 1564 | return listNode(AST_ARRAY_PATT, "elements", elts, pos, dst); |
michael@0 | 1565 | } |
michael@0 | 1566 | |
michael@0 | 1567 | bool |
michael@0 | 1568 | NodeBuilder::function(ASTType type, TokenPos *pos, |
michael@0 | 1569 | HandleValue id, NodeVector &args, NodeVector &defaults, |
michael@0 | 1570 | HandleValue body, HandleValue rest, |
michael@0 | 1571 | bool isGenerator, bool isExpression, |
michael@0 | 1572 | MutableHandleValue dst) |
michael@0 | 1573 | { |
michael@0 | 1574 | RootedValue array(cx), defarray(cx); |
michael@0 | 1575 | if (!newArray(args, &array)) |
michael@0 | 1576 | return false; |
michael@0 | 1577 | if (!newArray(defaults, &defarray)) |
michael@0 | 1578 | return false; |
michael@0 | 1579 | |
michael@0 | 1580 | RootedValue isGeneratorVal(cx, BooleanValue(isGenerator)); |
michael@0 | 1581 | RootedValue isExpressionVal(cx, BooleanValue(isExpression)); |
michael@0 | 1582 | |
michael@0 | 1583 | RootedValue cb(cx, callbacks[type]); |
michael@0 | 1584 | if (!cb.isNull()) { |
michael@0 | 1585 | return callback(cb, opt(id), array, body, isGeneratorVal, isExpressionVal, pos, dst); |
michael@0 | 1586 | } |
michael@0 | 1587 | |
michael@0 | 1588 | return newNode(type, pos, |
michael@0 | 1589 | "id", id, |
michael@0 | 1590 | "params", array, |
michael@0 | 1591 | "defaults", defarray, |
michael@0 | 1592 | "body", body, |
michael@0 | 1593 | "rest", rest, |
michael@0 | 1594 | "generator", isGeneratorVal, |
michael@0 | 1595 | "expression", isExpressionVal, |
michael@0 | 1596 | dst); |
michael@0 | 1597 | } |
michael@0 | 1598 | |
michael@0 | 1599 | namespace { |
michael@0 | 1600 | |
michael@0 | 1601 | /* |
michael@0 | 1602 | * Serialization of parse nodes to JavaScript objects. |
michael@0 | 1603 | * |
michael@0 | 1604 | * All serialization methods take a non-nullable ParseNode pointer. |
michael@0 | 1605 | */ |
michael@0 | 1606 | class ASTSerializer |
michael@0 | 1607 | { |
michael@0 | 1608 | JSContext *cx; |
michael@0 | 1609 | Parser<FullParseHandler> *parser; |
michael@0 | 1610 | NodeBuilder builder; |
michael@0 | 1611 | DebugOnly<uint32_t> lineno; |
michael@0 | 1612 | |
michael@0 | 1613 | Value unrootedAtomContents(JSAtom *atom) { |
michael@0 | 1614 | return StringValue(atom ? atom : cx->names().empty); |
michael@0 | 1615 | } |
michael@0 | 1616 | |
michael@0 | 1617 | BinaryOperator binop(ParseNodeKind kind, JSOp op); |
michael@0 | 1618 | UnaryOperator unop(ParseNodeKind kind, JSOp op); |
michael@0 | 1619 | AssignmentOperator aop(JSOp op); |
michael@0 | 1620 | |
michael@0 | 1621 | bool statements(ParseNode *pn, NodeVector &elts); |
michael@0 | 1622 | bool expressions(ParseNode *pn, NodeVector &elts); |
michael@0 | 1623 | bool leftAssociate(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1624 | bool functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestruct, ParseNode *pnbody, |
michael@0 | 1625 | NodeVector &args, NodeVector &defaults, MutableHandleValue rest); |
michael@0 | 1626 | |
michael@0 | 1627 | bool sourceElement(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1628 | |
michael@0 | 1629 | bool declaration(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1630 | bool variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst); |
michael@0 | 1631 | bool variableDeclarator(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); |
michael@0 | 1632 | bool let(ParseNode *pn, bool expr, MutableHandleValue dst); |
michael@0 | 1633 | bool importDeclaration(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1634 | bool importSpecifier(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1635 | bool exportDeclaration(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1636 | bool exportSpecifier(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1637 | |
michael@0 | 1638 | bool optStatement(ParseNode *pn, MutableHandleValue dst) { |
michael@0 | 1639 | if (!pn) { |
michael@0 | 1640 | dst.setMagic(JS_SERIALIZE_NO_NODE); |
michael@0 | 1641 | return true; |
michael@0 | 1642 | } |
michael@0 | 1643 | return statement(pn, dst); |
michael@0 | 1644 | } |
michael@0 | 1645 | |
michael@0 | 1646 | bool forInit(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1647 | bool forIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, |
michael@0 | 1648 | MutableHandleValue dst); |
michael@0 | 1649 | bool forOf(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, |
michael@0 | 1650 | MutableHandleValue dst); |
michael@0 | 1651 | bool statement(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1652 | bool blockStatement(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1653 | bool switchStatement(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1654 | bool switchCase(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1655 | bool tryStatement(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1656 | bool catchClause(ParseNode *pn, bool *isGuarded, MutableHandleValue dst); |
michael@0 | 1657 | |
michael@0 | 1658 | bool optExpression(ParseNode *pn, MutableHandleValue dst) { |
michael@0 | 1659 | if (!pn) { |
michael@0 | 1660 | dst.setMagic(JS_SERIALIZE_NO_NODE); |
michael@0 | 1661 | return true; |
michael@0 | 1662 | } |
michael@0 | 1663 | return expression(pn, dst); |
michael@0 | 1664 | } |
michael@0 | 1665 | |
michael@0 | 1666 | bool expression(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1667 | |
michael@0 | 1668 | bool propertyName(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1669 | bool property(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1670 | |
michael@0 | 1671 | bool optIdentifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst) { |
michael@0 | 1672 | if (!atom) { |
michael@0 | 1673 | dst.setMagic(JS_SERIALIZE_NO_NODE); |
michael@0 | 1674 | return true; |
michael@0 | 1675 | } |
michael@0 | 1676 | return identifier(atom, pos, dst); |
michael@0 | 1677 | } |
michael@0 | 1678 | |
michael@0 | 1679 | bool identifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 1680 | bool identifier(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1681 | bool literal(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1682 | |
michael@0 | 1683 | bool pattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); |
michael@0 | 1684 | bool arrayPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); |
michael@0 | 1685 | bool objectPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst); |
michael@0 | 1686 | |
michael@0 | 1687 | bool function(ParseNode *pn, ASTType type, MutableHandleValue dst); |
michael@0 | 1688 | bool functionArgsAndBody(ParseNode *pn, NodeVector &args, NodeVector &defaults, |
michael@0 | 1689 | MutableHandleValue body, MutableHandleValue rest); |
michael@0 | 1690 | bool functionBody(ParseNode *pn, TokenPos *pos, MutableHandleValue dst); |
michael@0 | 1691 | |
michael@0 | 1692 | bool comprehensionBlock(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1693 | bool comprehension(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1694 | bool generatorExpression(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1695 | |
michael@0 | 1696 | public: |
michael@0 | 1697 | ASTSerializer(JSContext *c, bool l, char const *src, uint32_t ln) |
michael@0 | 1698 | : cx(c) |
michael@0 | 1699 | , builder(c, l, src) |
michael@0 | 1700 | #ifdef DEBUG |
michael@0 | 1701 | , lineno(ln) |
michael@0 | 1702 | #endif |
michael@0 | 1703 | {} |
michael@0 | 1704 | |
michael@0 | 1705 | bool init(HandleObject userobj) { |
michael@0 | 1706 | return builder.init(userobj); |
michael@0 | 1707 | } |
michael@0 | 1708 | |
michael@0 | 1709 | void setParser(Parser<FullParseHandler> *p) { |
michael@0 | 1710 | parser = p; |
michael@0 | 1711 | builder.setTokenStream(&p->tokenStream); |
michael@0 | 1712 | } |
michael@0 | 1713 | |
michael@0 | 1714 | bool program(ParseNode *pn, MutableHandleValue dst); |
michael@0 | 1715 | }; |
michael@0 | 1716 | |
michael@0 | 1717 | } /* anonymous namespace */ |
michael@0 | 1718 | |
michael@0 | 1719 | AssignmentOperator |
michael@0 | 1720 | ASTSerializer::aop(JSOp op) |
michael@0 | 1721 | { |
michael@0 | 1722 | switch (op) { |
michael@0 | 1723 | case JSOP_NOP: |
michael@0 | 1724 | return AOP_ASSIGN; |
michael@0 | 1725 | case JSOP_ADD: |
michael@0 | 1726 | return AOP_PLUS; |
michael@0 | 1727 | case JSOP_SUB: |
michael@0 | 1728 | return AOP_MINUS; |
michael@0 | 1729 | case JSOP_MUL: |
michael@0 | 1730 | return AOP_STAR; |
michael@0 | 1731 | case JSOP_DIV: |
michael@0 | 1732 | return AOP_DIV; |
michael@0 | 1733 | case JSOP_MOD: |
michael@0 | 1734 | return AOP_MOD; |
michael@0 | 1735 | case JSOP_LSH: |
michael@0 | 1736 | return AOP_LSH; |
michael@0 | 1737 | case JSOP_RSH: |
michael@0 | 1738 | return AOP_RSH; |
michael@0 | 1739 | case JSOP_URSH: |
michael@0 | 1740 | return AOP_URSH; |
michael@0 | 1741 | case JSOP_BITOR: |
michael@0 | 1742 | return AOP_BITOR; |
michael@0 | 1743 | case JSOP_BITXOR: |
michael@0 | 1744 | return AOP_BITXOR; |
michael@0 | 1745 | case JSOP_BITAND: |
michael@0 | 1746 | return AOP_BITAND; |
michael@0 | 1747 | default: |
michael@0 | 1748 | return AOP_ERR; |
michael@0 | 1749 | } |
michael@0 | 1750 | } |
michael@0 | 1751 | |
michael@0 | 1752 | UnaryOperator |
michael@0 | 1753 | ASTSerializer::unop(ParseNodeKind kind, JSOp op) |
michael@0 | 1754 | { |
michael@0 | 1755 | if (kind == PNK_DELETE) |
michael@0 | 1756 | return UNOP_DELETE; |
michael@0 | 1757 | |
michael@0 | 1758 | switch (op) { |
michael@0 | 1759 | case JSOP_NEG: |
michael@0 | 1760 | return UNOP_NEG; |
michael@0 | 1761 | case JSOP_POS: |
michael@0 | 1762 | return UNOP_POS; |
michael@0 | 1763 | case JSOP_NOT: |
michael@0 | 1764 | return UNOP_NOT; |
michael@0 | 1765 | case JSOP_BITNOT: |
michael@0 | 1766 | return UNOP_BITNOT; |
michael@0 | 1767 | case JSOP_TYPEOF: |
michael@0 | 1768 | case JSOP_TYPEOFEXPR: |
michael@0 | 1769 | return UNOP_TYPEOF; |
michael@0 | 1770 | case JSOP_VOID: |
michael@0 | 1771 | return UNOP_VOID; |
michael@0 | 1772 | default: |
michael@0 | 1773 | return UNOP_ERR; |
michael@0 | 1774 | } |
michael@0 | 1775 | } |
michael@0 | 1776 | |
michael@0 | 1777 | BinaryOperator |
michael@0 | 1778 | ASTSerializer::binop(ParseNodeKind kind, JSOp op) |
michael@0 | 1779 | { |
michael@0 | 1780 | switch (kind) { |
michael@0 | 1781 | case PNK_LSH: |
michael@0 | 1782 | return BINOP_LSH; |
michael@0 | 1783 | case PNK_RSH: |
michael@0 | 1784 | return BINOP_RSH; |
michael@0 | 1785 | case PNK_URSH: |
michael@0 | 1786 | return BINOP_URSH; |
michael@0 | 1787 | case PNK_LT: |
michael@0 | 1788 | return BINOP_LT; |
michael@0 | 1789 | case PNK_LE: |
michael@0 | 1790 | return BINOP_LE; |
michael@0 | 1791 | case PNK_GT: |
michael@0 | 1792 | return BINOP_GT; |
michael@0 | 1793 | case PNK_GE: |
michael@0 | 1794 | return BINOP_GE; |
michael@0 | 1795 | case PNK_EQ: |
michael@0 | 1796 | return BINOP_EQ; |
michael@0 | 1797 | case PNK_NE: |
michael@0 | 1798 | return BINOP_NE; |
michael@0 | 1799 | case PNK_STRICTEQ: |
michael@0 | 1800 | return BINOP_STRICTEQ; |
michael@0 | 1801 | case PNK_STRICTNE: |
michael@0 | 1802 | return BINOP_STRICTNE; |
michael@0 | 1803 | case PNK_ADD: |
michael@0 | 1804 | return BINOP_ADD; |
michael@0 | 1805 | case PNK_SUB: |
michael@0 | 1806 | return BINOP_SUB; |
michael@0 | 1807 | case PNK_STAR: |
michael@0 | 1808 | return BINOP_STAR; |
michael@0 | 1809 | case PNK_DIV: |
michael@0 | 1810 | return BINOP_DIV; |
michael@0 | 1811 | case PNK_MOD: |
michael@0 | 1812 | return BINOP_MOD; |
michael@0 | 1813 | case PNK_BITOR: |
michael@0 | 1814 | return BINOP_BITOR; |
michael@0 | 1815 | case PNK_BITXOR: |
michael@0 | 1816 | return BINOP_BITXOR; |
michael@0 | 1817 | case PNK_BITAND: |
michael@0 | 1818 | return BINOP_BITAND; |
michael@0 | 1819 | case PNK_IN: |
michael@0 | 1820 | return BINOP_IN; |
michael@0 | 1821 | case PNK_INSTANCEOF: |
michael@0 | 1822 | return BINOP_INSTANCEOF; |
michael@0 | 1823 | default: |
michael@0 | 1824 | return BINOP_ERR; |
michael@0 | 1825 | } |
michael@0 | 1826 | } |
michael@0 | 1827 | |
michael@0 | 1828 | bool |
michael@0 | 1829 | ASTSerializer::statements(ParseNode *pn, NodeVector &elts) |
michael@0 | 1830 | { |
michael@0 | 1831 | JS_ASSERT(pn->isKind(PNK_STATEMENTLIST)); |
michael@0 | 1832 | JS_ASSERT(pn->isArity(PN_LIST)); |
michael@0 | 1833 | |
michael@0 | 1834 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 1835 | return false; |
michael@0 | 1836 | |
michael@0 | 1837 | for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { |
michael@0 | 1838 | JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); |
michael@0 | 1839 | |
michael@0 | 1840 | RootedValue elt(cx); |
michael@0 | 1841 | if (!sourceElement(next, &elt)) |
michael@0 | 1842 | return false; |
michael@0 | 1843 | elts.infallibleAppend(elt); |
michael@0 | 1844 | } |
michael@0 | 1845 | |
michael@0 | 1846 | return true; |
michael@0 | 1847 | } |
michael@0 | 1848 | |
michael@0 | 1849 | bool |
michael@0 | 1850 | ASTSerializer::expressions(ParseNode *pn, NodeVector &elts) |
michael@0 | 1851 | { |
michael@0 | 1852 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 1853 | return false; |
michael@0 | 1854 | |
michael@0 | 1855 | for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { |
michael@0 | 1856 | JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); |
michael@0 | 1857 | |
michael@0 | 1858 | RootedValue elt(cx); |
michael@0 | 1859 | if (!expression(next, &elt)) |
michael@0 | 1860 | return false; |
michael@0 | 1861 | elts.infallibleAppend(elt); |
michael@0 | 1862 | } |
michael@0 | 1863 | |
michael@0 | 1864 | return true; |
michael@0 | 1865 | } |
michael@0 | 1866 | |
michael@0 | 1867 | bool |
michael@0 | 1868 | ASTSerializer::blockStatement(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 1869 | { |
michael@0 | 1870 | JS_ASSERT(pn->isKind(PNK_STATEMENTLIST)); |
michael@0 | 1871 | |
michael@0 | 1872 | NodeVector stmts(cx); |
michael@0 | 1873 | return statements(pn, stmts) && |
michael@0 | 1874 | builder.blockStatement(stmts, &pn->pn_pos, dst); |
michael@0 | 1875 | } |
michael@0 | 1876 | |
michael@0 | 1877 | bool |
michael@0 | 1878 | ASTSerializer::program(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 1879 | { |
michael@0 | 1880 | JS_ASSERT(parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin) == lineno); |
michael@0 | 1881 | |
michael@0 | 1882 | NodeVector stmts(cx); |
michael@0 | 1883 | return statements(pn, stmts) && |
michael@0 | 1884 | builder.program(stmts, &pn->pn_pos, dst); |
michael@0 | 1885 | } |
michael@0 | 1886 | |
michael@0 | 1887 | bool |
michael@0 | 1888 | ASTSerializer::sourceElement(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 1889 | { |
michael@0 | 1890 | /* SpiderMonkey allows declarations even in pure statement contexts. */ |
michael@0 | 1891 | return statement(pn, dst); |
michael@0 | 1892 | } |
michael@0 | 1893 | |
michael@0 | 1894 | bool |
michael@0 | 1895 | ASTSerializer::declaration(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 1896 | { |
michael@0 | 1897 | JS_ASSERT(pn->isKind(PNK_FUNCTION) || |
michael@0 | 1898 | pn->isKind(PNK_VAR) || |
michael@0 | 1899 | pn->isKind(PNK_LET) || |
michael@0 | 1900 | pn->isKind(PNK_CONST)); |
michael@0 | 1901 | |
michael@0 | 1902 | switch (pn->getKind()) { |
michael@0 | 1903 | case PNK_FUNCTION: |
michael@0 | 1904 | return function(pn, AST_FUNC_DECL, dst); |
michael@0 | 1905 | |
michael@0 | 1906 | case PNK_VAR: |
michael@0 | 1907 | case PNK_CONST: |
michael@0 | 1908 | return variableDeclaration(pn, false, dst); |
michael@0 | 1909 | |
michael@0 | 1910 | default: |
michael@0 | 1911 | JS_ASSERT(pn->isKind(PNK_LET)); |
michael@0 | 1912 | return variableDeclaration(pn, true, dst); |
michael@0 | 1913 | } |
michael@0 | 1914 | } |
michael@0 | 1915 | |
michael@0 | 1916 | bool |
michael@0 | 1917 | ASTSerializer::variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst) |
michael@0 | 1918 | { |
michael@0 | 1919 | JS_ASSERT(let ? pn->isKind(PNK_LET) : (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))); |
michael@0 | 1920 | |
michael@0 | 1921 | /* Later updated to VARDECL_CONST if we find a PND_CONST declarator. */ |
michael@0 | 1922 | VarDeclKind kind = let ? VARDECL_LET : VARDECL_VAR; |
michael@0 | 1923 | |
michael@0 | 1924 | NodeVector dtors(cx); |
michael@0 | 1925 | if (!dtors.reserve(pn->pn_count)) |
michael@0 | 1926 | return false; |
michael@0 | 1927 | for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { |
michael@0 | 1928 | RootedValue child(cx); |
michael@0 | 1929 | if (!variableDeclarator(next, &kind, &child)) |
michael@0 | 1930 | return false; |
michael@0 | 1931 | dtors.infallibleAppend(child); |
michael@0 | 1932 | } |
michael@0 | 1933 | return builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst); |
michael@0 | 1934 | } |
michael@0 | 1935 | |
michael@0 | 1936 | bool |
michael@0 | 1937 | ASTSerializer::variableDeclarator(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) |
michael@0 | 1938 | { |
michael@0 | 1939 | ParseNode *pnleft; |
michael@0 | 1940 | ParseNode *pnright; |
michael@0 | 1941 | |
michael@0 | 1942 | if (pn->isKind(PNK_NAME)) { |
michael@0 | 1943 | pnleft = pn; |
michael@0 | 1944 | pnright = pn->isUsed() ? nullptr : pn->pn_expr; |
michael@0 | 1945 | JS_ASSERT_IF(pnright, pn->pn_pos.encloses(pnright->pn_pos)); |
michael@0 | 1946 | } else if (pn->isKind(PNK_ASSIGN)) { |
michael@0 | 1947 | pnleft = pn->pn_left; |
michael@0 | 1948 | pnright = pn->pn_right; |
michael@0 | 1949 | JS_ASSERT(pn->pn_pos.encloses(pnleft->pn_pos)); |
michael@0 | 1950 | JS_ASSERT(pn->pn_pos.encloses(pnright->pn_pos)); |
michael@0 | 1951 | } else { |
michael@0 | 1952 | /* This happens for a destructuring declarator in a for-in/of loop. */ |
michael@0 | 1953 | pnleft = pn; |
michael@0 | 1954 | pnright = nullptr; |
michael@0 | 1955 | } |
michael@0 | 1956 | |
michael@0 | 1957 | RootedValue left(cx), right(cx); |
michael@0 | 1958 | return pattern(pnleft, pkind, &left) && |
michael@0 | 1959 | optExpression(pnright, &right) && |
michael@0 | 1960 | builder.variableDeclarator(left, right, &pn->pn_pos, dst); |
michael@0 | 1961 | } |
michael@0 | 1962 | |
michael@0 | 1963 | bool |
michael@0 | 1964 | ASTSerializer::let(ParseNode *pn, bool expr, MutableHandleValue dst) |
michael@0 | 1965 | { |
michael@0 | 1966 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 1967 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 1968 | |
michael@0 | 1969 | ParseNode *letHead = pn->pn_left; |
michael@0 | 1970 | LOCAL_ASSERT(letHead->isArity(PN_LIST)); |
michael@0 | 1971 | |
michael@0 | 1972 | ParseNode *letBody = pn->pn_right; |
michael@0 | 1973 | LOCAL_ASSERT(letBody->isKind(PNK_LEXICALSCOPE)); |
michael@0 | 1974 | |
michael@0 | 1975 | NodeVector dtors(cx); |
michael@0 | 1976 | if (!dtors.reserve(letHead->pn_count)) |
michael@0 | 1977 | return false; |
michael@0 | 1978 | |
michael@0 | 1979 | VarDeclKind kind = VARDECL_LET_HEAD; |
michael@0 | 1980 | |
michael@0 | 1981 | for (ParseNode *next = letHead->pn_head; next; next = next->pn_next) { |
michael@0 | 1982 | RootedValue child(cx); |
michael@0 | 1983 | /* |
michael@0 | 1984 | * Unlike in |variableDeclaration|, this does not update |kind|; since let-heads do |
michael@0 | 1985 | * not contain const declarations, declarators should never have PND_CONST set. |
michael@0 | 1986 | */ |
michael@0 | 1987 | if (!variableDeclarator(next, &kind, &child)) |
michael@0 | 1988 | return false; |
michael@0 | 1989 | dtors.infallibleAppend(child); |
michael@0 | 1990 | } |
michael@0 | 1991 | |
michael@0 | 1992 | RootedValue v(cx); |
michael@0 | 1993 | return expr |
michael@0 | 1994 | ? expression(letBody->pn_expr, &v) && |
michael@0 | 1995 | builder.letExpression(dtors, v, &pn->pn_pos, dst) |
michael@0 | 1996 | : statement(letBody->pn_expr, &v) && |
michael@0 | 1997 | builder.letStatement(dtors, v, &pn->pn_pos, dst); |
michael@0 | 1998 | } |
michael@0 | 1999 | |
michael@0 | 2000 | bool |
michael@0 | 2001 | ASTSerializer::importDeclaration(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2002 | { |
michael@0 | 2003 | JS_ASSERT(pn->isKind(PNK_IMPORT)); |
michael@0 | 2004 | JS_ASSERT(pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST)); |
michael@0 | 2005 | JS_ASSERT(pn->pn_right->isKind(PNK_STRING)); |
michael@0 | 2006 | |
michael@0 | 2007 | NodeVector elts(cx); |
michael@0 | 2008 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 2009 | return false; |
michael@0 | 2010 | |
michael@0 | 2011 | for (ParseNode *next = pn->pn_left->pn_head; next; next = next->pn_next) { |
michael@0 | 2012 | RootedValue elt(cx); |
michael@0 | 2013 | if (!importSpecifier(next, &elt)) |
michael@0 | 2014 | return false; |
michael@0 | 2015 | elts.infallibleAppend(elt); |
michael@0 | 2016 | } |
michael@0 | 2017 | |
michael@0 | 2018 | RootedValue moduleSpec(cx); |
michael@0 | 2019 | return literal(pn->pn_right, &moduleSpec) && |
michael@0 | 2020 | builder.importDeclaration(elts, moduleSpec, &pn->pn_pos, dst); |
michael@0 | 2021 | } |
michael@0 | 2022 | |
michael@0 | 2023 | bool |
michael@0 | 2024 | ASTSerializer::importSpecifier(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2025 | { |
michael@0 | 2026 | JS_ASSERT(pn->isKind(PNK_IMPORT_SPEC)); |
michael@0 | 2027 | |
michael@0 | 2028 | RootedValue importName(cx); |
michael@0 | 2029 | RootedValue bindingName(cx); |
michael@0 | 2030 | return identifier(pn->pn_left, &importName) && |
michael@0 | 2031 | identifier(pn->pn_right, &bindingName) && |
michael@0 | 2032 | builder.importSpecifier(importName, bindingName, &pn->pn_pos, dst); |
michael@0 | 2033 | } |
michael@0 | 2034 | |
michael@0 | 2035 | bool |
michael@0 | 2036 | ASTSerializer::exportDeclaration(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2037 | { |
michael@0 | 2038 | JS_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_FROM)); |
michael@0 | 2039 | JS_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_right->isKind(PNK_STRING)); |
michael@0 | 2040 | |
michael@0 | 2041 | RootedValue decl(cx, NullValue()); |
michael@0 | 2042 | NodeVector elts(cx); |
michael@0 | 2043 | |
michael@0 | 2044 | ParseNode *kid = pn->isKind(PNK_EXPORT) ? pn->pn_kid : pn->pn_left; |
michael@0 | 2045 | switch (ParseNodeKind kind = kid->getKind()) { |
michael@0 | 2046 | case PNK_EXPORT_SPEC_LIST: |
michael@0 | 2047 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 2048 | return false; |
michael@0 | 2049 | |
michael@0 | 2050 | for (ParseNode *next = pn->pn_left->pn_head; next; next = next->pn_next) { |
michael@0 | 2051 | RootedValue elt(cx); |
michael@0 | 2052 | if (next->isKind(PNK_EXPORT_SPEC)) { |
michael@0 | 2053 | if (!exportSpecifier(next, &elt)) |
michael@0 | 2054 | return false; |
michael@0 | 2055 | } else { |
michael@0 | 2056 | if (!builder.exportBatchSpecifier(&pn->pn_pos, &elt)) |
michael@0 | 2057 | return false; |
michael@0 | 2058 | } |
michael@0 | 2059 | elts.infallibleAppend(elt); |
michael@0 | 2060 | } |
michael@0 | 2061 | break; |
michael@0 | 2062 | |
michael@0 | 2063 | case PNK_FUNCTION: |
michael@0 | 2064 | if (!function(kid, AST_FUNC_DECL, &decl)) |
michael@0 | 2065 | return false; |
michael@0 | 2066 | break; |
michael@0 | 2067 | |
michael@0 | 2068 | case PNK_VAR: |
michael@0 | 2069 | case PNK_CONST: |
michael@0 | 2070 | case PNK_LET: |
michael@0 | 2071 | if (!variableDeclaration(kid, kind == PNK_LET, &decl)) |
michael@0 | 2072 | return false; |
michael@0 | 2073 | break; |
michael@0 | 2074 | |
michael@0 | 2075 | default: |
michael@0 | 2076 | LOCAL_NOT_REACHED("unexpected statement type"); |
michael@0 | 2077 | } |
michael@0 | 2078 | |
michael@0 | 2079 | RootedValue moduleSpec(cx, NullValue()); |
michael@0 | 2080 | if (pn->isKind(PNK_EXPORT_FROM) && !literal(pn->pn_right, &moduleSpec)) |
michael@0 | 2081 | return false; |
michael@0 | 2082 | |
michael@0 | 2083 | return builder.exportDeclaration(decl, elts, moduleSpec, &pn->pn_pos, dst); |
michael@0 | 2084 | } |
michael@0 | 2085 | |
michael@0 | 2086 | bool |
michael@0 | 2087 | ASTSerializer::exportSpecifier(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2088 | { |
michael@0 | 2089 | JS_ASSERT(pn->isKind(PNK_EXPORT_SPEC)); |
michael@0 | 2090 | |
michael@0 | 2091 | RootedValue bindingName(cx); |
michael@0 | 2092 | RootedValue exportName(cx); |
michael@0 | 2093 | return identifier(pn->pn_left, &bindingName) && |
michael@0 | 2094 | identifier(pn->pn_right, &exportName) && |
michael@0 | 2095 | builder.exportSpecifier(bindingName, exportName, &pn->pn_pos, dst); |
michael@0 | 2096 | } |
michael@0 | 2097 | |
michael@0 | 2098 | bool |
michael@0 | 2099 | ASTSerializer::switchCase(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2100 | { |
michael@0 | 2101 | JS_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2102 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2103 | |
michael@0 | 2104 | NodeVector stmts(cx); |
michael@0 | 2105 | |
michael@0 | 2106 | RootedValue expr(cx); |
michael@0 | 2107 | |
michael@0 | 2108 | return optExpression(pn->pn_left, &expr) && |
michael@0 | 2109 | statements(pn->pn_right, stmts) && |
michael@0 | 2110 | builder.switchCase(expr, stmts, &pn->pn_pos, dst); |
michael@0 | 2111 | } |
michael@0 | 2112 | |
michael@0 | 2113 | bool |
michael@0 | 2114 | ASTSerializer::switchStatement(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2115 | { |
michael@0 | 2116 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2117 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2118 | |
michael@0 | 2119 | RootedValue disc(cx); |
michael@0 | 2120 | |
michael@0 | 2121 | if (!expression(pn->pn_left, &disc)) |
michael@0 | 2122 | return false; |
michael@0 | 2123 | |
michael@0 | 2124 | ParseNode *listNode; |
michael@0 | 2125 | bool lexical; |
michael@0 | 2126 | |
michael@0 | 2127 | if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) { |
michael@0 | 2128 | listNode = pn->pn_right->pn_expr; |
michael@0 | 2129 | lexical = true; |
michael@0 | 2130 | } else { |
michael@0 | 2131 | listNode = pn->pn_right; |
michael@0 | 2132 | lexical = false; |
michael@0 | 2133 | } |
michael@0 | 2134 | |
michael@0 | 2135 | NodeVector cases(cx); |
michael@0 | 2136 | if (!cases.reserve(listNode->pn_count)) |
michael@0 | 2137 | return false; |
michael@0 | 2138 | |
michael@0 | 2139 | for (ParseNode *next = listNode->pn_head; next; next = next->pn_next) { |
michael@0 | 2140 | RootedValue child(cx); |
michael@0 | 2141 | if (!switchCase(next, &child)) |
michael@0 | 2142 | return false; |
michael@0 | 2143 | cases.infallibleAppend(child); |
michael@0 | 2144 | } |
michael@0 | 2145 | |
michael@0 | 2146 | return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst); |
michael@0 | 2147 | } |
michael@0 | 2148 | |
michael@0 | 2149 | bool |
michael@0 | 2150 | ASTSerializer::catchClause(ParseNode *pn, bool *isGuarded, MutableHandleValue dst) |
michael@0 | 2151 | { |
michael@0 | 2152 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); |
michael@0 | 2153 | JS_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); |
michael@0 | 2154 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); |
michael@0 | 2155 | |
michael@0 | 2156 | RootedValue var(cx), guard(cx), body(cx); |
michael@0 | 2157 | |
michael@0 | 2158 | if (!pattern(pn->pn_kid1, nullptr, &var) || |
michael@0 | 2159 | !optExpression(pn->pn_kid2, &guard)) { |
michael@0 | 2160 | return false; |
michael@0 | 2161 | } |
michael@0 | 2162 | |
michael@0 | 2163 | *isGuarded = !guard.isMagic(JS_SERIALIZE_NO_NODE); |
michael@0 | 2164 | |
michael@0 | 2165 | return statement(pn->pn_kid3, &body) && |
michael@0 | 2166 | builder.catchClause(var, guard, body, &pn->pn_pos, dst); |
michael@0 | 2167 | } |
michael@0 | 2168 | |
michael@0 | 2169 | bool |
michael@0 | 2170 | ASTSerializer::tryStatement(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2171 | { |
michael@0 | 2172 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); |
michael@0 | 2173 | JS_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); |
michael@0 | 2174 | JS_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); |
michael@0 | 2175 | |
michael@0 | 2176 | RootedValue body(cx); |
michael@0 | 2177 | if (!statement(pn->pn_kid1, &body)) |
michael@0 | 2178 | return false; |
michael@0 | 2179 | |
michael@0 | 2180 | NodeVector guarded(cx); |
michael@0 | 2181 | RootedValue unguarded(cx, NullValue()); |
michael@0 | 2182 | |
michael@0 | 2183 | if (pn->pn_kid2) { |
michael@0 | 2184 | if (!guarded.reserve(pn->pn_kid2->pn_count)) |
michael@0 | 2185 | return false; |
michael@0 | 2186 | |
michael@0 | 2187 | for (ParseNode *next = pn->pn_kid2->pn_head; next; next = next->pn_next) { |
michael@0 | 2188 | RootedValue clause(cx); |
michael@0 | 2189 | bool isGuarded; |
michael@0 | 2190 | if (!catchClause(next->pn_expr, &isGuarded, &clause)) |
michael@0 | 2191 | return false; |
michael@0 | 2192 | if (isGuarded) |
michael@0 | 2193 | guarded.infallibleAppend(clause); |
michael@0 | 2194 | else |
michael@0 | 2195 | unguarded = clause; |
michael@0 | 2196 | } |
michael@0 | 2197 | } |
michael@0 | 2198 | |
michael@0 | 2199 | RootedValue finally(cx); |
michael@0 | 2200 | return optStatement(pn->pn_kid3, &finally) && |
michael@0 | 2201 | builder.tryStatement(body, guarded, unguarded, finally, &pn->pn_pos, dst); |
michael@0 | 2202 | } |
michael@0 | 2203 | |
michael@0 | 2204 | bool |
michael@0 | 2205 | ASTSerializer::forInit(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2206 | { |
michael@0 | 2207 | if (!pn) { |
michael@0 | 2208 | dst.setMagic(JS_SERIALIZE_NO_NODE); |
michael@0 | 2209 | return true; |
michael@0 | 2210 | } |
michael@0 | 2211 | |
michael@0 | 2212 | return (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) |
michael@0 | 2213 | ? variableDeclaration(pn, false, dst) |
michael@0 | 2214 | : expression(pn, dst); |
michael@0 | 2215 | } |
michael@0 | 2216 | |
michael@0 | 2217 | bool |
michael@0 | 2218 | ASTSerializer::forOf(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, |
michael@0 | 2219 | MutableHandleValue dst) |
michael@0 | 2220 | { |
michael@0 | 2221 | RootedValue expr(cx); |
michael@0 | 2222 | |
michael@0 | 2223 | return expression(head->pn_kid3, &expr) && |
michael@0 | 2224 | builder.forOfStatement(var, expr, stmt, &loop->pn_pos, dst); |
michael@0 | 2225 | } |
michael@0 | 2226 | |
michael@0 | 2227 | bool |
michael@0 | 2228 | ASTSerializer::forIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, |
michael@0 | 2229 | MutableHandleValue dst) |
michael@0 | 2230 | { |
michael@0 | 2231 | RootedValue expr(cx); |
michael@0 | 2232 | bool isForEach = loop->pn_iflags & JSITER_FOREACH; |
michael@0 | 2233 | |
michael@0 | 2234 | return expression(head->pn_kid3, &expr) && |
michael@0 | 2235 | builder.forInStatement(var, expr, stmt, isForEach, &loop->pn_pos, dst); |
michael@0 | 2236 | } |
michael@0 | 2237 | |
michael@0 | 2238 | bool |
michael@0 | 2239 | ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2240 | { |
michael@0 | 2241 | JS_CHECK_RECURSION(cx, return false); |
michael@0 | 2242 | switch (pn->getKind()) { |
michael@0 | 2243 | case PNK_FUNCTION: |
michael@0 | 2244 | case PNK_VAR: |
michael@0 | 2245 | case PNK_CONST: |
michael@0 | 2246 | return declaration(pn, dst); |
michael@0 | 2247 | |
michael@0 | 2248 | case PNK_LET: |
michael@0 | 2249 | return pn->isArity(PN_BINARY) |
michael@0 | 2250 | ? let(pn, false, dst) |
michael@0 | 2251 | : declaration(pn, dst); |
michael@0 | 2252 | |
michael@0 | 2253 | case PNK_IMPORT: |
michael@0 | 2254 | return importDeclaration(pn, dst); |
michael@0 | 2255 | |
michael@0 | 2256 | case PNK_EXPORT: |
michael@0 | 2257 | case PNK_EXPORT_FROM: |
michael@0 | 2258 | return exportDeclaration(pn, dst); |
michael@0 | 2259 | |
michael@0 | 2260 | case PNK_NAME: |
michael@0 | 2261 | LOCAL_ASSERT(pn->isUsed()); |
michael@0 | 2262 | return statement(pn->pn_lexdef, dst); |
michael@0 | 2263 | |
michael@0 | 2264 | case PNK_SEMI: |
michael@0 | 2265 | if (pn->pn_kid) { |
michael@0 | 2266 | RootedValue expr(cx); |
michael@0 | 2267 | return expression(pn->pn_kid, &expr) && |
michael@0 | 2268 | builder.expressionStatement(expr, &pn->pn_pos, dst); |
michael@0 | 2269 | } |
michael@0 | 2270 | return builder.emptyStatement(&pn->pn_pos, dst); |
michael@0 | 2271 | |
michael@0 | 2272 | case PNK_LEXICALSCOPE: |
michael@0 | 2273 | pn = pn->pn_expr; |
michael@0 | 2274 | if (!pn->isKind(PNK_STATEMENTLIST)) |
michael@0 | 2275 | return statement(pn, dst); |
michael@0 | 2276 | /* FALL THROUGH */ |
michael@0 | 2277 | |
michael@0 | 2278 | case PNK_STATEMENTLIST: |
michael@0 | 2279 | return blockStatement(pn, dst); |
michael@0 | 2280 | |
michael@0 | 2281 | case PNK_IF: |
michael@0 | 2282 | { |
michael@0 | 2283 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); |
michael@0 | 2284 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); |
michael@0 | 2285 | JS_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); |
michael@0 | 2286 | |
michael@0 | 2287 | RootedValue test(cx), cons(cx), alt(cx); |
michael@0 | 2288 | |
michael@0 | 2289 | return expression(pn->pn_kid1, &test) && |
michael@0 | 2290 | statement(pn->pn_kid2, &cons) && |
michael@0 | 2291 | optStatement(pn->pn_kid3, &alt) && |
michael@0 | 2292 | builder.ifStatement(test, cons, alt, &pn->pn_pos, dst); |
michael@0 | 2293 | } |
michael@0 | 2294 | |
michael@0 | 2295 | case PNK_SWITCH: |
michael@0 | 2296 | return switchStatement(pn, dst); |
michael@0 | 2297 | |
michael@0 | 2298 | case PNK_TRY: |
michael@0 | 2299 | return tryStatement(pn, dst); |
michael@0 | 2300 | |
michael@0 | 2301 | case PNK_WITH: |
michael@0 | 2302 | case PNK_WHILE: |
michael@0 | 2303 | { |
michael@0 | 2304 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2305 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2306 | |
michael@0 | 2307 | RootedValue expr(cx), stmt(cx); |
michael@0 | 2308 | |
michael@0 | 2309 | return expression(pn->pn_left, &expr) && |
michael@0 | 2310 | statement(pn->pn_right, &stmt) && |
michael@0 | 2311 | (pn->isKind(PNK_WITH) |
michael@0 | 2312 | ? builder.withStatement(expr, stmt, &pn->pn_pos, dst) |
michael@0 | 2313 | : builder.whileStatement(expr, stmt, &pn->pn_pos, dst)); |
michael@0 | 2314 | } |
michael@0 | 2315 | |
michael@0 | 2316 | case PNK_DOWHILE: |
michael@0 | 2317 | { |
michael@0 | 2318 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2319 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2320 | |
michael@0 | 2321 | RootedValue stmt(cx), test(cx); |
michael@0 | 2322 | |
michael@0 | 2323 | return statement(pn->pn_left, &stmt) && |
michael@0 | 2324 | expression(pn->pn_right, &test) && |
michael@0 | 2325 | builder.doWhileStatement(stmt, test, &pn->pn_pos, dst); |
michael@0 | 2326 | } |
michael@0 | 2327 | |
michael@0 | 2328 | case PNK_FOR: |
michael@0 | 2329 | { |
michael@0 | 2330 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2331 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2332 | |
michael@0 | 2333 | ParseNode *head = pn->pn_left; |
michael@0 | 2334 | |
michael@0 | 2335 | JS_ASSERT_IF(head->pn_kid1, head->pn_pos.encloses(head->pn_kid1->pn_pos)); |
michael@0 | 2336 | JS_ASSERT_IF(head->pn_kid2, head->pn_pos.encloses(head->pn_kid2->pn_pos)); |
michael@0 | 2337 | JS_ASSERT_IF(head->pn_kid3, head->pn_pos.encloses(head->pn_kid3->pn_pos)); |
michael@0 | 2338 | |
michael@0 | 2339 | RootedValue stmt(cx); |
michael@0 | 2340 | if (!statement(pn->pn_right, &stmt)) |
michael@0 | 2341 | return false; |
michael@0 | 2342 | |
michael@0 | 2343 | if (head->isKind(PNK_FORIN)) { |
michael@0 | 2344 | RootedValue var(cx); |
michael@0 | 2345 | return (!head->pn_kid1 |
michael@0 | 2346 | ? pattern(head->pn_kid2, nullptr, &var) |
michael@0 | 2347 | : head->pn_kid1->isKind(PNK_LEXICALSCOPE) |
michael@0 | 2348 | ? variableDeclaration(head->pn_kid1->pn_expr, true, &var) |
michael@0 | 2349 | : variableDeclaration(head->pn_kid1, false, &var)) && |
michael@0 | 2350 | forIn(pn, head, var, stmt, dst); |
michael@0 | 2351 | } |
michael@0 | 2352 | |
michael@0 | 2353 | if (head->isKind(PNK_FOROF)) { |
michael@0 | 2354 | RootedValue var(cx); |
michael@0 | 2355 | return (!head->pn_kid1 |
michael@0 | 2356 | ? pattern(head->pn_kid2, nullptr, &var) |
michael@0 | 2357 | : head->pn_kid1->isKind(PNK_LEXICALSCOPE) |
michael@0 | 2358 | ? variableDeclaration(head->pn_kid1->pn_expr, true, &var) |
michael@0 | 2359 | : variableDeclaration(head->pn_kid1, false, &var)) && |
michael@0 | 2360 | forOf(pn, head, var, stmt, dst); |
michael@0 | 2361 | } |
michael@0 | 2362 | |
michael@0 | 2363 | RootedValue init(cx), test(cx), update(cx); |
michael@0 | 2364 | |
michael@0 | 2365 | return forInit(head->pn_kid1, &init) && |
michael@0 | 2366 | optExpression(head->pn_kid2, &test) && |
michael@0 | 2367 | optExpression(head->pn_kid3, &update) && |
michael@0 | 2368 | builder.forStatement(init, test, update, stmt, &pn->pn_pos, dst); |
michael@0 | 2369 | } |
michael@0 | 2370 | |
michael@0 | 2371 | /* Synthesized by the parser when a for-in loop contains a variable initializer. */ |
michael@0 | 2372 | case PNK_SEQ: |
michael@0 | 2373 | { |
michael@0 | 2374 | LOCAL_ASSERT(pn->pn_count == 2); |
michael@0 | 2375 | |
michael@0 | 2376 | ParseNode *prelude = pn->pn_head; |
michael@0 | 2377 | ParseNode *loop = prelude->pn_next; |
michael@0 | 2378 | |
michael@0 | 2379 | LOCAL_ASSERT(prelude->isKind(PNK_VAR) && loop->isKind(PNK_FOR)); |
michael@0 | 2380 | |
michael@0 | 2381 | RootedValue var(cx); |
michael@0 | 2382 | if (!variableDeclaration(prelude, false, &var)) |
michael@0 | 2383 | return false; |
michael@0 | 2384 | |
michael@0 | 2385 | ParseNode *head = loop->pn_left; |
michael@0 | 2386 | JS_ASSERT(head->isKind(PNK_FORIN)); |
michael@0 | 2387 | |
michael@0 | 2388 | RootedValue stmt(cx); |
michael@0 | 2389 | |
michael@0 | 2390 | return statement(loop->pn_right, &stmt) && forIn(loop, head, var, stmt, dst); |
michael@0 | 2391 | } |
michael@0 | 2392 | |
michael@0 | 2393 | case PNK_BREAK: |
michael@0 | 2394 | case PNK_CONTINUE: |
michael@0 | 2395 | { |
michael@0 | 2396 | RootedValue label(cx); |
michael@0 | 2397 | RootedAtom pnAtom(cx, pn->pn_atom); |
michael@0 | 2398 | return optIdentifier(pnAtom, nullptr, &label) && |
michael@0 | 2399 | (pn->isKind(PNK_BREAK) |
michael@0 | 2400 | ? builder.breakStatement(label, &pn->pn_pos, dst) |
michael@0 | 2401 | : builder.continueStatement(label, &pn->pn_pos, dst)); |
michael@0 | 2402 | } |
michael@0 | 2403 | |
michael@0 | 2404 | case PNK_LABEL: |
michael@0 | 2405 | { |
michael@0 | 2406 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos)); |
michael@0 | 2407 | |
michael@0 | 2408 | RootedValue label(cx), stmt(cx); |
michael@0 | 2409 | RootedAtom pnAtom(cx, pn->as<LabeledStatement>().label()); |
michael@0 | 2410 | return identifier(pnAtom, nullptr, &label) && |
michael@0 | 2411 | statement(pn->pn_expr, &stmt) && |
michael@0 | 2412 | builder.labeledStatement(label, stmt, &pn->pn_pos, dst); |
michael@0 | 2413 | } |
michael@0 | 2414 | |
michael@0 | 2415 | case PNK_THROW: |
michael@0 | 2416 | case PNK_RETURN: |
michael@0 | 2417 | { |
michael@0 | 2418 | JS_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos)); |
michael@0 | 2419 | |
michael@0 | 2420 | RootedValue arg(cx); |
michael@0 | 2421 | |
michael@0 | 2422 | return optExpression(pn->pn_kid, &arg) && |
michael@0 | 2423 | (pn->isKind(PNK_THROW) |
michael@0 | 2424 | ? builder.throwStatement(arg, &pn->pn_pos, dst) |
michael@0 | 2425 | : builder.returnStatement(arg, &pn->pn_pos, dst)); |
michael@0 | 2426 | } |
michael@0 | 2427 | |
michael@0 | 2428 | case PNK_DEBUGGER: |
michael@0 | 2429 | return builder.debuggerStatement(&pn->pn_pos, dst); |
michael@0 | 2430 | |
michael@0 | 2431 | case PNK_NOP: |
michael@0 | 2432 | return builder.emptyStatement(&pn->pn_pos, dst); |
michael@0 | 2433 | |
michael@0 | 2434 | default: |
michael@0 | 2435 | LOCAL_NOT_REACHED("unexpected statement type"); |
michael@0 | 2436 | } |
michael@0 | 2437 | } |
michael@0 | 2438 | |
michael@0 | 2439 | bool |
michael@0 | 2440 | ASTSerializer::leftAssociate(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2441 | { |
michael@0 | 2442 | JS_ASSERT(pn->isArity(PN_LIST)); |
michael@0 | 2443 | JS_ASSERT(pn->pn_count >= 1); |
michael@0 | 2444 | |
michael@0 | 2445 | ParseNodeKind kind = pn->getKind(); |
michael@0 | 2446 | bool lor = kind == PNK_OR; |
michael@0 | 2447 | bool logop = lor || (kind == PNK_AND); |
michael@0 | 2448 | |
michael@0 | 2449 | ParseNode *head = pn->pn_head; |
michael@0 | 2450 | RootedValue left(cx); |
michael@0 | 2451 | if (!expression(head, &left)) |
michael@0 | 2452 | return false; |
michael@0 | 2453 | for (ParseNode *next = head->pn_next; next; next = next->pn_next) { |
michael@0 | 2454 | RootedValue right(cx); |
michael@0 | 2455 | if (!expression(next, &right)) |
michael@0 | 2456 | return false; |
michael@0 | 2457 | |
michael@0 | 2458 | TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end); |
michael@0 | 2459 | |
michael@0 | 2460 | if (logop) { |
michael@0 | 2461 | if (!builder.logicalExpression(lor, left, right, &subpos, &left)) |
michael@0 | 2462 | return false; |
michael@0 | 2463 | } else { |
michael@0 | 2464 | BinaryOperator op = binop(pn->getKind(), pn->getOp()); |
michael@0 | 2465 | LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); |
michael@0 | 2466 | |
michael@0 | 2467 | if (!builder.binaryExpression(op, left, right, &subpos, &left)) |
michael@0 | 2468 | return false; |
michael@0 | 2469 | } |
michael@0 | 2470 | } |
michael@0 | 2471 | |
michael@0 | 2472 | dst.set(left); |
michael@0 | 2473 | return true; |
michael@0 | 2474 | } |
michael@0 | 2475 | |
michael@0 | 2476 | bool |
michael@0 | 2477 | ASTSerializer::comprehensionBlock(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2478 | { |
michael@0 | 2479 | LOCAL_ASSERT(pn->isArity(PN_BINARY)); |
michael@0 | 2480 | |
michael@0 | 2481 | ParseNode *in = pn->pn_left; |
michael@0 | 2482 | |
michael@0 | 2483 | LOCAL_ASSERT(in && (in->isKind(PNK_FORIN) || in->isKind(PNK_FOROF))); |
michael@0 | 2484 | |
michael@0 | 2485 | bool isForEach = pn->pn_iflags & JSITER_FOREACH; |
michael@0 | 2486 | bool isForOf = in->isKind(PNK_FOROF); |
michael@0 | 2487 | |
michael@0 | 2488 | RootedValue patt(cx), src(cx); |
michael@0 | 2489 | return pattern(in->pn_kid2, nullptr, &patt) && |
michael@0 | 2490 | expression(in->pn_kid3, &src) && |
michael@0 | 2491 | builder.comprehensionBlock(patt, src, isForEach, isForOf, &in->pn_pos, dst); |
michael@0 | 2492 | } |
michael@0 | 2493 | |
michael@0 | 2494 | bool |
michael@0 | 2495 | ASTSerializer::comprehension(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2496 | { |
michael@0 | 2497 | LOCAL_ASSERT(pn->isKind(PNK_FOR)); |
michael@0 | 2498 | |
michael@0 | 2499 | NodeVector blocks(cx); |
michael@0 | 2500 | |
michael@0 | 2501 | ParseNode *next = pn; |
michael@0 | 2502 | while (next->isKind(PNK_FOR)) { |
michael@0 | 2503 | RootedValue block(cx); |
michael@0 | 2504 | if (!comprehensionBlock(next, &block) || !blocks.append(block)) |
michael@0 | 2505 | return false; |
michael@0 | 2506 | next = next->pn_right; |
michael@0 | 2507 | } |
michael@0 | 2508 | |
michael@0 | 2509 | RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE)); |
michael@0 | 2510 | |
michael@0 | 2511 | if (next->isKind(PNK_IF)) { |
michael@0 | 2512 | if (!optExpression(next->pn_kid1, &filter)) |
michael@0 | 2513 | return false; |
michael@0 | 2514 | next = next->pn_kid2; |
michael@0 | 2515 | } else if (next->isKind(PNK_STATEMENTLIST) && next->pn_count == 0) { |
michael@0 | 2516 | /* FoldConstants optimized away the push. */ |
michael@0 | 2517 | NodeVector empty(cx); |
michael@0 | 2518 | return builder.arrayExpression(empty, &pn->pn_pos, dst); |
michael@0 | 2519 | } |
michael@0 | 2520 | |
michael@0 | 2521 | LOCAL_ASSERT(next->isKind(PNK_ARRAYPUSH)); |
michael@0 | 2522 | |
michael@0 | 2523 | RootedValue body(cx); |
michael@0 | 2524 | |
michael@0 | 2525 | return expression(next->pn_kid, &body) && |
michael@0 | 2526 | builder.comprehensionExpression(body, blocks, filter, &pn->pn_pos, dst); |
michael@0 | 2527 | } |
michael@0 | 2528 | |
michael@0 | 2529 | bool |
michael@0 | 2530 | ASTSerializer::generatorExpression(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2531 | { |
michael@0 | 2532 | LOCAL_ASSERT(pn->isKind(PNK_FOR)); |
michael@0 | 2533 | |
michael@0 | 2534 | NodeVector blocks(cx); |
michael@0 | 2535 | |
michael@0 | 2536 | ParseNode *next = pn; |
michael@0 | 2537 | while (next->isKind(PNK_FOR)) { |
michael@0 | 2538 | RootedValue block(cx); |
michael@0 | 2539 | if (!comprehensionBlock(next, &block) || !blocks.append(block)) |
michael@0 | 2540 | return false; |
michael@0 | 2541 | next = next->pn_right; |
michael@0 | 2542 | } |
michael@0 | 2543 | |
michael@0 | 2544 | RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE)); |
michael@0 | 2545 | |
michael@0 | 2546 | if (next->isKind(PNK_IF)) { |
michael@0 | 2547 | if (!optExpression(next->pn_kid1, &filter)) |
michael@0 | 2548 | return false; |
michael@0 | 2549 | next = next->pn_kid2; |
michael@0 | 2550 | } |
michael@0 | 2551 | |
michael@0 | 2552 | LOCAL_ASSERT(next->isKind(PNK_SEMI) && |
michael@0 | 2553 | next->pn_kid->isKind(PNK_YIELD) && |
michael@0 | 2554 | next->pn_kid->pn_kid); |
michael@0 | 2555 | |
michael@0 | 2556 | RootedValue body(cx); |
michael@0 | 2557 | |
michael@0 | 2558 | return expression(next->pn_kid->pn_kid, &body) && |
michael@0 | 2559 | builder.generatorExpression(body, blocks, filter, &pn->pn_pos, dst); |
michael@0 | 2560 | } |
michael@0 | 2561 | |
michael@0 | 2562 | bool |
michael@0 | 2563 | ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2564 | { |
michael@0 | 2565 | JS_CHECK_RECURSION(cx, return false); |
michael@0 | 2566 | switch (pn->getKind()) { |
michael@0 | 2567 | case PNK_FUNCTION: |
michael@0 | 2568 | { |
michael@0 | 2569 | ASTType type = pn->pn_funbox->function()->isArrow() ? AST_ARROW_EXPR : AST_FUNC_EXPR; |
michael@0 | 2570 | return function(pn, type, dst); |
michael@0 | 2571 | } |
michael@0 | 2572 | |
michael@0 | 2573 | case PNK_COMMA: |
michael@0 | 2574 | { |
michael@0 | 2575 | NodeVector exprs(cx); |
michael@0 | 2576 | return expressions(pn, exprs) && |
michael@0 | 2577 | builder.sequenceExpression(exprs, &pn->pn_pos, dst); |
michael@0 | 2578 | } |
michael@0 | 2579 | |
michael@0 | 2580 | case PNK_CONDITIONAL: |
michael@0 | 2581 | { |
michael@0 | 2582 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); |
michael@0 | 2583 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); |
michael@0 | 2584 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); |
michael@0 | 2585 | |
michael@0 | 2586 | RootedValue test(cx), cons(cx), alt(cx); |
michael@0 | 2587 | |
michael@0 | 2588 | return expression(pn->pn_kid1, &test) && |
michael@0 | 2589 | expression(pn->pn_kid2, &cons) && |
michael@0 | 2590 | expression(pn->pn_kid3, &alt) && |
michael@0 | 2591 | builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst); |
michael@0 | 2592 | } |
michael@0 | 2593 | |
michael@0 | 2594 | case PNK_OR: |
michael@0 | 2595 | case PNK_AND: |
michael@0 | 2596 | { |
michael@0 | 2597 | if (pn->isArity(PN_BINARY)) { |
michael@0 | 2598 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2599 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2600 | |
michael@0 | 2601 | RootedValue left(cx), right(cx); |
michael@0 | 2602 | return expression(pn->pn_left, &left) && |
michael@0 | 2603 | expression(pn->pn_right, &right) && |
michael@0 | 2604 | builder.logicalExpression(pn->isKind(PNK_OR), left, right, &pn->pn_pos, dst); |
michael@0 | 2605 | } |
michael@0 | 2606 | return leftAssociate(pn, dst); |
michael@0 | 2607 | } |
michael@0 | 2608 | |
michael@0 | 2609 | case PNK_PREINCREMENT: |
michael@0 | 2610 | case PNK_PREDECREMENT: |
michael@0 | 2611 | { |
michael@0 | 2612 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); |
michael@0 | 2613 | |
michael@0 | 2614 | bool inc = pn->isKind(PNK_PREINCREMENT); |
michael@0 | 2615 | RootedValue expr(cx); |
michael@0 | 2616 | return expression(pn->pn_kid, &expr) && |
michael@0 | 2617 | builder.updateExpression(expr, inc, true, &pn->pn_pos, dst); |
michael@0 | 2618 | } |
michael@0 | 2619 | |
michael@0 | 2620 | case PNK_POSTINCREMENT: |
michael@0 | 2621 | case PNK_POSTDECREMENT: |
michael@0 | 2622 | { |
michael@0 | 2623 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); |
michael@0 | 2624 | |
michael@0 | 2625 | bool inc = pn->isKind(PNK_POSTINCREMENT); |
michael@0 | 2626 | RootedValue expr(cx); |
michael@0 | 2627 | return expression(pn->pn_kid, &expr) && |
michael@0 | 2628 | builder.updateExpression(expr, inc, false, &pn->pn_pos, dst); |
michael@0 | 2629 | } |
michael@0 | 2630 | |
michael@0 | 2631 | case PNK_ASSIGN: |
michael@0 | 2632 | case PNK_ADDASSIGN: |
michael@0 | 2633 | case PNK_SUBASSIGN: |
michael@0 | 2634 | case PNK_BITORASSIGN: |
michael@0 | 2635 | case PNK_BITXORASSIGN: |
michael@0 | 2636 | case PNK_BITANDASSIGN: |
michael@0 | 2637 | case PNK_LSHASSIGN: |
michael@0 | 2638 | case PNK_RSHASSIGN: |
michael@0 | 2639 | case PNK_URSHASSIGN: |
michael@0 | 2640 | case PNK_MULASSIGN: |
michael@0 | 2641 | case PNK_DIVASSIGN: |
michael@0 | 2642 | case PNK_MODASSIGN: |
michael@0 | 2643 | { |
michael@0 | 2644 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2645 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2646 | |
michael@0 | 2647 | AssignmentOperator op = aop(pn->getOp()); |
michael@0 | 2648 | LOCAL_ASSERT(op > AOP_ERR && op < AOP_LIMIT); |
michael@0 | 2649 | |
michael@0 | 2650 | RootedValue lhs(cx), rhs(cx); |
michael@0 | 2651 | return pattern(pn->pn_left, nullptr, &lhs) && |
michael@0 | 2652 | expression(pn->pn_right, &rhs) && |
michael@0 | 2653 | builder.assignmentExpression(op, lhs, rhs, &pn->pn_pos, dst); |
michael@0 | 2654 | } |
michael@0 | 2655 | |
michael@0 | 2656 | case PNK_ADD: |
michael@0 | 2657 | case PNK_SUB: |
michael@0 | 2658 | case PNK_STRICTEQ: |
michael@0 | 2659 | case PNK_EQ: |
michael@0 | 2660 | case PNK_STRICTNE: |
michael@0 | 2661 | case PNK_NE: |
michael@0 | 2662 | case PNK_LT: |
michael@0 | 2663 | case PNK_LE: |
michael@0 | 2664 | case PNK_GT: |
michael@0 | 2665 | case PNK_GE: |
michael@0 | 2666 | case PNK_LSH: |
michael@0 | 2667 | case PNK_RSH: |
michael@0 | 2668 | case PNK_URSH: |
michael@0 | 2669 | case PNK_STAR: |
michael@0 | 2670 | case PNK_DIV: |
michael@0 | 2671 | case PNK_MOD: |
michael@0 | 2672 | case PNK_BITOR: |
michael@0 | 2673 | case PNK_BITXOR: |
michael@0 | 2674 | case PNK_BITAND: |
michael@0 | 2675 | case PNK_IN: |
michael@0 | 2676 | case PNK_INSTANCEOF: |
michael@0 | 2677 | if (pn->isArity(PN_BINARY)) { |
michael@0 | 2678 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2679 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2680 | |
michael@0 | 2681 | BinaryOperator op = binop(pn->getKind(), pn->getOp()); |
michael@0 | 2682 | LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); |
michael@0 | 2683 | |
michael@0 | 2684 | RootedValue left(cx), right(cx); |
michael@0 | 2685 | return expression(pn->pn_left, &left) && |
michael@0 | 2686 | expression(pn->pn_right, &right) && |
michael@0 | 2687 | builder.binaryExpression(op, left, right, &pn->pn_pos, dst); |
michael@0 | 2688 | } |
michael@0 | 2689 | return leftAssociate(pn, dst); |
michael@0 | 2690 | |
michael@0 | 2691 | case PNK_DELETE: |
michael@0 | 2692 | case PNK_TYPEOF: |
michael@0 | 2693 | case PNK_VOID: |
michael@0 | 2694 | case PNK_NOT: |
michael@0 | 2695 | case PNK_BITNOT: |
michael@0 | 2696 | case PNK_POS: |
michael@0 | 2697 | case PNK_NEG: { |
michael@0 | 2698 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); |
michael@0 | 2699 | |
michael@0 | 2700 | UnaryOperator op = unop(pn->getKind(), pn->getOp()); |
michael@0 | 2701 | LOCAL_ASSERT(op > UNOP_ERR && op < UNOP_LIMIT); |
michael@0 | 2702 | |
michael@0 | 2703 | RootedValue expr(cx); |
michael@0 | 2704 | return expression(pn->pn_kid, &expr) && |
michael@0 | 2705 | builder.unaryExpression(op, expr, &pn->pn_pos, dst); |
michael@0 | 2706 | } |
michael@0 | 2707 | |
michael@0 | 2708 | #if JS_HAS_GENERATOR_EXPRS |
michael@0 | 2709 | case PNK_GENEXP: |
michael@0 | 2710 | return generatorExpression(pn->generatorExpr(), dst); |
michael@0 | 2711 | #endif |
michael@0 | 2712 | |
michael@0 | 2713 | case PNK_NEW: |
michael@0 | 2714 | case PNK_CALL: |
michael@0 | 2715 | { |
michael@0 | 2716 | ParseNode *next = pn->pn_head; |
michael@0 | 2717 | JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); |
michael@0 | 2718 | |
michael@0 | 2719 | RootedValue callee(cx); |
michael@0 | 2720 | if (!expression(next, &callee)) |
michael@0 | 2721 | return false; |
michael@0 | 2722 | |
michael@0 | 2723 | NodeVector args(cx); |
michael@0 | 2724 | if (!args.reserve(pn->pn_count - 1)) |
michael@0 | 2725 | return false; |
michael@0 | 2726 | |
michael@0 | 2727 | for (next = next->pn_next; next; next = next->pn_next) { |
michael@0 | 2728 | JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); |
michael@0 | 2729 | |
michael@0 | 2730 | RootedValue arg(cx); |
michael@0 | 2731 | if (!expression(next, &arg)) |
michael@0 | 2732 | return false; |
michael@0 | 2733 | args.infallibleAppend(arg); |
michael@0 | 2734 | } |
michael@0 | 2735 | |
michael@0 | 2736 | return pn->isKind(PNK_NEW) |
michael@0 | 2737 | ? builder.newExpression(callee, args, &pn->pn_pos, dst) |
michael@0 | 2738 | |
michael@0 | 2739 | : builder.callExpression(callee, args, &pn->pn_pos, dst); |
michael@0 | 2740 | } |
michael@0 | 2741 | |
michael@0 | 2742 | case PNK_DOT: |
michael@0 | 2743 | { |
michael@0 | 2744 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos)); |
michael@0 | 2745 | |
michael@0 | 2746 | RootedValue expr(cx), id(cx); |
michael@0 | 2747 | RootedAtom pnAtom(cx, pn->pn_atom); |
michael@0 | 2748 | return expression(pn->pn_expr, &expr) && |
michael@0 | 2749 | identifier(pnAtom, nullptr, &id) && |
michael@0 | 2750 | builder.memberExpression(false, expr, id, &pn->pn_pos, dst); |
michael@0 | 2751 | } |
michael@0 | 2752 | |
michael@0 | 2753 | case PNK_ELEM: |
michael@0 | 2754 | { |
michael@0 | 2755 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); |
michael@0 | 2756 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); |
michael@0 | 2757 | |
michael@0 | 2758 | RootedValue left(cx), right(cx); |
michael@0 | 2759 | return expression(pn->pn_left, &left) && |
michael@0 | 2760 | expression(pn->pn_right, &right) && |
michael@0 | 2761 | builder.memberExpression(true, left, right, &pn->pn_pos, dst); |
michael@0 | 2762 | } |
michael@0 | 2763 | |
michael@0 | 2764 | case PNK_ARRAY: |
michael@0 | 2765 | { |
michael@0 | 2766 | NodeVector elts(cx); |
michael@0 | 2767 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 2768 | return false; |
michael@0 | 2769 | |
michael@0 | 2770 | for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { |
michael@0 | 2771 | JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); |
michael@0 | 2772 | |
michael@0 | 2773 | if (next->isKind(PNK_ELISION)) { |
michael@0 | 2774 | elts.infallibleAppend(NullValue()); |
michael@0 | 2775 | } else { |
michael@0 | 2776 | RootedValue expr(cx); |
michael@0 | 2777 | if (!expression(next, &expr)) |
michael@0 | 2778 | return false; |
michael@0 | 2779 | elts.infallibleAppend(expr); |
michael@0 | 2780 | } |
michael@0 | 2781 | } |
michael@0 | 2782 | |
michael@0 | 2783 | return builder.arrayExpression(elts, &pn->pn_pos, dst); |
michael@0 | 2784 | } |
michael@0 | 2785 | |
michael@0 | 2786 | case PNK_SPREAD: |
michael@0 | 2787 | { |
michael@0 | 2788 | RootedValue expr(cx); |
michael@0 | 2789 | return expression(pn->pn_kid, &expr) && |
michael@0 | 2790 | builder.spreadExpression(expr, &pn->pn_pos, dst); |
michael@0 | 2791 | } |
michael@0 | 2792 | |
michael@0 | 2793 | case PNK_OBJECT: |
michael@0 | 2794 | { |
michael@0 | 2795 | /* The parser notes any uninitialized properties by setting the PNX_DESTRUCT flag. */ |
michael@0 | 2796 | if (pn->pn_xflags & PNX_DESTRUCT) { |
michael@0 | 2797 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_OBJECT_INIT); |
michael@0 | 2798 | return false; |
michael@0 | 2799 | } |
michael@0 | 2800 | NodeVector elts(cx); |
michael@0 | 2801 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 2802 | return false; |
michael@0 | 2803 | |
michael@0 | 2804 | for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { |
michael@0 | 2805 | JS_ASSERT(pn->pn_pos.encloses(next->pn_pos)); |
michael@0 | 2806 | |
michael@0 | 2807 | RootedValue prop(cx); |
michael@0 | 2808 | if (!property(next, &prop)) |
michael@0 | 2809 | return false; |
michael@0 | 2810 | elts.infallibleAppend(prop); |
michael@0 | 2811 | } |
michael@0 | 2812 | |
michael@0 | 2813 | return builder.objectExpression(elts, &pn->pn_pos, dst); |
michael@0 | 2814 | } |
michael@0 | 2815 | |
michael@0 | 2816 | case PNK_NAME: |
michael@0 | 2817 | return identifier(pn, dst); |
michael@0 | 2818 | |
michael@0 | 2819 | case PNK_THIS: |
michael@0 | 2820 | return builder.thisExpression(&pn->pn_pos, dst); |
michael@0 | 2821 | |
michael@0 | 2822 | case PNK_STRING: |
michael@0 | 2823 | case PNK_REGEXP: |
michael@0 | 2824 | case PNK_NUMBER: |
michael@0 | 2825 | case PNK_TRUE: |
michael@0 | 2826 | case PNK_FALSE: |
michael@0 | 2827 | case PNK_NULL: |
michael@0 | 2828 | return literal(pn, dst); |
michael@0 | 2829 | |
michael@0 | 2830 | case PNK_YIELD_STAR: |
michael@0 | 2831 | { |
michael@0 | 2832 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); |
michael@0 | 2833 | |
michael@0 | 2834 | RootedValue arg(cx); |
michael@0 | 2835 | return expression(pn->pn_kid, &arg) && |
michael@0 | 2836 | builder.yieldExpression(arg, Delegating, &pn->pn_pos, dst); |
michael@0 | 2837 | } |
michael@0 | 2838 | |
michael@0 | 2839 | case PNK_YIELD: |
michael@0 | 2840 | { |
michael@0 | 2841 | JS_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos)); |
michael@0 | 2842 | |
michael@0 | 2843 | RootedValue arg(cx); |
michael@0 | 2844 | return optExpression(pn->pn_kid, &arg) && |
michael@0 | 2845 | builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst); |
michael@0 | 2846 | } |
michael@0 | 2847 | |
michael@0 | 2848 | case PNK_ARRAYCOMP: |
michael@0 | 2849 | JS_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos)); |
michael@0 | 2850 | |
michael@0 | 2851 | /* NB: it's no longer the case that pn_count could be 2. */ |
michael@0 | 2852 | LOCAL_ASSERT(pn->pn_count == 1); |
michael@0 | 2853 | LOCAL_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE)); |
michael@0 | 2854 | |
michael@0 | 2855 | return comprehension(pn->pn_head->pn_expr, dst); |
michael@0 | 2856 | |
michael@0 | 2857 | case PNK_LET: |
michael@0 | 2858 | return let(pn, true, dst); |
michael@0 | 2859 | |
michael@0 | 2860 | default: |
michael@0 | 2861 | LOCAL_NOT_REACHED("unexpected expression type"); |
michael@0 | 2862 | } |
michael@0 | 2863 | } |
michael@0 | 2864 | |
michael@0 | 2865 | bool |
michael@0 | 2866 | ASTSerializer::propertyName(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2867 | { |
michael@0 | 2868 | if (pn->isKind(PNK_NAME)) |
michael@0 | 2869 | return identifier(pn, dst); |
michael@0 | 2870 | |
michael@0 | 2871 | LOCAL_ASSERT(pn->isKind(PNK_STRING) || pn->isKind(PNK_NUMBER)); |
michael@0 | 2872 | |
michael@0 | 2873 | return literal(pn, dst); |
michael@0 | 2874 | } |
michael@0 | 2875 | |
michael@0 | 2876 | bool |
michael@0 | 2877 | ASTSerializer::property(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2878 | { |
michael@0 | 2879 | PropKind kind; |
michael@0 | 2880 | switch (pn->getOp()) { |
michael@0 | 2881 | case JSOP_INITPROP: |
michael@0 | 2882 | kind = PROP_INIT; |
michael@0 | 2883 | break; |
michael@0 | 2884 | |
michael@0 | 2885 | case JSOP_INITPROP_GETTER: |
michael@0 | 2886 | kind = PROP_GETTER; |
michael@0 | 2887 | break; |
michael@0 | 2888 | |
michael@0 | 2889 | case JSOP_INITPROP_SETTER: |
michael@0 | 2890 | kind = PROP_SETTER; |
michael@0 | 2891 | break; |
michael@0 | 2892 | |
michael@0 | 2893 | default: |
michael@0 | 2894 | LOCAL_NOT_REACHED("unexpected object-literal property"); |
michael@0 | 2895 | } |
michael@0 | 2896 | |
michael@0 | 2897 | RootedValue key(cx), val(cx); |
michael@0 | 2898 | return propertyName(pn->pn_left, &key) && |
michael@0 | 2899 | expression(pn->pn_right, &val) && |
michael@0 | 2900 | builder.propertyInitializer(key, val, kind, &pn->pn_pos, dst); |
michael@0 | 2901 | } |
michael@0 | 2902 | |
michael@0 | 2903 | bool |
michael@0 | 2904 | ASTSerializer::literal(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 2905 | { |
michael@0 | 2906 | RootedValue val(cx); |
michael@0 | 2907 | switch (pn->getKind()) { |
michael@0 | 2908 | case PNK_STRING: |
michael@0 | 2909 | val.setString(pn->pn_atom); |
michael@0 | 2910 | break; |
michael@0 | 2911 | |
michael@0 | 2912 | case PNK_REGEXP: |
michael@0 | 2913 | { |
michael@0 | 2914 | RootedObject re1(cx, pn->as<RegExpLiteral>().objbox()->object); |
michael@0 | 2915 | LOCAL_ASSERT(re1 && re1->is<RegExpObject>()); |
michael@0 | 2916 | |
michael@0 | 2917 | RootedObject re2(cx, CloneRegExpObject(cx, re1)); |
michael@0 | 2918 | if (!re2) |
michael@0 | 2919 | return false; |
michael@0 | 2920 | |
michael@0 | 2921 | val.setObject(*re2); |
michael@0 | 2922 | break; |
michael@0 | 2923 | } |
michael@0 | 2924 | |
michael@0 | 2925 | case PNK_NUMBER: |
michael@0 | 2926 | val.setNumber(pn->pn_dval); |
michael@0 | 2927 | break; |
michael@0 | 2928 | |
michael@0 | 2929 | case PNK_NULL: |
michael@0 | 2930 | val.setNull(); |
michael@0 | 2931 | break; |
michael@0 | 2932 | |
michael@0 | 2933 | case PNK_TRUE: |
michael@0 | 2934 | val.setBoolean(true); |
michael@0 | 2935 | break; |
michael@0 | 2936 | |
michael@0 | 2937 | case PNK_FALSE: |
michael@0 | 2938 | val.setBoolean(false); |
michael@0 | 2939 | break; |
michael@0 | 2940 | |
michael@0 | 2941 | default: |
michael@0 | 2942 | LOCAL_NOT_REACHED("unexpected literal type"); |
michael@0 | 2943 | } |
michael@0 | 2944 | |
michael@0 | 2945 | return builder.literal(val, &pn->pn_pos, dst); |
michael@0 | 2946 | } |
michael@0 | 2947 | |
michael@0 | 2948 | bool |
michael@0 | 2949 | ASTSerializer::arrayPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) |
michael@0 | 2950 | { |
michael@0 | 2951 | JS_ASSERT(pn->isKind(PNK_ARRAY)); |
michael@0 | 2952 | |
michael@0 | 2953 | NodeVector elts(cx); |
michael@0 | 2954 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 2955 | return false; |
michael@0 | 2956 | |
michael@0 | 2957 | for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { |
michael@0 | 2958 | if (next->isKind(PNK_ELISION)) { |
michael@0 | 2959 | elts.infallibleAppend(NullValue()); |
michael@0 | 2960 | } else { |
michael@0 | 2961 | RootedValue patt(cx); |
michael@0 | 2962 | if (!pattern(next, pkind, &patt)) |
michael@0 | 2963 | return false; |
michael@0 | 2964 | elts.infallibleAppend(patt); |
michael@0 | 2965 | } |
michael@0 | 2966 | } |
michael@0 | 2967 | |
michael@0 | 2968 | return builder.arrayPattern(elts, &pn->pn_pos, dst); |
michael@0 | 2969 | } |
michael@0 | 2970 | |
michael@0 | 2971 | bool |
michael@0 | 2972 | ASTSerializer::objectPattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) |
michael@0 | 2973 | { |
michael@0 | 2974 | JS_ASSERT(pn->isKind(PNK_OBJECT)); |
michael@0 | 2975 | |
michael@0 | 2976 | NodeVector elts(cx); |
michael@0 | 2977 | if (!elts.reserve(pn->pn_count)) |
michael@0 | 2978 | return false; |
michael@0 | 2979 | |
michael@0 | 2980 | for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { |
michael@0 | 2981 | LOCAL_ASSERT(next->isOp(JSOP_INITPROP)); |
michael@0 | 2982 | |
michael@0 | 2983 | RootedValue key(cx), patt(cx), prop(cx); |
michael@0 | 2984 | if (!propertyName(next->pn_left, &key) || |
michael@0 | 2985 | !pattern(next->pn_right, pkind, &patt) || |
michael@0 | 2986 | !builder.propertyPattern(key, patt, &next->pn_pos, &prop)) { |
michael@0 | 2987 | return false; |
michael@0 | 2988 | } |
michael@0 | 2989 | |
michael@0 | 2990 | elts.infallibleAppend(prop); |
michael@0 | 2991 | } |
michael@0 | 2992 | |
michael@0 | 2993 | return builder.objectPattern(elts, &pn->pn_pos, dst); |
michael@0 | 2994 | } |
michael@0 | 2995 | |
michael@0 | 2996 | bool |
michael@0 | 2997 | ASTSerializer::pattern(ParseNode *pn, VarDeclKind *pkind, MutableHandleValue dst) |
michael@0 | 2998 | { |
michael@0 | 2999 | JS_CHECK_RECURSION(cx, return false); |
michael@0 | 3000 | switch (pn->getKind()) { |
michael@0 | 3001 | case PNK_OBJECT: |
michael@0 | 3002 | return objectPattern(pn, pkind, dst); |
michael@0 | 3003 | |
michael@0 | 3004 | case PNK_ARRAY: |
michael@0 | 3005 | return arrayPattern(pn, pkind, dst); |
michael@0 | 3006 | |
michael@0 | 3007 | case PNK_NAME: |
michael@0 | 3008 | if (pkind && (pn->pn_dflags & PND_CONST)) |
michael@0 | 3009 | *pkind = VARDECL_CONST; |
michael@0 | 3010 | /* FALL THROUGH */ |
michael@0 | 3011 | |
michael@0 | 3012 | default: |
michael@0 | 3013 | return expression(pn, dst); |
michael@0 | 3014 | } |
michael@0 | 3015 | } |
michael@0 | 3016 | |
michael@0 | 3017 | bool |
michael@0 | 3018 | ASTSerializer::identifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 3019 | { |
michael@0 | 3020 | RootedValue atomContentsVal(cx, unrootedAtomContents(atom)); |
michael@0 | 3021 | return builder.identifier(atomContentsVal, pos, dst); |
michael@0 | 3022 | } |
michael@0 | 3023 | |
michael@0 | 3024 | bool |
michael@0 | 3025 | ASTSerializer::identifier(ParseNode *pn, MutableHandleValue dst) |
michael@0 | 3026 | { |
michael@0 | 3027 | LOCAL_ASSERT(pn->isArity(PN_NAME) || pn->isArity(PN_NULLARY)); |
michael@0 | 3028 | LOCAL_ASSERT(pn->pn_atom); |
michael@0 | 3029 | |
michael@0 | 3030 | RootedAtom pnAtom(cx, pn->pn_atom); |
michael@0 | 3031 | return identifier(pnAtom, &pn->pn_pos, dst); |
michael@0 | 3032 | } |
michael@0 | 3033 | |
michael@0 | 3034 | bool |
michael@0 | 3035 | ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst) |
michael@0 | 3036 | { |
michael@0 | 3037 | RootedFunction func(cx, pn->pn_funbox->function()); |
michael@0 | 3038 | |
michael@0 | 3039 | // FIXME: Provide more information (legacy generator vs star generator). |
michael@0 | 3040 | bool isGenerator = pn->pn_funbox->isGenerator(); |
michael@0 | 3041 | |
michael@0 | 3042 | bool isExpression = |
michael@0 | 3043 | #if JS_HAS_EXPR_CLOSURES |
michael@0 | 3044 | func->isExprClosure(); |
michael@0 | 3045 | #else |
michael@0 | 3046 | false; |
michael@0 | 3047 | #endif |
michael@0 | 3048 | |
michael@0 | 3049 | RootedValue id(cx); |
michael@0 | 3050 | RootedAtom funcAtom(cx, func->atom()); |
michael@0 | 3051 | if (!optIdentifier(funcAtom, nullptr, &id)) |
michael@0 | 3052 | return false; |
michael@0 | 3053 | |
michael@0 | 3054 | NodeVector args(cx); |
michael@0 | 3055 | NodeVector defaults(cx); |
michael@0 | 3056 | |
michael@0 | 3057 | RootedValue body(cx), rest(cx); |
michael@0 | 3058 | if (func->hasRest()) |
michael@0 | 3059 | rest.setUndefined(); |
michael@0 | 3060 | else |
michael@0 | 3061 | rest.setNull(); |
michael@0 | 3062 | return functionArgsAndBody(pn->pn_body, args, defaults, &body, &rest) && |
michael@0 | 3063 | builder.function(type, &pn->pn_pos, id, args, defaults, body, |
michael@0 | 3064 | rest, isGenerator, isExpression, dst); |
michael@0 | 3065 | } |
michael@0 | 3066 | |
michael@0 | 3067 | bool |
michael@0 | 3068 | ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, NodeVector &defaults, |
michael@0 | 3069 | MutableHandleValue body, MutableHandleValue rest) |
michael@0 | 3070 | { |
michael@0 | 3071 | ParseNode *pnargs; |
michael@0 | 3072 | ParseNode *pnbody; |
michael@0 | 3073 | |
michael@0 | 3074 | /* Extract the args and body separately. */ |
michael@0 | 3075 | if (pn->isKind(PNK_ARGSBODY)) { |
michael@0 | 3076 | pnargs = pn; |
michael@0 | 3077 | pnbody = pn->last(); |
michael@0 | 3078 | } else { |
michael@0 | 3079 | pnargs = nullptr; |
michael@0 | 3080 | pnbody = pn; |
michael@0 | 3081 | } |
michael@0 | 3082 | |
michael@0 | 3083 | ParseNode *pndestruct; |
michael@0 | 3084 | |
michael@0 | 3085 | /* Extract the destructuring assignments. */ |
michael@0 | 3086 | if (pnbody->isArity(PN_LIST) && (pnbody->pn_xflags & PNX_DESTRUCT)) { |
michael@0 | 3087 | ParseNode *head = pnbody->pn_head; |
michael@0 | 3088 | LOCAL_ASSERT(head && head->isKind(PNK_SEMI)); |
michael@0 | 3089 | |
michael@0 | 3090 | pndestruct = head->pn_kid; |
michael@0 | 3091 | LOCAL_ASSERT(pndestruct); |
michael@0 | 3092 | LOCAL_ASSERT(pndestruct->isKind(PNK_VAR)); |
michael@0 | 3093 | } else { |
michael@0 | 3094 | pndestruct = nullptr; |
michael@0 | 3095 | } |
michael@0 | 3096 | |
michael@0 | 3097 | /* Serialize the arguments and body. */ |
michael@0 | 3098 | switch (pnbody->getKind()) { |
michael@0 | 3099 | case PNK_RETURN: /* expression closure, no destructured args */ |
michael@0 | 3100 | return functionArgs(pn, pnargs, nullptr, pnbody, args, defaults, rest) && |
michael@0 | 3101 | expression(pnbody->pn_kid, body); |
michael@0 | 3102 | |
michael@0 | 3103 | case PNK_SEQ: /* expression closure with destructured args */ |
michael@0 | 3104 | { |
michael@0 | 3105 | ParseNode *pnstart = pnbody->pn_head->pn_next; |
michael@0 | 3106 | LOCAL_ASSERT(pnstart && pnstart->isKind(PNK_RETURN)); |
michael@0 | 3107 | |
michael@0 | 3108 | return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) && |
michael@0 | 3109 | expression(pnstart->pn_kid, body); |
michael@0 | 3110 | } |
michael@0 | 3111 | |
michael@0 | 3112 | case PNK_STATEMENTLIST: /* statement closure */ |
michael@0 | 3113 | { |
michael@0 | 3114 | ParseNode *pnstart = (pnbody->pn_xflags & PNX_DESTRUCT) |
michael@0 | 3115 | ? pnbody->pn_head->pn_next |
michael@0 | 3116 | : pnbody->pn_head; |
michael@0 | 3117 | |
michael@0 | 3118 | return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) && |
michael@0 | 3119 | functionBody(pnstart, &pnbody->pn_pos, body); |
michael@0 | 3120 | } |
michael@0 | 3121 | |
michael@0 | 3122 | default: |
michael@0 | 3123 | LOCAL_NOT_REACHED("unexpected function contents"); |
michael@0 | 3124 | } |
michael@0 | 3125 | } |
michael@0 | 3126 | |
michael@0 | 3127 | bool |
michael@0 | 3128 | ASTSerializer::functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestruct, |
michael@0 | 3129 | ParseNode *pnbody, NodeVector &args, NodeVector &defaults, |
michael@0 | 3130 | MutableHandleValue rest) |
michael@0 | 3131 | { |
michael@0 | 3132 | uint32_t i = 0; |
michael@0 | 3133 | ParseNode *arg = pnargs ? pnargs->pn_head : nullptr; |
michael@0 | 3134 | ParseNode *destruct = pndestruct ? pndestruct->pn_head : nullptr; |
michael@0 | 3135 | RootedValue node(cx); |
michael@0 | 3136 | |
michael@0 | 3137 | /* |
michael@0 | 3138 | * Arguments are found in potentially two different places: 1) the |
michael@0 | 3139 | * argsbody sequence (which ends with the body node), or 2) a |
michael@0 | 3140 | * destructuring initialization at the beginning of the body. Loop |
michael@0 | 3141 | * |arg| through the argsbody and |destruct| through the initial |
michael@0 | 3142 | * destructuring assignments, stopping only when we've exhausted |
michael@0 | 3143 | * both. |
michael@0 | 3144 | */ |
michael@0 | 3145 | while ((arg && arg != pnbody) || destruct) { |
michael@0 | 3146 | if (destruct && destruct->pn_right->frameSlot() == i) { |
michael@0 | 3147 | if (!pattern(destruct->pn_left, nullptr, &node) || !args.append(node)) |
michael@0 | 3148 | return false; |
michael@0 | 3149 | destruct = destruct->pn_next; |
michael@0 | 3150 | } else if (arg && arg != pnbody) { |
michael@0 | 3151 | /* |
michael@0 | 3152 | * We don't check that arg->frameSlot() == i since we |
michael@0 | 3153 | * can't call that method if the arg def has been turned |
michael@0 | 3154 | * into a use, e.g.: |
michael@0 | 3155 | * |
michael@0 | 3156 | * function(a) { function a() { } } |
michael@0 | 3157 | * |
michael@0 | 3158 | * There's no other way to ask a non-destructuring arg its |
michael@0 | 3159 | * index in the formals list, so we rely on the ability to |
michael@0 | 3160 | * ask destructuring args their index above. |
michael@0 | 3161 | */ |
michael@0 | 3162 | JS_ASSERT(arg->isKind(PNK_NAME) || arg->isKind(PNK_ASSIGN)); |
michael@0 | 3163 | ParseNode *argName = arg->isKind(PNK_NAME) ? arg : arg->pn_left; |
michael@0 | 3164 | if (!identifier(argName, &node)) |
michael@0 | 3165 | return false; |
michael@0 | 3166 | if (rest.isUndefined() && arg->pn_next == pnbody) |
michael@0 | 3167 | rest.setObject(node.toObject()); |
michael@0 | 3168 | else if (!args.append(node)) |
michael@0 | 3169 | return false; |
michael@0 | 3170 | if (arg->pn_dflags & PND_DEFAULT) { |
michael@0 | 3171 | ParseNode *expr = arg->isDefn() ? arg->expr() : arg->pn_kid->pn_right; |
michael@0 | 3172 | RootedValue def(cx); |
michael@0 | 3173 | if (!expression(expr, &def) || !defaults.append(def)) |
michael@0 | 3174 | return false; |
michael@0 | 3175 | } |
michael@0 | 3176 | arg = arg->pn_next; |
michael@0 | 3177 | } else { |
michael@0 | 3178 | LOCAL_NOT_REACHED("missing function argument"); |
michael@0 | 3179 | } |
michael@0 | 3180 | ++i; |
michael@0 | 3181 | } |
michael@0 | 3182 | JS_ASSERT(!rest.isUndefined()); |
michael@0 | 3183 | |
michael@0 | 3184 | return true; |
michael@0 | 3185 | } |
michael@0 | 3186 | |
michael@0 | 3187 | bool |
michael@0 | 3188 | ASTSerializer::functionBody(ParseNode *pn, TokenPos *pos, MutableHandleValue dst) |
michael@0 | 3189 | { |
michael@0 | 3190 | NodeVector elts(cx); |
michael@0 | 3191 | |
michael@0 | 3192 | /* We aren't sure how many elements there are up front, so we'll check each append. */ |
michael@0 | 3193 | for (ParseNode *next = pn; next; next = next->pn_next) { |
michael@0 | 3194 | RootedValue child(cx); |
michael@0 | 3195 | if (!sourceElement(next, &child) || !elts.append(child)) |
michael@0 | 3196 | return false; |
michael@0 | 3197 | } |
michael@0 | 3198 | |
michael@0 | 3199 | return builder.blockStatement(elts, pos, dst); |
michael@0 | 3200 | } |
michael@0 | 3201 | |
michael@0 | 3202 | static bool |
michael@0 | 3203 | reflect_parse(JSContext *cx, uint32_t argc, jsval *vp) |
michael@0 | 3204 | { |
michael@0 | 3205 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 3206 | |
michael@0 | 3207 | if (args.length() < 1) { |
michael@0 | 3208 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, |
michael@0 | 3209 | "Reflect.parse", "0", "s"); |
michael@0 | 3210 | return false; |
michael@0 | 3211 | } |
michael@0 | 3212 | |
michael@0 | 3213 | RootedString src(cx, ToString<CanGC>(cx, args[0])); |
michael@0 | 3214 | if (!src) |
michael@0 | 3215 | return false; |
michael@0 | 3216 | |
michael@0 | 3217 | ScopedJSFreePtr<char> filename; |
michael@0 | 3218 | uint32_t lineno = 1; |
michael@0 | 3219 | bool loc = true; |
michael@0 | 3220 | |
michael@0 | 3221 | RootedObject builder(cx); |
michael@0 | 3222 | |
michael@0 | 3223 | RootedValue arg(cx, args.get(1)); |
michael@0 | 3224 | |
michael@0 | 3225 | if (!arg.isNullOrUndefined()) { |
michael@0 | 3226 | if (!arg.isObject()) { |
michael@0 | 3227 | js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, |
michael@0 | 3228 | JSDVG_SEARCH_STACK, arg, js::NullPtr(), |
michael@0 | 3229 | "not an object", nullptr); |
michael@0 | 3230 | return false; |
michael@0 | 3231 | } |
michael@0 | 3232 | |
michael@0 | 3233 | RootedObject config(cx, &arg.toObject()); |
michael@0 | 3234 | |
michael@0 | 3235 | RootedValue prop(cx); |
michael@0 | 3236 | |
michael@0 | 3237 | /* config.loc */ |
michael@0 | 3238 | RootedId locId(cx, NameToId(cx->names().loc)); |
michael@0 | 3239 | RootedValue trueVal(cx, BooleanValue(true)); |
michael@0 | 3240 | if (!GetPropertyDefault(cx, config, locId, trueVal, &prop)) |
michael@0 | 3241 | return false; |
michael@0 | 3242 | |
michael@0 | 3243 | loc = ToBoolean(prop); |
michael@0 | 3244 | |
michael@0 | 3245 | if (loc) { |
michael@0 | 3246 | /* config.source */ |
michael@0 | 3247 | RootedId sourceId(cx, NameToId(cx->names().source)); |
michael@0 | 3248 | RootedValue nullVal(cx, NullValue()); |
michael@0 | 3249 | if (!GetPropertyDefault(cx, config, sourceId, nullVal, &prop)) |
michael@0 | 3250 | return false; |
michael@0 | 3251 | |
michael@0 | 3252 | if (!prop.isNullOrUndefined()) { |
michael@0 | 3253 | RootedString str(cx, ToString<CanGC>(cx, prop)); |
michael@0 | 3254 | if (!str) |
michael@0 | 3255 | return false; |
michael@0 | 3256 | |
michael@0 | 3257 | size_t length = str->length(); |
michael@0 | 3258 | const jschar *chars = str->getChars(cx); |
michael@0 | 3259 | if (!chars) |
michael@0 | 3260 | return false; |
michael@0 | 3261 | |
michael@0 | 3262 | TwoByteChars tbchars(chars, length); |
michael@0 | 3263 | filename = LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str(); |
michael@0 | 3264 | if (!filename) |
michael@0 | 3265 | return false; |
michael@0 | 3266 | } |
michael@0 | 3267 | |
michael@0 | 3268 | /* config.line */ |
michael@0 | 3269 | RootedId lineId(cx, NameToId(cx->names().line)); |
michael@0 | 3270 | RootedValue oneValue(cx, Int32Value(1)); |
michael@0 | 3271 | if (!GetPropertyDefault(cx, config, lineId, oneValue, &prop) || |
michael@0 | 3272 | !ToUint32(cx, prop, &lineno)) { |
michael@0 | 3273 | return false; |
michael@0 | 3274 | } |
michael@0 | 3275 | } |
michael@0 | 3276 | |
michael@0 | 3277 | /* config.builder */ |
michael@0 | 3278 | RootedId builderId(cx, NameToId(cx->names().builder)); |
michael@0 | 3279 | RootedValue nullVal(cx, NullValue()); |
michael@0 | 3280 | if (!GetPropertyDefault(cx, config, builderId, nullVal, &prop)) |
michael@0 | 3281 | return false; |
michael@0 | 3282 | |
michael@0 | 3283 | if (!prop.isNullOrUndefined()) { |
michael@0 | 3284 | if (!prop.isObject()) { |
michael@0 | 3285 | js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, |
michael@0 | 3286 | JSDVG_SEARCH_STACK, prop, js::NullPtr(), |
michael@0 | 3287 | "not an object", nullptr); |
michael@0 | 3288 | return false; |
michael@0 | 3289 | } |
michael@0 | 3290 | builder = &prop.toObject(); |
michael@0 | 3291 | } |
michael@0 | 3292 | } |
michael@0 | 3293 | |
michael@0 | 3294 | /* Extract the builder methods first to report errors before parsing. */ |
michael@0 | 3295 | ASTSerializer serialize(cx, loc, filename, lineno); |
michael@0 | 3296 | if (!serialize.init(builder)) |
michael@0 | 3297 | return false; |
michael@0 | 3298 | |
michael@0 | 3299 | JSFlatString *flat = src->ensureFlat(cx); |
michael@0 | 3300 | if (!flat) |
michael@0 | 3301 | return false; |
michael@0 | 3302 | |
michael@0 | 3303 | CompileOptions options(cx); |
michael@0 | 3304 | options.setFileAndLine(filename, lineno); |
michael@0 | 3305 | options.setCanLazilyParse(false); |
michael@0 | 3306 | Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, flat->chars(), |
michael@0 | 3307 | flat->length(), /* foldConstants = */ false, nullptr, nullptr); |
michael@0 | 3308 | |
michael@0 | 3309 | serialize.setParser(&parser); |
michael@0 | 3310 | |
michael@0 | 3311 | ParseNode *pn = parser.parse(nullptr); |
michael@0 | 3312 | if (!pn) |
michael@0 | 3313 | return false; |
michael@0 | 3314 | |
michael@0 | 3315 | RootedValue val(cx); |
michael@0 | 3316 | if (!serialize.program(pn, &val)) { |
michael@0 | 3317 | args.rval().setNull(); |
michael@0 | 3318 | return false; |
michael@0 | 3319 | } |
michael@0 | 3320 | |
michael@0 | 3321 | args.rval().set(val); |
michael@0 | 3322 | return true; |
michael@0 | 3323 | } |
michael@0 | 3324 | |
michael@0 | 3325 | JS_PUBLIC_API(JSObject *) |
michael@0 | 3326 | JS_InitReflect(JSContext *cx, HandleObject obj) |
michael@0 | 3327 | { |
michael@0 | 3328 | static const JSFunctionSpec static_methods[] = { |
michael@0 | 3329 | JS_FN("parse", reflect_parse, 1, 0), |
michael@0 | 3330 | JS_FS_END |
michael@0 | 3331 | }; |
michael@0 | 3332 | |
michael@0 | 3333 | RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx)); |
michael@0 | 3334 | if (!proto) |
michael@0 | 3335 | return nullptr; |
michael@0 | 3336 | RootedObject Reflect(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, |
michael@0 | 3337 | obj, SingletonObject)); |
michael@0 | 3338 | if (!Reflect) |
michael@0 | 3339 | return nullptr; |
michael@0 | 3340 | |
michael@0 | 3341 | if (!JS_DefineProperty(cx, obj, "Reflect", Reflect, 0, |
michael@0 | 3342 | JS_PropertyStub, JS_StrictPropertyStub)) { |
michael@0 | 3343 | return nullptr; |
michael@0 | 3344 | } |
michael@0 | 3345 | |
michael@0 | 3346 | if (!JS_DefineFunctions(cx, Reflect, static_methods)) |
michael@0 | 3347 | return nullptr; |
michael@0 | 3348 | |
michael@0 | 3349 | return Reflect; |
michael@0 | 3350 | } |