1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/frontend/FoldConstants.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,892 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "frontend/FoldConstants.h" 1.11 + 1.12 +#include "mozilla/FloatingPoint.h" 1.13 +#include "mozilla/TypedEnum.h" 1.14 + 1.15 +#include "jslibmath.h" 1.16 + 1.17 +#include "frontend/ParseNode.h" 1.18 +#include "frontend/Parser.h" 1.19 +#include "vm/NumericConversions.h" 1.20 + 1.21 +#include "jscntxtinlines.h" 1.22 +#include "jsinferinlines.h" 1.23 +#include "jsobjinlines.h" 1.24 + 1.25 +using namespace js; 1.26 +using namespace js::frontend; 1.27 + 1.28 +using mozilla::IsNaN; 1.29 +using mozilla::IsNegative; 1.30 +using mozilla::NegativeInfinity; 1.31 +using mozilla::PositiveInfinity; 1.32 +using JS::GenericNaN; 1.33 + 1.34 +static bool 1.35 +ContainsVarOrConst(ExclusiveContext *cx, ParseNode *pn, ParseNode **resultp) 1.36 +{ 1.37 + JS_CHECK_RECURSION(cx, return false); 1.38 + 1.39 + if (!pn) { 1.40 + *resultp = nullptr; 1.41 + return true; 1.42 + } 1.43 + if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) { 1.44 + *resultp = pn; 1.45 + return true; 1.46 + } 1.47 + switch (pn->getArity()) { 1.48 + case PN_LIST: 1.49 + for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { 1.50 + if (!ContainsVarOrConst(cx, pn2, resultp)) 1.51 + return false; 1.52 + if (*resultp) 1.53 + return true; 1.54 + } 1.55 + break; 1.56 + 1.57 + case PN_TERNARY: 1.58 + if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp)) 1.59 + return false; 1.60 + if (*resultp) 1.61 + return true; 1.62 + if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp)) 1.63 + return false; 1.64 + if (*resultp) 1.65 + return true; 1.66 + return ContainsVarOrConst(cx, pn->pn_kid3, resultp); 1.67 + 1.68 + case PN_BINARY: 1.69 + case PN_BINARY_OBJ: 1.70 + // Limit recursion if pn is a binary expression, which can't contain a 1.71 + // var statement. 1.72 + if (!pn->isOp(JSOP_NOP)) { 1.73 + *resultp = nullptr; 1.74 + return true; 1.75 + } 1.76 + if (!ContainsVarOrConst(cx, pn->pn_left, resultp)) 1.77 + return false; 1.78 + if (*resultp) 1.79 + return true; 1.80 + return ContainsVarOrConst(cx, pn->pn_right, resultp); 1.81 + 1.82 + case PN_UNARY: 1.83 + if (!pn->isOp(JSOP_NOP)) { 1.84 + *resultp = nullptr; 1.85 + return true; 1.86 + } 1.87 + return ContainsVarOrConst(cx, pn->pn_kid, resultp); 1.88 + 1.89 + case PN_NAME: 1.90 + return ContainsVarOrConst(cx, pn->maybeExpr(), resultp); 1.91 + 1.92 + default:; 1.93 + } 1.94 + *resultp = nullptr; 1.95 + return true; 1.96 +} 1.97 + 1.98 +/* 1.99 + * Fold from one constant type to another. 1.100 + * XXX handles only strings and numbers for now 1.101 + */ 1.102 +static bool 1.103 +FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind) 1.104 +{ 1.105 + if (!pn->isKind(kind)) { 1.106 + switch (kind) { 1.107 + case PNK_NUMBER: 1.108 + if (pn->isKind(PNK_STRING)) { 1.109 + double d; 1.110 + if (!StringToNumber(cx, pn->pn_atom, &d)) 1.111 + return false; 1.112 + pn->pn_dval = d; 1.113 + pn->setKind(PNK_NUMBER); 1.114 + pn->setOp(JSOP_DOUBLE); 1.115 + } 1.116 + break; 1.117 + 1.118 + case PNK_STRING: 1.119 + if (pn->isKind(PNK_NUMBER)) { 1.120 + pn->pn_atom = NumberToAtom(cx, pn->pn_dval); 1.121 + if (!pn->pn_atom) 1.122 + return false; 1.123 + pn->setKind(PNK_STRING); 1.124 + pn->setOp(JSOP_STRING); 1.125 + } 1.126 + break; 1.127 + 1.128 + default:; 1.129 + } 1.130 + } 1.131 + return true; 1.132 +} 1.133 + 1.134 +/* 1.135 + * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless 1.136 + * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after 1.137 + * a successful call to this function. 1.138 + */ 1.139 +static bool 1.140 +FoldBinaryNumeric(ExclusiveContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2, 1.141 + ParseNode *pn) 1.142 +{ 1.143 + double d, d2; 1.144 + int32_t i, j; 1.145 + 1.146 + JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)); 1.147 + d = pn1->pn_dval; 1.148 + d2 = pn2->pn_dval; 1.149 + switch (op) { 1.150 + case JSOP_LSH: 1.151 + case JSOP_RSH: 1.152 + i = ToInt32(d); 1.153 + j = ToInt32(d2); 1.154 + j &= 31; 1.155 + d = int32_t((op == JSOP_LSH) ? uint32_t(i) << j : i >> j); 1.156 + break; 1.157 + 1.158 + case JSOP_URSH: 1.159 + j = ToInt32(d2); 1.160 + j &= 31; 1.161 + d = ToUint32(d) >> j; 1.162 + break; 1.163 + 1.164 + case JSOP_ADD: 1.165 + d += d2; 1.166 + break; 1.167 + 1.168 + case JSOP_SUB: 1.169 + d -= d2; 1.170 + break; 1.171 + 1.172 + case JSOP_MUL: 1.173 + d *= d2; 1.174 + break; 1.175 + 1.176 + case JSOP_DIV: 1.177 + if (d2 == 0) { 1.178 +#if defined(XP_WIN) 1.179 + /* XXX MSVC miscompiles such that (NaN == 0) */ 1.180 + if (IsNaN(d2)) 1.181 + d = GenericNaN(); 1.182 + else 1.183 +#endif 1.184 + if (d == 0 || IsNaN(d)) 1.185 + d = GenericNaN(); 1.186 + else if (IsNegative(d) != IsNegative(d2)) 1.187 + d = NegativeInfinity<double>(); 1.188 + else 1.189 + d = PositiveInfinity<double>(); 1.190 + } else { 1.191 + d /= d2; 1.192 + } 1.193 + break; 1.194 + 1.195 + case JSOP_MOD: 1.196 + if (d2 == 0) { 1.197 + d = GenericNaN(); 1.198 + } else { 1.199 + d = js_fmod(d, d2); 1.200 + } 1.201 + break; 1.202 + 1.203 + default:; 1.204 + } 1.205 + 1.206 + /* Take care to allow pn1 or pn2 to alias pn. */ 1.207 + pn->setKind(PNK_NUMBER); 1.208 + pn->setOp(JSOP_DOUBLE); 1.209 + pn->setArity(PN_NULLARY); 1.210 + pn->pn_dval = d; 1.211 + return true; 1.212 +} 1.213 + 1.214 +// Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode, 1.215 +// *pn, in its place. 1.216 +// 1.217 +// pnp points to a ParseNode pointer. This must be the only pointer that points 1.218 +// to the parse node being replaced. The replacement, *pn, is unchanged except 1.219 +// for its pn_next pointer; updating that is necessary if *pn's new parent is a 1.220 +// list node. 1.221 +static void 1.222 +ReplaceNode(ParseNode **pnp, ParseNode *pn) 1.223 +{ 1.224 + pn->pn_next = (*pnp)->pn_next; 1.225 + *pnp = pn; 1.226 +} 1.227 + 1.228 +enum Truthiness { Truthy, Falsy, Unknown }; 1.229 + 1.230 +static Truthiness 1.231 +Boolish(ParseNode *pn) 1.232 +{ 1.233 + switch (pn->getKind()) { 1.234 + case PNK_NUMBER: 1.235 + return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy; 1.236 + 1.237 + case PNK_STRING: 1.238 + return (pn->pn_atom->length() > 0) ? Truthy : Falsy; 1.239 + 1.240 + case PNK_TRUE: 1.241 + case PNK_FUNCTION: 1.242 + case PNK_GENEXP: 1.243 + return Truthy; 1.244 + 1.245 + case PNK_FALSE: 1.246 + case PNK_NULL: 1.247 + return Falsy; 1.248 + 1.249 + default: 1.250 + return Unknown; 1.251 + } 1.252 +} 1.253 + 1.254 +// Expressions that appear in a few specific places are treated specially 1.255 +// during constant folding. This enum tells where a parse node appears. 1.256 +MOZ_BEGIN_ENUM_CLASS(SyntacticContext, int) 1.257 + // pn is an expression, and it appears in a context where only its side 1.258 + // effects and truthiness matter: the condition of an if statement, 1.259 + // conditional expression, while loop, or for(;;) loop; or an operand of && 1.260 + // or || in such a context. 1.261 + Condition, 1.262 + 1.263 + // pn is the operand of the 'delete' keyword. 1.264 + Delete, 1.265 + 1.266 + // Any other syntactic context. 1.267 + Other 1.268 +MOZ_END_ENUM_CLASS(SyntacticContext) 1.269 + 1.270 +static SyntacticContext 1.271 +condIf(const ParseNode *pn, ParseNodeKind kind) 1.272 +{ 1.273 + return pn->isKind(kind) ? SyntacticContext::Condition : SyntacticContext::Other; 1.274 +} 1.275 + 1.276 +static bool 1.277 +Fold(ExclusiveContext *cx, ParseNode **pnp, 1.278 + FullParseHandler &handler, const ReadOnlyCompileOptions &options, 1.279 + bool inGenexpLambda, SyntacticContext sc) 1.280 +{ 1.281 + ParseNode *pn = *pnp; 1.282 + ParseNode *pn1 = nullptr, *pn2 = nullptr, *pn3 = nullptr; 1.283 + 1.284 + JS_CHECK_RECURSION(cx, return false); 1.285 + 1.286 + // First, recursively fold constants on the children of this node. 1.287 + switch (pn->getArity()) { 1.288 + case PN_CODE: 1.289 + if (pn->isKind(PNK_FUNCTION) && 1.290 + pn->pn_funbox->useAsmOrInsideUseAsm() && options.asmJSOption) 1.291 + { 1.292 + return true; 1.293 + } else { 1.294 + // Note: pn_body is nullptr for functions which are being lazily parsed. 1.295 + JS_ASSERT(pn->getKind() == PNK_FUNCTION); 1.296 + if (pn->pn_body) { 1.297 + if (!Fold(cx, &pn->pn_body, handler, options, pn->pn_funbox->inGenexpLambda, 1.298 + SyntacticContext::Other)) 1.299 + return false; 1.300 + } 1.301 + } 1.302 + break; 1.303 + 1.304 + case PN_LIST: 1.305 + { 1.306 + // Propagate Condition context through logical connectives. 1.307 + SyntacticContext kidsc = SyntacticContext::Other; 1.308 + if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) 1.309 + kidsc = sc; 1.310 + 1.311 + // Don't fold a parenthesized call expression. See bug 537673. 1.312 + ParseNode **listp = &pn->pn_head; 1.313 + if ((pn->isKind(PNK_CALL) || pn->isKind(PNK_NEW)) && (*listp)->isInParens()) 1.314 + listp = &(*listp)->pn_next; 1.315 + 1.316 + for (; *listp; listp = &(*listp)->pn_next) { 1.317 + if (!Fold(cx, listp, handler, options, inGenexpLambda, kidsc)) 1.318 + return false; 1.319 + } 1.320 + 1.321 + // If the last node in the list was replaced, pn_tail points into the wrong node. 1.322 + pn->pn_tail = listp; 1.323 + 1.324 + // Save the list head in pn1 for later use. 1.325 + pn1 = pn->pn_head; 1.326 + pn2 = nullptr; 1.327 + break; 1.328 + } 1.329 + 1.330 + case PN_TERNARY: 1.331 + /* Any kid may be null (e.g. for (;;)). */ 1.332 + if (pn->pn_kid1) { 1.333 + if (!Fold(cx, &pn->pn_kid1, handler, options, inGenexpLambda, condIf(pn, PNK_IF))) 1.334 + return false; 1.335 + } 1.336 + pn1 = pn->pn_kid1; 1.337 + 1.338 + if (pn->pn_kid2) { 1.339 + if (!Fold(cx, &pn->pn_kid2, handler, options, inGenexpLambda, condIf(pn, PNK_FORHEAD))) 1.340 + return false; 1.341 + if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) { 1.342 + handler.freeTree(pn->pn_kid2); 1.343 + pn->pn_kid2 = nullptr; 1.344 + } 1.345 + } 1.346 + pn2 = pn->pn_kid2; 1.347 + 1.348 + if (pn->pn_kid3) { 1.349 + if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other)) 1.350 + return false; 1.351 + } 1.352 + pn3 = pn->pn_kid3; 1.353 + break; 1.354 + 1.355 + case PN_BINARY: 1.356 + case PN_BINARY_OBJ: 1.357 + if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) { 1.358 + // Propagate Condition context through logical connectives. 1.359 + SyntacticContext kidsc = SyntacticContext::Other; 1.360 + if (sc == SyntacticContext::Condition) 1.361 + kidsc = sc; 1.362 + if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, kidsc)) 1.363 + return false; 1.364 + if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, kidsc)) 1.365 + return false; 1.366 + } else { 1.367 + /* First kid may be null (for default case in switch). */ 1.368 + if (pn->pn_left) { 1.369 + if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, condIf(pn, PNK_WHILE))) 1.370 + return false; 1.371 + } 1.372 + if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, condIf(pn, PNK_DOWHILE))) 1.373 + return false; 1.374 + } 1.375 + pn1 = pn->pn_left; 1.376 + pn2 = pn->pn_right; 1.377 + break; 1.378 + 1.379 + case PN_UNARY: 1.380 + /* 1.381 + * Kludge to deal with typeof expressions: because constant folding 1.382 + * can turn an expression into a name node, we have to check here, 1.383 + * before folding, to see if we should throw undefined name errors. 1.384 + * 1.385 + * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be 1.386 + * null. This assumption does not hold true for other unary 1.387 + * expressions. 1.388 + */ 1.389 + if (pn->isKind(PNK_TYPEOF) && !pn->pn_kid->isKind(PNK_NAME)) 1.390 + pn->setOp(JSOP_TYPEOFEXPR); 1.391 + 1.392 + if (pn->pn_kid) { 1.393 + SyntacticContext kidsc = 1.394 + pn->isKind(PNK_NOT) 1.395 + ? SyntacticContext::Condition 1.396 + : pn->isKind(PNK_DELETE) 1.397 + ? SyntacticContext::Delete 1.398 + : SyntacticContext::Other; 1.399 + if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc)) 1.400 + return false; 1.401 + } 1.402 + pn1 = pn->pn_kid; 1.403 + break; 1.404 + 1.405 + case PN_NAME: 1.406 + /* 1.407 + * Skip pn1 down along a chain of dotted member expressions to avoid 1.408 + * excessive recursion. Our only goal here is to fold constants (if 1.409 + * any) in the primary expression operand to the left of the first 1.410 + * dot in the chain. 1.411 + */ 1.412 + if (!pn->isUsed()) { 1.413 + ParseNode **lhsp = &pn->pn_expr; 1.414 + while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed()) 1.415 + lhsp = &(*lhsp)->pn_expr; 1.416 + if (*lhsp && !Fold(cx, lhsp, handler, options, inGenexpLambda, SyntacticContext::Other)) 1.417 + return false; 1.418 + pn1 = *lhsp; 1.419 + } 1.420 + break; 1.421 + 1.422 + case PN_NULLARY: 1.423 + break; 1.424 + } 1.425 + 1.426 + // The immediate child of a PNK_DELETE node should not be replaced 1.427 + // with node indicating a different syntactic form; |delete x| is not 1.428 + // the same as |delete (true && x)|. See bug 888002. 1.429 + // 1.430 + // pn is the immediate child in question. Its descendents were already 1.431 + // constant-folded above, so we're done. 1.432 + if (sc == SyntacticContext::Delete) 1.433 + return true; 1.434 + 1.435 + switch (pn->getKind()) { 1.436 + case PNK_IF: 1.437 + { 1.438 + ParseNode *decl; 1.439 + if (!ContainsVarOrConst(cx, pn2, &decl)) 1.440 + return false; 1.441 + if (decl) 1.442 + break; 1.443 + if (!ContainsVarOrConst(cx, pn3, &decl)) 1.444 + return false; 1.445 + if (decl) 1.446 + break; 1.447 + } 1.448 + /* FALL THROUGH */ 1.449 + 1.450 + case PNK_CONDITIONAL: 1.451 + /* Reduce 'if (C) T; else E' into T for true C, E for false. */ 1.452 + switch (pn1->getKind()) { 1.453 + case PNK_NUMBER: 1.454 + if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval)) 1.455 + pn2 = pn3; 1.456 + break; 1.457 + case PNK_STRING: 1.458 + if (pn1->pn_atom->length() == 0) 1.459 + pn2 = pn3; 1.460 + break; 1.461 + case PNK_TRUE: 1.462 + break; 1.463 + case PNK_FALSE: 1.464 + case PNK_NULL: 1.465 + pn2 = pn3; 1.466 + break; 1.467 + default: 1.468 + /* Early return to dodge common code that copies pn2 to pn. */ 1.469 + return true; 1.470 + } 1.471 + 1.472 +#if JS_HAS_GENERATOR_EXPRS 1.473 + /* Don't fold a trailing |if (0)| in a generator expression. */ 1.474 + if (!pn2 && inGenexpLambda) 1.475 + break; 1.476 +#endif 1.477 + 1.478 + if (pn2 && !pn2->isDefn()) { 1.479 + ReplaceNode(pnp, pn2); 1.480 + pn = pn2; 1.481 + } 1.482 + if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) { 1.483 + /* 1.484 + * False condition and no else, or an empty then-statement was 1.485 + * moved up over pn. Either way, make pn an empty block (not an 1.486 + * empty statement, which does not decompile, even when labeled). 1.487 + * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null 1.488 + * kid or an empty statement for a child. 1.489 + */ 1.490 + pn->setKind(PNK_STATEMENTLIST); 1.491 + pn->setArity(PN_LIST); 1.492 + pn->makeEmpty(); 1.493 + } 1.494 + if (pn3 && pn3 != pn2) 1.495 + handler.freeTree(pn3); 1.496 + break; 1.497 + 1.498 + case PNK_OR: 1.499 + case PNK_AND: 1.500 + if (sc == SyntacticContext::Condition) { 1.501 + if (pn->isArity(PN_LIST)) { 1.502 + ParseNode **listp = &pn->pn_head; 1.503 + JS_ASSERT(*listp == pn1); 1.504 + uint32_t orig = pn->pn_count; 1.505 + do { 1.506 + Truthiness t = Boolish(pn1); 1.507 + if (t == Unknown) { 1.508 + listp = &pn1->pn_next; 1.509 + continue; 1.510 + } 1.511 + if ((t == Truthy) == pn->isKind(PNK_OR)) { 1.512 + for (pn2 = pn1->pn_next; pn2; pn2 = pn3) { 1.513 + pn3 = pn2->pn_next; 1.514 + handler.freeTree(pn2); 1.515 + --pn->pn_count; 1.516 + } 1.517 + pn1->pn_next = nullptr; 1.518 + break; 1.519 + } 1.520 + JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND)); 1.521 + if (pn->pn_count == 1) 1.522 + break; 1.523 + *listp = pn1->pn_next; 1.524 + handler.freeTree(pn1); 1.525 + --pn->pn_count; 1.526 + } while ((pn1 = *listp) != nullptr); 1.527 + 1.528 + // We may have to change arity from LIST to BINARY. 1.529 + pn1 = pn->pn_head; 1.530 + if (pn->pn_count == 2) { 1.531 + pn2 = pn1->pn_next; 1.532 + pn1->pn_next = nullptr; 1.533 + JS_ASSERT(!pn2->pn_next); 1.534 + pn->setArity(PN_BINARY); 1.535 + pn->pn_left = pn1; 1.536 + pn->pn_right = pn2; 1.537 + } else if (pn->pn_count == 1) { 1.538 + ReplaceNode(pnp, pn1); 1.539 + pn = pn1; 1.540 + } else if (orig != pn->pn_count) { 1.541 + // Adjust list tail. 1.542 + pn2 = pn1->pn_next; 1.543 + for (; pn1; pn2 = pn1, pn1 = pn1->pn_next) 1.544 + ; 1.545 + pn->pn_tail = &pn2->pn_next; 1.546 + } 1.547 + } else { 1.548 + Truthiness t = Boolish(pn1); 1.549 + if (t != Unknown) { 1.550 + if ((t == Truthy) == pn->isKind(PNK_OR)) { 1.551 + handler.freeTree(pn2); 1.552 + ReplaceNode(pnp, pn1); 1.553 + pn = pn1; 1.554 + } else { 1.555 + JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND)); 1.556 + handler.freeTree(pn1); 1.557 + ReplaceNode(pnp, pn2); 1.558 + pn = pn2; 1.559 + } 1.560 + } 1.561 + } 1.562 + } 1.563 + break; 1.564 + 1.565 + case PNK_SUBASSIGN: 1.566 + case PNK_BITORASSIGN: 1.567 + case PNK_BITXORASSIGN: 1.568 + case PNK_BITANDASSIGN: 1.569 + case PNK_LSHASSIGN: 1.570 + case PNK_RSHASSIGN: 1.571 + case PNK_URSHASSIGN: 1.572 + case PNK_MULASSIGN: 1.573 + case PNK_DIVASSIGN: 1.574 + case PNK_MODASSIGN: 1.575 + /* 1.576 + * Compound operators such as *= should be subject to folding, in case 1.577 + * the left-hand side is constant, and so that the decompiler produces 1.578 + * the same string that you get from decompiling a script or function 1.579 + * compiled from that same string. += is special and so must be 1.580 + * handled below. 1.581 + */ 1.582 + goto do_binary_op; 1.583 + 1.584 + case PNK_ADDASSIGN: 1.585 + JS_ASSERT(pn->isOp(JSOP_ADD)); 1.586 + /* FALL THROUGH */ 1.587 + case PNK_ADD: 1.588 + if (pn->isArity(PN_LIST)) { 1.589 + bool folded = false; 1.590 + 1.591 + pn2 = pn1->pn_next; 1.592 + if (pn1->isKind(PNK_NUMBER)) { 1.593 + // Fold addition of numeric literals: (1 + 2 + x === 3 + x). 1.594 + // Note that we can only do this the front of the list: 1.595 + // (x + 1 + 2 !== x + 3) when x is a string. 1.596 + while (pn2 && pn2->isKind(PNK_NUMBER)) { 1.597 + pn1->pn_dval += pn2->pn_dval; 1.598 + pn1->pn_next = pn2->pn_next; 1.599 + handler.freeTree(pn2); 1.600 + pn2 = pn1->pn_next; 1.601 + pn->pn_count--; 1.602 + folded = true; 1.603 + } 1.604 + } 1.605 + 1.606 + // Now search for adjacent pairs of literals to fold for string 1.607 + // concatenation. 1.608 + // 1.609 + // isStringConcat is true if we know the operation we're looking at 1.610 + // will be string concatenation at runtime. As soon as we see a 1.611 + // string, we know that every addition to the right of it will be 1.612 + // string concatenation, even if both operands are numbers: 1.613 + // ("s" + x + 1 + 2 === "s" + x + "12"). 1.614 + // 1.615 + bool isStringConcat = false; 1.616 + RootedString foldedStr(cx); 1.617 + 1.618 + // (number + string) is definitely concatenation, but only at the 1.619 + // front of the list: (x + 1 + "2" !== x + "12") when x is a 1.620 + // number. 1.621 + if (pn1->isKind(PNK_NUMBER) && pn2 && pn2->isKind(PNK_STRING)) 1.622 + isStringConcat = true; 1.623 + 1.624 + while (pn2) { 1.625 + isStringConcat = isStringConcat || pn1->isKind(PNK_STRING); 1.626 + 1.627 + if (isStringConcat && 1.628 + (pn1->isKind(PNK_STRING) || pn1->isKind(PNK_NUMBER)) && 1.629 + (pn2->isKind(PNK_STRING) || pn2->isKind(PNK_NUMBER))) 1.630 + { 1.631 + // Fold string concatenation of literals. 1.632 + if (pn1->isKind(PNK_NUMBER) && !FoldType(cx, pn1, PNK_STRING)) 1.633 + return false; 1.634 + if (pn2->isKind(PNK_NUMBER) && !FoldType(cx, pn2, PNK_STRING)) 1.635 + return false; 1.636 + if (!foldedStr) 1.637 + foldedStr = pn1->pn_atom; 1.638 + RootedString right(cx, pn2->pn_atom); 1.639 + foldedStr = ConcatStrings<CanGC>(cx, foldedStr, right); 1.640 + if (!foldedStr) 1.641 + return false; 1.642 + pn1->pn_next = pn2->pn_next; 1.643 + handler.freeTree(pn2); 1.644 + pn2 = pn1->pn_next; 1.645 + pn->pn_count--; 1.646 + folded = true; 1.647 + } else { 1.648 + if (foldedStr) { 1.649 + // Convert the rope of folded strings into an Atom. 1.650 + pn1->pn_atom = AtomizeString(cx, foldedStr); 1.651 + if (!pn1->pn_atom) 1.652 + return false; 1.653 + foldedStr = nullptr; 1.654 + } 1.655 + pn1 = pn2; 1.656 + pn2 = pn2->pn_next; 1.657 + } 1.658 + } 1.659 + 1.660 + if (foldedStr) { 1.661 + // Convert the rope of folded strings into an Atom. 1.662 + pn1->pn_atom = AtomizeString(cx, foldedStr); 1.663 + if (!pn1->pn_atom) 1.664 + return false; 1.665 + } 1.666 + 1.667 + if (folded) { 1.668 + if (pn->pn_count == 1) { 1.669 + // We reduced the list to one constant. There is no 1.670 + // addition anymore. Replace the PNK_ADD node with the 1.671 + // single PNK_STRING or PNK_NUMBER node. 1.672 + ReplaceNode(pnp, pn1); 1.673 + pn = pn1; 1.674 + } else if (!pn2) { 1.675 + pn->pn_tail = &pn1->pn_next; 1.676 + } 1.677 + } 1.678 + break; 1.679 + } 1.680 + 1.681 + /* Handle a binary string concatenation. */ 1.682 + JS_ASSERT(pn->isArity(PN_BINARY)); 1.683 + if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) { 1.684 + if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING)) 1.685 + return false; 1.686 + if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING)) 1.687 + return true; 1.688 + RootedString left(cx, pn1->pn_atom); 1.689 + RootedString right(cx, pn2->pn_atom); 1.690 + RootedString str(cx, ConcatStrings<CanGC>(cx, left, right)); 1.691 + if (!str) 1.692 + return false; 1.693 + pn->pn_atom = AtomizeString(cx, str); 1.694 + if (!pn->pn_atom) 1.695 + return false; 1.696 + pn->setKind(PNK_STRING); 1.697 + pn->setOp(JSOP_STRING); 1.698 + pn->setArity(PN_NULLARY); 1.699 + handler.freeTree(pn1); 1.700 + handler.freeTree(pn2); 1.701 + break; 1.702 + } 1.703 + 1.704 + /* Can't concatenate string literals, let's try numbers. */ 1.705 + goto do_binary_op; 1.706 + 1.707 + case PNK_SUB: 1.708 + case PNK_STAR: 1.709 + case PNK_LSH: 1.710 + case PNK_RSH: 1.711 + case PNK_URSH: 1.712 + case PNK_DIV: 1.713 + case PNK_MOD: 1.714 + do_binary_op: 1.715 + if (pn->isArity(PN_LIST)) { 1.716 + JS_ASSERT(pn->pn_count > 2); 1.717 + for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { 1.718 + if (!FoldType(cx, pn2, PNK_NUMBER)) 1.719 + return false; 1.720 + } 1.721 + for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { 1.722 + /* XXX fold only if all operands convert to number */ 1.723 + if (!pn2->isKind(PNK_NUMBER)) 1.724 + break; 1.725 + } 1.726 + if (!pn2) { 1.727 + JSOp op = pn->getOp(); 1.728 + 1.729 + pn2 = pn1->pn_next; 1.730 + pn3 = pn2->pn_next; 1.731 + if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn)) 1.732 + return false; 1.733 + while ((pn2 = pn3) != nullptr) { 1.734 + pn3 = pn2->pn_next; 1.735 + if (!FoldBinaryNumeric(cx, op, pn, pn2, pn)) 1.736 + return false; 1.737 + } 1.738 + } 1.739 + } else { 1.740 + JS_ASSERT(pn->isArity(PN_BINARY)); 1.741 + if (!FoldType(cx, pn1, PNK_NUMBER) || 1.742 + !FoldType(cx, pn2, PNK_NUMBER)) { 1.743 + return false; 1.744 + } 1.745 + if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) { 1.746 + if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn)) 1.747 + return false; 1.748 + } 1.749 + } 1.750 + break; 1.751 + 1.752 + case PNK_TYPEOF: 1.753 + case PNK_VOID: 1.754 + case PNK_NOT: 1.755 + case PNK_BITNOT: 1.756 + case PNK_POS: 1.757 + case PNK_NEG: 1.758 + if (pn1->isKind(PNK_NUMBER)) { 1.759 + double d; 1.760 + 1.761 + /* Operate on one numeric constant. */ 1.762 + d = pn1->pn_dval; 1.763 + switch (pn->getKind()) { 1.764 + case PNK_BITNOT: 1.765 + d = ~ToInt32(d); 1.766 + break; 1.767 + 1.768 + case PNK_NEG: 1.769 + d = -d; 1.770 + break; 1.771 + 1.772 + case PNK_POS: 1.773 + break; 1.774 + 1.775 + case PNK_NOT: 1.776 + if (d == 0 || IsNaN(d)) { 1.777 + pn->setKind(PNK_TRUE); 1.778 + pn->setOp(JSOP_TRUE); 1.779 + } else { 1.780 + pn->setKind(PNK_FALSE); 1.781 + pn->setOp(JSOP_FALSE); 1.782 + } 1.783 + pn->setArity(PN_NULLARY); 1.784 + /* FALL THROUGH */ 1.785 + 1.786 + default: 1.787 + /* Return early to dodge the common PNK_NUMBER code. */ 1.788 + return true; 1.789 + } 1.790 + pn->setKind(PNK_NUMBER); 1.791 + pn->setOp(JSOP_DOUBLE); 1.792 + pn->setArity(PN_NULLARY); 1.793 + pn->pn_dval = d; 1.794 + handler.freeTree(pn1); 1.795 + } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) { 1.796 + if (pn->isKind(PNK_NOT)) { 1.797 + ReplaceNode(pnp, pn1); 1.798 + pn = pn1; 1.799 + if (pn->isKind(PNK_TRUE)) { 1.800 + pn->setKind(PNK_FALSE); 1.801 + pn->setOp(JSOP_FALSE); 1.802 + } else { 1.803 + pn->setKind(PNK_TRUE); 1.804 + pn->setOp(JSOP_TRUE); 1.805 + } 1.806 + } 1.807 + } 1.808 + break; 1.809 + 1.810 + case PNK_ELEM: { 1.811 + // An indexed expression, pn1[pn2]. A few cases can be improved. 1.812 + PropertyName *name = nullptr; 1.813 + if (pn2->isKind(PNK_STRING)) { 1.814 + JSAtom *atom = pn2->pn_atom; 1.815 + uint32_t index; 1.816 + 1.817 + if (atom->isIndex(&index)) { 1.818 + // Optimization 1: We have something like pn1["100"]. This is 1.819 + // equivalent to pn1[100] which is faster. 1.820 + pn2->setKind(PNK_NUMBER); 1.821 + pn2->setOp(JSOP_DOUBLE); 1.822 + pn2->pn_dval = index; 1.823 + } else { 1.824 + name = atom->asPropertyName(); 1.825 + } 1.826 + } else if (pn2->isKind(PNK_NUMBER)) { 1.827 + double number = pn2->pn_dval; 1.828 + if (number != ToUint32(number)) { 1.829 + // Optimization 2: We have something like pn1[3.14]. The number 1.830 + // is not an array index. This is equivalent to pn1["3.14"] 1.831 + // which enables optimization 3 below. 1.832 + JSAtom *atom = ToAtom<NoGC>(cx, DoubleValue(number)); 1.833 + if (!atom) 1.834 + return false; 1.835 + name = atom->asPropertyName(); 1.836 + } 1.837 + } 1.838 + 1.839 + if (name && NameToId(name) == types::IdToTypeId(NameToId(name))) { 1.840 + // Optimization 3: We have pn1["foo"] where foo is not an index. 1.841 + // Convert to a property access (like pn1.foo) which we optimize 1.842 + // better downstream. Don't bother with this for names which TI 1.843 + // considers to be indexes, to simplify downstream analysis. 1.844 + ParseNode *expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end); 1.845 + if (!expr) 1.846 + return false; 1.847 + ReplaceNode(pnp, expr); 1.848 + 1.849 + pn->pn_left = nullptr; 1.850 + pn->pn_right = nullptr; 1.851 + handler.freeTree(pn); 1.852 + pn = expr; 1.853 + } 1.854 + break; 1.855 + } 1.856 + 1.857 + default:; 1.858 + } 1.859 + 1.860 + if (sc == SyntacticContext::Condition) { 1.861 + Truthiness t = Boolish(pn); 1.862 + if (t != Unknown) { 1.863 + /* 1.864 + * We can turn function nodes into constant nodes here, but mutating function 1.865 + * nodes is tricky --- in particular, mutating a function node that appears on 1.866 + * a method list corrupts the method list. However, methods are M's in 1.867 + * statements of the form 'this.foo = M;', which we never fold, so we're okay. 1.868 + */ 1.869 + handler.prepareNodeForMutation(pn); 1.870 + if (t == Truthy) { 1.871 + pn->setKind(PNK_TRUE); 1.872 + pn->setOp(JSOP_TRUE); 1.873 + } else { 1.874 + pn->setKind(PNK_FALSE); 1.875 + pn->setOp(JSOP_FALSE); 1.876 + } 1.877 + pn->setArity(PN_NULLARY); 1.878 + } 1.879 + } 1.880 + 1.881 + return true; 1.882 +} 1.883 + 1.884 +bool 1.885 +frontend::FoldConstants(ExclusiveContext *cx, ParseNode **pnp, Parser<FullParseHandler> *parser) 1.886 +{ 1.887 + // Don't fold constants if the code has requested "use asm" as 1.888 + // constant-folding will misrepresent the source text for the purpose 1.889 + // of type checking. (Also guard against entering a function containing 1.890 + // "use asm", see PN_FUNC case below.) 1.891 + if (parser->pc->useAsmOrInsideUseAsm() && parser->options().asmJSOption) 1.892 + return true; 1.893 + 1.894 + return Fold(cx, pnp, parser->handler, parser->options(), false, SyntacticContext::Other); 1.895 +}