public virtual void VisitSwitch(BoundSwitch node) { DefaultVisit(node); }
public override void VisitSwitch(BoundSwitch node) { MarkRead(node.Temporary); // Mark the write as other to prevent removing of the temporary. // The whole point of the temporary is that we require it for the // switch construct, so it cannot be removed. GetStatistic(node.Temporary).WriteState = WriteType.DoNotRemove; base.VisitSwitch(node); }
public override void VisitSwitch(BoundSwitch node) { // Rules for switch: // * Target for break; // * If we don't have any cases, there is no work; // * If we only have a default case, _branch stays the default // and there is no special handling; // * Otherwise, we fully process the cases. Things become a bit // tricky in a switch because of label fall through. // Cases really are nested ifs. Say we have the following: // // switch (x) { // case 1: // a(); // case 2: // b(); // break; // } // // This can be rewritten as // // if (x == 1 || x == 2) { // if (x == 1) { // a(); // } // b(); // } // // With this rewrite, we can construct cases as follows: // - Every case is a fork; // - If the branch isn't killed, we create an extra fork // (the else of the 'if (x == 1)' above); // - Otherwise, we create a new fork for the next case. // - Default is treated like any other case; except that when // we don't have a default, we create an extra fork at the // end of the switch. // // One extra note: we don't have an expression here. The bound // tree has already introduced a temporary for the result of the // expression, so we don't have any handling for that here. // Mark a read on the temporary. MarkRead(node.Temporary); // No cases means no work. if (node.Cases.Count == 0) return; // When we only have a default case, we can just take the default. // However, we're still a break target so we do need to create // a block. if (node.Cases.Count == 1 && node.Cases[0].Expression == null) { var block = new Block(_branch, FindLabel(node), true, false, null); PushBlock(block); base.VisitSwitch(node); PopBlock(block); return; } bool hadDefault = false; // Push the block for the switch. var switchBlock = new Block(_branch, FindLabel(node), true, false, null); PushBlock(switchBlock); // Clear the branch to signal the creation of a new branch. BoundTypeManager.DefiniteAssignmentMarker.Branch caseBranch = null; _branch = null; for (int i = 0; i < node.Cases.Count; i++) { var @case = node.Cases[i]; if (@case.Expression == null) hadDefault = true; // Create a new branch for a series of cases (either the // first one or on fall through). if (caseBranch == null) caseBranch = switchBlock.Branch.Fork(); // Create a new branch for the contents of this case. _branch = caseBranch.Fork(); Visit(@case); // Do we have a fall through (and we're not the last, because // that doesn't fall through)? if (!_branch.IsKilled && i != node.Cases.Count - 1) { // If we have a fall through, join the branch // and an empty branch to close the current case. // This makes this case optional. caseBranch.Join(new[] { _branch, caseBranch.Fork() }); } else { // If we don't have a fall through, check whether the // branch is killed. If it isn't killed, we must be the // last case and it's missing the break, which we insert // here. if (!_branch.IsKilled) { Debug.Assert(i == node.Cases.Count - 1); JoinOnBlock(null, JoinType.Break); } // The case branch has already been joined on the switch // block. Close this case and signal the creation of a new // branch. caseBranch = null; } } // If we didn't have a default case, we need to create an empty // branch for the else. if (!hadDefault) switchBlock.JoinBranch(switchBlock.Branch.Fork()); // And pop the switch block. PopBlock(switchBlock); }
private void EmitSwitch(BoundSwitch node) { // Create the label that jumps to the end of the switch. var after = IL.DefineLabel(GetLabel(node)); _scope.BreakTargets.Push(after); // Create the bound node to get the temporary. var temporaryNode = new BoundGetVariable(node.Temporary); var bodies = new List<Tuple<Label, BoundBlock>>(); // Emit the jump table. Label? defaultTarget = null; foreach (var @case in node.Cases) { if (@case.Expression == null) { IL.MarkSequencePoint(@case.Location); defaultTarget = IL.DefineLabel(); bodies.Add(Tuple.Create(defaultTarget.Value, @case.Body)); } else { var target = IL.DefineLabel(); IL.MarkSequencePoint(@case.Location); EmitTest( new BoundBinary( BoundExpressionType.Equal, temporaryNode, @case.Expression ), target ); bodies.Add(Tuple.Create(target, @case.Body)); } } // Emit the jump to either the default block or after the switch. IL.Emit(OpCodes.Br, defaultTarget.GetValueOrDefault(after.Label)); // Emit the bodies. foreach (var body in bodies) { IL.MarkLabel(body.Item1); EmitStatement(body.Item2); } // Emit the label after the switch. IL.MarkLabel(after); // Pop the break target to make the previous one available. _scope.BreakTargets.Pop(); }