|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "frontend/FoldConstants.h" |
|
8 |
|
9 #include "mozilla/FloatingPoint.h" |
|
10 #include "mozilla/TypedEnum.h" |
|
11 |
|
12 #include "jslibmath.h" |
|
13 |
|
14 #include "frontend/ParseNode.h" |
|
15 #include "frontend/Parser.h" |
|
16 #include "vm/NumericConversions.h" |
|
17 |
|
18 #include "jscntxtinlines.h" |
|
19 #include "jsinferinlines.h" |
|
20 #include "jsobjinlines.h" |
|
21 |
|
22 using namespace js; |
|
23 using namespace js::frontend; |
|
24 |
|
25 using mozilla::IsNaN; |
|
26 using mozilla::IsNegative; |
|
27 using mozilla::NegativeInfinity; |
|
28 using mozilla::PositiveInfinity; |
|
29 using JS::GenericNaN; |
|
30 |
|
31 static bool |
|
32 ContainsVarOrConst(ExclusiveContext *cx, ParseNode *pn, ParseNode **resultp) |
|
33 { |
|
34 JS_CHECK_RECURSION(cx, return false); |
|
35 |
|
36 if (!pn) { |
|
37 *resultp = nullptr; |
|
38 return true; |
|
39 } |
|
40 if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) { |
|
41 *resultp = pn; |
|
42 return true; |
|
43 } |
|
44 switch (pn->getArity()) { |
|
45 case PN_LIST: |
|
46 for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
|
47 if (!ContainsVarOrConst(cx, pn2, resultp)) |
|
48 return false; |
|
49 if (*resultp) |
|
50 return true; |
|
51 } |
|
52 break; |
|
53 |
|
54 case PN_TERNARY: |
|
55 if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp)) |
|
56 return false; |
|
57 if (*resultp) |
|
58 return true; |
|
59 if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp)) |
|
60 return false; |
|
61 if (*resultp) |
|
62 return true; |
|
63 return ContainsVarOrConst(cx, pn->pn_kid3, resultp); |
|
64 |
|
65 case PN_BINARY: |
|
66 case PN_BINARY_OBJ: |
|
67 // Limit recursion if pn is a binary expression, which can't contain a |
|
68 // var statement. |
|
69 if (!pn->isOp(JSOP_NOP)) { |
|
70 *resultp = nullptr; |
|
71 return true; |
|
72 } |
|
73 if (!ContainsVarOrConst(cx, pn->pn_left, resultp)) |
|
74 return false; |
|
75 if (*resultp) |
|
76 return true; |
|
77 return ContainsVarOrConst(cx, pn->pn_right, resultp); |
|
78 |
|
79 case PN_UNARY: |
|
80 if (!pn->isOp(JSOP_NOP)) { |
|
81 *resultp = nullptr; |
|
82 return true; |
|
83 } |
|
84 return ContainsVarOrConst(cx, pn->pn_kid, resultp); |
|
85 |
|
86 case PN_NAME: |
|
87 return ContainsVarOrConst(cx, pn->maybeExpr(), resultp); |
|
88 |
|
89 default:; |
|
90 } |
|
91 *resultp = nullptr; |
|
92 return true; |
|
93 } |
|
94 |
|
95 /* |
|
96 * Fold from one constant type to another. |
|
97 * XXX handles only strings and numbers for now |
|
98 */ |
|
99 static bool |
|
100 FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind) |
|
101 { |
|
102 if (!pn->isKind(kind)) { |
|
103 switch (kind) { |
|
104 case PNK_NUMBER: |
|
105 if (pn->isKind(PNK_STRING)) { |
|
106 double d; |
|
107 if (!StringToNumber(cx, pn->pn_atom, &d)) |
|
108 return false; |
|
109 pn->pn_dval = d; |
|
110 pn->setKind(PNK_NUMBER); |
|
111 pn->setOp(JSOP_DOUBLE); |
|
112 } |
|
113 break; |
|
114 |
|
115 case PNK_STRING: |
|
116 if (pn->isKind(PNK_NUMBER)) { |
|
117 pn->pn_atom = NumberToAtom(cx, pn->pn_dval); |
|
118 if (!pn->pn_atom) |
|
119 return false; |
|
120 pn->setKind(PNK_STRING); |
|
121 pn->setOp(JSOP_STRING); |
|
122 } |
|
123 break; |
|
124 |
|
125 default:; |
|
126 } |
|
127 } |
|
128 return true; |
|
129 } |
|
130 |
|
131 /* |
|
132 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless |
|
133 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after |
|
134 * a successful call to this function. |
|
135 */ |
|
136 static bool |
|
137 FoldBinaryNumeric(ExclusiveContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2, |
|
138 ParseNode *pn) |
|
139 { |
|
140 double d, d2; |
|
141 int32_t i, j; |
|
142 |
|
143 JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)); |
|
144 d = pn1->pn_dval; |
|
145 d2 = pn2->pn_dval; |
|
146 switch (op) { |
|
147 case JSOP_LSH: |
|
148 case JSOP_RSH: |
|
149 i = ToInt32(d); |
|
150 j = ToInt32(d2); |
|
151 j &= 31; |
|
152 d = int32_t((op == JSOP_LSH) ? uint32_t(i) << j : i >> j); |
|
153 break; |
|
154 |
|
155 case JSOP_URSH: |
|
156 j = ToInt32(d2); |
|
157 j &= 31; |
|
158 d = ToUint32(d) >> j; |
|
159 break; |
|
160 |
|
161 case JSOP_ADD: |
|
162 d += d2; |
|
163 break; |
|
164 |
|
165 case JSOP_SUB: |
|
166 d -= d2; |
|
167 break; |
|
168 |
|
169 case JSOP_MUL: |
|
170 d *= d2; |
|
171 break; |
|
172 |
|
173 case JSOP_DIV: |
|
174 if (d2 == 0) { |
|
175 #if defined(XP_WIN) |
|
176 /* XXX MSVC miscompiles such that (NaN == 0) */ |
|
177 if (IsNaN(d2)) |
|
178 d = GenericNaN(); |
|
179 else |
|
180 #endif |
|
181 if (d == 0 || IsNaN(d)) |
|
182 d = GenericNaN(); |
|
183 else if (IsNegative(d) != IsNegative(d2)) |
|
184 d = NegativeInfinity<double>(); |
|
185 else |
|
186 d = PositiveInfinity<double>(); |
|
187 } else { |
|
188 d /= d2; |
|
189 } |
|
190 break; |
|
191 |
|
192 case JSOP_MOD: |
|
193 if (d2 == 0) { |
|
194 d = GenericNaN(); |
|
195 } else { |
|
196 d = js_fmod(d, d2); |
|
197 } |
|
198 break; |
|
199 |
|
200 default:; |
|
201 } |
|
202 |
|
203 /* Take care to allow pn1 or pn2 to alias pn. */ |
|
204 pn->setKind(PNK_NUMBER); |
|
205 pn->setOp(JSOP_DOUBLE); |
|
206 pn->setArity(PN_NULLARY); |
|
207 pn->pn_dval = d; |
|
208 return true; |
|
209 } |
|
210 |
|
211 // Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode, |
|
212 // *pn, in its place. |
|
213 // |
|
214 // pnp points to a ParseNode pointer. This must be the only pointer that points |
|
215 // to the parse node being replaced. The replacement, *pn, is unchanged except |
|
216 // for its pn_next pointer; updating that is necessary if *pn's new parent is a |
|
217 // list node. |
|
218 static void |
|
219 ReplaceNode(ParseNode **pnp, ParseNode *pn) |
|
220 { |
|
221 pn->pn_next = (*pnp)->pn_next; |
|
222 *pnp = pn; |
|
223 } |
|
224 |
|
225 enum Truthiness { Truthy, Falsy, Unknown }; |
|
226 |
|
227 static Truthiness |
|
228 Boolish(ParseNode *pn) |
|
229 { |
|
230 switch (pn->getKind()) { |
|
231 case PNK_NUMBER: |
|
232 return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy; |
|
233 |
|
234 case PNK_STRING: |
|
235 return (pn->pn_atom->length() > 0) ? Truthy : Falsy; |
|
236 |
|
237 case PNK_TRUE: |
|
238 case PNK_FUNCTION: |
|
239 case PNK_GENEXP: |
|
240 return Truthy; |
|
241 |
|
242 case PNK_FALSE: |
|
243 case PNK_NULL: |
|
244 return Falsy; |
|
245 |
|
246 default: |
|
247 return Unknown; |
|
248 } |
|
249 } |
|
250 |
|
251 // Expressions that appear in a few specific places are treated specially |
|
252 // during constant folding. This enum tells where a parse node appears. |
|
253 MOZ_BEGIN_ENUM_CLASS(SyntacticContext, int) |
|
254 // pn is an expression, and it appears in a context where only its side |
|
255 // effects and truthiness matter: the condition of an if statement, |
|
256 // conditional expression, while loop, or for(;;) loop; or an operand of && |
|
257 // or || in such a context. |
|
258 Condition, |
|
259 |
|
260 // pn is the operand of the 'delete' keyword. |
|
261 Delete, |
|
262 |
|
263 // Any other syntactic context. |
|
264 Other |
|
265 MOZ_END_ENUM_CLASS(SyntacticContext) |
|
266 |
|
267 static SyntacticContext |
|
268 condIf(const ParseNode *pn, ParseNodeKind kind) |
|
269 { |
|
270 return pn->isKind(kind) ? SyntacticContext::Condition : SyntacticContext::Other; |
|
271 } |
|
272 |
|
273 static bool |
|
274 Fold(ExclusiveContext *cx, ParseNode **pnp, |
|
275 FullParseHandler &handler, const ReadOnlyCompileOptions &options, |
|
276 bool inGenexpLambda, SyntacticContext sc) |
|
277 { |
|
278 ParseNode *pn = *pnp; |
|
279 ParseNode *pn1 = nullptr, *pn2 = nullptr, *pn3 = nullptr; |
|
280 |
|
281 JS_CHECK_RECURSION(cx, return false); |
|
282 |
|
283 // First, recursively fold constants on the children of this node. |
|
284 switch (pn->getArity()) { |
|
285 case PN_CODE: |
|
286 if (pn->isKind(PNK_FUNCTION) && |
|
287 pn->pn_funbox->useAsmOrInsideUseAsm() && options.asmJSOption) |
|
288 { |
|
289 return true; |
|
290 } else { |
|
291 // Note: pn_body is nullptr for functions which are being lazily parsed. |
|
292 JS_ASSERT(pn->getKind() == PNK_FUNCTION); |
|
293 if (pn->pn_body) { |
|
294 if (!Fold(cx, &pn->pn_body, handler, options, pn->pn_funbox->inGenexpLambda, |
|
295 SyntacticContext::Other)) |
|
296 return false; |
|
297 } |
|
298 } |
|
299 break; |
|
300 |
|
301 case PN_LIST: |
|
302 { |
|
303 // Propagate Condition context through logical connectives. |
|
304 SyntacticContext kidsc = SyntacticContext::Other; |
|
305 if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) |
|
306 kidsc = sc; |
|
307 |
|
308 // Don't fold a parenthesized call expression. See bug 537673. |
|
309 ParseNode **listp = &pn->pn_head; |
|
310 if ((pn->isKind(PNK_CALL) || pn->isKind(PNK_NEW)) && (*listp)->isInParens()) |
|
311 listp = &(*listp)->pn_next; |
|
312 |
|
313 for (; *listp; listp = &(*listp)->pn_next) { |
|
314 if (!Fold(cx, listp, handler, options, inGenexpLambda, kidsc)) |
|
315 return false; |
|
316 } |
|
317 |
|
318 // If the last node in the list was replaced, pn_tail points into the wrong node. |
|
319 pn->pn_tail = listp; |
|
320 |
|
321 // Save the list head in pn1 for later use. |
|
322 pn1 = pn->pn_head; |
|
323 pn2 = nullptr; |
|
324 break; |
|
325 } |
|
326 |
|
327 case PN_TERNARY: |
|
328 /* Any kid may be null (e.g. for (;;)). */ |
|
329 if (pn->pn_kid1) { |
|
330 if (!Fold(cx, &pn->pn_kid1, handler, options, inGenexpLambda, condIf(pn, PNK_IF))) |
|
331 return false; |
|
332 } |
|
333 pn1 = pn->pn_kid1; |
|
334 |
|
335 if (pn->pn_kid2) { |
|
336 if (!Fold(cx, &pn->pn_kid2, handler, options, inGenexpLambda, condIf(pn, PNK_FORHEAD))) |
|
337 return false; |
|
338 if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) { |
|
339 handler.freeTree(pn->pn_kid2); |
|
340 pn->pn_kid2 = nullptr; |
|
341 } |
|
342 } |
|
343 pn2 = pn->pn_kid2; |
|
344 |
|
345 if (pn->pn_kid3) { |
|
346 if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other)) |
|
347 return false; |
|
348 } |
|
349 pn3 = pn->pn_kid3; |
|
350 break; |
|
351 |
|
352 case PN_BINARY: |
|
353 case PN_BINARY_OBJ: |
|
354 if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) { |
|
355 // Propagate Condition context through logical connectives. |
|
356 SyntacticContext kidsc = SyntacticContext::Other; |
|
357 if (sc == SyntacticContext::Condition) |
|
358 kidsc = sc; |
|
359 if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, kidsc)) |
|
360 return false; |
|
361 if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, kidsc)) |
|
362 return false; |
|
363 } else { |
|
364 /* First kid may be null (for default case in switch). */ |
|
365 if (pn->pn_left) { |
|
366 if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, condIf(pn, PNK_WHILE))) |
|
367 return false; |
|
368 } |
|
369 if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, condIf(pn, PNK_DOWHILE))) |
|
370 return false; |
|
371 } |
|
372 pn1 = pn->pn_left; |
|
373 pn2 = pn->pn_right; |
|
374 break; |
|
375 |
|
376 case PN_UNARY: |
|
377 /* |
|
378 * Kludge to deal with typeof expressions: because constant folding |
|
379 * can turn an expression into a name node, we have to check here, |
|
380 * before folding, to see if we should throw undefined name errors. |
|
381 * |
|
382 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be |
|
383 * null. This assumption does not hold true for other unary |
|
384 * expressions. |
|
385 */ |
|
386 if (pn->isKind(PNK_TYPEOF) && !pn->pn_kid->isKind(PNK_NAME)) |
|
387 pn->setOp(JSOP_TYPEOFEXPR); |
|
388 |
|
389 if (pn->pn_kid) { |
|
390 SyntacticContext kidsc = |
|
391 pn->isKind(PNK_NOT) |
|
392 ? SyntacticContext::Condition |
|
393 : pn->isKind(PNK_DELETE) |
|
394 ? SyntacticContext::Delete |
|
395 : SyntacticContext::Other; |
|
396 if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc)) |
|
397 return false; |
|
398 } |
|
399 pn1 = pn->pn_kid; |
|
400 break; |
|
401 |
|
402 case PN_NAME: |
|
403 /* |
|
404 * Skip pn1 down along a chain of dotted member expressions to avoid |
|
405 * excessive recursion. Our only goal here is to fold constants (if |
|
406 * any) in the primary expression operand to the left of the first |
|
407 * dot in the chain. |
|
408 */ |
|
409 if (!pn->isUsed()) { |
|
410 ParseNode **lhsp = &pn->pn_expr; |
|
411 while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed()) |
|
412 lhsp = &(*lhsp)->pn_expr; |
|
413 if (*lhsp && !Fold(cx, lhsp, handler, options, inGenexpLambda, SyntacticContext::Other)) |
|
414 return false; |
|
415 pn1 = *lhsp; |
|
416 } |
|
417 break; |
|
418 |
|
419 case PN_NULLARY: |
|
420 break; |
|
421 } |
|
422 |
|
423 // The immediate child of a PNK_DELETE node should not be replaced |
|
424 // with node indicating a different syntactic form; |delete x| is not |
|
425 // the same as |delete (true && x)|. See bug 888002. |
|
426 // |
|
427 // pn is the immediate child in question. Its descendents were already |
|
428 // constant-folded above, so we're done. |
|
429 if (sc == SyntacticContext::Delete) |
|
430 return true; |
|
431 |
|
432 switch (pn->getKind()) { |
|
433 case PNK_IF: |
|
434 { |
|
435 ParseNode *decl; |
|
436 if (!ContainsVarOrConst(cx, pn2, &decl)) |
|
437 return false; |
|
438 if (decl) |
|
439 break; |
|
440 if (!ContainsVarOrConst(cx, pn3, &decl)) |
|
441 return false; |
|
442 if (decl) |
|
443 break; |
|
444 } |
|
445 /* FALL THROUGH */ |
|
446 |
|
447 case PNK_CONDITIONAL: |
|
448 /* Reduce 'if (C) T; else E' into T for true C, E for false. */ |
|
449 switch (pn1->getKind()) { |
|
450 case PNK_NUMBER: |
|
451 if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval)) |
|
452 pn2 = pn3; |
|
453 break; |
|
454 case PNK_STRING: |
|
455 if (pn1->pn_atom->length() == 0) |
|
456 pn2 = pn3; |
|
457 break; |
|
458 case PNK_TRUE: |
|
459 break; |
|
460 case PNK_FALSE: |
|
461 case PNK_NULL: |
|
462 pn2 = pn3; |
|
463 break; |
|
464 default: |
|
465 /* Early return to dodge common code that copies pn2 to pn. */ |
|
466 return true; |
|
467 } |
|
468 |
|
469 #if JS_HAS_GENERATOR_EXPRS |
|
470 /* Don't fold a trailing |if (0)| in a generator expression. */ |
|
471 if (!pn2 && inGenexpLambda) |
|
472 break; |
|
473 #endif |
|
474 |
|
475 if (pn2 && !pn2->isDefn()) { |
|
476 ReplaceNode(pnp, pn2); |
|
477 pn = pn2; |
|
478 } |
|
479 if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) { |
|
480 /* |
|
481 * False condition and no else, or an empty then-statement was |
|
482 * moved up over pn. Either way, make pn an empty block (not an |
|
483 * empty statement, which does not decompile, even when labeled). |
|
484 * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null |
|
485 * kid or an empty statement for a child. |
|
486 */ |
|
487 pn->setKind(PNK_STATEMENTLIST); |
|
488 pn->setArity(PN_LIST); |
|
489 pn->makeEmpty(); |
|
490 } |
|
491 if (pn3 && pn3 != pn2) |
|
492 handler.freeTree(pn3); |
|
493 break; |
|
494 |
|
495 case PNK_OR: |
|
496 case PNK_AND: |
|
497 if (sc == SyntacticContext::Condition) { |
|
498 if (pn->isArity(PN_LIST)) { |
|
499 ParseNode **listp = &pn->pn_head; |
|
500 JS_ASSERT(*listp == pn1); |
|
501 uint32_t orig = pn->pn_count; |
|
502 do { |
|
503 Truthiness t = Boolish(pn1); |
|
504 if (t == Unknown) { |
|
505 listp = &pn1->pn_next; |
|
506 continue; |
|
507 } |
|
508 if ((t == Truthy) == pn->isKind(PNK_OR)) { |
|
509 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) { |
|
510 pn3 = pn2->pn_next; |
|
511 handler.freeTree(pn2); |
|
512 --pn->pn_count; |
|
513 } |
|
514 pn1->pn_next = nullptr; |
|
515 break; |
|
516 } |
|
517 JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND)); |
|
518 if (pn->pn_count == 1) |
|
519 break; |
|
520 *listp = pn1->pn_next; |
|
521 handler.freeTree(pn1); |
|
522 --pn->pn_count; |
|
523 } while ((pn1 = *listp) != nullptr); |
|
524 |
|
525 // We may have to change arity from LIST to BINARY. |
|
526 pn1 = pn->pn_head; |
|
527 if (pn->pn_count == 2) { |
|
528 pn2 = pn1->pn_next; |
|
529 pn1->pn_next = nullptr; |
|
530 JS_ASSERT(!pn2->pn_next); |
|
531 pn->setArity(PN_BINARY); |
|
532 pn->pn_left = pn1; |
|
533 pn->pn_right = pn2; |
|
534 } else if (pn->pn_count == 1) { |
|
535 ReplaceNode(pnp, pn1); |
|
536 pn = pn1; |
|
537 } else if (orig != pn->pn_count) { |
|
538 // Adjust list tail. |
|
539 pn2 = pn1->pn_next; |
|
540 for (; pn1; pn2 = pn1, pn1 = pn1->pn_next) |
|
541 ; |
|
542 pn->pn_tail = &pn2->pn_next; |
|
543 } |
|
544 } else { |
|
545 Truthiness t = Boolish(pn1); |
|
546 if (t != Unknown) { |
|
547 if ((t == Truthy) == pn->isKind(PNK_OR)) { |
|
548 handler.freeTree(pn2); |
|
549 ReplaceNode(pnp, pn1); |
|
550 pn = pn1; |
|
551 } else { |
|
552 JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND)); |
|
553 handler.freeTree(pn1); |
|
554 ReplaceNode(pnp, pn2); |
|
555 pn = pn2; |
|
556 } |
|
557 } |
|
558 } |
|
559 } |
|
560 break; |
|
561 |
|
562 case PNK_SUBASSIGN: |
|
563 case PNK_BITORASSIGN: |
|
564 case PNK_BITXORASSIGN: |
|
565 case PNK_BITANDASSIGN: |
|
566 case PNK_LSHASSIGN: |
|
567 case PNK_RSHASSIGN: |
|
568 case PNK_URSHASSIGN: |
|
569 case PNK_MULASSIGN: |
|
570 case PNK_DIVASSIGN: |
|
571 case PNK_MODASSIGN: |
|
572 /* |
|
573 * Compound operators such as *= should be subject to folding, in case |
|
574 * the left-hand side is constant, and so that the decompiler produces |
|
575 * the same string that you get from decompiling a script or function |
|
576 * compiled from that same string. += is special and so must be |
|
577 * handled below. |
|
578 */ |
|
579 goto do_binary_op; |
|
580 |
|
581 case PNK_ADDASSIGN: |
|
582 JS_ASSERT(pn->isOp(JSOP_ADD)); |
|
583 /* FALL THROUGH */ |
|
584 case PNK_ADD: |
|
585 if (pn->isArity(PN_LIST)) { |
|
586 bool folded = false; |
|
587 |
|
588 pn2 = pn1->pn_next; |
|
589 if (pn1->isKind(PNK_NUMBER)) { |
|
590 // Fold addition of numeric literals: (1 + 2 + x === 3 + x). |
|
591 // Note that we can only do this the front of the list: |
|
592 // (x + 1 + 2 !== x + 3) when x is a string. |
|
593 while (pn2 && pn2->isKind(PNK_NUMBER)) { |
|
594 pn1->pn_dval += pn2->pn_dval; |
|
595 pn1->pn_next = pn2->pn_next; |
|
596 handler.freeTree(pn2); |
|
597 pn2 = pn1->pn_next; |
|
598 pn->pn_count--; |
|
599 folded = true; |
|
600 } |
|
601 } |
|
602 |
|
603 // Now search for adjacent pairs of literals to fold for string |
|
604 // concatenation. |
|
605 // |
|
606 // isStringConcat is true if we know the operation we're looking at |
|
607 // will be string concatenation at runtime. As soon as we see a |
|
608 // string, we know that every addition to the right of it will be |
|
609 // string concatenation, even if both operands are numbers: |
|
610 // ("s" + x + 1 + 2 === "s" + x + "12"). |
|
611 // |
|
612 bool isStringConcat = false; |
|
613 RootedString foldedStr(cx); |
|
614 |
|
615 // (number + string) is definitely concatenation, but only at the |
|
616 // front of the list: (x + 1 + "2" !== x + "12") when x is a |
|
617 // number. |
|
618 if (pn1->isKind(PNK_NUMBER) && pn2 && pn2->isKind(PNK_STRING)) |
|
619 isStringConcat = true; |
|
620 |
|
621 while (pn2) { |
|
622 isStringConcat = isStringConcat || pn1->isKind(PNK_STRING); |
|
623 |
|
624 if (isStringConcat && |
|
625 (pn1->isKind(PNK_STRING) || pn1->isKind(PNK_NUMBER)) && |
|
626 (pn2->isKind(PNK_STRING) || pn2->isKind(PNK_NUMBER))) |
|
627 { |
|
628 // Fold string concatenation of literals. |
|
629 if (pn1->isKind(PNK_NUMBER) && !FoldType(cx, pn1, PNK_STRING)) |
|
630 return false; |
|
631 if (pn2->isKind(PNK_NUMBER) && !FoldType(cx, pn2, PNK_STRING)) |
|
632 return false; |
|
633 if (!foldedStr) |
|
634 foldedStr = pn1->pn_atom; |
|
635 RootedString right(cx, pn2->pn_atom); |
|
636 foldedStr = ConcatStrings<CanGC>(cx, foldedStr, right); |
|
637 if (!foldedStr) |
|
638 return false; |
|
639 pn1->pn_next = pn2->pn_next; |
|
640 handler.freeTree(pn2); |
|
641 pn2 = pn1->pn_next; |
|
642 pn->pn_count--; |
|
643 folded = true; |
|
644 } else { |
|
645 if (foldedStr) { |
|
646 // Convert the rope of folded strings into an Atom. |
|
647 pn1->pn_atom = AtomizeString(cx, foldedStr); |
|
648 if (!pn1->pn_atom) |
|
649 return false; |
|
650 foldedStr = nullptr; |
|
651 } |
|
652 pn1 = pn2; |
|
653 pn2 = pn2->pn_next; |
|
654 } |
|
655 } |
|
656 |
|
657 if (foldedStr) { |
|
658 // Convert the rope of folded strings into an Atom. |
|
659 pn1->pn_atom = AtomizeString(cx, foldedStr); |
|
660 if (!pn1->pn_atom) |
|
661 return false; |
|
662 } |
|
663 |
|
664 if (folded) { |
|
665 if (pn->pn_count == 1) { |
|
666 // We reduced the list to one constant. There is no |
|
667 // addition anymore. Replace the PNK_ADD node with the |
|
668 // single PNK_STRING or PNK_NUMBER node. |
|
669 ReplaceNode(pnp, pn1); |
|
670 pn = pn1; |
|
671 } else if (!pn2) { |
|
672 pn->pn_tail = &pn1->pn_next; |
|
673 } |
|
674 } |
|
675 break; |
|
676 } |
|
677 |
|
678 /* Handle a binary string concatenation. */ |
|
679 JS_ASSERT(pn->isArity(PN_BINARY)); |
|
680 if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) { |
|
681 if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING)) |
|
682 return false; |
|
683 if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING)) |
|
684 return true; |
|
685 RootedString left(cx, pn1->pn_atom); |
|
686 RootedString right(cx, pn2->pn_atom); |
|
687 RootedString str(cx, ConcatStrings<CanGC>(cx, left, right)); |
|
688 if (!str) |
|
689 return false; |
|
690 pn->pn_atom = AtomizeString(cx, str); |
|
691 if (!pn->pn_atom) |
|
692 return false; |
|
693 pn->setKind(PNK_STRING); |
|
694 pn->setOp(JSOP_STRING); |
|
695 pn->setArity(PN_NULLARY); |
|
696 handler.freeTree(pn1); |
|
697 handler.freeTree(pn2); |
|
698 break; |
|
699 } |
|
700 |
|
701 /* Can't concatenate string literals, let's try numbers. */ |
|
702 goto do_binary_op; |
|
703 |
|
704 case PNK_SUB: |
|
705 case PNK_STAR: |
|
706 case PNK_LSH: |
|
707 case PNK_RSH: |
|
708 case PNK_URSH: |
|
709 case PNK_DIV: |
|
710 case PNK_MOD: |
|
711 do_binary_op: |
|
712 if (pn->isArity(PN_LIST)) { |
|
713 JS_ASSERT(pn->pn_count > 2); |
|
714 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { |
|
715 if (!FoldType(cx, pn2, PNK_NUMBER)) |
|
716 return false; |
|
717 } |
|
718 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { |
|
719 /* XXX fold only if all operands convert to number */ |
|
720 if (!pn2->isKind(PNK_NUMBER)) |
|
721 break; |
|
722 } |
|
723 if (!pn2) { |
|
724 JSOp op = pn->getOp(); |
|
725 |
|
726 pn2 = pn1->pn_next; |
|
727 pn3 = pn2->pn_next; |
|
728 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn)) |
|
729 return false; |
|
730 while ((pn2 = pn3) != nullptr) { |
|
731 pn3 = pn2->pn_next; |
|
732 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn)) |
|
733 return false; |
|
734 } |
|
735 } |
|
736 } else { |
|
737 JS_ASSERT(pn->isArity(PN_BINARY)); |
|
738 if (!FoldType(cx, pn1, PNK_NUMBER) || |
|
739 !FoldType(cx, pn2, PNK_NUMBER)) { |
|
740 return false; |
|
741 } |
|
742 if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) { |
|
743 if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn)) |
|
744 return false; |
|
745 } |
|
746 } |
|
747 break; |
|
748 |
|
749 case PNK_TYPEOF: |
|
750 case PNK_VOID: |
|
751 case PNK_NOT: |
|
752 case PNK_BITNOT: |
|
753 case PNK_POS: |
|
754 case PNK_NEG: |
|
755 if (pn1->isKind(PNK_NUMBER)) { |
|
756 double d; |
|
757 |
|
758 /* Operate on one numeric constant. */ |
|
759 d = pn1->pn_dval; |
|
760 switch (pn->getKind()) { |
|
761 case PNK_BITNOT: |
|
762 d = ~ToInt32(d); |
|
763 break; |
|
764 |
|
765 case PNK_NEG: |
|
766 d = -d; |
|
767 break; |
|
768 |
|
769 case PNK_POS: |
|
770 break; |
|
771 |
|
772 case PNK_NOT: |
|
773 if (d == 0 || IsNaN(d)) { |
|
774 pn->setKind(PNK_TRUE); |
|
775 pn->setOp(JSOP_TRUE); |
|
776 } else { |
|
777 pn->setKind(PNK_FALSE); |
|
778 pn->setOp(JSOP_FALSE); |
|
779 } |
|
780 pn->setArity(PN_NULLARY); |
|
781 /* FALL THROUGH */ |
|
782 |
|
783 default: |
|
784 /* Return early to dodge the common PNK_NUMBER code. */ |
|
785 return true; |
|
786 } |
|
787 pn->setKind(PNK_NUMBER); |
|
788 pn->setOp(JSOP_DOUBLE); |
|
789 pn->setArity(PN_NULLARY); |
|
790 pn->pn_dval = d; |
|
791 handler.freeTree(pn1); |
|
792 } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) { |
|
793 if (pn->isKind(PNK_NOT)) { |
|
794 ReplaceNode(pnp, pn1); |
|
795 pn = pn1; |
|
796 if (pn->isKind(PNK_TRUE)) { |
|
797 pn->setKind(PNK_FALSE); |
|
798 pn->setOp(JSOP_FALSE); |
|
799 } else { |
|
800 pn->setKind(PNK_TRUE); |
|
801 pn->setOp(JSOP_TRUE); |
|
802 } |
|
803 } |
|
804 } |
|
805 break; |
|
806 |
|
807 case PNK_ELEM: { |
|
808 // An indexed expression, pn1[pn2]. A few cases can be improved. |
|
809 PropertyName *name = nullptr; |
|
810 if (pn2->isKind(PNK_STRING)) { |
|
811 JSAtom *atom = pn2->pn_atom; |
|
812 uint32_t index; |
|
813 |
|
814 if (atom->isIndex(&index)) { |
|
815 // Optimization 1: We have something like pn1["100"]. This is |
|
816 // equivalent to pn1[100] which is faster. |
|
817 pn2->setKind(PNK_NUMBER); |
|
818 pn2->setOp(JSOP_DOUBLE); |
|
819 pn2->pn_dval = index; |
|
820 } else { |
|
821 name = atom->asPropertyName(); |
|
822 } |
|
823 } else if (pn2->isKind(PNK_NUMBER)) { |
|
824 double number = pn2->pn_dval; |
|
825 if (number != ToUint32(number)) { |
|
826 // Optimization 2: We have something like pn1[3.14]. The number |
|
827 // is not an array index. This is equivalent to pn1["3.14"] |
|
828 // which enables optimization 3 below. |
|
829 JSAtom *atom = ToAtom<NoGC>(cx, DoubleValue(number)); |
|
830 if (!atom) |
|
831 return false; |
|
832 name = atom->asPropertyName(); |
|
833 } |
|
834 } |
|
835 |
|
836 if (name && NameToId(name) == types::IdToTypeId(NameToId(name))) { |
|
837 // Optimization 3: We have pn1["foo"] where foo is not an index. |
|
838 // Convert to a property access (like pn1.foo) which we optimize |
|
839 // better downstream. Don't bother with this for names which TI |
|
840 // considers to be indexes, to simplify downstream analysis. |
|
841 ParseNode *expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end); |
|
842 if (!expr) |
|
843 return false; |
|
844 ReplaceNode(pnp, expr); |
|
845 |
|
846 pn->pn_left = nullptr; |
|
847 pn->pn_right = nullptr; |
|
848 handler.freeTree(pn); |
|
849 pn = expr; |
|
850 } |
|
851 break; |
|
852 } |
|
853 |
|
854 default:; |
|
855 } |
|
856 |
|
857 if (sc == SyntacticContext::Condition) { |
|
858 Truthiness t = Boolish(pn); |
|
859 if (t != Unknown) { |
|
860 /* |
|
861 * We can turn function nodes into constant nodes here, but mutating function |
|
862 * nodes is tricky --- in particular, mutating a function node that appears on |
|
863 * a method list corrupts the method list. However, methods are M's in |
|
864 * statements of the form 'this.foo = M;', which we never fold, so we're okay. |
|
865 */ |
|
866 handler.prepareNodeForMutation(pn); |
|
867 if (t == Truthy) { |
|
868 pn->setKind(PNK_TRUE); |
|
869 pn->setOp(JSOP_TRUE); |
|
870 } else { |
|
871 pn->setKind(PNK_FALSE); |
|
872 pn->setOp(JSOP_FALSE); |
|
873 } |
|
874 pn->setArity(PN_NULLARY); |
|
875 } |
|
876 } |
|
877 |
|
878 return true; |
|
879 } |
|
880 |
|
881 bool |
|
882 frontend::FoldConstants(ExclusiveContext *cx, ParseNode **pnp, Parser<FullParseHandler> *parser) |
|
883 { |
|
884 // Don't fold constants if the code has requested "use asm" as |
|
885 // constant-folding will misrepresent the source text for the purpose |
|
886 // of type checking. (Also guard against entering a function containing |
|
887 // "use asm", see PN_FUNC case below.) |
|
888 if (parser->pc->useAsmOrInsideUseAsm() && parser->options().asmJSOption) |
|
889 return true; |
|
890 |
|
891 return Fold(cx, pnp, parser->handler, parser->options(), false, SyntacticContext::Other); |
|
892 } |