PageRenderTime 62ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsparse.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1987 lines | 1451 code | 193 blank | 343 comment | 407 complexity | cb9396026941b3113ddbde18f4e6ddd8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=99:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is Mozilla Communicator client code, released
  18. * March 31, 1998.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Netscape Communications Corporation.
  22. * Portions created by the Initial Developer are Copyright (C) 1998
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. /*
  41. * JS parser.
  42. *
  43. * This is a recursive-descent parser for the JavaScript language specified by
  44. * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
  45. * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
  46. * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
  47. * After tree construction, it rewrites trees to fold constants and evaluate
  48. * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
  49. * generate bytecode.
  50. *
  51. * This parser attempts no error recovery.
  52. */
  53. #include "jsstddef.h"
  54. #include <stdlib.h>
  55. #include <string.h>
  56. #include <math.h>
  57. #include "jstypes.h"
  58. #include "jsarena.h" /* Added by JSIFY */
  59. #include "jsutil.h" /* Added by JSIFY */
  60. #include "jsapi.h"
  61. #include "jsarray.h"
  62. #include "jsatom.h"
  63. #include "jscntxt.h"
  64. #include "jsversion.h"
  65. #include "jsemit.h"
  66. #include "jsfun.h"
  67. #include "jsinterp.h"
  68. #include "jsiter.h"
  69. #include "jslock.h"
  70. #include "jsnum.h"
  71. #include "jsobj.h"
  72. #include "jsopcode.h"
  73. #include "jsparse.h"
  74. #include "jsscan.h"
  75. #include "jsscope.h"
  76. #include "jsscript.h"
  77. #include "jsstr.h"
  78. #include "jsstaticcheck.h"
  79. #if JS_HAS_XML_SUPPORT
  80. #include "jsxml.h"
  81. #endif
  82. #if JS_HAS_DESTRUCTURING
  83. #include "jsdhash.h"
  84. #endif
  85. /*
  86. * Asserts to verify assumptions behind pn_ macros.
  87. */
  88. JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) ==
  89. offsetof(JSParseNode, pn_u.apair.atom));
  90. JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.slot) ==
  91. offsetof(JSParseNode, pn_u.lexical.slot));
  92. /*
  93. * JS parsers, from lowest to highest precedence.
  94. *
  95. * Each parser takes a context, a token stream, and a tree context struct.
  96. * Each returns a parse node tree or null on error.
  97. */
  98. typedef JSParseNode *
  99. JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
  100. typedef JSParseNode *
  101. JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
  102. JSBool allowCallSyntax);
  103. typedef JSParseNode *
  104. JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
  105. JSTokenType tt, JSBool afterDot);
  106. typedef JSParseNode *
  107. JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
  108. JSParseNode *pn1, JSBool *genexp);
  109. static JSParser FunctionStmt;
  110. static JSParser FunctionExpr;
  111. static JSParser Statements;
  112. static JSParser Statement;
  113. static JSParser Variables;
  114. static JSParser Expr;
  115. static JSParser AssignExpr;
  116. static JSParser CondExpr;
  117. static JSParser OrExpr;
  118. static JSParser AndExpr;
  119. static JSParser BitOrExpr;
  120. static JSParser BitXorExpr;
  121. static JSParser BitAndExpr;
  122. static JSParser EqExpr;
  123. static JSParser RelExpr;
  124. static JSParser ShiftExpr;
  125. static JSParser AddExpr;
  126. static JSParser MulExpr;
  127. static JSParser UnaryExpr;
  128. static JSMemberParser MemberExpr;
  129. static JSPrimaryParser PrimaryExpr;
  130. static JSParenParser ParenExpr;
  131. /*
  132. * Insist that the next token be of type tt, or report errno and return null.
  133. * NB: this macro uses cx and ts from its lexical environment.
  134. */
  135. #define MUST_MATCH_TOKEN(tt, errno) \
  136. JS_BEGIN_MACRO \
  137. if (js_GetToken(cx, ts) != tt) { \
  138. js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
  139. return NULL; \
  140. } \
  141. JS_END_MACRO
  142. #ifdef METER_PARSENODES
  143. static uint32 parsenodes = 0;
  144. static uint32 maxparsenodes = 0;
  145. static uint32 recyclednodes = 0;
  146. #endif
  147. JSBool
  148. js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,
  149. JSStackFrame *callerFrame,
  150. const jschar *base, size_t length,
  151. FILE *fp, const char *filename, uintN lineno)
  152. {
  153. JS_ASSERT_IF(callerFrame, callerFrame->script);
  154. pc->tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
  155. if (!js_InitTokenStream(cx, TS(pc), base, length, fp, filename, lineno)) {
  156. JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
  157. return JS_FALSE;
  158. }
  159. if (principals)
  160. JSPRINCIPALS_HOLD(cx, principals);
  161. pc->principals = principals;
  162. pc->callerFrame = callerFrame;
  163. pc->nodeList = NULL;
  164. pc->traceListHead = NULL;
  165. /* Root atoms and objects allocated for the parsed tree. */
  166. JS_KEEP_ATOMS(cx->runtime);
  167. JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx, pc, &pc->tempRoot);
  168. return JS_TRUE;
  169. }
  170. void
  171. js_FinishParseContext(JSContext *cx, JSParseContext *pc)
  172. {
  173. if (pc->principals)
  174. JSPRINCIPALS_DROP(cx, pc->principals);
  175. JS_ASSERT(pc->tempRoot.u.parseContext == pc);
  176. JS_POP_TEMP_ROOT(cx, &pc->tempRoot);
  177. JS_UNKEEP_ATOMS(cx->runtime);
  178. js_CloseTokenStream(cx, TS(pc));
  179. JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
  180. }
  181. void
  182. js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,
  183. JSPrincipals *principals)
  184. {
  185. JS_ASSERT(!pc->principals);
  186. if (principals)
  187. JSPRINCIPALS_HOLD(cx, principals);
  188. pc->principals = principals;
  189. }
  190. JSParsedObjectBox *
  191. js_NewParsedObjectBox(JSContext *cx, JSParseContext *pc, JSObject *obj)
  192. {
  193. JSParsedObjectBox *pob;
  194. /*
  195. * We use JSContext.tempPool to allocate parsed objects and place them on
  196. * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
  197. * containing the entries must be alive until we are done with scanning,
  198. * parsing and code generation for the whole script or top-level function.
  199. */
  200. JS_ASSERT(obj);
  201. JS_ARENA_ALLOCATE_TYPE(pob, JSParsedObjectBox, &cx->tempPool);
  202. if (!pob) {
  203. js_ReportOutOfScriptQuota(cx);
  204. return NULL;
  205. }
  206. pob->traceLink = pc->traceListHead;
  207. pob->emitLink = NULL;
  208. pob->object = obj;
  209. pc->traceListHead = pob;
  210. return pob;
  211. }
  212. void
  213. js_TraceParseContext(JSTracer *trc, JSParseContext *pc)
  214. {
  215. JSParsedObjectBox *pob;
  216. JS_ASSERT(pc->tempRoot.u.parseContext == pc);
  217. pob = pc->traceListHead;
  218. while (pob) {
  219. JS_CALL_OBJECT_TRACER(trc, pob->object, "parser.object");
  220. pob = pob->traceLink;
  221. }
  222. }
  223. static JSParseNode *
  224. RecycleTree(JSParseNode *pn, JSTreeContext *tc)
  225. {
  226. JSParseNode *next;
  227. if (!pn)
  228. return NULL;
  229. /* Catch back-to-back dup recycles. */
  230. JS_ASSERT(pn != tc->parseContext->nodeList);
  231. next = pn->pn_next;
  232. pn->pn_next = tc->parseContext->nodeList;
  233. tc->parseContext->nodeList = pn;
  234. #ifdef METER_PARSENODES
  235. recyclednodes++;
  236. #endif
  237. return next;
  238. }
  239. static JSParseNode *
  240. NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
  241. {
  242. JSParseNode *pn;
  243. pn = tc->parseContext->nodeList;
  244. if (!pn) {
  245. JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
  246. if (!pn)
  247. js_ReportOutOfScriptQuota(cx);
  248. } else {
  249. tc->parseContext->nodeList = pn->pn_next;
  250. /* Recycle immediate descendents only, to save work and working set. */
  251. switch (pn->pn_arity) {
  252. case PN_FUNC:
  253. RecycleTree(pn->pn_body, tc);
  254. break;
  255. case PN_LIST:
  256. if (pn->pn_head) {
  257. /* XXX check for dup recycles in the list */
  258. *pn->pn_tail = tc->parseContext->nodeList;
  259. tc->parseContext->nodeList = pn->pn_head;
  260. #ifdef METER_PARSENODES
  261. recyclednodes += pn->pn_count;
  262. #endif
  263. }
  264. break;
  265. case PN_TERNARY:
  266. RecycleTree(pn->pn_kid1, tc);
  267. RecycleTree(pn->pn_kid2, tc);
  268. RecycleTree(pn->pn_kid3, tc);
  269. break;
  270. case PN_BINARY:
  271. if (pn->pn_left != pn->pn_right)
  272. RecycleTree(pn->pn_left, tc);
  273. RecycleTree(pn->pn_right, tc);
  274. break;
  275. case PN_UNARY:
  276. RecycleTree(pn->pn_kid, tc);
  277. break;
  278. case PN_NAME:
  279. RecycleTree(pn->pn_expr, tc);
  280. break;
  281. case PN_NULLARY:
  282. break;
  283. }
  284. }
  285. if (pn) {
  286. #ifdef METER_PARSENODES
  287. parsenodes++;
  288. if (parsenodes - recyclednodes > maxparsenodes)
  289. maxparsenodes = parsenodes - recyclednodes;
  290. #endif
  291. memset(&pn->pn_u, 0, sizeof pn->pn_u);
  292. pn->pn_next = NULL;
  293. }
  294. return pn;
  295. }
  296. /*
  297. * Allocate a JSParseNode from cx's temporary arena.
  298. */
  299. static JSParseNode *
  300. NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
  301. JSTreeContext *tc)
  302. {
  303. JSParseNode *pn;
  304. JSToken *tp;
  305. pn = NewOrRecycledNode(cx, tc);
  306. if (!pn)
  307. return NULL;
  308. tp = &CURRENT_TOKEN(ts);
  309. pn->pn_type = tp->type;
  310. pn->pn_pos = tp->pos;
  311. pn->pn_op = JSOP_NOP;
  312. pn->pn_arity = arity;
  313. return pn;
  314. }
  315. static JSParseNode *
  316. NewBinary(JSContext *cx, JSTokenType tt,
  317. JSOp op, JSParseNode *left, JSParseNode *right,
  318. JSTreeContext *tc)
  319. {
  320. JSParseNode *pn, *pn1, *pn2;
  321. if (!left || !right)
  322. return NULL;
  323. /*
  324. * Flatten a left-associative (left-heavy) tree of a given operator into
  325. * a list, to reduce js_FoldConstants and js_EmitTree recursion.
  326. */
  327. if (left->pn_type == tt &&
  328. left->pn_op == op &&
  329. (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
  330. if (left->pn_arity != PN_LIST) {
  331. pn1 = left->pn_left, pn2 = left->pn_right;
  332. left->pn_arity = PN_LIST;
  333. PN_INIT_LIST_1(left, pn1);
  334. PN_APPEND(left, pn2);
  335. if (tt == TOK_PLUS) {
  336. if (pn1->pn_type == TOK_STRING)
  337. left->pn_extra |= PNX_STRCAT;
  338. else if (pn1->pn_type != TOK_NUMBER)
  339. left->pn_extra |= PNX_CANTFOLD;
  340. if (pn2->pn_type == TOK_STRING)
  341. left->pn_extra |= PNX_STRCAT;
  342. else if (pn2->pn_type != TOK_NUMBER)
  343. left->pn_extra |= PNX_CANTFOLD;
  344. }
  345. }
  346. PN_APPEND(left, right);
  347. left->pn_pos.end = right->pn_pos.end;
  348. if (tt == TOK_PLUS) {
  349. if (right->pn_type == TOK_STRING)
  350. left->pn_extra |= PNX_STRCAT;
  351. else if (right->pn_type != TOK_NUMBER)
  352. left->pn_extra |= PNX_CANTFOLD;
  353. }
  354. return left;
  355. }
  356. /*
  357. * Fold constant addition immediately, to conserve node space and, what's
  358. * more, so js_FoldConstants never sees mixed addition and concatenation
  359. * operations with more than one leading non-string operand in a PN_LIST
  360. * generated for expressions such as 1 + 2 + "pt" (which should evaluate
  361. * to "3pt", not "12pt").
  362. */
  363. if (tt == TOK_PLUS &&
  364. left->pn_type == TOK_NUMBER &&
  365. right->pn_type == TOK_NUMBER) {
  366. left->pn_dval += right->pn_dval;
  367. left->pn_pos.end = right->pn_pos.end;
  368. RecycleTree(right, tc);
  369. return left;
  370. }
  371. pn = NewOrRecycledNode(cx, tc);
  372. if (!pn)
  373. return NULL;
  374. pn->pn_type = tt;
  375. pn->pn_pos.begin = left->pn_pos.begin;
  376. pn->pn_pos.end = right->pn_pos.end;
  377. pn->pn_op = op;
  378. pn->pn_arity = PN_BINARY;
  379. pn->pn_left = left;
  380. pn->pn_right = right;
  381. return pn;
  382. }
  383. #if JS_HAS_GETTER_SETTER
  384. static JSTokenType
  385. CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
  386. {
  387. JSAtom *atom;
  388. JSRuntime *rt;
  389. JSOp op;
  390. const char *name;
  391. JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
  392. atom = CURRENT_TOKEN(ts).t_atom;
  393. rt = cx->runtime;
  394. if (atom == rt->atomState.getterAtom)
  395. op = JSOP_GETTER;
  396. else if (atom == rt->atomState.setterAtom)
  397. op = JSOP_SETTER;
  398. else
  399. return TOK_NAME;
  400. if (js_PeekTokenSameLine(cx, ts) != tt)
  401. return TOK_NAME;
  402. (void) js_GetToken(cx, ts);
  403. if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
  404. js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
  405. JSMSG_BAD_GETTER_OR_SETTER,
  406. (op == JSOP_GETTER)
  407. ? js_getter_str
  408. : js_setter_str);
  409. return TOK_ERROR;
  410. }
  411. CURRENT_TOKEN(ts).t_op = op;
  412. if (JS_HAS_STRICT_OPTION(cx)) {
  413. name = js_AtomToPrintableString(cx, atom);
  414. if (!name ||
  415. !js_ReportCompileErrorNumber(cx, ts, NULL,
  416. JSREPORT_WARNING | JSREPORT_STRICT,
  417. JSMSG_DEPRECATED_USAGE,
  418. name)) {
  419. return TOK_ERROR;
  420. }
  421. }
  422. return tt;
  423. }
  424. #endif
  425. /*
  426. * Parse a top-level JS script.
  427. */
  428. JSParseNode *
  429. js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc)
  430. {
  431. JSTreeContext tc;
  432. JSParseNode *pn;
  433. /*
  434. * Protect atoms from being collected by a GC activation, which might
  435. * - nest on this thread due to out of memory (the so-called "last ditch"
  436. * GC attempted within js_NewGCThing), or
  437. * - run for any reason on another thread if this thread is suspended on
  438. * an object lock before it finishes generating bytecode into a script
  439. * protected from the GC by a root or a stack frame reference.
  440. */
  441. TREE_CONTEXT_INIT(&tc, pc);
  442. tc.u.scopeChain = chain;
  443. pn = Statements(cx, TS(pc), &tc);
  444. if (pn) {
  445. if (!js_MatchToken(cx, TS(pc), TOK_EOF)) {
  446. js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,
  447. JSMSG_SYNTAX_ERROR);
  448. pn = NULL;
  449. } else {
  450. pn->pn_type = TOK_LC;
  451. if (!js_FoldConstants(cx, pn, &tc))
  452. pn = NULL;
  453. }
  454. }
  455. TREE_CONTEXT_FINISH(cx, &tc);
  456. return pn;
  457. }
  458. /*
  459. * Compile a top-level script.
  460. */
  461. extern JSScript *
  462. js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
  463. JSPrincipals *principals, uint32 tcflags,
  464. const jschar *chars, size_t length,
  465. FILE *file, const char *filename, uintN lineno)
  466. {
  467. JSParseContext pc;
  468. JSArenaPool codePool, notePool;
  469. JSCodeGenerator cg;
  470. JSTokenType tt;
  471. JSParseNode *pn;
  472. uint32 scriptGlobals;
  473. JSScript *script;
  474. #ifdef METER_PARSENODES
  475. void *sbrk(ptrdiff_t), *before = sbrk(0);
  476. #endif
  477. JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
  478. TCF_STATIC_DEPTH_MASK)));
  479. /*
  480. * The scripted callerFrame can only be given for compile-and-go scripts
  481. * and non-zero static depth requires callerFrame.
  482. */
  483. JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
  484. JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame);
  485. if (!js_InitParseContext(cx, &pc, principals, callerFrame, chars, length,
  486. file, filename, lineno)) {
  487. return NULL;
  488. }
  489. JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
  490. &cx->scriptStackQuota);
  491. JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
  492. &cx->scriptStackQuota);
  493. js_InitCodeGenerator(cx, &cg, &pc, &codePool, &notePool,
  494. pc.tokenStream.lineno);
  495. MUST_FLOW_THROUGH("out");
  496. cg.treeContext.flags |= (uint16) tcflags;
  497. cg.treeContext.u.scopeChain = scopeChain;
  498. cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags);
  499. if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) {
  500. /*
  501. * An eval script in a caller frame needs to have its enclosing function
  502. * captured in case it uses an upvar reference, and someone wishes to
  503. * decompile it while running.
  504. */
  505. JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, callerFrame->callee);
  506. pob->emitLink = cg.objectList.lastPob;
  507. cg.objectList.lastPob = pob;
  508. cg.objectList.length++;
  509. }
  510. /* Inline Statements() to emit as we go to save space. */
  511. for (;;) {
  512. pc.tokenStream.flags |= TSF_OPERAND;
  513. tt = js_PeekToken(cx, &pc.tokenStream);
  514. pc.tokenStream.flags &= ~TSF_OPERAND;
  515. if (tt <= TOK_EOF) {
  516. if (tt == TOK_EOF)
  517. break;
  518. JS_ASSERT(tt == TOK_ERROR);
  519. script = NULL;
  520. goto out;
  521. }
  522. pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
  523. if (!pn) {
  524. script = NULL;
  525. goto out;
  526. }
  527. JS_ASSERT(!cg.treeContext.blockNode);
  528. if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
  529. !js_EmitTree(cx, &cg, pn)) {
  530. script = NULL;
  531. goto out;
  532. }
  533. RecycleTree(pn, &cg.treeContext);
  534. }
  535. /*
  536. * Global variables and regexps shares the index space with locals. Due to
  537. * incremental code generation we need to patch the bytecode to adjust the
  538. * local references to skip the globals.
  539. */
  540. scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length;
  541. if (scriptGlobals != 0) {
  542. jsbytecode *code, *end;
  543. JSOp op;
  544. const JSCodeSpec *cs;
  545. uintN len, slot;
  546. if (scriptGlobals >= SLOTNO_LIMIT)
  547. goto too_many_slots;
  548. code = CG_BASE(&cg);
  549. for (end = code + CG_OFFSET(&cg); code != end; code += len) {
  550. JS_ASSERT(code < end);
  551. op = (JSOp) *code;
  552. cs = &js_CodeSpec[op];
  553. len = (cs->length > 0)
  554. ? (uintN) cs->length
  555. : js_GetVariableBytecodeLength(code);
  556. if (JOF_TYPE(cs->format) == JOF_LOCAL ||
  557. (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
  558. /*
  559. * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
  560. * emitted only for a function.
  561. */
  562. JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
  563. (op == JSOP_GETLOCALPROP));
  564. slot = GET_SLOTNO(code);
  565. slot += scriptGlobals;
  566. if (slot >= SLOTNO_LIMIT)
  567. goto too_many_slots;
  568. SET_SLOTNO(code, slot);
  569. }
  570. }
  571. }
  572. #ifdef METER_PARSENODES
  573. printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
  574. (char *)sbrk(0) - (char *)before,
  575. parsenodes,
  576. maxparsenodes,
  577. parsenodes - recyclednodes);
  578. before = sbrk(0);
  579. #endif
  580. /*
  581. * Nowadays the threaded interpreter needs a stop instruction, so we
  582. * do have to emit that here.
  583. */
  584. if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {
  585. script = NULL;
  586. goto out;
  587. }
  588. #ifdef METER_PARSENODES
  589. printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
  590. (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
  591. #endif
  592. #ifdef JS_ARENAMETER
  593. JS_DumpArenaStats(stdout);
  594. #endif
  595. script = js_NewScriptFromCG(cx, &cg);
  596. #ifdef JS_SCOPE_DEPTH_METER
  597. if (script) {
  598. JSObject *obj = scopeChain;
  599. uintN depth = 1;
  600. while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
  601. ++depth;
  602. JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
  603. }
  604. #endif
  605. out:
  606. js_FinishCodeGenerator(cx, &cg);
  607. JS_FinishArenaPool(&codePool);
  608. JS_FinishArenaPool(&notePool);
  609. js_FinishParseContext(cx, &pc);
  610. return script;
  611. too_many_slots:
  612. js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
  613. JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
  614. script = NULL;
  615. goto out;
  616. }
  617. /*
  618. * Insist on a final return before control flows out of pn. Try to be a bit
  619. * smart about loops: do {...; return e2;} while(0) at the end of a function
  620. * that contains an early return e1 will get a strict warning. Similarly for
  621. * iloops: while (true){...} is treated as though ... returns.
  622. */
  623. #define ENDS_IN_OTHER 0
  624. #define ENDS_IN_RETURN 1
  625. #define ENDS_IN_BREAK 2
  626. static int
  627. HasFinalReturn(JSParseNode *pn)
  628. {
  629. JSParseNode *pn2, *pn3;
  630. uintN rv, rv2, hasDefault;
  631. switch (pn->pn_type) {
  632. case TOK_LC:
  633. if (!pn->pn_head)
  634. return ENDS_IN_OTHER;
  635. return HasFinalReturn(PN_LAST(pn));
  636. case TOK_IF:
  637. if (!pn->pn_kid3)
  638. return ENDS_IN_OTHER;
  639. return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
  640. case TOK_WHILE:
  641. pn2 = pn->pn_left;
  642. if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
  643. return ENDS_IN_RETURN;
  644. if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
  645. return ENDS_IN_RETURN;
  646. return ENDS_IN_OTHER;
  647. case TOK_DO:
  648. pn2 = pn->pn_right;
  649. if (pn2->pn_type == TOK_PRIMARY) {
  650. if (pn2->pn_op == JSOP_FALSE)
  651. return HasFinalReturn(pn->pn_left);
  652. if (pn2->pn_op == JSOP_TRUE)
  653. return ENDS_IN_RETURN;
  654. }
  655. if (pn2->pn_type == TOK_NUMBER) {
  656. if (pn2->pn_dval == 0)
  657. return HasFinalReturn(pn->pn_left);
  658. return ENDS_IN_RETURN;
  659. }
  660. return ENDS_IN_OTHER;
  661. case TOK_FOR:
  662. pn2 = pn->pn_left;
  663. if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
  664. return ENDS_IN_RETURN;
  665. return ENDS_IN_OTHER;
  666. case TOK_SWITCH:
  667. rv = ENDS_IN_RETURN;
  668. hasDefault = ENDS_IN_OTHER;
  669. pn2 = pn->pn_right;
  670. if (pn2->pn_type == TOK_LEXICALSCOPE)
  671. pn2 = pn2->pn_expr;
  672. for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
  673. if (pn2->pn_type == TOK_DEFAULT)
  674. hasDefault = ENDS_IN_RETURN;
  675. pn3 = pn2->pn_right;
  676. JS_ASSERT(pn3->pn_type == TOK_LC);
  677. if (pn3->pn_head) {
  678. rv2 = HasFinalReturn(PN_LAST(pn3));
  679. if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
  680. /* Falling through to next case or default. */;
  681. else
  682. rv &= rv2;
  683. }
  684. }
  685. /* If a final switch has no default case, we judge it harshly. */
  686. rv &= hasDefault;
  687. return rv;
  688. case TOK_BREAK:
  689. return ENDS_IN_BREAK;
  690. case TOK_WITH:
  691. return HasFinalReturn(pn->pn_right);
  692. case TOK_RETURN:
  693. return ENDS_IN_RETURN;
  694. case TOK_COLON:
  695. case TOK_LEXICALSCOPE:
  696. return HasFinalReturn(pn->pn_expr);
  697. case TOK_THROW:
  698. return ENDS_IN_RETURN;
  699. case TOK_TRY:
  700. /* If we have a finally block that returns, we are done. */
  701. if (pn->pn_kid3) {
  702. rv = HasFinalReturn(pn->pn_kid3);
  703. if (rv == ENDS_IN_RETURN)
  704. return rv;
  705. }
  706. /* Else check the try block and any and all catch statements. */
  707. rv = HasFinalReturn(pn->pn_kid1);
  708. if (pn->pn_kid2) {
  709. JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
  710. for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
  711. rv &= HasFinalReturn(pn2);
  712. }
  713. return rv;
  714. case TOK_CATCH:
  715. /* Check this catch block's body. */
  716. return HasFinalReturn(pn->pn_kid3);
  717. case TOK_LET:
  718. /* Non-binary let statements are let declarations. */
  719. if (pn->pn_arity != PN_BINARY)
  720. return ENDS_IN_OTHER;
  721. return HasFinalReturn(pn->pn_right);
  722. default:
  723. return ENDS_IN_OTHER;
  724. }
  725. }
  726. static JSBool
  727. ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
  728. uintN anonerrnum)
  729. {
  730. const char *name;
  731. JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
  732. if (tc->u.fun->atom) {
  733. name = js_AtomToPrintableString(cx, tc->u.fun->atom);
  734. } else {
  735. errnum = anonerrnum;
  736. name = NULL;
  737. }
  738. return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,
  739. errnum, name);
  740. }
  741. static JSBool
  742. CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
  743. {
  744. JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
  745. return HasFinalReturn(pn) == ENDS_IN_RETURN ||
  746. ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
  747. JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
  748. }
  749. static JSParseNode *
  750. FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
  751. {
  752. JSStmtInfo stmtInfo;
  753. uintN oldflags, firstLine;
  754. JSParseNode *pn;
  755. JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
  756. js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
  757. stmtInfo.flags = SIF_BODY_BLOCK;
  758. oldflags = tc->flags;
  759. tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
  760. /*
  761. * Save the body's first line, and store it in pn->pn_pos.begin.lineno
  762. * later, because we may have not peeked in ts yet, so Statements won't
  763. * acquire a valid pn->pn_pos.begin from the current token.
  764. */
  765. firstLine = ts->lineno;
  766. #if JS_HAS_EXPR_CLOSURES
  767. if (CURRENT_TOKEN(ts).type == TOK_LC) {
  768. pn = Statements(cx, ts, tc);
  769. } else {
  770. pn = NewParseNode(cx, ts, PN_UNARY, tc);
  771. if (pn) {
  772. pn->pn_kid = AssignExpr(cx, ts, tc);
  773. if (!pn->pn_kid) {
  774. pn = NULL;
  775. } else {
  776. if (tc->flags & TCF_FUN_IS_GENERATOR) {
  777. ReportBadReturn(cx, tc, JSREPORT_ERROR,
  778. JSMSG_BAD_GENERATOR_RETURN,
  779. JSMSG_BAD_ANON_GENERATOR_RETURN);
  780. pn = NULL;
  781. } else {
  782. pn->pn_type = TOK_RETURN;
  783. pn->pn_op = JSOP_RETURN;
  784. pn->pn_pos.end = pn->pn_kid->pn_pos.end;
  785. }
  786. }
  787. }
  788. }
  789. #else
  790. pn = Statements(cx, ts, tc);
  791. #endif
  792. if (pn) {
  793. js_PopStatement(tc);
  794. pn->pn_pos.begin.lineno = firstLine;
  795. /* Check for falling off the end of a function that returns a value. */
  796. if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
  797. !CheckFinalReturn(cx, tc, pn)) {
  798. pn = NULL;
  799. }
  800. }
  801. tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
  802. return pn;
  803. }
  804. /*
  805. * Compile a JS function body, which might appear as the value of an event
  806. * handler attribute in an HTML <INPUT> tag.
  807. */
  808. JSBool
  809. js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
  810. const jschar *chars, size_t length,
  811. const char *filename, uintN lineno)
  812. {
  813. JSParseContext pc;
  814. JSArenaPool codePool, notePool;
  815. JSCodeGenerator funcg;
  816. JSParseNode *pn;
  817. if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,
  818. filename, lineno)) {
  819. return JS_FALSE;
  820. }
  821. /* No early return from this point until js_FinishParseContext call. */
  822. JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
  823. &cx->scriptStackQuota);
  824. JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
  825. &cx->scriptStackQuota);
  826. js_InitCodeGenerator(cx, &funcg, &pc, &codePool, &notePool,
  827. pc.tokenStream.lineno);
  828. funcg.treeContext.flags |= TCF_IN_FUNCTION;
  829. funcg.treeContext.u.fun = fun;
  830. /*
  831. * Farble the body so that it looks like a block statement to js_EmitTree,
  832. * which is called beneath FunctionBody; see Statements, further below in
  833. * this file. FunctionBody pushes a STMT_BLOCK record around its call to
  834. * Statements, so Statements will not compile each statement as it loops
  835. * to save JSParseNode space -- it will not compile at all, only build a
  836. * JSParseNode tree.
  837. *
  838. * Therefore we must fold constants, allocate try notes, and generate code
  839. * for this function, including a stop opcode at the end.
  840. */
  841. CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC;
  842. pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext);
  843. if (pn) {
  844. if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {
  845. js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
  846. JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
  847. pn = NULL;
  848. } else {
  849. if (!js_FoldConstants(cx, pn, &funcg.treeContext) ||
  850. !js_EmitFunctionScript(cx, &funcg, pn)) {
  851. pn = NULL;
  852. }
  853. }
  854. }
  855. /* Restore saved state and release code generation arenas. */
  856. js_FinishCodeGenerator(cx, &funcg);
  857. JS_FinishArenaPool(&codePool);
  858. JS_FinishArenaPool(&notePool);
  859. js_FinishParseContext(cx, &pc);
  860. return pn != NULL;
  861. }
  862. /*
  863. * Parameter block types for the several Binder functions. We use a common
  864. * helper function signature in order to share code among destructuring and
  865. * simple variable declaration parsers. In the destructuring case, the binder
  866. * function is called indirectly from the variable declaration parser by way
  867. * of CheckDestructuring and its friends.
  868. */
  869. typedef struct BindData BindData;
  870. typedef JSBool
  871. (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
  872. struct BindData {
  873. JSParseNode *pn; /* error source coordinate */
  874. JSOp op; /* prolog bytecode or nop */
  875. Binder binder; /* binder, discriminates u */
  876. union {
  877. struct {
  878. uintN overflow;
  879. } let;
  880. } u;
  881. };
  882. static JSBool
  883. BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
  884. {
  885. const char *name;
  886. /*
  887. * Check for a duplicate parameter name, a "feature" required by ECMA-262.
  888. */
  889. JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
  890. if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
  891. name = js_AtomToPrintableString(cx, atom);
  892. if (!name ||
  893. !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,
  894. JSREPORT_WARNING | JSREPORT_STRICT,
  895. JSMSG_DUPLICATE_FORMAL,
  896. name)) {
  897. return JS_FALSE;
  898. }
  899. }
  900. return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);
  901. }
  902. static JSBool
  903. BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
  904. JSLocalKind localKind)
  905. {
  906. JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
  907. /*
  908. * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
  909. * Instead 'var arguments' always restates the predefined property of the
  910. * activation objects with unhidden name 'arguments'. Assignment to such
  911. * a variable must be handled specially.
  912. */
  913. if (atom == cx->runtime->atomState.argumentsAtom)
  914. return JS_TRUE;
  915. return js_AddLocal(cx, fun, atom, localKind);
  916. }
  917. #if JS_HAS_DESTRUCTURING
  918. /*
  919. * Forward declaration to maintain top-down presentation.
  920. */
  921. static JSParseNode *
  922. DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
  923. JSTokenType tt);
  924. static JSBool
  925. BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
  926. JSTreeContext *tc)
  927. {
  928. JSAtomListElement *ale;
  929. const char *name;
  930. JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
  931. ATOM_LIST_SEARCH(ale, &tc->decls, atom);
  932. if (!ale) {
  933. ale = js_IndexAtom(cx, atom, &tc->decls);
  934. if (!ale)
  935. return JS_FALSE;
  936. ALE_SET_JSOP(ale, data->op);
  937. }
  938. if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
  939. name = js_AtomToPrintableString(cx, atom);
  940. if (!name ||
  941. !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
  942. JSREPORT_WARNING | JSREPORT_STRICT,
  943. JSMSG_DUPLICATE_FORMAL,
  944. name)) {
  945. return JS_FALSE;
  946. }
  947. } else {
  948. if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))
  949. return JS_FALSE;
  950. }
  951. return JS_TRUE;
  952. }
  953. #endif /* JS_HAS_DESTRUCTURING */
  954. static JSFunction *
  955. NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
  956. uintN lambda)
  957. {
  958. JSObject *parent;
  959. JSFunction *fun;
  960. JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
  961. parent = (tc->flags & TCF_IN_FUNCTION)
  962. ? FUN_OBJECT(tc->u.fun)
  963. : tc->u.scopeChain;
  964. fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
  965. parent, atom);
  966. if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
  967. STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
  968. STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
  969. }
  970. return fun;
  971. }
  972. static JSParseNode *
  973. FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
  974. uintN lambda)
  975. {
  976. JSOp op, prevop;
  977. JSParseNode *pn, *body, *result;
  978. JSTokenType tt;
  979. JSAtom *funAtom;
  980. JSParsedObjectBox *funpob;
  981. JSAtomListElement *ale;
  982. JSFunction *fun;
  983. JSTreeContext funtc;
  984. #if JS_HAS_DESTRUCTURING
  985. JSParseNode *item, *list = NULL;
  986. #endif
  987. /* Make a TOK_FUNCTION node. */
  988. #if JS_HAS_GETTER_SETTER
  989. op = CURRENT_TOKEN(ts).t_op;
  990. #endif
  991. pn = NewParseNode(cx, ts, PN_FUNC, tc);
  992. if (!pn)
  993. return NULL;
  994. #ifdef DEBUG
  995. pn->pn_index = (uint32) -1;
  996. #endif
  997. /* Scan the optional function name into funAtom. */
  998. ts->flags |= TSF_KEYWORD_IS_NAME;
  999. tt = js_GetToken(cx, ts);
  1000. ts->flags &= ~TSF_KEYWORD_IS_NAME;
  1001. if (tt == TOK_NAME) {
  1002. funAtom = CURRENT_TOKEN(ts).t_atom;
  1003. } else {
  1004. if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
  1005. js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
  1006. JSMSG_SYNTAX_ERROR);
  1007. return NULL;
  1008. }
  1009. funAtom = NULL;
  1010. js_UngetToken(ts);
  1011. }
  1012. /*
  1013. * Record names for function statements in tc->decls so we know when to
  1014. * avoid optimizing variable references that might name a function.
  1015. */
  1016. if (lambda == 0 && funAtom) {
  1017. ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
  1018. if (ale) {
  1019. prevop = ALE_JSOP(ale);
  1020. if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
  1021. const char *name = js_AtomToPrintableString(cx, funAtom);
  1022. if (!name ||
  1023. !js_ReportCompileErrorNumber(cx, ts, NULL,
  1024. (prevop != JSOP_DEFCONST)
  1025. ? JSREPORT_WARNING |
  1026. JSREPORT_STRICT
  1027. : JSREPORT_ERROR,
  1028. JSMSG_REDECLARED_VAR,
  1029. (prevop == JSOP_DEFFUN)
  1030. ? js_function_str
  1031. : (prevop == JSOP_DEFCONST)
  1032. ? js_const_str
  1033. : js_var_str,
  1034. name)) {
  1035. return NULL;
  1036. }
  1037. }
  1038. if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
  1039. tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
  1040. } else {
  1041. ale = js_IndexAtom(cx, funAtom, &tc->decls);
  1042. if (!ale)
  1043. return NULL;
  1044. }
  1045. ALE_SET_JSOP(ale, JSOP_DEFFUN);
  1046. /*
  1047. * A function nested at top level inside another's body needs only a
  1048. * local variable to bind its name to its value, and not an activation
  1049. * object property (it might also need the activation property, if the
  1050. * outer function contains with statements, e.g., but the stack slot
  1051. * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
  1052. * JSOP_GETLOCAL bytecode).
  1053. */
  1054. if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
  1055. JSLocalKind localKind;
  1056. /*
  1057. * Define a property on the outer function so that BindNameToSlot
  1058. * can properly optimize accesses. Note that we need a variable,
  1059. * not an argument, for the function statement. Thus we add a
  1060. * variable even if the parameter with the given name already
  1061. * exists.
  1062. */
  1063. localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL);
  1064. if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) {
  1065. if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR))
  1066. return NULL;
  1067. }
  1068. }
  1069. }
  1070. fun = NewCompilerFunction(cx, tc, funAtom, lambda);
  1071. if (!fun)
  1072. return NULL;
  1073. #if JS_HAS_GETTER_SETTER
  1074. if (op != JSOP_NOP)
  1075. fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
  1076. #endif
  1077. /*
  1078. * Create wrapping box for fun->object early to protect against a
  1079. * last-ditch GC.
  1080. */
  1081. funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));
  1082. if (!funpob)
  1083. return NULL;
  1084. /* Initialize early for possible flags mutation via DestructuringExpr. */
  1085. TREE_CONTEXT_INIT(&funtc, tc->parseContext);
  1086. funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
  1087. funtc.u.fun = fun;
  1088. /* Now parse formal argument list and compute fun->nargs. */
  1089. MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
  1090. if (!js_MatchToken(cx, ts, TOK_RP)) {
  1091. do {
  1092. tt = js_GetToken(cx, ts);
  1093. switch (tt) {
  1094. #if JS_HAS_DESTRUCTURING
  1095. case TOK_LB:
  1096. case TOK_LC:
  1097. {
  1098. BindData data;
  1099. JSParseNode *lhs, *rhs;
  1100. jsint slot;
  1101. /*
  1102. * A destructuring formal parameter turns into one or more
  1103. * local variables initialized from properties of a single
  1104. * anonymous positional parameter, so here we must tweak our
  1105. * binder and its data.
  1106. */
  1107. data.pn = NULL;
  1108. data.op = JSOP_DEFVAR;
  1109. data.binder = BindDestructuringArg;
  1110. lhs = DestructuringExpr(cx, &data, &funtc, tt);
  1111. if (!lhs)
  1112. return NULL;
  1113. /*
  1114. * Adjust fun->nargs to count the single anonymous positional
  1115. * parameter that is to be destructured.
  1116. */
  1117. slot = fun->nargs;
  1118. if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
  1119. return NULL;
  1120. /*
  1121. * Synthesize a destructuring assignment from the single
  1122. * anonymous positional parameter into the destructuring
  1123. * left-hand-side expression and accumulate it in list.
  1124. */
  1125. rhs = NewParseNode(cx, ts, PN_NAME, tc);
  1126. if (!rhs)
  1127. return NULL;
  1128. rhs->pn_type = TOK_NAME;
  1129. rhs->pn_op = JSOP_GETARG;
  1130. rhs->pn_atom = cx->runtime->atomState.emptyAtom;
  1131. rhs->pn_slot = slot;
  1132. item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
  1133. if (!item)
  1134. return NULL;
  1135. if (!list) {
  1136. list = NewParseNode(cx, ts, PN_LIST, tc);
  1137. if (!list)
  1138. return NULL;
  1139. list->pn_type = TOK_COMMA;
  1140. PN_INIT_LIST(list);
  1141. }
  1142. PN_APPEND(list, item);
  1143. break;
  1144. }
  1145. #endif /* JS_HAS_DESTRUCTURING */
  1146. case TOK_NAME:
  1147. if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc))
  1148. return NULL;
  1149. break;
  1150. default:
  1151. js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
  1152. JSMSG_MISSING_FORMAL);
  1153. return NULL;
  1154. }
  1155. } while (js_MatchToken(cx, ts, TOK_COMMA));
  1156. MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
  1157. }
  1158. #if JS_HAS_EXPR_CLOSURES
  1159. ts->flags |= TSF_OPERAND;
  1160. tt = js_GetToken(cx, ts);
  1161. ts->flags &= ~TSF_OPERAND;
  1162. if (tt != TOK_LC) {
  1163. js_UngetToken(ts);
  1164. fun->flags |= JSFUN_EXPR_CLOSURE;
  1165. }
  1166. #else
  1167. MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
  1168. #endif
  1169. pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
  1170. body = FunctionBody(cx, ts, &funtc);
  1171. if (!body)
  1172. return NULL;
  1173. #if JS_HAS_EXPR_CLOSURES
  1174. if (tt == TOK_LC)
  1175. MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
  1176. else if (lambda == 0)
  1177. js_MatchToken(cx, ts, TOK_SEMI);
  1178. #else
  1179. MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
  1180. #endif
  1181. pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
  1182. #if JS_HAS_DESTRUCTURING
  1183. /*
  1184. * If there were destructuring formal parameters, prepend the initializing
  1185. * comma expression that we synthesized to body. If the body is a lexical
  1186. * scope node, we must make a special TOK_SEQ node, to prepend the formal
  1187. * parameter destructuring code without bracing the decompilation of the
  1188. * function body's lexical scope.
  1189. */
  1190. if (list) {
  1191. if (body->pn_arity != PN_LIST) {
  1192. JSParseNode *block;
  1193. block = NewParseNode(cx, ts, PN_LIST, tc);
  1194. if (!block)
  1195. return NULL;
  1196. block->pn_type = TOK_SEQ;
  1197. block->pn_pos = body->pn_pos;
  1198. PN_INIT_LIST_1(block, body);
  1199. body = block;
  1200. }
  1201. item = NewParseNode(cx, ts, PN_UNARY, tc);
  1202. if (!item)
  1203. return NULL;
  1204. item->pn_type = TOK_SEMI;
  1205. item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
  1206. item->pn_kid = list;
  1207. item->pn_next = body->pn_head;
  1208. body->pn_head = item;
  1209. if (body->pn_tail == &body->pn_head)
  1210. body->pn_tail = &item->pn_next;
  1211. ++body->pn_count;
  1212. }
  1213. #endif
  1214. /*
  1215. * If we collected flags that indicate nested heavyweight functions, or
  1216. * this function contains heavyweight-making statements (references to
  1217. * __parent__ or __proto__; use of with, or eval; and assignment to
  1218. * arguments), flag the function as heavyweight (requiring a call object
  1219. * per invocation).
  1220. */
  1221. if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
  1222. fun->flags |= JSFUN_HEAVYWEIGHT;
  1223. tc->flags |= TCF_FUN_HEAVYWEIGHT;
  1224. } else {
  1225. /*
  1226. * If this function is a named statement function not at top-level
  1227. * (i.e. not a top-level function definiton or expression), then
  1228. * our enclosing function, if any, must be heavyweight.
  1229. *
  1230. * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
  1231. * so it won't be set here. Assert that it's not. We have to check
  1232. * it later, in js_EmitTree, after js_EmitFunctionScript has traversed
  1233. * the function's body.
  1234. */
  1235. JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
  1236. if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc))
  1237. tc->flags |= TCF_FUN_HEAVYWEIGHT;
  1238. }
  1239. result = pn;
  1240. if (lambda != 0) {
  1241. /*
  1242. * ECMA ed. 3 standard: function expression, possibly anonymous.
  1243. */
  1244. op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
  1245. } else if (!funAtom) {
  1246. /*
  1247. * If this anonymous function definition is *not* embedded within a
  1248. * larger expression, we treat it as an expression statement, not as
  1249. * a function declaration -- and not as a syntax error (as ECMA-262
  1250. * Edition 3 would have it). Backward compatibility must trump all,
  1251. * unless JSOPTION_ANONFUNFIX is set.
  1252. */
  1253. result = NewParseNode(cx, ts, PN_UNARY, tc);
  1254. if (!result)
  1255. return NULL;
  1256. result->pn_type = TOK_SEMI;
  1257. result->pn_pos = pn->pn_pos;
  1258. result->pn_kid = pn;
  1259. op = JSOP_ANONFUNOBJ;
  1260. } else if (!AT_TOP_LEVEL(tc)) {
  1261. /*
  1262. * ECMA ed. 3 extension: a function expression statement not at the
  1263. * top level, e.g., in a compound statement such as the "then" part
  1264. * of an "if" statement, binds a closure only if control reaches that
  1265. * sub-statement.
  1266. */
  1267. op = JSOP_DEFFUN;
  1268. } else {
  1269. op = JSOP_NOP;
  1270. }
  1271. pn->pn_funpob = funpob;
  1272. pn->pn_op = op;
  1273. pn->pn_body = body;
  1274. pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO);
  1275. TREE_CONTEXT_FINISH(cx, &funtc);
  1276. return result;
  1277. }
  1278. static JSParseNode *
  1279. FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
  1280. {
  1281. return FunctionDef(cx, ts, tc, 0);
  1282. }
  1283. static JSParseNode *
  1284. FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
  1285. {
  1286. return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
  1287. }
  1288. /*
  1289. * Parse the statements in a block, creating a TOK_LC node that lists the
  1290. * statements' trees. If called from block-parsing code, the caller must
  1291. * match { before and } after.
  1292. */
  1293. static JSParseNode *
  1294. Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
  1295. {
  1296. JSParseNode *pn, *pn2, *saveBlock;
  1297. JSTokenType tt;
  1298. JS_CHECK_RECURSION(cx, return NULL);
  1299. pn = NewParseNode(cx, ts, PN_LIST, tc);
  1300. if (!pn)
  1301. return NULL;
  1302. saveBlock = tc->blockNode;
  1303. tc->blockNode = pn;
  1304. PN_INIT_LIST(pn);
  1305. for (;;) {
  1306. ts->flags |= TSF_OPERAND;
  1307. tt = js_PeekToken(cx, ts);
  1308. ts->flags &= ~TSF_OPERAND;
  1309. if (tt <= TOK_EOF || tt == TOK_RC) {
  1310. if (tt == TOK_ERROR)
  1311. return NULL;
  1312. break;
  1313. }
  1314. pn2 = Statement(cx, ts, tc);
  1315. if (!pn2) {
  1316. if (ts->flags & TSF_EOF)
  1317. ts->flags |= TSF_UNEXPECTED_EOF;
  1318. return NULL;
  1319. }
  1320. if (pn2->pn_type == TOK_FUNCTION) {
  1321. /*
  1322. * PNX_FUNCDEFS notifies the emitter that the block contains top-
  1323. * level function definitions that should be processed before the
  1324. * rest of nodes.
  1325. *
  1326. * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
  1327. * is relevant only for function definitions not at top-level,
  1328. * which we call function statements.
  1329. */
  1330. if (AT_TOP_LEVEL(tc))
  1331. pn->pn_extra |= PNX_FUNCDEFS;
  1332. else
  1333. tc->flags |= TCF_HAS_FUNCTION_STMT;
  1334. }
  1335. PN_APPEND(pn, pn2);
  1336. }
  1337. /*
  1338. * Handle the case where there was a let declaration under this block. If
  1339. * it replaced tc->blockNode with a new block node then we must refresh pn
  1340. * and then restore tc->blockNode.
  1341. */
  1342. if (tc->blockNode != pn)
  1343. pn = tc->blockNode;
  1344. tc->blockNode = saveBlock;
  1345. pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
  1346. return pn;
  1347. }
  1348. static JSParseNode *
  1349. Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
  1350. {
  1351. JSParseNode *pn;
  1352. MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
  1353. pn = ParenExpr(cx, ts, tc, NULL, NULL);
  1354. if (!pn)
  1355. return NULL;
  1356. MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
  1357. /*
  1358. * Check for (a = b) and warn about possible (a == b) mistype iff b's
  1359. * operator has greater precedence than ==.
  1360. */
  1361. if (pn->pn_type == TOK_ASSIGN &&
  1362. pn->pn_op == JSOP_NOP &&
  1363. pn->pn_right->pn_type > TOK_EQOP)
  1364. {
  1365. if (!js_ReportCompileErrorNumber(cx, ts, NULL,
  1366. JSREPORT_WARNING | JSREPORT_STRICT,
  1367. JSMSG_EQUAL_AS_ASSIGN,
  1368. "")) {
  1369. return NULL;
  1370. }
  1371. }
  1372. return pn;
  1373. }
  1374. static JSBool
  1375. MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
  1376. {
  1377. JSAtom *label;
  1378. JSTokenType tt;
  1379. tt = js_PeekTokenSameLine(cx, ts);
  1380. if (tt == TOK_ERROR)
  1381. return JS_FALSE;
  1382. if (tt == TOK_NAME) {
  1383. (void) js_GetToken(cx, ts);
  1384. label = CURRENT_TOKEN(ts).t_atom;
  1385. } else {
  1386. label = NULL;
  1387. }
  1388. pn->pn_atom = label;
  1389. return JS_TRUE;
  1390. }
  1391. static JSBool
  1392. BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
  1393. {
  1394. JSObject *blockObj;
  1395. JSScopeProperty *sprop;
  1396. JSAtomListElement *ale;
  1397. uintN n;
  1398. blockObj = tc->blockChain;
  1399. sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom));
  1400. ATOM_LIST_SEARCH(ale, &tc->decls, atom);
  1401. if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) {
  1402. const char *name;
  1403. if (sprop) {
  1404. JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
  1405. JS_ASSERT((uint16)sprop->shortid < OBJ_BLOCK_COUNT(cx, blockObj));
  1406. }
  1407. name = js_AtomToPrintableString(cx, atom);
  1408. if (name) {
  1409. js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
  1410. JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
  1411. (ale && ALE_JSOP(ale) == JSOP_DEFCONST)
  1412. ? js_const_str
  1413. : "variable",
  1414. name);
  1415. }
  1416. return JS_FALSE;
  1417. }
  1418. n = OBJ_BLOCK_COUNT(cx, blockObj);
  1419. if (n == JS_BIT(16)) {
  1420. js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
  1421. JSREPORT_ERROR, data->u.let.overflow);
  1422. return JS_FALSE;
  1423. }
  1424. /* Use JSPROP_ENUMERATE to aid the disassembler. */
  1425. return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),
  1426. JSVAL_VOID, NULL, NULL,
  1427. JSPROP_ENUMERATE |
  1428. JSPROP_PERMANENT |
  1429. JSPROP_SHARED,
  1430. SPROP_HAS_SHORTID, (int16) n, NULL);
  1431. }
  1432. static JSBool
  1433. BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
  1434. {
  1435. JSStmtInfo *stmt;
  1436. JSAtomListElement *ale;
  1437. JSOp op, prevop;
  1438. const char *name;
  1439. JSLocalKind localKind;
  1440. stmt = js_LexicalLookup(tc, atom, NULL);
  1441. ATOM_LIST_SEARCH(ale, &tc->decls, atom);
  1442. op = data->op;
  1443. if ((stmt && stmt->type != STMT_WITH) || ale) {
  1444. prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR;
  1445. if (JS_HAS_STRICT_OPTION(cx)
  1446. ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
  1447. : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
  1448. name = js_AtomToPrintableString(cx, atom);
  1449. if (!name ||
  1450. !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
  1451. (op != JSOP_DEFCONST &&
  1452. prevop != JSOP_DEFCONST)
  1453. ? JSREPORT_WARNING |
  1454. JSREPORT_STRICT
  1455. : JSREPORT_ERROR,
  1456. JSMSG_REDECLARED_VAR,
  1457. (prevop == JSOP_DEFFUN)
  1458. ? js_function_str
  1459. : (prevop == JSOP_DEFCONST)
  1460. ? js_const_str
  1461. : js_var_str,
  1462. name)) {
  1463. return JS_FALSE;
  1464. }
  1465. }
  1466. if (op == JSOP_DEFVAR && prevop == JSOP_DEFFUN)
  1467. tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
  1468. }
  1469. if (!ale) {
  1470. ale = js_IndexAtom(cx, atom, &tc->decls);
  1471. if (!ale)
  1472. return JS_FALSE;
  1473. }
  1474. ALE_SET_JSOP(ale, op);
  1475. if (!(tc->flags & TCF_IN_FUNCTION)) {
  1476. /*
  1477. * Don't lookup global variables or variables in an active frame at
  1478. * compile time.
  1479. */
  1480. return JS_TRUE;
  1481. }
  1482. localKind = js_LookupLocal(cx, tc->u.fun, atom, NULL);
  1483. if (localKind == JSLOCAL_NONE) {
  1484. /*
  1485. * Property not found in current variable scope: we have not seen this
  1486. * variable before. Define a new local variable by adding a property
  1487. * to the function's scope, allocating one slot in the function's vars
  1488. * frame. Any locals declared in with statement bodies are handled at
  1489. * runtime, by script prolog JSOP_DEFVAR opcodes generated for
  1490. * slot-less vars.
  1491. */
  1492. localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
  1493. if (!js_InWithStatement(tc) &&
  1494. !BindLocalVariable(cx, tc->u.fun, atom, localKind)) {
  1495. return JS_FALSE;
  1496. }
  1497. } else if (localKind == JSLOCAL_ARG) {
  1498. name = js_AtomToPrintableString(cx, atom);
  1499. if (!name)
  1500. return JS_FALSE;
  1501. if (op == JSOP_DEFCONST) {
  1502. js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
  1503. JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
  1504. name);
  1505. return JS_FALSE;
  1506. }
  1507. if (!js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
  1508. JSREPORT_WARNING | JSREPORT_STRICT,
  1509. JSMSG_VAR_HIDES_ARG, name)) {
  1510. return JS_FALSE;
  1511. }
  1512. } else {
  1513. /* Not an argument, must be a redeclared local var. */
  1514. JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
  1515. }
  1516. return JS_TRUE;
  1517. }
  1518. static JSBool
  1519. MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
  1520. {
  1521. JSParseNode *pn2;
  1522. JS_ASSERT(pn->pn_arity == PN_LIST);
  1523. JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
  1524. pn2 = pn->pn_head;
  1525. if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_flags & TCF_GENEXP_LAMBDA)) {
  1526. js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,
  1527. JSREPORT_ERROR, msg);
  1528. return JS_FALSE;
  1529. }
  1530. pn->pn_op = JSOP_SETCALL;
  1531. return JS_TRUE;
  1532. }
  1533. #if JS_HAS_DESTRUCTURING
  1534. static JSBool
  1535. BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
  1536. JSTreeContext *tc)
  1537. {
  1538. JSAtom *atom;
  1539. /*
  1540. * Destructuring is a form of assignment, so just as for an initialized
  1541. * simple variable, we must check for assignment to 'arguments' and flag
  1542. * the enclosing function (if any) as heavyweight.
  1543. */
  1544. JS_ASSERT(pn->pn_type == TOK_NAME);
  1545. atom = pn->pn_atom;
  1546. if (atom == cx->runtime->atomState.argumentsAtom)
  1547. tc->flags |= TCF_FUN_HEAVYWEIGHT;
  1548. data->pn = pn;
  1549. if (!data->binder(cx, data, atom, tc))
  1550. return JS_FALSE;
  1551. data->pn = NULL;
  1552. /*
  1553. * Select the appropriate name-setting opcode, which may be specialized
  1554. * further for local variable and argument slot optimizations. At this
  1555. * point, we can't select the optimal final opcode, yet we must preserve
  1556. * the CONST bit and convey "set", not "get".
  1557. */
  1558. if (data->op == JSOP_DEFCONST) {
  1559. pn->pn_op = JSOP_SETCONST;
  1560. pn->pn_const = JS_TRUE;
  1561. } else {
  1562. pn->pn_op = JSOP_SETNAME;
  1563. pn->pn_const = JS_FALSE;
  1564. }
  1565. return JS_TRUE;
  1566. }
  1567. /*
  1568. * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
  1569. * LHS expression except a destructuring initialiser, and R is on the stack.
  1570. * Because R is already evaluated, the usual LHS-specialized bytecodes won't
  1571. * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
  1572. * then push its property name QN. At this point the stack looks like
  1573. *
  1574. * [... R, R[P], QB, QN]
  1575. *
  1576. * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
  1577. * its operands with left-hand side above right-hand side:
  1578. *
  1579. * [rval, lval, xval]
  1580. *
  1581. * and pops all three values, setting lval[xval] = rval. But we cannot select
  1582. * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
  1583. * which can be optimized further. So we select JSOP_SETNAME.
  1584. */
  1585. static JSBool
  1586. BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
  1587. {
  1588. while (pn->pn_type == TOK_RP)
  1589. pn = pn->pn_kid;
  1590. switch (pn->pn_type) {
  1591. case TOK_NAME:
  1592. if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
  1593. tc->flags |= TCF_FUN_HEAVYWEIGHT;
  1594. /* FALL THROUGH */
  1595. case TOK_DOT:
  1596. case TOK_LB:
  1597. pn->pn_op = JSOP_SETNAME;
  1598. break;
  1599. #if JS_HAS_LVALUE_RETURN
  1600. case TOK_LP:
  1601. if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
  1602. return JS_FALSE;
  1603. break;
  1604. #endif
  1605. #if JS_HAS_XML_SUPPORT
  1606. case TOK_UNARYOP:
  1607. if (pn->pn_op == JSOP_XMLNAME) {
  1608. pn->pn_op = JSOP_BINDXMLNAME;
  1609. break;
  1610. }
  1611. /* FALL THROUGH */
  1612. #endif
  1613. default:
  1614. js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,
  1615. JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
  1616. return JS_FALSE;
  1617. }
  1618. return JS_TRUE;
  1619. }
  1620. typedef struct FindPropValData {
  1621. uint32 numvars; /* # of destructuring vars in left side */
  1622. uint32 maxstep; /* max # of steps searching right side */
  1623. JSDHashTable table; /* hash table for O(1) right side search */
  1624. } FindPropValData;
  1625. typedef struct FindPropValEntry {
  1626. JSDHashEntryHdr hdr;
  1627. JSParseNode *pnkey;
  1628. JSParseNode *pnval;
  1629. } FindPropValEntry;
  1630. #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
  1631. JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
  1632. ((pnkey)->pn_type == TOK_NUMBER || \
  1633. (pnkey)->pn_type == TOK_STRING || \
  1634. (pnkey)->pn_type == TOK_NAME))
  1635. static JSDHashNumber
  1636. HashFindPropValKey(JSDHashTable *table, const void *key)
  1637. {
  1638. const JSParseNode *pnkey = (const JSParseNode *)key;
  1639. ASSERT_VALID_PROPERTY_KEY(pnkey);
  1640. return (pnkey->pn_type == TOK_NUMBER)
  1641. ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
  1642. JSDOUBLE_LO32(pnkey->pn_dval))
  1643. : ATOM_HASH(pnkey->pn_atom);
  1644. }
  1645. static JSBool
  1646. MatchFindPropValEntry(JSDHashTable *table,
  1647. const JSDHashEntryHdr *entry,
  1648. const void *key)
  1649. {
  1650. const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
  1651. const JSParseNode *pnkey = (const JSParseNode *)key;
  1652. ASSERT_VALID_PROPERTY_KEY(pnkey);
  1653. return pnkey->pn_type == fpve->pnkey->pn_type &&
  1654. ((pnkey->pn_type == TOK_NUMBER)
  1655. ? pnkey->pn_dval == fpve->pnkey->pn_dval
  1656. : pnkey->pn_atom == fpve->pnkey->pn_atom);
  1657. }
  1658. static const JSDHashTableOps FindPropValOps = {
  1659. JS_DHashAllocTable,
  1660. JS_DHashFreeTable,
  1661. HashFindPropValKey,
  1662. MatchFindPropValEntry,
  1663. JS_DHashMoveEntryStub,
  1664. JS_DHashClearEntryStub,
  1665. JS_DHashFinalizeStub,
  1666. NULL
  1667. };
  1668. #define STEP_HASH_THRESHOLD 10
  1669. #define BIG_DESTRUCTURING 5
  1670. #define BIG_OBJECT_INIT 20
  1671. static JSParseNode *
  1672. FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
  1673. {
  1674. FindPropValEntry *entry;
  1675. JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
  1676. uint32 step;
  1677. /* If we have a hash table, use it as the sole source of truth. */
  1678. if (data->table.ops) {
  1679. entry = (FindPropValEntry *)
  1680. JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
  1681. return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
  1682. }
  1683. /* If pn is not an object initialiser node, we can't do anything here. */
  1684. if (pn->pn_type != TOK_RC)
  1685. return NULL;
  1686. /*
  1687. * We must search all the way through pn's list, to handle the case of an
  1688. * id duplicated for two or more property initialisers.
  1689. */
  1690. pnhit = NULL;
  1691. step = 0;
  1692. ASSERT_VALID_PROPERTY_KEY(pnid);
  1693. pnhead = pn->pn_head;
  1694. if (pnhead && pnhead->pn_type == TOK_DEFSHARP)
  1695. pnhead = pnhead->pn_next;
  1696. if (pnid->pn_type == TOK_NUMBER) {
  1697. for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
  1698. JS_ASSERT(pnprop->pn_type == TOK_COLON);
  1699. if (pnprop->pn_op == JSOP_NOP) {
  1700. pnkey = pnprop->pn_left;
  1701. ASSERT_VALID_PROPERTY_KEY(pnkey);
  1702. if (pnkey->pn_type == TOK_NUMBER &&
  1703. pnkey->pn_dval == pnid->pn_dval) {
  1704. pnhit = pnprop;
  1705. }
  1706. ++step;
  1707. }
  1708. }
  1709. } else {
  1710. for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
  1711. JS_ASSERT(pnprop->pn_type == TOK_COLON);
  1712. if (pnprop->pn_op == JSOP_NOP) {
  1713. pnkey = pnprop->pn_left;
  1714. ASSERT_VALID_PROPERTY_KEY(pnkey);
  1715. if (pnkey->pn_type == pnid->pn_type &&
  1716. pnkey->pn_atom == pnid->pn_atom) {
  1717. pnhit = pnprop;
  1718. }
  1719. ++step;
  1720. }
  1721. }
  1722. }
  1723. if (!pnhit)
  1724. return NULL;
  1725. /* Hit via full search -- see whether it's time to create the hash table. */
  1726. JS_ASSERT(!data->table.ops);
  1727. if (step > data->maxstep) {
  1728. data->maxstep = step;
  1729. if (step >= STEP_HASH_THRESHOLD &&
  1730. data->numvars >= BIG_DESTRUCTURING &&
  1731. pn->pn_count >= BIG_OBJECT_INIT &&
  1732. JS_DHashTableInit(&data->table, &FindPropValOps, pn,
  1733. sizeof(FindPropValEntry),
  1734. JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
  1735. {
  1736. for (pn = pnhead; pn; pn = pn->pn_next) {
  1737. JS_ASSERT(pnprop->pn_type == TOK_COLON);
  1738. ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
  1739. entry = (FindPropValEntry *)
  1740. JS_DHashTableOperate(&data->table, pn->pn_left,
  1741. JS_DHASH_ADD);
  1742. entry->pnval = pn->pn_right;
  1743. }
  1744. }
  1745. }
  1746. return pnhit->pn_right;
  1747. }
  1748. /*
  1749. * If data is null, the caller is AssignExpr and instead of binding variables,
  1750. * we specialize lvalues in the propery value positions of the left-hand side.
  1751. * If right is null, just check for well-formed lvalues.
  1752. */
  1753. static JSBool
  1754. CheckDestructuring(JSContext *cx, BindData *data,
  1755. JSParseNode *left, JSParseNode *right,
  1756. JSTreeContext *tc)
  1757. {
  1758. JSBool ok;
  1759. FindPropValData fpvd;
  1760. JSParseNode *lhs, *rhs, *pn, *pn2;
  1761. if (left->pn_type == TOK_ARRAYCOMP) {
  1762. js_ReportCompileErrorNumber(cx, TS(tc->parseContext), left,
  1763. JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
  1764. return JS_FALSE;
  1765. }
  1766. #if JS_HAS_DESTRUCTURING_SHORTHAND
  1767. if (right && right->pn_arity == PN_LIST && (right->pn_extra & PNX_SHORTHAND)) {
  1768. js_ReportCompileErrorNumber(cx, TS(tc->parseContext), right,
  1769. JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
  1770. return JS_FALSE;
  1771. }
  1772. #endif
  1773. fpvd.table.ops = NULL;
  1774. lhs = left->pn_head;
  1775. if (lhs && lhs->pn_type == TOK_DEFSHARP) {
  1776. pn = lhs;
  1777. goto no_var_name;
  1778. }
  1779. if (left->pn_type == TOK_RB) {
  1780. rhs = (right && right->pn_type == left->pn_type)
  1781. ? right->pn_head
  1782. : NULL;
  1783. while (lhs) {
  1784. pn = lhs, pn2 = rhs;
  1785. if (!data) {
  1786. /* Skip parenthesization if not in a variable declaration. */
  1787. while (pn->pn_type == TOK_RP)
  1788. pn = pn->pn_kid;
  1789. if (pn2) {
  1790. while (pn2->pn_type == TOK_RP)
  1791. pn2 = pn2->pn_kid;
  1792. }
  1793. }
  1794. /* Nullary comma is an eli