Browse Source

Add result type invariants for instruction arguments.

pull/1278/head
Siegfried Pammer 8 years ago
parent
commit
75a3f6d24b
  1. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  2. 23
      ICSharpCode.Decompiler/IL/ILReader.cs
  3. 46
      ICSharpCode.Decompiler/IL/Instructions.cs
  4. 46
      ICSharpCode.Decompiler/IL/Instructions.tt
  5. 31
      ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs
  6. 4
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  7. 1
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -285,6 +285,7 @@ @@ -285,6 +285,7 @@
<Compile Include="DotNetCore\UniversalAssemblyResolver.cs" />
<Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" />
<Compile Include="IL\ILAstWritingOptions.cs" />
<Compile Include="IL\Instructions\LdFlda.cs" />
<Compile Include="IL\Instructions\StLoc.cs" />
<Compile Include="IL\SequencePoint.cs" />
<Compile Include="IL\Instructions\CallIndirect.cs" />

23
ICSharpCode.Decompiler/IL/ILReader.cs

@ -861,14 +861,17 @@ namespace ICSharpCode.Decompiler.IL @@ -861,14 +861,17 @@ namespace ICSharpCode.Decompiler.IL
case Cil.Code.Ldfld:
{
var field = ReadAndDecodeFieldReference();
return Push(new LdObj(new LdFlda(Pop(), field) { DelayExceptions = true }, field.Type));
return Push(new LdObj(new LdFlda(PopLdFldTarget(field), field) { DelayExceptions = true }, field.Type));
}
case Cil.Code.Ldflda:
return Push(new LdFlda(Pop(), ReadAndDecodeFieldReference()));
{
var field = ReadAndDecodeFieldReference();
return Push(new LdFlda(PopFieldTarget(field), field));
}
case Cil.Code.Stfld:
{
var field = ReadAndDecodeFieldReference();
return new StObj(value: Pop(field.Type.GetStackType()), target: new LdFlda(Pop(), field) { DelayExceptions = true }, type: field.Type);
return new StObj(value: Pop(field.Type.GetStackType()), target: new LdFlda(PopFieldTarget(field), field) { DelayExceptions = true }, type: field.Type);
}
case Cil.Code.Ldlen:
return Push(new LdLen(StackType.I, Pop()));
@ -936,7 +939,6 @@ namespace ICSharpCode.Decompiler.IL @@ -936,7 +939,6 @@ namespace ICSharpCode.Decompiler.IL
}
}
StackType PeekStackType()
{
if (currentStack.IsEmpty)
@ -1079,6 +1081,19 @@ namespace ICSharpCode.Decompiler.IL @@ -1079,6 +1081,19 @@ namespace ICSharpCode.Decompiler.IL
}
}
ILInstruction PopFieldTarget(IField field)
{
return field.DeclaringType.IsReferenceType == true ? Pop(StackType.O) : PopPointer();
}
ILInstruction PopLdFldTarget(IField field)
{
if (field.DeclaringType.IsReferenceType == true)
return Pop(StackType.O);
return PeekStackType() == StackType.O ? new AddressOf(Pop()) : PopPointer();
}
private ILInstruction Return()
{
if (methodReturnStackType == StackType.Void)

46
ICSharpCode.Decompiler/IL/Instructions.cs

@ -1725,6 +1725,11 @@ namespace ICSharpCode.Decompiler.IL @@ -1725,6 +1725,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LockInstruction;
return o != null && this.onExpression.PerformMatch(o.onExpression, ref match) && this.body.PerformMatch(o.body, ref match);
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(onExpression.ResultType == StackType.O);
}
}
}
namespace ICSharpCode.Decompiler.IL
@ -1867,6 +1872,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1867,6 +1872,7 @@ namespace ICSharpCode.Decompiler.IL
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(resourceExpression.ResultType == StackType.O);
}
}
}
@ -3100,6 +3106,13 @@ namespace ICSharpCode.Decompiler.IL @@ -3100,6 +3106,13 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Cpblk;
return o != null && this.destAddress.PerformMatch(o.destAddress, ref match) && this.sourceAddress.PerformMatch(o.sourceAddress, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(destAddress.ResultType == StackType.I || destAddress.ResultType == StackType.Ref);
Debug.Assert(sourceAddress.ResultType == StackType.I || sourceAddress.ResultType == StackType.Ref);
Debug.Assert(size.ResultType == StackType.I4);
}
}
}
namespace ICSharpCode.Decompiler.IL
@ -3241,6 +3254,13 @@ namespace ICSharpCode.Decompiler.IL @@ -3241,6 +3254,13 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Initblk;
return o != null && this.address.PerformMatch(o.address, ref match) && this.value.PerformMatch(o.value, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(address.ResultType == StackType.I || address.ResultType == StackType.Ref);
Debug.Assert(value.ResultType == StackType.I4);
Debug.Assert(size.ResultType == StackType.I4);
}
}
}
namespace ICSharpCode.Decompiler.IL
@ -3589,6 +3609,11 @@ namespace ICSharpCode.Decompiler.IL @@ -3589,6 +3609,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdObj;
return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
}
}
}
namespace ICSharpCode.Decompiler.IL
@ -3720,6 +3745,12 @@ namespace ICSharpCode.Decompiler.IL @@ -3720,6 +3745,12 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StObj;
return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
Debug.Assert(value.ResultType == type.GetStackType());
}
}
}
namespace ICSharpCode.Decompiler.IL
@ -4238,6 +4269,11 @@ namespace ICSharpCode.Decompiler.IL @@ -4238,6 +4269,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLen;
return o != null && this.array.PerformMatch(o.array, ref match);
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(array.ResultType == StackType.O);
}
}
}
namespace ICSharpCode.Decompiler.IL
@ -4450,6 +4486,11 @@ namespace ICSharpCode.Decompiler.IL @@ -4450,6 +4486,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as ArrayToPointer;
return o != null && this.array.PerformMatch(o.array, ref match);
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(array.ResultType == StackType.O);
}
}
}
namespace ICSharpCode.Decompiler.IL
@ -4531,6 +4572,11 @@ namespace ICSharpCode.Decompiler.IL @@ -4531,6 +4572,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StringToInt;
return o != null && this.argument.PerformMatch(o.argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(argument.ResultType == StackType.O);
}
}
}
namespace ICSharpCode.Decompiler.IL

46
ICSharpCode.Decompiler/IL/Instructions.tt

@ -27,9 +27,9 @@ @@ -27,9 +27,9 @@
new OpCode("SimpleInstruction", "Instruction without any arguments",
AbstractBaseClass, CustomArguments(), CustomWriteTo, HasFlag("InstructionFlags.None")),
new OpCode("UnaryInstruction", "Instruction with a single argument",
AbstractBaseClass, CustomArguments("argument"), HasFlag("InstructionFlags.None")),
AbstractBaseClass, CustomArguments(("argument", null)), HasFlag("InstructionFlags.None")),
new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right",
AbstractBaseClass, CustomArguments("left", "right"), HasFlag("InstructionFlags.None")),
AbstractBaseClass, CustomArguments(("left", null), ("right", null)), HasFlag("InstructionFlags.None")),
new OpCode("CallInstruction", "Instruction with a list of arguments.",
AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}),
CustomWriteTo, MayThrow, SideEffect),
@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")),
new OpCode("compound", "Common instruction for compound assignments.",
CustomClassName("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags,
MayThrow, CustomArguments("target", "value"), HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo,
MayThrow, CustomArguments(("target", null), ("value", null)), HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator")),
new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")),
new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")),
@ -76,7 +76,7 @@ @@ -76,7 +76,7 @@
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch,
MatchCondition("this.TargetBlock == o.TargetBlock")),
new OpCode("leave", "Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction.",
CustomConstructor, CustomArguments("value"), UnconditionalBranch, MayBranch, CustomWriteTo, CustomComputeFlags,
CustomConstructor, CustomArguments(("value", null)), UnconditionalBranch, MayBranch, CustomWriteTo, CustomComputeFlags,
MatchCondition("this.TargetContainer == o.TargetContainer")),
new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>",
CustomClassName("IfInstruction"),
@ -116,12 +116,12 @@ @@ -116,12 +116,12 @@
MatchCondition("faultBlock.PerformMatch(o.faultBlock, ref match)")),
new OpCode("lock", "Lock statement", CustomClassName("LockInstruction"),
CustomChildren(new [] {
new ArgumentInfo("onExpression"),
new ArgumentInfo("onExpression") { ExpectedTypes = new[] { "O" }},
new ChildInfo("body")
}), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")),
new OpCode("using", "Using statement", CustomClassName("UsingInstruction"), HasVariableOperand("Store"),
CustomChildren(new [] {
new ArgumentInfo("resourceExpression"),
new ArgumentInfo("resourceExpression") { ExpectedTypes = new[] { "O" }},
new ChildInfo("body")
}), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")),
new OpCode("debug.break", "Breakpoint instruction",
@ -152,10 +152,10 @@ @@ -152,10 +152,10 @@
CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")),
new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine
+ "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign())",
CustomClassName("StLoc"), HasVariableOperand("Store", generateCheckInvariant: false), CustomArguments("value"),
CustomClassName("StLoc"), HasVariableOperand("Store", generateCheckInvariant: false), CustomArguments(("value", null)),
ResultType("variable.StackType")),
new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.",
CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")),
CustomClassName("AddressOf"), CustomArguments(("value", null)), ResultType("Ref")),
new OpCode("3vl.logic.and", "Three valued logic and. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.and(), does not have short-circuiting behavior.",
CustomClassName("ThreeValuedLogicAnd"), Binary, ResultType("O")),
new OpCode("3vl.logic.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.",
@ -186,16 +186,16 @@ @@ -186,16 +186,16 @@
new OpCode("localloc", "Allocates space in the stack frame",
CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow),
new OpCode("cpblk", "memcpy(destAddress, sourceAddress, size);",
CustomArguments("destAddress", "sourceAddress", "size"),
CustomArguments(("destAddress", new[] { "I", "Ref" }), ("sourceAddress", new[] { "I", "Ref" }), ("size", new[] { "I4" })),
MayThrow, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")),
new OpCode("initblk", "memset(address, value, size)",
CustomArguments("address", "value", "size"),
CustomArguments(("address", new[] { "I", "Ref" }), ("value", new[] { "I4" }), ("size", new[] { "I4" })),
MayThrow, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")),
new OpCode("ldflda", "Load address of instance field",
CustomClassName("LdFlda"), CustomArguments("target"), MayThrowIfNotDelayed, HasFieldOperand,
CustomClassName("LdFlda"), CustomArguments(("target", null)), MayThrowIfNotDelayed, HasFieldOperand,
ResultType("target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref")),
new OpCode("ldsflda", "Load static field address",
CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand),
@ -205,11 +205,11 @@ @@ -205,11 +205,11 @@
new OpCode("isinst", "Test if object is instance of class or interface.",
CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")),
new OpCode("ldobj", "Indirect load (ref/pointer dereference).",
CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
CustomClassName("LdObj"), CustomArguments(("target", new[] { "Ref", "I" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("stobj", "Indirect store (store to ref/pointer)." + Environment.NewLine
+ "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, sign/zero extended back to I4 based on type.GetSign())",
CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
CustomClassName("StObj"), CustomArguments(("target", new[] { "Ref", "I" }), ("value", new[] { "type.GetStackType()" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("box", "Boxes a value.",
@ -232,15 +232,15 @@ @@ -232,15 +232,15 @@
CustomClassName("SizeOf"), NoArguments, HasTypeOperand, ResultType("I4")),
new OpCode("ldlen", "Returns the length of an array as 'native unsigned int'.",
CustomClassName("LdLen"), CustomArguments("array"), CustomConstructor, CustomWriteTo, MayThrow),
CustomClassName("LdLen"), CustomArguments(("array", new[] { "O" })), CustomConstructor, CustomWriteTo, MayThrow),
new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix),
new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty." + Environment.NewLine
+ "Also used to convert a string to a reference to the first character.",
CustomArguments("array"), ResultType("Ref")),
CustomArguments(("array", new[] { "O" })), ResultType("Ref")),
new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).",
CustomArguments("argument"), CustomConstructor, CustomWriteTo, ResultType("I4")),
CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("I4")),
new OpCode("expression.tree.cast", "ILAst representation of Expression.Convert.",
CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"),
@ -256,11 +256,11 @@ @@ -256,11 +256,11 @@
new OpCode("yield.return", "Yield an element from an iterator.",
MayBranch, // yield return may end up returning if the consumer disposes the iterator
SideEffect, // consumer can have arbitrary side effects while we're yielding
CustomArguments("value"), VoidResult),
CustomArguments(("value", null)), VoidResult),
// note: "yield break" is always represented using a "leave" instruction
new OpCode("await", "C# await operator.",
SideEffect, // other code can run with arbitrary side effects while we're waiting
CustomArguments("value"), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")),
CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")),
// patterns
new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor),
@ -677,9 +677,9 @@ namespace ICSharpCode.Decompiler.IL @@ -677,9 +677,9 @@ namespace ICSharpCode.Decompiler.IL
opCode.WriteArguments.Add("output.Write(')');");
};
static Action<OpCode> CustomArguments(params string[] arguments)
static Action<OpCode> CustomArguments(params (string name, string[] expectedTypes)[] arguments)
{
return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg)).ToArray(), generateInline: true);
return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg.name) { ExpectedTypes = arg.expectedTypes }).ToArray(), generateInline: true);
}
class ChildInfo
@ -768,12 +768,16 @@ namespace ICSharpCode.Decompiler.IL @@ -768,12 +768,16 @@ namespace ICSharpCode.Decompiler.IL
+ "}");
}
if (children[i].ExpectedTypes?.Length > 0) {
string checkString = null;
foreach (var expectedType in children[i].ExpectedTypes) {
var expectedTypeCode = expectedType;
if (!expectedType.Contains("."))
expectedTypeCode = "StackType." + expectedTypeCode;
opCode.Invariants.Add("Debug.Assert(" + arg + ".ResultType == " + expectedTypeCode + ");");
if (checkString != null)
checkString += " || ";
checkString += arg + ".ResultType == " + expectedTypeCode;
}
opCode.Invariants.Add("Debug.Assert(" + checkString + ");");
}
}
opCode.WriteArguments.Add("output.Write(')');");

31
ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
// Copyright (c) 2014 Daniel Grunwald
//
// 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
{
public sealed partial class LdFlda
{
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(field.DeclaringType.IsReferenceType == true ? (target.ResultType == StackType.O) : (target.ResultType == StackType.I || target.ResultType == StackType.Ref));
}
}
}

4
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -386,8 +386,10 @@ namespace ICSharpCode.Decompiler.IL @@ -386,8 +386,10 @@ namespace ICSharpCode.Decompiler.IL
public bool MatchLdFld(out ILInstruction target, out IField field)
{
if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) {
target = ldflda.Target;
field = ldflda.Field;
if (field.DeclaringType.IsReferenceType == true || !ldflda.Target.MatchAddressOf(out target)) {
target = ldflda.Target;
}
return true;
}
target = null;

1
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -147,6 +147,7 @@ namespace ICSharpCode.Decompiler.IL @@ -147,6 +147,7 @@ namespace ICSharpCode.Decompiler.IL
}
Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases");
Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'");
Debug.Assert(this.IsLifted ? (value.ResultType == StackType.O) : (value.ResultType == StackType.I4 || value.ResultType == StackType.I8));
}
}

Loading…
Cancel
Save