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 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(); }