Browse Source

Implement NullCoalescingTransform with value types.

pull/1600/head
Siegfried Pammer 6 years ago
parent
commit
7d4b4c6433
  1. 2
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 2
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  3. 57
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs
  4. 2
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

2
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp
}; };
var cexpr = inst.AcceptVisitor(this, context); var cexpr = inst.AcceptVisitor(this, context);
#if DEBUG #if DEBUG
if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown) { if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown && cexpr.Type.Kind != TypeKind.None) {
// Validate the Translate post-condition (documented at beginning of this file): // Validate the Translate post-condition (documented at beginning of this file):
if (inst.ResultType.IsIntegerType()) { if (inst.ResultType.IsIntegerType()) {
Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type");

2
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.IL
protected abstract SlotInfo GetChildSlot(int index); protected abstract SlotInfo GetChildSlot(int index);
#region ChildrenCollection + ChildrenEnumerator #region ChildrenCollection + ChildrenEnumerator
public struct ChildrenCollection : IReadOnlyList<ILInstruction> public readonly struct ChildrenCollection : IReadOnlyList<ILInstruction>
{ {
readonly ILInstruction inst; readonly ILInstruction inst;

57
ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
@ -34,7 +35,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
public void Run(Block block, int pos, StatementTransformContext context) public void Run(Block block, int pos, StatementTransformContext context)
{ {
TransformRefTypes(block, pos, context); if (!TransformRefTypes(block, pos, context)) {
TransformThrowExpressionValueTypes(block, pos, context);
}
} }
/// <summary> /// <summary>
@ -89,7 +92,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// => // =>
// stloc obj(if.notnull(valueInst, throw(...))) // stloc obj(if.notnull(valueInst, throw(...)))
if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) { if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) {
context.Step("NullCoalescingTransform (throw expression)", stloc); context.Step("NullCoalescingTransform (reference types + throw expression)", stloc);
throwInst.resultType = StackType.O; throwInst.resultType = StackType.O;
stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst);
block.Instructions.RemoveAt(pos + 1); // remove if instruction block.Instructions.RemoveAt(pos + 1); // remove if instruction
@ -98,5 +101,55 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
return false; return false;
} }
/// <summary>
/// stloc v(value)
/// if (logic.not(call get_HasValue(ldloca v))) {
/// throw(...)
/// }
/// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ...
/// =>
/// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ...
/// </summary>
bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context)
{
if (pos + 2 >= block.Instructions.Count)
return false;
if (!(block.Instructions[pos] is StLoc stloc))
return false;
if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst))
return false;
if (!(Block.Unwrap(trueInst) is Throw throwInst))
return false;
ILVariable v = stloc.Variable;
if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2))
return false;
if (!NullableLiftingTransform.MatchNegatedHasValueCall(condition, v))
return false;
var throwInstParent = throwInst.Parent;
var throwInstChildIndex = throwInst.ChildIndex;
var nullcoalescingWithThrow = new NullCoalescingInstruction(
NullCoalescingKind.NullableWithValueFallback,
stloc.Value,
throwInst);
var resultType = NullableType.GetUnderlyingType(v.Type).GetStackType();
nullcoalescingWithThrow.UnderlyingResultType = resultType;
var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullcoalescingWithThrow, InliningOptions.None);
if (result.Type == ILInlining.FindResultType.Found
&& NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v))
{
context.Step("NullCoalescingTransform (value types + throw expression)", stloc);
throwInst.resultType = resultType;
result.LoadInst.Parent.ReplaceWith(nullcoalescingWithThrow);
block.Instructions.RemoveRange(pos, 2); // remove store and if instruction
return true;
} else {
// reset the primary position (see remarks on ILInstruction.Parent)
stloc.Value = stloc.Value;
var children = throwInstParent.Children;
children[throwInstChildIndex] = throwInst;
return false;
}
}
} }
} }

2
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -833,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Matches 'logic.not(call get_HasValue(ldloca v))' /// Matches 'logic.not(call get_HasValue(ldloca v))'
/// </summary> /// </summary>
static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v) internal static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v)
{ {
return inst.MatchLogicNot(out var arg) && MatchHasValueCall(arg, v); return inst.MatchLogicNot(out var arg) && MatchHasValueCall(arg, v);
} }

Loading…
Cancel
Save