Browse Source

Extended DeconstructInstruction.CheckInvariant

pull/2119/head
Siegfried Pammer 5 years ago
parent
commit
6bb6097ad4
  1. 4
      ICSharpCode.Decompiler/IL/Instructions.cs
  2. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  3. 89
      ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs
  4. 2
      ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs
  5. 12
      ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs

4
ICSharpCode.Decompiler/IL/Instructions.cs

@ -6138,7 +6138,7 @@ namespace ICSharpCode.Decompiler.IL @@ -6138,7 +6138,7 @@ namespace ICSharpCode.Decompiler.IL
readonly IMethod method;
/// <summary>Returns the method operand.</summary>
public IMethod Method { get { return method; } }
public bool Deconstruct;
public bool IsDeconstructCall;
public bool CheckType;
public bool CheckNotNull;
public static readonly SlotInfo TestedOperandSlot = new SlotInfo("TestedOperand", canInlineInto: true);
@ -6218,7 +6218,7 @@ namespace ICSharpCode.Decompiler.IL @@ -6218,7 +6218,7 @@ namespace ICSharpCode.Decompiler.IL
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.CheckType == o.CheckType && this.CheckNotNull == o.CheckNotNull && this.testedOperand.PerformMatch(o.testedOperand, ref match) && Patterns.ListMatch.DoMatch(this.SubPatterns, o.SubPatterns, ref match);
return o != null && variable == o.variable && object.Equals(method, o.method) && this.IsDeconstructCall == o.IsDeconstructCall && this.CheckType == o.CheckType && this.CheckNotNull == o.CheckNotNull && this.testedOperand.PerformMatch(o.testedOperand, ref match) && Patterns.ListMatch.DoMatch(this.SubPatterns, o.SubPatterns, ref match);
}
internal override void CheckInvariant(ILPhase phase)
{

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -336,7 +336,7 @@ @@ -336,7 +336,7 @@
new OpCode("match", "ILAst representation of C# patterns",
CustomClassName("MatchInstruction"), HasVariableOperand("Store"), HasMethodOperand,
BoolFlag("Deconstruct"), BoolFlag("CheckType"), BoolFlag("CheckNotNull"),
BoolFlag("IsDeconstructCall"), BoolFlag("CheckType"), BoolFlag("CheckNotNull"),
CustomChildren(new []{
new ChildInfo("testedOperand") { CanInlineInto = true, ExpectedTypes = new[] { "O" } },
new ChildInfo("subPatterns") { IsCollection = true }

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

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
// 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.Diagnostics;
using System.Linq;
@ -36,8 +38,8 @@ namespace ICSharpCode.Decompiler.IL @@ -36,8 +38,8 @@ namespace ICSharpCode.Decompiler.IL
public readonly InstructionCollection<StLoc> Init;
ILInstruction deconstruct;
public ILInstruction Deconstruct {
MatchInstruction deconstruct;
public MatchInstruction Deconstruct {
get { return this.deconstruct; }
set {
ValidateChild(value);
@ -86,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL @@ -86,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index - Init.Count) {
case 0:
this.Deconstruct = value;
this.Deconstruct = (MatchInstruction)value;
break;
case 1:
this.Conversions = (Block)value;
@ -118,7 +120,7 @@ namespace ICSharpCode.Decompiler.IL @@ -118,7 +120,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = new DeconstructInstruction();
clone.Init.AddRange(this.Init.Select(inst => (StLoc)inst.Clone()));
clone.Deconstruct = this.deconstruct.Clone();
clone.Deconstruct = (MatchInstruction)this.deconstruct.Clone();
clone.Conversions = (Block)this.conversions.Clone();
clone.Assignments = (Block)this.assignments.Clone();
return clone;
@ -178,13 +180,90 @@ namespace ICSharpCode.Decompiler.IL @@ -178,13 +180,90 @@ namespace ICSharpCode.Decompiler.IL
output.MarkFoldEnd();
}
internal static bool IsConversionStLoc(ILInstruction inst, out ILVariable variable, out ILVariable inputVariable)
{
inputVariable = null;
if (!inst.MatchStLoc(out variable, out var value))
return false;
ILInstruction input;
switch (value) {
case Conv conv:
input = conv.Argument;
break;
case Call { Method: { IsOperator: true, Name: "op_Implicit" }, Arguments: { Count: 1 } } call:
input = call.Arguments[0];
break;
default:
return false;
}
return input.MatchLdLoc(out inputVariable) || input.MatchLdLoca(out inputVariable);
}
internal static bool IsAssignment(ILInstruction inst, out ILVariable inputVariable)
{
inputVariable = null;
ILInstruction value;
switch (inst) {
case CallInstruction call:
// TODO
return false;
case StLoc stloc:
value = stloc.Value;
break;
case StObj stobj:
// TODO
return false;
default:
return false;
}
return value.MatchLdLoc(out inputVariable);
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
foreach (var init in this.Init) {
var patternVariables = new HashSet<ILVariable>();
var conversionVariables = new HashSet<ILVariable>();
foreach (StLoc init in this.Init) {
Debug.Assert(init.Variable.IsSingleDefinition && init.Variable.LoadCount == 1);
Debug.Assert(init.Variable.LoadInstructions[0].IsDescendantOf(assignments));
}
ValidatePattern(deconstruct);
foreach (var inst in this.conversions.Instructions) {
if (!IsConversionStLoc(inst, out var variable, out var inputVariable))
Debug.Fail("inst is not a conversion stloc!");
Debug.Assert(variable.IsSingleDefinition && variable.LoadCount == 1);
Debug.Assert(variable.LoadInstructions[0].IsDescendantOf(assignments));
Debug.Assert(patternVariables.Contains(inputVariable));
conversionVariables.Add(variable);
}
Debug.Assert(this.conversions.FinalInstruction is Nop);
foreach (var inst in assignments.Instructions) {
if (!IsAssignment(inst, out var inputVariable))
Debug.Fail("inst is not a assigment!");
Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable));
}
Debug.Assert(this.assignments.FinalInstruction is Nop);
void ValidatePattern(MatchInstruction inst)
{
Debug.Assert(inst.IsDeconstructCall);
Debug.Assert(!inst.CheckNotNull && !inst.CheckType);
Debug.Assert(!inst.HasDesignator);
foreach (var subPattern in inst.SubPatterns.Cast<MatchInstruction>()) {
if (subPattern.IsVar) {
Debug.Assert(subPattern.Variable.IsSingleDefinition && subPattern.Variable.LoadCount == 1);
Debug.Assert(subPattern.Variable.LoadInstructions[0].IsDescendantOf(this));
patternVariables.Add(subPattern.Variable);
} else {
ValidatePattern(subPattern);
}
}
}
}
}
}

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

@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.IL @@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.IL
void AdditionalInvariants()
{
var matchInst = FindMatch();
Debug.Assert(matchInst != null && matchInst.Deconstruct);
Debug.Assert(matchInst != null && matchInst.IsDeconstructCall);
Debug.Assert(Argument.MatchLdLoc(matchInst.Variable));
var outParamType = matchInst.GetDeconstructResult(this.Index).Type;
if (outParamType is ByReferenceType brt)

12
ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs

@ -87,6 +87,10 @@ namespace ICSharpCode.Decompiler.IL @@ -87,6 +87,10 @@ namespace ICSharpCode.Decompiler.IL
}
*/
public bool IsVar => !CheckType && !CheckNotNull && !IsDeconstructCall && SubPatterns.Count == 0;
public bool HasDesignator => Variable.LoadCount + Variable.AddressCount > SubPatterns.Count;
/// <summary>
/// Checks whether the input instruction can represent a pattern matching operation.
///
@ -129,7 +133,7 @@ namespace ICSharpCode.Decompiler.IL @@ -129,7 +133,7 @@ namespace ICSharpCode.Decompiler.IL
internal IParameter GetDeconstructResult(int index)
{
Debug.Assert(this.Deconstruct);
Debug.Assert(this.IsDeconstructCall);
int firstOutParam = (method.IsStatic ? 1 : 0);
return this.Method.Parameters[firstOutParam + index];
}
@ -137,7 +141,7 @@ namespace ICSharpCode.Decompiler.IL @@ -137,7 +141,7 @@ namespace ICSharpCode.Decompiler.IL
void AdditionalInvariants()
{
Debug.Assert(variable.Kind == VariableKind.PatternLocal);
if (this.Deconstruct) {
if (this.IsDeconstructCall) {
Debug.Assert(method.Name == "Deconstruct");
int firstOutParam = (method.IsStatic ? 1 : 0);
Debug.Assert(method.Parameters.Count >= firstOutParam);
@ -152,7 +156,7 @@ namespace ICSharpCode.Decompiler.IL @@ -152,7 +156,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter);
Debug.Assert(call.Arguments[0].MatchLdLoc(variable));
} else if (operand is DeconstructResultInstruction resultInstruction) {
Debug.Assert(this.Deconstruct);
Debug.Assert(this.IsDeconstructCall);
} else {
Debug.Fail("Tested operand of sub-pattern is invalid.");
}
@ -171,7 +175,7 @@ namespace ICSharpCode.Decompiler.IL @@ -171,7 +175,7 @@ namespace ICSharpCode.Decompiler.IL
variable.Type.WriteTo(output);
output.Write(']');
}
if (Deconstruct) {
if (IsDeconstructCall) {
output.Write(".deconstruct[");
method.WriteTo(output);
output.Write(']');

Loading…
Cancel
Save