summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs')
-rw-r--r--ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs1294
1 files changed, 1294 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
new file mode 100644
index 00000000..1931d390
--- /dev/null
+++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
@@ -0,0 +1,1294 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.ILAst
+{
+ /// <summary>
+ /// Assigns C# types to IL expressions.
+ /// </summary>
+ /// <remarks>
+ /// Types are inferred in a bidirectional manner:
+ /// The expected type flows from the outside to the inside, the actual inferred type flows from the inside to the outside.
+ /// </remarks>
+ public class TypeAnalysis
+ {
+ public static void Run(DecompilerContext context, ILBlock method)
+ {
+ TypeAnalysis ta = new TypeAnalysis();
+ ta.context = context;
+ ta.module = context.CurrentMethod.Module;
+ ta.typeSystem = ta.module.TypeSystem;
+ ta.method = method;
+ ta.CreateDependencyGraph(method);
+ ta.IdentifySingleLoadVariables();
+ ta.RunInference();
+ }
+
+ sealed class ExpressionToInfer
+ {
+ public ILExpression Expression;
+
+ public bool Done;
+
+ /// <summary>
+ /// Set for assignment expressions that should wait until the variable type is available
+ /// from the context where the variable is used.
+ /// </summary>
+ public ILVariable DependsOnSingleLoad;
+
+ /// <summary>
+ /// The list variables that are read by this expression.
+ /// </summary>
+ public List<ILVariable> Dependencies = new List<ILVariable>();
+
+ public override string ToString()
+ {
+ if (Done)
+ return "[Done] " + Expression.ToString();
+ else
+ return Expression.ToString();
+ }
+
+ }
+
+ DecompilerContext context;
+ TypeSystem typeSystem;
+ ILBlock method;
+ ModuleDefinition module;
+ List<ExpressionToInfer> allExpressions = new List<ExpressionToInfer>();
+ DefaultDictionary<ILVariable, List<ExpressionToInfer>> assignmentExpressions = new DefaultDictionary<ILVariable, List<ExpressionToInfer>>(_ => new List<ExpressionToInfer>());
+ HashSet<ILVariable> singleLoadVariables = new HashSet<ILVariable>();
+
+ #region CreateDependencyGraph
+ /// <summary>
+ /// Creates the "ExpressionToInfer" instances (=nodes in dependency graph)
+ /// </summary>
+ /// <remarks>
+ /// We are using a dependency graph to ensure that expressions are analyzed in the correct order.
+ /// </remarks>
+ void CreateDependencyGraph(ILNode node)
+ {
+ ILCondition cond = node as ILCondition;
+ if (cond != null) {
+ cond.Condition.ExpectedType = typeSystem.Boolean;
+ }
+ ILWhileLoop loop = node as ILWhileLoop;
+ if (loop != null && loop.Condition != null) {
+ loop.Condition.ExpectedType = typeSystem.Boolean;
+ }
+ ILTryCatchBlock.CatchBlock catchBlock = node as ILTryCatchBlock.CatchBlock;
+ if (catchBlock != null && catchBlock.ExceptionVariable != null && catchBlock.ExceptionType != null && catchBlock.ExceptionVariable.Type == null) {
+ catchBlock.ExceptionVariable.Type = catchBlock.ExceptionType;
+ }
+ ILExpression expr = node as ILExpression;
+ if (expr != null) {
+ ExpressionToInfer expressionToInfer = new ExpressionToInfer();
+ expressionToInfer.Expression = expr;
+ allExpressions.Add(expressionToInfer);
+ FindNestedAssignments(expr, expressionToInfer);
+
+ if (expr.Code == ILCode.Stloc && ((ILVariable)expr.Operand).Type == null)
+ assignmentExpressions[(ILVariable)expr.Operand].Add(expressionToInfer);
+ return;
+ }
+ foreach (ILNode child in node.GetChildren()) {
+ CreateDependencyGraph(child);
+ }
+ }
+
+ void FindNestedAssignments(ILExpression expr, ExpressionToInfer parent)
+ {
+ foreach (ILExpression arg in expr.Arguments) {
+ if (arg.Code == ILCode.Stloc) {
+ ExpressionToInfer expressionToInfer = new ExpressionToInfer();
+ expressionToInfer.Expression = arg;
+ allExpressions.Add(expressionToInfer);
+ FindNestedAssignments(arg, expressionToInfer);
+ ILVariable v = (ILVariable)arg.Operand;
+ if (v.Type == null) {
+ assignmentExpressions[v].Add(expressionToInfer);
+ // the instruction that consumes the stloc result is handled as if it was reading the variable
+ parent.Dependencies.Add(v);
+ }
+ } else {
+ ILVariable v;
+ if (arg.Match(ILCode.Ldloc, out v) && v.Type == null) {
+ parent.Dependencies.Add(v);
+ }
+ FindNestedAssignments(arg, parent);
+ }
+ }
+ }
+ #endregion
+
+ void IdentifySingleLoadVariables()
+ {
+ // Find all variables that are assigned to exactly a single time:
+ var q = from expr in allExpressions
+ from v in expr.Dependencies
+ group expr by v;
+ foreach (var g in q.ToArray()) {
+ ILVariable v = g.Key;
+ if (g.Count() == 1 && g.Single().Expression.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e.Operand == v) == 1) {
+ singleLoadVariables.Add(v);
+ // Mark the assignments as dependent on the type from the single load:
+ foreach (var assignment in assignmentExpressions[v]) {
+ assignment.DependsOnSingleLoad = v;
+ }
+ }
+ }
+ }
+
+ void RunInference()
+ {
+ int numberOfExpressionsAlreadyInferred = 0;
+ // Two flags that allow resolving cycles:
+ bool ignoreSingleLoadDependencies = false;
+ bool assignVariableTypesBasedOnPartialInformation = false;
+ while (numberOfExpressionsAlreadyInferred < allExpressions.Count) {
+ int oldCount = numberOfExpressionsAlreadyInferred;
+ foreach (ExpressionToInfer expr in allExpressions) {
+ if (!expr.Done && expr.Dependencies.TrueForAll(v => v.Type != null || singleLoadVariables.Contains(v))
+ && (expr.DependsOnSingleLoad == null || expr.DependsOnSingleLoad.Type != null || ignoreSingleLoadDependencies))
+ {
+ RunInference(expr.Expression);
+ expr.Done = true;
+ numberOfExpressionsAlreadyInferred++;
+ }
+ }
+ if (numberOfExpressionsAlreadyInferred == oldCount) {
+ if (ignoreSingleLoadDependencies) {
+ if (assignVariableTypesBasedOnPartialInformation)
+ throw new InvalidOperationException("Could not infer any expression");
+ else
+ assignVariableTypesBasedOnPartialInformation = true;
+ } else {
+ // We have a cyclic dependency; we'll try if we can resolve it by ignoring single-load dependencies.
+ // This can happen if the variable was not actually assigned an expected type by the single-load instruction.
+ ignoreSingleLoadDependencies = true;
+ continue;
+ }
+ } else {
+ assignVariableTypesBasedOnPartialInformation = false;
+ ignoreSingleLoadDependencies = false;
+ }
+ // Now infer types for variables:
+ foreach (var pair in assignmentExpressions) {
+ ILVariable v = pair.Key;
+ if (v.Type == null && (assignVariableTypesBasedOnPartialInformation ? pair.Value.Any(e => e.Done) : pair.Value.All(e => e.Done))) {
+ TypeReference inferredType = null;
+ foreach (ExpressionToInfer expr in pair.Value) {
+ Debug.Assert(expr.Expression.Code == ILCode.Stloc);
+ ILExpression assignedValue = expr.Expression.Arguments.Single();
+ if (assignedValue.InferredType != null) {
+ if (inferredType == null) {
+ inferredType = assignedValue.InferredType;
+ } else {
+ // pick the common base type
+ inferredType = TypeWithMoreInformation(inferredType, assignedValue.InferredType);
+ }
+ }
+ }
+ if (inferredType == null)
+ inferredType = typeSystem.Object;
+ v.Type = inferredType;
+ // Assign inferred type to all the assignments (in case they used different inferred types):
+ foreach (ExpressionToInfer expr in pair.Value) {
+ expr.Expression.InferredType = inferredType;
+ // re-infer if the expected type has changed
+ InferTypeForExpression(expr.Expression.Arguments.Single(), inferredType);
+ }
+ }
+ }
+ }
+ }
+
+ void RunInference(ILExpression expr)
+ {
+ bool anyArgumentIsMissingExpectedType = expr.Arguments.Any(a => a.ExpectedType == null);
+ if (expr.InferredType == null || anyArgumentIsMissingExpectedType)
+ InferTypeForExpression(expr, expr.ExpectedType, forceInferChildren: anyArgumentIsMissingExpectedType);
+ foreach (var arg in expr.Arguments) {
+ if (arg.Code != ILCode.Stloc) {
+ RunInference(arg);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Infers the C# type of <paramref name="expr"/>.
+ /// </summary>
+ /// <param name="expr">The expression</param>
+ /// <param name="expectedType">The expected type of the expression</param>
+ /// <param name="forceInferChildren">Whether direct children should be inferred even if its not necessary. (does not apply to nested children!)</param>
+ /// <returns>The inferred type</returns>
+ TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
+ {
+ if (expectedType != null && !IsSameType(expr.ExpectedType, expectedType)) {
+ expr.ExpectedType = expectedType;
+ if (expr.Code != ILCode.Stloc) // stloc is special case and never gets re-evaluated
+ forceInferChildren = true;
+ }
+ if (forceInferChildren || expr.InferredType == null)
+ expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren);
+ return expr.InferredType;
+ }
+
+ TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
+ {
+ switch (expr.Code) {
+ #region Logical operators
+ case ILCode.LogicNot:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
+ }
+ return typeSystem.Boolean;
+ case ILCode.LogicAnd:
+ case ILCode.LogicOr:
+ // if Operand is set the logic and/or expression is a custom operator
+ // we can deal with it the same as a normal invocation.
+ if (expr.Operand != null)
+ goto case ILCode.Call;
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean);
+ }
+ return typeSystem.Boolean;
+ case ILCode.TernaryOp:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
+ }
+ return InferBinaryArguments(expr.Arguments[1], expr.Arguments[2], expectedType, forceInferChildren);
+ case ILCode.NullCoalescing:
+ return InferBinaryArguments(expr.Arguments[0], expr.Arguments[1], expectedType, forceInferChildren);
+ #endregion
+ #region Variable load/store
+ case ILCode.Stloc:
+ {
+ ILVariable v = (ILVariable)expr.Operand;
+ if (forceInferChildren) {
+ // do not use 'expectedType' in here!
+ InferTypeForExpression(expr.Arguments.Single(), v.Type);
+ }
+ return v.Type;
+ }
+ case ILCode.Ldloc:
+ {
+ ILVariable v = (ILVariable)expr.Operand;
+ if (v.Type == null && singleLoadVariables.Contains(v)) {
+ v.Type = expectedType;
+ }
+ return v.Type;
+ }
+ case ILCode.Ldloca:
+ {
+ ILVariable v = (ILVariable)expr.Operand;
+ if (v.Type != null)
+ return new ByReferenceType(v.Type);
+ else
+ return null;
+ }
+ #endregion
+ #region Call / NewObj
+ case ILCode.Call:
+ case ILCode.Callvirt:
+ case ILCode.CallGetter:
+ case ILCode.CallvirtGetter:
+ case ILCode.CallSetter:
+ case ILCode.CallvirtSetter:
+ {
+ MethodReference method = (MethodReference)expr.Operand;
+ if (forceInferChildren) {
+ for (int i = 0; i < expr.Arguments.Count; i++) {
+ if (i == 0 && method.HasThis) {
+ InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(method.DeclaringType, expr.GetPrefix(ILCode.Constrained)));
+ } else {
+ InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(method.Parameters[method.HasThis ? i - 1 : i].ParameterType, method));
+ }
+ }
+ }
+ if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
+ return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method);
+ } else {
+ return SubstituteTypeArgs(method.ReturnType, method);
+ }
+ }
+ case ILCode.Newobj:
+ {
+ MethodReference ctor = (MethodReference)expr.Operand;
+ if (forceInferChildren) {
+ for (int i = 0; i < ctor.Parameters.Count; i++) {
+ InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(ctor.Parameters[i].ParameterType, ctor));
+ }
+ }
+ return ctor.DeclaringType;
+ }
+ case ILCode.InitObject:
+ case ILCode.InitCollection:
+ return InferTypeForExpression(expr.Arguments[0], expectedType);
+ case ILCode.InitializedObject:
+ // expectedType should always be known due to the parent method call / property setter
+ Debug.Assert(expectedType != null);
+ return expectedType;
+ #endregion
+ #region Load/Store Fields
+ case ILCode.Ldfld:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained)));
+ }
+ return GetFieldType((FieldReference)expr.Operand);
+ case ILCode.Ldsfld:
+ return GetFieldType((FieldReference)expr.Operand);
+ case ILCode.Ldflda:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained)));
+ }
+ return new ByReferenceType(GetFieldType((FieldReference)expr.Operand));
+ case ILCode.Ldsflda:
+ return new ByReferenceType(GetFieldType((FieldReference)expr.Operand));
+ case ILCode.Stfld:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained)));
+ InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand));
+ }
+ return GetFieldType((FieldReference)expr.Operand);
+ case ILCode.Stsfld:
+ if (forceInferChildren)
+ InferTypeForExpression(expr.Arguments[0], GetFieldType((FieldReference)expr.Operand));
+ return GetFieldType((FieldReference)expr.Operand);
+ #endregion
+ #region Reference/Pointer instructions
+ case ILCode.Ldind_Ref:
+ return UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
+ case ILCode.Stind_Ref:
+ if (forceInferChildren) {
+ TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
+ InferTypeForExpression(expr.Arguments[1], elementType);
+ }
+ return null;
+ case ILCode.Ldobj:
+ {
+ TypeReference type = (TypeReference)expr.Operand;
+ var argType = InferTypeForExpression(expr.Arguments[0], null);
+ if (argType is PointerType || argType is ByReferenceType) {
+ var elementType = ((TypeSpecification)argType).ElementType;
+ int infoAmount = GetInformationAmount(elementType);
+ if (infoAmount == 1 && GetInformationAmount(type) == 8) {
+ // A bool can be loaded from both bytes and sbytes.
+ type = elementType;
+ }
+ if (infoAmount >= 8 && infoAmount <= 64 && infoAmount == GetInformationAmount(type)) {
+ // An integer can be loaded as another integer of the same size.
+ // For integers smaller than 32 bit, the signs must match (as loading performs sign extension)
+ bool? elementTypeIsSigned = IsSigned(elementType);
+ bool? typeIsSigned = IsSigned(type);
+ if (elementTypeIsSigned != null && typeIsSigned != null) {
+ if (infoAmount >= 32 || elementTypeIsSigned == typeIsSigned)
+ type = elementType;
+ }
+ }
+ }
+ if (argType is PointerType)
+ InferTypeForExpression(expr.Arguments[0], new PointerType(type));
+ else
+ InferTypeForExpression(expr.Arguments[0], new ByReferenceType(type));
+ return type;
+ }
+ case ILCode.Stobj:
+ {
+ TypeReference operandType = (TypeReference)expr.Operand;
+ TypeReference pointerType = InferTypeForExpression(expr.Arguments[0], new ByReferenceType(operandType));
+ TypeReference elementType;
+ if (pointerType is PointerType)
+ elementType = ((PointerType)pointerType).ElementType;
+ else if (pointerType is ByReferenceType)
+ elementType = ((ByReferenceType)pointerType).ElementType;
+ else
+ elementType = null;
+ if (elementType != null) {
+ // An integer can be stored in any other integer of the same size.
+ int infoAmount = GetInformationAmount(elementType);
+ if (infoAmount == 1 && GetInformationAmount(operandType) == 8)
+ operandType = elementType;
+ else if (infoAmount == GetInformationAmount(operandType) && IsSigned(elementType) != null && IsSigned(operandType) != null)
+ operandType = elementType;
+ }
+ if (forceInferChildren) {
+ if (pointerType is PointerType)
+ InferTypeForExpression(expr.Arguments[0], new PointerType(operandType));
+ else if (!IsSameType(operandType, expr.Operand as TypeReference))
+ InferTypeForExpression(expr.Arguments[0], new ByReferenceType(operandType));
+ InferTypeForExpression(expr.Arguments[1], operandType);
+ }
+ return operandType;
+ }
+ case ILCode.Initobj:
+ return null;
+ case ILCode.DefaultValue:
+ return (TypeReference)expr.Operand;
+ case ILCode.Localloc:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], null);
+ }
+ if (expectedType is PointerType)
+ return expectedType;
+ else
+ return typeSystem.IntPtr;
+ case ILCode.Sizeof:
+ return typeSystem.Int32;
+ case ILCode.PostIncrement:
+ case ILCode.PostIncrement_Ovf:
+ case ILCode.PostIncrement_Ovf_Un:
+ {
+ TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
+ if (forceInferChildren && elementType != null) {
+ // Assign expected type to the child expression
+ InferTypeForExpression(expr.Arguments[0], new ByReferenceType(elementType));
+ }
+ return elementType;
+ }
+ case ILCode.Mkrefany:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], (TypeReference)expr.Operand);
+ }
+ return typeSystem.TypedReference;
+ case ILCode.Refanytype:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
+ }
+ return new TypeReference("System", "RuntimeTypeHandle", module, module.TypeSystem.Corlib, true);
+ case ILCode.Refanyval:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
+ }
+ return new ByReferenceType((TypeReference)expr.Operand);
+ case ILCode.AddressOf:
+ {
+ TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType));
+ return t != null ? new ByReferenceType(t) : null;
+ }
+ case ILCode.ValueOf:
+ return GetNullableTypeArgument(InferTypeForExpression(expr.Arguments[0], CreateNullableType(expectedType)));
+ case ILCode.NullableOf:
+ return CreateNullableType(InferTypeForExpression(expr.Arguments[0], GetNullableTypeArgument(expectedType)));
+ #endregion
+ #region Arithmetic instructions
+ case ILCode.Not: // bitwise complement
+ case ILCode.Neg:
+ return InferTypeForExpression(expr.Arguments.Single(), expectedType);
+ case ILCode.Add:
+ return InferArgumentsInAddition(expr, null, expectedType);
+ case ILCode.Sub:
+ return InferArgumentsInSubtraction(expr, null, expectedType);
+ case ILCode.Mul:
+ case ILCode.Or:
+ case ILCode.And:
+ case ILCode.Xor:
+ return InferArgumentsInBinaryOperator(expr, null, expectedType);
+ case ILCode.Add_Ovf:
+ return InferArgumentsInAddition(expr, true, expectedType);
+ case ILCode.Sub_Ovf:
+ return InferArgumentsInSubtraction(expr, true, expectedType);
+ case ILCode.Mul_Ovf:
+ case ILCode.Div:
+ case ILCode.Rem:
+ return InferArgumentsInBinaryOperator(expr, true, expectedType);
+ case ILCode.Add_Ovf_Un:
+ return InferArgumentsInAddition(expr, false, expectedType);
+ case ILCode.Sub_Ovf_Un:
+ return InferArgumentsInSubtraction(expr, false, expectedType);
+ case ILCode.Mul_Ovf_Un:
+ case ILCode.Div_Un:
+ case ILCode.Rem_Un:
+ return InferArgumentsInBinaryOperator(expr, false, expectedType);
+ case ILCode.Shl:
+ if (forceInferChildren)
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
+ if (expectedType != null && (
+ expectedType.MetadataType == MetadataType.Int32 || expectedType.MetadataType == MetadataType.UInt32 ||
+ expectedType.MetadataType == MetadataType.Int64 || expectedType.MetadataType == MetadataType.UInt64)
+ )
+ return NumericPromotion(InferTypeForExpression(expr.Arguments[0], expectedType));
+ else
+ return NumericPromotion(InferTypeForExpression(expr.Arguments[0], null));
+ case ILCode.Shr:
+ case ILCode.Shr_Un:
+ {
+ if (forceInferChildren)
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
+ TypeReference type = NumericPromotion(InferTypeForExpression(expr.Arguments[0], null));
+ if (type == null)
+ return null;
+ TypeReference expectedInputType = null;
+ switch (type.MetadataType) {
+ case MetadataType.Int32:
+ if (expr.Code == ILCode.Shr_Un)
+ expectedInputType = typeSystem.UInt32;
+ break;
+ case MetadataType.UInt32:
+ if (expr.Code == ILCode.Shr)
+ expectedInputType = typeSystem.Int32;
+ break;
+ case MetadataType.Int64:
+ if (expr.Code == ILCode.Shr_Un)
+ expectedInputType = typeSystem.UInt64;
+ break;
+ case MetadataType.UInt64:
+ if (expr.Code == ILCode.Shr)
+ expectedInputType = typeSystem.UInt64;
+ break;
+ }
+ if (expectedInputType != null) {
+ InferTypeForExpression(expr.Arguments[0], expectedInputType);
+ return expectedInputType;
+ } else {
+ return type;
+ }
+ }
+ case ILCode.CompoundAssignment:
+ {
+ var op = expr.Arguments[0];
+ if (op.Code == ILCode.NullableOf) op = op.Arguments[0].Arguments[0];
+ var varType = InferTypeForExpression(op.Arguments[0], null);
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], varType);
+ }
+ return varType;
+ }
+ #endregion
+ #region Constant loading instructions
+ case ILCode.Ldnull:
+ return typeSystem.Object;
+ case ILCode.Ldstr:
+ return typeSystem.String;
+ case ILCode.Ldftn:
+ case ILCode.Ldvirtftn:
+ return typeSystem.IntPtr;
+ case ILCode.Ldc_I4:
+ if (IsBoolean(expectedType) && ((int)expr.Operand == 0 || (int)expr.Operand == 1))
+ return typeSystem.Boolean;
+ if (expectedType is PointerType && (int)expr.Operand == 0)
+ return expectedType;
+ if (IsIntegerOrEnum(expectedType) && OperandFitsInType(expectedType, (int)expr.Operand))
+ return expectedType;
+ else
+ return typeSystem.Int32;
+ case ILCode.Ldc_I8:
+ if (expectedType is PointerType && (long)expr.Operand == 0)
+ return expectedType;
+ if (IsIntegerOrEnum(expectedType) && GetInformationAmount(expectedType) >= NativeInt)
+ return expectedType;
+ else
+ return typeSystem.Int64;
+ case ILCode.Ldc_R4:
+ return typeSystem.Single;
+ case ILCode.Ldc_R8:
+ return typeSystem.Double;
+ case ILCode.Ldc_Decimal:
+ return new TypeReference("System", "Decimal", module, module.TypeSystem.Corlib, true);
+ case ILCode.Ldtoken:
+ if (expr.Operand is TypeReference)
+ return new TypeReference("System", "RuntimeTypeHandle", module, module.TypeSystem.Corlib, true);
+ else if (expr.Operand is FieldReference)
+ return new TypeReference("System", "RuntimeFieldHandle", module, module.TypeSystem.Corlib, true);
+ else
+ return new TypeReference("System", "RuntimeMethodHandle", module, module.TypeSystem.Corlib, true);
+ case ILCode.Arglist:
+ return new TypeReference("System", "RuntimeArgumentHandle", module, module.TypeSystem.Corlib, true);
+ #endregion
+ #region Array instructions
+ case ILCode.Newarr:
+ if (forceInferChildren) {
+ var lengthType = InferTypeForExpression(expr.Arguments.Single(), null);
+ if (lengthType == typeSystem.IntPtr) {
+ lengthType = typeSystem.Int64;
+ } else if (lengthType == typeSystem.UIntPtr) {
+ lengthType = typeSystem.UInt64;
+ } else if (lengthType != typeSystem.UInt32 && lengthType != typeSystem.Int64 && lengthType != typeSystem.UInt64) {
+ lengthType = typeSystem.Int32;
+ }
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments.Single(), lengthType);
+ }
+ }
+ return new ArrayType((TypeReference)expr.Operand);
+ case ILCode.InitArray:
+ var operandAsArrayType = (ArrayType)expr.Operand;
+ if (forceInferChildren)
+ {
+ foreach (ILExpression arg in expr.Arguments)
+ InferTypeForExpression(arg, operandAsArrayType.ElementType);
+ }
+ return operandAsArrayType;
+ case ILCode.Ldlen:
+ return typeSystem.Int32;
+ case ILCode.Ldelem_U1:
+ case ILCode.Ldelem_U2:
+ case ILCode.Ldelem_U4:
+ case ILCode.Ldelem_I1:
+ case ILCode.Ldelem_I2:
+ case ILCode.Ldelem_I4:
+ case ILCode.Ldelem_I8:
+ case ILCode.Ldelem_R4:
+ case ILCode.Ldelem_R8:
+ case ILCode.Ldelem_I:
+ case ILCode.Ldelem_Ref:
+ {
+ ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
+ }
+ return arrayType != null ? arrayType.ElementType : null;
+ }
+ case ILCode.Ldelem_Any:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
+ }
+ return (TypeReference)expr.Operand;
+ case ILCode.Ldelema:
+ {
+ ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
+ if (forceInferChildren)
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
+ return arrayType != null ? new ByReferenceType(arrayType.ElementType) : null;
+ }
+ case ILCode.Stelem_I:
+ case ILCode.Stelem_I1:
+ case ILCode.Stelem_I2:
+ case ILCode.Stelem_I4:
+ case ILCode.Stelem_I8:
+ case ILCode.Stelem_R4:
+ case ILCode.Stelem_R8:
+ case ILCode.Stelem_Ref:
+ case ILCode.Stelem_Any:
+ {
+ ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
+ if (arrayType != null) {
+ InferTypeForExpression(expr.Arguments[2], arrayType.ElementType);
+ }
+ }
+ return arrayType != null ? arrayType.ElementType : null;
+ }
+ #endregion
+ #region Conversion instructions
+ case ILCode.Conv_I1:
+ case ILCode.Conv_Ovf_I1:
+ case ILCode.Conv_Ovf_I1_Un:
+ return HandleConversion(8, true, expr.Arguments[0], expectedType, typeSystem.SByte);
+ case ILCode.Conv_I2:
+ case ILCode.Conv_Ovf_I2:
+ case ILCode.Conv_Ovf_I2_Un:
+ return HandleConversion(16, true, expr.Arguments[0], expectedType, typeSystem.Int16);
+ case ILCode.Conv_I4:
+ case ILCode.Conv_Ovf_I4:
+ case ILCode.Conv_Ovf_I4_Un:
+ return HandleConversion(32, true, expr.Arguments[0], expectedType, typeSystem.Int32);
+ case ILCode.Conv_I8:
+ case ILCode.Conv_Ovf_I8:
+ case ILCode.Conv_Ovf_I8_Un:
+ return HandleConversion(64, true, expr.Arguments[0], expectedType, typeSystem.Int64);
+ case ILCode.Conv_U1:
+ case ILCode.Conv_Ovf_U1:
+ case ILCode.Conv_Ovf_U1_Un:
+ return HandleConversion(8, false, expr.Arguments[0], expectedType, typeSystem.Byte);
+ case ILCode.Conv_U2:
+ case ILCode.Conv_Ovf_U2:
+ case ILCode.Conv_Ovf_U2_Un:
+ return HandleConversion(16, false, expr.Arguments[0], expectedType, typeSystem.UInt16);
+ case ILCode.Conv_U4:
+ case ILCode.Conv_Ovf_U4:
+ case ILCode.Conv_Ovf_U4_Un:
+ return HandleConversion(32, false, expr.Arguments[0], expectedType, typeSystem.UInt32);
+ case ILCode.Conv_U8:
+ case ILCode.Conv_Ovf_U8:
+ case ILCode.Conv_Ovf_U8_Un:
+ return HandleConversion(64, false, expr.Arguments[0], expectedType, typeSystem.UInt64);
+ case ILCode.Conv_I:
+ case ILCode.Conv_Ovf_I:
+ case ILCode.Conv_Ovf_I_Un:
+ return HandleConversion(NativeInt, true, expr.Arguments[0], expectedType, typeSystem.IntPtr);
+ case ILCode.Conv_U:
+ case ILCode.Conv_Ovf_U:
+ case ILCode.Conv_Ovf_U_Un:
+ return HandleConversion(NativeInt, false, expr.Arguments[0], expectedType, typeSystem.UIntPtr);
+ case ILCode.Conv_R4:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], typeSystem.Single);
+ }
+ return typeSystem.Single;
+ case ILCode.Conv_R8:
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], typeSystem.Double);
+ }
+ return typeSystem.Double;
+ case ILCode.Conv_R_Un:
+ return (expectedType != null && expectedType.MetadataType == MetadataType.Single) ? typeSystem.Single : typeSystem.Double;
+ case ILCode.Castclass:
+ case ILCode.Unbox_Any:
+ return (TypeReference)expr.Operand;
+ case ILCode.Unbox:
+ return new ByReferenceType((TypeReference)expr.Operand);
+ case ILCode.Isinst:
+ {
+ // isinst performs the equivalent of a cast only for reference types;
+ // value types still need to be unboxed after an isinst instruction
+ TypeReference tr = (TypeReference)expr.Operand;
+ return tr.IsValueType ? typeSystem.Object : tr;
+ }
+ case ILCode.Box:
+ {
+ var tr = (TypeReference)expr.Operand;
+ if (forceInferChildren)
+ InferTypeForExpression(expr.Arguments.Single(), tr);
+ return tr.IsValueType ? typeSystem.Object : tr;
+ }
+ #endregion
+ #region Comparison instructions
+ case ILCode.Ceq:
+ case ILCode.Cne:
+ if (forceInferChildren)
+ InferArgumentsInBinaryOperator(expr, null, null);
+ return typeSystem.Boolean;
+ case ILCode.Clt:
+ case ILCode.Cgt:
+ case ILCode.Cle:
+ case ILCode.Cge:
+ if (forceInferChildren)
+ InferArgumentsInBinaryOperator(expr, true, null);
+ return typeSystem.Boolean;
+ case ILCode.Clt_Un:
+ case ILCode.Cgt_Un:
+ case ILCode.Cle_Un:
+ case ILCode.Cge_Un:
+ if (forceInferChildren)
+ InferArgumentsInBinaryOperator(expr, false, null);
+ return typeSystem.Boolean;
+ #endregion
+ #region Branch instructions
+ case ILCode.Brtrue:
+ if (forceInferChildren)
+ InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
+ return null;
+ case ILCode.Br:
+ case ILCode.Leave:
+ case ILCode.Endfinally:
+ case ILCode.Switch:
+ case ILCode.Throw:
+ case ILCode.Rethrow:
+ case ILCode.LoopOrSwitchBreak:
+ case ILCode.LoopContinue:
+ case ILCode.YieldBreak:
+ return null;
+ case ILCode.Ret:
+ if (forceInferChildren && expr.Arguments.Count == 1) {
+ TypeReference returnType = context.CurrentMethod.ReturnType;
+ if (context.CurrentMethodIsAsync && returnType != null && returnType.Namespace == "System.Threading.Tasks") {
+ if (returnType.Name == "Task") {
+ returnType = typeSystem.Void;
+ } else if (returnType.Name == "Task`1" && returnType.IsGenericInstance) {
+ returnType = ((GenericInstanceType)returnType).GenericArguments[0];
+ }
+ }
+ InferTypeForExpression(expr.Arguments[0], returnType);
+ }
+ return null;
+ case ILCode.YieldReturn:
+ if (forceInferChildren) {
+ GenericInstanceType genericType = context.CurrentMethod.ReturnType as GenericInstanceType;
+ if (genericType != null) { // IEnumerable<T> or IEnumerator<T>
+ InferTypeForExpression(expr.Arguments[0], genericType.GenericArguments[0]);
+ } else { // non-generic IEnumerable or IEnumerator
+ InferTypeForExpression(expr.Arguments[0], typeSystem.Object);
+ }
+ }
+ return null;
+ case ILCode.Await:
+ {
+ TypeReference taskType = InferTypeForExpression(expr.Arguments[0], null);
+ if (taskType != null && taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") {
+ return ((GenericInstanceType)taskType).GenericArguments[0];
+ }
+ return null;
+ }
+ #endregion
+ case ILCode.Pop:
+ return null;
+ case ILCode.Wrap:
+ case ILCode.Dup:
+ {
+ var arg = expr.Arguments.Single();
+ return arg.ExpectedType = InferTypeForExpression(arg, expectedType);
+ }
+ default:
+ Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName());
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Wraps 'type' in a ByReferenceType if it is a value type. If a constrained prefix is specified,
+ /// returns the constrained type wrapped in a ByReferenceType.
+ /// </summary>
+ TypeReference MakeRefIfValueType(TypeReference type, ILExpressionPrefix constrainedPrefix)
+ {
+ if (constrainedPrefix != null)
+ return new ByReferenceType((TypeReference)constrainedPrefix.Operand);
+ if (type.IsValueType)
+ return new ByReferenceType(type);
+ else
+ return type;
+ }
+
+ /// <summary>
+ /// Promotes primitive types smaller than int32 to int32.
+ /// </summary>
+ /// <remarks>
+ /// Always promotes to signed int32.
+ /// </remarks>
+ TypeReference NumericPromotion(TypeReference type)
+ {
+ if (type == null)
+ return null;
+ switch (type.MetadataType) {
+ case MetadataType.SByte:
+ case MetadataType.Int16:
+ case MetadataType.Byte:
+ case MetadataType.UInt16:
+ return typeSystem.Int32;
+ default:
+ return type;
+ }
+ }
+
+ TypeReference HandleConversion(int targetBitSize, bool targetSigned, ILExpression arg, TypeReference expectedType, TypeReference targetType)
+ {
+ if (targetBitSize >= NativeInt && expectedType is PointerType) {
+ InferTypeForExpression(arg, expectedType);
+ return expectedType;
+ }
+ TypeReference argType = InferTypeForExpression(arg, null);
+ if (targetBitSize >= NativeInt && argType is ByReferenceType) {
+ // conv instructions on managed references mean that the GC should stop tracking them, so they become pointers:
+ PointerType ptrType = new PointerType(((ByReferenceType)argType).ElementType);
+ InferTypeForExpression(arg, ptrType);
+ return ptrType;
+ } else if (targetBitSize >= NativeInt && argType is PointerType) {
+ return argType;
+ }
+ TypeReference resultType = (GetInformationAmount(expectedType) == targetBitSize && IsSigned(expectedType) == targetSigned) ? expectedType : targetType;
+ arg.ExpectedType = resultType; // store the expected type in the argument so that AstMethodBodyBuilder will insert a cast
+ return resultType;
+ }
+
+ public static TypeReference GetFieldType(FieldReference fieldReference)
+ {
+ return SubstituteTypeArgs(UnpackModifiers(fieldReference.FieldType), fieldReference);
+ }
+
+ public static TypeReference SubstituteTypeArgs(TypeReference type, MemberReference member)
+ {
+ if (type is TypeSpecification) {
+ ArrayType arrayType = type as ArrayType;
+ if (arrayType != null) {
+ TypeReference elementType = SubstituteTypeArgs(arrayType.ElementType, member);
+ if (elementType != arrayType.ElementType) {
+ ArrayType newArrayType = new ArrayType(elementType);
+ newArrayType.Dimensions.Clear(); // remove the single dimension that Cecil adds by default
+ foreach (ArrayDimension d in arrayType.Dimensions)
+ newArrayType.Dimensions.Add(d);
+ return newArrayType;
+ } else {
+ return type;
+ }
+ }
+ ByReferenceType refType = type as ByReferenceType;
+ if (refType != null) {
+ TypeReference elementType = SubstituteTypeArgs(refType.ElementType, member);
+ return elementType != refType.ElementType ? new ByReferenceType(elementType) : type;
+ }
+ GenericInstanceType giType = type as GenericInstanceType;
+ if (giType != null) {
+ GenericInstanceType newType = new GenericInstanceType(giType.ElementType);
+ bool isChanged = false;
+ for (int i = 0; i < giType.GenericArguments.Count; i++) {
+ newType.GenericArguments.Add(SubstituteTypeArgs(giType.GenericArguments[i], member));
+ isChanged |= newType.GenericArguments[i] != giType.GenericArguments[i];
+ }
+ return isChanged ? newType : type;
+ }
+ OptionalModifierType optmodType = type as OptionalModifierType;
+ if (optmodType != null) {
+ TypeReference elementType = SubstituteTypeArgs(optmodType.ElementType, member);
+ return elementType != optmodType.ElementType ? new OptionalModifierType(optmodType.ModifierType, elementType) : type;
+ }
+ RequiredModifierType reqmodType = type as RequiredModifierType;
+ if (reqmodType != null) {
+ TypeReference elementType = SubstituteTypeArgs(reqmodType.ElementType, member);
+ return elementType != reqmodType.ElementType ? new RequiredModifierType(reqmodType.ModifierType, elementType) : type;
+ }
+ PointerType ptrType = type as PointerType;
+ if (ptrType != null) {
+ TypeReference elementType = SubstituteTypeArgs(ptrType.ElementType, member);
+ return elementType != ptrType.ElementType ? new PointerType(elementType) : type;
+ }
+ }
+ GenericParameter gp = type as GenericParameter;
+ if (gp != null) {
+ if (member.DeclaringType is ArrayType) {
+ return ((ArrayType)member.DeclaringType).ElementType;
+ } else if (gp.Owner.GenericParameterType == GenericParameterType.Method) {
+ return ((GenericInstanceMethod)member).GenericArguments[gp.Position];
+ } else {
+ return ((GenericInstanceType)member.DeclaringType).GenericArguments[gp.Position];
+ }
+ }
+ return type;
+ }
+
+ static TypeReference UnpackPointer(TypeReference pointerOrManagedReference)
+ {
+ ByReferenceType refType = pointerOrManagedReference as ByReferenceType;
+ if (refType != null)
+ return refType.ElementType;
+ PointerType ptrType = pointerOrManagedReference as PointerType;
+ if (ptrType != null)
+ return ptrType.ElementType;
+ return null;
+ }
+
+ internal static TypeReference UnpackModifiers(TypeReference type)
+ {
+ while (type is OptionalModifierType || type is RequiredModifierType)
+ type = ((TypeSpecification)type).ElementType;
+ return type;
+ }
+
+ static TypeReference GetNullableTypeArgument(TypeReference type)
+ {
+ var t = type as GenericInstanceType;
+ return IsNullableType(t) ? t.GenericArguments[0] : type;
+ }
+
+ GenericInstanceType CreateNullableType(TypeReference type)
+ {
+ if (type == null) return null;
+ var t = new GenericInstanceType(new TypeReference("System", "Nullable`1", module, module.TypeSystem.Corlib, true));
+ t.GenericArguments.Add(type);
+ return t;
+ }
+
+ TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned, TypeReference expectedType)
+ {
+ return InferBinaryArguments(expr.Arguments[0], expr.Arguments[1], expectedType);
+ }
+
+ TypeReference InferArgumentsInAddition(ILExpression expr, bool? isSigned, TypeReference expectedType)
+ {
+ ILExpression left = expr.Arguments[0];
+ ILExpression right = expr.Arguments[1];
+ TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType);
+ if (leftPreferred is PointerType) {
+ left.InferredType = left.ExpectedType = leftPreferred;
+ InferTypeForExpression(right, null);
+ return leftPreferred;
+ }
+ if (IsEnum(leftPreferred)) {
+ //E+U=E
+ left.InferredType = left.ExpectedType = leftPreferred;
+ InferTypeForExpression(right, GetEnumUnderlyingType(leftPreferred));
+ return leftPreferred;
+ }
+ TypeReference rightPreferred = DoInferTypeForExpression(right, expectedType);
+ if (rightPreferred is PointerType) {
+ InferTypeForExpression(left, null);
+ right.InferredType = right.ExpectedType = rightPreferred;
+ return rightPreferred;
+ }
+ if (IsEnum(rightPreferred)) {
+ //U+E=E
+ right.InferredType = right.ExpectedType = rightPreferred;
+ InferTypeForExpression(left, GetEnumUnderlyingType(rightPreferred));
+ return rightPreferred;
+ }
+ return InferBinaryArguments(left, right, expectedType, leftPreferred: leftPreferred, rightPreferred: rightPreferred);
+ }
+
+ TypeReference InferArgumentsInSubtraction(ILExpression expr, bool? isSigned, TypeReference expectedType)
+ {
+ ILExpression left = expr.Arguments[0];
+ ILExpression right = expr.Arguments[1];
+ TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType);
+ if (leftPreferred is PointerType) {
+ left.InferredType = left.ExpectedType = leftPreferred;
+ InferTypeForExpression(right, null);
+ return leftPreferred;
+ }
+ if (IsEnum(leftPreferred)) {
+ if (expectedType != null && IsEnum(expectedType)) {
+ // E-U=E
+ left.InferredType = left.ExpectedType = leftPreferred;
+ InferTypeForExpression(right, GetEnumUnderlyingType(leftPreferred));
+ return leftPreferred;
+ } else {
+ // E-E=U
+ left.InferredType = left.ExpectedType = leftPreferred;
+ InferTypeForExpression(right, leftPreferred);
+ return GetEnumUnderlyingType(leftPreferred);
+ }
+ }
+ return InferBinaryArguments(left, right, expectedType, leftPreferred: leftPreferred);
+ }
+
+ TypeReference InferBinaryArguments(ILExpression left, ILExpression right, TypeReference expectedType, bool forceInferChildren = false, TypeReference leftPreferred = null, TypeReference rightPreferred = null)
+ {
+ if (leftPreferred == null) leftPreferred = DoInferTypeForExpression(left, expectedType, forceInferChildren);
+ if (rightPreferred == null) rightPreferred = DoInferTypeForExpression(right, expectedType, forceInferChildren);
+ if (IsSameType(leftPreferred, rightPreferred)) {
+ return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
+ } else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred, forceInferChildren))) {
+ return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred;
+ } else if (IsSameType(leftPreferred, DoInferTypeForExpression(right, leftPreferred, forceInferChildren))) {
+ // re-infer the left expression with the preferred type to reset any conflicts caused by the rightPreferred type
+ DoInferTypeForExpression(left, leftPreferred, forceInferChildren);
+ return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
+ } else {
+ left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred);
+ left.InferredType = DoInferTypeForExpression(left, left.ExpectedType, forceInferChildren);
+ right.InferredType = DoInferTypeForExpression(right, right.ExpectedType, forceInferChildren);
+ return left.ExpectedType;
+ }
+ }
+
+ TypeReference TypeWithMoreInformation(TypeReference leftPreferred, TypeReference rightPreferred)
+ {
+ int left = GetInformationAmount(leftPreferred);
+ int right = GetInformationAmount(rightPreferred);
+ if (left < right) {
+ return rightPreferred;
+ } else if (left > right) {
+ return leftPreferred;
+ } else {
+ // TODO
+ return leftPreferred;
+ }
+ }
+
+ /// <summary>
+ /// Information amount used for IntPtr.
+ /// </summary>
+ public const int NativeInt = 33; // treat native int as between int32 and int64
+
+ /// <summary>
+ /// Gets the underlying type, if the specified type is an enum.
+ /// Otherwise, returns null.
+ /// </summary>
+ public static TypeReference GetEnumUnderlyingType(TypeReference enumType)
+ {
+ // unfortunately we cannot rely on enumType.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec)
+ if (enumType != null && !IsArrayPointerOrReference(enumType)) {
+ // value type might be an enum
+ TypeDefinition typeDef = enumType.Resolve() as TypeDefinition;
+ if (typeDef != null && typeDef.IsEnum) {
+ return typeDef.Fields.Single(f => !f.IsStatic).FieldType;
+ }
+ }
+ return null;
+ }
+
+ public static int GetInformationAmount(TypeReference type)
+ {
+ type = GetEnumUnderlyingType(type) ?? type;
+ if (type == null)
+ return 0;
+ switch (type.MetadataType) {
+ case MetadataType.Void:
+ return 0;
+ case MetadataType.Boolean:
+ return 1;
+ case MetadataType.SByte:
+ case MetadataType.Byte:
+ return 8;
+ case MetadataType.Char:
+ case MetadataType.Int16:
+ case MetadataType.UInt16:
+ return 16;
+ case MetadataType.Int32:
+ case MetadataType.UInt32:
+ case MetadataType.Single:
+ return 32;
+ case MetadataType.Int64:
+ case MetadataType.UInt64:
+ case MetadataType.Double:
+ return 64;
+ case MetadataType.IntPtr:
+ case MetadataType.UIntPtr:
+ return NativeInt;
+ default:
+ return 100; // we consider structs/objects to have more information than any primitives
+ }
+ }
+
+ public static bool IsBoolean(TypeReference type)
+ {
+ return type != null && type.MetadataType == MetadataType.Boolean;
+ }
+
+ public static bool IsIntegerOrEnum(TypeReference type)
+ {
+ return IsSigned(type) != null;
+ }
+
+ public static bool IsEnum(TypeReference type)
+ {
+ // Arrays/Pointers/ByReference resolve to their element type, but we don't want to consider those to be enums
+ // However, GenericInstanceTypes, ModOpts etc. should be considered enums.
+ if (type == null || IsArrayPointerOrReference(type))
+ return false;
+ // unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec)
+ TypeDefinition typeDef = type.Resolve() as TypeDefinition;
+ return typeDef != null && typeDef.IsEnum;
+ }
+
+ static bool? IsSigned(TypeReference type)
+ {
+ type = GetEnumUnderlyingType(type) ?? type;
+ if (type == null)
+ return null;
+ switch (type.MetadataType) {
+ case MetadataType.SByte:
+ case MetadataType.Int16:
+ case MetadataType.Int32:
+ case MetadataType.Int64:
+ case MetadataType.IntPtr:
+ return true;
+ case MetadataType.Byte:
+ case MetadataType.Char:
+ case MetadataType.UInt16:
+ case MetadataType.UInt32:
+ case MetadataType.UInt64:
+ case MetadataType.UIntPtr:
+ return false;
+ default:
+ return null;
+ }
+ }
+
+ static bool OperandFitsInType(TypeReference type, int num)
+ {
+ type = GetEnumUnderlyingType(type) ?? type;
+ switch (type.MetadataType) {
+ case MetadataType.SByte:
+ return sbyte.MinValue <= num && num <= sbyte.MaxValue;
+ case MetadataType.Int16:
+ return short.MinValue <= num && num <= short.MaxValue;
+ case MetadataType.Byte:
+ return byte.MinValue <= num && num <= byte.MaxValue;
+ case MetadataType.Char:
+ return char.MinValue <= num && num <= char.MaxValue;
+ case MetadataType.UInt16:
+ return ushort.MinValue <= num && num <= ushort.MaxValue;
+ default:
+ return true;
+ }
+ }
+
+ static bool IsArrayPointerOrReference(TypeReference type)
+ {
+ TypeSpecification typeSpec = type as TypeSpecification;
+ while (typeSpec != null) {
+ if (typeSpec is ArrayType || typeSpec is PointerType || typeSpec is ByReferenceType)
+ return true;
+ typeSpec = typeSpec.ElementType as TypeSpecification;
+ }
+ return false;
+ }
+
+ internal static bool IsNullableType(TypeReference type)
+ {
+ return type != null && type.Name == "Nullable`1" && type.Namespace == "System";
+ }
+
+ public static TypeCode GetTypeCode(TypeReference type)
+ {
+ if (type == null)
+ return TypeCode.Empty;
+ switch (type.MetadataType) {
+ case MetadataType.Boolean:
+ return TypeCode.Boolean;
+ case MetadataType.Char:
+ return TypeCode.Char;
+ case MetadataType.SByte:
+ return TypeCode.SByte;
+ case MetadataType.Byte:
+ return TypeCode.Byte;
+ case MetadataType.Int16:
+ return TypeCode.Int16;
+ case MetadataType.UInt16:
+ return TypeCode.UInt16;
+ case MetadataType.Int32:
+ return TypeCode.Int32;
+ case MetadataType.UInt32:
+ return TypeCode.UInt32;
+ case MetadataType.Int64:
+ return TypeCode.Int64;
+ case MetadataType.UInt64:
+ return TypeCode.UInt64;
+ case MetadataType.Single:
+ return TypeCode.Single;
+ case MetadataType.Double:
+ return TypeCode.Double;
+ case MetadataType.String:
+ return TypeCode.String;
+ default:
+ return TypeCode.Object;
+ }
+ }
+
+ /// <summary>
+ /// Clears the type inference data on the method.
+ /// </summary>
+ public static void Reset(ILBlock method)
+ {
+ foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
+ expr.InferredType = null;
+ expr.ExpectedType = null;
+ ILVariable v = expr.Operand as ILVariable;
+ if (v != null && v.IsGenerated)
+ v.Type = null;
+ }
+ }
+
+ public static bool IsSameType(TypeReference type1, TypeReference type2)
+ {
+ if (type1 == type2)
+ return true;
+ if (type1 == null || type2 == null)
+ return false;
+ return type1.FullName == type2.FullName; // TODO: implement this more efficiently?
+ }
+ }
+}