private static void UnboxIfGeneric(XTypeReference type, AstExpression node, XTypeSystem typeSystem) { if (type.IsGenericParameter || type.IsGenericParameterArray()) { var resultType = node.GetResultType(); if (resultType.IsByReference && !type.IsByReference) { var elementType = resultType.ElementType; var clone = new AstExpression(node); node.SetCode(AstCode.SimpleCastclass).SetArguments(clone).Operand = elementType; } else { if (TreatAsStruct(type, resultType)) { ConvertUnboxStruct(node, resultType, typeSystem); } else { var clone = new AstExpression(node); node.SetCode(AstCode.UnboxFromGeneric).SetArguments(clone).Operand = type; } } } }
/// <summary> /// Convert node with code Cast. /// </summary> private static void ConvertCastclass(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem) { var type = (XTypeReference) node.Operand; if (type.IsSystemArray()) { // Call cast method var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var castToArray = arrayHelper.Methods.First(x => x.Name == "CastToArray"); var castToArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, castToArray, node.Arguments).SetType(type); node.CopyFrom(castToArrayExpr); return; } string castMethod = null; if (type.IsSystemCollectionsIEnumerable()) { castMethod = "CastToEnumerable"; } else if (type.IsSystemCollectionsICollection()) { castMethod = "CastToCollection"; } else if (type.IsSystemCollectionsIList()) { castMethod = "CastToList"; } else if (type.IsSystemIFormattable()) { castMethod = "CastToFormattable"; } if (castMethod != null) { // Call cast method var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var castToArray = arrayHelper.Methods.First(x => x.Name == castMethod); // Call "(x instanceof T) ? (T)x : asMethod(x)" // "instanceof x" var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, node.Arguments[0]).SetType(typeSystem.Bool); // CastX(x) var castXExpr = new AstExpression(node.SourceLocation, AstCode.Call, castToArray, node.Arguments[0]).SetType(typeSystem.Object); // T(x) var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, node.Arguments[0]).SetType(type); // Combine var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, castXExpr).SetType(type); node.CopyFrom(conditional); return; } // Normal castclass node.Code = AstCode.SimpleCastclass; }
/// <summary> /// Default ctor /// </summary> public AstBuilder(XModule module, MethodDefinition methodDef, XTypeDefinition declaringType, bool optimize) { this.module = module; typeSystem = module.TypeSystem; this.methodDef = methodDef; this.declaringType = declaringType; this.optimize = optimize; codeAttr = methodDef.Attributes.OfType<CodeAttribute>().FirstOrDefault(); validExceptionHandlers = (codeAttr != null) ? codeAttr.ExceptionHandlers.Where(IsValid).ToList() : null; }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, XTypeSystem typeSystem) { // Expand typeof foreach (var pair in ast.GetExpressionPairs()) { var node = pair.Expression; switch (node.Code) { case AstCode.Ldfld: //case AstCode.Ldsfld: // NOT YET { var field = (XFieldReference) node.Operand; UnboxIfGeneric(field.FieldType, node, typeSystem); } break; case AstCode.Stfld: { var field = (XFieldReference)node.Operand; BoxIfGeneric(field.FieldType, node.Arguments[1]); } break; case AstCode.Call: case AstCode.Calli: case AstCode.Callvirt: { var method = (XMethodReference)node.Operand; if ((!method.ReturnType.IsVoid()) && (pair.Parent != null)) { UnboxIfGeneric(method.ReturnType, node, typeSystem); } } break; case AstCode.Ret: { if (node.Arguments.Count > 0) { var expectedType = currentMethod.Method.ReturnType; BoxIfGeneric(expectedType, node.Arguments[0]); } } break; case AstCode.ByRefArray: case AstCode.ByRefOutArray: { if (node.Arguments.Count > 1) { var originalType = (XTypeReference) node.Arguments[1].Operand; UnboxByRefIfGeneric(originalType, node.StoreByRefExpression, typeSystem); } } break; } } }
public SimpleControlFlow(DecompilerContext context, AstBlock method) { this.context = context; this.typeSystem = context.CurrentModule.TypeSystem; foreach(AstLabel target in method.GetSelfAndChildrenRecursive<AstExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; } foreach(AstBasicBlock bb in method.GetSelfAndChildrenRecursive<AstBasicBlock>()) { foreach(AstLabel label in bb.GetChildren().OfType<AstLabel>()) { labelToBasicBlock[label] = bb; } } }
/// <summary> /// Create the body of the values() method. /// </summary> private AstBlock CreateValuesBody(XFieldDefinition enumInfoField, XTypeSystem typeSystem) { var internalEnumInfoType = Compiler.GetDot42InternalType("EnumInfo"); var valuesMethod = new XMethodReference.Simple("Values", true, typeSystem.Object, internalEnumInfoType); var ast = AstBlock.CreateOptimizedForTarget( new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.SimpleCastclass, new XArrayType(XType), new AstExpression(AstNode.NoSource, AstCode.Call, valuesMethod, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField))))); return ast; }
/// <summary> /// Convert node with code IsInst. /// </summary> private static void ConvertIsInst(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem) { var type = (XTypeReference)node.Operand; if (type.IsSystemArray()) { // Call ArrayHelper.AsArray var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var asArray = arrayHelper.Methods.First(x => x.Name == "AsArray"); var asArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, node.Arguments).SetType(typeSystem.Bool); node.CopyFrom(asArrayExpr); return; } string asMethod = null; if (type.IsSystemCollectionsIEnumerable()) { asMethod = "AsEnumerable"; } else if (type.IsSystemCollectionsICollection()) { asMethod = "AsCollection"; } else if (type.IsSystemCollectionsIList()) { asMethod = "AsList"; } else if (type.IsSystemIFormattable()) { asMethod = "AsFormattable"; } if (asMethod != null) { // Call "(x instanceof T) ? (T)x : asMethod(x)" var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var asArray = arrayHelper.Methods.First(x => x.Name == asMethod); // "instanceof x" var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, node.Arguments[0]).SetType(typeSystem.Bool); // AsX(x) var asXExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, node.Arguments[0]).SetType(typeSystem.Object); // T(x) var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, node.Arguments[0]).SetType(type); // Combine var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, asXExpr).SetType(type); node.CopyFrom(conditional); return; } // Normal "as": Convert to (x instanceof T) ? T(x) : null { // "instanceof x" var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, node.Arguments[0]).SetType(typeSystem.Bool); // T(x) var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, node.Arguments[0]).SetType(type); // null var nullExpr = new AstExpression(node.SourceLocation, AstCode.Ldnull, null).SetType(typeSystem.Object); // Combine var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, nullExpr).SetType(type); node.CopyFrom(conditional); return; } }
/// <summary> /// Convert node with code InstanceOf. /// </summary> private static void ConvertInstanceOf(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem) { var type = (XTypeReference)node.Operand; if (type.IsSystemArray()) // "is System.Array" { // Call ArrayHelper.IsArray var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var isArray = arrayHelper.Methods.First(x => x.Name == "IsArray"); var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, node.Arguments).SetType(typeSystem.Bool); node.CopyFrom(isArrayExpr); return; } if (type.IsSystemCollectionsIEnumerable() || type.IsSystemCollectionsICollection() || type.IsSystemCollectionsIList()) { // Call "(is x) || IsArray(x)" var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var isArray = arrayHelper.Methods.First(x => x.Name == "IsArray"); // "is" var isExpr = new AstExpression(node).SetCode(AstCode.SimpleInstanceOf); // Call IsArray var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, node.Arguments).SetType(typeSystem.Bool); // Combined var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isArrayExpr).SetType(typeSystem.Bool); node.CopyFrom(combined); return; } if (type.IsSystemIFormattable()) { // Call "(is x) || IsFormattable(x)" var formattable = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var isFormattable = formattable.Methods.First(x => x.Name == "IsVirtualFormattable"); // "is" var isExpr = new AstExpression(node).SetCode(AstCode.SimpleInstanceOf); // Call IsFormattable var isFormattableExpr = new AstExpression(node.SourceLocation, AstCode.Call, isFormattable, node.Arguments).SetType(typeSystem.Bool); // Combined var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isFormattableExpr).SetType(typeSystem.Bool); node.CopyFrom(combined); return; } // Normal instanceof node.Code = AstCode.SimpleInstanceOf; }
/// <summary> /// Load the GenericInstance of the current generic method. /// The result is a register that cannot be destroyed. /// </summary> private static AstExpression LoadMethodGenericArgument(ISourceLocation seqp, XTypeSystem typeSystem, XMethodDefinition method, int index) { bool loadFromArray = method.GenericParameters.Count > InternalConstants.GenericMethodParametersAsArrayThreshold; return LoadGenericArgument(seqp, typeSystem, AstCode.LdGenericInstanceMethodArgument, index, loadFromArray); }
private static AstExpression LoadGenericArgument(ISourceLocation seqp, XTypeSystem typeSystem, AstCode ldCode, int index, bool loadFromArray) { if (loadFromArray) { var getField = new AstExpression(seqp, ldCode, 0) { ExpectedType = new XArrayType(typeSystem.Type) }; var indexExpr = new AstExpression(seqp, AstCode.Ldc_I4, index) { ExpectedType = typeSystem.Int }; var loadExpr = new AstExpression(seqp, AstCode.Ldelem_Ref, null, getField, indexExpr) { ExpectedType = typeSystem.Type }; return loadExpr; } else { return new AstExpression(seqp, ldCode, index) { ExpectedType = typeSystem.Type }; } }
/// <summary> /// Default ctor /// </summary> public XModule() { typeSystem = new XTypeSystem(this); }
/// <summary> /// Load the GenericInstance of the current generic method. /// The result is a register that cannot be destroyed. /// </summary> private static AstExpression LoadMethodGenericInstance(ISourceLocation seqp, XTypeSystem typeSystem) { return new AstExpression(seqp, AstCode.LdGenericInstanceMethodArgument, null) { ExpectedType = new XArrayType(typeSystem.Type) }; }
/// <summary> /// Convert node with code IsInst. /// </summary> private static void ConvertIsInst(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem) { var type = (XTypeReference)node.Operand; if (type.IsSystemArray()) { // Call ArrayHelper.AsArray var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var asArray = arrayHelper.Methods.First(x => x.Name == "AsArray"); var asArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, node.Arguments).SetType(typeSystem.Bool); node.CopyFrom(asArrayExpr); return; } string asMethod = GetCollectionConvertMethodName(type); if (asMethod != null) { asMethod = "As" + asMethod; } else if (type.IsSystemIFormattable()) { asMethod = "AsFormattable"; } // make sure we don't evaluate the expression twice. var tempVar = new AstGeneratedVariable("temp$$", null) { Type = compiler.Module.TypeSystem.Object }; var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0]) { ExpectedType = compiler.Module.TypeSystem.Object }; var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object); if (asMethod != null) { // Call "(x instanceof T) ? (T)x : asMethod(x)" var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var asArray = arrayHelper.Methods.First(x => x.Name == asMethod); // "instanceof x" var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool); // AsX(x) var asXExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, loadTempVar).SetType(typeSystem.Object); // T(x) var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type); // Combine var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, asXExpr).SetType(type); node.CopyFrom(conditional); return; } // Normal "as": Convert to (x instanceof T) ? (T)x : null if(!type.IsPrimitive) { // "instanceof x" var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool); // T(x) var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type); // null var nullExpr = new AstExpression(node.SourceLocation, AstCode.Ldnull, null).SetType(typeSystem.Object); // Combine var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, nullExpr).SetType(type); node.CopyFrom(conditional); return; } else { // treat as "x is T" if(!node.ExpectedType.IsBoolean()) throw new NotImplementedException(); // can this happen? node.Code = AstCode.SimpleInstanceOf; } }
/// <summary> /// Convert ret or store field node. /// /// converts to IEnumerable, ICollection or IList if required. /// </summary> private static void ConvertRetOrStfldOrStsfld(AssemblyCompiler compiler, XTypeReference targetType, AstExpression node, XTypeSystem typeSystem) { var argument = node.Arguments.LastOrDefault(); if (argument == null) return; if (argument.InferredType == null || !argument.InferredType.IsArray) return; var methodName = GetCollectionConvertMethodName(targetType); if (methodName == null) return; // Call "ret asMethod(x)" var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var asArray = arrayHelper.Methods.First(x => x.Name == "As" + methodName); // AsX(x) var asXExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, argument).SetType(typeSystem.Object); // replace argument. node.Arguments[node.Arguments.Count-1] = asXExpr; }
/// <summary> /// Create the body of the valueOf(string) method. /// </summary> private AstBlock CreateValueOfBody(XSyntheticMethodDefinition method, XFieldDefinition enumInfoField, XTypeSystem typeSystem) { var internalEnumType = Compiler.GetDot42InternalType("Enum"); var internalEnumInfoType = Compiler.GetDot42InternalType("EnumInfo"); var parseMethod = new XMethodReference.Simple("Parse", true, internalEnumType, internalEnumInfoType, XParameter.Create("value", typeSystem.String), XParameter.Create("ignoreCase", typeSystem.Bool), XParameter.Create("throwIfNotFound", typeSystem.Bool)); var ast = AstBlock.CreateOptimizedForTarget( new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.SimpleCastclass, XType, new AstExpression(AstNode.NoSource, AstCode.Call, parseMethod, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField), new AstExpression(AstNode.NoSource, AstCode.Ldloc, method.AstParameters[0]), new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, 0), new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, 1))))); return ast; }
private static void ConvertUnboxStruct(AstExpression node, XTypeReference resultType, XTypeSystem typeSystem) { // Structs must never be null. We have to handle structs here, since a newobj // might need generic arguments. These would be difficult to provide at "UnboxFromGeneric", // but will be automatically filled in by the GenericInstanceConverter // convert to (temp$ = (T)x) != null ? temp$ : default(T) // replace any unbox, but keep if otherwise. var clone = node.Code == AstCode.Unbox ? new AstExpression(node.Arguments[0]) : new AstExpression(node); // make sure we don't evaluate the expression twice. var tempVar = new AstGeneratedVariable("temp$", "") { Type = typeSystem.Object }; // T(x) var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, resultType, clone) .SetType(resultType); // temporary storage var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, txExpr) { ExpectedType = resultType }; var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar) .SetType(resultType); // default (T) var defaultT = new AstExpression(node.SourceLocation, AstCode.DefaultValue, resultType).SetType(resultType); var constructor = StructCallConverter.GetDefaultValueCtor(resultType.Resolve()); StructCallConverter.ConvertDefaultValue(defaultT, constructor); // Combine var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, resultType, storeTempVar, loadTempVar, defaultT) .SetType(resultType); node.CopyFrom(conditional); }
private TypeAnalysis(DecompilerContext context) { this.context = context; module = context.CurrentModule; typeSystem = module.TypeSystem; }
/// <summary> /// Create the body of the class ctor. /// </summary> private AstBlock CreateClassCtorBody(bool isWide, XFieldDefinition enumInfoField, XFieldDefinition defaultField, XMethodReference enumInfoCtor, XTypeReference valueType, XTypeSystem typeSystem) { var internalEnumType = Compiler.GetDot42InternalType("Enum"); var internalEnumInfoType = Compiler.GetDot42InternalType("EnumInfo"); var valueToFieldMap = new Dictionary<object, XFieldDefinition>(); var ldc = isWide ? AstCode.Ldc_I8 : AstCode.Ldc_I4; var ast = AstBlock.CreateOptimizedForTarget( // Instantiate enum info field new AstExpression(AstNode.NoSource, AstCode.Stsfld, enumInfoField, new AstExpression(AstNode.NoSource, AstCode.Newobj, enumInfoCtor))); // Instantiate values for each field var ordinal = 0; foreach (var field in XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition))) { // Find dex field object value; if (!field.TryGetEnumValue(out value)) throw new CompilerException(string.Format("Cannot get enum value from field {0}", field.FullName)); value = isWide ? (object)XConvert.ToLong(value) : (object)XConvert.ToInt(value); XFieldDefinition existingField; AstExpression valueExpr; if (valueToFieldMap.TryGetValue(value, out existingField)) { // Re-use instance of existing field valueExpr = new AstExpression(AstNode.NoSource, AstCode.Ldsfld, existingField); } else { // Record valueToFieldMap[value] = field; // Call ctor valueExpr = new AstExpression(AstNode.NoSource, AstCode.Newobj, ctor, new AstExpression(AstNode.NoSource, AstCode.Ldstr, field.Name), new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, ordinal), new AstExpression(AstNode.NoSource, ldc, value)); } // Initialize static field ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stsfld, field, valueExpr)); // Add to info var addMethod = new XMethodReference.Simple("Add", true, typeSystem.Void, internalEnumInfoType, XParameter.Create("value", valueType), XParameter.Create("instance", internalEnumType)); ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Call, addMethod, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField), new AstExpression(AstNode.NoSource, ldc, value), new AstExpression(AstNode.NoSource, AstCode.Ldsfld, field))); // Increment ordinal ordinal++; } // Initialize default field var getValueMethod = new XMethodReference.Simple("GetValue", true, internalEnumType, internalEnumInfoType, XParameter.Create("value", valueType)); ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stsfld, defaultField, new AstExpression(AstNode.NoSource, AstCode.SimpleCastclass, XType, new AstExpression(AstNode.NoSource, AstCode.Call, getValueMethod, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField), new AstExpression(AstNode.NoSource, ldc, 0))))); // Return ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null)); return ast; }
/// <summary> /// Add generic instance field initialization code. /// </summary> private static void AddGenericInstanceFieldInitializationCode(MethodSource source, AstBlock ast, XTypeSystem typeSystem) { int paramCount = source.Method.DeclaringType.GenericParameters.Count; if (paramCount > InternalConstants.GenericTypeParametersAsArrayThreshold) { var xArrayType = new XArrayType(typeSystem.Type); var loadExpr = new AstExpression(ast.SourceLocation, AstCode.LdGenericInstanceTypeArgument, 0) {ExpectedType = xArrayType}; var initExpr = new AstExpression(ast.SourceLocation, AstCode.StGenericInstanceField, 0, loadExpr) { ExpectedType = xArrayType }; InsertAfter(ast, null, new[] {initExpr}); } else { InsertAfter(ast, null, Enumerable.Range(0, paramCount).Select(i => new AstExpression(ast.SourceLocation, AstCode.StGenericInstanceField, i, new AstExpression(ast.SourceLocation, AstCode.LdGenericInstanceTypeArgument, i) { ExpectedType = typeSystem.Type }) { ExpectedType = typeSystem.Type })); } }
/// <summary> /// Convert node with code Callvirt. /// /// For arrays: intercepts call to IEnumerable.IEnumerable_GetEnumerator generated /// by foreach statements and swaps them out to System.Array.GetEnumerator. /// /// This call will then at a later compilation stage be replaced with the final destination. /// </summary> private static void ConvertCallvirtIEnumerable(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem) { var targetMethodRef = ((XMethodReference)node.Operand); var targetMethodDefOrRef = targetMethodRef; if (!targetMethodDefOrRef.DeclaringType.IsSystemCollectionsIEnumerable()) return; if (targetMethodDefOrRef.Name != "IEnumerable_GetEnumerator") return; if (node.Arguments.Count != 1) return; var argument = node.Arguments[0]; if (!argument.InferredType.IsArray) return; // swap the call to System.Array var systemArray = compiler.GetDot42InternalType("System", "Array").Resolve(); var getEnumerator = systemArray.Methods.First(x => x.Name == "GetEnumerator" && !x.IsStatic && x.Parameters.Count == 0); node.Operand = getEnumerator; }
private static void ConvertAsNativeIFormattable(AstExpression node, XTypeSystem typeSystem) { var method = (XMethodReference)node.Operand; var type = method.ReturnType; if (method.Name == "AsNativeIFormattable" && method.DeclaringType.Name == InternalConstants.CompilerHelperName && type.FullName == "System.IFormattable") { // make sure we don't evaluate the expression twice. var tempVar = new AstGeneratedVariable("temp$$", null) { Type = typeSystem.Object }; var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0]) { ExpectedType = typeSystem.Object }; var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(typeSystem.Object); // Convert to "(x instanceof T) ? (T)x : null" // "instanceof x" var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool); // T(x) var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type); // null var nullExpr = new AstExpression(node.SourceLocation, AstCode.Ldnull, null).SetType(type); // Combine var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, nullExpr).SetType(type); node.CopyFrom(conditional); } }
private static void UnboxByRefIfGeneric(XTypeReference type, AstExpression node, XTypeSystem typeSystem) { if (!type.IsGenericParameter) return; var resultType = node.InferredType ?? node.ExpectedType; if (resultType == null) return; if (!TreatAsStruct(type, resultType)) return; // find the first unbox, which should be our target. var unbox = node.GetSelfAndChildrenRecursive<AstExpression>( n => n.Code == AstCode.Unbox).FirstOrDefault(); if (unbox == null) return; // TODO: Of course we need to unbox generic instances as well, // but at the moment the GenericInstanceConverter does // not look at 'node.StoreByRefExpression' and thus // does not add the required argument, resulting // in unverifyable code. This should be fixed, and // then these lines can be removed. if (resultType.IsGenericInstance) return; ConvertUnboxStruct(unbox, resultType, typeSystem); }
/// <summary> /// Create the body of the valueOf(string) method. /// </summary> private AstBlock CreateValueOfBody(XSyntheticMethodDefinition method, XTypeSystem typeSystem) { var fields = XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition)).ToList(); var ast = AstBlock.Create<AstExpression>(); // Find name foreach (var field in fields) { var notEqualLabel = new AstLabel(AstNode.NoSource, "not_equal_to" + field.Name); var equalsExpr = new AstExpression(AstNode.NoSource, AstCode.Call, FrameworkReferences.StringEquals(typeSystem), new AstExpression(AstNode.NoSource, AstCode.Ldstr, field.Name), new AstExpression(AstNode.NoSource, AstCode.Ldloc, method.AstParameters[0])); // If !equals(name, field.name) goto notEqualLabel ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Brfalse, notEqualLabel, equalsExpr)); // Return field object ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, field))); // notEqualLabel: ast.Body.Add(notEqualLabel); } // Return null ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.Ldnull, null))); return ast; }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, XTypeSystem typeSystem) { // Expand typeof foreach (var pair in ast.GetExpressionPairs()) { var node = pair.Expression; switch (node.Code) { case AstCode.Ldfld: //case AstCode.Ldsfld: // NOT YET { var field = (XFieldReference) node.Operand; UnboxIfGeneric(field.FieldType, node, typeSystem); } break; case AstCode.Stfld: { var field = (XFieldReference)node.Operand; BoxIfGeneric(field.FieldType, node.Arguments[1]); } break; case AstCode.Call: case AstCode.Calli: case AstCode.Callvirt: { var method = (XMethodReference)node.Operand; if ((!method.ReturnType.IsVoid()) && (pair.Parent != null)) { UnboxIfGeneric(method.ReturnType, node, typeSystem); } } break; case AstCode.Ret: { if (node.Arguments.Count > 0) { var expectedType = currentMethod.Method.ReturnType; BoxIfGeneric(expectedType, node.Arguments[0]); } } break; case AstCode.Box: { var type = (XTypeReference)node.Operand; // Honestly, the whole Generics code seems to be quite // complex. I hope this fix does not break anything else. // Also: is there any sense in having two codes 'box' and 'boxtogeneric'? // The Rosyln compiler apparently uses 'box' instructions to satisfy // generic constraints. Not sure if this is the right place to handle // this. // What we want to achive is to perform this conversion code only if the // expected type is assignable from any of the constraints. As we do not // have an 'IsAssignableFrom' logic for XTypes, a simpler check must suffice. if (type.IsGenericParameter && ((XGenericParameter)type).Constraints.Any() && node.ExpectedType != null && !node.ExpectedType.IsPrimitive && !node.ExpectedType.IsGenericParameter) { // or just enter the required cast here??? node.Code = AstCode.BoxToGeneric; node.InferredType = node.ExpectedType; } } break; case AstCode.ByRefArray: case AstCode.ByRefOutArray: { if (node.Arguments.Count > 1) { var originalType = (XTypeReference) node.Arguments[1].Operand; UnboxByRefIfGeneric(originalType, node.StoreByRefExpression, typeSystem); } } break; } } }
/// <summary> /// Create an expression that loads the given type at runtime. /// </summary> private static AstExpression LoadTypeForGenericInstance(ISourceLocation seqp, MethodSource currentMethod, XTypeReference type, AssemblyCompiler compiler, XTypeDefinition typeHelperType, XTypeSystem typeSystem, TypeConversion typeConversion, XGenericInstanceType typeGenericArguments=null) { if (type.IsArray) { // Array type var arrayType = (XArrayType)type; // Load element type var prefix = LoadTypeForGenericInstance(seqp, currentMethod, ((XArrayType)type).ElementType, compiler, typeHelperType, typeSystem, typeConversion); // Convert to array type if (arrayType.Dimensions.Count() == 1) { var giCreateArray = typeHelperType.Methods.Single(x => (x.Name == "Array") && (x.Parameters.Count == 1)); return new AstExpression(seqp, AstCode.Call, giCreateArray, prefix) { ExpectedType = typeSystem.Type }; } else { var giCreateArray = typeHelperType.Methods.Single(x => (x.Name == "Array") && (x.Parameters.Count == 2)); var dimensionsExpr = new AstExpression(seqp, AstCode.Ldc_I4, arrayType.Dimensions.Count()) { ExpectedType = typeSystem.Int }; return new AstExpression(seqp, AstCode.Call, giCreateArray, prefix, dimensionsExpr) { ExpectedType = typeSystem.Type }; } } var gp = type as XGenericParameter; if (gp != null) { AstExpression loadExpr; if (gp.Owner is XTypeReference) { // Class type parameter var owner = (XTypeReference)gp.Owner; if (owner.GetElementType().Resolve().HasDexImportAttribute()) { // Imported type return new AstExpression(seqp, AstCode.TypeOf, typeSystem.Object) { ExpectedType = typeSystem.Type }; } if (currentMethod.IsClassCtor) { // Class ctor's cannot have type information. // Return Object instead if(currentMethod.IsDotNet && !currentMethod.ILMethod.DeclaringType.HasSuppressMessageAttribute("StaticConstructorUsesGenericParameter")) { var msg = "Class (static) constructor of '{0}' tries to use generic parameter. This will always yield Object. " + "You can suppress this warning with a [SuppressMessage(\"dot42\", \"StaticConstructorUsesGenericParameter\")] " + "attribute on the class."; if(seqp != null && seqp.Document != null) DLog.Warning(DContext.CompilerCodeGenerator, seqp.Document, seqp.StartColumn,seqp.StartLine, msg, currentMethod.DeclaringTypeFullName); else DLog.Warning(DContext.CompilerCodeGenerator, msg, currentMethod.DeclaringTypeFullName); } return new AstExpression(seqp, AstCode.TypeOf, typeSystem.Object) { ExpectedType = typeSystem.Type }; } loadExpr = currentMethod.IsStatic ? LoadStaticClassGenericArgument(seqp, typeSystem, currentMethod.Method, gp.Position) : LoadInstanceClassGenericArgument(seqp, typeSystem, currentMethod.Method.DeclaringType, gp.Position); } else { // Method type parameter var owner = (XMethodReference)gp.Owner; if (owner.GetElementMethod().Resolve().DeclaringType.HasDexImportAttribute()) { // Imported type return LoadTypeForGenericInstance(seqp, currentMethod, type.Module.TypeSystem.Object, compiler, typeHelperType, typeSystem, typeConversion); } loadExpr = LoadMethodGenericArgument(seqp, typeSystem, currentMethod.Method, gp.Position); } if (typeConversion == TypeConversion.EnsureRuntimeType) return EnsureGenericRuntimeType(loadExpr, typeSystem, typeHelperType); else return loadExpr; } if (type is XTypeSpecification) { var typeSpec = (XTypeSpecification)type; var git = type as XGenericInstanceType; var baseType = LoadTypeForGenericInstance(seqp, currentMethod, typeSpec.ElementType, compiler, typeHelperType, typeSystem, typeConversion, git); if (typeConversion != TypeConversion.EnsureTrueOrMarkerType || typeSpec.GetElementType().IsNullableT()) return baseType; // Use the element type and make a generic proxy with the generic arguments. var parameters = CreateGenericInstanceCallArguments(seqp, git, currentMethod, compiler); if (parameters.Count == 1 && parameters[0].GetResultType().IsArray) { // array type call. var method = typeHelperType.Methods.Single(m => m.Name == "GetGenericInstanceType" && m.Parameters.Count == 2 && m.Parameters[1].ParameterType.IsArray); return new AstExpression(seqp, AstCode.Call, method, baseType, parameters[0]); } else { parameters.Insert(0, baseType); var method = typeHelperType.Methods.Single(m => m.Name == "GetGenericInstanceType" && m.Parameters.Count == parameters.Count && !m.Parameters[1].ParameterType.IsArray); return new AstExpression(seqp, AstCode.Call, method, parameters.ToArray()); } } if (typeConversion == TypeConversion.EnsureTrueOrMarkerType && type.GetElementType().IsNullableT()) { if (typeGenericArguments != null) { var underlying = typeGenericArguments.GenericArguments[0]; var code = underlying.IsPrimitive ? AstCode.BoxedTypeOf : AstCode.NullableTypeOf; return new AstExpression(seqp, code, underlying) { ExpectedType = typeSystem.Type }; } // if typeGenericArguments is null, this is a generic definition, e.g. typeof(Nullable<>). } // Plain type reference or definition return new AstExpression(seqp, AstCode.TypeOf, type) { ExpectedType = typeSystem.Type }; }
/// <summary> /// Convert node with code Callvirt. /// /// For arrays: intercepts call to IEnumerable.IEnumerable_GetEnumerator generated /// by foreach statements and swaps them out to System.Array.GetEnumerator. /// /// This call will then at a later compilation stage be replaced with the final destination. /// </summary> private static void ConvertCallvirtIEnumerable(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem) { var targetMethodRef = ((XMethodReference)node.Operand); var targetMethodDefOrRef = targetMethodRef; if (targetMethodDefOrRef.DeclaringType.IsSystemCollectionsIEnumerable() && targetMethodDefOrRef.Name == "IEnumerable_GetEnumerator" && node.Arguments.Count == 1) { var argument = node.Arguments[0]; if (!argument.InferredType.IsArray) return; // swap the call to System.Array var systemArray = compiler.GetDot42InternalType("System", "Array").Resolve(); var getEnumerator = systemArray.Methods.First(x => x.Name == "GetEnumerator" && !x.IsStatic && x.Parameters.Count == 0); node.Operand = getEnumerator; } else if (targetMethodDefOrRef.DeclaringType.IsSystemCollectionsIEnumerableT() && targetMethodDefOrRef.Name.EndsWith("_GetEnumerator") && node.Arguments.Count == 1) { var argument = node.Arguments[0]; if (!argument.InferredType.IsArray) return; var elementType = argument.InferredType.ElementType; // Use As...Enumerable to convert var asEnumerableName = FrameworkReferences.GetAsEnumerableTMethodName(elementType); var compilerHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var asEnumerableMethod = compilerHelper.Methods.First(x => x.Name == asEnumerableName); var call = new AstExpression(node.SourceLocation, AstCode.Call, asEnumerableMethod, argument) { InferredType = asEnumerableMethod.ReturnType }; node.Arguments[0] = call; argument.ExpectedType = argument.InferredType; } }
/// <summary> /// Expand the loadExpression, so that primitive types are converted to their boxed counterparts, /// and marker types are converted to their underlying types. /// </summary> private static AstExpression EnsureGenericRuntimeType(AstExpression loadExpr, XTypeSystem typeSystem, XTypeDefinition typeHelper) { var ensureMethod = typeHelper.Methods.Single(x => x.Name == "EnsureGenericRuntimeType"); return new AstExpression(loadExpr.SourceLocation, AstCode.Call, ensureMethod, loadExpr) .SetType(typeSystem.Type); }
/// <summary> /// Convert node with code InstanceOf. /// </summary> private static void ConvertInstanceOf(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem) { var type = (XTypeReference)node.Operand; if (type.IsSystemArray()) // "is System.Array" { // Call ArrayHelper.IsArray var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var isArray = arrayHelper.Methods.First(x => x.Name == "IsArray"); var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, node.Arguments).SetType(typeSystem.Bool); node.CopyFrom(isArrayExpr); return; } // make sure we don't evaluate the expression twice. var tempVar = new AstGeneratedVariable("temp$$", null) { Type = compiler.Module.TypeSystem.Object }; var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0]) { ExpectedType = compiler.Module.TypeSystem.Object }; var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object); if (type.IsSystemCollectionsIEnumerable() || type.IsSystemCollectionsICollection() || type.IsSystemCollectionsIList()) { // Call "(is x) || IsArray(x)" var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var isArray = arrayHelper.Methods.First(x => x.Name == "IsArray" && x.Parameters.Count == 1); // "is" var isExpr = new AstExpression(node).SetArguments(storeTempVar).SetCode(AstCode.SimpleInstanceOf); // Call IsArray var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, loadTempVar).SetType(typeSystem.Bool); // Combined var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isArrayExpr).SetType(typeSystem.Bool); node.CopyFrom(combined); return; } if (type.IsSystemCollectionsIEnumerableT() || type.IsSystemCollectionsICollectionT() || type.IsSystemCollectionsIListT()) { // TODO: implement InstanceOf with type check for array types. // (is that even possible here?) } if (type.IsSystemIFormattable()) { // Call "(is x) || IsFormattable(x)" var formattable = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var isFormattable = formattable.Methods.First(x => x.Name == "IsVirtualFormattable"); // "is" var isExpr = new AstExpression(node).SetArguments(storeTempVar).SetCode(AstCode.SimpleInstanceOf); // Call IsFormattable var isFormattableExpr = new AstExpression(node.SourceLocation, AstCode.Call, isFormattable, loadTempVar).SetType(typeSystem.Bool); // Combined var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isFormattableExpr).SetType(typeSystem.Bool); node.CopyFrom(combined); return; } // Normal instanceof node.Code = AstCode.SimpleInstanceOf; }
/// <summary> /// Load the GenericInstance of the current instance. /// The result is a temporary register. /// </summary> private static AstExpression LoadInstanceClassGenericArgument(ISourceLocation seqp, XTypeSystem typeSystem, XTypeDefinition type, int index) { bool loadFromArray = type.GenericParameters.Count > InternalConstants.GenericTypeParametersAsArrayThreshold; return LoadGenericArgument(seqp, typeSystem, AstCode.LdGenericInstanceField, index, loadFromArray); }
/// <summary> /// Reference to java.lang.String.equals(string) /// </summary> internal static XMethodReference StringEquals(XTypeSystem typeSystem) { return new XMethodReference.Simple("equals", true, typeSystem.Bool, typeSystem.String, XParameter.Create("other", typeSystem.Object)); }
/// <summary> /// Load the GenericInstance of the current instance. /// The result is a temporary register. /// </summary> private static AstExpression LoadInstanceClassGenericInstance(ISourceLocation seqp, XTypeSystem typeSystem) { return new AstExpression(seqp, AstCode.LdGenericInstanceField, null) { ExpectedType = new XArrayType(typeSystem.Type) }; }