From 95f2ae7aef8bc305211a31bd4650a1592741323c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 4 Jul 2020 10:12:48 +0200 Subject: [PATCH] Add ILAst base for Deconstruction and Pattern Matching --- .../Expressions/DeclarationExpression.cs | 4 +- .../CSharp/Syntax/VariableDesignation.cs | 4 +- .../ICSharpCode.Decompiler.csproj | 5 +- ICSharpCode.Decompiler/IL/Instructions.cs | 256 ++++++++++++++++++ ICSharpCode.Decompiler/IL/Instructions.tt | 19 ++ .../IL/Instructions/DeconstructInstruction.cs | 190 +++++++++++++ .../DeconstructResultInstruction.cs | 44 +++ .../IL/Instructions/ILInstruction.cs | 5 +- .../IL/Instructions/MatchInstruction.cs | 76 ++++++ 9 files changed, 596 insertions(+), 7 deletions(-) create mode 100644 ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs create mode 100644 ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs create mode 100644 ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DeclarationExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DeclarationExpression.cs index ff41e1cf3..c12c82abd 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DeclarationExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DeclarationExpression.cs @@ -31,8 +31,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } public VariableDesignation Designation { - get { return GetChildByRole(VariableDesignation.Role); } - set { SetChildByRole(VariableDesignation.Role, value); } + get { return GetChildByRole(VariableDesignation.VariableDesignationRole); } + set { SetChildByRole(VariableDesignation.VariableDesignationRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/VariableDesignation.cs b/ICSharpCode.Decompiler/CSharp/Syntax/VariableDesignation.cs index e1db55d33..d79ebdf65 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/VariableDesignation.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/VariableDesignation.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { public abstract class VariableDesignation : AstNode { - public static Role Role = new Role("VariableDesignation"); + public static Role VariableDesignationRole = new Role("VariableDesignation"); } /// @@ -80,7 +80,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } public AstNodeCollection VariableDesignations { - get { return GetChildrenByRole(VariableDesignation.Role); } + get { return GetChildrenByRole(VariableDesignation.VariableDesignationRole); } } public CSharpTokenNode RParToken { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index ba1f60d1f..b17de8717 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -70,10 +70,13 @@ - + + + + diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index cd420c1e7..b6b6888c0 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -220,6 +220,8 @@ namespace ICSharpCode.Decompiler.IL DynamicInvokeInstruction, /// ILAst representation of a call to the Binder.IsEvent method inside a dynamic expression. DynamicIsEventInstruction, + /// ILAst representation of C# patterns + MatchInstruction, /// Push a typed reference of type class onto the stack. MakeRefAny, /// Push the type token stored in a typed reference. @@ -230,6 +232,10 @@ namespace ICSharpCode.Decompiler.IL YieldReturn, /// C# await operator. Await, + /// Deconstruction statement + DeconstructInstruction, + /// Represents a deconstructed value + DeconstructResultInstruction, /// Matches any node AnyNode, } @@ -6084,6 +6090,147 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL +{ + /// ILAst representation of C# patterns + public sealed partial class MatchInstruction : ILInstruction, IStoreInstruction, IInstructionWithMethodOperand + { + public MatchInstruction(ILVariable variable, IMethod method, ILInstruction testedOperand, params ILInstruction[] subPatterns) : base(OpCode.MatchInstruction) + { + Debug.Assert(variable != null); + this.variable = variable; + this.method = method; + this.TestedOperand = testedOperand; + this.SubPatterns = new InstructionCollection(this, 1); + this.SubPatterns.AddRange(subPatterns); + } + ILVariable variable; + public ILVariable Variable { + get { return variable; } + set { + Debug.Assert(value != null); + if (IsConnected) + variable.RemoveStoreInstruction(this); + variable = value; + if (IsConnected) + variable.AddStoreInstruction(this); + } + } + + public int IndexInStoreInstructionList { get; set; } = -1; + + int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { + get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } + set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } + } + + protected override void Connected() + { + base.Connected(); + variable.AddStoreInstruction(this); + } + + protected override void Disconnected() + { + variable.RemoveStoreInstruction(this); + base.Disconnected(); + } + + readonly IMethod method; + /// Returns the method operand. + public IMethod Method { get { return method; } } + public bool Deconstruct; + public bool MatchType; + public bool MatchesNull; + public static readonly SlotInfo TestedOperandSlot = new SlotInfo("TestedOperand", canInlineInto: true); + ILInstruction testedOperand; + public ILInstruction TestedOperand { + get { return this.testedOperand; } + set { + ValidateChild(value); + SetChildInstruction(ref this.testedOperand, value, 0); + } + } + public static readonly SlotInfo SubPatternsSlot = new SlotInfo("SubPatterns"); + public InstructionCollection SubPatterns { get; private set; } + protected sealed override int GetChildCount() + { + return 1 + SubPatterns.Count; + } + protected sealed override ILInstruction GetChild(int index) + { + switch (index) { + case 0: + return this.testedOperand; + default: + return this.SubPatterns[index - 1]; + } + } + protected sealed override void SetChild(int index, ILInstruction value) + { + switch (index) { + case 0: + this.TestedOperand = value; + break; + default: + this.SubPatterns[index - 1] = (ILInstruction)value; + break; + } + } + protected sealed override SlotInfo GetChildSlot(int index) + { + switch (index) { + case 0: + return TestedOperandSlot; + default: + return SubPatternsSlot; + } + } + public sealed override ILInstruction Clone() + { + var clone = (MatchInstruction)ShallowClone(); + clone.TestedOperand = this.testedOperand.Clone(); + clone.SubPatterns = new InstructionCollection(clone, 1); + clone.SubPatterns.AddRange(this.SubPatterns.Select(arg => (ILInstruction)arg.Clone())); + return clone; + } + public override StackType ResultType { get { return StackType.I4; } } + protected override InstructionFlags ComputeFlags() + { + return InstructionFlags.MayWriteLocals | testedOperand.Flags | SubPatterns.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.SideEffect | InstructionFlags.MayThrow | InstructionFlags.ControlFlow; + } + public override InstructionFlags DirectFlags { + get { + return InstructionFlags.MayWriteLocals | InstructionFlags.SideEffect | InstructionFlags.MayThrow | InstructionFlags.ControlFlow; + } + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitMatchInstruction(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitMatchInstruction(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitMatchInstruction(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as MatchInstruction; + return o != null && variable == o.variable && object.Equals(method, o.method) && this.Deconstruct == o.Deconstruct && this.MatchType == o.MatchType && this.MatchesNull == o.MatchesNull && this.testedOperand.PerformMatch(o.testedOperand, ref match) && Patterns.ListMatch.DoMatch(this.SubPatterns, o.SubPatterns, ref match); + } + internal override void CheckInvariant(ILPhase phase) + { + base.CheckInvariant(phase); + Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function)); + Debug.Assert(phase <= ILPhase.InILReader || variable.Function.Variables[variable.IndexInFunction] == variable); + Debug.Assert(testedOperand.ResultType == StackType.O); + AdditionalInvariants(); + } + } +} +namespace ICSharpCode.Decompiler.IL { /// Push a typed reference of type class onto the stack. public sealed partial class MakeRefAny : UnaryInstruction @@ -6396,6 +6543,62 @@ namespace ICSharpCode.Decompiler.IL } } } +namespace ICSharpCode.Decompiler.IL +{ + /// Deconstruction statement + public sealed partial class DeconstructInstruction : ILInstruction + { + public override StackType ResultType { get { return StackType.O; } } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitDeconstructInstruction(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitDeconstructInstruction(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitDeconstructInstruction(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as DeconstructInstruction; + return o != null; + } + } +} +namespace ICSharpCode.Decompiler.IL +{ + /// Represents a deconstructed value + public sealed partial class DeconstructResultInstruction : UnaryInstruction + { + IType type; + /// Returns the type operand. + public IType Type { + get { return type; } + set { type = value; InvalidateFlags(); } + } + public override StackType ResultType { get { return type.GetStackType(); } } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitDeconstructResultInstruction(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitDeconstructResultInstruction(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitDeconstructResultInstruction(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as DeconstructResultInstruction; + return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); + } + } +} namespace ICSharpCode.Decompiler.IL.Patterns { /// Matches any node @@ -6807,6 +7010,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitMatchInstruction(MatchInstruction inst) + { + Default(inst); + } protected internal virtual void VisitMakeRefAny(MakeRefAny inst) { Default(inst); @@ -6827,6 +7034,14 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitDeconstructInstruction(DeconstructInstruction inst) + { + Default(inst); + } + protected internal virtual void VisitDeconstructResultInstruction(DeconstructResultInstruction inst) + { + Default(inst); + } } /// @@ -7193,6 +7408,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitMatchInstruction(MatchInstruction inst) + { + return Default(inst); + } protected internal virtual T VisitMakeRefAny(MakeRefAny inst) { return Default(inst); @@ -7213,6 +7432,14 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitDeconstructInstruction(DeconstructInstruction inst) + { + return Default(inst); + } + protected internal virtual T VisitDeconstructResultInstruction(DeconstructResultInstruction inst) + { + return Default(inst); + } } /// @@ -7579,6 +7806,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitMatchInstruction(MatchInstruction inst, C context) + { + return Default(inst, context); + } protected internal virtual T VisitMakeRefAny(MakeRefAny inst, C context) { return Default(inst, context); @@ -7599,6 +7830,14 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitDeconstructInstruction(DeconstructInstruction inst, C context) + { + return Default(inst, context); + } + protected internal virtual T VisitDeconstructResultInstruction(DeconstructResultInstruction inst, C context) + { + return Default(inst, context); + } } partial class InstructionOutputExtensions @@ -7693,11 +7932,14 @@ namespace ICSharpCode.Decompiler.IL "dynamic.invokeconstructor", "dynamic.invoke", "dynamic.isevent", + "match", "mkrefany", "refanytype", "refanyval", "yield.return", "await", + "deconstruct", + "deconstruct.result", "AnyNode", }; } @@ -8254,6 +8496,20 @@ namespace ICSharpCode.Decompiler.IL right = default(ILInstruction); return false; } + public bool MatchMatchInstruction(out ILVariable variable, out IMethod method, out ILInstruction testedOperand) + { + var inst = this as MatchInstruction; + if (inst != null) { + variable = inst.Variable; + method = inst.Method; + testedOperand = inst.TestedOperand; + return true; + } + variable = default(ILVariable); + method = default(IMethod); + testedOperand = default(ILInstruction); + return false; + } public bool MatchMakeRefAny(out ILInstruction argument, out IType type) { var inst = this as MakeRefAny; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 2e3e35d83..34448f37e 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -334,6 +334,13 @@ new OpCode("dynamic.isevent", "ILAst representation of a call to the Binder.IsEvent method inside a dynamic expression.", CustomClassName("DynamicIsEventInstruction"), Dynamic, CustomArguments(("argument", new[] { "O" })), CustomWriteTo), + new OpCode("match", "ILAst representation of C# patterns", + CustomClassName("MatchInstruction"), HasVariableOperand("Store"), HasMethodOperand, + BoolFlag("Deconstruct"), BoolFlag("MatchType"), BoolFlag("MatchesNull"), + CustomChildren(new []{ + new ChildInfo("testedOperand") { CanInlineInto = true, ExpectedTypes = new[] { "O" } }, + new ChildInfo("subPatterns") { IsCollection = true } + }), ResultType("I4"), CustomWriteTo, SideEffect, MayThrow, ControlFlow, CustomInvariant("AdditionalInvariants();")), new OpCode("mkrefany", "Push a typed reference of type class onto the stack.", CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("O")), @@ -351,6 +358,11 @@ SideEffect, // other code can run with arbitrary side effects while we're waiting CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")), + new OpCode("deconstruct", "Deconstruction statement", + CustomClassName("DeconstructInstruction"), CustomConstructor, ResultType("O"), CustomWriteTo), + new OpCode("deconstruct.result", "Represents a deconstructed value", + CustomClassName("DeconstructResultInstruction"), CustomConstructor, Unary, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo), + // patterns new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor), }; @@ -1167,6 +1179,13 @@ protected override void Disconnected() }; } + static Action CustomInvariant(string code) + { + return opCode => { + opCode.Invariants.Add(code); + }; + } + static Action Pattern = opCode => { BaseClass("PatternInstruction")(opCode); opCode.Namespace = "ICSharpCode.Decompiler.IL.Patterns"; diff --git a/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs new file mode 100644 index 000000000..146dcbd33 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs @@ -0,0 +1,190 @@ +// Copyright (c) 2020 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.Diagnostics; +using System.Linq; + +namespace ICSharpCode.Decompiler.IL +{ + partial class DeconstructInstruction + { + public static readonly SlotInfo InitSlot = new SlotInfo("Init", canInlineInto: true, isCollection: true); + public static readonly SlotInfo DeconstructSlot = new SlotInfo("Deconstruct", canInlineInto: true); + public static readonly SlotInfo ConversionsSlot = new SlotInfo("Conversions"); + public static readonly SlotInfo AssignmentsSlot = new SlotInfo("Assignments"); + + public DeconstructInstruction() + : base(OpCode.DeconstructInstruction) + { + this.Init = new InstructionCollection(this, 0); + } + + public readonly InstructionCollection Init; + + ILInstruction deconstruct; + public ILInstruction Deconstruct { + get { return this.deconstruct; } + set { + ValidateChild(value); + SetChildInstruction(ref this.deconstruct, value, Init.Count); + } + } + + Block conversions; + public Block Conversions { + get { return this.conversions; } + set { + ValidateChild(value); + SetChildInstruction(ref this.conversions, value, Init.Count + 1); + } + } + + Block assignments; + public Block Assignments { + get { return this.assignments; } + set { + ValidateChild(value); + SetChildInstruction(ref this.assignments, value, Init.Count + 2); + } + } + + protected sealed override int GetChildCount() + { + return Init.Count + 3; + } + + protected sealed override ILInstruction GetChild(int index) + { + switch (index - Init.Count) { + case 0: + return this.deconstruct; + case 1: + return this.conversions; + case 2: + return this.assignments; + default: + return this.Init[index]; + } + } + + protected sealed override void SetChild(int index, ILInstruction value) + { + switch (index - Init.Count) { + case 0: + this.Deconstruct = value; + break; + case 1: + this.Conversions = (Block)value; + break; + case 2: + this.Assignments = (Block)value; + break; + default: + this.Init[index] = (StLoc)value; + break; + } + } + + protected sealed override SlotInfo GetChildSlot(int index) + { + switch (index - Init.Count) { + case 0: + return DeconstructSlot; + case 1: + return ConversionsSlot; + case 2: + return AssignmentsSlot; + default: + return InitSlot; + } + } + + public sealed override ILInstruction Clone() + { + var clone = new DeconstructInstruction(); + clone.Init.AddRange(this.Init.Select(inst => (StLoc)inst.Clone())); + clone.Deconstruct = this.deconstruct.Clone(); + clone.Conversions = (Block)this.conversions.Clone(); + clone.Assignments = (Block)this.assignments.Clone(); + return clone; + } + + protected override InstructionFlags ComputeFlags() + { + var flags = InstructionFlags.None; + foreach (var inst in Init) { + flags |= inst.Flags; + } + flags |= deconstruct.Flags | conversions.Flags | assignments.Flags; + return flags; + } + + public override InstructionFlags DirectFlags { + get { + return InstructionFlags.None; + } + } + + protected internal override void InstructionCollectionUpdateComplete() + { + base.InstructionCollectionUpdateComplete(); + if (deconstruct.Parent == this) + deconstruct.ChildIndex = Init.Count; + if (conversions.Parent == this) + conversions.ChildIndex = Init.Count + 1; + if (assignments.Parent == this) + assignments.ChildIndex = Init.Count + 2; + } + + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + WriteILRange(output, options); + output.Write("deconstruct"); + output.MarkFoldStart("{...}"); + output.WriteLine("{"); + output.Indent(); + output.WriteLine("init:"); + output.Indent(); + foreach (var inst in this.Init) { + inst.WriteTo(output, options); + output.WriteLine(); + } + output.Unindent(); + output.WriteLine("deconstruct:"); + output.Indent(); + deconstruct.WriteTo(output, options); + output.Unindent(); + output.Write("conversions:"); + conversions.WriteTo(output, options); + output.Write("assignments: "); + assignments.WriteTo(output, options); + output.Unindent(); + output.Write('}'); + output.MarkFoldEnd(); + } + + internal override void CheckInvariant(ILPhase phase) + { + base.CheckInvariant(phase); + foreach (var init in this.Init) { + Debug.Assert(init.Variable.IsSingleDefinition && init.Variable.LoadCount == 1); + Debug.Assert(init.Variable.LoadInstructions[0].IsDescendantOf(assignments)); + } + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs new file mode 100644 index 000000000..19134db99 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2020 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. + +namespace ICSharpCode.Decompiler.IL +{ + partial class DeconstructResultInstruction + { + public int Index { get; set; } + + public DeconstructResultInstruction(int index, ILInstruction argument) + : base(OpCode.DeconstructResultInstruction, argument) + { + Index = index; + } + + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + WriteILRange(output, options); + output.Write(OpCode); + output.Write(' '); + type.WriteTo(output); + output.Write(' '); + output.Write(Index.ToString()); + output.Write('('); + this.Argument.WriteTo(output, options); + output.Write(')'); + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index 1bc534954..e016e89de 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -632,9 +632,10 @@ namespace ICSharpCode.Decompiler.IL /// Reference to the field holding the child /// New child /// Index of the field in the Children collection - protected internal void SetChildInstruction(ref ILInstruction childPointer, ILInstruction newValue, int index) + protected internal void SetChildInstruction(ref T childPointer, T newValue, int index) + where T : ILInstruction { - ILInstruction oldValue = childPointer; + T oldValue = childPointer; Debug.Assert(oldValue == GetChild(index)); if (oldValue == newValue && newValue?.parent == this && newValue.ChildIndex == index) return; diff --git a/ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs new file mode 100644 index 000000000..e5307b5a7 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2020 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.Diagnostics; + +namespace ICSharpCode.Decompiler.IL +{ + partial class MatchInstruction : ILInstruction + { + public bool IsPattern(ILInstruction inst, out ILInstruction testedOperand) + { + switch (inst) { + case MatchInstruction m: + testedOperand = m.testedOperand; + return true; + case Comp comp: + testedOperand = comp.Left; + return IsConstant(comp.Right); + case ILInstruction logicNot when logicNot.MatchLogicNot(out var operand): + return IsPattern(operand, out testedOperand); + default: + testedOperand = null; + return false; + } + } + + private static bool IsConstant(ILInstruction inst) + { + return inst.OpCode switch + { + OpCode.LdcDecimal => true, + OpCode.LdcF4 => true, + OpCode.LdcF8 => true, + OpCode.LdcI4 => true, + OpCode.LdcI8 => true, + OpCode.LdNull => true, + _ => false + }; + } + + void AdditionalInvariants() + { + foreach (var subPattern in SubPatterns) { + ILInstruction operand; + Debug.Assert(IsPattern(subPattern, out operand)); + } + } + + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + WriteILRange(output, options); + output.Write(OpCode); + output.Write(' '); + output.Write('('); + Variable.WriteTo(output); + output.Write(" = "); + TestedOperand.WriteTo(output, options); + output.Write(')'); + } + } +}