void SwitchStatement::analyzeProgramImpl(AnalysisResultPtr ar) { m_exp->analyzeProgram(ar); if (m_cases) m_cases->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll && m_exp->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr exp = dynamic_pointer_cast<SimpleVariable>(m_exp); if (exp && exp->getSymbol() && exp->getSymbol()->isClassName()) { // Mark some classes as volitle since the name is used in switch for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); ASSERT(stmt); ExpressionPtr caseCond = stmt->getCondition(); if (caseCond && caseCond->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(caseCond); if (name && name->isLiteralString()) { string className = name->getLiteralString(); ClassScopePtr cls = ar->findClass(Util::toLower(className)); if (cls && cls->isUserClass()) { cls->setVolatile(); } } } } // Also note this down as code error ConstructPtr self = shared_from_this(); Compiler::Error(Compiler::ConditionalClassLoading, self); } } }
void NodeVisitor::visitCase(const CaseStatementPtr& node) { for(auto condition : node->getConditions()) { ACCEPT(condition.condition); ACCEPT(condition.guard); } ACCEPT(node->getCodeBlock()); }
void SwitchStatement::inferTypes(AnalysisResultPtr ar) { // we optimize the most two common cases of switch statements bool allInteger = true; bool allString = true; if (m_cases && m_cases->getCount()) { for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); if (!stmt->getCondition()) { if (m_cases->getCount() == 1) allInteger = allString = false; } else { if (!stmt->isLiteralInteger()) allInteger = false; if (!stmt->isLiteralString()) allString = false; } } } if (allInteger && allString) { allInteger = allString = false; } TypePtr ret; if (allInteger) { ret = m_exp->inferAndCheck(ar, Type::Int64, false); } else if (allString) { // We're not able to do this, because switch($obj) may work on an object // that didn't implement __toString(), throwing an exception, which isn't // consistent with PHP. //ret = m_exp->inferAndCheck(ar, Type::String, false); ret = m_exp->inferAndCheck(ar, NEW_TYPE(Some), false); } else { ret = m_exp->inferAndCheck(ar, NEW_TYPE(Some), false); } if (ret->is(Type::KindOfObject) && ret->isSpecificObject()) { m_exp->setExpectedType(NEW_TYPE(Object)); } ConstructPtr self = shared_from_this(); if (m_cases && m_cases->getCount()) { bool checking = false; vector<int> defaults; int defaultCount = 0; for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); stmt->inferAndCheck(ar, NEW_TYPE(Some), false); ExpressionPtr cond = stmt->getCondition(); if (!cond) { checking = true; defaultCount++; } else if (checking && cond && ar->isFirstPass()) { defaults.push_back(i); ar->getCodeError()->record(self, CodeError::CaseAfterDefault, stmt); } } if (defaultCount > 1 && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MoreThanOneDefault, m_cases); } } }
void SwitchStatement::inferTypes(AnalysisResultPtr ar) { // we optimize the most two common cases of switch statements bool allInteger = true; bool allString = true; if (m_cases && m_cases->getCount()) { for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); if (!stmt->getCondition()) { if (m_cases->getCount() == 1) allInteger = allString = false; } else { if (!stmt->isLiteralInteger()) allInteger = false; if (!stmt->isLiteralString()) allString = false; } } } if (allInteger && allString) { allInteger = allString = false; } TypePtr ret = m_exp->inferAndCheck(ar, Type::Some, false); // these are the cases where toInt64(x) is OK for the switch statement if (allInteger && (ret->is(Type::KindOfInt32) || ret->is(Type::KindOfInt64))) { m_exp->setExpectedType(Type::Int64); } if (ret->is(Type::KindOfObject) && ret->isSpecificObject()) { m_exp->setExpectedType(Type::Object); } ConstructPtr self = shared_from_this(); if (m_cases && m_cases->getCount()) { int defaultCount = 0; for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); stmt->inferAndCheck(ar, Type::Some, false); ExpressionPtr cond = stmt->getCondition(); if (!cond) { defaultCount++; } } // TODO: this really belongs in analyzeProgram() if (defaultCount > 1 && getScope()->isFirstPass()) { Compiler::Error(Compiler::MoreThanOneDefault, m_cases); } } }
void Parser::parseSwitchStatements(const CaseStatementPtr& case_) { Token token; Flags flags(this); flags -= SUPPRESS_TRAILING_CLOSURE; CodeBlockPtr codeBlock = case_->getCodeBlock(); while(true) { expect_next(token); if(token.type == TokenType::Semicolon) continue; restore(token); Keyword::T k = token.getKeyword(); if(token.type == TokenType::CloseBrace || (k == Keyword::Case || k == Keyword::Default)) { break; } StatementPtr st = parseStatement(); codeBlock->addStatement(st); } }
void SwitchStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { int labelId = cg.createNewLocalId(shared_from_this()); bool staticCases = true; if (!m_exp->getType()->isInteger()) { staticCases = false; } else if (m_cases) { bool seenDefault = false; set<int64> seenNums; for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); if (stmt->getCondition()) { if (!stmt->isLiteralInteger() || seenDefault) { staticCases = false; break; } // detecting duplicate case value int64 num = stmt->getLiteralInteger(); if (seenNums.find(num) != seenNums.end()) { staticCases = false; break; } seenNums.insert(num); } else { seenDefault = true; } } } labelId |= CodeGenerator::InsideSwitch; if (staticCases) labelId |= CodeGenerator::StaticCases; cg.pushBreakScope(labelId, false); labelId &= ~CodeGenerator::BreakScopeBitMask; cg_indentBegin("{\n"); string var; int varId = -1; if (m_exp->preOutputCPP(cg, ar, 0)) { varId = cg.createNewLocalId(shared_from_this()); var = string(Option::SwitchPrefix) + lexical_cast<string>(varId); m_exp->getType()->outputCPPDecl(cg, ar, getScope()); cg_printf(" %s;\n", var.c_str()); m_exp->outputCPPBegin(cg, ar); cg_printf("%s = (", var.c_str()); m_exp->outputCPP(cg, ar); cg_printf(");\n"); m_exp->outputCPPEnd(cg, ar); } if (staticCases) { cg_printf("switch ("); if (!var.empty()) { cg_printf("%s", var.c_str()); } else { m_exp->outputCPP(cg, ar); } cg_printf(") {\n"); if (m_cases) m_cases->outputCPP(cg, ar); cg_printf("}\n"); } else { if (var.empty()) { varId = cg.createNewLocalId(shared_from_this()); if (m_exp->hasContext(Expression::LValue) && m_exp->is(Expression::KindOfSimpleVariable)) { var = getScope()->getVariables()->getVariableName( cg, ar, static_pointer_cast<SimpleVariable>(m_exp)->getName()); } else { var = string(Option::SwitchPrefix) + lexical_cast<string>(varId); m_exp->getType()->outputCPPDecl(cg, ar, getScope()); cg_printf(" %s = (", var.c_str()); m_exp->outputCPP(cg, ar); cg_printf(");\n"); } } if (m_cases && m_cases->getCount()) { CaseStatementPtr defaultCase; int defaultCaseNum = -1; for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); if (stmt->getCondition()) { stmt->outputCPPAsIf(cg, ar, varId, var.c_str(), i); } else { defaultCase = stmt; defaultCaseNum = i; } } if (defaultCaseNum != -1) { defaultCase->outputCPPAsIf(cg, ar, varId, var.c_str(), defaultCaseNum); } else { cg_printf("goto break%d;\n", labelId); cg.addLabelId("break", labelId); } cg_printf("\n"); for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); stmt->outputCPPByNumber(cg, ar, varId, !stmt->getCondition() && defaultCaseNum != i ? -1 : i); } } } // Even though switch's break/continue will never goto these labels, we need // them for "break/continue n" inside switches. if (cg.findLabelId("continue", labelId)) { cg_printf("continue%d:;\n", labelId); } if (cg.findLabelId("break", labelId)) { cg_printf("break%d:;\n", labelId); } cg_indentEnd("}\n"); cg.popBreakScope(); }
/* GRAMMAR OF A SWITCH STATEMENT switch-statement -> switchexpression{switch-casesopt} switch-cases -> switch-case switch-cases opt switch-case -> case-labelstatements |default-labelstatements switch-case -> case-label; default-label; case-label -> casecase-item-list: case-item-list -> patternguard-clauseopt patternguard-clauseopt,case-item-list default-label -> default: guard-clause -> whereguard-expression guard-expression -> expression */ StatementPtr Parser::parseSwitch() { Token token; Flags flags(this, UNDER_SWITCH_CASE | SUPPRESS_TRAILING_CLOSURE); expect(Keyword::Switch, token); SwitchCasePtr ret = nodeFactory->createSwitch(token.state); { Flags f(flags); f += SUPPRESS_TRAILING_CLOSURE; ExpressionPtr controlExpr = parseExpression(); ret->setControlExpression(controlExpr); } expect(L"{"); // switch-cases -> switch-case switch-cases opt while(!predicate(L"}")) { //switch-case -> case-label statements |default-label statements expect_next(token); tassert(token, token.type == TokenType::Identifier, Errors::E_EXPECT_CASE, token.token); // case-label -> casecase-item-list: if(token.identifier.keyword == Keyword::Case) { Flags fcase(flags); fcase += UNDER_CASE; //case-item-list -> pattern guard-clause opt | pattern guard-clause opt , case-item-list CaseStatementPtr caseCond = nodeFactory->createCase(token.state); CodeBlockPtr codeBlock = nodeFactory->createCodeBlock(token.state); caseCond->setCodeBlock(codeBlock); do { PatternPtr pattern = parsePattern(); ExpressionPtr guard = NULL; if(match(L"where")) guard = parseExpression(); caseCond->addCondition(pattern, guard); } while(match(L",")); expect(L":"); ret->addCase(caseCond); parseSwitchStatements(caseCond); } else if(token.identifier.keyword == Keyword::Default) { expect(L":"); CaseStatementPtr caseCond = nodeFactory->createCase(token.state); CodeBlockPtr codeBlock = nodeFactory->createCodeBlock(token.state); caseCond->setCodeBlock(codeBlock); parseSwitchStatements(caseCond); ret->setDefaultCase(caseCond); } else { tassert(token, false, Errors::E_EXPECT_CASE, token.token); } } expect(L"}"); return ret; }
int ControlFlowBuilder::before(ConstructRawPtr cp) { int ret = FunctionWalker::before(cp); if (ret == WalkContinue) { if (m_pass == 1) { if (StatementPtr s = dynamic_pointer_cast<Statement>(cp)) { if (FunctionWalker::SkipRecurse(s)) not_reached(); Statement::KindOf stype = s->getKindOf(); switch (stype) { case Statement::KindOfUseTraitStatement: case Statement::KindOfTraitPrecStatement: case Statement::KindOfTraitAliasStatement: not_reached(); case Statement::KindOfStaticStatement: addEdge(s, BeforeConstruct, s, AfterConstruct); break; case Statement::KindOfClassVariable: not_reached(); case Statement::KindOfClassConstant: case Statement::KindOfGlobalStatement: case Statement::KindOfUnsetStatement: case Statement::KindOfExpStatement: case Statement::KindOfStatementList: case Statement::KindOfBlockStatement: break; case Statement::KindOfTryStatement: { TryStatementPtr t = static_pointer_cast<TryStatement>(s); StatementListPtr catches = t->getCatches(); StatementPtr body = t->getBody(); FinallyStatementPtr finally = static_pointer_cast<FinallyStatement>(t->getFinally()); if (body) { for (int n = catches->getCount(), j = 0; j < n; ++j) { addEdge(body, BeforeConstruct, (*catches)[j], BeforeConstruct); addEdge(body, AfterConstruct, (*catches)[j], BeforeConstruct); } if (finally) { addEdge(body, BeforeConstruct, finally, BeforeConstruct); addEdge(body, AfterConstruct, finally, BeforeConstruct); } addEdge(body, AfterConstruct, t, AfterConstruct); noFallThrough(body); } break; } case Statement::KindOfThrowStatement: { size_t d = depth(); for (size_t i = 1; i < d; i++) { TryStatementPtr t = dynamic_pointer_cast<TryStatement>(top(i)); if (t) { StatementListPtr catches = t->getCatches(); for (int n = catches->getCount(), j = 0; j < n; ++j) { addEdge(s, AfterConstruct, (*catches)[j], BeforeConstruct); } break; } } break; } case Statement::KindOfFinallyStatement: break; case Statement::KindOfCatchStatement: break; case Statement::KindOfIfStatement: break; case Statement::KindOfIfBranchStatement: { IfBranchStatementPtr ibr = static_pointer_cast<IfBranchStatement>(s); if (ibr->getCondition()) { if (ibr->getStmt()) { addEdge(ibr->getCondition(), AfterConstruct, ibr, AfterConstruct); addEdge(ibr->getStmt(), AfterConstruct, top(2), AfterConstruct); noFallThrough(ibr); } else { addEdge(ibr->getCondition(), AfterConstruct, top(2), AfterConstruct); } } break; } case Statement::KindOfForStatement: { ForStatementPtr fs(static_pointer_cast<ForStatement>(s)); ConstructRawPtr body(fs->getBody()); ConstructRawPtr cond(fs->getCondExp()); ConstructRawPtr incr(fs->getIncExp()); if (cond) addEdge(cond, AfterConstruct, s, AfterConstruct); ConstructRawPtr end = incr ? incr : body ? body : cond; ConstructRawPtr start = cond ? cond : body ? body : incr; if (end) addEdge(end, AfterConstruct, start, BeforeConstruct); noFallThrough(s); break; } case Statement::KindOfWhileStatement: { WhileStatementPtr ws(static_pointer_cast<WhileStatement>(s)); ConstructRawPtr body(ws->getBody()); ConstructRawPtr cond(ws->getCondExp()); addEdge(cond, AfterConstruct, s, AfterConstruct); addEdge(body ? body : cond, AfterConstruct, cond, BeforeConstruct); noFallThrough(s); break; } case Statement::KindOfDoStatement: { DoStatementPtr ds(static_pointer_cast<DoStatement>(s)); addEdge(ds->getCondExp(), AfterConstruct, s, BeforeConstruct); break; } case Statement::KindOfForEachStatement: { ForEachStatementPtr fs(static_pointer_cast<ForEachStatement>(s)); ConstructRawPtr body(fs->getBody()); ConstructRawPtr name(fs->getNameExp()); ConstructRawPtr value(fs->getValueExp()); ConstructRawPtr begin = name ? name : value; ConstructRawPtr end = body ? body : value; addEdge(end, AfterConstruct, begin, BeforeConstruct); addEdge(value, AfterConstruct, s, AfterConstruct); break; } case Statement::KindOfSwitchStatement: { SwitchStatementPtr sw(static_pointer_cast<SwitchStatement>(s)); if (StatementListPtr cases = sw->getCases()) { ExpressionPtr exp; StatementPtr def; for (int n = cases->getCount(), i = 0; i < n; ++i) { CaseStatementPtr caseStmt = static_pointer_cast<CaseStatement>((*cases)[i]); if (!caseStmt->getCondition()) { def = caseStmt->getStatement(); } else { if (exp) { addEdge(exp, AfterConstruct, caseStmt, BeforeConstruct); } exp = caseStmt->getCondition(); } } if (exp) { if (def) { addEdge(exp, AfterConstruct, def, BeforeConstruct); } else { addEdge(exp, AfterConstruct, s, AfterConstruct); } } } break; } case Statement::KindOfCaseStatement: // already handled by switch break; case Statement::KindOfLabelStatement: { LabelStatementPtr l(static_pointer_cast<LabelStatement>(s)); m_labelInfoMap[l->label()].first = l; cfi(l).m_isTarget[BeforeConstruct] = true; break; } case Statement::KindOfGotoStatement: { GotoStatementPtr g(static_pointer_cast<GotoStatement>(s)); m_labelInfoMap[g->label()].second.push_back(g); noFallThrough(s); break; } case Statement::KindOfReturnStatement: { setEdge(s, AfterConstruct, root(), AfterConstruct); if (!m_isGenerator) noFallThrough(s); /* * Since almost anything in php /might/ throw, we * approximate, and add edges from the beginning and * end of a try block. But if there's a return, that * would kill the edge from the end of the block. * Explicitly add them here if the return is in a try */ size_t d = depth(); for (size_t i = 1; i < d; i++) { TryStatementPtr t = dynamic_pointer_cast<TryStatement>(top(i)); if (t) { StatementListPtr catches = t->getCatches(); for (int n = catches->getCount(), j = 0; j < n; ++j) { addEdge(s, AfterConstruct, (*catches)[j], BeforeConstruct); } break; } } break; } case Statement::KindOfBreakStatement: case Statement::KindOfContinueStatement: { noFallThrough(s); int val = dynamic_pointer_cast<BreakStatement>(s)->getDepth(); size_t d = depth(); for (size_t i = 1; i < d; i++) { ConstructRawPtr c = top(i); if (LoopStatementPtr l = dynamic_pointer_cast<LoopStatement>(c)) { if (val <= 1) { if (stype == Statement::KindOfBreakStatement) { addEdge(s, AfterConstruct, l, AfterConstruct); } else { ConstructRawPtr kid; switch (l->getKindOf()) { case Statement::KindOfForEachStatement: { ForEachStatementPtr fs(static_pointer_cast<ForEachStatement>(l)); kid = fs->getNameExp(); if (!kid) { kid = fs->getValueExp(); } break; } case Statement::KindOfForStatement: { ForStatementPtr fs(static_pointer_cast<ForStatement>(l)); kid = fs->getIncExp(); if (!kid) kid = fs->getCondExp(); if (!kid) kid = fs->getBody(); break; } case Statement::KindOfWhileStatement: { WhileStatementPtr ws(static_pointer_cast<WhileStatement>(l)); kid = ws->getCondExp(); break; } case Statement::KindOfDoStatement: { DoStatementPtr ds(static_pointer_cast<DoStatement>(l)); kid = ds->getCondExp(); break; } default: always_assert(0); } always_assert(kid); addEdge(s, AfterConstruct, kid, BeforeConstruct); } } if (val && !--val) break; } else if (SwitchStatementPtr sw = dynamic_pointer_cast<SwitchStatement>(c)) { if (val <= 1) { addEdge(s, AfterConstruct, sw, AfterConstruct); } if (val && !--val) break; } } break; } case Statement::KindOfEchoStatement: case Statement::KindOfTypedefStatement: break; default: not_reached(); } } else { ExpressionPtr e(dynamic_pointer_cast<Expression>(cp)); switch (e->getKindOf()) { case Expression::KindOfBinaryOpExpression: { BinaryOpExpressionPtr b(static_pointer_cast<BinaryOpExpression>(e)); if (b->isShortCircuitOperator()) { ConstructPtr trueBranch, falseBranch; ConstructLocation tLoc, fLoc; getTrueFalseBranches(0, trueBranch, tLoc, falseBranch, fLoc); assert(trueBranch); assert(falseBranch); if (b->isLogicalOrOperator()) { addEdge(b->getExp1(), AfterConstruct, trueBranch, tLoc); } else { addEdge(b->getExp1(), AfterConstruct, falseBranch, fLoc); } } break; } case Expression::KindOfQOpExpression: { QOpExpressionPtr q(static_pointer_cast<QOpExpression>(e)); if (ExpressionPtr e1 = q->getYes()) { addEdge(q->getCondition(), AfterConstruct, q->getNo(), BeforeConstruct); addEdge(e1, AfterConstruct, q->getNo(), AfterConstruct); noFallThrough(e1); } else { addEdge(q->getCondition(), AfterConstruct, q->getNo(), AfterConstruct); } break; } default: break; } } if (ControlFlowInfo *c = get(cp)) { if (c->m_targets[BeforeConstruct].size()) { if (!m_cur) newBlock(); m_ccbpMap[HoldingBlock][cp] = m_cur; endBlock(cp, BeforeConstruct); } else if (c->m_isTarget[BeforeConstruct]) { endBlock(cp, BeforeConstruct); } } if (!m_cur) newBlock(); m_ccbpMap[BeforeConstruct][cp] = m_cur; } else if (m_pass == 2) { ControlBlock *hb = m_ccbpMap[HoldingBlock][cp]; if (hb) { if (hb != m_cur) { if (m_cur) { addCFEdge(m_cur, hb); } m_cur = hb; } } ControlBlock *bb = m_ccbpMap[BeforeConstruct][cp]; always_assert(bb); if (bb != m_cur) { if (m_cur) { addCFEdge(m_cur, bb); } m_cur = bb; } if (ControlFlowInfo *c = get(cp)) { ConstructPtrLocMap &beforeTargets = c->m_targets[BeforeConstruct]; if (beforeTargets.size()) { always_assert(hb); addCFEdge(hb, bb); ConstructPtrLocMap::iterator it = beforeTargets.begin(), end = beforeTargets.end(); while (it != end) { addCFEdge(hb, it->first, it->second); ++it; } } } } } return ret; }