Browse Source

Fix bugs in ILAst

pull/2119/head
Siegfried Pammer 6 years ago
parent
commit
b01e9484d5
  1. 3
      ICSharpCode.Decompiler/IL/ILVariable.cs
  2. 12
      ICSharpCode.Decompiler/IL/Instructions.cs
  3. 4
      ICSharpCode.Decompiler/IL/Instructions.tt
  4. 14
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  5. 37
      ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs
  6. 12
      ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs
  7. 86
      ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs

3
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -428,6 +428,9 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.DisplayClassLocal: case VariableKind.DisplayClassLocal:
output.Write("display_class local "); output.Write("display_class local ");
break; break;
case VariableKind.PatternLocal:
output.Write("pattern local ");
break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }

12
ICSharpCode.Decompiler/IL/Instructions.cs

@ -6548,7 +6548,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Deconstruction statement</summary> /// <summary>Deconstruction statement</summary>
public sealed partial class DeconstructInstruction : ILInstruction public sealed partial class DeconstructInstruction : ILInstruction
{ {
public override StackType ResultType { get { return StackType.O; } } public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitDeconstructInstruction(this); visitor.VisitDeconstructInstruction(this);
@ -6573,13 +6573,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Represents a deconstructed value</summary> /// <summary>Represents a deconstructed value</summary>
public sealed partial class DeconstructResultInstruction : UnaryInstruction public sealed partial class DeconstructResultInstruction : UnaryInstruction
{ {
IType type;
/// <summary>Returns the type operand.</summary>
public IType Type {
get { return type; }
set { type = value; InvalidateFlags(); }
}
public override StackType ResultType { get { return type.GetStackType(); } }
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitDeconstructResultInstruction(this); visitor.VisitDeconstructResultInstruction(this);
@ -6595,7 +6589,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as DeconstructResultInstruction; var o = other as DeconstructResultInstruction;
return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); return o != null && this.Argument.PerformMatch(o.Argument, ref match);
} }
internal override void CheckInvariant(ILPhase phase) internal override void CheckInvariant(ILPhase phase)
{ {

4
ICSharpCode.Decompiler/IL/Instructions.tt

@ -359,10 +359,10 @@
CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")), CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")),
new OpCode("deconstruct", "Deconstruction statement", new OpCode("deconstruct", "Deconstruction statement",
CustomClassName("DeconstructInstruction"), CustomConstructor, ResultType("O"), CustomWriteTo), CustomClassName("DeconstructInstruction"), CustomConstructor, ResultType("Void"), CustomWriteTo),
new OpCode("deconstruct.result", "Represents a deconstructed value", new OpCode("deconstruct.result", "Represents a deconstructed value",
CustomClassName("DeconstructResultInstruction"), CustomConstructor, CustomInvariant("AdditionalInvariants();"), CustomClassName("DeconstructResultInstruction"), CustomConstructor, CustomInvariant("AdditionalInvariants();"),
Unary, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo), Unary, CustomWriteTo),
// patterns // patterns
new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor), new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor),

14
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -171,6 +171,12 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(Instructions[i] is StLoc || AccessPathElement.GetAccessPath(Instructions[i], type2).Kind != IL.Transforms.AccessPathKind.Invalid); Debug.Assert(Instructions[i] is StLoc || AccessPathElement.GetAccessPath(Instructions[i], type2).Kind != IL.Transforms.AccessPathKind.Invalid);
} }
break; break;
case BlockKind.DeconstructionConversions:
Debug.Assert(this.SlotInfo == DeconstructInstruction.ConversionsSlot);
break;
case BlockKind.DeconstructionAssignments:
Debug.Assert(this.SlotInfo == DeconstructInstruction.AssignmentsSlot);
break;
} }
} }
@ -420,5 +426,13 @@ namespace ICSharpCode.Decompiler.IL
/// } /// }
/// </example> /// </example>
CallWithNamedArgs, CallWithNamedArgs,
/// <summary>
/// <see cref="DeconstructInstruction"/>
/// </summary>
DeconstructionConversions,
/// <summary>
/// <see cref="DeconstructInstruction"/>
/// </summary>
DeconstructionAssignments,
} }
} }

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

@ -26,7 +26,7 @@ namespace ICSharpCode.Decompiler.IL
partial class DeconstructInstruction partial class DeconstructInstruction
{ {
public static readonly SlotInfo InitSlot = new SlotInfo("Init", canInlineInto: true, isCollection: true); 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 PatternSlot = new SlotInfo("Pattern", canInlineInto: true);
public static readonly SlotInfo ConversionsSlot = new SlotInfo("Conversions"); public static readonly SlotInfo ConversionsSlot = new SlotInfo("Conversions");
public static readonly SlotInfo AssignmentsSlot = new SlotInfo("Assignments"); public static readonly SlotInfo AssignmentsSlot = new SlotInfo("Assignments");
@ -38,12 +38,12 @@ namespace ICSharpCode.Decompiler.IL
public readonly InstructionCollection<StLoc> Init; public readonly InstructionCollection<StLoc> Init;
MatchInstruction deconstruct; MatchInstruction pattern;
public MatchInstruction Deconstruct { public MatchInstruction Pattern {
get { return this.deconstruct; } get { return this.pattern; }
set { set {
ValidateChild(value); ValidateChild(value);
SetChildInstruction(ref this.deconstruct, value, Init.Count); SetChildInstruction(ref this.pattern, value, Init.Count);
} }
} }
@ -74,7 +74,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index - Init.Count) { switch (index - Init.Count) {
case 0: case 0:
return this.deconstruct; return this.pattern;
case 1: case 1:
return this.conversions; return this.conversions;
case 2: case 2:
@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index - Init.Count) { switch (index - Init.Count) {
case 0: case 0:
this.Deconstruct = (MatchInstruction)value; this.Pattern = (MatchInstruction)value;
break; break;
case 1: case 1:
this.Conversions = (Block)value; this.Conversions = (Block)value;
@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index - Init.Count) { switch (index - Init.Count) {
case 0: case 0:
return DeconstructSlot; return PatternSlot;
case 1: case 1:
return ConversionsSlot; return ConversionsSlot;
case 2: case 2:
@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = new DeconstructInstruction(); var clone = new DeconstructInstruction();
clone.Init.AddRange(this.Init.Select(inst => (StLoc)inst.Clone())); clone.Init.AddRange(this.Init.Select(inst => (StLoc)inst.Clone()));
clone.Deconstruct = (MatchInstruction)this.deconstruct.Clone(); clone.Pattern = (MatchInstruction)this.pattern.Clone();
clone.Conversions = (Block)this.conversions.Clone(); clone.Conversions = (Block)this.conversions.Clone();
clone.Assignments = (Block)this.assignments.Clone(); clone.Assignments = (Block)this.assignments.Clone();
return clone; return clone;
@ -132,7 +132,7 @@ namespace ICSharpCode.Decompiler.IL
foreach (var inst in Init) { foreach (var inst in Init) {
flags |= inst.Flags; flags |= inst.Flags;
} }
flags |= deconstruct.Flags | conversions.Flags | assignments.Flags; flags |= pattern.Flags | conversions.Flags | assignments.Flags;
return flags; return flags;
} }
@ -145,8 +145,8 @@ namespace ICSharpCode.Decompiler.IL
protected internal override void InstructionCollectionUpdateComplete() protected internal override void InstructionCollectionUpdateComplete()
{ {
base.InstructionCollectionUpdateComplete(); base.InstructionCollectionUpdateComplete();
if (deconstruct.Parent == this) if (pattern.Parent == this)
deconstruct.ChildIndex = Init.Count; pattern.ChildIndex = Init.Count;
if (conversions.Parent == this) if (conversions.Parent == this)
conversions.ChildIndex = Init.Count + 1; conversions.ChildIndex = Init.Count + 1;
if (assignments.Parent == this) if (assignments.Parent == this)
@ -156,7 +156,7 @@ namespace ICSharpCode.Decompiler.IL
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{ {
WriteILRange(output, options); WriteILRange(output, options);
output.Write("deconstruct"); output.Write("deconstruct ");
output.MarkFoldStart("{...}"); output.MarkFoldStart("{...}");
output.WriteLine("{"); output.WriteLine("{");
output.Indent(); output.Indent();
@ -167,15 +167,18 @@ namespace ICSharpCode.Decompiler.IL
output.WriteLine(); output.WriteLine();
} }
output.Unindent(); output.Unindent();
output.WriteLine("deconstruct:"); output.WriteLine("pattern:");
output.Indent(); output.Indent();
deconstruct.WriteTo(output, options); pattern.WriteTo(output, options);
output.Unindent(); output.Unindent();
output.WriteLine();
output.Write("conversions:"); output.Write("conversions:");
conversions.WriteTo(output, options); conversions.WriteTo(output, options);
output.WriteLine();
output.Write("assignments: "); output.Write("assignments: ");
assignments.WriteTo(output, options); assignments.WriteTo(output, options);
output.Unindent(); output.Unindent();
output.WriteLine();
output.Write('}'); output.Write('}');
output.MarkFoldEnd(); output.MarkFoldEnd();
} }
@ -230,7 +233,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(init.Variable.LoadInstructions[0].IsDescendantOf(assignments)); Debug.Assert(init.Variable.LoadInstructions[0].IsDescendantOf(assignments));
} }
ValidatePattern(deconstruct); ValidatePattern(pattern);
foreach (var inst in this.conversions.Instructions) { foreach (var inst in this.conversions.Instructions) {
if (!IsConversionStLoc(inst, out var variable, out var inputVariable)) if (!IsConversionStLoc(inst, out var variable, out var inputVariable))
@ -244,7 +247,7 @@ namespace ICSharpCode.Decompiler.IL
foreach (var inst in assignments.Instructions) { foreach (var inst in assignments.Instructions) {
if (!IsAssignment(inst, out var inputVariable)) if (!IsAssignment(inst, out var inputVariable))
Debug.Fail("inst is not a assigment!"); Debug.Fail("inst is not a assignment!");
Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable)); Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable));
} }
Debug.Assert(this.assignments.FinalInstruction is Nop); Debug.Assert(this.assignments.FinalInstruction is Nop);

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

@ -23,12 +23,16 @@ namespace ICSharpCode.Decompiler.IL
{ {
partial class DeconstructResultInstruction partial class DeconstructResultInstruction
{ {
public int Index { get; set; } public int Index { get; }
public DeconstructResultInstruction(int index, ILInstruction argument) public override StackType ResultType { get; }
public DeconstructResultInstruction(int index, StackType resultType, ILInstruction argument)
: base(OpCode.DeconstructResultInstruction, argument) : base(OpCode.DeconstructResultInstruction, argument)
{ {
Debug.Assert(index >= 0);
Index = index; Index = index;
ResultType = resultType;
} }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
@ -36,8 +40,6 @@ namespace ICSharpCode.Decompiler.IL
WriteILRange(output, options); WriteILRange(output, options);
output.Write(OpCode); output.Write(OpCode);
output.Write(' '); output.Write(' ');
type.WriteTo(output);
output.Write(' ');
output.Write(Index.ToString()); output.Write(Index.ToString());
output.Write('('); output.Write('(');
this.Argument.WriteTo(output, options); this.Argument.WriteTo(output, options);
@ -60,7 +62,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(Argument.MatchLdLoc(matchInst.Variable)); Debug.Assert(Argument.MatchLdLoc(matchInst.Variable));
var outParamType = matchInst.GetDeconstructResult(this.Index).Type; var outParamType = matchInst.GetDeconstructResult(this.Index).Type;
if (outParamType is ByReferenceType brt) if (outParamType is ByReferenceType brt)
Debug.Assert(brt.ElementType.Equals(this.Type)); Debug.Assert(brt.ElementType.GetStackType() == ResultType);
else else
Debug.Fail("deconstruct out param must be by reference"); Debug.Fail("deconstruct out param must be by reference");
} }

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

@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL
return false; return false;
if (this.CheckType && !(value is this.Variable.Type)) if (this.CheckType && !(value is this.Variable.Type))
return false; return false;
if (this.Deconstruct) { if (this.IsDeconstructCall) {
deconstructResult = new[numArgs]; deconstructResult = new[numArgs];
EvalCall(this.Method, value, out deconstructResult[0], .., out deconstructResult[numArgs-1]); EvalCall(this.Method, value, out deconstructResult[0], .., out deconstructResult[numArgs-1]);
// any occurrences of 'deconstruct.result' in the subPatterns will refer // any occurrences of 'deconstruct.result' in the subPatterns will refer
@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL
match(x = expr) match(x = expr)
expr is {} x: expr is {} x:
match.notnull(x = expr match.notnull(x = expr)
expr is T x: expr is T x:
match.type[T](x = expr) match.type[T](x = expr)
@ -72,17 +72,17 @@ namespace ICSharpCode.Decompiler.IL
expr is C(var x, var y, <4): expr is C(var x, var y, <4):
match.type[C].deconstruct[C.Deconstruct](tmp1 = expr) { match.type[C].deconstruct[C.Deconstruct](tmp1 = expr) {
match(x = deconstruct.result0(tmp1)), match(x = deconstruct.result1(tmp1)),
match(y = deconstruct.result1(tmp1)), match(y = deconstruct.result2(tmp1)),
comp(deconstruct.result2(tmp1) < 4), comp(deconstruct.result3(tmp1) < 4),
} }
expr is C(1, D(2, 3)): expr is C(1, D(2, 3)):
match.type[C].deconstruct(c = expr) { match.type[C].deconstruct(c = expr) {
comp(deconstruct.result0(c) == 1), comp(deconstruct.result1(c) == 1),
match.type[D].deconstruct(d = deconstruct.result1(c)) { match.type[D].deconstruct(d = deconstruct.result2(c)) {
comp(deconstruct.result0(d) == 2),
comp(deconstruct.result1(d) == 2), comp(deconstruct.result1(d) == 2),
comp(deconstruct.result2(d) == 2),
} }
} }
*/ */
@ -91,6 +91,20 @@ namespace ICSharpCode.Decompiler.IL
public bool HasDesignator => Variable.LoadCount + Variable.AddressCount > SubPatterns.Count; public bool HasDesignator => Variable.LoadCount + Variable.AddressCount > SubPatterns.Count;
public int NumPositionalPatterns {
get {
if (IsDeconstructCall)
return method.Parameters.Count - (method.IsStatic ? 1 : 0);
else
return 0;
}
}
public MatchInstruction(ILVariable variable, ILInstruction testedOperand)
: this(variable, method: null, testedOperand)
{
}
/// <summary> /// <summary>
/// Checks whether the input instruction can represent a pattern matching operation. /// Checks whether the input instruction can represent a pattern matching operation.
/// ///
@ -142,27 +156,57 @@ namespace ICSharpCode.Decompiler.IL
{ {
Debug.Assert(variable.Kind == VariableKind.PatternLocal); Debug.Assert(variable.Kind == VariableKind.PatternLocal);
if (this.IsDeconstructCall) { if (this.IsDeconstructCall) {
Debug.Assert(method.Name == "Deconstruct"); Debug.Assert(IsDeconstructMethod(method));
int firstOutParam = (method.IsStatic ? 1 : 0); Debug.Assert(SubPatterns.Count >= NumPositionalPatterns);
Debug.Assert(method.Parameters.Count >= firstOutParam);
Debug.Assert(method.Parameters.Skip(firstOutParam).All(p => p.IsOut));
} }
foreach (var subPattern in SubPatterns) { foreach (var subPattern in SubPatterns) {
if (!IsPatternMatch(subPattern, out ILInstruction operand)) if (!IsPatternMatch(subPattern, out ILInstruction operand))
Debug.Fail("Sub-Pattern must be a valid pattern"); Debug.Fail("Sub-Pattern must be a valid pattern");
if (operand.MatchLdFld(out var target, out _)) { // the first child is TestedOperand
int subPatternIndex = subPattern.ChildIndex - 1;
if (subPatternIndex < NumPositionalPatterns) {
// positional pattern
Debug.Assert(operand is DeconstructResultInstruction result && result.Index == subPatternIndex);
} else if (operand.MatchLdFld(out var target, out _)) {
Debug.Assert(target.MatchLdLoc(variable)); Debug.Assert(target.MatchLdLoc(variable));
} else if (operand is CallInstruction call) { } else if (operand is CallInstruction call) {
Debug.Assert(call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter); Debug.Assert(call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter);
Debug.Assert(call.Arguments[0].MatchLdLoc(variable)); Debug.Assert(call.Arguments[0].MatchLdLoc(variable));
} else if (operand is DeconstructResultInstruction resultInstruction) {
Debug.Assert(this.IsDeconstructCall);
} else { } else {
Debug.Fail("Tested operand of sub-pattern is invalid."); Debug.Fail("Tested operand of sub-pattern is invalid.");
} }
} }
} }
internal static bool IsDeconstructMethod(IMethod method)
{
if (method.Name != "Deconstruct")
return false;
if (method.ReturnType.Kind != TypeKind.Void)
return false;
int firstOutParam = (method.IsStatic ? 1 : 0);
if (method.IsStatic) {
if (!method.IsExtensionMethod)
return false;
// TODO : check whether all type arguments can be inferred from the first argument
} else {
if (method.TypeParameters.Count != 0)
return false;
}
// TODO : check whether the method is ambigious
if (method.Parameters.Count < firstOutParam)
return false;
for (int i = firstOutParam; i < method.Parameters.Count; i++) {
if (!method.Parameters[i].IsOut)
return false;
}
return true;
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{ {
WriteILRange(output, options); WriteILRange(output, options);
@ -186,6 +230,18 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" = "); output.Write(" = ");
TestedOperand.WriteTo(output, options); TestedOperand.WriteTo(output, options);
output.Write(')'); output.Write(')');
if (SubPatterns.Count > 0) {
output.MarkFoldStart("{...}");
output.WriteLine("{");
output.Indent();
foreach (var pattern in SubPatterns) {
pattern.WriteTo(output, options);
output.WriteLine();
}
output.Unindent();
output.Write('}');
output.MarkFoldEnd();
}
} }
} }
} }

Loading…
Cancel
Save