FoldConstants.cpp (52703B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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/Maybe.h" // mozilla::Maybe 10 #include "mozilla/Try.h" // MOZ_TRY* 11 12 #include "jslibmath.h" 13 #include "jsmath.h" 14 15 #include "frontend/FullParseHandler.h" 16 #include "frontend/ParseNode.h" 17 #include "frontend/ParseNodeVisitor.h" 18 #include "frontend/Parser-macros.h" // MOZ_TRY_VAR_OR_RETURN 19 #include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex 20 #include "js/Conversions.h" 21 #include "js/Stack.h" // JS::NativeStackLimit 22 #include "util/StringBuilder.h" // StringBuilder 23 24 using namespace js; 25 using namespace js::frontend; 26 27 using JS::ToInt32; 28 using JS::ToUint32; 29 30 struct FoldInfo { 31 FrontendContext* fc; 32 ParserAtomsTable& parserAtoms; 33 BigIntStencilVector& bigInts; 34 FullParseHandler* handler; 35 }; 36 37 // Don't use ReplaceNode directly, because we want the constant folder to keep 38 // the attributes isInParens and isDirectRHSAnonFunction of the old node being 39 // replaced. 40 [[nodiscard]] inline bool TryReplaceNode(ParseNode** pnp, 41 ParseNodeResult result) { 42 // convenience check: can call TryReplaceNode(pnp, alloc_parsenode()) 43 // directly, without having to worry about alloc returning null. 44 if (result.isErr()) { 45 return false; 46 } 47 auto* pn = result.unwrap(); 48 pn->setInParens((*pnp)->isInParens()); 49 pn->setDirectRHSAnonFunction((*pnp)->isDirectRHSAnonFunction()); 50 ReplaceNode(pnp, pn); 51 return true; 52 } 53 54 static bool ContainsHoistedDeclaration(FoldInfo& info, ParseNode* node, 55 bool* result); 56 57 static bool ListContainsHoistedDeclaration(FoldInfo& info, ListNode* list, 58 bool* result) { 59 for (ParseNode* node : list->contents()) { 60 if (!ContainsHoistedDeclaration(info, node, result)) { 61 return false; 62 } 63 if (*result) { 64 return true; 65 } 66 } 67 68 *result = false; 69 return true; 70 } 71 72 // Determines whether the given ParseNode contains any declarations whose 73 // visibility will extend outside the node itself -- that is, whether the 74 // ParseNode contains any var statements. 75 // 76 // THIS IS NOT A GENERAL-PURPOSE FUNCTION. It is only written to work in the 77 // specific context of deciding that |node|, as one arm of a ParseNodeKind::If 78 // controlled by a constant condition, contains a declaration that forbids 79 // |node| being completely eliminated as dead. 80 static bool ContainsHoistedDeclaration(FoldInfo& info, ParseNode* node, 81 bool* result) { 82 AutoCheckRecursionLimit recursion(info.fc); 83 if (!recursion.check(info.fc)) { 84 return false; 85 } 86 87 restart: 88 89 // With a better-typed AST, we would have distinct parse node classes for 90 // expressions and for statements and would characterize expressions with 91 // ExpressionKind and statements with StatementKind. Perhaps someday. In 92 // the meantime we must characterize every ParseNodeKind, even the 93 // expression/sub-expression ones that, if we handle all statement kinds 94 // correctly, we'll never see. 95 switch (node->getKind()) { 96 // Base case. 97 case ParseNodeKind::VarStmt: 98 *result = true; 99 return true; 100 101 // Non-global lexical declarations are block-scoped (ergo not hoistable). 102 case ParseNodeKind::LetDecl: 103 case ParseNodeKind::ConstDecl: 104 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 105 case ParseNodeKind::UsingDecl: 106 case ParseNodeKind::AwaitUsingDecl: 107 #endif 108 MOZ_ASSERT(node->is<ListNode>()); 109 *result = false; 110 return true; 111 112 // Similarly to the lexical declarations above, classes cannot add hoisted 113 // declarations 114 case ParseNodeKind::ClassDecl: 115 MOZ_ASSERT(node->is<ClassNode>()); 116 *result = false; 117 return true; 118 119 // Function declarations *can* be hoisted declarations. But in the 120 // magical world of the rewritten frontend, the declaration necessitated 121 // by a nested function statement, not at body level, doesn't require 122 // that we preserve an unreachable function declaration node against 123 // dead-code removal. 124 case ParseNodeKind::Function: 125 *result = false; 126 return true; 127 128 case ParseNodeKind::Module: 129 *result = false; 130 return true; 131 132 // Statements with no sub-components at all. 133 case ParseNodeKind::EmptyStmt: 134 MOZ_ASSERT(node->is<NullaryNode>()); 135 *result = false; 136 return true; 137 138 case ParseNodeKind::DebuggerStmt: 139 MOZ_ASSERT(node->is<DebuggerStatement>()); 140 *result = false; 141 return true; 142 143 // Statements containing only an expression have no declarations. 144 case ParseNodeKind::ExpressionStmt: 145 case ParseNodeKind::ThrowStmt: 146 case ParseNodeKind::ReturnStmt: 147 MOZ_ASSERT(node->is<UnaryNode>()); 148 *result = false; 149 return true; 150 151 // These two aren't statements in the spec, but we sometimes insert them 152 // in statement lists anyway. 153 case ParseNodeKind::InitialYield: 154 case ParseNodeKind::YieldStarExpr: 155 case ParseNodeKind::YieldExpr: 156 MOZ_ASSERT(node->is<UnaryNode>()); 157 *result = false; 158 return true; 159 160 // Other statements with no sub-statement components. 161 case ParseNodeKind::BreakStmt: 162 case ParseNodeKind::ContinueStmt: 163 case ParseNodeKind::ImportDecl: 164 case ParseNodeKind::ImportSpecList: 165 case ParseNodeKind::ImportSpec: 166 case ParseNodeKind::ImportNamespaceSpec: 167 case ParseNodeKind::ExportFromStmt: 168 case ParseNodeKind::ExportDefaultStmt: 169 case ParseNodeKind::ExportSpecList: 170 case ParseNodeKind::ExportSpec: 171 case ParseNodeKind::ExportNamespaceSpec: 172 case ParseNodeKind::ExportStmt: 173 case ParseNodeKind::ExportBatchSpecStmt: 174 case ParseNodeKind::CallImportExpr: 175 case ParseNodeKind::CallImportSpec: 176 case ParseNodeKind::ImportAttributeList: 177 case ParseNodeKind::ImportAttribute: 178 case ParseNodeKind::ImportModuleRequest: 179 *result = false; 180 return true; 181 182 // Statements possibly containing hoistable declarations only in the left 183 // half, in ParseNode terms -- the loop body in AST terms. 184 case ParseNodeKind::DoWhileStmt: 185 return ContainsHoistedDeclaration(info, node->as<BinaryNode>().left(), 186 result); 187 188 // Statements possibly containing hoistable declarations only in the 189 // right half, in ParseNode terms -- the loop body or nested statement 190 // (usually a block statement), in AST terms. 191 case ParseNodeKind::WhileStmt: 192 case ParseNodeKind::WithStmt: 193 return ContainsHoistedDeclaration(info, node->as<BinaryNode>().right(), 194 result); 195 196 case ParseNodeKind::LabelStmt: 197 return ContainsHoistedDeclaration( 198 info, node->as<LabeledStatement>().statement(), result); 199 200 // Statements with more complicated structures. 201 202 // if-statement nodes may have hoisted declarations in their consequent 203 // and alternative components. 204 case ParseNodeKind::IfStmt: { 205 TernaryNode* ifNode = &node->as<TernaryNode>(); 206 ParseNode* consequent = ifNode->kid2(); 207 if (!ContainsHoistedDeclaration(info, consequent, result)) { 208 return false; 209 } 210 if (*result) { 211 return true; 212 } 213 214 if ((node = ifNode->kid3())) { 215 goto restart; 216 } 217 218 *result = false; 219 return true; 220 } 221 222 // try-statements have statements to execute, and one or both of a 223 // catch-list and a finally-block. 224 case ParseNodeKind::TryStmt: { 225 TernaryNode* tryNode = &node->as<TernaryNode>(); 226 227 MOZ_ASSERT(tryNode->kid2() || tryNode->kid3(), 228 "must have either catch or finally"); 229 230 ParseNode* tryBlock = tryNode->kid1(); 231 if (!ContainsHoistedDeclaration(info, tryBlock, result)) { 232 return false; 233 } 234 if (*result) { 235 return true; 236 } 237 238 if (ParseNode* catchScope = tryNode->kid2()) { 239 BinaryNode* catchNode = 240 &catchScope->as<LexicalScopeNode>().scopeBody()->as<BinaryNode>(); 241 MOZ_ASSERT(catchNode->isKind(ParseNodeKind::Catch)); 242 243 ParseNode* catchStatements = catchNode->right(); 244 if (!ContainsHoistedDeclaration(info, catchStatements, result)) { 245 return false; 246 } 247 if (*result) { 248 return true; 249 } 250 } 251 252 if (ParseNode* finallyBlock = tryNode->kid3()) { 253 return ContainsHoistedDeclaration(info, finallyBlock, result); 254 } 255 256 *result = false; 257 return true; 258 } 259 260 // A switch node's left half is an expression; only its right half (a 261 // list of cases/defaults, or a block node) could contain hoisted 262 // declarations. 263 case ParseNodeKind::SwitchStmt: { 264 SwitchStatement* switchNode = &node->as<SwitchStatement>(); 265 return ContainsHoistedDeclaration(info, &switchNode->lexicalForCaseList(), 266 result); 267 } 268 269 case ParseNodeKind::Case: { 270 CaseClause* caseClause = &node->as<CaseClause>(); 271 return ContainsHoistedDeclaration(info, caseClause->statementList(), 272 result); 273 } 274 275 case ParseNodeKind::ForStmt: { 276 ForNode* forNode = &node->as<ForNode>(); 277 TernaryNode* loopHead = forNode->head(); 278 MOZ_ASSERT(loopHead->isKind(ParseNodeKind::ForHead) || 279 loopHead->isKind(ParseNodeKind::ForIn) || 280 loopHead->isKind(ParseNodeKind::ForOf)); 281 282 if (loopHead->isKind(ParseNodeKind::ForHead)) { 283 // for (init?; cond?; update?), with only init possibly containing 284 // a hoisted declaration. (Note: a lexical-declaration |init| is 285 // (at present) hoisted in SpiderMonkey parlance -- but such 286 // hoisting doesn't extend outside of this statement, so it is not 287 // hoisting in the sense meant by ContainsHoistedDeclaration.) 288 ParseNode* init = loopHead->kid1(); 289 if (init && init->isKind(ParseNodeKind::VarStmt)) { 290 *result = true; 291 return true; 292 } 293 } else { 294 MOZ_ASSERT(loopHead->isKind(ParseNodeKind::ForIn) || 295 loopHead->isKind(ParseNodeKind::ForOf)); 296 297 // for each? (target in ...), where only target may introduce 298 // hoisted declarations. 299 // 300 // -- or -- 301 // 302 // for (target of ...), where only target may introduce hoisted 303 // declarations. 304 // 305 // Either way, if |target| contains a declaration, it's |loopHead|'s 306 // first kid. 307 ParseNode* decl = loopHead->kid1(); 308 if (decl && decl->isKind(ParseNodeKind::VarStmt)) { 309 *result = true; 310 return true; 311 } 312 } 313 314 ParseNode* loopBody = forNode->body(); 315 return ContainsHoistedDeclaration(info, loopBody, result); 316 } 317 318 case ParseNodeKind::LexicalScope: { 319 LexicalScopeNode* scope = &node->as<LexicalScopeNode>(); 320 ParseNode* expr = scope->scopeBody(); 321 322 if (expr->isKind(ParseNodeKind::ForStmt) || expr->is<FunctionNode>()) { 323 return ContainsHoistedDeclaration(info, expr, result); 324 } 325 326 MOZ_ASSERT(expr->isKind(ParseNodeKind::StatementList)); 327 return ListContainsHoistedDeclaration( 328 info, &scope->scopeBody()->as<ListNode>(), result); 329 } 330 331 // List nodes with all non-null children. 332 case ParseNodeKind::StatementList: 333 return ListContainsHoistedDeclaration(info, &node->as<ListNode>(), 334 result); 335 336 // Grammar sub-components that should never be reached directly by this 337 // method, because some parent component should have asserted itself. 338 case ParseNodeKind::ObjectPropertyName: 339 case ParseNodeKind::ComputedName: 340 case ParseNodeKind::Spread: 341 case ParseNodeKind::MutateProto: 342 case ParseNodeKind::PropertyDefinition: 343 case ParseNodeKind::Shorthand: 344 case ParseNodeKind::ConditionalExpr: 345 case ParseNodeKind::TypeOfNameExpr: 346 case ParseNodeKind::TypeOfExpr: 347 case ParseNodeKind::AwaitExpr: 348 case ParseNodeKind::VoidExpr: 349 case ParseNodeKind::NotExpr: 350 case ParseNodeKind::BitNotExpr: 351 case ParseNodeKind::DeleteNameExpr: 352 case ParseNodeKind::DeletePropExpr: 353 case ParseNodeKind::DeleteElemExpr: 354 case ParseNodeKind::DeleteOptionalChainExpr: 355 case ParseNodeKind::DeleteExpr: 356 case ParseNodeKind::PosExpr: 357 case ParseNodeKind::NegExpr: 358 case ParseNodeKind::PreIncrementExpr: 359 case ParseNodeKind::PostIncrementExpr: 360 case ParseNodeKind::PreDecrementExpr: 361 case ParseNodeKind::PostDecrementExpr: 362 case ParseNodeKind::CoalesceExpr: 363 case ParseNodeKind::OrExpr: 364 case ParseNodeKind::AndExpr: 365 case ParseNodeKind::BitOrExpr: 366 case ParseNodeKind::BitXorExpr: 367 case ParseNodeKind::BitAndExpr: 368 case ParseNodeKind::StrictEqExpr: 369 case ParseNodeKind::EqExpr: 370 case ParseNodeKind::StrictNeExpr: 371 case ParseNodeKind::NeExpr: 372 case ParseNodeKind::LtExpr: 373 case ParseNodeKind::LeExpr: 374 case ParseNodeKind::GtExpr: 375 case ParseNodeKind::GeExpr: 376 case ParseNodeKind::InstanceOfExpr: 377 case ParseNodeKind::InExpr: 378 case ParseNodeKind::PrivateInExpr: 379 case ParseNodeKind::LshExpr: 380 case ParseNodeKind::RshExpr: 381 case ParseNodeKind::UrshExpr: 382 case ParseNodeKind::AddExpr: 383 case ParseNodeKind::SubExpr: 384 case ParseNodeKind::MulExpr: 385 case ParseNodeKind::DivExpr: 386 case ParseNodeKind::ModExpr: 387 case ParseNodeKind::PowExpr: 388 case ParseNodeKind::InitExpr: 389 case ParseNodeKind::AssignExpr: 390 case ParseNodeKind::AddAssignExpr: 391 case ParseNodeKind::SubAssignExpr: 392 case ParseNodeKind::CoalesceAssignExpr: 393 case ParseNodeKind::OrAssignExpr: 394 case ParseNodeKind::AndAssignExpr: 395 case ParseNodeKind::BitOrAssignExpr: 396 case ParseNodeKind::BitXorAssignExpr: 397 case ParseNodeKind::BitAndAssignExpr: 398 case ParseNodeKind::LshAssignExpr: 399 case ParseNodeKind::RshAssignExpr: 400 case ParseNodeKind::UrshAssignExpr: 401 case ParseNodeKind::MulAssignExpr: 402 case ParseNodeKind::DivAssignExpr: 403 case ParseNodeKind::ModAssignExpr: 404 case ParseNodeKind::PowAssignExpr: 405 case ParseNodeKind::CommaExpr: 406 case ParseNodeKind::ArrayExpr: 407 case ParseNodeKind::ObjectExpr: 408 case ParseNodeKind::PropertyNameExpr: 409 case ParseNodeKind::DotExpr: 410 case ParseNodeKind::ArgumentsLength: 411 case ParseNodeKind::ElemExpr: 412 case ParseNodeKind::Arguments: 413 case ParseNodeKind::CallExpr: 414 case ParseNodeKind::PrivateMemberExpr: 415 case ParseNodeKind::OptionalChain: 416 case ParseNodeKind::OptionalDotExpr: 417 case ParseNodeKind::OptionalElemExpr: 418 case ParseNodeKind::OptionalCallExpr: 419 case ParseNodeKind::OptionalPrivateMemberExpr: 420 case ParseNodeKind::Name: 421 case ParseNodeKind::PrivateName: 422 case ParseNodeKind::TemplateStringExpr: 423 case ParseNodeKind::TemplateStringListExpr: 424 case ParseNodeKind::TaggedTemplateExpr: 425 case ParseNodeKind::CallSiteObj: 426 case ParseNodeKind::StringExpr: 427 case ParseNodeKind::RegExpExpr: 428 case ParseNodeKind::TrueExpr: 429 case ParseNodeKind::FalseExpr: 430 case ParseNodeKind::NullExpr: 431 case ParseNodeKind::RawUndefinedExpr: 432 case ParseNodeKind::ThisExpr: 433 case ParseNodeKind::Elision: 434 case ParseNodeKind::NumberExpr: 435 case ParseNodeKind::BigIntExpr: 436 case ParseNodeKind::NewExpr: 437 case ParseNodeKind::Generator: 438 case ParseNodeKind::ParamsBody: 439 case ParseNodeKind::Catch: 440 case ParseNodeKind::ForIn: 441 case ParseNodeKind::ForOf: 442 case ParseNodeKind::ForHead: 443 case ParseNodeKind::DefaultConstructor: 444 case ParseNodeKind::ClassBodyScope: 445 case ParseNodeKind::ClassMethod: 446 case ParseNodeKind::ClassField: 447 case ParseNodeKind::StaticClassBlock: 448 case ParseNodeKind::ClassMemberList: 449 case ParseNodeKind::ClassNames: 450 case ParseNodeKind::NewTargetExpr: 451 case ParseNodeKind::ImportMetaExpr: 452 case ParseNodeKind::PosHolder: 453 case ParseNodeKind::SuperCallExpr: 454 case ParseNodeKind::SuperBase: 455 case ParseNodeKind::SetThis: 456 #ifdef ENABLE_DECORATORS 457 case ParseNodeKind::DecoratorList: 458 #endif 459 MOZ_CRASH( 460 "ContainsHoistedDeclaration should have indicated false on " 461 "some parent node without recurring to test this node"); 462 case ParseNodeKind::LastUnused: 463 case ParseNodeKind::Limit: 464 MOZ_CRASH("unexpected sentinel ParseNodeKind in node"); 465 } 466 467 MOZ_CRASH("invalid node kind"); 468 } 469 470 /* 471 * Fold from one constant type to another. 472 * XXX handles only strings and numbers for now 473 */ 474 static bool FoldType(FoldInfo info, ParseNode** pnp, ParseNodeKind kind) { 475 const ParseNode* pn = *pnp; 476 if (!pn->isKind(kind)) { 477 switch (kind) { 478 case ParseNodeKind::NumberExpr: 479 if (pn->isKind(ParseNodeKind::StringExpr)) { 480 auto atom = pn->as<NameNode>().atom(); 481 double d = info.parserAtoms.toNumber(atom); 482 if (!TryReplaceNode( 483 pnp, info.handler->newNumber(d, NoDecimal, pn->pn_pos))) { 484 return false; 485 } 486 } 487 break; 488 489 case ParseNodeKind::StringExpr: 490 if (pn->isKind(ParseNodeKind::NumberExpr)) { 491 TaggedParserAtomIndex atom = 492 pn->as<NumericLiteral>().toAtom(info.fc, info.parserAtoms); 493 if (!atom) { 494 return false; 495 } 496 if (!TryReplaceNode( 497 pnp, info.handler->newStringLiteral(atom, pn->pn_pos))) { 498 return false; 499 } 500 } 501 break; 502 503 default: 504 MOZ_CRASH("Invalid type in constant folding FoldType"); 505 } 506 } 507 return true; 508 } 509 510 static bool IsEffectless(ParseNode* node) { 511 return node->isKind(ParseNodeKind::TrueExpr) || 512 node->isKind(ParseNodeKind::FalseExpr) || 513 node->isKind(ParseNodeKind::StringExpr) || 514 node->isKind(ParseNodeKind::TemplateStringExpr) || 515 node->isKind(ParseNodeKind::NumberExpr) || 516 node->isKind(ParseNodeKind::BigIntExpr) || 517 node->isKind(ParseNodeKind::NullExpr) || 518 node->isKind(ParseNodeKind::RawUndefinedExpr) || 519 node->isKind(ParseNodeKind::Function); 520 } 521 522 enum Truthiness { Truthy, Falsy, Unknown }; 523 524 static Truthiness Boolish(const FoldInfo& info, ParseNode* pn) { 525 switch (pn->getKind()) { 526 case ParseNodeKind::NumberExpr: 527 return (pn->as<NumericLiteral>().value() != 0 && 528 !std::isnan(pn->as<NumericLiteral>().value())) 529 ? Truthy 530 : Falsy; 531 532 case ParseNodeKind::BigIntExpr: 533 return info.bigInts[pn->as<BigIntLiteral>().index()].isZero() ? Falsy 534 : Truthy; 535 536 case ParseNodeKind::StringExpr: 537 case ParseNodeKind::TemplateStringExpr: 538 return (pn->as<NameNode>().atom() == 539 TaggedParserAtomIndex::WellKnown::empty()) 540 ? Falsy 541 : Truthy; 542 543 case ParseNodeKind::TrueExpr: 544 case ParseNodeKind::Function: 545 return Truthy; 546 547 case ParseNodeKind::FalseExpr: 548 case ParseNodeKind::NullExpr: 549 case ParseNodeKind::RawUndefinedExpr: 550 return Falsy; 551 552 case ParseNodeKind::VoidExpr: { 553 // |void <foo>| evaluates to |undefined| which isn't truthy. But the 554 // sense of this method requires that the expression be literally 555 // replaceable with true/false: not the case if the nested expression 556 // is effectful, might throw, &c. Walk past the |void| (and nested 557 // |void| expressions, for good measure) and check that the nested 558 // expression doesn't break this requirement before indicating falsity. 559 do { 560 pn = pn->as<UnaryNode>().kid(); 561 } while (pn->isKind(ParseNodeKind::VoidExpr)); 562 563 return IsEffectless(pn) ? Falsy : Unknown; 564 } 565 566 default: 567 return Unknown; 568 } 569 } 570 571 static bool SimplifyCondition(FoldInfo info, ParseNode** nodePtr) { 572 // Conditions fold like any other expression, but then they sometimes can be 573 // further folded to constants. *nodePtr should already have been 574 // constant-folded. 575 576 ParseNode* node = *nodePtr; 577 if (Truthiness t = Boolish(info, node); t != Unknown) { 578 // We can turn function nodes into constant nodes here, but mutating 579 // function nodes is tricky --- in particular, mutating a function node 580 // that appears on a method list corrupts the method list. However, 581 // methods are M's in statements of the form 'this.foo = M;', which we 582 // never fold, so we're okay. 583 if (!TryReplaceNode(nodePtr, info.handler->newBooleanLiteral( 584 t == Truthy, node->pn_pos))) { 585 return false; 586 } 587 } 588 589 return true; 590 } 591 592 static bool FoldTypeOfExpr(FoldInfo info, ParseNode** nodePtr) { 593 UnaryNode* node = &(*nodePtr)->as<UnaryNode>(); 594 MOZ_ASSERT(node->isKind(ParseNodeKind::TypeOfExpr)); 595 ParseNode* expr = node->kid(); 596 597 // Constant-fold the entire |typeof| if given a constant with known type. 598 TaggedParserAtomIndex result; 599 if (expr->isKind(ParseNodeKind::StringExpr) || 600 expr->isKind(ParseNodeKind::TemplateStringExpr)) { 601 result = TaggedParserAtomIndex::WellKnown::string(); 602 } else if (expr->isKind(ParseNodeKind::NumberExpr)) { 603 result = TaggedParserAtomIndex::WellKnown::number(); 604 } else if (expr->isKind(ParseNodeKind::BigIntExpr)) { 605 result = TaggedParserAtomIndex::WellKnown::bigint(); 606 } else if (expr->isKind(ParseNodeKind::NullExpr)) { 607 result = TaggedParserAtomIndex::WellKnown::object(); 608 } else if (expr->isKind(ParseNodeKind::TrueExpr) || 609 expr->isKind(ParseNodeKind::FalseExpr)) { 610 result = TaggedParserAtomIndex::WellKnown::boolean(); 611 } else if (expr->is<FunctionNode>()) { 612 result = TaggedParserAtomIndex::WellKnown::function(); 613 } 614 615 if (result) { 616 if (!TryReplaceNode(nodePtr, 617 info.handler->newStringLiteral(result, node->pn_pos))) { 618 return false; 619 } 620 } 621 622 return true; 623 } 624 625 static bool FoldDeleteExpr(FoldInfo info, ParseNode** nodePtr) { 626 UnaryNode* node = &(*nodePtr)->as<UnaryNode>(); 627 628 MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteExpr)); 629 ParseNode* expr = node->kid(); 630 631 // Expression deletion evaluates the expression, then evaluates to true. 632 // For effectless expressions, eliminate the expression evaluation. 633 if (IsEffectless(expr)) { 634 if (!TryReplaceNode(nodePtr, 635 info.handler->newBooleanLiteral(true, node->pn_pos))) { 636 return false; 637 } 638 } 639 640 return true; 641 } 642 643 static bool FoldDeleteElement(FoldInfo info, ParseNode** nodePtr) { 644 UnaryNode* node = &(*nodePtr)->as<UnaryNode>(); 645 MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteElemExpr)); 646 ParseNode* expr = node->kid(); 647 648 // If we're deleting an element, but constant-folding converted our 649 // element reference into a dotted property access, we must *also* 650 // morph the node's kind. 651 // 652 // In principle this also applies to |super["foo"] -> super.foo|, 653 // but we don't constant-fold |super["foo"]| yet. 654 MOZ_ASSERT(expr->isKind(ParseNodeKind::ElemExpr) || 655 expr->isKind(ParseNodeKind::DotExpr)); 656 if (expr->isKind(ParseNodeKind::DotExpr)) { 657 // newDelete will detect and use DeletePropExpr 658 if (!TryReplaceNode(nodePtr, 659 info.handler->newDelete(node->pn_pos.begin, expr))) { 660 return false; 661 } 662 MOZ_ASSERT((*nodePtr)->getKind() == ParseNodeKind::DeletePropExpr); 663 } 664 665 return true; 666 } 667 668 static bool FoldNot(FoldInfo info, ParseNode** nodePtr) { 669 UnaryNode* node = &(*nodePtr)->as<UnaryNode>(); 670 MOZ_ASSERT(node->isKind(ParseNodeKind::NotExpr)); 671 672 if (!SimplifyCondition(info, node->unsafeKidReference())) { 673 return false; 674 } 675 676 ParseNode* expr = node->kid(); 677 678 if (expr->isKind(ParseNodeKind::TrueExpr) || 679 expr->isKind(ParseNodeKind::FalseExpr)) { 680 bool newval = !expr->isKind(ParseNodeKind::TrueExpr); 681 682 if (!TryReplaceNode( 683 nodePtr, info.handler->newBooleanLiteral(newval, node->pn_pos))) { 684 return false; 685 } 686 } 687 688 return true; 689 } 690 691 static bool FoldUnaryArithmetic(FoldInfo info, ParseNode** nodePtr) { 692 UnaryNode* node = &(*nodePtr)->as<UnaryNode>(); 693 MOZ_ASSERT(node->isKind(ParseNodeKind::BitNotExpr) || 694 node->isKind(ParseNodeKind::PosExpr) || 695 node->isKind(ParseNodeKind::NegExpr), 696 "need a different method for this node kind"); 697 698 ParseNode* expr = node->kid(); 699 700 if (expr->isKind(ParseNodeKind::NumberExpr) || 701 expr->isKind(ParseNodeKind::TrueExpr) || 702 expr->isKind(ParseNodeKind::FalseExpr)) { 703 double d = expr->isKind(ParseNodeKind::NumberExpr) 704 ? expr->as<NumericLiteral>().value() 705 : double(expr->isKind(ParseNodeKind::TrueExpr)); 706 707 if (node->isKind(ParseNodeKind::BitNotExpr)) { 708 d = ~ToInt32(d); 709 } else if (node->isKind(ParseNodeKind::NegExpr)) { 710 d = -d; 711 } else { 712 MOZ_ASSERT(node->isKind(ParseNodeKind::PosExpr)); // nothing to do 713 } 714 715 if (!TryReplaceNode(nodePtr, 716 info.handler->newNumber(d, NoDecimal, node->pn_pos))) { 717 return false; 718 } 719 } else if (expr->is<BigIntLiteral>()) { 720 auto* literal = &expr->as<BigIntLiteral>(); 721 auto& bigInt = info.bigInts[literal->index()]; 722 723 if (node->isKind(ParseNodeKind::BitNotExpr)) { 724 if (bigInt.inplaceBitNot()) { 725 return TryReplaceNode(nodePtr, literal); 726 } 727 } else if (node->isKind(ParseNodeKind::NegExpr)) { 728 if (bigInt.inplaceNegate()) { 729 return TryReplaceNode(nodePtr, literal); 730 } 731 } else { 732 MOZ_ASSERT(node->isKind(ParseNodeKind::PosExpr)); // nothing to do 733 } 734 } 735 736 return true; 737 } 738 739 static bool FoldAndOrCoalesce(FoldInfo info, ParseNode** nodePtr) { 740 ListNode* node = &(*nodePtr)->as<ListNode>(); 741 742 MOZ_ASSERT(node->isKind(ParseNodeKind::AndExpr) || 743 node->isKind(ParseNodeKind::CoalesceExpr) || 744 node->isKind(ParseNodeKind::OrExpr)); 745 746 bool isOrNode = node->isKind(ParseNodeKind::OrExpr); 747 bool isAndNode = node->isKind(ParseNodeKind::AndExpr); 748 bool isCoalesceNode = node->isKind(ParseNodeKind::CoalesceExpr); 749 ParseNode** elem = node->unsafeHeadReference(); 750 do { 751 Truthiness t = Boolish(info, *elem); 752 753 // If we don't know the constant-folded node's truthiness, we can't 754 // reduce this node with its surroundings. Continue folding any 755 // remaining nodes. 756 if (t == Unknown) { 757 elem = &(*elem)->pn_next; 758 continue; 759 } 760 761 bool isTruthyCoalesceNode = 762 isCoalesceNode && !((*elem)->isKind(ParseNodeKind::NullExpr) || 763 (*elem)->isKind(ParseNodeKind::VoidExpr) || 764 (*elem)->isKind(ParseNodeKind::RawUndefinedExpr)); 765 bool canShortCircuit = (isOrNode && t == Truthy) || 766 (isAndNode && t == Falsy) || isTruthyCoalesceNode; 767 768 // If the constant-folded node's truthiness will terminate the 769 // condition -- `a || true || expr` or `b && false && expr` or 770 // `false ?? c ?? expr` -- then trailing nodes will never be 771 // evaluated. Truncate the list after the known-truthiness node, 772 // as it's the overall result. 773 if (canShortCircuit) { 774 for (ParseNode* next = (*elem)->pn_next; next; next = next->pn_next) { 775 node->unsafeDecrementCount(); 776 } 777 778 // Terminate the original and/or list at the known-truthiness 779 // node. 780 (*elem)->pn_next = nullptr; 781 elem = &(*elem)->pn_next; 782 break; 783 } 784 785 // We've encountered a vacuous node that'll never short-circuit 786 // evaluation. 787 if ((*elem)->pn_next) { 788 // This node is never the overall result when there are 789 // subsequent nodes. Remove it. 790 ParseNode* elt = *elem; 791 *elem = elt->pn_next; 792 node->unsafeDecrementCount(); 793 } else { 794 // Otherwise this node is the result of the overall expression, 795 // so leave it alone. And we're done. 796 elem = &(*elem)->pn_next; 797 break; 798 } 799 } while (*elem); 800 801 node->unsafeReplaceTail(elem); 802 803 // If we removed nodes, we may have to replace a one-element list with 804 // its element. 805 if (node->count() == 1) { 806 ParseNode* first = node->head(); 807 if (!TryReplaceNode(nodePtr, first)) { 808 ; 809 return false; 810 } 811 } 812 813 return true; 814 } 815 816 static bool Fold(FoldInfo info, ParseNode** pnp); 817 818 static bool FoldConditional(FoldInfo info, ParseNode** nodePtr) { 819 ParseNode** nextNode = nodePtr; 820 821 do { 822 // |nextNode| on entry points to the C?T:F expression to be folded. 823 // Reset it to exit the loop in the common case where F isn't another 824 // ?: expression. 825 nodePtr = nextNode; 826 nextNode = nullptr; 827 828 TernaryNode* node = &(*nodePtr)->as<TernaryNode>(); 829 MOZ_ASSERT(node->isKind(ParseNodeKind::ConditionalExpr)); 830 831 ParseNode** expr = node->unsafeKid1Reference(); 832 if (!Fold(info, expr)) { 833 return false; 834 } 835 if (!SimplifyCondition(info, expr)) { 836 return false; 837 } 838 839 ParseNode** ifTruthy = node->unsafeKid2Reference(); 840 if (!Fold(info, ifTruthy)) { 841 return false; 842 } 843 844 ParseNode** ifFalsy = node->unsafeKid3Reference(); 845 846 // If our C?T:F node has F as another ?: node, *iteratively* constant- 847 // fold F *after* folding C and T (and possibly eliminating C and one 848 // of T/F entirely); otherwise fold F normally. Making |nextNode| non- 849 // null causes this loop to run again to fold F. 850 // 851 // Conceivably we could instead/also iteratively constant-fold T, if T 852 // were more complex than F. Such an optimization is unimplemented. 853 if ((*ifFalsy)->isKind(ParseNodeKind::ConditionalExpr)) { 854 MOZ_ASSERT((*ifFalsy)->is<TernaryNode>()); 855 nextNode = ifFalsy; 856 } else { 857 if (!Fold(info, ifFalsy)) { 858 return false; 859 } 860 } 861 862 // Try to constant-fold based on the condition expression. 863 Truthiness t = Boolish(info, *expr); 864 if (t == Unknown) { 865 continue; 866 } 867 868 // Otherwise reduce 'C ? T : F' to T or F as directed by C. 869 ParseNode* replacement = t == Truthy ? *ifTruthy : *ifFalsy; 870 871 // Otherwise perform a replacement. This invalidates |nextNode|, so 872 // reset it (if the replacement requires folding) or clear it (if 873 // |ifFalsy| is dead code) as needed. 874 if (nextNode) { 875 nextNode = (*nextNode == replacement) ? nodePtr : nullptr; 876 } 877 if (!TryReplaceNode(nodePtr, replacement)) { 878 return false; 879 } 880 } while (nextNode); 881 882 return true; 883 } 884 885 static bool FoldIf(FoldInfo info, ParseNode** nodePtr) { 886 ParseNode** nextNode = nodePtr; 887 888 do { 889 // |nextNode| on entry points to the initial |if| to be folded. Reset 890 // it to exit the loop when the |else| arm isn't another |if|. 891 nodePtr = nextNode; 892 nextNode = nullptr; 893 894 TernaryNode* node = &(*nodePtr)->as<TernaryNode>(); 895 MOZ_ASSERT(node->isKind(ParseNodeKind::IfStmt)); 896 897 ParseNode** expr = node->unsafeKid1Reference(); 898 if (!Fold(info, expr)) { 899 return false; 900 } 901 if (!SimplifyCondition(info, expr)) { 902 return false; 903 } 904 905 ParseNode** consequent = node->unsafeKid2Reference(); 906 if (!Fold(info, consequent)) { 907 return false; 908 } 909 910 ParseNode** alternative = node->unsafeKid3Reference(); 911 if (*alternative) { 912 // If in |if (C) T; else F;| we have |F| as another |if|, 913 // *iteratively* constant-fold |F| *after* folding |C| and |T| (and 914 // possibly completely replacing the whole thing with |T| or |F|); 915 // otherwise fold F normally. Making |nextNode| non-null causes 916 // this loop to run again to fold F. 917 if ((*alternative)->isKind(ParseNodeKind::IfStmt)) { 918 MOZ_ASSERT((*alternative)->is<TernaryNode>()); 919 nextNode = alternative; 920 } else { 921 if (!Fold(info, alternative)) { 922 return false; 923 } 924 } 925 } 926 927 // Eliminate the consequent or alternative if the condition has 928 // constant truthiness. 929 Truthiness t = Boolish(info, *expr); 930 if (t == Unknown) { 931 continue; 932 } 933 934 // Careful! Either of these can be null: |replacement| in |if (0) T;|, 935 // and |discarded| in |if (true) T;|. 936 ParseNode* replacement; 937 ParseNode* discarded; 938 if (t == Truthy) { 939 replacement = *consequent; 940 discarded = *alternative; 941 } else { 942 replacement = *alternative; 943 discarded = *consequent; 944 } 945 946 bool performReplacement = true; 947 if (discarded) { 948 // A declaration that hoists outside the discarded arm prevents the 949 // |if| from being folded away. 950 bool containsHoistedDecls; 951 if (!ContainsHoistedDeclaration(info, discarded, &containsHoistedDecls)) { 952 return false; 953 } 954 955 performReplacement = !containsHoistedDecls; 956 } 957 958 if (!performReplacement) { 959 continue; 960 } 961 962 if (!replacement) { 963 // If there's no replacement node, we have a constantly-false |if| 964 // with no |else|. Replace the entire thing with an empty 965 // statement list. 966 if (!TryReplaceNode(nodePtr, 967 info.handler->newStatementList(node->pn_pos))) { 968 return false; 969 } 970 } else { 971 // Replacement invalidates |nextNode|, so reset it (if the 972 // replacement requires folding) or clear it (if |alternative| 973 // is dead code) as needed. 974 if (nextNode) { 975 nextNode = (*nextNode == replacement) ? nodePtr : nullptr; 976 } 977 ReplaceNode(nodePtr, replacement); 978 } 979 } while (nextNode); 980 981 return true; 982 } 983 984 static double ComputeBinary(ParseNodeKind kind, double left, double right) { 985 if (kind == ParseNodeKind::AddExpr) { 986 return left + right; 987 } 988 989 if (kind == ParseNodeKind::SubExpr) { 990 return left - right; 991 } 992 993 if (kind == ParseNodeKind::MulExpr) { 994 return left * right; 995 } 996 997 if (kind == ParseNodeKind::ModExpr) { 998 return NumberMod(left, right); 999 } 1000 1001 if (kind == ParseNodeKind::UrshExpr) { 1002 return ToUint32(left) >> (ToUint32(right) & 31); 1003 } 1004 1005 if (kind == ParseNodeKind::DivExpr) { 1006 return NumberDiv(left, right); 1007 } 1008 1009 MOZ_ASSERT(kind == ParseNodeKind::LshExpr || kind == ParseNodeKind::RshExpr); 1010 1011 int32_t i = ToInt32(left); 1012 uint32_t j = ToUint32(right) & 31; 1013 return int32_t((kind == ParseNodeKind::LshExpr) ? uint32_t(i) << j : i >> j); 1014 } 1015 1016 static bool FoldBinaryArithmetic(FoldInfo info, ParseNode** nodePtr) { 1017 ListNode* node = &(*nodePtr)->as<ListNode>(); 1018 MOZ_ASSERT(node->isKind(ParseNodeKind::SubExpr) || 1019 node->isKind(ParseNodeKind::MulExpr) || 1020 node->isKind(ParseNodeKind::LshExpr) || 1021 node->isKind(ParseNodeKind::RshExpr) || 1022 node->isKind(ParseNodeKind::UrshExpr) || 1023 node->isKind(ParseNodeKind::DivExpr) || 1024 node->isKind(ParseNodeKind::ModExpr)); 1025 MOZ_ASSERT(node->count() >= 2); 1026 1027 // Fold each operand to a number if possible. 1028 ParseNode** listp = node->unsafeHeadReference(); 1029 for (; *listp; listp = &(*listp)->pn_next) { 1030 if (!FoldType(info, listp, ParseNodeKind::NumberExpr)) { 1031 return false; 1032 } 1033 } 1034 node->unsafeReplaceTail(listp); 1035 1036 // Now fold all leading numeric terms together into a single number. 1037 // (Trailing terms for the non-shift operations can't be folded together 1038 // due to floating point imprecision. For example, if |x === -2**53|, 1039 // |x - 1 - 1 === -2**53| but |x - 2 === -2**53 - 2|. Shifts could be 1040 // folded, but it doesn't seem worth the effort.) 1041 ParseNode** elem = node->unsafeHeadReference(); 1042 ParseNode** next = &(*elem)->pn_next; 1043 if ((*elem)->isKind(ParseNodeKind::NumberExpr)) { 1044 ParseNodeKind kind = node->getKind(); 1045 while (true) { 1046 if (!*next || !(*next)->isKind(ParseNodeKind::NumberExpr)) { 1047 break; 1048 } 1049 1050 double d = ComputeBinary(kind, (*elem)->as<NumericLiteral>().value(), 1051 (*next)->as<NumericLiteral>().value()); 1052 1053 TokenPos pos((*elem)->pn_pos.begin, (*next)->pn_pos.end); 1054 if (!TryReplaceNode(elem, info.handler->newNumber(d, NoDecimal, pos))) { 1055 return false; 1056 } 1057 1058 (*elem)->pn_next = (*next)->pn_next; 1059 next = &(*elem)->pn_next; 1060 node->unsafeDecrementCount(); 1061 } 1062 1063 if (node->count() == 1) { 1064 MOZ_ASSERT(node->head() == *elem); 1065 MOZ_ASSERT((*elem)->isKind(ParseNodeKind::NumberExpr)); 1066 1067 if (!TryReplaceNode(nodePtr, *elem)) { 1068 return false; 1069 } 1070 } 1071 } 1072 1073 return true; 1074 } 1075 1076 static bool FoldExponentiation(FoldInfo info, ParseNode** nodePtr) { 1077 ListNode* node = &(*nodePtr)->as<ListNode>(); 1078 MOZ_ASSERT(node->isKind(ParseNodeKind::PowExpr)); 1079 MOZ_ASSERT(node->count() >= 2); 1080 1081 // Fold each operand, ideally into a number. 1082 ParseNode** listp = node->unsafeHeadReference(); 1083 for (; *listp; listp = &(*listp)->pn_next) { 1084 if (!FoldType(info, listp, ParseNodeKind::NumberExpr)) { 1085 return false; 1086 } 1087 } 1088 1089 node->unsafeReplaceTail(listp); 1090 1091 // Unlike all other binary arithmetic operators, ** is right-associative: 1092 // 2**3**5 is 2**(3**5), not (2**3)**5. As list nodes singly-link their 1093 // children, full constant-folding requires either linear space or dodgy 1094 // in-place linked list reversal. So we only fold one exponentiation: it's 1095 // easy and addresses common cases like |2**32|. 1096 if (node->count() > 2) { 1097 return true; 1098 } 1099 1100 ParseNode* base = node->head(); 1101 ParseNode* exponent = base->pn_next; 1102 if (!base->isKind(ParseNodeKind::NumberExpr) || 1103 !exponent->isKind(ParseNodeKind::NumberExpr)) { 1104 return true; 1105 } 1106 1107 double d1 = base->as<NumericLiteral>().value(); 1108 double d2 = exponent->as<NumericLiteral>().value(); 1109 1110 return TryReplaceNode(nodePtr, info.handler->newNumber( 1111 ecmaPow(d1, d2), NoDecimal, node->pn_pos)); 1112 } 1113 1114 static bool FoldElement(FoldInfo info, ParseNode** nodePtr) { 1115 PropertyByValue* elem = &(*nodePtr)->as<PropertyByValue>(); 1116 1117 ParseNode* expr = &elem->expression(); 1118 ParseNode* key = &elem->key(); 1119 TaggedParserAtomIndex name; 1120 if (key->isKind(ParseNodeKind::StringExpr)) { 1121 auto keyIndex = key->as<NameNode>().atom(); 1122 uint32_t index; 1123 if (info.parserAtoms.isIndex(keyIndex, &index)) { 1124 // Optimization 1: We have something like expr["100"]. This is 1125 // equivalent to expr[100] which is faster. 1126 if (!TryReplaceNode( 1127 elem->unsafeRightReference(), 1128 info.handler->newNumber(index, NoDecimal, key->pn_pos))) { 1129 return false; 1130 } 1131 key = &elem->key(); 1132 } else { 1133 name = keyIndex; 1134 } 1135 } else if (key->isKind(ParseNodeKind::NumberExpr)) { 1136 auto* numeric = &key->as<NumericLiteral>(); 1137 double number = numeric->value(); 1138 if (number != ToUint32(number)) { 1139 // Optimization 2: We have something like expr[3.14]. The number 1140 // isn't an array index, so it converts to a string ("3.14"), 1141 // enabling optimization 3 below. 1142 name = numeric->toAtom(info.fc, info.parserAtoms); 1143 if (!name) { 1144 return false; 1145 } 1146 } 1147 } 1148 1149 // If we don't have a name, we can't optimize to getprop. 1150 if (!name) { 1151 return true; 1152 } 1153 1154 // Optimization 3: We have expr["foo"] where foo is not an index. Convert 1155 // to a property access (like expr.foo) that optimizes better downstream. 1156 1157 NameNode* propertyNameExpr; 1158 MOZ_TRY_VAR_OR_RETURN(propertyNameExpr, 1159 info.handler->newPropertyName(name, key->pn_pos), 1160 false); 1161 if (!TryReplaceNode( 1162 nodePtr, info.handler->newPropertyAccess(expr, propertyNameExpr))) { 1163 return false; 1164 } 1165 1166 return true; 1167 } 1168 1169 static bool FoldAdd(FoldInfo info, ParseNode** nodePtr) { 1170 ListNode* node = &(*nodePtr)->as<ListNode>(); 1171 1172 MOZ_ASSERT(node->isKind(ParseNodeKind::AddExpr)); 1173 MOZ_ASSERT(node->count() >= 2); 1174 1175 // Fold leading numeric operands together: 1176 // 1177 // (1 + 2 + x) becomes (3 + x) 1178 // 1179 // Don't go past the leading operands: additions after a string are 1180 // string concatenations, not additions: ("1" + 2 + 3 === "123"). 1181 ParseNode** current = node->unsafeHeadReference(); 1182 ParseNode** next = &(*current)->pn_next; 1183 if ((*current)->isKind(ParseNodeKind::NumberExpr)) { 1184 do { 1185 if (!(*next)->isKind(ParseNodeKind::NumberExpr)) { 1186 break; 1187 } 1188 1189 double left = (*current)->as<NumericLiteral>().value(); 1190 double right = (*next)->as<NumericLiteral>().value(); 1191 TokenPos pos((*current)->pn_pos.begin, (*next)->pn_pos.end); 1192 1193 if (!TryReplaceNode( 1194 current, info.handler->newNumber(left + right, NoDecimal, pos))) { 1195 return false; 1196 } 1197 1198 (*current)->pn_next = (*next)->pn_next; 1199 next = &(*current)->pn_next; 1200 1201 node->unsafeDecrementCount(); 1202 } while (*next); 1203 } 1204 1205 // If any operands remain, attempt string concatenation folding. 1206 do { 1207 // If no operands remain, we're done. 1208 if (!*next) { 1209 break; 1210 } 1211 1212 // (number + string) is string concatenation *only* at the start of 1213 // the list: (x + 1 + "2" !== x + "12") when x is a number. 1214 if ((*current)->isKind(ParseNodeKind::NumberExpr) && 1215 (*next)->isKind(ParseNodeKind::StringExpr)) { 1216 if (!FoldType(info, current, ParseNodeKind::StringExpr)) { 1217 return false; 1218 } 1219 next = &(*current)->pn_next; 1220 } 1221 1222 // The first string forces all subsequent additions to be 1223 // string concatenations. 1224 do { 1225 if ((*current)->isKind(ParseNodeKind::StringExpr)) { 1226 break; 1227 } 1228 1229 current = next; 1230 next = &(*current)->pn_next; 1231 } while (*next); 1232 1233 // If there's nothing left to fold, we're done. 1234 if (!*next) { 1235 break; 1236 } 1237 1238 do { 1239 // Concat all strings. 1240 MOZ_ASSERT((*current)->isKind(ParseNodeKind::StringExpr)); 1241 1242 // To avoid unnecessarily copy when there's no strings after the 1243 // first item, lazily construct StringBuilder and append the first item. 1244 mozilla::Maybe<StringBuilder> accum; 1245 TaggedParserAtomIndex firstAtom; 1246 firstAtom = (*current)->as<NameNode>().atom(); 1247 1248 do { 1249 // Try folding the next operand to a string. 1250 if (!FoldType(info, next, ParseNodeKind::StringExpr)) { 1251 return false; 1252 } 1253 1254 // Stop glomming once folding doesn't produce a string. 1255 if (!(*next)->isKind(ParseNodeKind::StringExpr)) { 1256 break; 1257 } 1258 1259 if (!accum) { 1260 accum.emplace(info.fc); 1261 if (!accum->append(info.parserAtoms, firstAtom)) { 1262 return false; 1263 } 1264 } 1265 // Append this string and remove the node. 1266 if (!accum->append(info.parserAtoms, (*next)->as<NameNode>().atom())) { 1267 return false; 1268 } 1269 1270 (*current)->pn_next = (*next)->pn_next; 1271 next = &(*current)->pn_next; 1272 1273 node->unsafeDecrementCount(); 1274 } while (*next); 1275 1276 // Replace with concatenation if we multiple nodes. 1277 if (accum) { 1278 auto combination = accum->finishParserAtom(info.parserAtoms, info.fc); 1279 if (!combination) { 1280 return false; 1281 } 1282 1283 // Replace |current|'s string with the entire combination. 1284 MOZ_ASSERT((*current)->isKind(ParseNodeKind::StringExpr)); 1285 (*current)->as<NameNode>().setAtom(combination); 1286 } 1287 1288 // If we're out of nodes, we're done. 1289 if (!*next) { 1290 break; 1291 } 1292 1293 current = next; 1294 next = &(*current)->pn_next; 1295 1296 // If we're out of nodes *after* the non-foldable-to-string 1297 // node, we're done. 1298 if (!*next) { 1299 break; 1300 } 1301 1302 // Otherwise find the next node foldable to a string, and loop. 1303 do { 1304 current = next; 1305 1306 if (!FoldType(info, current, ParseNodeKind::StringExpr)) { 1307 return false; 1308 } 1309 next = &(*current)->pn_next; 1310 } while (!(*current)->isKind(ParseNodeKind::StringExpr) && *next); 1311 } while (*next); 1312 } while (false); 1313 1314 MOZ_ASSERT(!*next, "must have considered all nodes here"); 1315 MOZ_ASSERT(!(*current)->pn_next, "current node must be the last node"); 1316 1317 node->unsafeReplaceTail(&(*current)->pn_next); 1318 1319 if (node->count() == 1) { 1320 // We reduced the list to a constant. Replace the ParseNodeKind::Add node 1321 // with that constant. 1322 ReplaceNode(nodePtr, *current); 1323 } 1324 1325 return true; 1326 } 1327 1328 class FoldVisitor : public RewritingParseNodeVisitor<FoldVisitor> { 1329 using Base = RewritingParseNodeVisitor; 1330 1331 ParserAtomsTable& parserAtoms; 1332 BigIntStencilVector& bigInts; 1333 FullParseHandler* handler; 1334 1335 FoldInfo info() const { return FoldInfo{fc_, parserAtoms, bigInts, handler}; } 1336 1337 public: 1338 FoldVisitor(FrontendContext* fc, ParserAtomsTable& parserAtoms, 1339 BigIntStencilVector& bigInts, FullParseHandler* handler) 1340 : RewritingParseNodeVisitor(fc), 1341 parserAtoms(parserAtoms), 1342 bigInts(bigInts), 1343 handler(handler) {} 1344 1345 bool visitElemExpr(ParseNode*& pn) { 1346 return Base::visitElemExpr(pn) && FoldElement(info(), &pn); 1347 } 1348 1349 bool visitTypeOfExpr(ParseNode*& pn) { 1350 return Base::visitTypeOfExpr(pn) && FoldTypeOfExpr(info(), &pn); 1351 } 1352 1353 bool visitDeleteExpr(ParseNode*& pn) { 1354 return Base::visitDeleteExpr(pn) && FoldDeleteExpr(info(), &pn); 1355 } 1356 1357 bool visitDeleteElemExpr(ParseNode*& pn) { 1358 return Base::visitDeleteElemExpr(pn) && FoldDeleteElement(info(), &pn); 1359 } 1360 1361 bool visitNotExpr(ParseNode*& pn) { 1362 return Base::visitNotExpr(pn) && FoldNot(info(), &pn); 1363 } 1364 1365 bool visitBitNotExpr(ParseNode*& pn) { 1366 return Base::visitBitNotExpr(pn) && FoldUnaryArithmetic(info(), &pn); 1367 } 1368 1369 bool visitPosExpr(ParseNode*& pn) { 1370 return Base::visitPosExpr(pn) && FoldUnaryArithmetic(info(), &pn); 1371 } 1372 1373 bool visitNegExpr(ParseNode*& pn) { 1374 return Base::visitNegExpr(pn) && FoldUnaryArithmetic(info(), &pn); 1375 } 1376 1377 bool visitPowExpr(ParseNode*& pn) { 1378 return Base::visitPowExpr(pn) && FoldExponentiation(info(), &pn); 1379 } 1380 1381 bool visitMulExpr(ParseNode*& pn) { 1382 return Base::visitMulExpr(pn) && FoldBinaryArithmetic(info(), &pn); 1383 } 1384 1385 bool visitDivExpr(ParseNode*& pn) { 1386 return Base::visitDivExpr(pn) && FoldBinaryArithmetic(info(), &pn); 1387 } 1388 1389 bool visitModExpr(ParseNode*& pn) { 1390 return Base::visitModExpr(pn) && FoldBinaryArithmetic(info(), &pn); 1391 } 1392 1393 bool visitAddExpr(ParseNode*& pn) { 1394 return Base::visitAddExpr(pn) && FoldAdd(info(), &pn); 1395 } 1396 1397 bool visitSubExpr(ParseNode*& pn) { 1398 return Base::visitSubExpr(pn) && FoldBinaryArithmetic(info(), &pn); 1399 } 1400 1401 bool visitLshExpr(ParseNode*& pn) { 1402 return Base::visitLshExpr(pn) && FoldBinaryArithmetic(info(), &pn); 1403 } 1404 1405 bool visitRshExpr(ParseNode*& pn) { 1406 return Base::visitRshExpr(pn) && FoldBinaryArithmetic(info(), &pn); 1407 } 1408 1409 bool visitUrshExpr(ParseNode*& pn) { 1410 return Base::visitUrshExpr(pn) && FoldBinaryArithmetic(info(), &pn); 1411 } 1412 1413 bool visitAndExpr(ParseNode*& pn) { 1414 // Note that this does result in the unfortunate fact that dead arms of this 1415 // node get constant folded. The same goes for visitOr and visitCoalesce. 1416 return Base::visitAndExpr(pn) && FoldAndOrCoalesce(info(), &pn); 1417 } 1418 1419 bool visitOrExpr(ParseNode*& pn) { 1420 return Base::visitOrExpr(pn) && FoldAndOrCoalesce(info(), &pn); 1421 } 1422 1423 bool visitCoalesceExpr(ParseNode*& pn) { 1424 return Base::visitCoalesceExpr(pn) && FoldAndOrCoalesce(info(), &pn); 1425 } 1426 1427 bool visitConditionalExpr(ParseNode*& pn) { 1428 // Don't call base-class visitConditional because FoldConditional processes 1429 // pn's child nodes specially to save stack space. 1430 return FoldConditional(info(), &pn); 1431 } 1432 1433 private: 1434 bool internalVisitCall(BinaryNode* node) { 1435 MOZ_ASSERT(node->isKind(ParseNodeKind::CallExpr) || 1436 node->isKind(ParseNodeKind::OptionalCallExpr) || 1437 node->isKind(ParseNodeKind::SuperCallExpr) || 1438 node->isKind(ParseNodeKind::NewExpr) || 1439 node->isKind(ParseNodeKind::TaggedTemplateExpr)); 1440 1441 // Don't fold a parenthesized callable component in an invocation, as this 1442 // might cause a different |this| value to be used, changing semantics: 1443 // 1444 // var prop = "global"; 1445 // var obj = { prop: "obj", f: function() { return this.prop; } }; 1446 // assertEq((true ? obj.f : null)(), "global"); 1447 // assertEq(obj.f(), "obj"); 1448 // assertEq((true ? obj.f : null)``, "global"); 1449 // assertEq(obj.f``, "obj"); 1450 // 1451 // As an exception to this, we do allow folding the function in 1452 // `(function() { ... })()` (the module pattern), because that lets us 1453 // constant fold code inside that function. 1454 // 1455 // See bug 537673 and bug 1182373. 1456 ParseNode* callee = node->left(); 1457 if (node->isKind(ParseNodeKind::NewExpr) || !callee->isInParens() || 1458 callee->is<FunctionNode>()) { 1459 if (!visit(*node->unsafeLeftReference())) { 1460 return false; 1461 } 1462 } 1463 1464 if (!visit(*node->unsafeRightReference())) { 1465 return false; 1466 } 1467 1468 return true; 1469 } 1470 1471 public: 1472 bool visitCallExpr(ParseNode*& pn) { 1473 return internalVisitCall(&pn->as<BinaryNode>()); 1474 } 1475 1476 bool visitOptionalCallExpr(ParseNode*& pn) { 1477 return internalVisitCall(&pn->as<BinaryNode>()); 1478 } 1479 1480 bool visitNewExpr(ParseNode*& pn) { 1481 return internalVisitCall(&pn->as<BinaryNode>()); 1482 } 1483 1484 bool visitSuperCallExpr(ParseNode*& pn) { 1485 return internalVisitCall(&pn->as<BinaryNode>()); 1486 } 1487 1488 bool visitTaggedTemplateExpr(ParseNode*& pn) { 1489 return internalVisitCall(&pn->as<BinaryNode>()); 1490 } 1491 1492 bool visitIfStmt(ParseNode*& pn) { 1493 // Don't call base-class visitIf because FoldIf processes pn's child nodes 1494 // specially to save stack space. 1495 return FoldIf(info(), &pn); 1496 } 1497 1498 bool visitForStmt(ParseNode*& pn) { 1499 if (!Base::visitForStmt(pn)) { 1500 return false; 1501 } 1502 1503 ForNode& stmt = pn->as<ForNode>(); 1504 if (stmt.left()->isKind(ParseNodeKind::ForHead)) { 1505 TernaryNode& head = stmt.left()->as<TernaryNode>(); 1506 ParseNode** test = head.unsafeKid2Reference(); 1507 if (*test) { 1508 if (!SimplifyCondition(info(), test)) { 1509 return false; 1510 } 1511 if ((*test)->isKind(ParseNodeKind::TrueExpr)) { 1512 *test = nullptr; 1513 } 1514 } 1515 } 1516 1517 return true; 1518 } 1519 1520 bool visitWhileStmt(ParseNode*& pn) { 1521 BinaryNode& node = pn->as<BinaryNode>(); 1522 return Base::visitWhileStmt(pn) && 1523 SimplifyCondition(info(), node.unsafeLeftReference()); 1524 } 1525 1526 bool visitDoWhileStmt(ParseNode*& pn) { 1527 BinaryNode& node = pn->as<BinaryNode>(); 1528 return Base::visitDoWhileStmt(pn) && 1529 SimplifyCondition(info(), node.unsafeRightReference()); 1530 } 1531 1532 bool visitFunction(ParseNode*& pn) { 1533 FunctionNode& node = pn->as<FunctionNode>(); 1534 1535 // Don't constant-fold inside "use asm" code, as this could create a parse 1536 // tree that doesn't type-check as asm.js. 1537 if (node.funbox()->useAsmOrInsideUseAsm()) { 1538 return true; 1539 } 1540 1541 return Base::visitFunction(pn); 1542 } 1543 1544 bool visitArrayExpr(ParseNode*& pn) { 1545 if (!Base::visitArrayExpr(pn)) { 1546 return false; 1547 } 1548 1549 ListNode* list = &pn->as<ListNode>(); 1550 // Empty arrays are non-constant, since we cannot easily determine their 1551 // type. 1552 if (list->hasNonConstInitializer() && list->count() > 0) { 1553 for (ParseNode* node : list->contents()) { 1554 if (!node->isConstant()) { 1555 return true; 1556 } 1557 } 1558 list->unsetHasNonConstInitializer(); 1559 } 1560 return true; 1561 } 1562 1563 bool visitObjectExpr(ParseNode*& pn) { 1564 if (!Base::visitObjectExpr(pn)) { 1565 return false; 1566 } 1567 1568 ListNode* list = &pn->as<ListNode>(); 1569 if (list->hasNonConstInitializer()) { 1570 for (ParseNode* node : list->contents()) { 1571 if (node->getKind() != ParseNodeKind::PropertyDefinition) { 1572 return true; 1573 } 1574 BinaryNode* binary = &node->as<BinaryNode>(); 1575 if (binary->left()->isKind(ParseNodeKind::ComputedName)) { 1576 return true; 1577 } 1578 if (!binary->right()->isConstant()) { 1579 return true; 1580 } 1581 } 1582 list->unsetHasNonConstInitializer(); 1583 } 1584 return true; 1585 } 1586 }; 1587 1588 static bool Fold(FrontendContext* fc, ParserAtomsTable& parserAtoms, 1589 BigIntStencilVector& bigInts, FullParseHandler* handler, 1590 ParseNode** pnp) { 1591 FoldVisitor visitor(fc, parserAtoms, bigInts, handler); 1592 return visitor.visit(*pnp); 1593 } 1594 static bool Fold(FoldInfo info, ParseNode** pnp) { 1595 return Fold(info.fc, info.parserAtoms, info.bigInts, info.handler, pnp); 1596 } 1597 1598 bool frontend::FoldConstants(FrontendContext* fc, ParserAtomsTable& parserAtoms, 1599 BigIntStencilVector& bigInts, ParseNode** pnp, 1600 FullParseHandler* handler) { 1601 return Fold(fc, parserAtoms, bigInts, handler, pnp); 1602 }