From 6988260ea3522f35bb97a0abcb67eeda45d672de Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 25 Sep 2017 00:29:21 +0200 Subject: [PATCH] Add ILInlining.IsUsedAsThisPointerInCall(). --- .../TestCases/Correctness/Using.cs | 1 + .../TestCases/Correctness/ValueTypeCall.cs | 25 +++++++++- .../IL/Transforms/ILInlining.cs | 49 ++++++++++--------- .../TypeSystem/NullableType.cs | 2 +- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs index bbc286dd6..1878456bb 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs @@ -45,6 +45,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness NoUsingDueToByRefCall(); NoUsingDueToContinuedDisposableUse(); ContinuedObjectUse(); + VariableAlreadyUsedBefore(); UsingObject(); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs index 48ac5b628..72dbb9101 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs @@ -94,11 +94,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness static void Box() { + Console.WriteLine("Box"); object o = new MutValueType { val = 300 }; ((MutValueType)o).Increment(); ((MutValueType)o).Increment(); + MutValueType unboxed1 = (MutValueType)o; + unboxed1.Increment(); + unboxed1.Increment(); + ((MutValueType)o).Increment(); + MutValueType unboxed2 = (MutValueType)o; + unboxed2.val = 100; + ((MutValueType)o).Dispose(); } - + MutValueType instanceField; ValueTypeWithReadOnlyMember mutableInstanceFieldWithReadOnlyMember; @@ -111,11 +119,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness } static void Using() + { + Using1(); + Using2(); + Using3(); + } + + static void Using1() { Console.WriteLine("Using:"); using (var x = new MutValueType()) { x.Increment(); } + } + + static void Using2() + { Console.WriteLine("Not using:"); var y = new MutValueType(); try { @@ -124,6 +143,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness MutValueType x = y; x.Dispose(); } + } + + static void Using3() + { Console.WriteLine("Using with variable declared outside:"); MutValueType z; using (z = new MutValueType()) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 40278bb46..2a5cf6236 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -164,7 +164,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInstruction loadInst; if (FindLoadInNext(next, v, inlinedExpression, out loadInst) == true) { if (loadInst.OpCode == OpCode.LdLoca) { - if (!IsGeneratedValueTypeTemporary(next, loadInst.Parent, loadInst.ChildIndex, v, inlinedExpression)) + if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression)) return false; } else { Debug.Assert(loadInst.OpCode == OpCode.LdLoc); @@ -187,36 +187,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return false; } - + /// /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// /// The next top-level expression - /// The direct parent of the load within 'next' - /// Index of the load within 'parent' + /// The load instruction (a descendant within 'next') /// The variable being inlined. - static bool IsGeneratedValueTypeTemporary(ILInstruction next, ILInstruction parent, int pos, ILVariable v, ILInstruction inlinedExpression) + static bool IsGeneratedValueTypeTemporary(ILInstruction next, LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression) + { + Debug.Assert(loadInst.Variable == v); + // Inlining a value type variable is allowed only if the resulting code will maintain the semantics + // that the method is operating on a copy. + // Thus, we have to ensure we're operating on an r-value. + // Additionally, we cannot inline in cases where the C# compiler prohibits the direct use + // of the rvalue (e.g. M(ref (MyStruct)obj); is invalid). + return IsUsedAsThisPointerInCall(loadInst) && !IsLValue(inlinedExpression); + } + + internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca) { - if (pos == 0 && v.Type != null && v.Type.IsReferenceType == false) { - // Inlining a value type variable is allowed only if the resulting code will maintain the semantics - // that the method is operating on a copy. - // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers - if (IsLValue(inlinedExpression)) { + if (ldloca.ChildIndex != 0) + return false; + if (ldloca.Variable.Type.IsReferenceType != false) + return false; + switch (ldloca.Parent.OpCode) { + case OpCode.Call: + case OpCode.CallVirt: + return !((CallInstruction)ldloca.Parent).Method.IsStatic; + case OpCode.Await: + return true; + default: return false; - } - - // inline the compiler-generated variable that are used when accessing a member on a value type: - switch (parent.OpCode) { - case OpCode.Call: - return !((Call)parent).Method.IsStatic; - case OpCode.CallVirt: - return !((CallVirt)parent).Method.IsStatic; - case OpCode.LdFlda: - case OpCode.Await: - return true; - } } - return false; } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs index c28488018..505d08f9a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs @@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem if (type == null) throw new ArgumentNullException("type"); ParameterizedType pt = type as ParameterizedType; - if (pt != null && pt.TypeParameterCount == 1 && pt.FullName == "System.Nullable") + if (pt != null && pt.TypeParameterCount == 1 && pt.GetDefinition().KnownTypeCode == KnownTypeCode.NullableOfT) return pt.GetTypeArgument(0); else return type;