/js/src/frontend/FoldConstants.cpp

http://github.com/zpao/v8monkey · C++ · 935 lines · 710 code · 87 blank · 138 comment · 250 complexity · 2df0141c2e3eb8ce53a0c0d56461e7ae MD5 · raw file

  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-2011
  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 the GNU General Public License Version 2 or later (the "GPL"), or
  29. * 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. #include "frontend/FoldConstants.h"
  41. #include "jslibmath.h"
  42. #include "frontend/BytecodeEmitter.h"
  43. #include "frontend/ParseNode.h"
  44. #if JS_HAS_XML_SUPPORT
  45. #include "jsxml.h"
  46. #endif
  47. #include "jsatominlines.h"
  48. #include "vm/String-inl.h"
  49. using namespace js;
  50. static ParseNode *
  51. ContainsVarOrConst(ParseNode *pn)
  52. {
  53. if (!pn)
  54. return NULL;
  55. if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))
  56. return pn;
  57. switch (pn->getArity()) {
  58. case PN_LIST:
  59. for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
  60. if (ParseNode *pnt = ContainsVarOrConst(pn2))
  61. return pnt;
  62. }
  63. break;
  64. case PN_TERNARY:
  65. if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid1))
  66. return pnt;
  67. if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid2))
  68. return pnt;
  69. return ContainsVarOrConst(pn->pn_kid3);
  70. case PN_BINARY:
  71. /*
  72. * Limit recursion if pn is a binary expression, which can't contain a
  73. * var statement.
  74. */
  75. if (!pn->isOp(JSOP_NOP))
  76. return NULL;
  77. if (ParseNode *pnt = ContainsVarOrConst(pn->pn_left))
  78. return pnt;
  79. return ContainsVarOrConst(pn->pn_right);
  80. case PN_UNARY:
  81. if (!pn->isOp(JSOP_NOP))
  82. return NULL;
  83. return ContainsVarOrConst(pn->pn_kid);
  84. case PN_NAME:
  85. return ContainsVarOrConst(pn->maybeExpr());
  86. case PN_NAMESET:
  87. return ContainsVarOrConst(pn->pn_tree);
  88. default:;
  89. }
  90. return NULL;
  91. }
  92. /*
  93. * Fold from one constant type to another.
  94. * XXX handles only strings and numbers for now
  95. */
  96. static JSBool
  97. FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
  98. {
  99. if (!pn->isKind(kind)) {
  100. switch (kind) {
  101. case PNK_NUMBER:
  102. if (pn->isKind(PNK_STRING)) {
  103. jsdouble d;
  104. if (!ToNumber(cx, StringValue(pn->pn_atom), &d))
  105. return JS_FALSE;
  106. pn->pn_dval = d;
  107. pn->setKind(PNK_NUMBER);
  108. pn->setOp(JSOP_DOUBLE);
  109. }
  110. break;
  111. case PNK_STRING:
  112. if (pn->isKind(PNK_NUMBER)) {
  113. JSString *str = js_NumberToString(cx, pn->pn_dval);
  114. if (!str)
  115. return JS_FALSE;
  116. pn->pn_atom = js_AtomizeString(cx, str);
  117. if (!pn->pn_atom)
  118. return JS_FALSE;
  119. pn->setKind(PNK_STRING);
  120. pn->setOp(JSOP_STRING);
  121. }
  122. break;
  123. default:;
  124. }
  125. }
  126. return JS_TRUE;
  127. }
  128. /*
  129. * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
  130. * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
  131. * a successful call to this function.
  132. */
  133. static JSBool
  134. FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
  135. ParseNode *pn, TreeContext *tc)
  136. {
  137. jsdouble d, d2;
  138. int32_t i, j;
  139. JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
  140. d = pn1->pn_dval;
  141. d2 = pn2->pn_dval;
  142. switch (op) {
  143. case JSOP_LSH:
  144. case JSOP_RSH:
  145. i = js_DoubleToECMAInt32(d);
  146. j = js_DoubleToECMAInt32(d2);
  147. j &= 31;
  148. d = (op == JSOP_LSH) ? i << j : i >> j;
  149. break;
  150. case JSOP_URSH:
  151. j = js_DoubleToECMAInt32(d2);
  152. j &= 31;
  153. d = js_DoubleToECMAUint32(d) >> j;
  154. break;
  155. case JSOP_ADD:
  156. d += d2;
  157. break;
  158. case JSOP_SUB:
  159. d -= d2;
  160. break;
  161. case JSOP_MUL:
  162. d *= d2;
  163. break;
  164. case JSOP_DIV:
  165. if (d2 == 0) {
  166. #if defined(XP_WIN)
  167. /* XXX MSVC miscompiles such that (NaN == 0) */
  168. if (JSDOUBLE_IS_NaN(d2))
  169. d = js_NaN;
  170. else
  171. #endif
  172. if (d == 0 || JSDOUBLE_IS_NaN(d))
  173. d = js_NaN;
  174. else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
  175. d = js_NegativeInfinity;
  176. else
  177. d = js_PositiveInfinity;
  178. } else {
  179. d /= d2;
  180. }
  181. break;
  182. case JSOP_MOD:
  183. if (d2 == 0) {
  184. d = js_NaN;
  185. } else {
  186. d = js_fmod(d, d2);
  187. }
  188. break;
  189. default:;
  190. }
  191. /* Take care to allow pn1 or pn2 to alias pn. */
  192. if (pn1 != pn)
  193. tc->freeTree(pn1);
  194. if (pn2 != pn)
  195. tc->freeTree(pn2);
  196. pn->setKind(PNK_NUMBER);
  197. pn->setOp(JSOP_DOUBLE);
  198. pn->setArity(PN_NULLARY);
  199. pn->pn_dval = d;
  200. return JS_TRUE;
  201. }
  202. #if JS_HAS_XML_SUPPORT
  203. static JSBool
  204. FoldXMLConstants(JSContext *cx, ParseNode *pn, TreeContext *tc)
  205. {
  206. JS_ASSERT(pn->isArity(PN_LIST));
  207. ParseNodeKind kind = pn->getKind();
  208. ParseNode **pnp = &pn->pn_head;
  209. ParseNode *pn1 = *pnp;
  210. JSString *accum = NULL;
  211. JSString *str = NULL;
  212. if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
  213. if (kind == PNK_XMLETAGO)
  214. accum = cx->runtime->atomState.etagoAtom;
  215. else if (kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC)
  216. accum = cx->runtime->atomState.stagoAtom;
  217. }
  218. /*
  219. * GC Rooting here is tricky: for most of the loop, |accum| is safe via
  220. * the newborn string root. However, when |pn2->getKind()| is PNK_XMLCDATA,
  221. * PNK_XMLCOMMENT, or PNK_XMLPI it is knocked out of the newborn root.
  222. * Therefore, we have to add additonal protection from GC nesting under
  223. * js_ConcatStrings.
  224. */
  225. ParseNode *pn2;
  226. uint32_t i, j;
  227. for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
  228. /* The parser already rejected end-tags with attributes. */
  229. JS_ASSERT(kind != PNK_XMLETAGO || i == 0);
  230. switch (pn2->getKind()) {
  231. case PNK_XMLATTR:
  232. if (!accum)
  233. goto cantfold;
  234. /* FALL THROUGH */
  235. case PNK_XMLNAME:
  236. case PNK_XMLSPACE:
  237. case PNK_XMLTEXT:
  238. case PNK_STRING:
  239. if (pn2->isArity(PN_LIST))
  240. goto cantfold;
  241. str = pn2->pn_atom;
  242. break;
  243. case PNK_XMLCDATA:
  244. str = js_MakeXMLCDATAString(cx, pn2->pn_atom);
  245. if (!str)
  246. return JS_FALSE;
  247. break;
  248. case PNK_XMLCOMMENT:
  249. str = js_MakeXMLCommentString(cx, pn2->pn_atom);
  250. if (!str)
  251. return JS_FALSE;
  252. break;
  253. case PNK_XMLPI: {
  254. XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction();
  255. str = js_MakeXMLPIString(cx, pi.target(), pi.data());
  256. if (!str)
  257. return JS_FALSE;
  258. break;
  259. }
  260. cantfold:
  261. default:
  262. JS_ASSERT(*pnp == pn1);
  263. if ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) &&
  264. (i & 1) ^ (j & 1)) {
  265. #ifdef DEBUG_brendanXXX
  266. printf("1: %d, %d => ", i, j);
  267. if (accum)
  268. FileEscapedString(stdout, accum, 0);
  269. else
  270. fputs("NULL", stdout);
  271. fputc('\n', stdout);
  272. #endif
  273. } else if (accum && pn1 != pn2) {
  274. while (pn1->pn_next != pn2) {
  275. pn1 = tc->freeTree(pn1);
  276. --pn->pn_count;
  277. }
  278. pn1->setKind(PNK_XMLTEXT);
  279. pn1->setOp(JSOP_STRING);
  280. pn1->setArity(PN_NULLARY);
  281. pn1->pn_atom = js_AtomizeString(cx, accum);
  282. if (!pn1->pn_atom)
  283. return JS_FALSE;
  284. JS_ASSERT(pnp != &pn1->pn_next);
  285. *pnp = pn1;
  286. }
  287. pnp = &pn2->pn_next;
  288. pn1 = *pnp;
  289. accum = NULL;
  290. continue;
  291. }
  292. if (accum) {
  293. {
  294. str = ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) && i != 0)
  295. ? js_AddAttributePart(cx, i & 1, accum, str)
  296. : js_ConcatStrings(cx, accum, str);
  297. }
  298. if (!str)
  299. return JS_FALSE;
  300. #ifdef DEBUG_brendanXXX
  301. printf("2: %d, %d => ", i, j);
  302. FileEscapedString(stdout, str, 0);
  303. printf(" (%u)\n", str->length());
  304. #endif
  305. ++j;
  306. }
  307. accum = str;
  308. }
  309. if (accum) {
  310. str = NULL;
  311. if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
  312. if (kind == PNK_XMLPTAGC)
  313. str = cx->runtime->atomState.ptagcAtom;
  314. else if (kind == PNK_XMLSTAGO || kind == PNK_XMLETAGO)
  315. str = cx->runtime->atomState.tagcAtom;
  316. }
  317. if (str) {
  318. accum = js_ConcatStrings(cx, accum, str);
  319. if (!accum)
  320. return JS_FALSE;
  321. }
  322. JS_ASSERT(*pnp == pn1);
  323. while (pn1->pn_next) {
  324. pn1 = tc->freeTree(pn1);
  325. --pn->pn_count;
  326. }
  327. pn1->setKind(PNK_XMLTEXT);
  328. pn1->setOp(JSOP_STRING);
  329. pn1->setArity(PN_NULLARY);
  330. pn1->pn_atom = js_AtomizeString(cx, accum);
  331. if (!pn1->pn_atom)
  332. return JS_FALSE;
  333. JS_ASSERT(pnp != &pn1->pn_next);
  334. *pnp = pn1;
  335. }
  336. if (pn1 && pn->pn_count == 1) {
  337. /*
  338. * Only one node under pn, and it has been folded: move pn1 onto pn
  339. * unless pn is an XML root (in which case we need it to tell the code
  340. * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
  341. * XML root *and* it's a point-tag, rewrite it to PNK_XMLELEM to avoid
  342. * extra "<" and "/>" bracketing at runtime.
  343. */
  344. if (!(pn->pn_xflags & PNX_XMLROOT)) {
  345. pn->become(pn1);
  346. } else if (kind == PNK_XMLPTAGC) {
  347. pn->setKind(PNK_XMLELEM);
  348. pn->setOp(JSOP_TOXML);
  349. }
  350. }
  351. return JS_TRUE;
  352. }
  353. #endif /* JS_HAS_XML_SUPPORT */
  354. enum Truthiness { Truthy, Falsy, Unknown };
  355. static Truthiness
  356. Boolish(ParseNode *pn)
  357. {
  358. switch (pn->getOp()) {
  359. case JSOP_DOUBLE:
  360. return (pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval)) ? Truthy : Falsy;
  361. case JSOP_STRING:
  362. return (pn->pn_atom->length() > 0) ? Truthy : Falsy;
  363. #if JS_HAS_GENERATOR_EXPRS
  364. case JSOP_CALL:
  365. {
  366. /*
  367. * A generator expression as an if or loop condition has no effects, it
  368. * simply results in a truthy object reference. This condition folding
  369. * is needed for the decompiler. See bug 442342 and bug 443074.
  370. */
  371. if (pn->pn_count != 1)
  372. return Unknown;
  373. ParseNode *pn2 = pn->pn_head;
  374. if (!pn2->isKind(PNK_FUNCTION))
  375. return Unknown;
  376. if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
  377. return Unknown;
  378. return Truthy;
  379. }
  380. #endif
  381. case JSOP_DEFFUN:
  382. case JSOP_LAMBDA:
  383. case JSOP_TRUE:
  384. return Truthy;
  385. case JSOP_NULL:
  386. case JSOP_FALSE:
  387. return Falsy;
  388. default:
  389. return Unknown;
  390. }
  391. }
  392. bool
  393. js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond)
  394. {
  395. ParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
  396. JS_CHECK_RECURSION(cx, return false);
  397. switch (pn->getArity()) {
  398. case PN_FUNC:
  399. {
  400. uint32_t oldflags = tc->flags;
  401. FunctionBox *oldlist = tc->functionList;
  402. tc->flags = pn->pn_funbox->tcflags;
  403. tc->functionList = pn->pn_funbox->kids;
  404. if (!FoldConstants(cx, pn->pn_body, tc))
  405. return false;
  406. pn->pn_funbox->kids = tc->functionList;
  407. tc->flags = oldflags;
  408. tc->functionList = oldlist;
  409. break;
  410. }
  411. case PN_LIST:
  412. {
  413. /* Propagate inCond through logical connectives. */
  414. bool cond = inCond && (pn->isKind(PNK_OR) || pn->isKind(PNK_AND));
  415. /* Don't fold a parenthesized call expression. See bug 537673. */
  416. pn1 = pn2 = pn->pn_head;
  417. if ((pn->isKind(PNK_LP) || pn->isKind(PNK_NEW)) && pn2->isInParens())
  418. pn2 = pn2->pn_next;
  419. /* Save the list head in pn1 for later use. */
  420. for (; pn2; pn2 = pn2->pn_next) {
  421. if (!FoldConstants(cx, pn2, tc, cond))
  422. return false;
  423. }
  424. break;
  425. }
  426. case PN_TERNARY:
  427. /* Any kid may be null (e.g. for (;;)). */
  428. pn1 = pn->pn_kid1;
  429. pn2 = pn->pn_kid2;
  430. pn3 = pn->pn_kid3;
  431. if (pn1 && !FoldConstants(cx, pn1, tc, pn->isKind(PNK_IF)))
  432. return false;
  433. if (pn2) {
  434. if (!FoldConstants(cx, pn2, tc, pn->isKind(PNK_FORHEAD)))
  435. return false;
  436. if (pn->isKind(PNK_FORHEAD) && pn2->isOp(JSOP_TRUE)) {
  437. tc->freeTree(pn2);
  438. pn->pn_kid2 = NULL;
  439. }
  440. }
  441. if (pn3 && !FoldConstants(cx, pn3, tc))
  442. return false;
  443. break;
  444. case PN_BINARY:
  445. pn1 = pn->pn_left;
  446. pn2 = pn->pn_right;
  447. /* Propagate inCond through logical connectives. */
  448. if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
  449. if (!FoldConstants(cx, pn1, tc, inCond))
  450. return false;
  451. if (!FoldConstants(cx, pn2, tc, inCond))
  452. return false;
  453. break;
  454. }
  455. /* First kid may be null (for default case in switch). */
  456. if (pn1 && !FoldConstants(cx, pn1, tc, pn->isKind(PNK_WHILE)))
  457. return false;
  458. if (!FoldConstants(cx, pn2, tc, pn->isKind(PNK_DOWHILE)))
  459. return false;
  460. break;
  461. case PN_UNARY:
  462. pn1 = pn->pn_kid;
  463. /*
  464. * Kludge to deal with typeof expressions: because constant folding
  465. * can turn an expression into a name node, we have to check here,
  466. * before folding, to see if we should throw undefined name errors.
  467. *
  468. * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
  469. * null. This assumption does not hold true for other unary
  470. * expressions.
  471. */
  472. if (pn->isOp(JSOP_TYPEOF) && !pn1->isKind(PNK_NAME))
  473. pn->setOp(JSOP_TYPEOFEXPR);
  474. if (pn1 && !FoldConstants(cx, pn1, tc, pn->isOp(JSOP_NOT)))
  475. return false;
  476. break;
  477. case PN_NAME:
  478. /*
  479. * Skip pn1 down along a chain of dotted member expressions to avoid
  480. * excessive recursion. Our only goal here is to fold constants (if
  481. * any) in the primary expression operand to the left of the first
  482. * dot in the chain.
  483. */
  484. if (!pn->isUsed()) {
  485. pn1 = pn->pn_expr;
  486. while (pn1 && pn1->isArity(PN_NAME) && !pn1->isUsed())
  487. pn1 = pn1->pn_expr;
  488. if (pn1 && !FoldConstants(cx, pn1, tc))
  489. return false;
  490. }
  491. break;
  492. case PN_NAMESET:
  493. pn1 = pn->pn_tree;
  494. if (!FoldConstants(cx, pn1, tc))
  495. return false;
  496. break;
  497. case PN_NULLARY:
  498. break;
  499. }
  500. switch (pn->getKind()) {
  501. case PNK_IF:
  502. if (ContainsVarOrConst(pn2) || ContainsVarOrConst(pn3))
  503. break;
  504. /* FALL THROUGH */
  505. case PNK_CONDITIONAL:
  506. /* Reduce 'if (C) T; else E' into T for true C, E for false. */
  507. switch (pn1->getKind()) {
  508. case PNK_NUMBER:
  509. if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
  510. pn2 = pn3;
  511. break;
  512. case PNK_STRING:
  513. if (pn1->pn_atom->length() == 0)
  514. pn2 = pn3;
  515. break;
  516. case PNK_TRUE:
  517. break;
  518. case PNK_FALSE:
  519. case PNK_NULL:
  520. pn2 = pn3;
  521. break;
  522. default:
  523. /* Early return to dodge common code that copies pn2 to pn. */
  524. return true;
  525. }
  526. #if JS_HAS_GENERATOR_EXPRS
  527. /* Don't fold a trailing |if (0)| in a generator expression. */
  528. if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
  529. break;
  530. #endif
  531. if (pn2 && !pn2->isDefn())
  532. pn->become(pn2);
  533. if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) {
  534. /*
  535. * False condition and no else, or an empty then-statement was
  536. * moved up over pn. Either way, make pn an empty block (not an
  537. * empty statement, which does not decompile, even when labeled).
  538. * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
  539. * kid or an empty statement for a child.
  540. */
  541. pn->setKind(PNK_STATEMENTLIST);
  542. pn->setArity(PN_LIST);
  543. pn->makeEmpty();
  544. }
  545. tc->freeTree(pn2);
  546. if (pn3 && pn3 != pn2)
  547. tc->freeTree(pn3);
  548. break;
  549. case PNK_OR:
  550. case PNK_AND:
  551. if (inCond) {
  552. if (pn->isArity(PN_LIST)) {
  553. ParseNode **pnp = &pn->pn_head;
  554. JS_ASSERT(*pnp == pn1);
  555. do {
  556. Truthiness t = Boolish(pn1);
  557. if (t == Unknown) {
  558. pnp = &pn1->pn_next;
  559. continue;
  560. }
  561. if ((t == Truthy) == pn->isKind(PNK_OR)) {
  562. for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
  563. pn3 = pn2->pn_next;
  564. tc->freeTree(pn2);
  565. --pn->pn_count;
  566. }
  567. pn1->pn_next = NULL;
  568. break;
  569. }
  570. JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
  571. if (pn->pn_count == 1)
  572. break;
  573. *pnp = pn1->pn_next;
  574. tc->freeTree(pn1);
  575. --pn->pn_count;
  576. } while ((pn1 = *pnp) != NULL);
  577. // We may have to change arity from LIST to BINARY.
  578. pn1 = pn->pn_head;
  579. if (pn->pn_count == 2) {
  580. pn2 = pn1->pn_next;
  581. pn1->pn_next = NULL;
  582. JS_ASSERT(!pn2->pn_next);
  583. pn->setArity(PN_BINARY);
  584. pn->pn_left = pn1;
  585. pn->pn_right = pn2;
  586. } else if (pn->pn_count == 1) {
  587. pn->become(pn1);
  588. tc->freeTree(pn1);
  589. }
  590. } else {
  591. Truthiness t = Boolish(pn1);
  592. if (t != Unknown) {
  593. if ((t == Truthy) == pn->isKind(PNK_OR)) {
  594. tc->freeTree(pn2);
  595. pn->become(pn1);
  596. } else {
  597. JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
  598. tc->freeTree(pn1);
  599. pn->become(pn2);
  600. }
  601. }
  602. }
  603. }
  604. break;
  605. case PNK_SUBASSIGN:
  606. case PNK_BITORASSIGN:
  607. case PNK_BITXORASSIGN:
  608. case PNK_BITANDASSIGN:
  609. case PNK_LSHASSIGN:
  610. case PNK_RSHASSIGN:
  611. case PNK_URSHASSIGN:
  612. case PNK_MULASSIGN:
  613. case PNK_DIVASSIGN:
  614. case PNK_MODASSIGN:
  615. /*
  616. * Compound operators such as *= should be subject to folding, in case
  617. * the left-hand side is constant, and so that the decompiler produces
  618. * the same string that you get from decompiling a script or function
  619. * compiled from that same string. += is special and so must be
  620. * handled below.
  621. */
  622. goto do_binary_op;
  623. case PNK_ADDASSIGN:
  624. JS_ASSERT(pn->isOp(JSOP_ADD));
  625. /* FALL THROUGH */
  626. case PNK_ADD:
  627. if (pn->isArity(PN_LIST)) {
  628. /*
  629. * Any string literal term with all others number or string means
  630. * this is a concatenation. If any term is not a string or number
  631. * literal, we can't fold.
  632. */
  633. JS_ASSERT(pn->pn_count > 2);
  634. if (pn->pn_xflags & PNX_CANTFOLD)
  635. return true;
  636. if (pn->pn_xflags != PNX_STRCAT)
  637. goto do_binary_op;
  638. /* Ok, we're concatenating: convert non-string constant operands. */
  639. size_t length = 0;
  640. for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
  641. if (!FoldType(cx, pn2, PNK_STRING))
  642. return false;
  643. /* XXX fold only if all operands convert to string */
  644. if (!pn2->isKind(PNK_STRING))
  645. return true;
  646. length += pn2->pn_atom->length();
  647. }
  648. /* Allocate a new buffer and string descriptor for the result. */
  649. jschar *chars = (jschar *) cx->malloc_((length + 1) * sizeof(jschar));
  650. if (!chars)
  651. return false;
  652. chars[length] = 0;
  653. JSString *str = js_NewString(cx, chars, length);
  654. if (!str) {
  655. cx->free_(chars);
  656. return false;
  657. }
  658. /* Fill the buffer, advancing chars and recycling kids as we go. */
  659. for (pn2 = pn1; pn2; pn2 = tc->freeTree(pn2)) {
  660. JSAtom *atom = pn2->pn_atom;
  661. size_t length2 = atom->length();
  662. js_strncpy(chars, atom->chars(), length2);
  663. chars += length2;
  664. }
  665. JS_ASSERT(*chars == 0);
  666. /* Atomize the result string and mutate pn to refer to it. */
  667. pn->pn_atom = js_AtomizeString(cx, str);
  668. if (!pn->pn_atom)
  669. return false;
  670. pn->setKind(PNK_STRING);
  671. pn->setOp(JSOP_STRING);
  672. pn->setArity(PN_NULLARY);
  673. break;
  674. }
  675. /* Handle a binary string concatenation. */
  676. JS_ASSERT(pn->isArity(PN_BINARY));
  677. if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) {
  678. JSString *left, *right, *str;
  679. if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING))
  680. return false;
  681. if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING))
  682. return true;
  683. left = pn1->pn_atom;
  684. right = pn2->pn_atom;
  685. str = js_ConcatStrings(cx, left, right);
  686. if (!str)
  687. return false;
  688. pn->pn_atom = js_AtomizeString(cx, str);
  689. if (!pn->pn_atom)
  690. return false;
  691. pn->setKind(PNK_STRING);
  692. pn->setOp(JSOP_STRING);
  693. pn->setArity(PN_NULLARY);
  694. tc->freeTree(pn1);
  695. tc->freeTree(pn2);
  696. break;
  697. }
  698. /* Can't concatenate string literals, let's try numbers. */
  699. goto do_binary_op;
  700. case PNK_SUB:
  701. case PNK_STAR:
  702. case PNK_LSH:
  703. case PNK_RSH:
  704. case PNK_URSH:
  705. case PNK_DIV:
  706. case PNK_MOD:
  707. do_binary_op:
  708. if (pn->isArity(PN_LIST)) {
  709. JS_ASSERT(pn->pn_count > 2);
  710. for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
  711. if (!FoldType(cx, pn2, PNK_NUMBER))
  712. return false;
  713. }
  714. for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
  715. /* XXX fold only if all operands convert to number */
  716. if (!pn2->isKind(PNK_NUMBER))
  717. break;
  718. }
  719. if (!pn2) {
  720. JSOp op = pn->getOp();
  721. pn2 = pn1->pn_next;
  722. pn3 = pn2->pn_next;
  723. if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
  724. return false;
  725. while ((pn2 = pn3) != NULL) {
  726. pn3 = pn2->pn_next;
  727. if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
  728. return false;
  729. }
  730. }
  731. } else {
  732. JS_ASSERT(pn->isArity(PN_BINARY));
  733. if (!FoldType(cx, pn1, PNK_NUMBER) ||
  734. !FoldType(cx, pn2, PNK_NUMBER)) {
  735. return false;
  736. }
  737. if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
  738. if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn, tc))
  739. return false;
  740. }
  741. }
  742. break;
  743. case PNK_TYPEOF:
  744. case PNK_VOID:
  745. case PNK_NOT:
  746. case PNK_BITNOT:
  747. case PNK_POS:
  748. case PNK_NEG:
  749. if (pn1->isKind(PNK_NUMBER)) {
  750. jsdouble d;
  751. /* Operate on one numeric constant. */
  752. d = pn1->pn_dval;
  753. switch (pn->getOp()) {
  754. case JSOP_BITNOT:
  755. d = ~js_DoubleToECMAInt32(d);
  756. break;
  757. case JSOP_NEG:
  758. d = -d;
  759. break;
  760. case JSOP_POS:
  761. break;
  762. case JSOP_NOT:
  763. if (d == 0 || JSDOUBLE_IS_NaN(d)) {
  764. pn->setKind(PNK_TRUE);
  765. pn->setOp(JSOP_TRUE);
  766. } else {
  767. pn->setKind(PNK_FALSE);
  768. pn->setOp(JSOP_FALSE);
  769. }
  770. pn->setArity(PN_NULLARY);
  771. /* FALL THROUGH */
  772. default:
  773. /* Return early to dodge the common PNK_NUMBER code. */
  774. return true;
  775. }
  776. pn->setKind(PNK_NUMBER);
  777. pn->setOp(JSOP_DOUBLE);
  778. pn->setArity(PN_NULLARY);
  779. pn->pn_dval = d;
  780. tc->freeTree(pn1);
  781. } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) {
  782. if (pn->isOp(JSOP_NOT)) {
  783. pn->become(pn1);
  784. if (pn->isKind(PNK_TRUE)) {
  785. pn->setKind(PNK_FALSE);
  786. pn->setOp(JSOP_FALSE);
  787. } else {
  788. pn->setKind(PNK_TRUE);
  789. pn->setOp(JSOP_TRUE);
  790. }
  791. tc->freeTree(pn1);
  792. }
  793. }
  794. break;
  795. #if JS_HAS_XML_SUPPORT
  796. case PNK_XMLELEM:
  797. case PNK_XMLLIST:
  798. case PNK_XMLPTAGC:
  799. case PNK_XMLSTAGO:
  800. case PNK_XMLETAGO:
  801. case PNK_XMLNAME:
  802. if (pn->isArity(PN_LIST)) {
  803. JS_ASSERT(pn->isKind(PNK_XMLLIST) || pn->pn_count != 0);
  804. if (!FoldXMLConstants(cx, pn, tc))
  805. return false;
  806. }
  807. break;
  808. case PNK_AT:
  809. if (pn1->isKind(PNK_XMLNAME)) {
  810. Value v = StringValue(pn1->pn_atom);
  811. if (!js_ToAttributeName(cx, &v))
  812. return false;
  813. JS_ASSERT(v.isObject());
  814. ObjectBox *xmlbox = tc->parser->newObjectBox(&v.toObject());
  815. if (!xmlbox)
  816. return false;
  817. pn->setKind(PNK_XMLNAME);
  818. pn->setOp(JSOP_OBJECT);
  819. pn->setArity(PN_NULLARY);
  820. pn->pn_objbox = xmlbox;
  821. tc->freeTree(pn1);
  822. }
  823. break;
  824. #endif /* JS_HAS_XML_SUPPORT */
  825. default:;
  826. }
  827. if (inCond) {
  828. Truthiness t = Boolish(pn);
  829. if (t != Unknown) {
  830. /*
  831. * We can turn function nodes into constant nodes here, but mutating function
  832. * nodes is tricky --- in particular, mutating a function node that appears on
  833. * a method list corrupts the method list. However, methods are M's in
  834. * statements of the form 'this.foo = M;', which we never fold, so we're okay.
  835. */
  836. tc->parser->allocator.prepareNodeForMutation(pn);
  837. if (t == Truthy) {
  838. pn->setKind(PNK_TRUE);
  839. pn->setOp(JSOP_TRUE);
  840. } else {
  841. pn->setKind(PNK_FALSE);
  842. pn->setOp(JSOP_FALSE);
  843. }
  844. pn->setArity(PN_NULLARY);
  845. }
  846. }
  847. return true;
  848. }