Ejemplo n.º 1
0
 internal void RemoveLoadInstruction(LdLoc inst) => RemoveInstruction(loadInstructions, inst.IndexInLoadInstructionList, inst);
Ejemplo n.º 2
0
        /// <summary>
        /// The transform works like this:
        ///
        /// <para>
        /// local functions can either be used in method calls, i.e., call and callvirt instructions,
        /// or can be used as part of the "delegate construction" pattern, i.e.,
        /// <c>newobj Delegate(&lt;target-expression&gt;, ldftn &lt;method&gt;)</c>.
        /// </para>
        /// As local functions can be declared practically anywhere, we have to take a look at
        /// all use-sites and infer the declaration location from that. Use-sites can be call,
        /// callvirt and ldftn instructions.
        /// After all use-sites are collected we construct the ILAst of the local function
        /// and add it to the parent function.
        /// Then all use-sites of the local-function are transformed to a call to the
        /// <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>.
        /// In a next step we handle all nested local functions.
        /// After all local functions are transformed, we move all local functions that capture
        /// any variables to their respective declaration scope.
        /// </summary>
        public void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.LocalFunctions)
            {
                return;
            }
            // Disable the transform if we are decompiling a display-class or local function method:
            // This happens if a local function or display class is selected in the ILSpy tree view.
            if (IsLocalFunctionMethod(function.Method, context) || IsLocalFunctionDisplayClass(function.Method.ParentModule.PEFile, (TypeDefinitionHandle)function.Method.DeclaringTypeDefinition.MetadataToken, context))
            {
                return;
            }
            this.context        = context;
            this.resolveContext = new SimpleTypeResolveContext(function.Method);
            var localFunctions    = new Dictionary <MethodDefinitionHandle, LocalFunctionInfo>();
            var cancellationToken = context.CancellationToken;

            // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas.
            FindUseSites(function, context, localFunctions);
            foreach (var(_, info) in localFunctions)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (info.Definition == null)
                {
                    function.Warnings.Add($"Could not decode local function '{info.Method}'");
                    continue;
                }

                context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition);
                try {
                    var localFunction = info.Definition;
                    if (!localFunction.Method.IsStatic)
                    {
                        var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
                        var target  = info.UseSites.Where(us => us.Arguments[0].MatchLdLoc(out _)).FirstOrDefault()?.Arguments[0];
                        if (target == null)
                        {
                            target = info.UseSites[0].Arguments[0];
                            if (target.MatchLdFld(out var target1, out var field) && thisVar.Type.Equals(field.Type) && field.Type.Kind == TypeKind.Class && TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition()))
                            {
                                var variable = function.Descendants.OfType <ILFunction>().SelectMany(f => f.Variables).Where(v => !v.IsThis() && TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(field.Type)).OnlyOrDefault();
                                if (variable != null)
                                {
                                    target = new LdLoc(variable);
                                    HandleArgument(localFunction, 1, 0, target);
                                }
                            }
                        }
                        context.Step($"Replace 'this' with {target}", localFunction);
                        localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
                    }

                    foreach (var useSite in info.UseSites)
                    {
                        DetermineCaptureAndDeclarationScope(localFunction, useSite);

                        if (function.Method.IsConstructor && localFunction.DeclarationScope == null)
                        {
                            localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite);
                        }
                    }

                    if (localFunction.DeclarationScope == null)
                    {
                        localFunction.DeclarationScope = (BlockContainer)function.Body;
                    }

                    ILFunction declaringFunction = GetDeclaringFunction(localFunction);
                    if (declaringFunction != function)
                    {
                        function.LocalFunctions.Remove(localFunction);
                        declaringFunction.LocalFunctions.Add(localFunction);
                    }

                    if (TryValidateSkipCount(info, out int skipCount) && skipCount != localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters)
                    {
                        Debug.Assert(false);
                        function.Warnings.Add($"Could not decode local function '{info.Method}'");
                        if (declaringFunction != function)
                        {
                            declaringFunction.LocalFunctions.Remove(localFunction);
                        }
                        continue;
                    }

                    foreach (var useSite in info.UseSites)
                    {
                        context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite);
                        if (useSite.OpCode == OpCode.NewObj)
                        {
                            TransformToLocalFunctionReference(localFunction, useSite);
                        }
                        else
                        {
                            TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
                        }
                    }
                } finally {
                    context.StepEndGroup();
                }
            }
        }
Ejemplo n.º 3
0
 internal void AddLoadInstruction(LdLoc inst) => inst.IndexInLoadInstructionList = AddInstruction(loadInstructions, inst);
Ejemplo n.º 4
0
        /// <summary>
        /// Matches Roslyn C# switch on nullable.
        /// </summary>
        bool MatchRoslynSwitchOnNullable(InstructionCollection <ILInstruction> instructions, int i, out SwitchInstruction newSwitch)
        {
            newSwitch = null;
            // match first block:
            // if (logic.not(call get_HasValue(target))) br nullCaseBlock
            // br switchBlock
            if (!instructions[i].MatchIfInstruction(out var condition, out var trueInst))
            {
                return(false);
            }
            if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock))
            {
                return(false);
            }
            if (!condition.MatchLogicNot(out var getHasValue) || !NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction target) || !SemanticHelper.IsPure(target.Flags))
            {
                return(false);
            }
            // match second block: switchBlock
            // note: I have seen cases where switchVar is inlined into the switch.
            // stloc switchVar(call GetValueOrDefault(ldloca tmp))
            // switch (ldloc switchVar) {
            //  case [0..1): br caseBlock1
            // ... more cases ...
            //  case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock
            // }
            if (switchBlock.IncomingEdgeCount != 1)
            {
                return(false);
            }
            SwitchInstruction switchInst;

            switch (switchBlock.Instructions.Count)
            {
            case 2:
            {
                // this is the normal case described by the pattern above
                if (!switchBlock.Instructions[0].MatchStLoc(out var switchVar, out var getValueOrDefault))
                {
                    return(false);
                }
                if (!switchVar.IsSingleDefinition || switchVar.LoadCount != 1)
                {
                    return(false);
                }
                if (!(NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction target2) && target2.Match(target).Success))
                {
                    return(false);
                }
                if (!(switchBlock.Instructions[1] is SwitchInstruction si))
                {
                    return(false);
                }
                switchInst = si;
                break;
            }

            case 1:
            {
                // this is the special case where `call GetValueOrDefault(ldloca tmp)` is inlined into the switch.
                if (!(switchBlock.Instructions[0] is SwitchInstruction si))
                {
                    return(false);
                }
                if (!(NullableLiftingTransform.MatchGetValueOrDefault(si.Value, out ILInstruction target2) && target2.Match(target).Success))
                {
                    return(false);
                }
                switchInst = si;
                break;
            }

            default:
            {
                return(false);
            }
            }
            ILInstruction switchValue;

            if (target.MatchLdLoca(out var v))
            {
                switchValue = new LdLoc(v).WithILRange(target);
            }
            else
            {
                switchValue = new LdObj(target, ((CallInstruction)getHasValue).Method.DeclaringType);
            }
            newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, switchValue);
            return(true);
        }
Ejemplo n.º 5
0
 protected internal override void VisitLdLoc(LdLoc inst)
 {
     EnsureInitialized(inst.Variable);
 }
Ejemplo n.º 6
0
        Statement TransformToForeach(UsingInstruction inst, out Expression resource)
        {
            // Check if the using resource matches the GetEnumerator pattern.
            resource = exprBuilder.Translate(inst.ResourceExpression);
            var m = getEnumeratorPattern.Match(resource);

            // The using body must be a BlockContainer.
            if (!(inst.Body is BlockContainer container) || !m.Success)
            {
                return(null);
            }
            // The using-variable is the enumerator.
            var enumeratorVar = inst.Variable;
            // If there's another BlockContainer nested in this container and it only has one child block, unwrap it.
            // If there's an extra leave inside the block, extract it into optionalReturnAfterLoop.
            var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
            // Detect whether we're dealing with a while loop with multiple embedded statements.
            var loop = DetectedLoop.DetectLoop(loopContainer);

            if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
            {
                return(null);
            }
            // The loop condition must be a call to enumerator.MoveNext()
            var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
            var m2        = moveNextConditionPattern.Match(condition.Expression);

            if (!m2.Success)
            {
                return(null);
            }
            // Check enumerator variable references.
            var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable();

            if (enumeratorVar2 != enumeratorVar)
            {
                return(null);
            }
            // Detect which foreach-variable transformation is necessary/possible.
            var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, condition.ILInstructions.Single(),
                                                                out var singleGetter, out var foreachVariable);

            if (transformation == RequiredGetCurrentTransformation.NoForeach)
            {
                return(null);
            }
            // The existing foreach variable, if found, can only be used in the loop container.
            if (foreachVariable != null && !(foreachVariable.CaptureScope == null || foreachVariable.CaptureScope == loopContainer))
            {
                return(null);
            }
            // Extract in-expression
            var collectionExpr = m.Get <Expression>("collection").Single();

            // Special case: foreach (var item in this) is decompiled as foreach (var item in base)
            // but a base reference is not valid in this context.
            if (collectionExpr is BaseReferenceExpression)
            {
                collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr);
            }
            // Handle explicit casts:
            // This is the case if an explicit type different from the collection-item-type was used.
            // For example: foreach (ClassA item in nonGenericEnumerable)
            var           type          = singleGetter.Method.ReturnType;
            ILInstruction instToReplace = singleGetter;

            switch (instToReplace.Parent)
            {
            case CastClass cc:
                type          = cc.Type;
                instToReplace = cc;
                break;

            case UnboxAny ua:
                type          = ua.Type;
                instToReplace = ua;
                break;
            }
            // Handle the required foreach-variable transformation:
            switch (transformation)
            {
            case RequiredGetCurrentTransformation.UseExistingVariable:
                foreachVariable.Type = type;
                foreachVariable.Kind = VariableKind.ForeachLocal;
                foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), foreachVariable);
                break;

            case RequiredGetCurrentTransformation.UninlineAndUseExistingVariable:
                // Unwrap stloc chain.
                var nestedStores = new Stack <ILVariable>();
                var currentInst  = instToReplace;                        // instToReplace is the innermost value of the stloc chain.
                while (currentInst.Parent is StLoc stloc)
                {
                    // Exclude nested stores to foreachVariable
                    // we'll insert one store at the beginning of the block.
                    if (stloc.Variable != foreachVariable && stloc.Parent is StLoc)
                    {
                        nestedStores.Push(stloc.Variable);
                    }
                    currentInst = stloc;
                }
                // Rebuild the nested store instructions:
                ILInstruction reorderedStores = new LdLoc(foreachVariable);
                while (nestedStores.Count > 0)
                {
                    reorderedStores = new StLoc(nestedStores.Pop(), reorderedStores);
                }
                currentInst.ReplaceWith(reorderedStores);
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                // Adjust variable type, kind and name.
                goto case RequiredGetCurrentTransformation.UseExistingVariable;

            case RequiredGetCurrentTransformation.IntroduceNewVariable:
                foreachVariable = currentFunction.RegisterVariable(
                    VariableKind.ForeachLocal, type,
                    AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>())
                    );
                instToReplace.ReplaceWith(new LdLoc(foreachVariable));
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                break;
            }
            // Convert the modified body to C# AST:
            var            whileLoop   = (WhileStatement)ConvertAsBlock(container).First();
            BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();

            // Remove the first statement, as it is the foreachVariable = enumerator.Current; statement.
            foreachBody.Statements.First().Detach();
            // Construct the foreach loop.
            var foreachStmt = new ForeachStatement {
                VariableType      = settings.AnonymousTypes && foreachVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type),
                VariableName      = foreachVariable.Name,
                InExpression      = collectionExpr.Detach(),
                EmbeddedStatement = foreachBody
            };

            // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
            foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
            // If there was an optional return statement, return it as well.
            if (optionalReturnAfterLoop != null)
            {
                return(new BlockStatement {
                    Statements =
                    {
                        foreachStmt,
                        optionalReturnAfterLoop.AcceptVisitor(this)
                    }
                });
            }
            return(foreachStmt);
        }
Ejemplo n.º 7
0
        bool TransformDeconstruction(Block block, int pos)
        {
            int startPos = pos;
            Action <DeconstructInstruction> delayedActions = null;

            if (MatchDeconstruction(block.Instructions[pos], out IMethod deconstructMethod,
                                    out ILInstruction rootTestedOperand))
            {
                pos++;
            }
            if (!MatchConversions(block, ref pos, out var conversions, out var conversionStLocs, ref delayedActions))
            {
                return(false);
            }

            if (!MatchAssignments(block, ref pos, conversions, conversionStLocs, ref delayedActions))
            {
                return(false);
            }
            // first tuple element may not be discarded,
            // otherwise we would run this transform on a suffix of the actual pattern.
            if (deconstructionResults[0] == null)
            {
                return(false);
            }
            context.Step("Deconstruction", block.Instructions[startPos]);
            DeconstructInstruction replacement = new DeconstructInstruction();
            IType deconstructedType;

            if (deconstructMethod == null)
            {
                deconstructedType = this.tupleType;
                rootTestedOperand = new LdLoc(this.tupleVariable);
            }
            else
            {
                if (deconstructMethod.IsStatic)
                {
                    deconstructedType = deconstructMethod.Parameters[0].Type;
                }
                else
                {
                    deconstructedType = deconstructMethod.DeclaringType;
                }
            }
            var rootTempVariable = context.Function.RegisterVariable(VariableKind.PatternLocal, deconstructedType);

            replacement.Pattern = new MatchInstruction(rootTempVariable, deconstructMethod, rootTestedOperand)
            {
                IsDeconstructCall  = deconstructMethod != null,
                IsDeconstructTuple = this.tupleType != null
            };
            int index = 0;

            foreach (ILVariable v in deconstructionResults)
            {
                var result = v;
                if (result == null)
                {
                    var freshVar = new ILVariable(VariableKind.PatternLocal, this.tupleType.ElementTypes[index])
                    {
                        Name = "E_" + index
                    };
                    context.Function.Variables.Add(freshVar);
                    result = freshVar;
                }
                else
                {
                    result.Kind = VariableKind.PatternLocal;
                }
                replacement.Pattern.SubPatterns.Add(
                    new MatchInstruction(
                        result,
                        new DeconstructResultInstruction(index, result.StackType, new LdLoc(rootTempVariable))
                        )
                    );
                index++;
            }
            replacement.Conversions = new Block(BlockKind.DeconstructionConversions);
            foreach (var convInst in conversionStLocs)
            {
                replacement.Conversions.Instructions.Add(convInst);
            }
            replacement.Assignments = new Block(BlockKind.DeconstructionAssignments);
            delayedActions?.Invoke(replacement);
            block.Instructions[startPos] = replacement;
            block.Instructions.RemoveRange(startPos + 1, pos - startPos - 1);
            return(true);
        }
Ejemplo n.º 8
0
        static ILInstruction GetRef(ILVariable v, string Name)
        {
            var ty = TypeInference.GetVariableType(v);

            if (ty == null)
            {
                // happens on ldNull;
                return(null);
            }
            var isPointer = false;

            if (ty.Kind == TypeKind.Pointer)
            {
                ty        = ((ICSharpCode.Decompiler.TypeSystem.PointerType)ty).ElementType;
                isPointer = true;
            }

            IType         etype;
            ILInstruction loadInst;

            if (ty.IsReferenceType.Value)
            {
                etype = GetETypeBase(ty);
                if (isPointer)
                {
                    loadInst = new LdObj(new LdLoc(v), ty);
                }
                else
                {
                    loadInst = new LdLoc(v);
                }



                if (etype == null && !ty.FullName.StartsWith("System."))
                {
                    Debug.Assert(false, "Type is broken??");
                }

                // only do ref counting for our own types
                if (etype == null)
                {
                    return(null);
                }

                // avoid compiler warnings
                loadInst = new CastClass(loadInst, etype);
            }
            else                 // value type
            {
                etype = ty;

                if (v.StackType == StackType.O)
                {
                    loadInst = new Conv(new LdLoca(v), PrimitiveType.I, false, Sign.None);
                }
                else
                {
                    loadInst = new Conv(new LdLoc(v), PrimitiveType.I, false, Sign.None);
                }
            }


            // make sure we don't end up with crazy unintended names
            //v.HasGeneratedName = false;

            //var test = (DefaultResolvedTypeDefinition)ty;

            var m    = etype.GetMethods().First(x => x.Name.EndsWith(Name + "Ref"));
            var inst = new Call(m);

            inst.Arguments.Add(loadInst);

            return(inst);
        }
 static bool IsUsedAsNativeInt(LdLoc load)
 {
     return(load.Parent switch {
         BinaryNumericInstruction {
             UnderlyingResultType : StackType.I
         } => true,
Ejemplo n.º 10
0
        /// <summary>
        /// Performs nullable lifting.
        ///
        /// Produces a lifted instruction with semantics equivalent to:
        ///   (v1 != null && ... && vn != null) ? trueInst : falseInst,
        /// where the v1,...,vn are the <c>this.nullableVars</c>.
        /// If lifting fails, returns <c>null</c>.
        /// </summary>
        ILInstruction LiftNormal(ILInstruction trueInst, ILInstruction falseInst, Interval ilrange)
        {
            bool isNullCoalescingWithNonNullableFallback = false;

            if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift))
            {
                isNullCoalescingWithNonNullableFallback = true;
                utype      = context.TypeSystem.Compilation.FindType(trueInst.ResultType.ToKnownTypeCode());
                exprToLift = trueInst;
                if (nullableVars.Count == 1 && exprToLift.MatchLdLoc(nullableVars[0]))
                {
                    // v.HasValue ? ldloc v : fallback
                    // => v ?? fallback
                    context.Step("v.HasValue ? v : fallback => v ?? fallback", trueInst);
                    return(new NullCoalescingInstruction(NullCoalescingKind.Nullable, trueInst, falseInst)
                    {
                        UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType(),
                        ILRange = ilrange
                    });
                }
            }
            ILInstruction lifted;

            if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0]))
            {
                // v.HasValue ? call GetValueOrDefault(ldloca v) : fallback
                // => conv.nop.lifted(ldloc v) ?? fallback
                // This case is handled separately from DoLift() because
                // that doesn't introduce nop-conversions.
                context.Step("v.HasValue ? v.GetValueOrDefault() : fallback => v ?? fallback", trueInst);
                var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type);
                lifted = new LdLoc(nullableVars[0]);
                if (!inputUType.Equals(utype) && utype.ToPrimitiveType() != PrimitiveType.None)
                {
                    // While the ILAst allows implicit conversions between short and int
                    // (because both map to I4); it does not allow implicit conversions
                    // between short? and int? (structs of different types).
                    // So use 'conv.nop.lifted' to allow the conversion.
                    lifted = new Conv(
                        lifted,
                        inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(),
                        checkForOverflow: false,
                        isLifted: true
                        )
                    {
                        ILRange = ilrange
                    };
                }
            }
            else
            {
                context.Step("NullableLiftingTransform.DoLift", trueInst);
                BitSet bits;
                (lifted, bits) = DoLift(exprToLift);
                if (lifted == null)
                {
                    return(null);
                }
                if (!bits.All(0, nullableVars.Count))
                {
                    // don't lift if a nullableVar doesn't contribute to the result
                    return(null);
                }
                Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted &&
                             liftable.UnderlyingResultType == exprToLift.ResultType);
            }
            if (isNullCoalescingWithNonNullableFallback)
            {
                lifted = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            else if (!MatchNull(falseInst, utype))
            {
                // Normal lifting, but the falseInst isn't `default(utype?)`
                // => use the `??` operator to provide the fallback value.
                lifted = new NullCoalescingInstruction(NullCoalescingKind.Nullable, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            return(lifted);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Performs nullable lifting.
        ///
        /// Produces a lifted instruction with semantics equivalent to:
        ///   (v1 != null && ... && vn != null) ? trueInst : falseInst,
        /// where the v1,...,vn are the <c>this.nullableVars</c>.
        /// If lifting fails, returns <c>null</c>.
        /// </summary>
        ILInstruction LiftNormal(ILInstruction trueInst, ILInstruction falseInst, Interval ilrange)
        {
            bool isNullCoalescingWithNonNullableFallback = false;

            if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift))
            {
                isNullCoalescingWithNonNullableFallback = true;
                utype      = context.TypeSystem.Compilation.FindType(trueInst.ResultType.ToKnownTypeCode());
                exprToLift = trueInst;
                if (nullableVars.Count == 1 && exprToLift.MatchLdLoc(nullableVars[0]))
                {
                    // v.HasValue ? ldloc v : fallback
                    // => v ?? fallback
                    context.Step("v.HasValue ? v : fallback => v ?? fallback", trueInst);
                    return(new NullCoalescingInstruction(NullCoalescingKind.Nullable, trueInst, falseInst)
                    {
                        UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType(),
                        ILRange = ilrange
                    });
                }
                else if (trueInst is Call call && !call.IsLifted &&
                         CSharp.Resolver.CSharpOperators.IsComparisonOperator(call.Method) &&
                         call.Method.Name != "op_Equality" && call.Method.Name != "op_Inequality" &&
                         falseInst.MatchLdcI4(0))
                {
                    // (v1 != null && ... && vn != null) ? call op_LessThan(lhs, rhs) : ldc.i4(0)
                    var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method);
                    if (liftedOperator != null)
                    {
                        var(left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1]);
                        if (left != null && right != null && bits.All(0, nullableVars.Count))
                        {
                            return(new Call(liftedOperator)
                            {
                                Arguments = { left, right },
                                ConstrainedTo = call.ConstrainedTo,
                                ILRange = call.ILRange,
                                ILStackWasEmpty = call.ILStackWasEmpty,
                                IsTail = call.IsTail
                            });
                        }
                    }
                }
            }
            ILInstruction lifted;

            if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0]))
            {
                // v.HasValue ? call GetValueOrDefault(ldloca v) : fallback
                // => conv.nop.lifted(ldloc v) ?? fallback
                // This case is handled separately from DoLift() because
                // that doesn't introduce nop-conversions.
                context.Step("v.HasValue ? v.GetValueOrDefault() : fallback => v ?? fallback", trueInst);
                var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type);
                lifted = new LdLoc(nullableVars[0]);
                if (!inputUType.Equals(utype) && utype.ToPrimitiveType() != PrimitiveType.None)
                {
                    // While the ILAst allows implicit conversions between short and int
                    // (because both map to I4); it does not allow implicit conversions
                    // between short? and int? (structs of different types).
                    // So use 'conv.nop.lifted' to allow the conversion.
                    lifted = new Conv(
                        lifted,
                        inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(),
                        checkForOverflow: false,
                        isLifted: true
                        )
                    {
                        ILRange = ilrange
                    };
                }
            }
            else
            {
                context.Step("NullableLiftingTransform.DoLift", trueInst);
                BitSet bits;
                (lifted, bits) = DoLift(exprToLift);
                if (lifted == null)
                {
                    return(null);
                }
                if (!bits.All(0, nullableVars.Count))
                {
                    // don't lift if a nullableVar doesn't contribute to the result
                    return(null);
                }
                Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted &&
                             liftable.UnderlyingResultType == exprToLift.ResultType);
            }
            if (isNullCoalescingWithNonNullableFallback)
            {
                lifted = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            else if (!MatchNull(falseInst, utype))
            {
                // Normal lifting, but the falseInst isn't `default(utype?)`
                // => use the `??` operator to provide the fallback value.
                lifted = new NullCoalescingInstruction(NullCoalescingKind.Nullable, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            return(lifted);
        }
Ejemplo n.º 12
0
 protected internal override void VisitLdLoc(LdLoc inst)
 {
     base.VisitLdLoc(inst);
     HandleLoad(inst);
 }