| // Copyright (c) 2018, 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; |
| |
| import com.android.tools.r8.androidapi.ComputedApiLevel; |
| import com.android.tools.r8.cf.code.CfInstanceFieldRead; |
| import com.android.tools.r8.cf.code.CfInstanceFieldWrite; |
| 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.CfNew; |
| import com.android.tools.r8.cf.code.CfReturn; |
| import com.android.tools.r8.cf.code.CfReturnVoid; |
| import com.android.tools.r8.cf.code.CfStackInstruction; |
| import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; |
| import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; |
| import com.android.tools.r8.dex.ApplicationReader; |
| import com.android.tools.r8.dex.Constants; |
| import com.android.tools.r8.errors.BackportDiagnostic; |
| import com.android.tools.r8.errors.Unreachable; |
| import com.android.tools.r8.graph.AppInfo; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.CfCode; |
| import com.android.tools.r8.graph.Code; |
| import com.android.tools.r8.graph.DexApplication; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexClassAndMethod; |
| import com.android.tools.r8.graph.DexEncodedField; |
| 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.DexString; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.FieldAccessFlags; |
| import com.android.tools.r8.graph.MethodAccessFlags; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.ir.code.ValueType; |
| import com.android.tools.r8.ir.desugar.backports.BackportedMethodDesugaringEventConsumer; |
| import com.android.tools.r8.ir.desugar.backports.BackportedMethods; |
| import com.android.tools.r8.ir.desugar.backports.BooleanMethodRewrites; |
| import com.android.tools.r8.ir.desugar.backports.CollectionMethodGenerators; |
| import com.android.tools.r8.ir.desugar.backports.CollectionMethodRewrites; |
| import com.android.tools.r8.ir.desugar.backports.FloatMethodRewrites; |
| import com.android.tools.r8.ir.desugar.backports.LongMethodRewrites; |
| import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites; |
| import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites; |
| import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites; |
| import com.android.tools.r8.ir.desugar.backports.SparseArrayMethodRewrites; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter; |
| import com.android.tools.r8.position.MethodPosition; |
| import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy; |
| import com.android.tools.r8.synthesis.SyntheticNaming; |
| import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; |
| import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.Timing; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ExecutorService; |
| import java.util.function.Consumer; |
| import org.objectweb.asm.Opcodes; |
| |
| public final class BackportedMethodRewriter implements CfInstructionDesugaring { |
| |
| private final AppView<?> appView; |
| private final RewritableMethods rewritableMethods; |
| |
| public BackportedMethodRewriter(AppView<?> appView) { |
| assert appView.options().desugarState.isOn(); |
| this.appView = appView; |
| this.rewritableMethods = new RewritableMethods(appView); |
| } |
| |
| public boolean hasBackports() { |
| return !rewritableMethods.isEmpty(); |
| } |
| |
| @Override |
| public Collection<CfInstruction> desugarInstruction( |
| CfInstruction instruction, |
| FreshLocalProvider freshLocalProvider, |
| LocalStackAllocator localStackAllocator, |
| CfInstructionDesugaringEventConsumer eventConsumer, |
| ProgramMethod context, |
| MethodProcessingContext methodProcessingContext, |
| CfInstructionDesugaringCollection desugaringCollection, |
| DexItemFactory dexItemFactory) { |
| if (!instruction.isInvoke()) { |
| return null; |
| } |
| |
| CfInvoke invoke = instruction.asInvoke(); |
| MethodProvider methodProvider = getMethodProviderOrNull(invoke.getMethod(), context); |
| return methodProvider != null |
| ? methodProvider.rewriteInvoke( |
| invoke, appView, eventConsumer, methodProcessingContext, localStackAllocator) |
| : null; |
| } |
| |
| @Override |
| public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) { |
| return instruction.isInvoke() |
| && getMethodProviderOrNull(instruction.asInvoke().getMethod(), context) != null |
| && !appView |
| .getSyntheticItems() |
| .isSyntheticOfKind(context.getContextType(), kinds -> kinds.BACKPORT_WITH_FORWARDING); |
| } |
| |
| public static List<DexMethod> generateListOfBackportedMethods( |
| AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException { |
| List<DexMethod> methods = new ArrayList<>(); |
| AppInfo appInfo = null; |
| if (androidApp != null) { |
| DexApplication app = |
| new ApplicationReader(androidApp, options, Timing.empty()).read(executor); |
| options.loadMachineDesugaredLibrarySpecification(Timing.empty(), app); |
| appInfo = AppInfo.createInitialAppInfo(app, GlobalSyntheticsStrategy.forNonSynthesizing()); |
| } |
| TypeRewriter typeRewriter = options.getTypeRewriter(); |
| AppView<?> appView = AppView.createForD8(appInfo, typeRewriter, Timing.empty()); |
| BackportedMethodRewriter.RewritableMethods rewritableMethods = |
| new BackportedMethodRewriter.RewritableMethods(appView); |
| rewritableMethods.visit(methods::add); |
| if (appInfo != null) { |
| DesugaredLibraryRetargeter desugaredLibraryRetargeter = |
| new DesugaredLibraryRetargeter(appView); |
| desugaredLibraryRetargeter.visit(methods::add); |
| } |
| return methods; |
| } |
| |
| public static void registerAssumedLibraryTypes(InternalOptions options) { |
| // TODO(b/150693139): Remove the pre-registration once fixed. |
| BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory); |
| } |
| |
| private MethodProvider getMethodProviderOrNull(DexMethod method, ProgramMethod context) { |
| DexMethod original = appView.graphLens().getOriginalMethodSignature(method); |
| assert original != null; |
| MethodProvider provider = rewritableMethods.getProvider(original); |
| // Old versions of desugared library have in the jar file pre-desugared code. This is used |
| // to undesugar pre-desugared code, then the code is re-desugared with D8/R8. This is |
| // maintained for legacy only, recent desugared library should not be shipped with |
| // pre-desugared code. |
| Map<DexType, DexType> legacyBackport = |
| appView.options().machineDesugaredLibrarySpecification.getLegacyBackport(); |
| if (provider == null |
| && appView.options().isDesugaredLibraryCompilation() |
| && legacyBackport.containsKey(method.holder)) { |
| DexType newHolder = legacyBackport.get(method.holder); |
| DexMethod backportedMethod = |
| appView.dexItemFactory().createMethod(newHolder, method.proto, method.name); |
| provider = rewritableMethods.getProvider(backportedMethod); |
| } |
| if (provider != null && appView.options().disableBackportsWithErrorDiagnostics) { |
| appView |
| .reporter() |
| .error( |
| new BackportDiagnostic( |
| provider.method, context.getOrigin(), MethodPosition.create(context))); |
| return null; |
| } |
| return provider; |
| } |
| |
| private static final class RewritableMethods { |
| |
| private final Map<DexType, AndroidApiLevel> typeMinApi; |
| |
| private final AppView<?> appView; |
| |
| // Map backported method to a provider for creating the actual target method (with code). |
| private final Map<DexMethod, MethodProvider> rewritable = new IdentityHashMap<>(); |
| |
| RewritableMethods(AppView<?> appView) { |
| InternalOptions options = appView.options(); |
| DexItemFactory factory = options.dexItemFactory(); |
| this.appView = appView; |
| this.typeMinApi = initializeTypeMinApi(factory); |
| if (!options.enableBackportedMethodRewriting()) { |
| return; |
| } |
| |
| if (options.getMinApiLevel().isLessThan(AndroidApiLevel.K)) { |
| initializeAndroidKMethodProviders(factory); |
| if (typeIsAbsentOrPresentWithoutBackportsFrom(factory.objectsType, AndroidApiLevel.K)) { |
| initializeAndroidKObjectsMethodProviders(factory); |
| } |
| } |
| if (options.getMinApiLevel().isLessThan(AndroidApiLevel.N)) { |
| initializeAndroidNMethodProviders(factory); |
| if (typeIsAbsentOrPresentWithoutBackportsFrom(factory.objectsType, AndroidApiLevel.N)) { |
| initializeAndroidNObjectsMethodProviders(factory); |
| if (typeIsPresent(factory.supplierType)) { |
| initializeAndroidNObjectsMethodProviderWithSupplier(factory); |
| } |
| } |
| } |
| if (options.getMinApiLevel().isLessThan(AndroidApiLevel.O)) { |
| initializeAndroidOMethodProviders(factory); |
| if (typeIsPresent(factory.supplierType)) { |
| initializeAndroidOThreadLocalMethodProviderWithSupplier(factory); |
| } |
| } |
| if (options.getMinApiLevel().isLessThan(AndroidApiLevel.R)) { |
| if (options.testing.alwaysBackportListSetMapMethods |
| || typeIsPresentWithoutBackportsFrom(factory.setType, AndroidApiLevel.R)) { |
| initializeAndroidRSetListMapMethodProviders(factory); |
| } |
| if (typeIsAbsentOrPresentWithoutBackportsFrom(factory.objectsType, AndroidApiLevel.R)) { |
| initializeAndroidRObjectsMethodProviders(factory); |
| if (typeIsPresent(factory.supplierType)) { |
| initializeAndroidRObjectsMethodProviderWithSupplier(factory); |
| } |
| } |
| } |
| if (options.getMinApiLevel().isLessThan(AndroidApiLevel.S)) { |
| initializeAndroidSMethodProviders(factory); |
| if (options.testing.alwaysBackportListSetMapMethods |
| || typeIsPresentWithoutBackportsFrom(factory.setType, AndroidApiLevel.S)) { |
| initializeAndroidSSetListMapMethodProviders(factory); |
| } |
| } |
| if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Sv2)) { |
| initializeAndroidSv2MethodProviders(factory); |
| } |
| if (options.getMinApiLevel().isLessThan(AndroidApiLevel.T)) { |
| initializeAndroidTMethodProviders(factory); |
| if (typeIsPresentWithoutBackportsFrom(factory.optionalType, AndroidApiLevel.T)) { |
| initializeAndroidOptionalTMethodProviders(factory); |
| } |
| } |
| |
| // These are currently not implemented at any API level in Android. |
| if (typeIsPresentWithoutNeverIntroducedBackports(factory.streamType)) { |
| initializeStreamMethodProviders(factory); |
| } |
| if (typeIsPresentWithoutNeverIntroducedBackports(factory.predicateType)) { |
| initializePredicateMethodProviders(factory); |
| } |
| initializeJava9MethodProviders(factory); |
| initializeJava10MethodProviders(factory); |
| initializeJava11MethodProviders(factory); |
| } |
| |
| private Map<DexType, AndroidApiLevel> initializeTypeMinApi(DexItemFactory factory) { |
| ImmutableMap.Builder<DexType, AndroidApiLevel> builder = ImmutableMap.builder(); |
| builder.put(factory.objectsType, AndroidApiLevel.K); |
| builder.put(factory.optionalType, AndroidApiLevel.N); |
| builder.put(factory.predicateType, AndroidApiLevel.N); |
| builder.put(factory.setType, AndroidApiLevel.B); |
| builder.put(factory.streamType, AndroidApiLevel.N); |
| builder.put(factory.supplierType, AndroidApiLevel.N); |
| ImmutableMap<DexType, AndroidApiLevel> typeMinApi = builder.build(); |
| assert minApiMatchDatabaseMinApi(typeMinApi); |
| return typeMinApi; |
| } |
| |
| private boolean minApiMatchDatabaseMinApi(ImmutableMap<DexType, AndroidApiLevel> typeMinApi) { |
| // TODO(b/224954240): Remove the assertion and always use the apiDatabase. |
| typeMinApi.forEach( |
| (type, api) -> { |
| ComputedApiLevel apiLevel = |
| appView |
| .apiLevelCompute() |
| .computeApiLevelForLibraryReference(type, ComputedApiLevel.unknown()); |
| if (!apiLevel.isKnownApiLevel()) { |
| // API database is missing. |
| return; |
| } |
| AndroidApiLevel theApi = apiLevel.asKnownApiLevel().getApiLevel(); |
| if (typeIsInDesugaredLibrary(type)) { |
| assert theApi.equals(appView.options().getMinApiLevel()); |
| return; |
| } |
| DexClass clazz = |
| appView |
| .contextIndependentDefinitionForWithResolutionResult(type) |
| .toSingleClassWithProgramOverLibrary(); |
| assert theApi.equals(api.max(appView.options().getMinApiLevel())) |
| || (clazz != null && clazz.isProgramClass()); |
| }); |
| return true; |
| } |
| |
| private boolean typeIsInDesugaredLibrary(DexType type) { |
| return appView.typeRewriter.hasRewrittenType(type, appView) |
| || appView |
| .options() |
| .machineDesugaredLibrarySpecification |
| .getEmulatedInterfaces() |
| .containsKey(type) |
| || appView |
| .options() |
| .machineDesugaredLibrarySpecification |
| .getMaintainType() |
| .contains(type); |
| } |
| |
| private boolean typeIsAbsentOrPresentWithoutBackportsFrom( |
| DexType type, AndroidApiLevel apiLevel) { |
| return !typeIsPresent(type) || typeIsPresentWithoutBackportsFrom(type, apiLevel); |
| } |
| |
| private boolean typeIsPresentWithoutNeverIntroducedBackports(DexType type) { |
| return typeIsPresentWithoutBackportsFrom(type, AndroidApiLevel.ANDROID_PLATFORM); |
| } |
| |
| private boolean typeIsPresentWithoutBackportsFrom(DexType type, AndroidApiLevel methodsMinAPI) { |
| if (typeIsInDesugaredLibrary(type)) { |
| // Desugared library is enabled, the methods are present if desugared library specifies it. |
| return methodsMinAPI.isGreaterThan(AndroidApiLevel.N) |
| && !appView.options().machineDesugaredLibrarySpecification.includesJDK11Methods(); |
| } |
| // TODO(b/224954240): Always use the apiDatabase when always available. |
| if (!appView.options().getMinApiLevel().isGreaterThanOrEqualTo(typeMinApi.get(type))) { |
| // If the class is not present, we do not backport to avoid confusion in error messages. |
| return false; |
| } |
| return appView.options().getMinApiLevel().isLessThan(methodsMinAPI); |
| } |
| |
| private boolean typeIsPresent(DexType type) { |
| // TODO(b/224954240): Always use the apiDatabase when always available. |
| return appView.options().getMinApiLevel().isGreaterThanOrEqualTo(typeMinApi.get(type)) |
| || typeIsInDesugaredLibrary(type); |
| } |
| |
| boolean isEmpty() { |
| return rewritable.isEmpty(); |
| } |
| |
| public void visit(Consumer<DexMethod> consumer) { |
| rewritable.keySet().forEach(consumer); |
| } |
| |
| private void initializeAndroidKObjectsMethodProviders(DexItemFactory factory) { |
| DexType type; |
| DexString name; |
| DexProto proto; |
| DexMethod method; |
| DexClassAndMethod definition; |
| |
| // Objects |
| type = factory.objectsType; |
| |
| // int Objects.compare(T a, T b, Comparator<? super T> c) |
| name = factory.createString("compare"); |
| proto = |
| factory.createProto( |
| factory.intType, factory.objectType, factory.objectType, factory.comparatorType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_compare)); |
| |
| // boolean Objects.deepEquals(Object a, Object b) |
| name = factory.createString("deepEquals"); |
| proto = factory.createProto(factory.booleanType, factory.objectType, factory.objectType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_deepEquals)); |
| |
| // boolean Objects.equals(Object a, Object b) |
| name = factory.createString("equals"); |
| proto = factory.createProto(factory.booleanType, factory.objectType, factory.objectType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_equals)); |
| |
| // int Objects.hash(Object... o) |
| name = factory.createString("hash"); |
| proto = factory.createProto(factory.intType, factory.objectArrayType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, ObjectsMethodRewrites.rewriteToArraysHashCode())); |
| |
| // int Objects.hashCode(Object o) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.objectType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_hashCode)); |
| |
| // T Objects.requireNonNull(T obj) |
| method = factory.objectsMethods.requireNonNull; |
| addProvider(new InvokeRewriter(method, ObjectsMethodRewrites.rewriteRequireNonNull())); |
| |
| // T Objects.requireNonNull(T obj, String message) |
| method = factory.objectsMethods.requireNonNullWithMessage; |
| definition = appView.enableWholeProgramOptimizations() ? appView.definitionFor(method) : null; |
| // Only backport requireNonNull(Object, String) if it is not matched by -convertchecknotnull. |
| // Otherwise all calls will be rewritten to getClass(). |
| if (definition == null || !definition.getOptimizationInfo().isConvertCheckNotNull()) { |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::ObjectsMethods_requireNonNullMessage, |
| "requireNonNullMessage")); |
| } |
| |
| // String Objects.toString(Object o) |
| name = factory.createString("toString"); |
| proto = factory.createProto(factory.stringType, factory.objectType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_toString)); |
| |
| // String Objects.toString(Object o, String nullDefault); |
| name = factory.createString("toString"); |
| proto = factory.createProto(factory.stringType, factory.objectType, factory.stringType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::ObjectsMethods_toStringDefault, "toStringDefault")); |
| } |
| |
| private void initializeAndroidKMethodProviders(DexItemFactory factory) { |
| // Byte |
| DexType type = factory.boxedByteType; |
| // int Byte.compare(byte a, byte b) |
| DexString name = factory.createString("compare"); |
| DexProto proto = factory.createProto(factory.intType, factory.byteType, factory.byteType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ByteMethods_compare)); |
| |
| // Short |
| type = factory.boxedShortType; |
| // int Short.compare(short a, short b) |
| name = factory.createString("compare"); |
| proto = factory.createProto(factory.intType, factory.shortType, factory.shortType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ShortMethods_compare)); |
| |
| // Integer |
| type = factory.boxedIntType; |
| // int Integer.compare(int a, int b) |
| name = factory.createString("compare"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::IntegerMethods_compare)); |
| |
| // Long |
| type = factory.boxedLongType; |
| // int Long.compare(long a, long b) |
| name = factory.createString("compare"); |
| proto = factory.createProto(factory.intType, factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, LongMethodRewrites.rewriteCompare())); |
| |
| // Boolean |
| type = factory.boxedBooleanType; |
| // int Boolean.compare(boolean a, boolean b) |
| name = factory.createString("compare"); |
| proto = factory.createProto(factory.intType, factory.booleanType, factory.booleanType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::BooleanMethods_compare)); |
| |
| // Character |
| type = factory.boxedCharType; |
| // int Character.compare(char a, char b) |
| name = factory.createString("compare"); |
| proto = factory.createProto(factory.intType, factory.charType, factory.charType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::CharacterMethods_compare)); |
| |
| // Collections |
| type = factory.collectionsType; |
| |
| // Enumeration<T> Collections.emptyEnumeration(); |
| name = factory.createString("emptyEnumeration"); |
| proto = factory.createProto(factory.enumerationType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::CollectionsMethods_emptyEnumeration)); |
| |
| // Iterator<T> Collections.emptyIterator(); |
| name = factory.createString("emptyIterator"); |
| proto = factory.createProto(factory.iteratorType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::CollectionsMethods_emptyIterator)); |
| |
| // ListIterator<T> Collections.emptyListIterator(); |
| name = factory.createString("emptyListIterator"); |
| proto = factory.createProto(factory.listIteratorType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::CollectionsMethods_emptyListIterator)); |
| } |
| |
| private void initializeAndroidNObjectsMethodProviders(DexItemFactory factory) { |
| DexString name; |
| DexProto proto; |
| DexMethod method; |
| |
| // Objects |
| DexType type = factory.objectsType; |
| |
| // boolean Objects.isNull(Object o) |
| name = factory.createString("isNull"); |
| proto = factory.createProto(factory.booleanType, factory.objectType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_isNull)); |
| |
| // boolean Objects.nonNull(Object a) |
| name = factory.createString("nonNull"); |
| proto = factory.createProto(factory.booleanType, factory.objectType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_nonNull)); |
| } |
| |
| private void initializeAndroidNMethodProviders(DexItemFactory factory) { |
| // Byte |
| DexType type = factory.boxedByteType; |
| // int Byte.hashCode(byte i) |
| DexString name = factory.createString("hashCode"); |
| DexProto proto = factory.createProto(factory.intType, factory.byteType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity())); |
| |
| // Short |
| type = factory.boxedShortType; |
| // int Short.hashCode(short i) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.shortType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity())); |
| |
| // Integer |
| type = factory.boxedIntType; |
| |
| // int Integer.hashCode(int i) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity())); |
| |
| // int Integer.max(int a, int b) |
| name = factory.createString("max"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // int Integer.min(int a, int b) |
| name = factory.createString("min"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // int Integer.sum(int a, int b) |
| name = factory.createString("sum"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction())); |
| |
| // Double |
| type = factory.boxedDoubleType; |
| |
| // int Double.hashCode(double d) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.doubleType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::DoubleMethods_hashCode)); |
| |
| // double Double.max(double a, double b) |
| name = factory.createString("max"); |
| proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // double Double.min(double a, double b) |
| name = factory.createString("min"); |
| proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // double Double.sum(double a, double b) |
| name = factory.createString("sum"); |
| proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction())); |
| |
| // boolean Double.isFinite(double a) |
| name = factory.createString("isFinite"); |
| proto = factory.createProto(factory.booleanType, factory.doubleType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::DoubleMethods_isFinite)); |
| |
| // Float |
| type = factory.boxedFloatType; |
| |
| // int Float.hashCode(float d) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.floatType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, FloatMethodRewrites.rewriteHashCode())); |
| |
| // float Float.max(float a, float b) |
| name = factory.createString("max"); |
| proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // float Float.min(float a, float b) |
| name = factory.createString("min"); |
| proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // float Float.sum(float a, float b) |
| name = factory.createString("sum"); |
| proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction())); |
| |
| // boolean Float.isFinite(float a) |
| name = factory.createString("isFinite"); |
| proto = factory.createProto(factory.booleanType, factory.floatType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::FloatMethods_isFinite)); |
| |
| // Boolean |
| type = factory.boxedBooleanType; |
| |
| // int Boolean.hashCode(boolean b) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.booleanType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::BooleanMethods_hashCode)); |
| |
| // boolean Boolean.logicalAnd(boolean a, boolean b) |
| name = factory.createString("logicalAnd"); |
| proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalAnd())); |
| |
| // boolean Boolean.logicalOr(boolean a, boolean b) |
| name = factory.createString("logicalOr"); |
| proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalOr())); |
| |
| // boolean Boolean.logicalXor(boolean a, boolean b) |
| name = factory.createString("logicalXor"); |
| proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalXor())); |
| |
| // Long |
| type = factory.boxedLongType; |
| |
| // int Long.hashCode(long i) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::LongMethods_hashCode)); |
| |
| // long Long.max(long a, long b) |
| name = factory.createString("max"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // long Long.min(long a, long b) |
| name = factory.createString("min"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath())); |
| |
| // long Long.sum(long a, long b) |
| name = factory.createString("sum"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction())); |
| |
| // Character |
| type = factory.boxedCharType; |
| |
| // int Character.hashCode(char i) |
| name = factory.createString("hashCode"); |
| proto = factory.createProto(factory.intType, factory.charType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity())); |
| |
| // Math & StrictMath, which have some symmetric, binary-compatible APIs |
| DexType[] mathTypes = {factory.mathType, factory.strictMathType}; |
| for (DexType mathType : mathTypes) { |
| |
| // int {Math,StrictMath}.addExact(int, int) |
| name = factory.createString("addExact"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::MathMethods_addExactInt, "addExactInt")); |
| |
| // long {Math,StrictMath}.addExact(long, long) |
| name = factory.createString("addExact"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_addExactLong, "addExactLong")); |
| |
| // int {Math,StrictMath}.floorDiv(int, int) |
| name = factory.createString("floorDiv"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::MathMethods_floorDivInt, "floorDivInt")); |
| |
| // long {Math,StrictMath}.floorDiv(long, long) |
| name = factory.createString("floorDiv"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_floorDivLong, "floorDivLong")); |
| |
| // int {Math,StrictMath}.floorMod(int, int) |
| name = factory.createString("floorMod"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::MathMethods_floorModInt, "floorModInt")); |
| |
| // long {Math,StrictMath}.floorMod(long, long) |
| name = factory.createString("floorMod"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_floorModLong, "floorModLong")); |
| |
| // int {Math,StrictMath}.multiplyExact(int, int) |
| name = factory.createString("multiplyExact"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_multiplyExactInt, "multiplyExactInt")); |
| |
| // long {Math,StrictMath}.multiplyExact(long, long) |
| name = factory.createString("multiplyExact"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_multiplyExactLong, "multiplyExactLong")); |
| |
| // double {Math,StrictMath}.nextDown(double) |
| name = factory.createString("nextDown"); |
| proto = factory.createProto(factory.doubleType, factory.doubleType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_nextDownDouble, "nextDownDouble")); |
| |
| // float {Math,StrictMath}.nextDown(float) |
| name = factory.createString("nextDown"); |
| proto = factory.createProto(factory.floatType, factory.floatType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_nextDownFloat, "nextDownFloat")); |
| |
| // int {Math,StrictMath}.subtractExact(int, int) |
| name = factory.createString("subtractExact"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_subtractExactInt, "subtractExactInt")); |
| |
| // long {Math,StrictMath}.subtractExact(long, long) |
| name = factory.createString("subtractExact"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_subtractExactLong, "subtractExactLong")); |
| |
| // int {Math,StrictMath}.toIntExact(long) |
| name = factory.createString("toIntExact"); |
| proto = factory.createProto(factory.intType, factory.longType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_toIntExact)); |
| } |
| |
| // Math (APIs which are not mirrored by StrictMath) |
| type = factory.mathType; |
| |
| // int Math.decrementExact(int) |
| name = factory.createString("decrementExact"); |
| proto = factory.createProto(factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_decrementExactInt, "decrementExactInt")); |
| |
| // long Math.decrementExact(long) |
| name = factory.createString("decrementExact"); |
| proto = factory.createProto(factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_decrementExactLong, "decrementExactLong")); |
| |
| // int Math.incrementExact(int) |
| name = factory.createString("incrementExact"); |
| proto = factory.createProto(factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_incrementExactInt, "incrementExactInt")); |
| |
| // long Math.incrementExact(long) |
| name = factory.createString("incrementExact"); |
| proto = factory.createProto(factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_incrementExactLong, "incrementExactLong")); |
| |
| // int Math.negateExact(int) |
| name = factory.createString("negateExact"); |
| proto = factory.createProto(factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_negateExactInt, "negateExactInt")); |
| |
| // long Math.negateExact(long) |
| name = factory.createString("negateExact"); |
| proto = factory.createProto(factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_negateExactLong, "negateExactLong")); |
| } |
| |
| private void initializeAndroidOMethodProviders(DexItemFactory factory) { |
| // Byte |
| DexType type = factory.boxedByteType; |
| |
| // int Byte.toUnsignedInt(byte value) |
| DexString name = factory.createString("toUnsignedInt"); |
| DexProto proto = factory.createProto(factory.intType, factory.byteType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ByteMethods_toUnsignedInt)); |
| |
| // long Byte.toUnsignedLong(byte value) |
| name = factory.createString("toUnsignedLong"); |
| proto = factory.createProto(factory.longType, factory.byteType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ByteMethods_toUnsignedLong)); |
| |
| // Short |
| type = factory.boxedShortType; |
| |
| // int Short.toUnsignedInt(short value) |
| name = factory.createString("toUnsignedInt"); |
| proto = factory.createProto(factory.intType, factory.shortType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ShortMethods_toUnsignedInt)); |
| |
| // long Short.toUnsignedLong(short value) |
| name = factory.createString("toUnsignedLong"); |
| proto = factory.createProto(factory.longType, factory.shortType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ShortMethods_toUnsignedLong)); |
| |
| // Integer |
| type = factory.boxedIntType; |
| |
| // int Integer.divideUnsigned(int a, int b) |
| name = factory.createString("divideUnsigned"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::IntegerMethods_divideUnsigned)); |
| |
| // int Integer.remainderUnsigned(int a, int b) |
| name = factory.createString("remainderUnsigned"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::IntegerMethods_remainderUnsigned)); |
| |
| // int Integer.compareUnsigned(int a, int b) |
| name = factory.createString("compareUnsigned"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::IntegerMethods_compareUnsigned)); |
| |
| // long Integer.toUnsignedLong(int value) |
| name = factory.createString("toUnsignedLong"); |
| proto = factory.createProto(factory.longType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::IntegerMethods_toUnsignedLong)); |
| |
| // int Integer.parseUnsignedInt(String value) |
| name = factory.createString("parseUnsignedInt"); |
| proto = factory.createProto(factory.intType, factory.stringType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::IntegerMethods_parseUnsignedInt)); |
| |
| // int Integer.parseUnsignedInt(String value, int radix) |
| name = factory.createString("parseUnsignedInt"); |
| proto = factory.createProto(factory.intType, factory.stringType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::IntegerMethods_parseUnsignedIntWithRadix, |
| "parseUnsignedIntWithRadix")); |
| |
| // String Integer.toUnsignedString(int value) |
| name = factory.createString("toUnsignedString"); |
| proto = factory.createProto(factory.stringType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::IntegerMethods_toUnsignedString)); |
| |
| // String Integer.toUnsignedString(int value, int radix) |
| name = factory.createString("toUnsignedString"); |
| proto = factory.createProto(factory.stringType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::IntegerMethods_toUnsignedStringWithRadix, |
| "toUnsignedStringWithRadix")); |
| |
| // Long |
| type = factory.boxedLongType; |
| |
| // long Long.divideUnsigned(long a, long b) |
| name = factory.createString("divideUnsigned"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::LongMethods_divideUnsigned)); |
| |
| // long Long.remainderUnsigned(long a, long b) |
| name = factory.createString("remainderUnsigned"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::LongMethods_remainderUnsigned)); |
| |
| // int Long.compareUnsigned(long a, long b) |
| name = factory.createString("compareUnsigned"); |
| proto = factory.createProto(factory.intType, factory.longType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::LongMethods_compareUnsigned)); |
| |
| // long Long.parseUnsignedLong(String value) |
| name = factory.createString("parseUnsignedLong"); |
| proto = factory.createProto(factory.longType, factory.stringType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::LongMethods_parseUnsignedLong)); |
| |
| // long Long.parseUnsignedLong(String value, int radix) |
| name = factory.createString("parseUnsignedLong"); |
| proto = factory.createProto(factory.longType, factory.stringType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::LongMethods_parseUnsignedLongWithRadix, |
| "parseUnsignedLongWithRadix")); |
| |
| // String Long.toUnsignedString(long value) |
| name = factory.createString("toUnsignedString"); |
| proto = factory.createProto(factory.stringType, factory.longType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::LongMethods_toUnsignedString)); |
| |
| // String Long.toUnsignedString(long value, int radix) |
| name = factory.createString("toUnsignedString"); |
| proto = factory.createProto(factory.stringType, factory.longType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::LongMethods_toUnsignedStringWithRadix, |
| "toUnsignedStringWithRadix")); |
| |
| // String |
| type = factory.stringType; |
| |
| // String String.join(CharSequence, CharSequence...) |
| name = factory.createString("join"); |
| proto = |
| factory.createProto( |
| factory.stringType, factory.charSequenceType, factory.charSequenceArrayType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::StringMethods_joinArray, "joinArray")); |
| |
| // String String.join(CharSequence, Iterable<? extends CharSequence>) |
| name = factory.createString("join"); |
| proto = |
| factory.createProto(factory.stringType, factory.charSequenceType, factory.iterableType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::StringMethods_joinIterable, "joinIterable")); |
| } |
| |
| private void initializeAndroidRObjectsMethodProviderWithSupplier(DexItemFactory factory) { |
| // Objects |
| DexType type = factory.objectsType; |
| |
| // T Objects.requireNonNullElseGet(T, Supplier<? extends T>) |
| DexString name = factory.createString("requireNonNullElseGet"); |
| DexProto proto = |
| factory.createProto(factory.objectType, factory.objectType, factory.supplierType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::ObjectsMethods_requireNonNullElseGet)); |
| } |
| |
| private void initializeAndroidRObjectsMethodProviders(DexItemFactory factory) { |
| DexType type; |
| DexString name; |
| DexProto proto; |
| DexMethod method; |
| |
| // Objects |
| type = factory.objectsType; |
| |
| // T Objects.requireNonNullElse(T, T) |
| name = factory.createString("requireNonNullElse"); |
| proto = factory.createProto(factory.objectType, factory.objectType, factory.objectType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::ObjectsMethods_requireNonNullElse)); |
| |
| // int Objects.checkIndex(int, int) |
| name = factory.createString("checkIndex"); |
| proto = factory.createProto(factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_checkIndex)); |
| |
| // int Objects.checkFromToIndex(int, int, int) |
| name = factory.createString("checkFromToIndex"); |
| proto = |
| factory.createProto(factory.intType, factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_checkFromToIndex)); |
| |
| // int Objects.checkFromIndexSize(int, int, int) |
| name = factory.createString("checkFromIndexSize"); |
| proto = |
| factory.createProto(factory.intType, factory.intType, factory.intType, factory.intType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::ObjectsMethods_checkFromIndexSize)); |
| } |
| |
| private void initializeAndroidRSetListMapMethodProviders(DexItemFactory factory) { |
| DexType type; |
| DexString name; |
| DexProto proto; |
| DexMethod method; |
| |
| // List<E> List.of(<args>) for 0 to 10 arguments and List.of(E[]) |
| type = factory.listType; |
| name = factory.createString("of"); |
| for (int i = 0; i <= 10; i++) { |
| final int formalCount = i; |
| proto = factory.createProto(type, Collections.nCopies(i, factory.objectType)); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| i == 0 |
| ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteListOfEmpty()) |
| : new MethodGenerator( |
| method, |
| (options, methodArg) -> |
| CollectionMethodGenerators.generateListOf( |
| options, methodArg, formalCount))); |
| } |
| proto = factory.createProto(type, factory.objectArrayType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::CollectionMethods_listOfArray, "ofArray")); |
| |
| // Set<E> Set.of(<args>) for 0 to 10 arguments and Set.of(E[]) |
| type = factory.setType; |
| name = factory.createString("of"); |
| for (int i = 0; i <= 10; i++) { |
| final int formalCount = i; |
| proto = factory.createProto(type, Collections.nCopies(i, factory.objectType)); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| i == 0 |
| ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteSetOfEmpty()) |
| : new MethodGenerator( |
| method, |
| (options, methodArg) -> |
| CollectionMethodGenerators.generateSetOf(options, methodArg, formalCount))); |
| } |
| proto = factory.createProto(type, factory.objectArrayType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::CollectionMethods_setOfArray, "ofArray")); |
| |
| // Map<K, V> Map.of(<K, V args>) for 0 to 10 pairs and Map.ofEntries(Map.Entry<K, V>[]) |
| type = factory.mapType; |
| name = factory.createString("of"); |
| for (int i = 0; i <= 10; i++) { |
| final int formalCount = i; |
| proto = factory.createProto(type, Collections.nCopies(i * 2, factory.objectType)); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| i == 0 |
| ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteMapOfEmpty()) |
| : new MethodGenerator( |
| method, |
| (ignore, methodArg) -> |
| CollectionMethodGenerators.generateMapOf(factory, methodArg, formalCount))); |
| } |
| proto = factory.createProto(type, factory.createArrayType(1, factory.mapEntryType)); |
| method = factory.createMethod(type, proto, "ofEntries"); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::CollectionMethods_mapOfEntries, "ofEntries")); |
| |
| // Map.Entry<K, V> Map.entry(K, V) |
| type = factory.mapType; |
| proto = factory.createProto(factory.mapEntryType, factory.objectType, factory.objectType); |
| method = factory.createMethod(type, proto, "entry"); |
| addProvider(new MethodGenerator(method, BackportedMethods::CollectionMethods_mapEntry)); |
| } |
| |
| private void initializeAndroidSSetListMapMethodProviders(DexItemFactory factory) { |
| DexType type; |
| DexString name; |
| DexProto proto; |
| DexMethod method; |
| |
| // List |
| type = factory.listType; |
| |
| // List List.copyOf(Collection) |
| name = factory.createString("copyOf"); |
| proto = factory.createProto(factory.listType, factory.collectionType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::CollectionsMethods_copyOfList, "copyOfList")); |
| |
| // Set |
| type = factory.setType; |
| |
| // Set Set.copyOf(Collection) |
| name = factory.createString("copyOf"); |
| proto = factory.createProto(factory.setType, factory.collectionType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::CollectionsMethods_copyOfSet, "copyOfSet")); |
| |
| // Map |
| type = factory.mapType; |
| |
| // Map Map.copyOf(Map) |
| name = factory.createString("copyOf"); |
| proto = factory.createProto(factory.mapType, factory.mapType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::CollectionsMethods_copyOfMap, "copyOfMap")); |
| } |
| |
| private void initializeAndroidSMethodProviders(DexItemFactory factory) { |
| DexType type; |
| DexString name; |
| DexProto proto; |
| DexMethod method; |
| |
| // Byte |
| type = factory.boxedByteType; |
| |
| // int Byte.compareUnsigned(byte, byte) |
| name = factory.createString("compareUnsigned"); |
| proto = factory.createProto(factory.intType, factory.byteType, factory.byteType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ByteMethods_compareUnsigned)); |
| |
| // Short |
| type = factory.boxedShortType; |
| |
| // int Short.compareUnsigned(short, short) |
| name = factory.createString("compareUnsigned"); |
| proto = factory.createProto(factory.intType, factory.shortType, factory.shortType); |
| method = factory.createMethod(type, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::ShortMethods_compareUnsigned)); |
| |
| // Math & StrictMath, which have some symmetric, binary-compatible APIs |
| DexType[] mathTypes = {factory.mathType, factory.strictMathType}; |
| for (DexType mathType : mathTypes) { |
| |
| // long {Math,StrictMath}.multiplyExact(long, int) |
| name = factory.createString("multiplyExact"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::MathMethods_multiplyExactLongInt, |
| "multiplyExactLongInt")); |
| |
| // long {Math,StrictMath}.multiplyFull(int, int) |
| name = factory.createString("multiplyFull"); |
| proto = factory.createProto(factory.longType, factory.intType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_multiplyFull)); |
| |
| // long {Math,StrictMath}.multiplyHigh(long, long) |
| name = factory.createString("multiplyHigh"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.longType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_multiplyHigh)); |
| |
| // long {Math,StrictMath}.floorDiv(long, int) |
| name = factory.createString("floorDiv"); |
| proto = factory.createProto(factory.longType, factory.longType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_floorDivLongInt, "floorDivLongInt")); |
| |
| // int {Math,StrictMath}.floorMod(long, int) |
| name = factory.createString("floorMod"); |
| proto = factory.createProto(factory.intType, factory.longType, factory.intType); |
| method = factory.createMethod(mathType, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::MathMethods_floorModLongInt, "floorModLongInt")); |
| } |
| |
| // android.util.SparseArray |
| |
| // void android.util.SparseArray.set(int, Object)) |
| addProvider( |
| new InvokeRewriter( |
| factory.androidUtilSparseArrayMembers.set, SparseArrayMethodRewrites.rewriteSet())); |
| } |
| |
| private void initializeAndroidSv2MethodProviders(DexItemFactory factory) { |
| // sun.misc.Unsafe |
| { |
| // compareAndSwapObject(Object receiver, long offset, Object expect, Object update) |
| DexType type = factory.unsafeType; |
| DexString name = factory.createString("compareAndSwapObject"); |
| DexProto proto = |
| factory.createProto( |
| factory.booleanType, |
| factory.objectType, |
| factory.longType, |
| factory.objectType, |
| factory.objectType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodWithForwardingGenerator( |
| method, |
| BackportedMethods::UnsafeMethods_compareAndSwapObject, |
| "compareAndSwapObject", |
| type)); |
| } |
| |
| // java.util.concurrent.atomic.AtomicReference |
| { |
| // compareAndSet(Object expect, Object update) |
| DexType type = factory.createType("Ljava/util/concurrent/atomic/AtomicReference;"); |
| DexString name = factory.createString("compareAndSet"); |
| DexProto proto = |
| factory.createProto(factory.booleanType, factory.objectType, factory.objectType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodWithForwardingGenerator( |
| method, |
| BackportedMethods::AtomicReferenceMethods_compareAndSet, |
| "compareAndSet", |
| type)); |
| } |
| |
| // java.util.concurrent.atomic.AtomicReferenceArray |
| { |
| // compareAndSet(int index, Object expect, Object update) |
| DexType type = factory.createType("Ljava/util/concurrent/atomic/AtomicReferenceArray;"); |
| DexString name = factory.createString("compareAndSet"); |
| DexProto proto = |
| factory.createProto( |
| factory.booleanType, factory.intType, factory.objectType, factory.objectType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodWithForwardingGenerator( |
| method, |
| BackportedMethods::AtomicReferenceArrayMethods_compareAndSet, |
| "compareAndSet", |
| type)); |
| } |
| |
| // java.util.concurrent.atomic.AtomicReferenceFieldUpdater |
| { |
| // compareAndSet(Object object, Object expect, Object update) |
| DexType type = |
| factory.createType("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"); |
| DexString name = factory.createString("compareAndSet"); |
| DexProto proto = |
| factory.createProto( |
| factory.booleanType, factory.objectType, factory.objectType, factory.objectType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodWithForwardingGenerator( |
| method, |
| BackportedMethods::AtomicReferenceFieldUpdaterMethods_compareAndSet, |
| "compareAndSet", |
| type)); |
| } |
| } |
| |
| private void initializeAndroidTMethodProviders(DexItemFactory factory) { |
| // java.lang.Integer. |
| { |
| // int Integer.parseInt(CharSequence s, int beginIndex, int endIndex, int radix) |
| DexType type = factory.boxedIntType; |
| DexString name = factory.createString("parseInt"); |
| DexProto proto = |
| factory.createProto( |
| factory.intType, |
| factory.charSequenceType, |
| factory.intType, |
| factory.intType, |
| factory.intType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| appView.options().canParseNumbersWithPlusPrefix() |
| ? new MethodGenerator( |
| method, |
| BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadix, |
| "parseIntSubsequenceWithRadix") |
| : new MethodGenerator( |
| method, |
| BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadixDalvik, |
| "parseIntSubsequenceWithRadix")); |
| } |
| { |
| // int Integer.parseUnsignedInt(CharSequence s, int beginIndex, int endIndex, int radix) |
| DexType type = factory.boxedIntType; |
| DexString name = factory.createString("parseUnsignedInt"); |
| DexProto proto = |
| factory.createProto( |
| factory.intType, |
| factory.charSequenceType, |
| factory.intType, |
| factory.intType, |
| factory.intType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::IntegerMethods_parseUnsignedIntSubsequenceWithRadix, |
| "parseIntSubsequenceWithRadix")); |
| } |
| |
| // java.lang.Long. |
| { |
| // long Long.parseLong(CharSequence s, int beginIndex, int endIndex, int radix) |
| DexType type = factory.boxedLongType; |
| DexString name = factory.createString("parseLong"); |
| DexProto proto = |
| factory.createProto( |
| factory.longType, |
| factory.charSequenceType, |
| factory.intType, |
| factory.intType, |
| factory.intType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| appView.options().canParseNumbersWithPlusPrefix() |
| ? new MethodGenerator( |
| method, |
| BackportedMethods::LongMethods_parseLongSubsequenceWithRadix, |
| "parseLongSubsequenceWithRadix") |
| : new MethodGenerator( |
| method, |
| BackportedMethods::LongMethods_parseLongSubsequenceWithRadixDalvik, |
| "parseLongSubsequenceWithRadix")); |
| } |
| { |
| // long Long.parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix) |
| DexType type = factory.boxedLongType; |
| DexString name = factory.createString("parseUnsignedLong"); |
| DexProto proto = |
| factory.createProto( |
| factory.longType, |
| factory.charSequenceType, |
| factory.intType, |
| factory.intType, |
| factory.intType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, |
| BackportedMethods::LongMethods_parseUnsignedLongSubsequenceWithRadix, |
| "parseUnsignedLongSubsequenceWithRadix")); |
| } |
| // java.lang.String. |
| { |
| // String String.repeat(int) |
| DexType type = factory.stringType; |
| DexString name = factory.createString("repeat"); |
| DexProto proto = factory.createProto(factory.stringType, factory.intType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, BackportedMethods::StringMethods_repeat, "repeat", type)); |
| } |
| { |
| // boolean String.isBlank() |
| DexType type = factory.stringType; |
| DexString name = factory.createString("isBlank"); |
| DexProto proto = factory.createProto(factory.booleanType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, BackportedMethods::StringMethods_isBlank, "isBlank", type)); |
| } |
| { |
| // String String.strip() |
| DexType type = factory.stringType; |
| DexString name = factory.createString("strip"); |
| DexProto proto = factory.createProto(factory.stringType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, BackportedMethods::StringMethods_strip, "strip", type)); |
| } |
| { |
| // String String.stripLeading() |
| DexType type = factory.stringType; |
| DexString name = factory.createString("stripLeading"); |
| DexProto proto = factory.createProto(factory.stringType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, BackportedMethods::StringMethods_stripLeading, "stripLeading", type)); |
| } |
| { |
| // String String.stripTrailing() |
| DexType type = factory.stringType; |
| DexString name = factory.createString("stripTrailing"); |
| DexProto proto = factory.createProto(factory.stringType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, BackportedMethods::StringMethods_stripTrailing, "stripTrailing", type)); |
| } |
| } |
| |
| private void initializeAndroidOptionalTMethodProviders(DexItemFactory factory) { |
| DexType optionalType = factory.optionalType; |
| DexType[] optionalTypes = |
| new DexType[] { |
| factory.optionalType, |
| factory.optionalDoubleType, |
| factory.optionalLongType, |
| factory.optionalIntType, |
| }; |
| |
| // or (added in Java 9). |
| { |
| DexString name = factory.createString("or"); |
| DexProto proto = factory.createProto(optionalType, factory.supplierType); |
| DexMethod method = factory.createMethod(optionalType, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, BackportedMethods::OptionalMethods_or, "or", optionalType)); |
| } |
| |
| // stream (added in Java 9). |
| { |
| DexType[] streamReturnTypes = |
| new DexType[] { |
| factory.streamType, |
| factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")), |
| factory.createType(factory.createString("Ljava/util/stream/LongStream;")), |
| factory.createType(factory.createString("Ljava/util/stream/IntStream;")), |
| }; |
| TemplateMethodFactory[] streamMethodFactories = |
| new TemplateMethodFactory[] { |
| BackportedMethods::OptionalMethods_stream, |
| BackportedMethods::OptionalMethods_streamDouble, |
| BackportedMethods::OptionalMethods_streamLong, |
| BackportedMethods::OptionalMethods_streamInt, |
| }; |
| DexString name = factory.createString("stream"); |
| for (int i = 0; i < optionalTypes.length; i++) { |
| DexType optional = optionalTypes[i]; |
| DexType streamReturnType = streamReturnTypes[i]; |
| DexProto proto = factory.createProto(streamReturnType); |
| DexMethod method = factory.createMethod(optional, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator(method, streamMethodFactories[i], "stream", optional)); |
| } |
| } |
| |
| // ifPresentOrElse (added in Java 9). |
| { |
| DexType[] consumerTypes = |
| new DexType[] { |
| factory.consumerType, |
| factory.doubleConsumer, |
| factory.longConsumer, |
| factory.intConsumer |
| }; |
| TemplateMethodFactory[] methodFactories = |
| new TemplateMethodFactory[] { |
| BackportedMethods::OptionalMethods_ifPresentOrElse, |
| BackportedMethods::OptionalMethods_ifPresentOrElseDouble, |
| BackportedMethods::OptionalMethods_ifPresentOrElseLong, |
| BackportedMethods::OptionalMethods_ifPresentOrElseInt |
| }; |
| for (int i = 0; i < optionalTypes.length; i++) { |
| DexType optional = optionalTypes[i]; |
| DexType consumer = consumerTypes[i]; |
| DexString name = factory.createString("ifPresentOrElse"); |
| DexProto proto = factory.createProto(factory.voidType, consumer, factory.runnableType); |
| DexMethod method = factory.createMethod(optional, proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, methodFactories[i], "ifPresentOrElse", optional)); |
| } |
| } |
| |
| // orElseThrow (added in Java 10). |
| { |
| DexType[] returnTypes = |
| new DexType[] { |
| factory.objectType, factory.doubleType, factory.longType, factory.intType, |
| }; |
| MethodInvokeRewriter[] rewriters = |
| new MethodInvokeRewriter[] { |
| OptionalMethodRewrites.rewriteOrElseGet(), |
| OptionalMethodRewrites.rewriteDoubleOrElseGet(), |
| OptionalMethodRewrites.rewriteLongOrElseGet(), |
| OptionalMethodRewrites.rewriteIntOrElseGet(), |
| }; |
| DexString name = factory.createString("orElseThrow"); |
| for (int i = 0; i < optionalTypes.length; i++) { |
| DexProto proto = factory.createProto(returnTypes[i]); |
| DexMethod method = factory.createMethod(optionalTypes[i], proto, name); |
| addProvider(new InvokeRewriter(method, rewriters[i])); |
| } |
| } |
| |
| // isEmpty (added in Java 11). |
| { |
| TemplateMethodFactory[] methodFactories = |
| new TemplateMethodFactory[] { |
| BackportedMethods::OptionalMethods_isEmpty, |
| BackportedMethods::OptionalMethods_isEmptyDouble, |
| BackportedMethods::OptionalMethods_isEmptyLong, |
| BackportedMethods::OptionalMethods_isEmptyInt |
| }; |
| DexString name = factory.createString("isEmpty"); |
| for (int i = 0; i < optionalTypes.length; i++) { |
| DexProto proto = factory.createProto(factory.booleanType); |
| DexMethod method = factory.createMethod(optionalTypes[i], proto, name); |
| addProvider( |
| new StatifyingMethodGenerator( |
| method, methodFactories[i], "isEmpty", optionalTypes[i])); |
| } |
| } |
| } |
| |
| private void initializeJava9MethodProviders(DexItemFactory factory) { |
| // Nothing right now. |
| } |
| |
| private void initializeJava10MethodProviders(DexItemFactory factory) { |
| // Nothing right now. |
| } |
| |
| private void initializeJava11MethodProviders(DexItemFactory factory) { |
| // Character |
| DexType type = factory.boxedCharType; |
| |
| // String Character.toString(int) |
| DexString name = factory.createString("toString"); |
| DexProto proto = factory.createProto(factory.stringType, factory.intType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator( |
| method, BackportedMethods::CharacterMethods_toStringCodepoint, "toStringCodepoint")); |
| |
| // CharSequence |
| type = factory.charSequenceType; |
| |
| // int CharSequence.compare(CharSequence, CharSequence) |
| name = factory.createString("compare"); |
| proto = |
| factory.createProto(factory.intType, factory.charSequenceType, factory.charSequenceType); |
| method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::CharSequenceMethods_compare, "compare")); |
| } |
| |
| private void initializeStreamMethodProviders(DexItemFactory factory) { |
| // Stream |
| DexType streamType = factory.streamType; |
| |
| // Stream.ofNullable(object) |
| DexString name = factory.createString("ofNullable"); |
| DexProto proto = factory.createProto(factory.streamType, factory.objectType); |
| DexMethod method = factory.createMethod(streamType, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::StreamMethods_ofNullable, "ofNullable")); |
| } |
| |
| private void initializePredicateMethodProviders(DexItemFactory factory) { |
| // Predicate |
| DexType predicateType = factory.predicateType; |
| |
| // Predicate.not(Predicate) |
| DexString name = factory.createString("not"); |
| DexProto proto = factory.createProto(predicateType, predicateType); |
| DexMethod method = factory.createMethod(predicateType, proto, name); |
| addProvider(new MethodGenerator(method, BackportedMethods::PredicateMethods_not, "not")); |
| } |
| |
| private void initializeAndroidNObjectsMethodProviderWithSupplier(DexItemFactory factory) { |
| // Objects |
| DexType type = factory.objectsType; |
| |
| // Objects.requireNonNull(Object, Supplier) |
| DexString name = factory.createString("requireNonNull"); |
| DexProto proto = |
| factory.createProto(factory.objectType, factory.objectType, factory.supplierType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider( |
| new MethodGenerator(method, BackportedMethods::ObjectsMethods_requireNonNullSupplier)); |
| } |
| |
| private void initializeAndroidOThreadLocalMethodProviderWithSupplier(DexItemFactory factory) { |
| // ThreadLocal |
| DexType type = factory.threadLocalType; |
| |
| // ThreadLocal.withInitial(Supplier) |
| DexString name = factory.createString("withInitial"); |
| DexProto proto = factory.createProto(type, factory.supplierType); |
| DexMethod method = factory.createMethod(type, proto, name); |
| addProvider(new ThreadLocalWithInitialWithSupplierGenerator(method)); |
| } |
| |
| private void addProvider(MethodProvider generator) { |
| MethodProvider replaced = rewritable.put(generator.method, generator); |
| assert replaced == null; |
| } |
| |
| MethodProvider getProvider(DexMethod method) { |
| return rewritable.get(method); |
| } |
| } |
| |
| public abstract static class MethodProvider { |
| |
| final DexMethod method; |
| |
| public MethodProvider(DexMethod method) { |
| this.method = method; |
| } |
| |
| public abstract Collection<CfInstruction> rewriteInvoke( |
| CfInvoke invoke, |
| AppView<?> appView, |
| BackportedMethodDesugaringEventConsumer eventConsumer, |
| MethodProcessingContext methodProcessingContext, |
| LocalStackAllocator localStackAllocator); |
| } |
| |
| private static final class InvokeRewriter extends MethodProvider { |
| |
| private final MethodInvokeRewriter rewriter; |
| |
| InvokeRewriter(DexMethod method, MethodInvokeRewriter rewriter) { |
| super(method); |
| this.rewriter = rewriter; |
| } |
| |
| @Override |
| public Collection<CfInstruction> rewriteInvoke( |
| CfInvoke invoke, |
| AppView<?> appView, |
| BackportedMethodDesugaringEventConsumer eventConsumer, |
| MethodProcessingContext methodProcessingContext, |
| LocalStackAllocator localStackAllocator) { |
| return rewriter.rewrite(invoke, appView.dexItemFactory(), localStackAllocator); |
| } |
| } |
| |
| private static class MethodGenerator extends MethodProvider { |
| |
| private final TemplateMethodFactory factory; |
| private final String methodName; |
| |
| MethodGenerator(DexMethod method, TemplateMethodFactory factory) { |
| this(method, factory, method.name.toString()); |
| } |
| |
| MethodGenerator(DexMethod method, TemplateMethodFactory factory, String methodName) { |
| super(method); |
| this.factory = factory; |
| this.methodName = methodName; |
| } |
| |
| protected SyntheticKind getSyntheticKind(SyntheticNaming naming) { |
| return naming.BACKPORT; |
| } |
| |
| @Override |
| public Collection<CfInstruction> rewriteInvoke( |
| CfInvoke invoke, |
| AppView<?> appView, |
| BackportedMethodDesugaringEventConsumer eventConsumer, |
| MethodProcessingContext methodProcessingContext, |
| LocalStackAllocator localStackAllocator) { |
| ProgramMethod method = getSyntheticMethod(appView, methodProcessingContext); |
| eventConsumer.acceptBackportedMethod(method, methodProcessingContext.getMethodContext()); |
| return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false)); |
| } |
| |
| private ProgramMethod getSyntheticMethod( |
| AppView<?> appView, MethodProcessingContext methodProcessingContext) { |
| return appView |
| .getSyntheticItems() |
| .createMethod( |
| this::getSyntheticKind, |
| methodProcessingContext.createUniqueContext(), |
| appView, |
| builder -> |
| builder |
| .disableAndroidApiLevelCheck() |
| .setProto(getProto(appView.dexItemFactory())) |
| .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic()) |
| .setCode( |
| methodSig -> |
| generateTemplateMethod(appView.dexItemFactory(), methodSig))); |
| } |
| |
| public DexProto getProto(DexItemFactory itemFactory) { |
| return method.proto; |
| } |
| |
| public Code generateTemplateMethod(DexItemFactory dexItemFactory, DexMethod method) { |
| return factory.create(dexItemFactory, method); |
| } |
| } |
| |
| // Specific subclass to transform virtual methods into static desugared methods. |
| // To be correct, the method has to be on a final class or be a final method, and to be |
| // implemented directly on the class (no overrides). |
| private static class StatifyingMethodGenerator extends MethodGenerator { |
| |
| private final DexType receiverType; |
| |
| StatifyingMethodGenerator( |
| DexMethod method, TemplateMethodFactory factory, String methodName, DexType receiverType) { |
| super(method, factory, methodName); |
| this.receiverType = receiverType; |
| } |
| |
| @Override |
| public DexProto getProto(DexItemFactory itemFactory) { |
| return itemFactory.prependTypeToProto(receiverType, super.getProto(itemFactory)); |
| } |
| } |
| |
| // Version of StatifyingMethodGenerator for backports which will call the method they backport. |
| // Such backports will not go through backporting again as that would cause infinite recursion. |
| private static class StatifyingMethodWithForwardingGenerator extends StatifyingMethodGenerator { |
| StatifyingMethodWithForwardingGenerator( |
| DexMethod method, TemplateMethodFactory factory, String methodName, DexType receiverType) { |
| super(method, factory, methodName, receiverType); |
| } |
| |
| @Override |
| protected SyntheticKind getSyntheticKind(SyntheticNaming naming) { |
| return naming.BACKPORT_WITH_FORWARDING; |
| } |
| } |
| |
| /* |
| Generate code for the SynthesizedThreadLocalSubClass class below, and rewrite |
| |
| ThreadLocal.withInitial(someSupplier); |
| |
| to |
| |
| new SynthesizedThreadLocalSubClass(someSupplier); |
| |
| class SynthesizedThreadLocalSubClass extends ThreadLocal<Object> { |
| private Supplier<Object> supplier; |
| |
| SynthesizedThreadLocalSubClass(Supplier<Object> supplier) { |
| this.supplier = supplier; |
| } |
| |
| protected Object initialValue() { |
| return supplier.get(); |
| } |
| } |
| */ |
| private static class ThreadLocalWithInitialWithSupplierGenerator extends MethodGenerator { |
| |
| ThreadLocalWithInitialWithSupplierGenerator(DexMethod method) { |
| super(method, null, method.name.toString()); |
| } |
| |
| @Override |
| protected SyntheticKind getSyntheticKind(SyntheticNaming naming) { |
| return naming.THREAD_LOCAL; |
| } |
| |
| @Override |
| public Collection<CfInstruction> rewriteInvoke( |
| CfInvoke invoke, |
| AppView<?> appView, |
| BackportedMethodDesugaringEventConsumer eventConsumer, |
| MethodProcessingContext methodProcessingContext, |
| LocalStackAllocator localStackAllocator) { |
| DexItemFactory factory = appView.dexItemFactory(); |
| DexProgramClass threadLocalSubclass = |
| appView |
| .getSyntheticItems() |
| .createClass( |
| kinds -> kinds.THREAD_LOCAL, |
| methodProcessingContext.createUniqueContext(), |
| appView, |
| builder -> new ThreadLocalSubclassGenerator(builder, appView)); |
| eventConsumer.acceptBackportedClass( |
| threadLocalSubclass, methodProcessingContext.getMethodContext()); |
| localStackAllocator.allocateLocalStack(2); |
| return ImmutableList.of( |
| new CfNew(threadLocalSubclass.type), |
| // Massage the stack with dup_x1 and swap: |
| // ..., SomeSupplier, ThreadLocalSubClass -> |
| // ..., ThreadLocalSubClass, ThreadLocalSubClass, SomeSupplier |
| new CfStackInstruction(Opcode.DupX1), |
| new CfStackInstruction(Opcode.Swap), |
| new CfInvoke( |
| Opcodes.INVOKESPECIAL, |
| factory.createMethod( |
| threadLocalSubclass.type, |
| factory.createProto(factory.voidType, factory.supplierType), |
| factory.constructorMethodName), |
| false)); |
| } |
| } |
| |
| private static class ThreadLocalSubclassGenerator { |
| private final DexItemFactory factory; |
| private final DexType type; |
| private final DexField supplierField; |
| private final DexMethod constructor; |
| private final DexMethod initialValueMethod; |
| |
| ThreadLocalSubclassGenerator(SyntheticProgramClassBuilder builder, AppView<?> appView) { |
| this.factory = appView.dexItemFactory(); |
| this.type = builder.getType(); |
| this.supplierField = |
| factory.createField( |
| builder.getType(), |
| factory.supplierType, |
| factory.createString("initialValueSupplier")); |
| this.constructor = |
| factory.createMethod( |
| type, |
| factory.createProto(factory.voidType, factory.supplierType), |
| factory.constructorMethodName); |
| this.initialValueMethod = |
| factory.createMethod( |
| type, factory.createProto(factory.objectType), factory.createString("initialValue")); |
| |
| builder.setSuperType(factory.threadLocalType); |
| synthesizeInstanceFields(builder); |
| synthesizeDirectMethods(builder); |
| synthesizeVirtualMethods(builder); |
| } |
| |
| private void synthesizeInstanceFields(SyntheticProgramClassBuilder builder) { |
| // Instance field: |
| // |
| // private Supplier<Object> supplier |
| builder.setInstanceFields( |
| ImmutableList.of( |
| DexEncodedField.syntheticBuilder() |
| .setField(supplierField) |
| .setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic()) |
| .disableAndroidApiLevelCheck() |
| .build())); |
| } |
| |
| private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) { |
| // Code for: |
| // |
| // SynthesizedThreadLocalSubClass(Supplier<Object> supplier) { |
| // this.supplier = supplier; |
| // } |
| List<CfInstruction> instructions = new ArrayList<>(); |
| instructions.add(new CfLoad(ValueType.OBJECT, 0)); |
| instructions.add(new CfStackInstruction(Opcode.Dup)); |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESPECIAL, |
| factory.createMethod( |
| factory.threadLocalType, |
| factory.createProto(factory.voidType), |
| factory.constructorMethodName), |
| false)); |
| instructions.add(new CfLoad(ValueType.fromDexType(factory.supplierType), 1)); |
| instructions.add(new CfInstanceFieldWrite(supplierField)); |
| instructions.add(new CfReturnVoid()); |
| |
| builder.setDirectMethods( |
| ImmutableList.of( |
| DexEncodedMethod.syntheticBuilder() |
| .setMethod(constructor) |
| .setAccessFlags( |
| MethodAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, true)) |
| .setCode(new CfCode(type, 2, 2, instructions)) |
| .disableAndroidApiLevelCheck() |
| .build())); |
| } |
| |
| private void synthesizeVirtualMethods(SyntheticProgramClassBuilder builder) { |
| // Code for: |
| // |
| // protected Object initialValue() { |
| // return supplier.get(); |
| // } |
| List<CfInstruction> instructions = new ArrayList<>(); |
| instructions.add(new CfLoad(ValueType.OBJECT, 0)); |
| instructions.add(new CfInstanceFieldRead(supplierField)); |
| instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, factory.supplierMembers.get, true)); |
| instructions.add(new CfReturn(ValueType.OBJECT)); |
| |
| builder.setVirtualMethods( |
| ImmutableList.of( |
| DexEncodedMethod.syntheticBuilder() |
| .setMethod(initialValueMethod) |
| .setAccessFlags( |
| MethodAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_PROTECTED | Constants.ACC_SYNTHETIC, false)) |
| .setCode(new CfCode(type, 1, 1, instructions)) |
| .disableAndroidApiLevelCheck() |
| .build())); |
| } |
| } |
| |
| private interface TemplateMethodFactory { |
| |
| CfCode create(DexItemFactory factory, DexMethod method); |
| } |
| |
| public interface MethodInvokeRewriter { |
| |
| CfInstruction rewriteSingle(CfInvoke invoke, DexItemFactory factory); |
| |
| // Convenience wrapper since most rewrites are to a single instruction. |
| default Collection<CfInstruction> rewrite( |
| CfInvoke invoke, DexItemFactory factory, LocalStackAllocator localStackAllocator) { |
| return ImmutableList.of(rewriteSingle(invoke, factory)); |
| } |
| } |
| |
| public abstract static class FullMethodInvokeRewriter implements MethodInvokeRewriter { |
| |
| @Override |
| public final CfInstruction rewriteSingle(CfInvoke invoke, DexItemFactory factory) { |
| throw new Unreachable(); |
| } |
| |
| @Override |
| public abstract Collection<CfInstruction> rewrite( |
| CfInvoke invoke, DexItemFactory factory, LocalStackAllocator localStackAllocator); |
| } |
| } |