/// <summary> /// Visit switch-case item. /// Case expression and case body. /// </summary> /// <param name="x"></param> virtual public void VisitCaseItem(CaseItem x) { VisitElement(x.CaseVal); VisitSwitchItem(x); }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override void Emit(CodeGenerator /*!*/ codeGenerator) { Statistics.AST.AddNode("SwitchStmt"); ILEmitter il = codeGenerator.IL; // Note: // SwitchStmt is now implemented in the most general (and unefficient) way. The whole switch // is understood as a series of if-elseif-else statements. Label exit_label = il.DefineLabel(); bool fall_through = false; Label fall_through_label = il.DefineLabel(); Label last_default_label = il.DefineLabel(); DefaultItem last_default = GetLastDefaultItem(); LocalBuilder branch_to_lastdefault = null; if (last_default != null) { branch_to_lastdefault = il.DeclareLocal(Types.Bool[0]); il.LdcI4(0); il.Stloc(branch_to_lastdefault); } codeGenerator.BranchingStack.BeginLoop(exit_label, exit_label, codeGenerator.ExceptionBlockNestingLevel); // marks a sequence point containing the discriminator evaluation: codeGenerator.MarkSequencePoint( switchValue.Position.FirstLine, switchValue.Position.FirstColumn, switchValue.Position.LastLine, switchValue.Position.LastColumn + 1); // Evaluate condition value and store the result into local variable codeGenerator.EmitBoxing(switchValue.Emit(codeGenerator)); LocalBuilder condition_value = il.DeclareLocal(Types.Object[0]); il.Stloc(condition_value); foreach (SwitchItem item in switchItems) { item.MarkSequencePoint(codeGenerator); // switch item is either CaseItem ("case xxx:") or DefaultItem ("default") item: CaseItem case_item = item as CaseItem; if (case_item != null) { Label false_label = il.DefineLabel(); // PhpComparer.Default.CompareEq(<switch expr. value>,<case value>); /*changed to static method*/ //il.Emit(OpCodes.Ldsfld, Fields.PhpComparer_Default); codeGenerator.EmitCompareEq( cg => { cg.IL.Ldloc(condition_value); return(PhpTypeCode.Object); }, cg => case_item.EmitCaseValue(cg)); // IF (!STACK) GOTO false_label; il.Emit(OpCodes.Brfalse, false_label); if (fall_through == true) { il.MarkLabel(fall_through_label, true); fall_through = false; } case_item.EmitStatements(codeGenerator); if (fall_through == false) { fall_through_label = il.DefineLabel(); fall_through = true; } il.Emit(OpCodes.Br, fall_through_label); il.MarkLabel(false_label, true); } else { DefaultItem default_item = (DefaultItem)item; // Only the last default branch defined in source code is used. // So skip default while testing "case" items at runtime. Label false_label = il.DefineLabel(); il.Emit(OpCodes.Br, false_label); if (default_item == last_default) { il.MarkLabel(last_default_label, false); } if (fall_through == true) { il.MarkLabel(fall_through_label, true); fall_through = false; } default_item.EmitStatements(codeGenerator); if (fall_through == false) { fall_through_label = il.DefineLabel(); fall_through = true; } il.Emit(OpCodes.Br, fall_through_label); il.MarkLabel(false_label, true); } } // If no case branch matched, branch to last default case if any is defined if (last_default != null) { // marks a sequence point containing the condition evaluation or skip of the default case: codeGenerator.MarkSequencePoint( last_default.Position.FirstLine, last_default.Position.FirstColumn, last_default.Position.LastLine, last_default.Position.LastColumn + 1); Debug.Assert(branch_to_lastdefault != null); Label temp = il.DefineLabel(); // IF (!branch_to_lastdefault) THEN il.Ldloc(branch_to_lastdefault); il.LdcI4(0); il.Emit(OpCodes.Bne_Un, temp); if (true) { // branch_to_lastdefault = TRUE; il.LdcI4(1); il.Stloc(branch_to_lastdefault); // GOTO last_default_label; il.Emit(OpCodes.Br, last_default_label); } il.MarkLabel(temp, true); // END IF; il.ForgetLabel(last_default_label); } if (fall_through == true) { il.MarkLabel(fall_through_label, true); } il.MarkLabel(exit_label); codeGenerator.BranchingStack.EndLoop(); il.ForgetLabel(exit_label); }