| // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion; |
| |
| import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature; |
| import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.vivifiedTypeFor; |
| |
| import com.android.tools.r8.cf.code.CfArrayLoad; |
| import com.android.tools.r8.cf.code.CfArrayStore; |
| import com.android.tools.r8.cf.code.CfCheckCast; |
| import com.android.tools.r8.cf.code.CfConstNumber; |
| import com.android.tools.r8.cf.code.CfInstruction; |
| import com.android.tools.r8.cf.code.CfInvoke; |
| import com.android.tools.r8.cf.code.CfLoad; |
| import com.android.tools.r8.cf.code.CfNewArray; |
| import com.android.tools.r8.cf.code.CfReturn; |
| import com.android.tools.r8.cf.code.CfStackInstruction; |
| import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; |
| import com.android.tools.r8.contexts.CompilationContext.MainThreadContext; |
| import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; |
| import com.android.tools.r8.contexts.CompilationContext.UniqueContext; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.CfCode; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.graph.DexProto; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.MethodAccessFlags; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.ir.code.MemberType; |
| import com.android.tools.r8.ir.code.ValueType; |
| import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer; |
| import com.android.tools.r8.ir.desugar.LocalStackAllocator; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPIConverterEventConsumer; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer; |
| import com.android.tools.r8.ir.synthetic.apiconverter.APIConversionCfCodeProvider; |
| import com.android.tools.r8.ir.synthetic.apiconverter.EqualsCfCodeProvider; |
| import com.android.tools.r8.ir.synthetic.apiconverter.HashCodeCfCodeProvider; |
| import com.android.tools.r8.utils.OptionalBool; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.function.BiFunction; |
| import java.util.function.Supplier; |
| import org.objectweb.asm.Opcodes; |
| |
| public class DesugaredLibraryConversionCfProvider { |
| |
| private final AppView<?> appView; |
| private final DexItemFactory factory; |
| private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizer; |
| |
| public DesugaredLibraryConversionCfProvider( |
| AppView<?> appView, DesugaredLibraryWrapperSynthesizer wrapperSynthesizer) { |
| this.appView = appView; |
| this.factory = appView.dexItemFactory(); |
| this.wrapperSynthesizer = wrapperSynthesizer; |
| } |
| |
| public DexEncodedMethod generateWrapperConversionWithoutCode( |
| DexMethod method, DexField wrapperField) { |
| DexMethod methodToInstall = |
| methodWithVivifiedTypeInSignature(method, wrapperField.getHolderType(), appView); |
| return wrapperSynthesizer.newSynthesizedMethod(methodToInstall, null); |
| } |
| |
| public DexEncodedMethod generateWrapperConversion( |
| DexMethod method, |
| DexField wrapperField, |
| DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer, |
| Supplier<UniqueContext> contextSupplier) { |
| DexClass holderClass = appView.definitionFor(method.getHolderType()); |
| assert holderClass != null || appView.options().isDesugaredLibraryCompilation(); |
| boolean isInterface = holderClass == null || holderClass.isInterface(); |
| ProgramMethod context = resolveContext(method, isInterface); |
| DexMethod returnConversion = |
| computeReturnConversion(method, true, eventConsumer, context, contextSupplier); |
| DexMethod[] parameterConversions = |
| computeParameterConversions(method, false, eventConsumer, context, contextSupplier); |
| DexMethod methodToInstall = |
| convertedMethod( |
| method, false, returnConversion, parameterConversions, wrapperField.getHolderType()); |
| CfCode cfCode = |
| new APIConversionCfCodeProvider( |
| appView, |
| wrapperField.getHolderType(), |
| method, |
| isInterface, |
| returnConversion, |
| parameterConversions, |
| wrapperField) |
| .generateCfCode(); |
| return wrapperSynthesizer.newSynthesizedMethod(methodToInstall, cfCode); |
| } |
| |
| public DexEncodedMethod generateWrapperHashCode(DexField wrapperField) { |
| return wrapperSynthesizer.newSynthesizedMethod( |
| appView |
| .dexItemFactory() |
| .createMethod( |
| wrapperField.getHolderType(), |
| appView.dexItemFactory().createProto(appView.dexItemFactory().intType), |
| appView.dexItemFactory().hashCodeMethodName), |
| new HashCodeCfCodeProvider(appView, wrapperField.getHolderType(), wrapperField) |
| .generateCfCode()); |
| } |
| |
| public DexEncodedMethod generateWrapperEquals(DexField wrapperField) { |
| return wrapperSynthesizer.newSynthesizedMethod( |
| appView |
| .dexItemFactory() |
| .createMethod( |
| wrapperField.getHolderType(), |
| appView |
| .dexItemFactory() |
| .createProto( |
| appView.dexItemFactory().booleanType, appView.dexItemFactory().objectType), |
| appView.dexItemFactory().equalsMethodName), |
| new EqualsCfCodeProvider(appView, wrapperField.getHolderType(), wrapperField) |
| .generateCfCode()); |
| } |
| |
| public DexEncodedMethod generateVivifiedWrapperConversionWithoutCode( |
| DexMethod method, DexField wrapperField) { |
| DexMethod methodToInstall = |
| factory.createMethod(wrapperField.getHolderType(), method.proto, method.name); |
| return wrapperSynthesizer.newSynthesizedMethod(methodToInstall, null); |
| } |
| |
| public DexEncodedMethod generateVivifiedWrapperConversion( |
| DexMethod method, |
| DexField wrapperField, |
| DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer, |
| Supplier<UniqueContext> contextSupplier) { |
| DexMethod methodToInstall = |
| factory.createMethod(wrapperField.getHolderType(), method.proto, method.name); |
| DexClass holderClass = appView.definitionFor(method.getHolderType()); |
| boolean isInterface; |
| if (holderClass == null) { |
| assert appView |
| .options() |
| .machineDesugaredLibrarySpecification |
| .isEmulatedInterfaceRewrittenType(method.getHolderType()); |
| isInterface = true; |
| } else { |
| isInterface = holderClass.isInterface(); |
| } |
| ProgramMethod context = resolveContext(method, isInterface); |
| DexMethod returnConversion = |
| computeReturnConversion(method, false, eventConsumer, context, contextSupplier); |
| DexMethod[] parameterConversions = |
| computeParameterConversions(method, true, eventConsumer, context, contextSupplier); |
| DexType newHolder = |
| appView.typeRewriter.hasRewrittenType(method.getHolderType(), appView) |
| ? vivifiedTypeFor(method.getHolderType(), appView) |
| : method.getHolderType(); |
| DexMethod forwardMethod = |
| convertedMethod(method, true, returnConversion, parameterConversions, newHolder); |
| CfCode cfCode = |
| new APIConversionCfCodeProvider( |
| appView, |
| wrapperField.getHolderType(), |
| forwardMethod, |
| isInterface, |
| returnConversion, |
| parameterConversions, |
| wrapperField) |
| .generateCfCode(); |
| return wrapperSynthesizer.newSynthesizedMethod(methodToInstall, cfCode); |
| } |
| |
| public ProgramMethod generateCallbackConversion( |
| ProgramMethod method, |
| DesugaredLibraryAPICallbackSynthesizorEventConsumer eventConsumer, |
| MainThreadContext context) { |
| DexProgramClass clazz = method.getHolder(); |
| DexMethod returnConversion = |
| computeReturnConversion( |
| method.getReference(), |
| true, |
| eventConsumer, |
| method, |
| () -> context.createUniqueContext(clazz)); |
| DexMethod[] parameterConversions = |
| computeParameterConversions( |
| method.getReference(), |
| false, |
| eventConsumer, |
| method, |
| () -> context.createUniqueContext(clazz)); |
| DexMethod methodToInstall = |
| convertedMethod(method.getReference(), false, returnConversion, parameterConversions); |
| CfCode cfCode = |
| new APIConversionCfCodeProvider( |
| appView, |
| method.getHolderType(), |
| method.getReference(), |
| clazz.isInterface(), |
| returnConversion, |
| parameterConversions) |
| .generateCfCode(); |
| DexEncodedMethod newMethod = wrapperSynthesizer.newSynthesizedMethod(methodToInstall, cfCode); |
| newMethod.setCode(cfCode, DexEncodedMethod.NO_PARAMETER_INFO); |
| if (method.getDefinition().isLibraryMethodOverride().isTrue()) { |
| newMethod.setLibraryMethodOverride(OptionalBool.TRUE); |
| } |
| ProgramMethod callback = new ProgramMethod(clazz, newMethod); |
| assert eventConsumer != null; |
| eventConsumer.acceptAPIConversionCallback(callback); |
| return callback; |
| } |
| |
| public ProgramMethod generateOutlinedAPIConversion( |
| CfInvoke invoke, |
| DesugaredLibraryAPIConverterEventConsumer eventConsumer, |
| ProgramMethod context, |
| MethodProcessingContext methodProcessingContext) { |
| DexMethod method = invoke.getMethod(); |
| DexProto newProto = |
| invoke.isInvokeStatic() |
| ? method.proto |
| : factory.prependTypeToProto(method.getHolderType(), method.getProto()); |
| DexMethod returnConversion = |
| computeReturnConversion( |
| method, false, eventConsumer, context, methodProcessingContext::createUniqueContext); |
| DexMethod[] parameterConversions = |
| computeParameterConversions( |
| method, true, eventConsumer, context, methodProcessingContext::createUniqueContext); |
| ProgramMethod outline = |
| appView |
| .getSyntheticItems() |
| .createMethod( |
| kinds -> kinds.API_CONVERSION, |
| methodProcessingContext.createUniqueContext(), |
| appView, |
| builder -> |
| builder |
| .setProto(newProto) |
| .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic()) |
| // Will be traced by the enqueuer. |
| .disableAndroidApiLevelCheck() |
| .setCode( |
| methodSignature -> |
| new APIConversionCfCodeProvider( |
| appView, |
| methodSignature.holder, |
| convertedMethod( |
| method, true, returnConversion, parameterConversions), |
| invoke.isInterface(), |
| returnConversion, |
| parameterConversions, |
| invoke.getOpcode()) |
| .generateCfCode())); |
| eventConsumer.acceptAPIConversion(outline); |
| return outline; |
| } |
| |
| public Collection<CfInstruction> generateInlinedAPIConversion( |
| CfInvoke invoke, |
| MethodProcessingContext methodProcessingContext, |
| LocalStackAllocator localStackAllocator, |
| CfInstructionDesugaringEventConsumer eventConsumer, |
| ProgramMethod context) { |
| |
| DexMethod invokedMethod = invoke.getMethod(); |
| DexMethod returnConversion = |
| computeReturnConversion( |
| invokedMethod, |
| false, |
| eventConsumer, |
| context, |
| methodProcessingContext::createUniqueContext); |
| DexMethod[] parameterConversions = |
| computeParameterConversions( |
| invokedMethod, |
| true, |
| eventConsumer, |
| context, |
| methodProcessingContext::createUniqueContext); |
| |
| // If only the last 2 parameters require conversion, we do everything inlined. |
| // If other parameters require conversion, we outline the parameter conversion but keep the API |
| // call inlined. |
| // The returned value is always converted inlined. |
| boolean requireOutlinedParameterConversion = false; |
| for (int i = 0; i < parameterConversions.length - 2; i++) { |
| requireOutlinedParameterConversion |= parameterConversions[i] != null; |
| } |
| |
| ArrayList<CfInstruction> cfInstructions = new ArrayList<>(); |
| if (requireOutlinedParameterConversion) { |
| addOutlineParameterConversionInstructions( |
| parameterConversions, |
| cfInstructions, |
| methodProcessingContext, |
| invokedMethod, |
| localStackAllocator, |
| eventConsumer); |
| } else { |
| addInlineParameterConversionInstructions(parameterConversions, cfInstructions); |
| } |
| |
| DexMethod convertedMethod = |
| convertedMethod(invokedMethod, true, returnConversion, parameterConversions); |
| cfInstructions.add(new CfInvoke(invoke.getOpcode(), convertedMethod, invoke.isInterface())); |
| |
| if (returnConversion != null) { |
| cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, returnConversion, false)); |
| } |
| |
| return cfInstructions; |
| } |
| |
| // The parameters are converted and returned in an array of converted parameters. The parameter |
| // array then needs to be unwrapped at the call site. |
| private void addOutlineParameterConversionInstructions( |
| DexMethod[] parameterConversions, |
| ArrayList<CfInstruction> cfInstructions, |
| MethodProcessingContext methodProcessingContext, |
| DexMethod invokedMethod, |
| LocalStackAllocator localStackAllocator, |
| CfInstructionDesugaringEventConsumer eventConsumer) { |
| localStackAllocator.allocateLocalStack(4); |
| DexProto newProto = |
| appView |
| .dexItemFactory() |
| .createProto( |
| appView.dexItemFactory().objectArrayType, invokedMethod.getParameters().values); |
| ProgramMethod parameterConversion = |
| appView |
| .getSyntheticItems() |
| .createMethod( |
| kinds -> kinds.API_CONVERSION_PARAMETERS, |
| methodProcessingContext.createUniqueContext(), |
| appView, |
| builder -> |
| builder |
| .setProto(newProto) |
| .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic()) |
| // Will be traced by the enqueuer. |
| .disableAndroidApiLevelCheck() |
| .setCode( |
| methodSignature -> |
| computeParameterConversionCfCode( |
| methodSignature.holder, invokedMethod, parameterConversions))); |
| eventConsumer.acceptAPIConversion(parameterConversion); |
| cfInstructions.add( |
| new CfInvoke(Opcodes.INVOKESTATIC, parameterConversion.getReference(), false)); |
| for (int i = 0; i < parameterConversions.length; i++) { |
| cfInstructions.add(new CfStackInstruction(Opcode.Dup)); |
| cfInstructions.add(new CfConstNumber(i, ValueType.INT)); |
| DexType parameterType = |
| parameterConversions[i] != null |
| ? parameterConversions[i].getReturnType() |
| : invokedMethod.getParameter(i); |
| cfInstructions.add(new CfArrayLoad(MemberType.OBJECT)); |
| if (parameterType.isPrimitiveType()) { |
| cfInstructions.add(new CfCheckCast(factory.getBoxedForPrimitiveType(parameterType))); |
| DexMethod method = appView.dexItemFactory().getUnboxPrimitiveMethod(parameterType); |
| cfInstructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false)); |
| } else { |
| cfInstructions.add(new CfCheckCast(parameterType)); |
| } |
| cfInstructions.add(new CfStackInstruction(Opcode.Swap)); |
| } |
| cfInstructions.add(new CfStackInstruction(Opcode.Pop)); |
| } |
| |
| private CfCode computeParameterConversionCfCode( |
| DexType holder, DexMethod invokedMethod, DexMethod[] parameterConversions) { |
| ArrayList<CfInstruction> cfInstructions = new ArrayList<>(); |
| cfInstructions.add(new CfConstNumber(parameterConversions.length, ValueType.INT)); |
| cfInstructions.add(new CfNewArray(factory.objectArrayType)); |
| int stackIndex = 0; |
| for (int i = 0; i < invokedMethod.getArity(); i++) { |
| cfInstructions.add(new CfStackInstruction(Opcode.Dup)); |
| cfInstructions.add(new CfConstNumber(i, ValueType.INT)); |
| DexType param = invokedMethod.getParameter(i); |
| cfInstructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex)); |
| if (parameterConversions[i] != null) { |
| cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, parameterConversions[i], false)); |
| } |
| if (param.isPrimitiveType()) { |
| DexMethod method = appView.dexItemFactory().getBoxPrimitiveMethod(param); |
| cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false)); |
| } |
| cfInstructions.add(new CfArrayStore(MemberType.OBJECT)); |
| if (param == appView.dexItemFactory().longType |
| || param == appView.dexItemFactory().doubleType) { |
| stackIndex++; |
| } |
| stackIndex++; |
| } |
| cfInstructions.add(new CfReturn(ValueType.OBJECT)); |
| return new CfCode( |
| holder, |
| invokedMethod.getParameters().size() + 4, |
| invokedMethod.getParameters().size(), |
| cfInstructions); |
| } |
| |
| private void addInlineParameterConversionInstructions( |
| DexMethod[] parameterConversions, ArrayList<CfInstruction> cfInstructions) { |
| if (parameterConversions.length > 0 |
| && parameterConversions[parameterConversions.length - 1] != null) { |
| cfInstructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESTATIC, parameterConversions[parameterConversions.length - 1], false)); |
| } |
| if (parameterConversions.length > 1 |
| && parameterConversions[parameterConversions.length - 2] != null) { |
| cfInstructions.add(new CfStackInstruction(Opcode.Swap)); |
| cfInstructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESTATIC, parameterConversions[parameterConversions.length - 2], false)); |
| cfInstructions.add(new CfStackInstruction(Opcode.Swap)); |
| } |
| } |
| |
| private ProgramMethod resolveContext(DexMethod method, boolean isInterface) { |
| // The context is provided to improve the error message with origin and position. If the method |
| // is missing from the input, the context is null but the code runs correctly. |
| return appView |
| .appInfoForDesugaring() |
| .resolveMethodLegacy(method, isInterface) |
| .getResolvedProgramMethod(); |
| } |
| |
| private DexMethod computeReturnConversion( |
| DexMethod invokedMethod, |
| boolean destIsVivified, |
| DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer, |
| ProgramMethod context, |
| Supplier<UniqueContext> contextSupplier) { |
| return internalComputeReturnConversion( |
| invokedMethod, |
| (returnType, apiConversionCollection) -> |
| wrapperSynthesizer.ensureConversionMethod( |
| returnType, |
| destIsVivified, |
| apiConversionCollection, |
| eventConsumer, |
| contextSupplier), |
| context); |
| } |
| |
| private DexMethod computeReturnConversion( |
| DexMethod invokedMethod, |
| boolean destIsVivified, |
| DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer, |
| ProgramMethod context, |
| Supplier<UniqueContext> contextSupplier) { |
| return internalComputeReturnConversion( |
| invokedMethod, |
| (returnType, apiConversionCollection) -> |
| wrapperSynthesizer.getExistingProgramConversionMethod( |
| returnType, |
| destIsVivified, |
| apiConversionCollection, |
| eventConsumer, |
| contextSupplier), |
| context); |
| } |
| |
| private DexMethod internalComputeReturnConversion( |
| DexMethod invokedMethod, |
| BiFunction<DexType, DexMethod, DexMethod> methodSupplier, |
| ProgramMethod context) { |
| DexType returnType = invokedMethod.proto.returnType; |
| DexMethod apiGenericTypesConversion = getReturnApiGenericConversion(invokedMethod); |
| if (wrapperSynthesizer.shouldConvert( |
| returnType, apiGenericTypesConversion, invokedMethod, context)) { |
| return methodSupplier.apply(returnType, apiGenericTypesConversion); |
| } |
| return null; |
| } |
| |
| private DexMethod[] computeParameterConversions( |
| DexMethod invokedMethod, |
| boolean destIsVivified, |
| DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer, |
| ProgramMethod context, |
| Supplier<UniqueContext> contextSupplier) { |
| return internalComputeParameterConversions( |
| invokedMethod, |
| wrapperSynthesizer, |
| (argType, apiGenericTypesConversion) -> |
| wrapperSynthesizer.ensureConversionMethod( |
| argType, destIsVivified, apiGenericTypesConversion, eventConsumer, contextSupplier), |
| context); |
| } |
| |
| private DexMethod[] computeParameterConversions( |
| DexMethod invokedMethod, |
| boolean destIsVivified, |
| DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer, |
| ProgramMethod context, |
| Supplier<UniqueContext> contextSupplier) { |
| return internalComputeParameterConversions( |
| invokedMethod, |
| wrapperSynthesizer, |
| (argType, apiGenericTypesConversion) -> |
| wrapperSynthesizer.getExistingProgramConversionMethod( |
| argType, destIsVivified, apiGenericTypesConversion, eventConsumer, contextSupplier), |
| context); |
| } |
| |
| private DexMethod[] internalComputeParameterConversions( |
| DexMethod invokedMethod, |
| DesugaredLibraryWrapperSynthesizer wrapperSynthesizor, |
| BiFunction<DexType, DexMethod, DexMethod> methodSupplier, |
| ProgramMethod context) { |
| DexMethod[] parameterConversions = new DexMethod[invokedMethod.getArity()]; |
| DexType[] parameters = invokedMethod.proto.parameters.values; |
| for (int i = 0; i < parameters.length; i++) { |
| DexMethod apiGenericTypesConversion = getApiGenericConversion(invokedMethod, i); |
| DexType argType = parameters[i]; |
| if (wrapperSynthesizor.shouldConvert( |
| argType, apiGenericTypesConversion, invokedMethod, context)) { |
| parameterConversions[i] = methodSupplier.apply(argType, apiGenericTypesConversion); |
| } |
| } |
| return parameterConversions; |
| } |
| |
| public DexMethod getReturnApiGenericConversion(DexMethod method) { |
| return getApiGenericConversion(method, method.getArity()); |
| } |
| |
| public DexMethod getApiGenericConversion(DexMethod method, int parameterIndex) { |
| DexMethod[] conversions = |
| appView |
| .options() |
| .machineDesugaredLibrarySpecification |
| .getApiGenericConversion() |
| .get(method); |
| return conversions == null ? null : conversions[parameterIndex]; |
| } |
| |
| private DexMethod convertedMethod( |
| DexMethod method, |
| boolean parameterDestIsVivified, |
| DexMethod returnConversion, |
| DexMethod[] parameterConversions) { |
| return convertedMethod( |
| method, |
| parameterDestIsVivified, |
| returnConversion, |
| parameterConversions, |
| method.getHolderType()); |
| } |
| |
| private DexMethod convertedMethod( |
| DexMethod method, |
| boolean parameterDestIsVivified, |
| DexMethod returnConversion, |
| DexMethod[] parameterConversions, |
| DexType newHolder) { |
| DexType newReturnType = |
| returnConversion != null |
| ? parameterDestIsVivified |
| ? returnConversion.getParameter(0) |
| : returnConversion.getReturnType() |
| : method.getReturnType(); |
| DexType[] newParameterTypes = new DexType[parameterConversions.length]; |
| for (int i = 0; i < parameterConversions.length; i++) { |
| newParameterTypes[i] = |
| parameterConversions[i] != null |
| ? parameterDestIsVivified |
| ? parameterConversions[i].getReturnType() |
| : parameterConversions[i].getParameter(0) |
| : method.getParameter(i); |
| } |
| DexMethod convertedAPI = |
| appView |
| .dexItemFactory() |
| .createMethod( |
| newHolder, |
| appView.dexItemFactory().createProto(newReturnType, newParameterTypes), |
| method.name); |
| assert convertedAPI == methodWithVivifiedTypeInSignature(method, newHolder, appView) |
| || appView |
| .options() |
| .machineDesugaredLibrarySpecification |
| .getApiGenericConversion() |
| .containsKey(method) |
| || invalidType(method, returnConversion, parameterConversions, appView) != null; |
| return convertedAPI; |
| } |
| |
| private static DexType invalidType( |
| DexMethod invokedMethod, |
| DexMethod returnConversion, |
| DexMethod[] parameterConversions, |
| AppView<?> appView) { |
| DexMethod convertedMethod = |
| methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView); |
| if (invokedMethod.getReturnType() != convertedMethod.getReturnType() |
| && returnConversion == null) { |
| return invokedMethod.getReturnType(); |
| } |
| for (int i = 0; i < invokedMethod.getArity(); i++) { |
| if (invokedMethod.getParameter(i) != convertedMethod.getParameter(i) |
| && parameterConversions[i] == null) { |
| return invokedMethod.getParameter(i); |
| } |
| } |
| return null; |
| } |
| } |