Beispiel #1
0
 public virtual void VisitSwitch(BoundSwitch node)
 {
     DefaultVisit(node);
 }
Beispiel #2
0
            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);
            }
Beispiel #4
0
        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();
        }