/// <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> /// Default ctor /// </summary> public RLRange(RLRange prefix, Instruction first, Instruction last, RegisterSpec resultRegister) { if ((prefix != null) && (prefix.first != null)) first = prefix.first; if ((last == null) && (prefix != null)) last = prefix.last; this.first = first; this.last = last; this.resultRegister = resultRegister; }
/// <summary> /// Default ctor /// </summary> public RLRange(IEnumerable<RLRange> prefix, Instruction first, Instruction last, RegisterSpec resultRegister) { var prefixList = (prefix != null) ? prefix.Where(x => x != null).ToList() : null; var firstInPrefix = (prefixList != null) ? prefixList.Select(x => x.First).FirstOrDefault() : null; var lastInPrefix = (prefixList != null) ? prefixList.Select(x => x.Last).LastOrDefault() : null; if (firstInPrefix != null) first = firstInPrefix; if (last == null) last = lastInPrefix; this.first = first; this.last = last; this.resultRegister = resultRegister; }
/// <summary> /// Default ctor /// </summary> public RLRange(RLRange prefix, Instruction first, Instruction last, RegisterSpec resultRegister) { if ((prefix != null) && (prefix.first != null)) { first = prefix.first; } if ((last == null) && (prefix != null)) { last = prefix.last; } this.first = first; this.last = last; this.resultRegister = resultRegister; }
/// <summary> /// Default ctor /// </summary> public RLRange(IEnumerable <RLRange> prefix, Instruction first, Instruction last, RegisterSpec resultRegister) { var prefixList = (prefix != null) ? prefix.Where(x => x != null).ToList() : null; var firstInPrefix = (prefixList != null) ? prefixList.Select(x => x.First).FirstOrDefault() : null; var lastInPrefix = (prefixList != null) ? prefixList.Select(x => x.Last).LastOrDefault() : null; if (firstInPrefix != null) { first = firstInPrefix; } if (last == null) { last = lastInPrefix; } this.first = first; this.last = last; this.resultRegister = resultRegister; }
/// <summary> /// Ensure the given value register is a temp register. /// If it is not, allocate a temp register an copy to it. /// </summary> public static RLRange EnsureTemp(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec value, IRegisterAllocator frame) { // Is temp? if (value.Register.Category == RCategory.Temp) return new RLRange(value); // No, allocate temp var tmp = frame.AllocateTemp(value.Type); Instruction ins; switch (value.Register.Type) { case RType.Object: ins = builder.Add(sequencePoint, RCode.Move_object, tmp.Register, value.Register); break; case RType.Wide: ins = builder.Add(sequencePoint, RCode.Move_wide, tmp.Register, value.Register); break; case RType.Value: ins = builder.Add(sequencePoint, RCode.Move, tmp.Register, value.Register); break; default: throw new ArgumentException("Unknown register type " + (int)value.Register.Type); } return new RLRange(ins, tmp); }
/// <summary> /// Create the body of the invoke method. /// </summary> private static MethodBody CreateInvokeBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition calledMethod, XMethodDefinition invokeMethod, Prototype invokePrototype, Prototype calledMethodPrototype, FieldDefinition instanceField, ClassReference delegateClass) { var body = new MethodBody(null); var rthis = body.AllocateRegister(RCategory.Argument, RType.Object); foreach (var p in invokePrototype.Parameters) { if (p.Type.IsWide()) { body.AllocateWideRegister(RCategory.Argument); } else { var type = (p.Type is PrimitiveType) ? RType.Value : RType.Object; body.AllocateRegister(RCategory.Argument, type); } } var incomingMethodArgs = body.Registers.ToArray(); // Create code var ins = body.Instructions; Register instance = null; if (!calledMethod.IsStatic) { // load instance instance = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Iget_object, instance, rthis) { Operand = instanceField }); } // Invoke var calledMethodRef = calledMethod.GetReference(targetPackage); var inputArgs = calledMethod.IsStatic ? incomingMethodArgs.Skip(1).ToArray() : incomingMethodArgs; // Cast arguments (if needed) var outputArgs = new List<Register>(); if (!calledMethod.IsStatic) { outputArgs.Add(instance); } var parameterIndex = 0; for (var i = calledMethod.IsStatic ? 0 : 1; i < inputArgs.Length; ) { var invokeType = invokePrototype.Parameters[parameterIndex].Type; var inputIsWide = invokeType.IsWide(); var calledType = calledMethodPrototype.Parameters[parameterIndex].Type; if (!invokeType.Equals(calledType)) { // Add cast / unbox var source = inputIsWide ? new RegisterSpec(inputArgs[i], inputArgs[i + 1], invokeType) : new RegisterSpec(inputArgs[i], null, invokeType); var tmp = ins.Unbox(sequencePoint, source, calledMethod.Parameters[parameterIndex].ParameterType, compiler, targetPackage, body); outputArgs.Add(tmp.Result.Register); if (calledType.IsWide()) { outputArgs.Add(tmp.Result.Register2); } } else { outputArgs.Add(inputArgs[i]); if (calledType.IsWide()) { outputArgs.Add(inputArgs[i + 1]); } } i += inputIsWide ? 2 : 1; parameterIndex++; } // Actual call ins.Add(new Instruction(calledMethod.Invoke(calledMethod, null), calledMethodRef, outputArgs.ToArray())); // Collect return value var invokeReturnType = invokePrototype.ReturnType; var calledReturnType = calledMethodPrototype.ReturnType; var needsBoxing = !invokeReturnType.Equals(calledReturnType); Instruction returnInstruction; if (calledReturnType.IsWide()) { var r = body.AllocateWideRegister(RCategory.Temp); ins.Add(new Instruction(RCode.Move_result_wide, r.Item1)); if (needsBoxing) { // Box var source = new RegisterSpec(r.Item1, r.Item2, calledReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return wide returnInstruction = new Instruction(RCode.Return_wide, r.Item1); } } else if (calledMethod.ReturnType.IsVoid()) { // Void return returnInstruction = new Instruction(RCode.Return_void); } else if (calledReturnType is PrimitiveType) { // Single register return var r = body.AllocateRegister(RCategory.Temp, RType.Value); ins.Add(new Instruction(RCode.Move_result, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return, r); } } else { var r = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Move_result_object, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, invokeMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return_object, r); } } // Call next delegate (if any) var next = body.AllocateRegister(RCategory.Temp, RType.Object); var multicastDelegateType = new ClassReference(targetPackage.NameConverter.GetConvertedFullName("System.MulticastDelegate")); var nextReference = new FieldReference(multicastDelegateType, "next", multicastDelegateType); ins.Add(new Instruction(RCode.Iget_object, nextReference, new[] { next, rthis })); // load this.next var afterCallNext = new Instruction(RCode.Nop); ins.Add(new Instruction(RCode.If_eqz, afterCallNext, new[] { next })); // if next == null, continue ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { next })); var nextInvokeMethod = new MethodReference(delegateClass, "Invoke", invokePrototype); var nextInvokeArgs = new[] { next }.Concat(incomingMethodArgs.Skip(1)).ToArray(); ins.Add(new Instruction(RCode.Invoke_virtual, nextInvokeMethod, nextInvokeArgs)); ins.Add(afterCallNext); // Add return instructions ins.Add(returnInstruction); return body; }
/// <summary> /// No code, register only ctor /// </summary> public RLRange(RLRange prefix, RegisterSpec resultRegister) : this(prefix, null, null, resultRegister) { }
/// <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> /// 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> /// 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> /// Ensure the given value register is a temp register. /// If it is not, allocate a temp register an copy to it. /// </summary> public static RLRange EnsureTemp(this IRLBuilder builder, ISourceLocation sequencePoint, RegisterSpec value, IRegisterAllocator frame) { // Is temp? if (value.Register.Category == RCategory.Temp) { return(new RLRange(value)); } // No, allocate temp var tmp = frame.AllocateTemp(value.Type); Instruction ins; switch (value.Register.Type) { case RType.Object: ins = builder.Add(sequencePoint, RCode.Move_object, tmp.Register, value.Register); break; case RType.Wide: ins = builder.Add(sequencePoint, RCode.Move_wide, tmp.Register, value.Register); break; case RType.Value: ins = builder.Add(sequencePoint, RCode.Move, tmp.Register, value.Register); break; default: throw new ArgumentException("Unknown register type " + (int)value.Register.Type); } return(new RLRange(ins, tmp)); }
/// <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> /// Default ctor /// </summary> public RLRange(Instruction first, Instruction last, RegisterSpec resultRegister) : this((RLRange)null, first, last, resultRegister) { }
/// <summary> /// No code, register only ctor /// </summary> public RLRange(IEnumerable<RLRange> prefix, RegisterSpec resultRegister) : this(prefix, null, null, resultRegister) { }
/// <summary> /// Default ctor /// </summary> public RLRange(Instruction first, Instruction last, RegisterSpec resultRegister) : this((RLRange)null, first, last, resultRegister) { }
/// <summary> /// Single instruction range ctor /// </summary> public RLRange(Instruction single, RegisterSpec resultRegister) : this((RLRange)null, single, single, resultRegister) { }
/// <summary> /// Single instruction range ctor /// </summary> public RLRange(RLRange prefix, Instruction single, RegisterSpec resultRegister) : this(prefix, single, single, resultRegister) { }
/// <summary> /// Single instruction range ctor /// </summary> public RLRange(IEnumerable <RLRange> prefix, Instruction single, RegisterSpec resultRegister) : this(prefix, single, single, resultRegister) { }
/// <summary> /// No code, register only ctor /// </summary> public RLRange(RegisterSpec resultRegister) : this((RLRange)null, null, null, resultRegister) { }
/// <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> /// No code, register only ctor /// </summary> public RLRange(RLRange prefix, RegisterSpec resultRegister) : this(prefix, null, null, resultRegister) { }
/// <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> /// 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 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 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 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 the body of the invoke method. /// </summary> /// <param name="calledMethodPrototype"></param> private MethodBody CreateInvokeBody(Prototype calledMethodPrototype) { var body = new MethodBody(null); var rthis = body.AllocateRegister(RCategory.Argument, RType.Object); foreach (var p in invokePrototype.Parameters) { if (p.Type.IsWide()) { body.AllocateWideRegister(RCategory.Argument); } else { var type = (p.Type is PrimitiveType) ? RType.Value : RType.Object; body.AllocateRegister(RCategory.Argument, type); } } var incomingMethodArgs = body.Registers.ToArray(); // Create code var ins = body.Instructions; Register instanceReg = null; if (!calledMethod.IsStatic) { // load instance instanceReg = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Iget_object, instanceReg, rthis) { Operand = instanceField }); } List<Register> genericTypeParameterRegs = new List<Register>(); foreach(var field in GenericTypeFields) { var r = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Iget_object, r, rthis) { Operand = field }); genericTypeParameterRegs.Add(r); } // Invoke var calledMethodRef = calledMethod.GetReference(targetPackage); var inputArgs = calledMethod.IsStatic ? incomingMethodArgs.Skip(1).ToArray() : incomingMethodArgs; // Cast arguments (if needed) var outputArgs = new List<Register>(); if (!calledMethod.IsStatic) { outputArgs.Add(instanceReg); } var parameterIndex = 0; for (var i = calledMethod.IsStatic ? 0 : 1; i < inputArgs.Length; ) { var invokeType = invokePrototype.Parameters[parameterIndex].Type; var inputIsWide = invokeType.IsWide(); var calledType = calledMethodPrototype.Parameters[parameterIndex].Type; if (!invokeType.Equals(calledType)) { // Add cast / unbox var source = inputIsWide ? new RegisterSpec(inputArgs[i], inputArgs[i + 1], invokeType) : new RegisterSpec(inputArgs[i], null, invokeType); var tmp = ins.Unbox(sequencePoint, source, calledMethod.Parameters[parameterIndex].ParameterType, compiler, targetPackage, body); outputArgs.Add(tmp.Result.Register); if (calledType.IsWide()) { outputArgs.Add(tmp.Result.Register2); } } else { outputArgs.Add(inputArgs[i]); if (calledType.IsWide()) { outputArgs.Add(inputArgs[i + 1]); } } i += inputIsWide ? 2 : 1; parameterIndex++; } outputArgs.AddRange(genericTypeParameterRegs); // Actual call ins.Add(new Instruction(calledMethod.Invoke(calledMethod, null), calledMethodRef, outputArgs.ToArray())); // Collect return value var invokeReturnType = invokePrototype.ReturnType; var calledReturnType = calledMethodPrototype.ReturnType; var needsBoxing = !invokeReturnType.Equals(calledReturnType); Instruction returnInstruction; Instruction nextMoveResultInstruction = null; if (calledReturnType.IsWide()) { var r = body.AllocateWideRegister(RCategory.Temp); ins.Add(new Instruction(RCode.Move_result_wide, r.Item1)); if (needsBoxing) { // Box var source = new RegisterSpec(r.Item1, r.Item2, calledReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); nextMoveResultInstruction = new Instruction(RCode.Move_result_object, tmp.Result.Register); } else { // Return wide returnInstruction = new Instruction(RCode.Return_wide, r.Item1); nextMoveResultInstruction = new Instruction(RCode.Move_result_wide, r.Item1); } } else if (calledMethod.ReturnType.IsVoid()) { // Void return returnInstruction = new Instruction(RCode.Return_void); } else if (calledReturnType is PrimitiveType) { // Single register return var r = body.AllocateRegister(RCategory.Temp, RType.Value); ins.Add(new Instruction(RCode.Move_result, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); nextMoveResultInstruction = new Instruction(RCode.Move_result_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return, r); nextMoveResultInstruction = new Instruction(RCode.Move_result, r); } } else { var r = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Move_result_object, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, invokeMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); nextMoveResultInstruction = new Instruction(RCode.Move_result_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return_object, r); nextMoveResultInstruction = new Instruction(RCode.Move_result_object, r); } } // Call delegate list var multicastDelegateType = new ClassReference(targetPackage.NameConverter.GetConvertedFullName("System.MulticastDelegate")); var invListLengthReference = new FieldReference(multicastDelegateType, "InvocationListLength", PrimitiveType.Int); var multicastDelegateArray = new ArrayType(multicastDelegateType); var invListReference = new FieldReference(multicastDelegateType, "InvocationList", multicastDelegateArray); var index = body.AllocateRegister(RCategory.Temp, RType.Value); var count = body.AllocateRegister(RCategory.Temp, RType.Value); var next = body.AllocateRegister(RCategory.Temp, RType.Object); var invList = body.AllocateRegister(RCategory.Temp, RType.Object); var done = new Instruction(RCode.Nop); var nextInvokeMethod = new MethodReference(delegateClass, "Invoke", invokePrototype); var nextInvokeArgs = new[] { next }.Concat(incomingMethodArgs.Skip(1)).ToArray(); ins.Add(new Instruction(RCode.Iget, invListLengthReference, new[] {count, rthis})); ins.Add(new Instruction(RCode.If_eqz, done, new[] { count })); ins.Add(new Instruction(RCode.Const, 0, new[] { index })); ins.Add(new Instruction(RCode.Iget_object, invListReference, new[] {invList, rthis})); var getNext = new Instruction(RCode.Aget_object, null, new[] { next, invList, index }); ins.Add(getNext); ins.Add(new Instruction(RCode.Check_cast, delegateClass, new [] { next })); ins.Add(new Instruction(RCode.Invoke_virtual, nextInvokeMethod, nextInvokeArgs)); if (nextMoveResultInstruction != null) ins.Add(nextMoveResultInstruction); ins.Add(new Instruction(RCode.Add_int_lit8, 1, new[] { index, index })); ins.Add(new Instruction(RCode.If_lt, getNext, new[] { index, count })); ins.Add(done); // Add return instructions ins.Add(returnInstruction); return body; }
/// <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> /// Single instruction range ctor /// </summary> public RLRange(Instruction single, RegisterSpec resultRegister) : this((RLRange)null, single, single, resultRegister) { }
/// <summary> /// No code, register only ctor /// </summary> public RLRange(RegisterSpec resultRegister) : this((RLRange)null, null, null, resultRegister) { }
/// <summary> /// No code, register only ctor /// </summary> public RLRange(IEnumerable <RLRange> prefix, RegisterSpec resultRegister) : this(prefix, null, null, resultRegister) { }
/// <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> /// Single instruction range ctor /// </summary> public RLRange(IEnumerable<RLRange> prefix, Instruction single, RegisterSpec resultRegister) : this(prefix, single, single, resultRegister) { }
/// <summary> /// Single instruction range ctor /// </summary> public RLRange(RLRange prefix, Instruction single, RegisterSpec resultRegister) : this(prefix, single, single, resultRegister) { }
/// <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); }