/// <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); }
private static MethodDefinition CreateFactoryMethod(AssemblyCompiler compiler, DexTargetPackage targetPackage, CustomAttribute attribute, AttributeAnnotationMapping mapping) { var targetClass = mapping.AttributeClass; // is this really the right place for the factory methods? ISourceLocation seqp = null; var attributeTypeDef = attribute.AttributeType.Resolve(); // create method string methodName = CreateAttributeFactoryMethodName(targetClass); MethodDefinition method = new MethodDefinition(targetClass, methodName, new Prototype(mapping.AttributeClass)); method.AccessFlags = AccessFlags.Public | AccessFlags.Static | AccessFlags.Synthetic; targetClass.Methods.Add(method); // create method body MethodBody body = new MethodBody(null); // Allocate attribute Register attributeReg = body.AllocateRegister(RCategory.Temp, RType.Object); body.Instructions.Add(seqp, RCode.New_instance, mapping.AttributeClass, attributeReg); // collect ctor arguments List <Register> ctorArgRegs = new List <Register>() { attributeReg }; foreach (var p in attribute.ConstructorArguments) { XTypeReference xType = XBuilder.AsTypeReference(compiler.Module, p.Type); Register[] valueRegs = CreateInitializeValueInstructions(seqp, body, xType, p, compiler, targetPackage); ctorArgRegs.AddRange(valueRegs); } // Invoke ctor DexLib.MethodReference dctor = attribute.Constructor.GetReference(targetPackage, compiler.Module); body.Instructions.Add(seqp, RCode.Invoke_direct, dctor, ctorArgRegs.ToArray()); // set field values foreach (var p in attribute.Fields) { var field = GetField(attributeTypeDef, p.Name); var xField = XBuilder.AsFieldReference(compiler.Module, field); Register[] valueRegs = CreateInitializeValueInstructions(seqp, body, xField.FieldType, p.Argument, compiler, targetPackage); body.Instructions.Add(seqp, xField.FieldType.IPut(), xField.GetReference(targetPackage), valueRegs[0], attributeReg); } // set property values foreach (var p in attribute.Properties) { PropertyDefinition property = GetSettableProperty(attributeTypeDef, p.Name); XTypeReference xType = XBuilder.AsTypeReference(compiler.Module, property.PropertyType); Register[] valueRegs = CreateInitializeValueInstructions(seqp, body, xType, p.Argument, compiler, targetPackage); XMethodDefinition xSetMethod = XBuilder.AsMethodDefinition(compiler.Module, property.SetMethod); body.Instructions.Add(seqp, xSetMethod.Invoke(xSetMethod, null), xSetMethod.GetReference(targetPackage), new[] { attributeReg }.Concat(valueRegs).ToArray()); } // Return attribute body.Instructions.Add(seqp, RCode.Return_object, attributeReg); // Register method body targetPackage.Record(new CompiledMethod() { DexMethod = method, RLBody = body }); // Return method return(method); }
/// <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); }