/// <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> /// Implement the class now that all classes have been created /// </summary> protected override void CreateMembers(DexTargetPackage targetPackage) { base.CreateMembers(targetPackage); // Build default ctor XTypeSystem typeSystem = Compiler.Module.TypeSystem; XSyntheticMethodDefinition ctor = XSyntheticMethodDefinition.Create(XType, XSyntheticMethodFlags.Constructor, "<init>", null, typeSystem.Void); ctor.Body = CreateCtorBody(); Class.Methods.Add(ctor.GetDexMethod(Class, targetPackage)); // Build Invoke method. XMethodDefinition sourceMethod = XType.Methods.Single(x => x.EqualsName("Invoke")); Prototype prototype = PrototypeBuilder.BuildPrototype(Compiler, targetPackage, Class, sourceMethod); MethodDefinition method = new MethodDefinition(Class, sourceMethod.Name, prototype) { AccessFlags = AccessFlags.Public | AccessFlags.Abstract, MapFileId = Compiler.GetNextMapFileId() }; Class.Methods.Add(method); // Find xSource method targetPackage.NameConverter.Record(sourceMethod, method); // If void() delegate, implement java.lang.Runnable if (sourceMethod.ReturnType.IsVoid() && (sourceMethod.Parameters.Count == 0)) { // Implement interface Class.Interfaces.Add(FrameworkReferences.Runnable); // Build run method var run = new MethodDefinition(Class, "run", new Prototype(PrimitiveType.Void)) { AccessFlags = AccessFlags.Public | AccessFlags.Final }; Class.Methods.Add(run); run.Body = new DexLib.Instructions.MethodBody(run, 1) { IncomingArguments = 1, OutgoingArguments = 1 }; var insList = run.Body.Instructions; var rThis = run.Body.Registers[0]; insList.Add(new DexLib.Instructions.Instruction(OpCodes.Invoke_virtual, method, rThis)); insList.Add(new DexLib.Instructions.Instruction(OpCodes.Return_void)); } }
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 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> /// 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> /// 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); }
/// <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)); }
/// <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)); }
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); }
/// <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> /// 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> /// Load the GenericInstance of the current static method. /// The result is a register that cannot be destroyed. /// </summary> private static AstExpression LoadStaticClassGenericInstance(ISourceLocation seqp, XTypeSystem typeSystem) { return(new AstExpression(seqp, AstCode.LdGenericInstanceTypeArgument, null) { ExpectedType = new XArrayType(typeSystem.Type) }); }
/// <summary> /// Create an expression that loads the given type at runtime. /// </summary> private static AstExpression LoadTypeForGenericInstance(ISourceLocation seqp, MethodSource currentMethod, XTypeReference type, XTypeDefinition typeHelperType, XTypeSystem typeSystem, bool boxPrimitiveTypes = true) { if (type.IsArray) { // Array type var arrayType = (XArrayType)type; // Load element type var prefix = LoadTypeForGenericInstance(seqp, currentMethod, ((XArrayType)type).ElementType, typeHelperType, typeSystem, boxPrimitiveTypes); // 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 gi; 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 DLog.Warning(DContext.CompilerCodeGenerator, "Class constructor of {0} tries to use generic parameter. This will always yield Object.", currentMethod.DeclaringTypeFullName); return(new AstExpression(seqp, AstCode.TypeOf, typeSystem.Object) { ExpectedType = typeSystem.Type }); } gi = currentMethod.IsStatic ? LoadStaticClassGenericInstance(seqp, typeSystem) : LoadInstanceClassGenericInstance(seqp, typeSystem); } 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, typeHelperType, typeSystem, boxPrimitiveTypes)); } gi = LoadMethodGenericInstance(seqp, typeSystem); } var indexExpr = new AstExpression(seqp, AstCode.Ldc_I4, gp.Position) { ExpectedType = typeSystem.Int }; return(new AstExpression(seqp, AstCode.Ldelem_Ref, null, gi, indexExpr) { ExpectedType = typeSystem.Type }); } if (type is XTypeSpecification) { // Just use the element type var typeSpec = (XTypeSpecification)type; return(LoadTypeForGenericInstance(seqp, currentMethod, typeSpec.ElementType, typeHelperType, typeSystem, boxPrimitiveTypes)); } if (type.IsPrimitive && boxPrimitiveTypes) { return(new AstExpression(seqp, AstCode.BoxedTypeOf, type) { ExpectedType = typeSystem.Type }); } // Plain type reference or definition return(new AstExpression(seqp, AstCode.TypeOf, type) { ExpectedType = typeSystem.Type }); }
/// <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 })); } }
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> /// 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> /// 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 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 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> /// 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> /// 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> /// 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; }