|
|
|
@ -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); |
|
|
|
@ -187,36 +187,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -187,36 +187,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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) |
|
|
|
|
{ |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|