/// <summary> /// Create code to box the given source value into the given type. /// </summary> public static RLRange Box(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec source, XTypeReference type, DexTargetPackage targetPackage, IRegisterAllocator frame) { if (type.IsPrimitive) { if (type.IsByte()) builder.Add(sequencePoint, RCode.Int_to_byte, source.Register, source.Register); else if (type.IsUInt16()) builder.Add(sequencePoint, RCode.Int_to_short, source.Register, source.Register); // Call appropriate valueOf method var boxedType = type.Module.TypeSystem.Object; var r = frame.AllocateTemp(boxedType.GetReference(targetPackage)); var call = builder.Add(sequencePoint, RCode.Invoke_static, type.GetBoxValueOfMethod(), source.Registers); var last = builder.Add(sequencePoint, RCode.Move_result_object, r); return new RLRange(call, last, r); } if (type.IsGenericParameter) { var nop = builder.Add(sequencePoint, RCode.Nop); return new RLRange(nop, source); } XTypeDefinition typeDef ; if (type.TryResolve(out typeDef) && (typeDef.IsEnum)) { // Call appropriate valueOf method /*var boxedType = type.Module.TypeSystem.Object; var r = frame.AllocateTemp(boxedType.GetReference(target, nsConverter)); var call = builder.Add(sequencePoint, RCode.Invoke_static, typeDef.GetEnumUnderlyingType().GetBoxValueOfMethod(), source.Registers); var last = builder.Add(sequencePoint, RCode.Move_result_object, r); return new RLRange(call, last, r);*/ } // Just cast var checkCast = builder.Add(sequencePoint, RCode.Check_cast, type.GetReference(targetPackage), source); return new RLRange(checkCast, source); }
/// <summary> /// Create code to unbox the given source value into the given type. /// </summary> public static RLRange Unbox(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec source, XTypeReference type, AssemblyCompiler compiler, DexTargetPackage targetPackage, IRegisterAllocator frame) { if (type.IsPrimitive) { RCode convertAfterCode; var rUnboxed = frame.AllocateTemp(type.GetReference(targetPackage)); var unboxValueMethod = type.GetUnboxValueMethod(compiler, targetPackage, out convertAfterCode); var first = builder.Add(sequencePoint, RCode.Invoke_static, unboxValueMethod, source); var last = builder.Add(sequencePoint, type.MoveResult(), rUnboxed); if (convertAfterCode != RCode.Nop) { last = builder.Add(sequencePoint, convertAfterCode, rUnboxed, rUnboxed); } return new RLRange(first, last, rUnboxed); } XTypeDefinition enumTypeDef; if (type.IsEnum(out enumTypeDef)) { var rUnboxed = frame.AllocateTemp(type.GetReference(targetPackage)); var unboxValueMethod = enumTypeDef.Methods.First(x => x.Name == NameConstants.Enum.UnboxMethodName).GetReference(targetPackage); var first = builder.Add(sequencePoint, RCode.Invoke_static, unboxValueMethod, source); var last = builder.Add(sequencePoint, type.MoveResult(), rUnboxed); return new RLRange(first, last, rUnboxed); } if (!type.IsGenericParameter) { // Just cast var checkCast = builder.Add(sequencePoint, RCode.Check_cast, type.GetReference(targetPackage), source); return new RLRange(checkCast, source); } // Do nothing var nop = builder.Add(sequencePoint, RCode.Nop); return new RLRange(nop, source); }
/// <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 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(); }
public static Annotation GetGenericDefinitionAnnotationForType(XTypeReference xtype, bool forceTypeDefinition, AssemblyCompiler compiler, DexTargetPackage targetPackage) { var genericsDefAnnotationClass = compiler.GetDot42InternalType(InternalConstants.GenericDefinitionAnnotation) .GetClassReference(targetPackage); var annotation = new Annotation {Type = genericsDefAnnotationClass, Visibility = AnnotationVisibility.Runtime}; if (xtype.IsGenericInstance) { bool handled = false; List<object> genericArguments = new List<object>(); if (xtype.GetElementType().IsNullableT()) { // privitives and enums are represented by their marker classes. // no annotation needed. var argument = ((XGenericInstanceType) xtype).GenericArguments[0]; if (argument.IsEnum() || argument.IsPrimitive && !forceTypeDefinition) { return null; } // structs have marker classes. var classRef = xtype.GetReference(targetPackage) as ClassReference; var @class = classRef == null ? null : targetPackage.DexFile.GetClass(classRef.Fullname); if (@class != null && @class.NullableMarkerClass != null) { annotation.Arguments.Add(new AnnotationArgument("GenericInstanceType", @class.NullableMarkerClass)); handled = true; } } if (!handled) { foreach (var arg in ((XGenericInstanceType) xtype).GenericArguments) { if (arg.IsGenericParameter) { var gparm = (XGenericParameter) arg; // TODO: if we wanted to annotate methods as well, we should differentiate // between generic method arguments and generic type arguments. genericArguments.Add(gparm.Position); } else if (arg.IsGenericInstance) { var giparm = GetGenericDefinitionAnnotationForType((XGenericInstanceType) arg, true,compiler, targetPackage); genericArguments.Add(giparm); } else { genericArguments.Add(arg.GetReference(targetPackage)); } } annotation.Arguments.Add(new AnnotationArgument("GenericArguments", genericArguments.ToArray())); } } else // generic parameter { var parm = (XGenericParameter) xtype; annotation.Arguments.Add(new AnnotationArgument("GenericParameter", parm.Position)); } if(forceTypeDefinition && annotation.Arguments.All(a => a.Name != "GenericInstanceType")) annotation.Arguments.Add(new AnnotationArgument("GenericTypeDefinition", xtype.ElementType.GetReference(targetPackage))); return annotation; }
/// <summary> /// Create 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> /// The syntax is experimental, and neither fully fixed nor officially defined. /// </summary> private static string GetDefinition(XTypeReference xtype, bool forceTypeDefinition, AssemblyCompiler compiler, DexTargetPackage targetPackage) { StringBuilder s = new StringBuilder(); // TODO: reorganize, so that the syntax becomes clear. bool setGenericInstanceType = false; if (xtype.IsGenericInstance) { bool handled = false; if (xtype.GetElementType().IsNullableT()) { // privitives and enums are represented by their marker classes. // no annotation needed. var argument = ((XGenericInstanceType) xtype).GenericArguments[0]; if (!forceTypeDefinition && (argument.IsEnum() || argument.IsPrimitive)) { return ""; } // structs have marker classes. var classRef = xtype.GetReference(targetPackage) as ClassReference; var @class = classRef == null ? null : targetPackage.DexFile.GetClass(classRef.Fullname); if (@class != null && @class.NullableMarkerClass != null) { s.Append("@"); s.Append(GetClassName(@class.NullableMarkerClass)); setGenericInstanceType = true; handled = true; } } if (!handled) { bool isFirst = true; foreach (var arg in ((XGenericInstanceType) xtype).GenericArguments) { if (!isFirst) s.Append(","); isFirst = false; if (arg.IsGenericParameter) { var gparm = (XGenericParameter) arg; // TODO: if we wanted to annotate methods as well, we should differentiate // between generic method arguments and generic type arguments. s.Append("!"); s.Append(gparm.Position); } else if (arg.IsGenericInstance) { var giparm = GetDefinition((XGenericInstanceType)arg, true, compiler, targetPackage); s.Append("{"); s.Append(giparm); s.Append("}"); } else { s.Append(GetClassName(arg.GetReference(targetPackage))); } } } } else // generic parameter { var parm = (XGenericParameter) xtype; s.Append("!!"); s.Append(parm.Position); } if (forceTypeDefinition && !setGenericInstanceType) { string def = GetClassName(xtype.ElementType.GetReference(targetPackage)); s.Insert(0, def + "<"); s.Append(">"); } return s.ToString(); }
/// <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); } } 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); }