Browse Source

Implement ref local assignments

pull/2119/head
Siegfried Pammer 5 years ago
parent
commit
a549b03bea
  1. 25
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs
  2. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs
  3. 23
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 19
      ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs
  5. 15
      ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs

25
ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs

@ -132,6 +132,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -132,6 +132,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Property_IntToUIntConversion();
NoDeconstruction_NotUsingConver();
NoDeconstruction_NotUsingConver_Tuple();
NullReferenceException_Field_Deconstruction(out _);
NullReferenceException_RefLocalReferencesField_Deconstruction(out _);
}
public void Property_NoDeconstruction_SwappedAssignments()
@ -186,5 +188,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -186,5 +188,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
t0.UIntProperty = t.Item2;
Console.WriteLine(c);
}
public void NullReferenceException_Field_Deconstruction(out int a)
{
try {
AssignmentTargets t0 = null;
(t0.IntField, a) = GetSource<int, int>();
} catch (Exception ex) {
a = 0;
Console.WriteLine(ex.GetType().FullName);
}
}
public void NullReferenceException_RefLocalReferencesField_Deconstruction(out int a)
{
try {
AssignmentTargets t0 = null;
ref int i = ref t0.IntField;
(i, a) = GetSource<int, int>();
} catch (Exception ex) {
a = 0;
Console.WriteLine(ex.GetType().FullName);
}
}
}
}

10
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs

@ -117,6 +117,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -117,6 +117,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return null;
}
private ref T GetRef<T>()
{
throw new NotImplementedException();
}
private (T, T2) GetTuple<T, T2>()
{
return default((T, T2));
@ -221,5 +226,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -221,5 +226,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
(Get(0).NMy, Get(1).My, _) = GetTuple<MyInt?, MyInt, int>();
}
public void RefLocal_FloatToDoubleConversion(out double a)
{
(a, GetRef<double>()) = GetSource<double, float>();
}
}
}

23
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -3435,22 +3435,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3435,22 +3435,31 @@ namespace ICSharpCode.Decompiler.CSharp
break;
case CallInstruction call:
for (int i = 0; i < call.Arguments.Count - 1; i++) {
if (call.Arguments[i].MatchLdLoc(out var v)
&& v.Kind == VariableKind.DeconstructionInitTemporary)
{
Debug.Assert(inits[initPos].Variable == v);
call.Arguments[i] = inits[initPos].Value;
initPos++;
}
ReplaceAssignmentTarget(call.Arguments[i]);
}
Debug.Assert(call.Arguments.Last().MatchLdLoc(value));
break;
case StObj stobj:
ReplaceAssignmentTarget(stobj.Target);
Debug.Assert(stobj.Value.MatchLdLoc(value));
break;
default:
throw new NotSupportedException();
}
var expr = Translate(assignment);
return expr.UnwrapChild(((AssignmentExpression)expr).Left);
}
void ReplaceAssignmentTarget(ILInstruction target)
{
if (target.MatchLdLoc(out var v)
&& v.Kind == VariableKind.DeconstructionInitTemporary)
{
Debug.Assert(inits[initPos].Variable == v);
target.ReplaceWith(inits[initPos].Value);
initPos++;
}
}
}
protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context)

19
ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs

@ -204,7 +204,7 @@ namespace ICSharpCode.Decompiler.IL @@ -204,7 +204,7 @@ namespace ICSharpCode.Decompiler.IL
return input.MatchLdLoc(out inputVariable) || input.MatchLdLoca(out inputVariable);
}
internal static bool IsAssignment(ILInstruction inst, out IType expectedType, out ILInstruction value)
internal static bool IsAssignment(ILInstruction inst, ICompilation typeSystem, out IType expectedType, out ILInstruction value)
{
expectedType = null;
value = null;
@ -229,8 +229,19 @@ namespace ICSharpCode.Decompiler.IL @@ -229,8 +229,19 @@ namespace ICSharpCode.Decompiler.IL
value = stloc.Value;
return true;
case StObj stobj:
// TODO
return false;
var target = stobj.Target;
if (target.Flags == InstructionFlags.None) {
// OK - we accept integer literals, etc.
} else if (target.MatchLdLoc(out var v)) {
} else {
return false;
}
if (target.InferType(typeSystem) is ByReferenceType brt)
expectedType = brt.ElementType;
else
expectedType = SpecialType.UnknownType;
value = stobj.Value;
return true;
default:
return false;
}
@ -260,7 +271,7 @@ namespace ICSharpCode.Decompiler.IL @@ -260,7 +271,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(this.conversions.FinalInstruction is Nop);
foreach (var inst in assignments.Instructions) {
if (!(IsAssignment(inst, out _, out var value) && value.MatchLdLoc(out var inputVariable)))
if (!(IsAssignment(inst, typeSystem: null, out _, out var value) && value.MatchLdLoc(out var inputVariable)))
throw new InvalidOperationException("inst is not an assignment!");
Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable));
}

15
ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs

@ -140,10 +140,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -140,10 +140,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
/// <summary>
/// stloc v(lhs)
/// stloc v(value)
/// expr(..., deconstruct { ... }, ...)
/// =>
/// expr(..., deconstruct { init: stloc v(lhs) ... }, ...)
/// expr(..., deconstruct { init: stloc v(value) ... }, ...)
/// </summary>
bool InlineDeconstructionInitializer(Block block, int pos)
{
@ -157,8 +157,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -157,8 +157,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (result.Type != ILInlining.FindResultType.Deconstruction)
return false;
var deconstruction = (DeconstructInstruction)result.LoadInst;
if (!v.LoadInstructions[0].IsDescendantOf(deconstruction.Assignments))
LdLoc loadInst = v.LoadInstructions[0];
if (!loadInst.IsDescendantOf(deconstruction.Assignments))
return false;
if (loadInst.SlotInfo == StObj.TargetSlot) {
if (value.OpCode == OpCode.LdFlda || value.OpCode == OpCode.LdElema)
return false;
}
if (deconstruction.Init.Count > 0) {
var a = deconstruction.Init[0].Variable.LoadInstructions.Single();
var b = v.LoadInstructions.Single();
@ -378,7 +383,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -378,7 +383,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (inst.MatchStLoc(out var v, out var value)
&& value is Block block && block.MatchInlineAssignBlock(out var call, out valueInst)) {
if (!DeconstructInstruction.IsAssignment(call, out targetType, out _))
if (!DeconstructInstruction.IsAssignment(call, context.TypeSystem, out targetType, out _))
return false;
if (!(v.IsSingleDefinition && v.LoadCount == 0))
return false;
@ -388,7 +393,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -388,7 +393,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
deconstructInst.Assignments.Instructions.Add(call);
};
return true;
} else if (DeconstructInstruction.IsAssignment(inst, out targetType, out valueInst)) {
} else if (DeconstructInstruction.IsAssignment(inst, context.TypeSystem, out targetType, out valueInst)) {
// OK - use the assignment as is
addAssignment = (DeconstructInstruction deconstructInst) => {
deconstructInst.Assignments.Instructions.Add(inst);

Loading…
Cancel
Save