Browse Source

Add NullCoalescingInstruction

pull/734/merge
Siegfried Pammer 8 years ago
parent
commit
5007c660ca
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 21
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 104
      ICSharpCode.Decompiler/IL/Instructions.cs
  5. 6
      ICSharpCode.Decompiler/IL/Instructions.tt
  6. 69
      ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs
  7. 64
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -106,6 +106,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -106,6 +106,7 @@ namespace ICSharpCode.Decompiler.CSharp
// Pretty much all transforms that open up new expression inlining
// opportunities belong in this category.
new ExpressionTransforms(),
new NullCoalescingTransform(),
new TransformArrayInitializers(),
new TransformCollectionAndObjectInitializers(),
new ILInlining()

21
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1593,6 +1593,27 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1593,6 +1593,27 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(resolver.WithCheckForOverflow(inst.CheckForOverflow).ResolveUnaryOperator(op, target.ResolveResult));
}
protected internal override TranslatedExpression VisitNullCoalescingInstruction(NullCoalescingInstruction inst, TranslationContext context)
{
var value = Translate(inst.ValueInst);
var fallback = Translate(inst.FallbackInst);
var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.NullCoalescing, value.ResolveResult, fallback.ResolveResult);
if (rr.IsError) {
IType targetType;
if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) {
targetType = compilation.FindType(inst.ResultType.ToKnownTypeCode());
} else {
targetType = value.Type.Equals(SpecialType.NullType) ? fallback.Type : value.Type;
}
value = value.ConvertTo(targetType, this);
fallback = fallback.ConvertTo(targetType, this);
rr = new ResolveResult(targetType);
}
return new BinaryOperatorExpression(value, BinaryOperatorType.NullCoalescing, fallback)
.WithILInstruction(inst)
.WithRR(rr);
}
protected internal override TranslatedExpression VisitIfInstruction(IfInstruction inst, TranslationContext context)
{
var condition = TranslateCondition(inst.Condition);

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -283,8 +283,10 @@ @@ -283,8 +283,10 @@
<Compile Include="IL\ControlFlow\StateRangeAnalysis.cs" />
<Compile Include="IL\ControlFlow\SymbolicExecution.cs" />
<Compile Include="IL\Instructions\ILVariableCollection.cs" />
<Compile Include="IL\Instructions\NullCoalescingInstruction.cs" />
<Compile Include="IL\Patterns\AnyNode.cs" />
<Compile Include="IL\ControlFlow\YieldReturnDecompiler.cs" />
<Compile Include="IL\Transforms\NullCoalescingTransform.cs" />
<Compile Include="IL\Transforms\TransformCollectionAndObjectInitializers.cs" />
<Compile Include="Util\UnicodeNewline.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />

104
ICSharpCode.Decompiler/IL/Instructions.cs

@ -59,6 +59,8 @@ namespace ICSharpCode.Decompiler.IL @@ -59,6 +59,8 @@ namespace ICSharpCode.Decompiler.IL
Leave,
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
IfInstruction,
/// <summary>Null coalescing operator expression. <c>if.notnull(valueInst, fallbackInst)</c></summary>
NullCoalescingInstruction,
/// <summary>Switch statement</summary>
SwitchInstruction,
/// <summary>Switch section within a switch statement</summary>
@ -836,7 +838,6 @@ namespace ICSharpCode.Decompiler.IL @@ -836,7 +838,6 @@ namespace ICSharpCode.Decompiler.IL
{
}
public override StackType ResultType { get { return StackType.I4; } }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitLogicNot(this);
@ -1185,6 +1186,94 @@ namespace ICSharpCode.Decompiler.IL @@ -1185,6 +1186,94 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Null coalescing operator expression. <c>if.notnull(valueInst, fallbackInst)</c></summary>
public sealed partial class NullCoalescingInstruction : ILInstruction
{
public static readonly SlotInfo ValueInstSlot = new SlotInfo("ValueInst");
ILInstruction valueInst;
public ILInstruction ValueInst {
get { return this.valueInst; }
set {
ValidateChild(value);
SetChildInstruction(ref this.valueInst, value, 0);
}
}
public static readonly SlotInfo FallbackInstSlot = new SlotInfo("FallbackInst");
ILInstruction fallbackInst;
public ILInstruction FallbackInst {
get { return this.fallbackInst; }
set {
ValidateChild(value);
SetChildInstruction(ref this.fallbackInst, value, 1);
}
}
protected sealed override int GetChildCount()
{
return 2;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.valueInst;
case 1:
return this.fallbackInst;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.ValueInst = value;
break;
case 1:
this.FallbackInst = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ValueInstSlot;
case 1:
return FallbackInstSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (NullCoalescingInstruction)ShallowClone();
clone.ValueInst = this.valueInst.Clone();
clone.FallbackInst = this.fallbackInst.Clone();
return clone;
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitNullCoalescingInstruction(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitNullCoalescingInstruction(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitNullCoalescingInstruction(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as NullCoalescingInstruction;
return o != null && this.valueInst.PerformMatch(o.valueInst, ref match) && this.fallbackInst.PerformMatch(o.fallbackInst, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Switch statement</summary>
public sealed partial class SwitchInstruction : ILInstruction
@ -4020,6 +4109,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4020,6 +4109,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitNullCoalescingInstruction(NullCoalescingInstruction inst)
{
Default(inst);
}
protected internal virtual void VisitSwitchInstruction(SwitchInstruction inst)
{
Default(inst);
@ -4290,6 +4383,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4290,6 +4383,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitNullCoalescingInstruction(NullCoalescingInstruction inst)
{
return Default(inst);
}
protected internal virtual T VisitSwitchInstruction(SwitchInstruction inst)
{
return Default(inst);
@ -4560,6 +4657,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4560,6 +4657,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitNullCoalescingInstruction(NullCoalescingInstruction inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitSwitchInstruction(SwitchInstruction inst, C context)
{
return Default(inst, context);
@ -4780,6 +4881,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4780,6 +4881,7 @@ namespace ICSharpCode.Decompiler.IL
"br",
"leave",
"if",
"if.notnull",
"switch",
"switch.section",
"try.catch",

6
ICSharpCode.Decompiler/IL/Instructions.tt

@ -87,6 +87,12 @@ @@ -87,6 +87,12 @@
new ChildInfo("trueInst"),
new ChildInfo("falseInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("if.notnull", "Null coalescing operator expression. <c>if.notnull(valueInst, fallbackInst)</c>",
CustomClassName("NullCoalescingInstruction"),
CustomChildren(new []{
new ChildInfo("valueInst"),
new ChildInfo("fallbackInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("switch", "Switch statement",
CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"),
MatchCondition("Value.PerformMatch(o.Value, ref match) && DefaultBody.PerformMatch(o.DefaultBody, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")),

69
ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
// Copyright (c) 2017 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Threading;
using ICSharpCode.Decompiler.IL.Transforms;
using Mono.Cecil;
using ICSharpCode.Decompiler.Disassembler;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Null coalescing operator expression. <c>if.notnull(valueInst, fallbackInst)</c></summary>
partial class NullCoalescingInstruction
{
public NullCoalescingInstruction(ILInstruction valueInst, ILInstruction fallbackInst) : base(OpCode.NullCoalescingInstruction)
{
this.ValueInst = valueInst;
this.FallbackInst = fallbackInst;
}
public override StackType ResultType {
get {
return CommonResultType(valueInst.ResultType, fallbackInst.ResultType);
}
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.ControlFlow;
}
}
protected override InstructionFlags ComputeFlags()
{
return InstructionFlags.ControlFlow | SemanticHelper.CombineBranches(valueInst.Flags, fallbackInst.Flags);
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write(" (");
valueInst.WriteTo(output);
output.Write(", ");
fallbackInst.WriteTo(output);
output.Write(")");
}
}
}

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

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
// Copyright (c) 2017 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class NullCoalescingTransform : IBlockTransform
{
BlockTransformContext context;
void IBlockTransform.Run(Block block, BlockTransformContext context)
{
this.context = context;
for (int i = block.Instructions.Count - 1; i >= 0; i--) {
if (TransformNullCoalescing(block, i)) {
block.Instructions.RemoveAt(i);
continue;
}
}
}
/// <summary>
/// stloc s(valueInst)
/// if (comp(ldloc s == ldnull)) {
/// stloc s(fallbackInst)
/// }
/// =>
/// stloc s(if.notnull(valueInst, fallbackInst)
/// </summary>
bool TransformNullCoalescing(Block block, int i)
{
if (i == 0) return false;
if (!(block.Instructions[i] is IfInstruction ifInstruction) || !(block.Instructions[i - 1] is StLoc stloc) || stloc.Variable.Kind != VariableKind.StackSlot)
return false;
if (!ifInstruction.Condition.MatchCompEquals(out var left, out var right) || !left.MatchLdLoc(stloc.Variable) || !right.MatchLdNull())
return false;
if (!ifInstruction.FalseInst.MatchNop() || !(ifInstruction.TrueInst is Block b) || b.Instructions.Count != 1 || !(b.Instructions[0] is StLoc fallbackStore) || fallbackStore.Variable != stloc.Variable)
return false;
context.Step("TransformNullCoalescing", stloc);
stloc.Value.ReplaceWith(new NullCoalescingInstruction(stloc.Value.Clone(), fallbackStore.Value.Clone()));
return true;
}
}
}
Loading…
Cancel
Save