public static Annotation GetGenericDefinitionAnnotationForType(XTypeReference xtype, bool forceTypeDefinition, AssemblyCompiler compiler, DexTargetPackage targetPackage) { var genericsDefAnnotationClass = compiler.GetDot42InternalType(InternalConstants.GenericDefinitionAnnotation) .GetClassReference(targetPackage); var annotation = new Annotation {Type = genericsDefAnnotationClass, Visibility = AnnotationVisibility.Runtime}; if (xtype.IsGenericInstance) { bool handled = false; List<object> genericArguments = new List<object>(); if (xtype.GetElementType().IsNullableT()) { // privitives and enums are represented by their marker classes. // no annotation needed. var argument = ((XGenericInstanceType) xtype).GenericArguments[0]; if (argument.IsEnum() || argument.IsPrimitive && !forceTypeDefinition) { return null; } // structs have marker classes. var classRef = xtype.GetReference(targetPackage) as ClassReference; var @class = classRef == null ? null : targetPackage.DexFile.GetClass(classRef.Fullname); if (@class != null && @class.NullableMarkerClass != null) { annotation.Arguments.Add(new AnnotationArgument("GenericInstanceType", @class.NullableMarkerClass)); handled = true; } } if (!handled) { foreach (var arg in ((XGenericInstanceType) xtype).GenericArguments) { if (arg.IsGenericParameter) { var gparm = (XGenericParameter) arg; // TODO: if we wanted to annotate methods as well, we should differentiate // between generic method arguments and generic type arguments. genericArguments.Add(gparm.Position); } else if (arg.IsGenericInstance) { var giparm = GetGenericDefinitionAnnotationForType((XGenericInstanceType) arg, true,compiler, targetPackage); genericArguments.Add(giparm); } else { genericArguments.Add(arg.GetReference(targetPackage)); } } annotation.Arguments.Add(new AnnotationArgument("GenericArguments", genericArguments.ToArray())); } } else // generic parameter { var parm = (XGenericParameter) xtype; annotation.Arguments.Add(new AnnotationArgument("GenericParameter", parm.Position)); } if(forceTypeDefinition && annotation.Arguments.All(a => a.Name != "GenericInstanceType")) annotation.Arguments.Add(new AnnotationArgument("GenericTypeDefinition", xtype.ElementType.GetReference(targetPackage))); return annotation; }
/// <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> /// Gets the deepest <see cref="XTypeReference.ElementType"/>. /// </summary> public override XTypeReference GetElementType() { return(elementType.GetElementType()); }
/// <summary> /// The syntax is experimental, and neither fully fixed nor officially defined. /// </summary> private static string GetDefinition(XTypeReference xtype, bool forceTypeDefinition, AssemblyCompiler compiler, DexTargetPackage targetPackage) { StringBuilder s = new StringBuilder(); // TODO: reorganize, so that the syntax becomes clear. bool setGenericInstanceType = false; if (xtype.IsGenericInstance) { bool handled = false; if (xtype.GetElementType().IsNullableT()) { // privitives and enums are represented by their marker classes. // no annotation needed. var argument = ((XGenericInstanceType) xtype).GenericArguments[0]; if (!forceTypeDefinition && (argument.IsEnum() || argument.IsPrimitive)) { return ""; } // structs have marker classes. var classRef = xtype.GetReference(targetPackage) as ClassReference; var @class = classRef == null ? null : targetPackage.DexFile.GetClass(classRef.Fullname); if (@class != null && @class.NullableMarkerClass != null) { s.Append("@"); s.Append(GetClassName(@class.NullableMarkerClass)); setGenericInstanceType = true; handled = true; } } if (!handled) { bool isFirst = true; foreach (var arg in ((XGenericInstanceType) xtype).GenericArguments) { if (!isFirst) s.Append(","); isFirst = false; if (arg.IsGenericParameter) { var gparm = (XGenericParameter) arg; // TODO: if we wanted to annotate methods as well, we should differentiate // between generic method arguments and generic type arguments. s.Append("!"); s.Append(gparm.Position); } else if (arg.IsGenericInstance) { var giparm = GetDefinition((XGenericInstanceType)arg, true, compiler, targetPackage); s.Append("{"); s.Append(giparm); s.Append("}"); } else { s.Append(GetClassName(arg.GetReference(targetPackage))); } } } } else // generic parameter { var parm = (XGenericParameter) xtype; s.Append("!!"); s.Append(parm.Position); } if (forceTypeDefinition && !setGenericInstanceType) { string def = GetClassName(xtype.ElementType.GetReference(targetPackage)); s.Insert(0, def + "<"); s.Append(">"); } return s.ToString(); }