Browse Source

Add two more patterns with throw expressions.

pull/1600/head
Siegfried Pammer 6 years ago
parent
commit
50c4fa1c13
  1. 130
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

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

@ -36,7 +36,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public void Run(Block block, int pos, StatementTransformContext context) public void Run(Block block, int pos, StatementTransformContext context)
{ {
if (!TransformRefTypes(block, pos, context)) { if (!TransformRefTypes(block, pos, context)) {
TransformThrowExpressionValueTypes(block, pos, context); if (!TransformThrowExpressionValueTypes(block, pos, context)) {
TransformThrowExpressionOnAddress(block, pos, context);
}
} }
} }
@ -102,14 +104,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
delegate bool PatternMatcher(ILInstruction input, out ILInstruction output);
/// <summary> /// <summary>
/// stloc v(value) /// stloc v(value)
/// if (logic.not(call get_HasValue(ldloca v))) { /// if (logic.not(call get_HasValue(ldloca v))) throw(...)
/// throw(...)
/// }
/// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ... /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ...
/// => /// =>
/// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ...
///
/// -or-
///
/// stloc v(value)
/// stloc s(ldloca v)
/// if (logic.not(call get_HasValue(ldloc s))) throw(...)
/// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ...
/// =>
/// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ...
/// </summary> /// </summary>
bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context) bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context)
{ {
@ -117,30 +128,114 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!(block.Instructions[pos] is StLoc stloc)) if (!(block.Instructions[pos] is StLoc stloc))
return false; return false;
if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) int offset = 1;
ILVariable v = stloc.Variable;
// alternative pattern using stack slot containing address of v
if (block.Instructions[pos + offset] is StLoc addrCopyStore
&& addrCopyStore.Variable.Kind == VariableKind.StackSlot
&& addrCopyStore.Value.MatchLdLoca(v)
&& pos + 3 < block.Instructions.Count)
{
offset++;
// v turns into s in the pattern above.
v = addrCopyStore.Variable;
if (!(v.StoreCount == 1 && v.LoadCount == 2 && v.AddressCount == 0))
return false;
} else {
if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2))
return false;
}
if (!block.Instructions[pos + offset].MatchIfInstruction(out var condition, out var trueInst))
return false; return false;
if (!(Block.Unwrap(trueInst) is Throw throwInst)) if (!(Block.Unwrap(trueInst) is Throw throwInst))
return false; return false;
ILVariable v = stloc.Variable; if (!condition.MatchLogicNot(out var arg))
if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2))
return false; return false;
if (!NullableLiftingTransform.MatchNegatedHasValueCall(condition, v)) if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall))
return false; return false;
var throwInstParent = throwInst.Parent; var throwInstParent = throwInst.Parent;
var throwInstChildIndex = throwInst.ChildIndex; var throwInstChildIndex = throwInst.ChildIndex;
var nullcoalescingWithThrow = new NullCoalescingInstruction( var nullCoalescingWithThrow = new NullCoalescingInstruction(
NullCoalescingKind.NullableWithValueFallback, NullCoalescingKind.NullableWithValueFallback,
stloc.Value, stloc.Value,
throwInst); throwInst);
var resultType = NullableType.GetUnderlyingType(v.Type).GetStackType(); var resultType = NullableType.GetUnderlyingType(v.Type).GetStackType();
nullcoalescingWithThrow.UnderlyingResultType = resultType; nullCoalescingWithThrow.UnderlyingResultType = resultType;
var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullcoalescingWithThrow, InliningOptions.None); var result = ILInlining.FindLoadInNext(block.Instructions[pos + offset + 1], v, nullCoalescingWithThrow, InliningOptions.None);
if (result.Type == ILInlining.FindResultType.Found if (result.Type == ILInlining.FindResultType.Found
&& NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v)) && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault))
{ {
context.Step("NullCoalescingTransform (value types + throw expression)", stloc); context.Step("NullCoalescingTransform (value types + throw expression)", stloc);
throwInst.resultType = resultType; throwInst.resultType = resultType;
result.LoadInst.Parent.ReplaceWith(nullcoalescingWithThrow); result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow);
block.Instructions.RemoveRange(pos, offset + 1); // remove store(s) 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;
}
bool MatchNullableCall(ILInstruction input, PatternMatcher matcher)
{
if (!matcher(input, out var loadInst))
return false;
if (offset == 1) { // Pattern 1
if (!loadInst.MatchLdLoca(v))
return false;
} else {
if (!loadInst.MatchLdLoc(v))
return false;
}
return true;
}
}
/// <summary>
/// stloc s(addressOfValue)
/// if (logic.not(call get_HasValue(ldloc s))) throw(...)
/// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ...
/// =>
/// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ...
/// </summary>
bool TransformThrowExpressionOnAddress(Block block, int pos, StatementTransformContext context)
{
if (pos + 2 >= block.Instructions.Count)
return false;
if (!(block.Instructions[pos] is StLoc stloc))
return false;
var s = stloc.Variable;
if (s.Kind != VariableKind.StackSlot)
return false;
if (!(s.StoreCount == 1 && s.LoadCount == 2 && s.AddressCount == 0))
return false;
if (!(s.Type is ByReferenceType byRef && byRef.ElementType.IsReferenceType == false))
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;
if (!condition.MatchLogicNot(out var arg))
return false;
if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall))
return false;
var throwInstParent = throwInst.Parent;
var throwInstChildIndex = throwInst.ChildIndex;
var ldobj = new LdObj(stloc.Value, byRef.ElementType);
var nullCoalescingWithThrow = new NullCoalescingInstruction(
NullCoalescingKind.NullableWithValueFallback,
ldobj,
throwInst);
var resultType = NullableType.GetUnderlyingType(byRef.ElementType).GetStackType();
nullCoalescingWithThrow.UnderlyingResultType = resultType;
var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], s, nullCoalescingWithThrow, InliningOptions.None);
if (result.Type == ILInlining.FindResultType.Found
&& MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) {
context.Step("NullCoalescingTransform (address + throw expression)", stloc);
throwInst.resultType = resultType;
result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow);
block.Instructions.RemoveRange(pos, 2); // remove store and if instruction block.Instructions.RemoveRange(pos, 2); // remove store and if instruction
return true; return true;
} else { } else {
@ -150,6 +245,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
children[throwInstChildIndex] = throwInst; children[throwInstChildIndex] = throwInst;
return false; return false;
} }
bool MatchNullableCall(ILInstruction input, PatternMatcher matcher)
{
if (!matcher(input, out var loadInst))
return false;
if (!loadInst.MatchLdLoc(s))
return false;
return true;
}
} }
} }
} }

Loading…
Cancel
Save