diff options
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs')
-rw-r--r-- | ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 1294 |
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? + } + } +} |