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