/// <summary> /// Gets the type of stelem code to use for the given element type. /// </summary> public static AstCode GetStElemCode(this XTypeReference elementType) { if (elementType.IsByte() || elementType.IsSByte() || elementType.IsBoolean()) { return(AstCode.Stelem_I1); } if (elementType.IsChar() || elementType.IsInt16() || elementType.IsUInt16()) { return(AstCode.Stelem_I2); } if (elementType.IsInt32() || elementType.IsUInt32()) { return(AstCode.Stelem_I4); } if (elementType.IsInt64() || elementType.IsUInt64()) { return(AstCode.Stelem_I8); } if (elementType.IsFloat()) { return(AstCode.Stelem_R4); } if (elementType.IsDouble()) { return(AstCode.Stelem_R8); } return(AstCode.Stelem_Ref); }
/// <summary> /// Generate converter for the value of an Const instruction. /// </summary> internal static Func <object, object> ConstValueConverter(this XTypeReference elementType, bool keepUnsigned) { if (elementType.IsBoolean()) { return(x => Convert.ToBoolean(x) ? 1 : 0); } if (elementType.IsByte()) { if (keepUnsigned) { return(x => (int)Convert.ToByte(x)); } return(x => (int)((sbyte)unchecked (Convert.ToByte(x)))); } if (elementType.IsSByte()) { return(x => (int)(Convert.ToSByte(x))); } if (elementType.IsChar()) { return(x => (int)(Convert.ToChar(x))); } if (elementType.IsUInt16()) { return(x => (int)(Convert.ToUInt16(x))); } if (elementType.IsInt16()) { return(x => (int)(Convert.ToInt16(x))); } if (elementType.IsInt32()) { return(x => XConvert.ToInt(x)); } if (elementType.IsUInt32()) { return(x => XConvert.ToInt(x)); // unchecked((int)Convert.ToUInt32(Convert.ToInt64(x) & 0xFFFFFFFF)); } if (elementType.IsFloat()) { return(x => Convert.ToSingle(x)); } if (elementType.IsInt64()) { return(x => XConvert.ToLong(x)); } if (elementType.IsUInt64()) { return(x => XConvert.ToLong(x)); // unchecked((long)Convert.ToInt64(x)); } if (elementType.IsDouble()) { return(x => Convert.ToDouble(x)); } if (elementType.IsDexObject() && !elementType.IsVoid()) { return(x => x); } throw new NotSupportedException("Unknown type for constValueConverter " + elementType); }
/// <summary> /// Generate an Add opcode. /// </summary> private static RCode OpcodeForType(XTypeReference type, RCode[] opcodes) { if (type.IsInt32() || type.IsUInt32() || type.IsInt16() || type.IsUInt16() || type.IsChar() || type.IsByte() || type.IsSByte() || type.IsBoolean()) { return(opcodes[0]); } if (type.IsInt64() || type.IsUInt64()) { return(opcodes[1]); } if (type.IsFloat()) { return(opcodes[2]); } if (type.IsDouble()) { return(opcodes[3]); } XTypeDefinition typeDef; if (type.TryResolve(out typeDef)) { if (typeDef.IsEnum) { return(OpcodeForType(typeDef.GetEnumUnderlyingType(), opcodes)); } } throw new ArgumentException("Unsupported type " + type); }
/// <summary> /// Returns the method name when converting an array to an IEnumerableT in compiler helper. /// /// (not sure if this is the best place for this method...) /// </summary> public static string GetAsEnumerableTMethodName(XTypeReference sourceArrayElementType) { 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); } } return(convertMethodName); }
/// <summary> /// Reverses the code, taking account float/double NaN comparisons /// </summary> private static AstCode ReverseCode(AstCode code, XTypeReference type) { bool isFlt = type.IsDouble() || type.IsFloat(); if (!isFlt) { return(code.Reverse()); } switch (code) { case AstCode.Ceq: return(AstCode.Cne); case AstCode.Cne: return(AstCode.Ceq); case AstCode.Cle: return(AstCode.Cgt_Un); case AstCode.Cle_Un: return(AstCode.Cgt); case AstCode.Clt: return(AstCode.Cge_Un); case AstCode.Clt_Un: return(AstCode.Cge); case AstCode.Cgt: return(AstCode.Cle_Un); case AstCode.Cgt_Un: return(AstCode.Cle); case AstCode.Cge: return(AstCode.Clt_Un); case AstCode.Cge_Un: return(AstCode.Clt); default: throw new ArgumentOutOfRangeException("code", code.ToString()); } }
/// <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()); }