js/src/frontend/FoldConstants.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial