Browse Source

Add ILInlining.IsUsedAsThisPointerInCall().

pull/877/head
Daniel Grunwald 8 years ago
parent
commit
6988260ea3
  1. 1
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs
  2. 23
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs
  3. 47
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  4. 2
      ICSharpCode.Decompiler/TypeSystem/NullableType.cs

1
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs

@ -45,6 +45,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -45,6 +45,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
NoUsingDueToByRefCall();
NoUsingDueToContinuedDisposableUse();
ContinuedObjectUse();
VariableAlreadyUsedBefore();
UsingObject();
}

23
ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs

@ -94,9 +94,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -94,9 +94,17 @@ 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;
@ -111,11 +119,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -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 @@ -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()) {

47
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -164,7 +164,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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);
@ -192,31 +192,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -192,31 +192,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="pos">Index of the load within 'parent'</param>
/// <param name="loadInst">The load instruction (a descendant within 'next')</param>
/// <param name="v">The variable being inlined.</param>
static bool IsGeneratedValueTypeTemporary(ILInstruction next, ILInstruction parent, int pos, ILVariable v, ILInstruction inlinedExpression)
static bool IsGeneratedValueTypeTemporary(ILInstruction next, LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression)
{
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)) {
return false;
}
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);
}
// 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;
}
internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca)
{
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;
}
return false;
}
/// <summary>

2
ICSharpCode.Decompiler/TypeSystem/NullableType.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -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;

Loading…
Cancel
Save