Browse Source

Use ILCode instead of Code in type analysis.

pull/37/head
Daniel Grunwald 15 years ago
parent
commit
72bb24167e
  1. 292
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

292
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -106,6 +106,7 @@ namespace Decompiler
TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
{ {
switch (expr.Code) { switch (expr.Code) {
#region Logical operators
case ILCode.LogicNot: case ILCode.LogicNot:
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
@ -118,10 +119,9 @@ namespace Decompiler
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
} }
return typeSystem.Boolean; return typeSystem.Boolean;
} #endregion
switch ((Code)expr.Code) {
#region Variable load/store #region Variable load/store
case Code.Stloc: case ILCode.Stloc:
{ {
ILVariable v = (ILVariable)expr.Operand; ILVariable v = (ILVariable)expr.Operand;
if (forceInferChildren || v.Type == null) { if (forceInferChildren || v.Type == null) {
@ -131,7 +131,7 @@ namespace Decompiler
} }
return v.Type; return v.Type;
} }
case Code.Ldloc: case ILCode.Ldloc:
{ {
ILVariable v = (ILVariable)expr.Operand; ILVariable v = (ILVariable)expr.Operand;
if (v.Type == null) { if (v.Type == null) {
@ -143,20 +143,20 @@ namespace Decompiler
} }
return v.Type; return v.Type;
} }
case Code.Starg: case ILCode.Starg:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), ((ParameterReference)expr.Operand).ParameterType); InferTypeForExpression(expr.Arguments.Single(), ((ParameterReference)expr.Operand).ParameterType);
return null; return null;
case Code.Ldarg: case ILCode.Ldarg:
return ((ParameterReference)expr.Operand).ParameterType; return ((ParameterReference)expr.Operand).ParameterType;
case Code.Ldloca: case ILCode.Ldloca:
return new ByReferenceType(((ILVariable)expr.Operand).Type); return new ByReferenceType(((ILVariable)expr.Operand).Type);
case Code.Ldarga: case ILCode.Ldarga:
return new ByReferenceType(((ParameterReference)expr.Operand).ParameterType); return new ByReferenceType(((ParameterReference)expr.Operand).ParameterType);
#endregion #endregion
#region Call / NewObj #region Call / NewObj
case Code.Call: case ILCode.Call:
case Code.Callvirt: case ILCode.Callvirt:
{ {
MethodReference method = (MethodReference)expr.Operand; MethodReference method = (MethodReference)expr.Operand;
if (forceInferChildren) { if (forceInferChildren) {
@ -174,7 +174,7 @@ namespace Decompiler
} }
return SubstituteTypeArgs(method.ReturnType, method); return SubstituteTypeArgs(method.ReturnType, method);
} }
case Code.Newobj: case ILCode.Newobj:
{ {
MethodReference ctor = (MethodReference)expr.Operand; MethodReference ctor = (MethodReference)expr.Operand;
if (forceInferChildren) { if (forceInferChildren) {
@ -186,139 +186,139 @@ namespace Decompiler
} }
#endregion #endregion
#region Load/Store Fields #region Load/Store Fields
case Code.Ldfld: case ILCode.Ldfld:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType); InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType);
return GetFieldType((FieldReference)expr.Operand); return GetFieldType((FieldReference)expr.Operand);
case Code.Ldsfld: case ILCode.Ldsfld:
return GetFieldType((FieldReference)expr.Operand); return GetFieldType((FieldReference)expr.Operand);
case Code.Ldflda: case ILCode.Ldflda:
case Code.Ldsflda: case ILCode.Ldsflda:
return new ByReferenceType(GetFieldType((FieldReference)expr.Operand)); return new ByReferenceType(GetFieldType((FieldReference)expr.Operand));
case Code.Stfld: case ILCode.Stfld:
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType); InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType);
InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand)); InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand));
} }
return null; return null;
case Code.Stsfld: case ILCode.Stsfld:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments[0], GetFieldType((FieldReference)expr.Operand)); InferTypeForExpression(expr.Arguments[0], GetFieldType((FieldReference)expr.Operand));
return null; return null;
#endregion #endregion
#region Reference/Pointer instructions #region Reference/Pointer instructions
case Code.Ldind_I: case ILCode.Ldind_I:
case Code.Ldind_I1: case ILCode.Ldind_I1:
case Code.Ldind_I2: case ILCode.Ldind_I2:
case Code.Ldind_I4: case ILCode.Ldind_I4:
case Code.Ldind_I8: case ILCode.Ldind_I8:
case Code.Ldind_U1: case ILCode.Ldind_U1:
case Code.Ldind_U2: case ILCode.Ldind_U2:
case Code.Ldind_U4: case ILCode.Ldind_U4:
case Code.Ldind_R4: case ILCode.Ldind_R4:
case Code.Ldind_R8: case ILCode.Ldind_R8:
case Code.Ldind_Ref: case ILCode.Ldind_Ref:
return UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); return UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
case Code.Stind_I1: case ILCode.Stind_I1:
case Code.Stind_I2: case ILCode.Stind_I2:
case Code.Stind_I4: case ILCode.Stind_I4:
case Code.Stind_I8: case ILCode.Stind_I8:
case Code.Stind_R4: case ILCode.Stind_R4:
case Code.Stind_R8: case ILCode.Stind_R8:
case Code.Stind_I: case ILCode.Stind_I:
case Code.Stind_Ref: case ILCode.Stind_Ref:
if (forceInferChildren) { if (forceInferChildren) {
TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
InferTypeForExpression(expr.Arguments[1], elementType); InferTypeForExpression(expr.Arguments[1], elementType);
} }
return null; return null;
case Code.Ldobj: case ILCode.Ldobj:
return (TypeReference)expr.Operand; return (TypeReference)expr.Operand;
case Code.Stobj: case ILCode.Stobj:
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[1], (TypeReference)expr.Operand); InferTypeForExpression(expr.Arguments[1], (TypeReference)expr.Operand);
} }
return null; return null;
case Code.Initobj: case ILCode.Initobj:
return null; return null;
case Code.Localloc: case ILCode.Localloc:
return typeSystem.IntPtr; return typeSystem.IntPtr;
#endregion #endregion
#region Arithmetic instructions #region Arithmetic instructions
case Code.Not: // bitwise complement case ILCode.Not: // bitwise complement
case Code.Neg: case ILCode.Neg:
return InferTypeForExpression(expr.Arguments.Single(), expectedType); return InferTypeForExpression(expr.Arguments.Single(), expectedType);
case Code.Add: case ILCode.Add:
case Code.Sub: case ILCode.Sub:
case Code.Mul: case ILCode.Mul:
case Code.Or: case ILCode.Or:
case Code.And: case ILCode.And:
case Code.Xor: case ILCode.Xor:
return InferArgumentsInBinaryOperator(expr, null); return InferArgumentsInBinaryOperator(expr, null);
case Code.Add_Ovf: case ILCode.Add_Ovf:
case Code.Sub_Ovf: case ILCode.Sub_Ovf:
case Code.Mul_Ovf: case ILCode.Mul_Ovf:
case Code.Div: case ILCode.Div:
case Code.Rem: case ILCode.Rem:
return InferArgumentsInBinaryOperator(expr, true); return InferArgumentsInBinaryOperator(expr, true);
case Code.Add_Ovf_Un: case ILCode.Add_Ovf_Un:
case Code.Sub_Ovf_Un: case ILCode.Sub_Ovf_Un:
case Code.Mul_Ovf_Un: case ILCode.Mul_Ovf_Un:
case Code.Div_Un: case ILCode.Div_Un:
case Code.Rem_Un: case ILCode.Rem_Un:
return InferArgumentsInBinaryOperator(expr, false); return InferArgumentsInBinaryOperator(expr, false);
case Code.Shl: case ILCode.Shl:
case Code.Shr: case ILCode.Shr:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return InferTypeForExpression(expr.Arguments[0], typeSystem.Int32); return InferTypeForExpression(expr.Arguments[0], typeSystem.Int32);
case Code.Shr_Un: case ILCode.Shr_Un:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32); return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32);
#endregion #endregion
#region Constant loading instructions #region Constant loading instructions
case Code.Ldnull: case ILCode.Ldnull:
return typeSystem.Object; return typeSystem.Object;
case Code.Ldstr: case ILCode.Ldstr:
return typeSystem.String; return typeSystem.String;
case Code.Ldftn: case ILCode.Ldftn:
case Code.Ldvirtftn: case ILCode.Ldvirtftn:
return typeSystem.IntPtr; return typeSystem.IntPtr;
case Code.Ldc_I4: case ILCode.Ldc_I4:
return (IsIntegerOrEnum(expectedType) || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; return (IsIntegerOrEnum(expectedType) || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32;
case Code.Ldc_I8: case ILCode.Ldc_I8:
return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64; return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64;
case Code.Ldc_R4: case ILCode.Ldc_R4:
return typeSystem.Single; return typeSystem.Single;
case Code.Ldc_R8: case ILCode.Ldc_R8:
return typeSystem.Double; return typeSystem.Double;
case Code.Ldtoken: case ILCode.Ldtoken:
if (expr.Operand is TypeReference) if (expr.Operand is TypeReference)
return new TypeReference("System", "RuntimeTypeHandle", module, module, true); return new TypeReference("System", "RuntimeTypeHandle", module, module, true);
else if (expr.Operand is FieldReference) else if (expr.Operand is FieldReference)
return new TypeReference("System", "RuntimeFieldHandle", module, module, true); return new TypeReference("System", "RuntimeFieldHandle", module, module, true);
else else
return new TypeReference("System", "RuntimeMethodHandle", module, module, true); return new TypeReference("System", "RuntimeMethodHandle", module, module, true);
case Code.Arglist: case ILCode.Arglist:
return new TypeReference("System", "RuntimeArgumentHandle", module, module, true); return new TypeReference("System", "RuntimeArgumentHandle", module, module, true);
#endregion #endregion
#region Array instructions #region Array instructions
case Code.Newarr: case ILCode.Newarr:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32); InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32);
return new ArrayType((TypeReference)expr.Operand); return new ArrayType((TypeReference)expr.Operand);
case Code.Ldlen: case ILCode.Ldlen:
return typeSystem.Int32; return typeSystem.Int32;
case Code.Ldelem_U1: case ILCode.Ldelem_U1:
case Code.Ldelem_U2: case ILCode.Ldelem_U2:
case Code.Ldelem_U4: case ILCode.Ldelem_U4:
case Code.Ldelem_I1: case ILCode.Ldelem_I1:
case Code.Ldelem_I2: case ILCode.Ldelem_I2:
case Code.Ldelem_I4: case ILCode.Ldelem_I4:
case Code.Ldelem_I8: case ILCode.Ldelem_I8:
case Code.Ldelem_I: case ILCode.Ldelem_I:
case Code.Ldelem_Ref: case ILCode.Ldelem_Ref:
{ {
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
if (forceInferChildren) { if (forceInferChildren) {
@ -327,27 +327,27 @@ namespace Decompiler
} }
return arrayType != null ? arrayType.ElementType : null; return arrayType != null ? arrayType.ElementType : null;
} }
case Code.Ldelem_Any: case ILCode.Ldelem_Any:
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
} }
return (TypeReference)expr.Operand; return (TypeReference)expr.Operand;
case Code.Ldelema: case ILCode.Ldelema:
{ {
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return arrayType != null ? new ByReferenceType(arrayType.ElementType) : null; return arrayType != null ? new ByReferenceType(arrayType.ElementType) : null;
} }
case Code.Stelem_I: case ILCode.Stelem_I:
case Code.Stelem_I1: case ILCode.Stelem_I1:
case Code.Stelem_I2: case ILCode.Stelem_I2:
case Code.Stelem_I4: case ILCode.Stelem_I4:
case Code.Stelem_I8: case ILCode.Stelem_I8:
case Code.Stelem_R4: case ILCode.Stelem_R4:
case Code.Stelem_R8: case ILCode.Stelem_R8:
case Code.Stelem_Ref: case ILCode.Stelem_Ref:
case Code.Stelem_Any: case ILCode.Stelem_Any:
if (forceInferChildren) { if (forceInferChildren) {
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
@ -358,107 +358,107 @@ namespace Decompiler
return null; return null;
#endregion #endregion
#region Conversion instructions #region Conversion instructions
case Code.Conv_I1: case ILCode.Conv_I1:
case Code.Conv_Ovf_I1: case ILCode.Conv_Ovf_I1:
return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte; return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte;
case Code.Conv_I2: case ILCode.Conv_I2:
case Code.Conv_Ovf_I2: case ILCode.Conv_Ovf_I2:
return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16; return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16;
case Code.Conv_I4: case ILCode.Conv_I4:
case Code.Conv_Ovf_I4: case ILCode.Conv_Ovf_I4:
return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32; return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32;
case Code.Conv_I8: case ILCode.Conv_I8:
case Code.Conv_Ovf_I8: case ILCode.Conv_Ovf_I8:
return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64;
case Code.Conv_U1: case ILCode.Conv_U1:
case Code.Conv_Ovf_U1: case ILCode.Conv_Ovf_U1:
return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte; return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte;
case Code.Conv_U2: case ILCode.Conv_U2:
case Code.Conv_Ovf_U2: case ILCode.Conv_Ovf_U2:
return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16; return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16;
case Code.Conv_U4: case ILCode.Conv_U4:
case Code.Conv_Ovf_U4: case ILCode.Conv_Ovf_U4:
return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32; return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32;
case Code.Conv_U8: case ILCode.Conv_U8:
case Code.Conv_Ovf_U8: case ILCode.Conv_Ovf_U8:
return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64; return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64;
case Code.Conv_I: case ILCode.Conv_I:
case Code.Conv_Ovf_I: case ILCode.Conv_Ovf_I:
return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == true) ? expectedType : typeSystem.IntPtr; return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == true) ? expectedType : typeSystem.IntPtr;
case Code.Conv_U: case ILCode.Conv_U:
case Code.Conv_Ovf_U: case ILCode.Conv_Ovf_U:
return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == false) ? expectedType : typeSystem.UIntPtr; return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == false) ? expectedType : typeSystem.UIntPtr;
case Code.Conv_R4: case ILCode.Conv_R4:
return typeSystem.Single; return typeSystem.Single;
case Code.Conv_R8: case ILCode.Conv_R8:
return typeSystem.Double; return typeSystem.Double;
case Code.Conv_R_Un: case ILCode.Conv_R_Un:
return (expectedType == typeSystem.Single) ? typeSystem.Single : typeSystem.Double; return (expectedType == typeSystem.Single) ? typeSystem.Single : typeSystem.Double;
case Code.Castclass: case ILCode.Castclass:
case Code.Isinst: case ILCode.Isinst:
case Code.Unbox_Any: case ILCode.Unbox_Any:
return (TypeReference)expr.Operand; return (TypeReference)expr.Operand;
case Code.Box: case ILCode.Box:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), (TypeReference)expr.Operand); InferTypeForExpression(expr.Arguments.Single(), (TypeReference)expr.Operand);
return (TypeReference)expr.Operand; return (TypeReference)expr.Operand;
#endregion #endregion
#region Comparison instructions #region Comparison instructions
case Code.Ceq: case ILCode.Ceq:
if (forceInferChildren) if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, null); InferArgumentsInBinaryOperator(expr, null);
return typeSystem.Boolean; return typeSystem.Boolean;
case Code.Clt: case ILCode.Clt:
case Code.Cgt: case ILCode.Cgt:
if (forceInferChildren) if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, true); InferArgumentsInBinaryOperator(expr, true);
return typeSystem.Boolean; return typeSystem.Boolean;
case Code.Clt_Un: case ILCode.Clt_Un:
case Code.Cgt_Un: case ILCode.Cgt_Un:
if (forceInferChildren) if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, false); InferArgumentsInBinaryOperator(expr, false);
return typeSystem.Boolean; return typeSystem.Boolean;
#endregion #endregion
#region Branch instructions #region Branch instructions
case Code.Beq: case ILCode.Beq:
case Code.Bne_Un: case ILCode.Bne_Un:
if (forceInferChildren) if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, null); InferArgumentsInBinaryOperator(expr, null);
return null; return null;
case Code.Brtrue: case ILCode.Brtrue:
case Code.Brfalse: case ILCode.Brfalse:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
return null; return null;
case Code.Blt: case ILCode.Blt:
case Code.Ble: case ILCode.Ble:
case Code.Bgt: case ILCode.Bgt:
case Code.Bge: case ILCode.Bge:
if (forceInferChildren) if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, true); InferArgumentsInBinaryOperator(expr, true);
return null; return null;
case Code.Blt_Un: case ILCode.Blt_Un:
case Code.Ble_Un: case ILCode.Ble_Un:
case Code.Bgt_Un: case ILCode.Bgt_Un:
case Code.Bge_Un: case ILCode.Bge_Un:
if (forceInferChildren) if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, false); InferArgumentsInBinaryOperator(expr, false);
return null; return null;
case Code.Br: case ILCode.Br:
case Code.Leave: case ILCode.Leave:
case Code.Endfinally: case ILCode.Endfinally:
case Code.Switch: case ILCode.Switch:
case Code.Throw: case ILCode.Throw:
case Code.Rethrow: case ILCode.Rethrow:
return null; return null;
case Code.Ret: case ILCode.Ret:
if (forceInferChildren && expr.Arguments.Count == 1) if (forceInferChildren && expr.Arguments.Count == 1)
InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType); InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType);
return null; return null;
#endregion #endregion
case Code.Pop: case ILCode.Pop:
return null; return null;
case Code.Dup: case ILCode.Dup:
return InferTypeForExpression(expr.Arguments.Single(), expectedType); return InferTypeForExpression(expr.Arguments.Single(), expectedType);
default: default:
Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName()); Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName());

Loading…
Cancel
Save