Beispiel #1
0
        private void HandleSwitchExpression(BlockContainer container, SwitchInstruction switchInst)
        {
            if (!context.Settings.SwitchExpressions)
            {
                return;
            }
            Debug.Assert(container.Kind == ContainerKind.Switch);
            Debug.Assert(container.ResultType == StackType.Void);
            var            defaultSection = switchInst.GetDefaultSection();
            StackType      resultType     = StackType.Void;
            BlockContainer leaveTarget    = null;
            ILVariable     resultVariable = null;

            foreach (var section in switchInst.Sections)
            {
                if (section != defaultSection)
                {
                    // every section except for the default must have exactly 1 label
                    if (section.Labels.Count() != (section.HasNullLabel ? 0u : 1u))
                    {
                        return;
                    }
                }
                if (!section.Body.MatchBranch(out var sectionBlock))
                {
                    return;
                }
                if (sectionBlock.IncomingEdgeCount != 1)
                {
                    return;
                }
                if (sectionBlock.Parent != container)
                {
                    return;
                }
                if (sectionBlock.Instructions.Count == 1)
                {
                    if (sectionBlock.Instructions[0] is Throw)
                    {
                        // OK
                    }
                    else if (sectionBlock.Instructions[0] is Leave leave)
                    {
                        if (!leave.IsLeavingFunction)
                        {
                            return;
                        }
                        leaveTarget ??= leave.TargetContainer;
                        Debug.Assert(leaveTarget == leave.TargetContainer);
                        resultType = leave.Value.ResultType;
                    }
                    else
                    {
                        return;
                    }
                }
                else if (sectionBlock.Instructions.Count == 2)
                {
                    if (!sectionBlock.Instructions[0].MatchStLoc(out var v, out _))
                    {
                        return;
                    }
                    if (!sectionBlock.Instructions[1].MatchLeave(container))
                    {
                        return;
                    }
                    resultVariable ??= v;
                    if (resultVariable != v)
                    {
                        return;
                    }
                    resultType = resultVariable.StackType;
                }
                else
                {
                    return;
                }
            }
            // Exactly one of resultVariable/leaveTarget must be null
            if ((resultVariable == null) == (leaveTarget == null))
            {
                return;
            }
            if (switchInst.Value is StringToInt str2int)
            {
                // validate that each integer is used for exactly one value
                var integersUsed = new HashSet <int>();
                foreach ((string key, int val) in str2int.Map)
                {
                    if (!integersUsed.Add(val))
                    {
                        return;
                    }
                }
            }

            context.Step("Switch Expression", switchInst);
            switchInst.SetResultType(resultType);
            foreach (var section in switchInst.Sections)
            {
                var block = ((Branch)section.Body).TargetBlock;
                if (block.Instructions.Count == 1)
                {
                    if (block.Instructions[0] is Throw t)
                    {
                        t.resultType = resultType;
                        section.Body = t;
                    }
                    else if (block.Instructions[0] is Leave leave)
                    {
                        section.Body = leave.Value;
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }
                }
                else
                {
                    section.Body = ((StLoc)block.Instructions[0]).Value;
                }
            }
            if (resultVariable != null)
            {
                container.ReplaceWith(new StLoc(resultVariable, switchInst));
            }
            else
            {
                container.ReplaceWith(new Leave(leaveTarget, switchInst));
            }
            context.RequestRerun();             // new StLoc might trigger inlining
        }