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

12
ICSharpCode.Decompiler/IL/Instructions.cs

@ -6548,7 +6548,7 @@ namespace ICSharpCode.Decompiler.IL @@ -6548,7 +6548,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Deconstruction statement</summary>
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)
{
visitor.VisitDeconstructInstruction(this);
@ -6573,13 +6573,7 @@ namespace ICSharpCode.Decompiler.IL @@ -6573,13 +6573,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Represents a deconstructed value</summary>
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)
{
visitor.VisitDeconstructResultInstruction(this);
@ -6595,7 +6589,7 @@ namespace ICSharpCode.Decompiler.IL @@ -6595,7 +6589,7 @@ namespace ICSharpCode.Decompiler.IL
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);
return o != null && this.Argument.PerformMatch(o.Argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
{

4
ICSharpCode.Decompiler/IL/Instructions.tt

@ -359,10 +359,10 @@ @@ -359,10 +359,10 @@
CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")),
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",
CustomClassName("DeconstructResultInstruction"), CustomConstructor, CustomInvariant("AdditionalInvariants();"),
Unary, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo),
Unary, CustomWriteTo),
// patterns
new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor),

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

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

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

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

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

@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL @@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL
return false;
if (this.CheckType && !(value is this.Variable.Type))
return false;
if (this.Deconstruct) {
if (this.IsDeconstructCall) {
deconstructResult = new[numArgs];
EvalCall(this.Method, value, out deconstructResult[0], .., out deconstructResult[numArgs-1]);
// any occurrences of 'deconstruct.result' in the subPatterns will refer
@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL @@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL
match(x = expr)
expr is {} x:
match.notnull(x = expr
match.notnull(x = expr)
expr is T x:
match.type[T](x = expr)
@ -72,17 +72,17 @@ namespace ICSharpCode.Decompiler.IL @@ -72,17 +72,17 @@ namespace ICSharpCode.Decompiler.IL
expr is C(var x, var y, <4):
match.type[C].deconstruct[C.Deconstruct](tmp1 = expr) {
match(x = deconstruct.result0(tmp1)),
match(y = deconstruct.result1(tmp1)),
comp(deconstruct.result2(tmp1) < 4),
match(x = deconstruct.result1(tmp1)),
match(y = deconstruct.result2(tmp1)),
comp(deconstruct.result3(tmp1) < 4),
}
expr is C(1, D(2, 3)):
match.type[C].deconstruct(c = expr) {
comp(deconstruct.result0(c) == 1),
match.type[D].deconstruct(d = deconstruct.result1(c)) {
comp(deconstruct.result0(d) == 2),
comp(deconstruct.result1(c) == 1),
match.type[D].deconstruct(d = deconstruct.result2(c)) {
comp(deconstruct.result1(d) == 2),
comp(deconstruct.result2(d) == 2),
}
}
*/
@ -91,6 +91,20 @@ namespace ICSharpCode.Decompiler.IL @@ -91,6 +91,20 @@ namespace ICSharpCode.Decompiler.IL
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>
/// Checks whether the input instruction can represent a pattern matching operation.
///
@ -142,27 +156,57 @@ namespace ICSharpCode.Decompiler.IL @@ -142,27 +156,57 @@ namespace ICSharpCode.Decompiler.IL
{
Debug.Assert(variable.Kind == VariableKind.PatternLocal);
if (this.IsDeconstructCall) {
Debug.Assert(method.Name == "Deconstruct");
int firstOutParam = (method.IsStatic ? 1 : 0);
Debug.Assert(method.Parameters.Count >= firstOutParam);
Debug.Assert(method.Parameters.Skip(firstOutParam).All(p => p.IsOut));
Debug.Assert(IsDeconstructMethod(method));
Debug.Assert(SubPatterns.Count >= NumPositionalPatterns);
}
foreach (var subPattern in SubPatterns) {
if (!IsPatternMatch(subPattern, out ILInstruction operand))
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));
} else if (operand is CallInstruction call) {
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.IsDeconstructCall);
} else {
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)
{
WriteILRange(output, options);
@ -186,6 +230,18 @@ namespace ICSharpCode.Decompiler.IL @@ -186,6 +230,18 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" = ");
TestedOperand.WriteTo(output, options);
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