/// <summary> /// Convert the given argument if it does not match the target type. /// </summary> private static void ConvertForRetIfNeeded(AstExpression arg, XTypeReference targetType) { var argType = arg.GetResultType(); if (!targetType.IsSame(argType)) { if (targetType.IsChar()) { if (argType.IsUInt16() || argType.IsByte() || argType.IsInt16()) { Convert(arg, AstCode.Conv_U2, targetType); } } else if (targetType.IsUInt16()) { if (argType.IsChar() || argType.IsByte()) { Convert(arg, AstCode.Int_to_ushort, targetType); } } else if (targetType.IsInt16()) { if (argType.IsChar() || argType.IsByte()) { Convert(arg, AstCode.Conv_I2, targetType); } } } }
/// <summary> /// Convert a nullable .GetValueOrDefault() /// </summary> private static void ConvertGetValueOrDefault(AstExpression node, XTypeReference type, XModule module) { var defExpr = node.Arguments.Count == 1 ? new AstExpression(node.SourceLocation, AstCode.DefaultValue, type).SetType(type) : node.Arguments[1]; defExpr.ExpectedType = type; if (type.IsPrimitive) { // replace with obj != null ? unbox(obj) : defExpr AstExpression compareExpr, valueExpr; var loadExpr = node.Arguments[0]; ConvertLoad(loadExpr); loadExpr.InferredType = module.TypeSystem.Object; loadExpr.ExpectedType = module.TypeSystem.Object; if (loadExpr.Code != AstCode.Ldloc) { // TODO: how can we get the backend to remove/combine these variables again? var tmpVar = new AstGeneratedVariable("tmp$", null) { Type = module.TypeSystem.Object }; compareExpr = new AstExpression(node.SourceLocation, AstCode.Stloc, tmpVar, loadExpr).SetType(module.TypeSystem.Object); valueExpr = new AstExpression(node.SourceLocation, AstCode.Ldloc, tmpVar).SetType(module.TypeSystem.Object); } else { compareExpr = loadExpr; valueExpr = loadExpr; } valueExpr = new AstExpression(node.SourceLocation, AstCode.Unbox, type, valueExpr).SetType(type); var newNode = new AstExpression(node.SourceLocation, AstCode.Conditional, type, compareExpr, valueExpr, defExpr) .SetType(type); node.CopyFrom(newNode); } else { // replace with obj ?? defExpr var loadExpr = node.Arguments[0]; ConvertLoad(loadExpr); if (!type.IsSame(loadExpr.InferredType)) { //loadExpr.ExpectedType = type; // todo: how to get the cast inserted automatically? loadExpr = new AstExpression(loadExpr.SourceLocation, AstCode.SimpleCastclass, type, loadExpr); } var nullCoalescing = new AstExpression(node.SourceLocation, AstCode.NullCoalescing, null, loadExpr, defExpr); nullCoalescing.InferredType = type; node.CopyFrom(nullCoalescing); } }
/// <summary> /// Convert the given node to a array creation operation with given element type and argument. /// </summary> private static void ConvertToByRefArray(AstExpression node, XTypeReference elementType, AstExpression argument, AstExpression storeArgument, bool isOut, bool argIsGenParam, XModule assembly) { var arrayElementType = argIsGenParam ? assembly.TypeSystem.Object : elementType; if (argIsGenParam && ((!isOut) || (elementType == null) || !elementType.IsSame(argument.GetResultType()))) { argument = new AstExpression(node.SourceLocation, AstCode.Box, elementType, argument); } node.Code = isOut ? AstCode.ByRefOutArray : AstCode.ByRefArray; node.Operand = arrayElementType; node.InferredType = new XByReferenceType(arrayElementType); node.ExpectedType = new XByReferenceType(arrayElementType); node.Arguments.Clear(); node.Arguments.Add(argument); node.StoreByRefExpression = storeArgument; }
private static bool CanPullComparisonUp(AstCode code, XTypeReference arg1, XTypeReference arg2, PullTarget target) { if (arg1 == null || arg2 == null) { return(false); } bool isReference = arg1.IsDexObject() && arg1.IsDexObject(); if (!isReference && !arg1.IsSame(arg2)) { return(false); } if (target == PullTarget.Comparison) { return(true); } if (arg1.Is(XTypeReferenceKind.Float)) { return(false); } if (arg1.IsDexWide()) { return(false); } bool isEq = IsEqualsBranchOrComparison(code); if (isEq) { return(true); } bool isUnsigned = arg1.IsUInt16() || arg1.IsUInt32(); // TODO: check if we really have to exclude unsigned. if (isReference || isUnsigned) { return(false); } return(true); }
/// <summary> /// Does this type reference point to the same type as the given other reference? /// </summary> public override bool IsSame(XTypeReference other, bool ignoreSign) { if (base.IsSame(other, ignoreSign)) { return(true); } EnsureDexImportType(); if (dexImportType != Module.TypeSystem.NoType) { if (dexImportType.IsSame(other, ignoreSign)) { return(true); } } EnsureJavaImportType(); if (javaImportType != Module.TypeSystem.NoType) { if (javaImportType.IsSame(other, ignoreSign)) { return(true); } } return(false); }
/// <summary> /// Is my parameter type the same as the type of the other parameter? /// </summary> public bool IsSame(XParameter other) { /*if (Kind != other.Kind) * return false;*/ return(ParameterType.IsSame(other.ParameterType)); }
/// <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> /// 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)); }