static IMethod ImplementIntoCaseConversion(this VirtualType type, IType valueType, IMethod caseCtor, bool isImplicit = true)
        {
            Debug.Assert(valueType == caseCtor.Parameters.Single().Type);
            Debug.Assert(valueType.Kind != TypeKind.Interface); // can't have conversion from interface
            Debug.Assert(valueType != type);                    // can't have conversion from itself

            var caseFactory = new VirtualMethod(type, Accessibility.Public,
                                                isImplicit ? "op_Implicit" : "op_Explicit",
                                                new[] { new VirtualParameter(valueType, "item") },
                                                returnType: type,
                                                isStatic: true
                                                );

            caseFactory.BodyFactory = () => {
                var @this             = new IL.ILVariable(IL.VariableKind.Parameter, valueType, 0);
                IL.ILInstruction body = new IL.NewObj(caseCtor)
                {
                    Arguments = { new IL.LdLoc(@this) }
                };
                if (valueType.IsReferenceType == true)
                {
                    // pass nulls
                    body = new IL.IfInstruction(
                        new IL.Comp(IL.ComparisonKind.Inequality, Sign.None, new IL.LdLoc(@this), new IL.LdNull()),
                        body,
                        new IL.LdNull()
                        );
                }
                return(EmitExtensions.CreateExpressionFunction(caseFactory, body));
            };
            type.Methods.Add(caseFactory);
            return(caseFactory);
        }
Beispiel #2
0
        /// <summary>
        /// if (cond) { ...; exit; } else { ... }
        /// ...;
        ///   ->
        /// if (cond) { ...; exit; }
        /// ...;
        /// ...;
        /// </summary>
        /// <param name="ifInst"></param>
        private void ExtractElseBlock(IfInstruction ifInst)
        {
            Debug.Assert(ifInst.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable));
            var block      = (Block)ifInst.Parent;
            var falseBlock = (Block)ifInst.FalseInst;

            context.Step("Extract else block", ifInst);
            int insertAt = block.Instructions.IndexOf(ifInst) + 1;

            for (int i = 0; i < falseBlock.Instructions.Count; i++)
            {
                block.Instructions.Insert(insertAt++, falseBlock.Instructions[i]);
            }

            ifInst.FalseInst = new Nop();
        }
        /// <summary>
        /// For an if statement with an unreachable end point and no else block,
        /// inverts to match IL order of the first statement of each branch
        /// </summary>
        private void ImproveILOrdering(Block block, IfInstruction ifInst)
        {
            if (!block.HasFlag(InstructionFlags.EndPointUnreachable) ||
                !ifInst.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable) ||
                !ifInst.FalseInst.MatchNop())
            {
                return;
            }

            Debug.Assert(ifInst != block.Instructions.Last());

            var trueRangeStart  = ConditionDetection.GetStartILOffset(ifInst.TrueInst, out bool trueRangeIsEmpty);
            var falseRangeStart = ConditionDetection.GetStartILOffset(block.Instructions[block.Instructions.IndexOf(ifInst) + 1], out bool falseRangeIsEmpty);

            if (!trueRangeIsEmpty && !falseRangeIsEmpty && falseRangeStart < trueRangeStart)
            {
                ConditionDetection.InvertIf(block, ifInst, context);
            }
        }
        /// <summary>
        /// Reduce Nesting in if/else statements by duplicating an exit instruction.
        /// Does not affect IL order
        /// </summary>
        private bool ReduceNesting(Block block, IfInstruction ifInst, ILInstruction exitInst)
        {
            // start tallying stats for heuristics from then and else-if blocks
            int maxStatements = 0, maxDepth = 0;

            UpdateStats(ifInst.TrueInst, ref maxStatements, ref maxDepth);

            // if (cond) { ... } exit;
            if (ifInst.FalseInst.MatchNop())
            {
                // a separate heuristic tp ShouldReduceNesting as there is visual balancing to be performed based on number of statments
                if (maxDepth < 2)
                {
                    return(false);
                }

                //   ->
                // if (!cond) exit;
                // ...; exit;
                EnsureEndPointUnreachable(ifInst.TrueInst, exitInst);
                EnsureEndPointUnreachable(block, exitInst);
                ConditionDetection.InvertIf(block, ifInst, context);
                return(true);
            }

            // else-if trees are considered as a single group from the root IfInstruction
            if (GetElseIfParent(ifInst) != null)
            {
                return(false);
            }

            // find the else block and tally stats for each else-if block
            while (Block.Unwrap(ifInst.FalseInst) is IfInstruction elseIfInst)
            {
                UpdateStats(elseIfInst.TrueInst, ref maxStatements, ref maxDepth);
                ifInst = elseIfInst;
            }

            if (!ShouldReduceNesting(ifInst.FalseInst, maxStatements, maxDepth))
            {
                return(false);
            }

            // extract the else block and insert exit points all the way up the else-if tree
            do
            {
                var elseIfInst = GetElseIfParent(ifInst);

                // if (cond) { ... } else { ... } exit;
                //   ->
                // if (cond) { ...; exit; }
                // ...; exit;
                EnsureEndPointUnreachable(ifInst.TrueInst, exitInst);
                if (ifInst.FalseInst.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    Debug.Assert(ifInst.HasFlag(InstructionFlags.EndPointUnreachable));
                    Debug.Assert(ifInst.Parent == block);
                    int removeAfter = ifInst.ChildIndex + 1;
                    if (removeAfter < block.Instructions.Count)
                    {
                        // Remove all instructions that ended up dead
                        // (this should just be exitInst itself)
                        Debug.Assert(block.Instructions.SecondToLastOrDefault() == ifInst);
                        Debug.Assert(block.Instructions.Last() == exitInst);
                        block.Instructions.RemoveRange(removeAfter, block.Instructions.Count - removeAfter);
                    }
                }
                ExtractElseBlock(ifInst);
                ifInst = elseIfInst;
            } while (ifInst != null);

            return(true);
        }