/// <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; }
public static void CreateAssemblyTypes(AssemblyCompiler compiler, DexTargetPackage targetPackage, IEnumerable <TypeDefinition> reachableTypes) { var xAssemblyTypes = compiler.GetDot42InternalType("AssemblyTypes"); var assemblyTypes = (ClassDefinition)xAssemblyTypes.GetClassReference(targetPackage); var entryAssembly = assemblyTypes.Fields.First(f => f.Name == "EntryAssembly"); var iAssemblyTypes = compiler.GetDot42InternalType("IAssemblyTypes").GetClassReference(targetPackage); entryAssembly.Value = compiler.Assemblies.First().Name.Name; List <object> values = new List <object>(); string prevAssemblyName = null; foreach (var type in reachableTypes.OrderBy(t => t.Module.Assembly.Name.Name) .ThenBy(t => t.Namespace) .ThenBy(t => t.Name)) { var assemblyName = type.module.Assembly.Name.Name; if (assemblyName == "dot42") { // group all android types into virtual "android" assembly, // so that MvvmCross can find all view-types. // -- is this a hack? if (type.Namespace.StartsWith("Android")) { assemblyName = "android"; } else // ignore other types, these will get the "default" assembly. { continue; } } if (prevAssemblyName != assemblyName) { values.Add("!" + assemblyName); // we need some identification of assemblies. prevAssemblyName = assemblyName; } // TODO: With compilationmode=all reachable types contains <Module> // this should be excluded earlier. if (type.FullName == "<Module>") { continue; } var tRef = type.GetReference(targetPackage, compiler.Module) as ClassReference; if (tRef != null) { values.Add(tRef.Fullname); } } var anno = new Annotation(iAssemblyTypes, AnnotationVisibility.Runtime, new AnnotationArgument("AssemblyTypeList", values.ToArray())); ((IAnnotationProvider)assemblyTypes).Annotations.Add(anno); }
/// <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> /// 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; }
public static void CreateAssemblyTypes(AssemblyCompiler compiler, DexTargetPackage targetPackage, IEnumerable<TypeDefinition> reachableTypes) { var xAssemblyTypes = compiler.GetDot42InternalType("AssemblyTypes"); var assemblyTypes = (ClassDefinition)xAssemblyTypes.GetClassReference(targetPackage); var entryAssembly = assemblyTypes.Fields.First(f => f.Name == "EntryAssembly"); var iAssemblyTypes = compiler.GetDot42InternalType("IAssemblyTypes").GetClassReference(targetPackage); entryAssembly.Value = compiler.Assemblies.First().Name.Name; List<object> values = new List<object>(); string prevAssemblyName = null; foreach (var type in reachableTypes.OrderBy(t => t.Module.Assembly.Name.Name) .ThenBy(t => t.Namespace) .ThenBy(t => t.Name)) { var assemblyName = type.module.Assembly.Name.Name; if (assemblyName == "dot42") { // group all android types into virtual "android" assembly, // so that MvvmCross can find all view-types. // -- is this a hack? if (type.Namespace.StartsWith("Android")) assemblyName = "android"; else // ignore other types, these will get the "default" assembly. continue; } if (prevAssemblyName != assemblyName) { values.Add("!" + assemblyName); // we need some identification of assemblies. prevAssemblyName = assemblyName; } // TODO: With compilationmode=all reachable types contains <Module> // this should be excluded earlier. if (type.FullName == "<Module>") continue; var tRef = type.GetReference(targetPackage, compiler.Module) as ClassReference; if(tRef != null) values.Add(tRef.Fullname); } var anno = new Annotation(iAssemblyTypes, AnnotationVisibility.Runtime, new AnnotationArgument("AssemblyTypeList", values.ToArray())); ((IAnnotationProvider)assemblyTypes).Annotations.Add(anno); }
/// <summary> /// Build expression that creates an instance of GenericInstance with arguments from the given .NET generic instance. /// </summary> private static AstExpression CreateGenericInstance(ISourceLocation seqp, XReference member, MethodSource currentMethod, AssemblyCompiler compiler) { // Prepare var genericInstance = member as IXGenericInstance; if (genericInstance == null) { #if DEBUG //Debugger.Launch(); #endif throw new CompilerException(string.Format("{0} is not a generic instance", member)); } var count = genericInstance.GenericArguments.Count; var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); // Foreach type argument var typeExpressions = new List <AstExpression>(); for (var i = 0; i < count; i++) { var argType = genericInstance.GenericArguments[i]; var typeExpr = LoadTypeForGenericInstance(seqp, currentMethod, argType, typeHelperType, compiler.Module.TypeSystem); typeExpressions.Add(typeExpr); } var elementType = compiler.Module.TypeSystem.Type; return(new AstExpression(seqp, AstCode.InitArrayFromArguments, new XArrayType(elementType), typeExpressions) { ExpectedType = new XArrayType(elementType) }); }
/// <summary> /// Create annotations for all included attributes /// </summary> public static void Create(AssemblyCompiler compiler, ICustomAttributeProvider attributeProvider, IAnnotationProvider annotationProvider, DexTargetPackage targetPackage, bool customAttributesOnly = false) { if (!attributeProvider.HasCustomAttributes) return; var annotations = new List<Annotation>(); foreach (var attr in attributeProvider.CustomAttributes) { var attributeType = attr.AttributeType.Resolve(); if (!attributeType.HasIgnoreAttribute()) { Create(compiler, attr, attributeType, annotations, targetPackage); } } if (annotations.Count > 0) { // Create 1 IAttributes annotation var attrsAnnotation = new Annotation { Visibility = AnnotationVisibility.Runtime }; attrsAnnotation.Type = compiler.GetDot42InternalType("IAttributes").GetClassReference(targetPackage); attrsAnnotation.Arguments.Add(new AnnotationArgument("Attributes", annotations.ToArray())); annotationProvider.Annotations.Add(attrsAnnotation); } if (!customAttributesOnly) { // Add annotations specified using AnnotationAttribute foreach (var attr in attributeProvider.CustomAttributes.Where(IsAnnotationAttribute)) { var annotationType = (TypeReference) attr.ConstructorArguments[0].Value; var annotationClass = annotationType.GetClassReference(targetPackage, compiler.Module); annotationProvider.Annotations.Add(new Annotation(annotationClass, AnnotationVisibility.Runtime)); } } }
/// <summary> /// Create the body of the unbox(object) method. /// </summary> private AstBlock CreateUnboxBody(XSyntheticMethodDefinition method, bool isWide, AssemblyCompiler compiler) { // Prepare var loc = AstNode.NoSource; Func <AstExpression> ldValue = () => new AstExpression(loc, AstCode.Ldloc, method.AstParameters[0]); var afterMyEnum = new AstLabel(loc, "_afterMyEnum"); // value instanceof MyEnumType? var ifNotInstanceOfMyEnum = new AstExpression(loc, AstCode.Brfalse, afterMyEnum, new AstExpression(loc, AstCode.SimpleInstanceOf, XType, ldValue())); var returnMyEnum = new AstExpression(loc, AstCode.Ret, null, new AstExpression(loc, AstCode.SimpleCastclass, XType, ldValue())); // boxToMyEnum(UnboxInteger(value)) var boxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var unboxNumericMethod = boxingType.Methods.First(x => x.Name == (isWide ? "UnboxLong" : "UnboxInteger")); var unboxToNumeric = new AstExpression(loc, AstCode.Call, unboxNumericMethod, ldValue()); var numericToMyEnum = new AstExpression(loc, isWide ? AstCode.Long_to_enum : AstCode.Int_to_enum, XType, unboxToNumeric).SetType(XType); var returnX = new AstExpression(loc, AstCode.Ret, null, numericToMyEnum); var ast = new AstBlock(loc, new AstNode[] { ifNotInstanceOfMyEnum, returnMyEnum, afterMyEnum, returnX }); return(ast); }
/// <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 annotation for the given attribute /// </summary> private static void CreateAttributeAnnotation(AssemblyCompiler compiler, CustomAttribute attribute, TypeDefinition attributeType, List <Annotation> annotationList, DexTargetPackage targetPackage) { // Gets the mapping for the type of attribute var mapping = compiler.GetAttributeAnnotationMapping(attributeType); MethodDefinition factoryMethod; // Note: not multithreading capable. see my comments elsewhere. if (mapping.FactoryMethodMap.ContainsKey(attribute)) { factoryMethod = mapping.FactoryMethodMap[attribute]; } else { // create the factory method. factoryMethod = CreateFactoryMethod(compiler, targetPackage, attribute, mapping); mapping.FactoryMethodMap[attribute] = factoryMethod; } // Create attribute annotation var attrAnnotation = new Annotation { Visibility = AnnotationVisibility.Runtime }; attrAnnotation.Type = compiler.GetDot42InternalType("IAttribute").GetClassReference(targetPackage); attrAnnotation.Arguments.Add(new AnnotationArgument("AttributeType", attributeType.GetReference(targetPackage, compiler.Module))); attrAnnotation.Arguments.Add(new AnnotationArgument("FactoryMethod", factoryMethod.Name)); // Add annotation annotationList.Add(attrAnnotation); }
/// <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> /// Gets a unbox method for the given primitive type. /// </summary> internal static MethodReference GetUnboxValueMethod(XTypeReference type, AssemblyCompiler compiler, DexTargetPackage targetPackage, out RCode convertAfterCode) { var info = Get(type); convertAfterCode = info.convertAfterCode; var boxingClass = compiler.GetDot42InternalType("Boxing").GetClassReference(targetPackage); return(new MethodReference(boxingClass, info.unboxMethodName, new Prototype(info.primitiveType, new Parameter(FrameworkReferences.Object, "value")))); }
/// <summary> /// Create an annotation for the given attribute /// </summary> private static void Create(AssemblyCompiler compiler, CustomAttribute attribute, TypeDefinition attributeType, List <Annotation> annotationList, DexTargetPackage targetPackage) { // Gets the mapping for the type of attribute var mapping = compiler.GetAttributeAnnotationType(attributeType); var ctorMap = mapping.CtorMap[attribute.Constructor.Resolve()]; // Create annotation var annotation = new Annotation { Visibility = AnnotationVisibility.Runtime }; annotation.Type = mapping.AnnotationInterfaceClass; // Add ctor arguments var argIndex = 0; foreach (var arg in attribute.ConstructorArguments) { var name = ctorMap.ArgumentGetters[argIndex].Name; annotation.Arguments.Add(CreateAnnotationArgument(name, arg.Type, arg.Value, targetPackage, compiler.Module)); argIndex++; } // Add field values foreach (var arg in attribute.Fields) { var entry = mapping.FieldToGetMethodMap.First(x => x.Key.Name == arg.Name); var name = entry.Value.Name; annotation.Arguments.Add(CreateAnnotationArgument(name, arg.Argument.Type, arg.Argument.Value, targetPackage, compiler.Module)); } // Add property values foreach (var arg in attribute.Properties) { if (mapping.PropertyToGetMethodMap.Keys.Any(x => x.Name == arg.Name)) { var entry = mapping.PropertyToGetMethodMap.First(x => x.Key.Name == arg.Name); var name = entry.Value.Name; annotation.Arguments.Add(CreateAnnotationArgument(name, arg.Argument.Type, arg.Argument.Value, targetPackage, compiler.Module)); } } // Create attribute annotation var attrAnnotation = new Annotation { Visibility = AnnotationVisibility.Runtime }; attrAnnotation.Type = compiler.GetDot42InternalType("IAttribute").GetClassReference(targetPackage); attrAnnotation.Arguments.Add(new AnnotationArgument("AttributeBuilder", ctorMap.Builder)); attrAnnotation.Arguments.Add(new AnnotationArgument("AttributeType", attributeType.GetReference(targetPackage, compiler.Module))); attrAnnotation.Arguments.Add(new AnnotationArgument("Annotation", annotation)); // Add annotation annotationList.Add(attrAnnotation); }
internal static void FinalizeFrameworkPropertyAnnotations(AssemblyCompiler compiler, DexTargetPackage targetPackage) { var reflInfoRef = compiler.GetDot42InternalType("Dot42.Internal.ReflectionInfo", "FrameworkPropertyInfoProvider") .GetClassReference(targetPackage); var relfInfo = targetPackage.DexFile.GetClass(reflInfoRef.Fullname); var propertiesClass = compiler.GetDot42InternalType("IProperties").GetClassReference(targetPackage); var properties = relfInfo.Annotations.FirstOrDefault(a => a.Type.Fullname == propertiesClass.Fullname); if (properties == null) { return; } var property = properties.Arguments.Single(); properties.Arguments.Clear(); properties.Arguments.Add(new AnnotationArgument(property.Name, ((List <Annotation>)property.Value).ToArray())); }
/// <summary> /// Create code to unbox the given source array of boxed type elements into an array of primitive elements. /// </summary> public static RLRange UnboxGenericArray(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec source, RegisterSpec boxedArray, XTypeReference type, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler) { var internalBoxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var ilUnboxMethod = internalBoxingType.Methods.First(x => x.EqualsName("UnboxTo") && (x.Parameters.Count == 2) && (x.Parameters[1].ParameterType.IsSame(type, true))); var unboxMethod = ilUnboxMethod.GetReference(targetPackage); var call = builder.Add(sequencePoint, RCode.Invoke_static, unboxMethod, boxedArray, source); return(new RLRange(call, null)); }
/// <summary> /// Add generic parameters to the prototype based on the given XMethodDefinition /// </summary> public static void AddGenericParameters(AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition method, Prototype result) { if (method.NeedsGenericInstanceTypeParameter) { var annType = compiler.GetDot42InternalType(InternalConstants.GenericTypeParameterAnnotation).GetClassReference(targetPackage); int numParameters = method.DeclaringType.GenericParameters.Count; var buildAsArray = numParameters > InternalConstants.GenericTypeParametersAsArrayThreshold; var parameterArray = result.GenericInstanceTypeParameters; AddGenericParameterArguments(result, buildAsArray, numParameters, "__$$git", annType, parameterArray); } if (method.NeedsGenericInstanceMethodParameter) { // Add GenericInstance parameter var annType = compiler.GetDot42InternalType(InternalConstants.GenericMethodParameterAnnotation).GetClassReference(targetPackage); int numParameters = method.GenericParameters.Count; var buildAsArray = numParameters > InternalConstants.GenericMethodParametersAsArrayThreshold; var parameterArray = result.GenericInstanceMethodParameters; AddGenericParameterArguments(result, buildAsArray, numParameters, "__$$gim", annType, parameterArray); } }
/// <summary> /// Add generic parameters to the prototype based on the given XMethodDefinition /// </summary> public static void AddGenericParameters(AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition method, Prototype result) { if (method.NeedsGenericInstanceTypeParameter) { var annType = compiler.GetDot42InternalType(InternalConstants.GenericTypeParameterAnnotation).GetClassReference(targetPackage); int numParameters = method.DeclaringType.GenericParameters.Count; var buildAsArray = numParameters > InternalConstants.GenericTypeParametersAsArrayThreshold; var parameterArray = result.GenericInstanceTypeParameters; AddGenericParameterArguments(result, buildAsArray, numParameters, "__$$git", annType, parameterArray); } if (method.NeedsGenericInstanceMethodParameter) { // Add GenericInstance parameter var annType = compiler.GetDot42InternalType(InternalConstants.GenericMethodParameterAnnotation).GetClassReference(targetPackage); int numParameters = method.GenericParameters.Count; var buildAsArray = numParameters > InternalConstants.GenericMethodParametersAsArrayThreshold; var parameterArray = result.GenericInstanceMethodParameters; AddGenericParameterArguments(result, buildAsArray, numParameters, "__$$gim", annType, parameterArray); } }
/// <summary> /// Create code to unbox the given source array of boxed type elements resulting from a call into an array of primitive elements. /// </summary> public static RLRange UnboxGenericArrayResult(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec boxedArray, XTypeReference type, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler) { var internalBoxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var primitiveArray = frame.AllocateTemp(type.GetReference(targetPackage)); var ilUnboxMethod = internalBoxingType.Methods.First(x => x.Name.StartsWith("Unbox") && (x.Parameters.Count == 1) && (x.ReturnType.IsSame(type, true))); var unboxMethod = ilUnboxMethod.GetReference(targetPackage); var call = builder.Add(sequencePoint, RCode.Invoke_static, unboxMethod, boxedArray); var saveArray = builder.Add(sequencePoint, RCode.Move_result_object, primitiveArray); return(new RLRange(call, saveArray, primitiveArray)); }
/// <summary> /// Create code to box the given source array of primitive type elements into an array of boxed elements. /// </summary> public static RLRange BoxGenericArray(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec source, XTypeReference type, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler) { var objectArrayType = new DexLib.ArrayType(new ClassReference("java.lang.Object")); var boxedArray = frame.AllocateTemp(objectArrayType); var internalBoxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var ilBoxMethod = internalBoxingType.Methods.First(x => x.EqualsName("Box") && (x.Parameters.Count == 1) && (x.Parameters[0].ParameterType.IsSame(type, true))); var boxMethod = ilBoxMethod.GetReference(targetPackage); var call = builder.Add(sequencePoint, RCode.Invoke_static, boxMethod, source); var saveArray = builder.Add(sequencePoint, RCode.Move_result_object, boxedArray); return(new RLRange(call, saveArray, boxedArray)); }
/// <summary> /// Create a prototype for the given methods signature /// </summary> internal static Prototype BuildPrototype(AssemblyCompiler compiler, DexTargetPackage targetPackage, ClassDefinition declaringClass, XMethodDefinition method) { var result = new Prototype(); result.ReturnType = method.ReturnType.GetReference(targetPackage); if (method.IsAndroidExtension && !method.IsStatic) { // Add "this" parameter var dparameter = new Parameter(method.DeclaringType.GetReference(targetPackage), "this"); result.Parameters.Add(dparameter); } foreach (var p in method.Parameters) { var dparameter = new Parameter(p.ParameterType.GetReference(targetPackage), p.Name); result.Parameters.Add(dparameter); } if (method.NeedsGenericInstanceTypeParameter) { // Add GenericInstance parameter (to pass the generic instance array of the declaring type) var paramType = FrameworkReferences.ClassArray; var dparameter = new Parameter(paramType, "__$$git"); var annType = compiler.GetDot42InternalType(InternalConstants.GenericTypeParameterAnnotation).GetClassReference(targetPackage); dparameter.Annotations.Add(new Annotation(annType, AnnotationVisibility.Runtime)); result.Parameters.Add(dparameter); result.GenericInstanceTypeParameter = dparameter; } if (method.NeedsGenericInstanceMethodParameter) { // Add GenericInstance parameter var paramType = FrameworkReferences.ClassArray; var dparameter = new Parameter(paramType, "__$$gim"); var annType = compiler.GetDot42InternalType(InternalConstants.GenericMethodParameterAnnotation).GetClassReference(targetPackage); dparameter.Annotations.Add(new Annotation(annType, AnnotationVisibility.Runtime)); result.Parameters.Add(dparameter); result.GenericInstanceMethodParameter = dparameter; } return(result); }
/// <summary> /// Create a prototype for the given methods signature /// </summary> internal static Prototype BuildPrototype(AssemblyCompiler compiler, DexTargetPackage targetPackage, ClassDefinition declaringClass, XMethodDefinition method) { var result = new Prototype(); result.ReturnType = method.ReturnType.GetReference(targetPackage); if (method.IsAndroidExtension && !method.IsStatic) { // Add "this" parameter var dparameter = new Parameter(method.DeclaringType.GetReference(targetPackage), "this"); result.Parameters.Add(dparameter); } foreach (var p in method.Parameters) { var dparameter = new Parameter(p.ParameterType.GetReference(targetPackage), p.Name); result.Parameters.Add(dparameter); } if (method.NeedsGenericInstanceTypeParameter) { // Add GenericInstance parameter (to pass the generic instance array of the declaring type) var paramType = FrameworkReferences.ClassArray; var dparameter = new Parameter(paramType, "__$$git"); var annType = compiler.GetDot42InternalType(InternalConstants.GenericTypeParameterAnnotation).GetClassReference(targetPackage); dparameter.Annotations.Add(new Annotation(annType, AnnotationVisibility.Runtime)); result.Parameters.Add(dparameter); result.GenericInstanceTypeParameter = dparameter; } if (method.NeedsGenericInstanceMethodParameter) { // Add GenericInstance parameter var paramType = FrameworkReferences.ClassArray; var dparameter = new Parameter(paramType, "__$$gim"); var annType = compiler.GetDot42InternalType(InternalConstants.GenericMethodParameterAnnotation).GetClassReference(targetPackage); dparameter.Annotations.Add(new Annotation(annType, AnnotationVisibility.Runtime)); result.Parameters.Add(dparameter); result.GenericInstanceMethodParameter = dparameter; } return result; }
/// <summary> /// Create the current type as class definition. /// </summary> internal DelegateInstanceTypeBuilder( ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, ClassDefinition delegateClass, XMethodDefinition invokeMethod, Prototype invokePrototype, XMethodDefinition calledMethod) { this.sequencePoint = sequencePoint; this.compiler = compiler; this.targetPackage = targetPackage; this.delegateClass = delegateClass; this.invokeMethod = invokeMethod; this.invokePrototype = invokePrototype; this.calledMethod = calledMethod; this.multicastDelegateClass = compiler.GetDot42InternalType("System", "MulticastDelegate").GetClassReference(targetPackage); }
/// <summary> /// Create the current type as class definition. /// </summary> internal DelegateInstanceTypeBuilder( ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, ClassDefinition delegateClass, XMethodDefinition invokeMethod, Prototype invokePrototype, XMethodDefinition calledMethod) { this.sequencePoint = sequencePoint; this.compiler = compiler; this.targetPackage = targetPackage; this.delegateClass = delegateClass; this.invokeMethod = invokeMethod; this.invokePrototype = invokePrototype; this.calledMethod = calledMethod; this.multicastDelegateClass = compiler.GetDot42InternalType("System", "MulticastDelegate").GetClassReference(targetPackage); }
/// <summary> /// Build expressions that are used when calling a .NET method with generic instance parameters, one expression for /// each required parameter. /// </summary> private static IList <AstExpression> CreateGenericInstanceCallArguments(ISourceLocation seqp, XReference member, MethodSource currentMethod, AssemblyCompiler compiler) { // Prepare var genericInstance = member as IXGenericInstance; if (genericInstance == null) { #if DEBUG //Debugger.Launch(); #endif throw new CompilerException(string.Format("{0} is not a generic instance", member)); } var count = genericInstance.GenericArguments.Count; var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); // Foreach type argument var typeExpressions = new List <AstExpression>(); for (var i = 0; i < count; i++) { var argType = genericInstance.GenericArguments[i]; var typeExpr = LoadTypeForGenericInstance(seqp, currentMethod, argType, compiler, typeHelperType, compiler.Module.TypeSystem, TypeConversion.EnsureTrueOrMarkerType); typeExpressions.Add(typeExpr); } bool isMethod = member is XMethodReference; bool buildArray = count > (isMethod ? InternalConstants.GenericMethodParametersAsArrayThreshold : InternalConstants.GenericTypeParametersAsArrayThreshold); if (buildArray) { //TODO: when we can determine that all expressions just load our classes generic instance (array) field, // and extract all values in sequence, we should shortcut and just return a load of the field, // instead of unpacking and re-packing each value, putting pressure on the garbage collector // in the process. This is not a huge problem any longer, as we do not use arrays as often // as we used to. Nevertheless, where arrays are used, possible re-use should be faily common, // e.g. when instatiating nested classes or invoking static methods. var elementType = compiler.Module.TypeSystem.Type; return(new [] { new AstExpression(seqp, AstCode.InitArrayFromArguments, new XArrayType(elementType), typeExpressions) { ExpectedType = new XArrayType(elementType) } }); } else { return(typeExpressions); } }
public static Annotation CreateAnnotation(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 }; string s = GetDefinition(xtype, forceTypeDefinition, compiler, targetPackage); if (string.IsNullOrEmpty(s)) return null; annotation.Arguments.Add(new AnnotationArgument("Definition", s)); return annotation; }
/// <summary> /// Add references to all implemented interfaces. /// </summary> protected virtual void ImplementInterfaces(DexTargetPackage targetPackage) { // Implement interfaces foreach (var intf in typeDef.Interfaces.Select(x => x.InterfaceType.GetClassReference(targetPackage, compiler.Module)) .Distinct()) { classDef.Interfaces.Add(intf); } if (IsNetGenericType) { var genericMarker = compiler.GetDot42InternalType(InternalConstants.GenericTypeDefinitionMarker); classDef.Interfaces.Add(genericMarker.GetClassReference(targetPackage)); } }
/// <summary> /// Create annotations for all included attributes /// </summary> public static void CreateAttributeAnnotations(AssemblyCompiler compiler, ICustomAttributeProvider attributeProvider, IAnnotationProvider annotationProvider, DexTargetPackage targetPackage, bool customAttributesOnly = false) { if (!attributeProvider.HasCustomAttributes) { return; } var annotations = new List <Annotation>(); foreach (var attr in attributeProvider.CustomAttributes) { var attributeType = attr.AttributeType.Resolve(); if (!attributeType.HasIgnoreAttribute()) { CreateAttributeAnnotation(compiler, attr, attributeType, annotations, targetPackage); } } if (annotations.Count > 0) { // Create 1 IAttributes annotation var attrsAnnotation = new Annotation { Visibility = AnnotationVisibility.Runtime }; attrsAnnotation.Type = compiler.GetDot42InternalType("IAttributes").GetClassReference(targetPackage); attrsAnnotation.Arguments.Add(new AnnotationArgument("Attributes", annotations.ToArray())); annotationProvider.Annotations.Add(attrsAnnotation); } if (!customAttributesOnly) { // Add annotations specified using AnnotationAttribute foreach (var attr in attributeProvider.CustomAttributes.Where(IsAnnotationAttribute)) { var annotationType = (TypeReference)attr.ConstructorArguments[0].Value; var annotationClass = annotationType.GetClassReference(targetPackage, compiler.Module); annotationProvider.Annotations.Add(new Annotation(annotationClass, AnnotationVisibility.Runtime)); } } }
public static Annotation CreateAnnotation(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 }; string s = GetDefinition(xtype, forceTypeDefinition, compiler, targetPackage); if (string.IsNullOrEmpty(s)) { return(null); } annotation.Arguments.Add(new AnnotationArgument("Definition", s)); return(annotation); }
/// <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 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> /// Create an annotation interface. /// </summary> internal static AttributeAnnotationInterface Create( ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, TypeDefinition attributeType, ClassDefinition attributeClass) { // Create class ClassDefinition @interface = new ClassDefinition(); @interface.Name = CreateAnnotationTypeName(attributeClass); @interface.Namespace = attributeClass.Namespace; @interface.AccessFlags = AccessFlags.Public | AccessFlags.Abstract | AccessFlags.Interface | AccessFlags.Annotation; @interface.Owner = attributeClass; attributeClass.InnerClasses.Add(@interface); // Set super class @interface.SuperClass = new ClassReference("java/lang/Object"); // Implement Dot42.Internal.IAttribute @interface.Interfaces.Add(new ClassReference("java/lang/annotation/Annotation")); // Prepare result AttributeAnnotationInterface result = new AttributeAnnotationInterface(@interface); // Add methods from IAttribute XModel.XTypeDefinition baseIntfType = compiler.GetDot42InternalType("IAttribute").Resolve(); foreach (XModel.XMethodDefinition imethod in baseIntfType.Methods) { if (imethod.Parameters.Count > 0) throw new CompilerException(string.Format("Invalid IAttribute method {0}", imethod)); string methodName = NameConverter.GetConvertedName(imethod); TypeReference dfieldType = imethod.ReturnType.GetReference(targetPackage); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dfieldType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; @interface.Methods.Add(method); } // Add field mapping foreach (var field in attributeType.Fields.Where(x => x.IsReachable && x.IsPublic)) { string methodName = CreateGetMethodName(NameConverter.GetConvertedName(field), result); TypeReference dfieldType = field.FieldType.GetReference(targetPackage, compiler.Module); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dfieldType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; result.FieldToGetMethodMap.Add(field, method); @interface.Methods.Add(method); } // Add property mapping foreach (var property in attributeType.Properties.Where(x => x.IsReachable && (x.SetMethod != null) && (x.SetMethod.IsPublic) && x.SetMethod.IsReachable)) { string methodName = CreateGetMethodName(NameConverter.GetConvertedName(property), result); TypeReference dpropType = property.PropertyType.GetReference(targetPackage, compiler.Module); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dpropType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; result.PropertyToGetMethodMap.Add(property, method); @interface.Methods.Add(method); } // Add ctor mapping var argIndex = 0; foreach (var ctor in attributeType.Methods.Where(x => (x.Name == ".ctor") && x.IsReachable)) { // Add methods for the ctor arguments List<MethodDefinition> paramGetMethods = new List<MethodDefinition>(); foreach (ParameterDefinition p in ctor.Parameters) { string methodName = CreateGetMethodName("c" + argIndex++, result); TypeReference dparamType = p.ParameterType.GetReference(targetPackage, compiler.Module); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dparamType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; @interface.Methods.Add(method); paramGetMethods.Add(method); } // Add a builder method MethodDefinition buildMethod = CreateBuildMethod(sequencePoint, ctor, paramGetMethods, compiler, targetPackage, attributeClass, result); result.CtorMap.Add(ctor, new AttributeCtorMapping(buildMethod, paramGetMethods)); } // Create default values annotation Annotation defAnnotation = CreateDefaultAnnotation(result); result.AnnotationInterfaceClass.Annotations.Add(defAnnotation); return result; }
/// <summary> /// Build expressions that are used when calling a .NET method with generic instance parameters, one expression for /// each required parameter. /// </summary> private static IList<AstExpression> CreateGenericInstanceCallArguments(ISourceLocation seqp, XReference member, MethodSource currentMethod, AssemblyCompiler compiler) { // Prepare var genericInstance = member as IXGenericInstance; if (genericInstance == null) { #if DEBUG //Debugger.Launch(); #endif throw new CompilerException(string.Format("{0} is not a generic instance", member)); } var count = genericInstance.GenericArguments.Count; var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); // Foreach type argument var typeExpressions = new List<AstExpression>(); for (var i = 0; i < count; i++) { var argType = genericInstance.GenericArguments[i]; var typeExpr = LoadTypeForGenericInstance(seqp, currentMethod, argType, compiler, typeHelperType, compiler.Module.TypeSystem, TypeConversion.EnsureTrueOrMarkerType); typeExpressions.Add(typeExpr); } bool isMethod = member is XMethodReference; bool buildArray = count > (isMethod ? InternalConstants.GenericMethodParametersAsArrayThreshold : InternalConstants.GenericTypeParametersAsArrayThreshold); if (buildArray) { //TODO: when we can determine that all expressions just load our classes generic instance (array) field, // and extract all values in sequence, we should shortcut and just return a load of the field, // instead of unpacking and re-packing each value, putting pressure on the garbage collector // in the process. This is not a huge problem any longer, as we do not use arrays as often // as we used to. Nevertheless, where arrays are used, possible re-use should be faily common, // e.g. when instatiating nested classes or invoking static methods. var elementType = compiler.Module.TypeSystem.Type; return new [] { new AstExpression(seqp, AstCode.InitArrayFromArguments, new XArrayType(elementType), typeExpressions) { ExpectedType = new XArrayType(elementType) }}; } else { return typeExpressions; } }
/// <summary> /// Create code to unbox the given source array of boxed type elements into an array of primitive elements. /// </summary> public static RLRange UnboxGenericArray(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec source, RegisterSpec boxedArray, XTypeReference type, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler) { var internalBoxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var ilUnboxMethod = internalBoxingType.Methods.First(x => x.EqualsName("UnboxTo") && (x.Parameters.Count == 2) && (x.Parameters[1].ParameterType.IsSame(type, true))); var unboxMethod = ilUnboxMethod.GetReference(targetPackage); var call = builder.Add(sequencePoint, RCode.Invoke_static, unboxMethod, boxedArray, source); return new RLRange(call, null); }
/// <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> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, AssemblyCompiler compiler) { var typeSystem = compiler.Module.TypeSystem; // Expand typeof foreach (var node in ast.GetSelfAndChildrenRecursive <AstExpression>(x => x.Code == AstCode.TypeOf)) { var type = (XTypeReference)node.Operand; var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); var loadExpr = LoadTypeForGenericInstance(node.SourceLocation, currentMethod, type, compiler, typeHelperType, typeSystem, TypeConversion.EnsureTrueOrMarkerType); node.CopyFrom(loadExpr); } // Expand instanceOf foreach (var node in ast.GetSelfAndChildrenRecursive <AstExpression>(x => x.Code == AstCode.SimpleInstanceOf)) { var type = (XTypeReference)node.Operand; var gp = type as XGenericParameter; if (gp == null) { continue; } var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); var loadExpr = LoadTypeForGenericInstance(node.SourceLocation, currentMethod, type, compiler, typeHelperType, typeSystem, TypeConversion.EnsureRuntimeType); //// both types are boxed, no need for conversion. var typeType = compiler.GetDot42InternalType("System", "Type").Resolve(); var isInstanceOfType = typeType.Methods.Single(n => n.Name == "JavaIsInstance" && n.Parameters.Count == 1); var call = new AstExpression(node.SourceLocation, AstCode.Call, isInstanceOfType, loadExpr, node.Arguments[0]); node.CopyFrom(call); } // Expand newarr foreach (var node in ast.GetSelfAndChildrenRecursive <AstExpression>(x => x.Code == AstCode.Newarr)) { var type = (XTypeReference)node.Operand; if (!type.IsDefinitionOrReferenceOrPrimitive()) { // Resolve type to a Class<?> var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); // while having primitive arrays for primitive types would be nice, a lot of boxing and unboxing // would be needed. only for-primitive-specialized generic classes we could optimize this. var ldType = LoadTypeForGenericInstance(node.SourceLocation, currentMethod, type, compiler, typeHelperType, typeSystem, TypeConversion.EnsureRuntimeType); var newInstanceExpr = new AstExpression(node.SourceLocation, AstCode.ArrayNewInstance, null, ldType, node.Arguments[0]) { ExpectedType = typeSystem.Object }; var arrayType = new XArrayType(type); var cast = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, arrayType, newInstanceExpr) { ExpectedType = arrayType }; node.CopyFrom(cast); } } // Add generic instance call arguments foreach (var node in ast.GetSelfAndChildrenRecursive <AstExpression>(x => x.Code.IsCall())) { var method = (XMethodReference)node.Operand; if (method.DeclaringType.IsArray) { continue; } XMethodDefinition methodDef; if (!method.TryResolve(out methodDef)) { continue; } if (methodDef.HasDexNativeAttribute()) { continue; } if (methodDef.NeedsGenericInstanceTypeParameter) { // Add generic instance type parameter value var arg = CreateGenericInstanceCallArguments(node.SourceLocation, method.DeclaringType, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } if (methodDef.NeedsGenericInstanceMethodParameter) { // Add generic instance method parameter var arg = CreateGenericInstanceCallArguments(node.SourceLocation, method, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } } // Add generic instance Delegate arguments for static methods. foreach (var node in ast.GetSelfAndChildrenRecursive <AstExpression>(x => x.Code == AstCode.Delegate)) { var delegateInfo = (Tuple <XTypeDefinition, XMethodReference>)node.Operand; var genMethodDef = delegateInfo.Item2 as IXGenericInstance; var genTypeDef = delegateInfo.Item2.DeclaringType as IXGenericInstance; // Add generic instance type parameter value, if method is static if (genTypeDef != null && delegateInfo.Item2.Resolve().IsStatic) { var arg = CreateGenericInstanceCallArguments(node.SourceLocation, delegateInfo.Item2.DeclaringType, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } // add generic method type parameter value. if (genMethodDef != null) { var arg = CreateGenericInstanceCallArguments(node.SourceLocation, delegateInfo.Item2, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } } // Convert NewObj when needed foreach (var node in ast.GetSelfAndChildrenRecursive <AstExpression>(x => x.Code == AstCode.Newobj)) { var ctorRef = (XMethodReference)node.Operand; var declaringType = ctorRef.DeclaringType; if (declaringType.IsArray) { // New multi dimensional array // Get element type var elemType = ((XArrayType)declaringType).ElementType; var typeExpr = new AstExpression(node.SourceLocation, AstCode.TypeOf, elemType); // Create dimensions array var intArrayType = new XArrayType(typeSystem.Int); var dimArrayExpr = new AstExpression(node.SourceLocation, AstCode.InitArrayFromArguments, intArrayType, node.Arguments).SetType(intArrayType); // Call java.lang.reflect.Array.newInstance(type, int[]) var newInstanceExpr = new AstExpression(node.SourceLocation, AstCode.ArrayNewInstance2, null, typeExpr, dimArrayExpr).SetType(typeSystem.Object); // Cast to correct type var cast = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, declaringType, newInstanceExpr).SetType(declaringType); // Replace node node.CopyFrom(cast); } else { // Normal "new object" XMethodDefinition ctorDef; if (ctorRef.TryResolve(out ctorDef) && ctorDef.NeedsGenericInstanceTypeParameter) { // Add generic instance type parameter value var arg = CreateGenericInstanceCallArguments(node.SourceLocation, ctorRef.DeclaringType, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } } } }
/// <summary> /// Create an annotation interface. /// </summary> internal static AttributeAnnotationInterface Create( ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, TypeDefinition attributeType, ClassDefinition attributeClass) { // Create class ClassDefinition @interface = new ClassDefinition(); @interface.Name = CreateAnnotationTypeName(attributeClass); @interface.Namespace = attributeClass.Namespace; @interface.AccessFlags = AccessFlags.Public | AccessFlags.Abstract | AccessFlags.Interface | AccessFlags.Annotation; @interface.Owner = attributeClass; attributeClass.InnerClasses.Add(@interface); // Set super class @interface.SuperClass = new ClassReference("java/lang/Object"); // Implement Dot42.Internal.IAttribute @interface.Interfaces.Add(new ClassReference("java/lang/annotation/Annotation")); // Prepare result AttributeAnnotationInterface result = new AttributeAnnotationInterface(@interface); // Add methods from IAttribute XModel.XTypeDefinition baseIntfType = compiler.GetDot42InternalType("IAttribute").Resolve(); foreach (XModel.XMethodDefinition imethod in baseIntfType.Methods) { if (imethod.Parameters.Count > 0) { throw new CompilerException(string.Format("Invalid IAttribute method {0}", imethod)); } string methodName = NameConverter.GetConvertedName(imethod); TypeReference dfieldType = imethod.ReturnType.GetReference(targetPackage); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dfieldType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; @interface.Methods.Add(method); } // Add field mapping foreach (var field in attributeType.Fields.Where(x => x.IsReachable && x.IsPublic)) { string methodName = CreateGetMethodName(NameConverter.GetConvertedName(field), result); TypeReference dfieldType = field.FieldType.GetReference(targetPackage, compiler.Module); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dfieldType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; result.FieldToGetMethodMap.Add(field, method); @interface.Methods.Add(method); } // Add property mapping foreach (var property in attributeType.Properties.Where(x => x.IsReachable && (x.SetMethod != null) && (x.SetMethod.IsPublic) && x.SetMethod.IsReachable)) { string methodName = CreateGetMethodName(NameConverter.GetConvertedName(property), result); TypeReference dpropType = property.PropertyType.GetReference(targetPackage, compiler.Module); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dpropType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; result.PropertyToGetMethodMap.Add(property, method); @interface.Methods.Add(method); } // Add ctor mapping var argIndex = 0; foreach (var ctor in attributeType.Methods.Where(x => (x.Name == ".ctor") && x.IsReachable)) { // Add methods for the ctor arguments List <MethodDefinition> paramGetMethods = new List <MethodDefinition>(); foreach (ParameterDefinition p in ctor.Parameters) { string methodName = CreateGetMethodName("c" + argIndex++, result); TypeReference dparamType = p.ParameterType.GetReference(targetPackage, compiler.Module); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dparamType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; @interface.Methods.Add(method); paramGetMethods.Add(method); } // Add a builder method MethodDefinition buildMethod = CreateBuildMethod(sequencePoint, ctor, paramGetMethods, compiler, targetPackage, attributeClass, result); result.CtorMap.Add(ctor, new AttributeCtorMapping(buildMethod, paramGetMethods)); } // Create default values annotation Annotation defAnnotation = CreateDefaultAnnotation(result); result.AnnotationInterfaceClass.Annotations.Add(defAnnotation); return(result); }
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> /// 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> /// Create code to initialize a value from an attribute. /// </summary> /// <returns>The register(s) holding the value</returns> private static Register[] CreateInitializeValueInstructions(ISourceLocation seqp, MethodBody body, XTypeReference targetType, CustomAttributeArgument value, AssemblyCompiler compiler, DexTargetPackage targetPackage) { List <Register> result = new List <Register>(); // allocate result, initialize to default value. if (targetType.IsWide()) { Tuple <Register, Register> regs = body.AllocateWideRegister(RCategory.Temp); //body.Instructions.Add(seqp, RCode.Const_wide, 0, regs.Item1); result.Add(regs.Item1); result.Add(regs.Item2); } else if (targetType.IsPrimitive) { Register reg = body.AllocateRegister(RCategory.Temp, RType.Value); //body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } else // object { Register reg = body.AllocateRegister(RCategory.Temp, RType.Object); //body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } // load data if (value.Value == null) // must be a reference type { body.Instructions.Add(seqp, RCode.Const, 0, result[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); return(result.ToArray()); } var valueType = XBuilder.AsTypeReference(compiler.Module, value.Type); if (value.Value is CustomAttributeArgument) { // this happens if a type conversion is neccessary var nestedValue = (CustomAttributeArgument)value.Value; valueType = XBuilder.AsTypeReference(compiler.Module, nestedValue.Type); var rOrigValue = CreateInitializeValueInstructions(seqp, body, valueType, nestedValue, compiler, targetPackage); if (!nestedValue.Type.IsPrimitive) { body.Instructions.Add(seqp, RCode.Move_object, result[0], rOrigValue[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else if (!targetType.IsPrimitive) { body.Instructions.Add(seqp, RCode.Invoke_static, valueType.GetBoxValueOfMethod(), rOrigValue); body.Instructions.Add(seqp, RCode.Move_result_object, result[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else { throw new Exception(string.Format("type converstion in attribute {0}=>{1} not yet supported", valueType.FullName, targetType.FullName)); } } else if (valueType.IsArray) { var array = (CustomAttributeArgument[])value.Value; var elementType = valueType.ElementType; Register rIndex = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Const, array.Length, rIndex); body.Instructions.Add(seqp, RCode.New_array, valueType.GetReference(targetPackage), result[0], rIndex); // iterate through each value for (int i = 0; i < array.Length; i++) { Register rLoaded = CreateInitializeValueInstructions(seqp, body, elementType, array[i], compiler, targetPackage)[0]; body.Instructions.Add(seqp, RCode.Const, i, rIndex); body.Instructions.Add(seqp, valueType.APut(), rLoaded, result[0], rIndex); } } else if (targetType.IsEnum()) { var enumClass = (targetType.IsEnum()? targetType:valueType).GetReference(targetPackage); Register rEnumClass = body.AllocateRegister(RCategory.Temp, RType.Object); body.Instructions.Add(seqp, RCode.Const_class, enumClass, rEnumClass); long lVal = Convert.ToInt64(value.Value); if (lVal <= int.MaxValue && lVal >= int.MinValue) { Register regTmp = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Const, (int)lVal, regTmp); var get = compiler.GetDot42InternalType("Enum").Resolve() .Methods.Single(p => p.Name == "Get" && p.Parameters.Count == 2 && !p.Parameters[1].ParameterType.IsWide()) .GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, get, rEnumClass, regTmp); body.Instructions.Add(seqp, targetType.MoveResult(), result[0]); } else { var regTmp = body.AllocateWideRegister(RCategory.Temp); body.Instructions.Add(seqp, RCode.Const, (long)lVal, regTmp.Item1); var get = compiler.GetDot42InternalType("Enum").Resolve() .Methods.Single(p => p.Name == "Get" && p.Parameters.Count == 2 && p.Parameters[1].ParameterType.IsWide()) .GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, get, rEnumClass, regTmp.Item1); body.Instructions.Add(seqp, targetType.MoveResult(), result[0]); } body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else if (valueType.IsSystemString()) { body.Instructions.Add(seqp, RCode.Const_string, (string)value.Value, result[0]); } else if (valueType.IsSystemType()) { var type = XBuilder.AsTypeReference(compiler.Module, (TypeReference)value.Value); // TODO: this might not work with typeof(void) on ART runtime. body.Instructions.Add(seqp, RCode.Const_class, type.GetReference(targetPackage), result[0]); } else if (!valueType.IsPrimitive) { // can this happen? throw new Exception("invalid value type in attribute: " + targetType.FullName); } else { if (targetType.IsSystemObject()) { // can this happen? or is this always handled above? // boxing required. var rUnboxed = CreateInitializeValueInstructions(seqp, body, valueType, value, compiler, targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, valueType.GetBoxValueOfMethod(), rUnboxed); body.Instructions.Add(seqp, RCode.Move_result_object, result[0]); } else if (targetType.IsDouble()) { body.Instructions.Add(seqp, RCode.Const_wide, Convert.ToDouble(value.Value), result[0]); } else if (targetType.IsWide() && valueType.IsUInt64()) { body.Instructions.Add(seqp, RCode.Const_wide, (long)Convert.ToUInt64(value.Value), result[0]); } else if (targetType.IsWide()) { body.Instructions.Add(seqp, RCode.Const_wide, Convert.ToInt64(value.Value), result[0]); } else if (targetType.IsFloat()) { body.Instructions.Add(seqp, RCode.Const, Convert.ToSingle(value.Value), result[0]); } else { body.Instructions.Add(seqp, RCode.Const, (int)Convert.ToInt64(value.Value), result[0]); } } return(result.ToArray()); }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Call) || (x.Code == AstCode.Calli) || (x.Code == AstCode.Callvirt))) { var method = (XMethodReference)node.Operand; bool processed = false; XMethodDefinition methodDef; if (method.TryResolve(out methodDef) && methodDef.DeclaringType.IsPrimitive) { if (methodDef.IsConstructor && (node.Arguments.Count == 2)) { // primitive.ctor(addressof_primitive, value) -> primitive.cast(value) var locVar = node.Arguments[0].Operand; node.Arguments.RemoveAt(0); node.SetCode(AstCode.Stloc).SetType(node.Arguments[0].GetResultType()).Operand = locVar; processed = true; } else if (methodDef.Name == "GetHashCode" && node.Arguments.Count == 1 && methodDef.HasThis) { // we try to avoid boxing. var arg = node.Arguments[0]; switch (arg.Code) { case AstCode.AddressOf: arg = arg.Arguments[0]; break; case AstCode.Ldloca: arg.SetCode(AstCode.Ldloc); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldflda: arg.SetCode(AstCode.Ldfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldsflda: arg.SetCode(AstCode.Ldsfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldelema: arg.SetCode(AstCode.Ldelem_Any); arg.SetType(arg.InferredType.ElementType); break; } var type = arg.GetResultType(); if (type.IsBoolean() || type.IsFloat() || type.IsDouble() || type.IsInt64() || type.IsUInt64()) { var ch = compiler.GetDot42InternalType("CompilerHelper").Resolve(); // these need special handling var replMethod = ch.Methods.FirstOrDefault(m => m.Name == "GetHashCode" && m.IsStatic && m.Parameters[0].ParameterType.IsSame(type, true)); if (replMethod != null) { node.Operand = replMethod; node.SetCode(AstCode.Call); node.Arguments.Clear(); node.Arguments.Add(arg); } } else { // for the other primitive types, we just return the value itself. node.CopyFrom(arg); node.ExpectedType = compiler.Module.TypeSystem.Int; } processed = true; } } if (!processed) { for (var i = 0; i < node.Arguments.Count; i++) { ProcessArgument(node, method, i, currentMethod, compiler.Module); } } } }
internal static void FinalizeFrameworkPropertyAnnotations(AssemblyCompiler compiler, DexTargetPackage targetPackage) { var reflInfoRef = compiler.GetDot42InternalType("Dot42.Internal.ReflectionInfo", "FrameworkPropertyInfoProvider") .GetClassReference(targetPackage); var relfInfo = targetPackage.DexFile.GetClass(reflInfoRef.Fullname); var propertiesClass = compiler.GetDot42InternalType("IProperties").GetClassReference(targetPackage); var properties = relfInfo.Annotations.FirstOrDefault(a => a.Type.Fullname == propertiesClass.Fullname); if (properties == null) return; var property = properties.Arguments.Single(); properties.Arguments.Clear(); properties.Arguments.Add(new AnnotationArgument(property.Name, ((List<Annotation>)property.Value).ToArray())); }
/// <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 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> /// Create an annotation interface. /// </summary> internal static AttributeAnnotationInterface Create( ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, TypeDefinition attributeType, ClassDefinition attributeClass) { // Create class ClassDefinition @interface = new ClassDefinition(); @interface.Name = CreateAnnotationTypeName(attributeClass); @interface.Namespace = attributeClass.Namespace; @interface.AccessFlags = AccessFlags.Public | AccessFlags.Abstract | AccessFlags.Interface | AccessFlags.Annotation; @interface.Owner = attributeClass; attributeClass.AddInnerClass(@interface); // Set super class @interface.SuperClass = FrameworkReferences.Object; // Implement Dot42.Internal.IAttribute @interface.Interfaces.Add(new ClassReference("java/lang/annotation/Annotation")); // Prepare result AttributeAnnotationInterface result = new AttributeAnnotationInterface(@interface); // Add methods from IAttribute XModel.XTypeDefinition baseIntfType = compiler.GetDot42InternalType("IAttribute").Resolve(); foreach (XModel.XMethodDefinition imethod in baseIntfType.Methods) { if (imethod.Parameters.Count > 0) throw new CompilerException(string.Format("Invalid IAttribute method {0}", imethod)); string methodName = NameConverter.GetConvertedName(imethod); TypeReference dfieldType = imethod.ReturnType.GetReference(targetPackage); MethodDefinition method = new MethodDefinition(@interface, methodName, new Prototype(dfieldType)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; @interface.Methods.Add(method); } TypeDefinition currentType = attributeType; while (currentType != null && currentType.FullName != typeof(Attribute).FullName) { // Add field mapping foreach (var field in currentType.Fields.Where(x => x.IsReachable && x.IsPublic)) { string methodName = CreateGetMethodName(NameConverter.GetConvertedName(field), result); MethodDefinition method = new MethodDefinition(@interface, methodName, MakePrototype(field.FieldType, targetPackage, compiler.Module)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; result.FieldToGetMethodMap.Add(field, method); @interface.Methods.Add(method); } // Add property mapping foreach (var property in currentType.Properties.Where( x => x.IsReachable && (x.SetMethod != null) && x.SetMethod.IsPublic && x.SetMethod.IsReachable)) { // ignore properties with same name [might be overriden] if (result.PropertyToGetMethodMap.Keys.Any(k => k.Name == property.Name)) continue; string methodName = CreateGetMethodName(NameConverter.GetConvertedName(property), result); Mono.Cecil.TypeReference propType = property.PropertyType; MethodDefinition method = new MethodDefinition(@interface, methodName, MakePrototype(propType, targetPackage, compiler.Module)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; result.PropertyToGetMethodMap.Add(property, method); @interface.Methods.Add(method); } if (currentType.BaseType == null || currentType.BaseType.IsSystemObject()) break; currentType = currentType.BaseType.Resolve(); } // Add ctor mapping var argIndex = 0; foreach (var ctor in attributeType.Methods.Where(x => (x.Name == ".ctor") && x.IsReachable)) { // Add methods for the ctor arguments List<Tuple<MethodDefinition, Mono.Cecil.TypeReference>> paramGetMethods = new List<Tuple<MethodDefinition, Mono.Cecil.TypeReference>>(); foreach (ParameterDefinition p in ctor.Parameters) { string methodName = CreateGetMethodName("c" + argIndex++, result); MethodDefinition method = new MethodDefinition(@interface, methodName, MakePrototype(p.ParameterType, targetPackage, compiler.Module)); method.AccessFlags = AccessFlags.Public | AccessFlags.Abstract; @interface.Methods.Add(method); paramGetMethods.Add(Tuple.Create(method, p.ParameterType)); } // Add a builder method MethodDefinition buildMethod = CreateBuildMethod(sequencePoint, ctor, paramGetMethods, compiler, targetPackage, attributeClass, result); result.CtorMap.Add(ctor, new AttributeCtorMapping(buildMethod, paramGetMethods.Select(p=>p.Item1).ToList())); } // Create default values annotation Annotation defAnnotation = CreateDefaultAnnotation(result); result.AnnotationInterfaceClass.Annotations.Add(defAnnotation); return result; }
/// <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> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Call) || (x.Code == AstCode.Calli) || (x.Code == AstCode.Callvirt))) { var method = (XMethodReference)node.Operand; bool processed = false; XMethodDefinition methodDef; if (method.TryResolve(out methodDef) && methodDef.DeclaringType.IsPrimitive) { if (methodDef.IsConstructor && (node.Arguments.Count == 2)) { // primitive.ctor(addressof_primitive, value) -> primitive.cast(value) var locVar = node.Arguments[0].Operand; node.Arguments.RemoveAt(0); node.SetCode(AstCode.Stloc).SetType(node.Arguments[0].GetResultType()).Operand = locVar; processed = true; } else if (methodDef.Name == "GetHashCode" && node.Arguments.Count == 1 && methodDef.HasThis) { // we try to avoid boxing. var arg = node.Arguments[0]; switch (arg.Code) { case AstCode.AddressOf: arg = arg.Arguments[0]; break; case AstCode.Ldloca: arg.SetCode(AstCode.Ldloc); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldflda: arg.SetCode(AstCode.Ldfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldsflda: arg.SetCode(AstCode.Ldsfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldelema: arg.SetCode(AstCode.Ldelem_Any); arg.SetType(arg.InferredType.ElementType); break; } var type = arg.GetResultType(); if (type.IsBoolean() || type.IsFloat() || type.IsDouble() || type.IsInt64() || type.IsUInt64()) { var ch = compiler.GetDot42InternalType("CompilerHelper").Resolve(); // these need special handling var replMethod = ch.Methods.FirstOrDefault(m => m.Name == "GetHashCode" && m.IsStatic && m.Parameters[0].ParameterType.IsSame(type, true)); if (replMethod != null) { node.Operand = replMethod; node.SetCode(AstCode.Call); node.Arguments.Clear(); node.Arguments.Add(arg); } } else { // for the other primitive types, we just return the value itself. node.CopyFrom(arg); node.ExpectedType = compiler.Module.TypeSystem.Int; } processed = true; } } if(!processed) { for (var i = 0; i < node.Arguments.Count; i++) { ProcessArgument(node, method, i, currentMethod, compiler.Module); } } } }
/// <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> /// Emit code (if needed) to convert a value from source type to destination type. /// This method is used in "store" opcodes such stloc, stfld, stsfld, call /// </summary> internal static RLRange ConvertTypeBeforeStore(this IRLBuilder builder, ISourceLocation sequencePoint, XTypeReference sourceType, XTypeReference destinationType, RegisterSpec source, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler, out bool converted) { converted = false; if (sourceType.IsSame(destinationType)) { // Unsigned conversions if (sourceType.IsByte()) { var tmp = builder.EnsureTemp(sequencePoint, source, frame); var ins = builder.Add(sequencePoint, RCode.Int_to_byte, tmp.Result, tmp.Result); converted = true; return new RLRange(tmp, ins, tmp.Result); } else if (sourceType.IsUInt16()) { var tmp = builder.EnsureTemp(sequencePoint, source, frame); var ins = builder.Add(sequencePoint, RCode.Int_to_short, tmp.Result, tmp.Result); converted = true; return new RLRange(tmp, ins, tmp.Result); } return new RLRange(source); } if (sourceType.IsArray) { var compilerHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var arrayType = targetPackage.DexFile.GetClass(targetPackage.NameConverter.GetConvertedFullName(compilerHelper)); var sourceArrayElementType = ((XArrayType)sourceType).ElementType; if (destinationType.ExtendsIList()) { // Use ArrayHelper.AsList to convert var convertMethodName = "AsList"; var convertMethod = arrayType.GetMethod(convertMethodName); // Add code var tmp = builder.EnsureTemp(sequencePoint, source, frame); builder.Add(sequencePoint, RCode.Invoke_static, convertMethod, tmp.Result.Register); var last = builder.Add(sequencePoint, RCode.Move_result_object, tmp.Result.Register); converted = true; return new RLRange(tmp, last, tmp.Result); } if (destinationType.ExtendsICollection()) { // Use ArrayHelper.AsCollection to convert var convertMethodName = "AsCollection"; var convertMethod = arrayType.GetMethod(convertMethodName); // Add code var tmp = builder.EnsureTemp(sequencePoint, source, frame); builder.Add(sequencePoint, RCode.Invoke_static, convertMethod, tmp.Result.Register); var last = builder.Add(sequencePoint, RCode.Move_result_object, tmp.Result.Register); converted = true; return new RLRange(tmp, last, tmp.Result); } if (destinationType.ExtendsIEnumerable()) { // Use ArrayHelper.As...Enumerable to convert var convertMethodName = "AsObjectEnumerable"; if (sourceArrayElementType.IsPrimitive) { if (sourceArrayElementType.IsBoolean()) convertMethodName = "AsBoolEnumerable"; else if (sourceArrayElementType.IsByte()) convertMethodName = "AsByteEnumerable"; else if (sourceArrayElementType.IsSByte()) convertMethodName = "AsSByteEnumerable"; else if (sourceArrayElementType.IsChar()) convertMethodName = "AsCharEnumerable"; else if (sourceArrayElementType.IsInt16()) convertMethodName = "AsInt16Enumerable"; else if (sourceArrayElementType.IsUInt16()) convertMethodName = "AsUInt16Enumerable"; else if (sourceArrayElementType.IsInt32()) convertMethodName = "AsInt32Enumerable"; else if (sourceArrayElementType.IsUInt32()) convertMethodName = "AsUInt32Enumerable"; else if (sourceArrayElementType.IsInt64()) convertMethodName = "AsInt64Enumerable"; else if (sourceArrayElementType.IsFloat()) convertMethodName = "AsFloatEnumerable"; else if (sourceArrayElementType.IsDouble()) convertMethodName = "AsDoubleEnumerable"; else throw new ArgumentOutOfRangeException("Unknown primitive array element type " + sourceArrayElementType); } var convertMethod = arrayType.GetMethod(convertMethodName); // Add code var tmp = builder.EnsureTemp(sequencePoint, source, frame); builder.Add(sequencePoint, RCode.Invoke_static, convertMethod, tmp.Result.Register); var last = builder.Add(sequencePoint, RCode.Move_result_object, tmp.Result.Register); converted = true; return new RLRange(tmp, last, tmp.Result); } } // Do not convert return new RLRange(source); }
/// <summary> /// Emit code (if needed) to convert a value from source type to destination type. /// This method is used in "store" opcodes such stloc, stfld, stsfld, call /// </summary> internal static RLRange ConvertTypeBeforeStore(this IRLBuilder builder, ISourceLocation sequencePoint, XTypeReference sourceType, XTypeReference destinationType, RegisterSpec source, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler, out bool converted) { converted = false; if (sourceType.IsSame(destinationType)) { // Unsigned conversions if (sourceType.IsByte()) { var tmp = builder.EnsureTemp(sequencePoint, source, frame); var ins = builder.Add(sequencePoint, RCode.Int_to_byte, tmp.Result, tmp.Result); converted = true; return(new RLRange(tmp, ins, tmp.Result)); } else if (sourceType.IsUInt16()) { var tmp = builder.EnsureTemp(sequencePoint, source, frame); var ins = builder.Add(sequencePoint, RCode.Int_to_short, tmp.Result, tmp.Result); converted = true; return(new RLRange(tmp, ins, tmp.Result)); } return(new RLRange(source)); } if (sourceType.IsArray) { var compilerHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve(); var arrayType = targetPackage.DexFile.GetClass(targetPackage.NameConverter.GetConvertedFullName(compilerHelper)); var sourceArrayElementType = ((XArrayType)sourceType).ElementType; if (destinationType.ExtendsIList()) { // Use ArrayHelper.AsList to convert var convertMethodName = "AsList"; var convertMethod = arrayType.GetMethod(convertMethodName); // Add code var tmp = builder.EnsureTemp(sequencePoint, source, frame); builder.Add(sequencePoint, RCode.Invoke_static, convertMethod, tmp.Result.Register); var last = builder.Add(sequencePoint, RCode.Move_result_object, tmp.Result.Register); converted = true; return(new RLRange(tmp, last, tmp.Result)); } if (destinationType.ExtendsICollection()) { // Use ArrayHelper.AsCollection to convert var convertMethodName = "AsCollection"; var convertMethod = arrayType.GetMethod(convertMethodName); // Add code var tmp = builder.EnsureTemp(sequencePoint, source, frame); builder.Add(sequencePoint, RCode.Invoke_static, convertMethod, tmp.Result.Register); var last = builder.Add(sequencePoint, RCode.Move_result_object, tmp.Result.Register); converted = true; return(new RLRange(tmp, last, tmp.Result)); } if (destinationType.ExtendsIEnumerable()) { // Use ArrayHelper.As...Enumerable to convert var convertMethodName = FrameworkReferences.GetAsEnumerableTMethodName(sourceArrayElementType); var convertMethod = arrayType.GetMethod(convertMethodName); // Add code var tmp = builder.EnsureTemp(sequencePoint, source, frame); builder.Add(sequencePoint, RCode.Invoke_static, convertMethod, tmp.Result.Register); var last = builder.Add(sequencePoint, RCode.Move_result_object, tmp.Result.Register); converted = true; return(new RLRange(tmp, last, tmp.Result)); } } if (sourceType.IsGenericParameter && !destinationType.IsSystemObject()) { var gp = (XGenericParameter)sourceType; if (gp.Constraints.Length > 0) { // we could find the best matching constraint here, and check if we actually // need to cast. This would probably allow us to skip some unneccesary casts. // We would need some sort of IsAssignableFrom though, and I'm not sure we have // this logic with XTypeDefinition implemented yet. // Therefore, we just assume that the original compiler has done its job well, // and always cast to the destination type. // Apparently dex seems not to need a cast when destinationType is an interface. // Since i'm not to sure about this, we nevertheless insert the cast here. // [TODO: check if this is needed] var tmp = builder.EnsureTemp(sequencePoint, source, frame); var cast = builder.Add(sequencePoint, RCode.Check_cast, destinationType.GetReference(targetPackage), tmp.Result); converted = true; return(new RLRange(tmp, cast, tmp.Result)); } } // Do not convert return(new RLRange(source)); }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, AssemblyCompiler compiler) { var typeSystem = compiler.Module.TypeSystem; // Expand typeof foreach (var node in ast.GetSelfAndChildrenRecursive<AstExpression>(x => x.Code == AstCode.TypeOf)) { var type = (XTypeReference) node.Operand; var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); var loadExpr = LoadTypeForGenericInstance(node.SourceLocation, currentMethod, type, compiler, typeHelperType, typeSystem, TypeConversion.EnsureTrueOrMarkerType); node.CopyFrom(loadExpr); } // Expand instanceOf foreach (var node in ast.GetSelfAndChildrenRecursive<AstExpression>(x => x.Code == AstCode.SimpleInstanceOf)) { var type = (XTypeReference)node.Operand; var gp = type as XGenericParameter; if (gp == null) continue; var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); var loadExpr = LoadTypeForGenericInstance(node.SourceLocation, currentMethod, type, compiler, typeHelperType, typeSystem, TypeConversion.EnsureRuntimeType); //// both types are boxed, no need for conversion. var typeType = compiler.GetDot42InternalType("System", "Type").Resolve(); var isInstanceOfType = typeType.Methods.Single(n => n.Name == "JavaIsInstance" && n.Parameters.Count == 1); var call = new AstExpression(node.SourceLocation, AstCode.Call, isInstanceOfType, loadExpr, node.Arguments[0]); node.CopyFrom(call); } // Expand newarr foreach (var node in ast.GetSelfAndChildrenRecursive<AstExpression>(x => x.Code == AstCode.Newarr)) { var type = (XTypeReference)node.Operand; if (!type.IsDefinitionOrReferenceOrPrimitive()) { // Resolve type to a Class<?> var typeHelperType = compiler.GetDot42InternalType(InternalConstants.TypeHelperName).Resolve(); // while having primitive arrays for primitive types would be nice, a lot of boxing and unboxing // would be needed. only for-primitive-specialized generic classes we could optimize this. var ldType = LoadTypeForGenericInstance(node.SourceLocation, currentMethod, type, compiler, typeHelperType, typeSystem, TypeConversion.EnsureRuntimeType); var newInstanceExpr = new AstExpression(node.SourceLocation, AstCode.ArrayNewInstance, null, ldType, node.Arguments[0]) { ExpectedType = typeSystem.Object }; var arrayType = new XArrayType(type); var cast = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, arrayType, newInstanceExpr) { ExpectedType = arrayType }; node.CopyFrom(cast); } } // Add generic instance call arguments foreach (var node in ast.GetSelfAndChildrenRecursive<AstExpression>(x => x.Code.IsCall())) { var method = (XMethodReference)node.Operand; if (method.DeclaringType.IsArray) continue; XMethodDefinition methodDef; if (!method.TryResolve(out methodDef)) continue; if (methodDef.HasDexNativeAttribute()) continue; if (methodDef.NeedsGenericInstanceTypeParameter) { // Add generic instance type parameter value var arg = CreateGenericInstanceCallArguments(node.SourceLocation, method.DeclaringType, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } if (methodDef.NeedsGenericInstanceMethodParameter) { // Add generic instance method parameter var arg = CreateGenericInstanceCallArguments(node.SourceLocation, method, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } } // Add generic instance Delegate arguments for static methods. foreach (var node in ast.GetSelfAndChildrenRecursive<AstExpression>(x => x.Code == AstCode.Delegate)) { var delegateInfo = (Tuple<XTypeDefinition, XMethodReference>)node.Operand; var genMethodDef = delegateInfo.Item2 as IXGenericInstance; var genTypeDef = delegateInfo.Item2.DeclaringType as IXGenericInstance; // Add generic instance type parameter value, if method is static if (genTypeDef != null && delegateInfo.Item2.Resolve().IsStatic) { var arg = CreateGenericInstanceCallArguments(node.SourceLocation, delegateInfo.Item2.DeclaringType, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } // add generic method type parameter value. if (genMethodDef != null) { var arg = CreateGenericInstanceCallArguments(node.SourceLocation, delegateInfo.Item2, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } } // Convert NewObj when needed foreach (var node in ast.GetSelfAndChildrenRecursive<AstExpression>(x => x.Code == AstCode.Newobj)) { var ctorRef = (XMethodReference)node.Operand; var declaringType = ctorRef.DeclaringType; if (declaringType.IsArray) { // New multi dimensional array // Get element type var elemType = ((XArrayType) declaringType).ElementType; var typeExpr = new AstExpression(node.SourceLocation, AstCode.TypeOf, elemType); // Create dimensions array var intArrayType = new XArrayType(typeSystem.Int); var dimArrayExpr = new AstExpression(node.SourceLocation, AstCode.InitArrayFromArguments, intArrayType, node.Arguments).SetType(intArrayType); // Call java.lang.reflect.Array.newInstance(type, int[]) var newInstanceExpr = new AstExpression(node.SourceLocation, AstCode.ArrayNewInstance2, null, typeExpr, dimArrayExpr).SetType(typeSystem.Object); // Cast to correct type var cast = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, declaringType, newInstanceExpr).SetType(declaringType); // Replace node node.CopyFrom(cast); } else { // Normal "new object" XMethodDefinition ctorDef; if (ctorRef.TryResolve(out ctorDef) && ctorDef.NeedsGenericInstanceTypeParameter) { // Add generic instance type parameter value var arg = CreateGenericInstanceCallArguments(node.SourceLocation, ctorRef.DeclaringType, currentMethod, compiler); node.Arguments.AddRange(arg); node.GenericInstanceArgCount += arg.Count; } } } }
/// <summary> /// Create code to load a value from an annotation interface. /// </summary> /// <returns>The register(s) holding the value</returns> private static Register[] CreateLoadValueSequence( ISourceLocation seqp, MethodBody body, XTypeReference valueType, Register annotationReg, MethodDefinition getter, AssemblyCompiler compiler, DexTargetPackage targetPackage, out Instruction branchIfNotSet) { // NOTE: It would be better if we wouldn't get the values as object arrays // but as arrays of the actual type. // Apparently though the DexWriter will not write our attributes // if they contain arrays not of type object[]. Therefore the // conversion code below. // All in all it would be much cleaner if we could emit Ast code here // instead of RL code. List<Register> result = new List<Register>(); // get the array. Register regObject = body.AllocateRegister(RCategory.Temp, RType.Object); Register regIntVal = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Invoke_interface, getter, annotationReg); body.Instructions.Add(seqp, RCode.Move_result_object, regObject); // allocate result, initialize to default value. if (valueType.IsWide()) { Tuple<Register, Register> regs = body.AllocateWideRegister(RCategory.Temp); body.Instructions.Add(seqp, RCode.Const_wide, 0, regs.Item1); result.Add(regs.Item1); result.Add(regs.Item2); } else if (valueType.IsPrimitive) { Register reg = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } else // object { Register reg = body.AllocateRegister(RCategory.Temp, RType.Object); body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } // check if value is unset (array length 0) or null (array length 2) body.Instructions.Add(seqp, RCode.Array_length, regIntVal, regObject); branchIfNotSet = body.Instructions.Add(seqp, RCode.If_eqz, regIntVal); body.Instructions.Add(seqp, RCode.Rsub_int, 1, regIntVal, regIntVal); var branchOnNull = body.Instructions.Add(seqp, RCode.If_nez, regIntVal); // get the (boxed) value body.Instructions.Add(seqp, RCode.Const, 0, regIntVal); // convert to target type. if (valueType.IsArray) { Register regTmp = body.AllocateRegister(RCategory.Temp, RType.Object); Register regType = body.AllocateRegister(RCategory.Temp, RType.Object); var helper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName); var convertArray = helper.Resolve().Methods.First(p => p.Name == "ConvertArray" && p.Parameters.Count == 2) .GetReference(targetPackage); var underlying = valueType.ElementType.GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Aget_object, regTmp, regObject, regIntVal); body.Instructions.Add(seqp, RCode.Const_class, underlying, regType); body.Instructions.Add(seqp, RCode.Invoke_static, convertArray, regTmp, regType); body.Instructions.Add(seqp, RCode.Move_result_object, result[0]); body.Instructions.Add(seqp, RCode.Check_cast, valueType.GetReference(targetPackage), result[0]); } else if (valueType.IsEnum()) { Register regTmp = body.AllocateRegister(RCategory.Temp, RType.Object); Register regType = body.AllocateRegister(RCategory.Temp, RType.Object); var getFromObject = compiler.GetDot42InternalType("Enum").Resolve() .Methods.Single(p=>p.Name == "GetFromObject") .GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Aget_object, regTmp, regObject, regIntVal); body.Instructions.Add(seqp, RCode.Const_class, valueType.GetReference(targetPackage), regType); body.Instructions.Add(seqp, RCode.Invoke_static, getFromObject, regType, regTmp); body.Instructions.Add(seqp, valueType.MoveResult(), result[0]); body.Instructions.Add(seqp, RCode.Check_cast, valueType.GetReference(targetPackage), result[0]); } else if(!valueType.IsPrimitive) { body.Instructions.Add(seqp, RCode.Aget_object, result[0], regObject, regIntVal); body.Instructions.Add(seqp, RCode.Check_cast, valueType.GetReference(targetPackage), result[0]); } else { Register regTmp = body.AllocateRegister(RCategory.Temp, RType.Object); // unbox and store RCode afterConvert; var unbox = valueType.GetUnboxValueMethod(compiler, targetPackage, out afterConvert); body.Instructions.Add(seqp, RCode.Aget_object, regTmp, regObject, regIntVal); body.Instructions.Add(seqp, RCode.Invoke_static, unbox, regTmp); body.Instructions.Add(seqp, valueType.MoveResult(), result[0]); if (afterConvert != RCode.Nop) { body.Instructions.Add(seqp, afterConvert, result[0], result[0]); } } // nop will be removed at some stage later. var nop = body.Instructions.Add(seqp, RCode.Nop); branchOnNull.Operand = nop; return result.ToArray(); }
/// <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> /// Create all annotations for this method /// </summary> internal virtual void CreateAnnotations(DexTargetPackage targetPackage) { // Build method annotations AttributeAnnotationInstanceBuilder.CreateAttributeAnnotations(compiler, method, dmethod, targetPackage); // only add generics annotation for getters or setters or constructors if (method.IsGetter) { // Note that the return type might has been // changed above, to compensate for interface // inheritance and generic specialization. // We need to use the original declaration. // TODO: why not get rid of "OriginalReturnType" // and use the IL's return type?? var returnType = xMethod.OriginalReturnType; var xType = XBuilder.AsTypeReference(compiler.Module, returnType); dmethod.AddGenericDefinitionAnnotationIfGeneric(xType, compiler, targetPackage); } else if (method.IsSetter) { for (int i = 0; i < xMethod.Parameters.Count; ++i) { var dp = dmethod.Prototype.Parameters[i]; dp.AddGenericDefinitionAnnotationIfGeneric(xMethod.Parameters[i].ParameterType, compiler, targetPackage); } } else if (method.IsConstructor && !method.IsStatic) { // Add parameter names and original access flags, as these might be important // in serialization and/or dependency injection. var reflectionInfo = compiler.GetDot42InternalType(InternalConstants.ReflectionInfoAnnotation) .GetClassReference(targetPackage); var annotation = new Annotation { Type = reflectionInfo, Visibility = AnnotationVisibility.Runtime }; bool isPublic = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; // Not sure if it makes any sense to remap the access flags. bool isProtected = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; bool isInternal = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; bool isPrivate = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; // only create accessFlags if they differs from java's if (isPublic != dmethod.IsPublic || isProtected != dmethod.IsProtected || isPrivate != dmethod.IsPrivate || isInternal) { int accessFlags = 0; if (isPublic) { accessFlags |= 0x01; } if (isProtected) { accessFlags |= 0x02; } if (isPrivate) { accessFlags |= 0x04; } if (isInternal) { accessFlags |= 0x08; } annotation.Arguments.Add(new AnnotationArgument(InternalConstants.ReflectionInfoAccessFlagsField, accessFlags)); } if (method.Parameters.Count > 0) { annotation.Arguments.Add(new AnnotationArgument(InternalConstants.ReflectionInfoParameterNamesField, method.Parameters.Select(p => p.Name).ToArray())); } if (annotation.Arguments.Count > 0) { dmethod.Annotations.Add(annotation); } //for (int i = 0; i < xMethod.Parameters.Count; ++i) //{ // var dp = dmethod.Prototype.Parameters[i]; // dp.AddGenericDefinitionAnnotationIfGeneric(xMethod.Parameters[i].ParameterType, compiler, // targetPackage); //} } }
/// <summary> /// Create code to initialize a value from an attribute. /// </summary> /// <returns>The register(s) holding the value</returns> private static Register[] CreateInitializeValueInstructions(ISourceLocation seqp, MethodBody body, XTypeReference targetType, CustomAttributeArgument value, AssemblyCompiler compiler, DexTargetPackage targetPackage) { List<Register> result = new List<Register>(); // allocate result, initialize to default value. if (targetType.IsWide()) { Tuple<Register, Register> regs = body.AllocateWideRegister(RCategory.Temp); //body.Instructions.Add(seqp, RCode.Const_wide, 0, regs.Item1); result.Add(regs.Item1); result.Add(regs.Item2); } else if (targetType.IsPrimitive) { Register reg = body.AllocateRegister(RCategory.Temp, RType.Value); //body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } else // object { Register reg = body.AllocateRegister(RCategory.Temp, RType.Object); //body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } // load data if (value.Value == null) // must be a reference type { body.Instructions.Add(seqp, RCode.Const, 0, result[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); return result.ToArray(); } var valueType = XBuilder.AsTypeReference(compiler.Module, value.Type); if (value.Value is CustomAttributeArgument) { // this happens if a type conversion is neccessary var nestedValue = (CustomAttributeArgument)value.Value; valueType = XBuilder.AsTypeReference(compiler.Module, nestedValue.Type); var rOrigValue = CreateInitializeValueInstructions(seqp, body, valueType, nestedValue, compiler, targetPackage); if (!nestedValue.Type.IsPrimitive) { body.Instructions.Add(seqp, RCode.Move_object, result[0], rOrigValue[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else if(!targetType.IsPrimitive) { body.Instructions.Add(seqp, RCode.Invoke_static, valueType.GetBoxValueOfMethod(), rOrigValue); body.Instructions.Add(seqp, RCode.Move_result_object, result[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else { throw new Exception(string.Format("type converstion in attribute {0}=>{1} not yet supported", valueType.FullName, targetType.FullName)); } } else if (valueType.IsArray) { var array = (CustomAttributeArgument[])value.Value; var elementType = valueType.ElementType; Register rIndex = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Const, array.Length, rIndex); body.Instructions.Add(seqp, RCode.New_array, valueType.GetReference(targetPackage), result[0], rIndex); // iterate through each value for (int i = 0; i < array.Length; i++) { Register rLoaded = CreateInitializeValueInstructions(seqp, body, elementType, array[i], compiler, targetPackage)[0]; body.Instructions.Add(seqp, RCode.Const, i, rIndex); body.Instructions.Add(seqp, valueType.APut(), rLoaded, result[0], rIndex); } } else if (targetType.IsEnum()) { var enumClass = (targetType.IsEnum()? targetType:valueType).GetReference(targetPackage) ; Register rEnumClass = body.AllocateRegister(RCategory.Temp, RType.Object); body.Instructions.Add(seqp, RCode.Const_class, enumClass, rEnumClass); long lVal = Convert.ToInt64(value.Value); if (lVal <= int.MaxValue && lVal >= int.MinValue) { Register regTmp = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Const, (int)lVal, regTmp); var get = compiler.GetDot42InternalType("Enum").Resolve() .Methods.Single(p => p.Name == "Get" && p.Parameters.Count == 2 && !p.Parameters[1].ParameterType.IsWide()) .GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, get, rEnumClass, regTmp); body.Instructions.Add(seqp, targetType.MoveResult(), result[0]); } else { var regTmp = body.AllocateWideRegister(RCategory.Temp); body.Instructions.Add(seqp, RCode.Const, (long)lVal, regTmp.Item1); var get = compiler.GetDot42InternalType("Enum").Resolve() .Methods.Single(p => p.Name == "Get" && p.Parameters.Count == 2 && p.Parameters[1].ParameterType.IsWide()) .GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, get, rEnumClass, regTmp.Item1); body.Instructions.Add(seqp, targetType.MoveResult(), result[0]); } body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else if (valueType.IsSystemString()) { body.Instructions.Add(seqp, RCode.Const_string, (string)value.Value, result[0]); } else if (valueType.IsSystemType()) { var type = XBuilder.AsTypeReference(compiler.Module, (TypeReference)value.Value); // TODO: this might not work with typeof(void) on ART runtime. body.Instructions.Add(seqp, RCode.Const_class, type.GetReference(targetPackage), result[0]); } else if (!valueType.IsPrimitive) { // can this happen? throw new Exception("invalid value type in attribute: " + targetType.FullName); } else { if (targetType.IsSystemObject()) { // can this happen? or is this always handled above? // boxing required. var rUnboxed = CreateInitializeValueInstructions(seqp, body, valueType, value, compiler, targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, valueType.GetBoxValueOfMethod(), rUnboxed); body.Instructions.Add(seqp, RCode.Move_result_object, result[0]); } else if(targetType.IsDouble()) { body.Instructions.Add(seqp, RCode.Const_wide, Convert.ToDouble(value.Value), result[0]); } else if (targetType.IsWide() && valueType.IsUInt64()) { body.Instructions.Add(seqp, RCode.Const_wide, (long)Convert.ToUInt64(value.Value), result[0]); } else if (targetType.IsWide()) { body.Instructions.Add(seqp, RCode.Const_wide, Convert.ToInt64(value.Value), result[0]); } else if (targetType.IsFloat()) { body.Instructions.Add(seqp, RCode.Const, Convert.ToSingle(value.Value), result[0]); } else { body.Instructions.Add(seqp, RCode.Const, (int)Convert.ToInt64(value.Value), result[0]); } } return result.ToArray(); }
/// <summary> /// Create an annotation for the given attribute /// </summary> private static void Create(AssemblyCompiler compiler, CustomAttribute attribute, TypeDefinition attributeType, List<Annotation> annotationList, DexTargetPackage targetPackage) { // Gets the mapping for the type of attribute var mapping = compiler.GetAttributeAnnotationType(attributeType); var ctorMap = mapping.CtorMap[attribute.Constructor.Resolve()]; // Create annotation var annotation = new Annotation {Visibility = AnnotationVisibility.Runtime}; annotation.Type = mapping.AnnotationInterfaceClass; // Add ctor arguments var argIndex = 0; foreach (var arg in attribute.ConstructorArguments) { var name = ctorMap.ArgumentGetters[argIndex].Name; annotation.Arguments.Add(CreateAnnotationArgument(name, arg.Type, arg.Value, targetPackage, compiler.Module)); argIndex++; } // Add field values foreach (var arg in attribute.Fields) { var entry = mapping.FieldToGetMethodMap.First(x => x.Key.Name == arg.Name); var name = entry.Value.Name; annotation.Arguments.Add(CreateAnnotationArgument(name, arg.Argument.Type, arg.Argument.Value, targetPackage, compiler.Module)); } // Add property values foreach (var arg in attribute.Properties) { if (mapping.PropertyToGetMethodMap.Keys.Any(x => x.Name == arg.Name)) { var entry = mapping.PropertyToGetMethodMap.First(x => x.Key.Name == arg.Name); var name = entry.Value.Name; annotation.Arguments.Add(CreateAnnotationArgument(name, arg.Argument.Type, arg.Argument.Value, targetPackage, compiler.Module)); } } // Create attribute annotation var attrAnnotation = new Annotation { Visibility = AnnotationVisibility.Runtime }; attrAnnotation.Type = compiler.GetDot42InternalType("IAttribute").GetClassReference(targetPackage); attrAnnotation.Arguments.Add(new AnnotationArgument("AttributeBuilder", ctorMap.Builder)); attrAnnotation.Arguments.Add(new AnnotationArgument("AttributeType", attributeType.GetReference(targetPackage, compiler.Module))); attrAnnotation.Arguments.Add(new AnnotationArgument("Annotation", annotation)); // Add annotation annotationList.Add(attrAnnotation); }
/// <summary> /// Create an annotation for the given attribute /// </summary> private static void CreateAttributeAnnotation(AssemblyCompiler compiler, CustomAttribute attribute, TypeDefinition attributeType, List<Annotation> annotationList, DexTargetPackage targetPackage) { // Gets the mapping for the type of attribute var mapping = compiler.GetAttributeAnnotationMapping(attributeType); MethodDefinition factoryMethod; // Note: not multithreading capable. see my comments elsewhere. if(mapping.FactoryMethodMap.ContainsKey(attribute)) factoryMethod = mapping.FactoryMethodMap[attribute]; else { // create the factory method. factoryMethod = CreateFactoryMethod(compiler, targetPackage, attribute, mapping); mapping.FactoryMethodMap[attribute] = factoryMethod; } // Create attribute annotation var attrAnnotation = new Annotation { Visibility = AnnotationVisibility.Runtime }; attrAnnotation.Type = compiler.GetDot42InternalType("IAttribute").GetClassReference(targetPackage); attrAnnotation.Arguments.Add(new AnnotationArgument("AttributeType", attributeType.GetReference(targetPackage, compiler.Module))); attrAnnotation.Arguments.Add(new AnnotationArgument("FactoryMethod", factoryMethod.Name)); // Add annotation annotationList.Add(attrAnnotation); }
/// <summary> /// Create code to box the given source array of primitive type elements into an array of boxed elements. /// </summary> public static RLRange BoxGenericArray(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec source, XTypeReference type, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler) { var objectArrayType = new DexLib.ArrayType(new ClassReference("java.lang.Object")); var boxedArray = frame.AllocateTemp(objectArrayType); var internalBoxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var ilBoxMethod = internalBoxingType.Methods.First(x => x.EqualsName("Box") && (x.Parameters.Count == 1) && (x.Parameters[0].ParameterType.IsSame(type, true))); var boxMethod = ilBoxMethod.GetReference(targetPackage); var call = builder.Add(sequencePoint, RCode.Invoke_static, boxMethod, source); var saveArray = builder.Add(sequencePoint, RCode.Move_result_object, boxedArray); return new RLRange(call, saveArray, boxedArray); }
/// <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; }
/// <summary> /// Create code to unbox the given source array of boxed type elements resulting from a call into an array of primitive elements. /// </summary> public static RLRange UnboxGenericArrayResult(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec boxedArray, XTypeReference type, DexTargetPackage targetPackage, IRegisterAllocator frame, AssemblyCompiler compiler) { var internalBoxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var primitiveArray = frame.AllocateTemp(type.GetReference(targetPackage)); var ilUnboxMethod = internalBoxingType.Methods.First(x => x.Name.StartsWith("Unbox") && (x.Parameters.Count == 1) && (x.ReturnType.IsSame(type, true))); var unboxMethod = ilUnboxMethod.GetReference(targetPackage); var call = builder.Add(sequencePoint, RCode.Invoke_static, unboxMethod, boxedArray); var saveArray = builder.Add(sequencePoint, RCode.Move_result_object, primitiveArray); return new RLRange(call, saveArray, primitiveArray); }
/// <summary> /// Gets a unbox method for the given primitive type. /// </summary> internal static MethodReference GetUnboxValueMethod(XTypeReference type, AssemblyCompiler compiler, DexTargetPackage targetPackage, out RCode convertAfterCode) { var info = Get(type); convertAfterCode = info.convertAfterCode; var boxingClass = compiler.GetDot42InternalType("Boxing").GetClassReference(targetPackage); return new MethodReference(boxingClass, info.unboxMethodName, new Prototype(info.primitiveType, new Parameter(FrameworkReferences.Object, "value"))); }