blob: c56524e4550dd8d719e377e91af81e0608a606ef [file] [log] [blame]
// 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 static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
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.DexProgramClass.ChecksumSupplier;
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.DexTypeList;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
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.origin.SynthesizedOrigin;
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.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
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.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
public final class BackportedMethodRewriter {
// Don't change this name, at least not without adding special-casing in DexType to support
// merging old dex code in Bundletool.
public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$backportedMethods$utility";
private final AppView<?> appView;
private final IRConverter converter;
private final DexItemFactory factory;
private final RewritableMethods rewritableMethods;
private final boolean enabled;
private final Set<DexType> holders = Sets.newConcurrentHashSet();
private final Map<DexMethod, MethodProvider> methodProviders = new ConcurrentHashMap<>();
public BackportedMethodRewriter(AppView<?> appView, IRConverter converter) {
this.appView = appView;
this.converter = converter;
this.factory = appView.dexItemFactory();
this.rewritableMethods = new RewritableMethods(appView.options(), appView);
// Disable rewriting if there are no methods to rewrite or if the API level is higher than
// the highest known API level when the compiler is built. This ensures that when this is used
// by the Android Platform build (which normally use an API level of 10000) there will be
// no rewriting of backported methods. See b/147480264.
this.enabled =
(appView.options().desugarState == DesugarState.ON
|| appView.options().desugarState == DesugarState.ONLY_BACKPORT_STATICS)
&& !this.rewritableMethods.isEmpty()
&& appView.options().minApiLevel <= AndroidApiLevel.LATEST.getLevel();
}
public static List<DexMethod> generateListOfBackportedMethods(
AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
try {
List<DexMethod> methods = new ArrayList<>();
PrefixRewritingMapper rewritePrefix =
options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
AppInfo appInfo = null;
if (androidApp != null) {
DexApplication app =
new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
appInfo =
options.desugaredLibraryConfiguration.getRewritePrefix().isEmpty()
? new AppInfo(app)
: new AppInfoWithClassHierarchy(app);
}
AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
BackportedMethodRewriter.RewritableMethods rewritableMethods =
new BackportedMethodRewriter.RewritableMethods(options, appView);
rewritableMethods.visit(methods::add);
DesugaredLibraryRetargeter desugaredLibraryRetargeter =
new DesugaredLibraryRetargeter(appView);
desugaredLibraryRetargeter.visit(methods::add);
return methods;
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
}
}
public void desugar(IRCode code) {
if (!enabled) {
return; // Nothing to do!
}
Set<Value> affectedValues = Sets.newIdentityHashSet();
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
if (!instruction.isInvokeMethod()) {
continue;
}
InvokeMethod invoke = instruction.asInvokeMethod();
if (appView.options().desugarState == DesugarState.ONLY_BACKPORT_STATICS
&& !invoke.isInvokeStatic()) {
continue;
}
MethodProvider provider = getMethodProviderOrNull(invoke.getInvokedMethod());
if (provider == null) {
continue;
}
provider.rewriteInvoke(invoke, iterator, code, appView, affectedValues);
if (provider.requiresGenerationOfCode()) {
DexMethod newMethod = provider.provideMethod(appView);
methodProviders.putIfAbsent(newMethod, provider);
holders.add(code.method.method.holder);
}
}
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
}
}
private Collection<DexProgramClass> findSynthesizedFrom(Builder<?> builder, DexType holder) {
for (DexProgramClass synthesizedClass : builder.getSynthesizedClasses()) {
if (holder == synthesizedClass.getType()) {
return synthesizedClass.getSynthesizedFrom();
}
}
return null;
}
public static boolean hasRewrittenMethodPrefix(DexType clazz) {
return clazz.descriptor.toString().contains(UTILITY_CLASS_NAME_PREFIX);
}
public void synthesizeUtilityClasses(Builder<?> builder, ExecutorService executorService)
throws ExecutionException {
if (!enabled) {
return;
}
if (holders.isEmpty()) {
return;
}
// Compute referencing classes ignoring references in-between utility classes.
Set<DexProgramClass> referencingClasses = Sets.newConcurrentHashSet();
for (DexType holder : holders) {
DexClass definitionFor = appView.definitionFor(holder);
if (definitionFor == null) {
Collection<DexProgramClass> synthesizedFrom = findSynthesizedFrom(builder, holder);
assert synthesizedFrom != null;
referencingClasses.addAll(synthesizedFrom);
} else {
referencingClasses.add(definitionFor.asProgramClass());
}
}
MethodAccessFlags flags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
ClassAccessFlags classAccessFlags =
ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
// Generate the utility classes in a loop since utility classes can require the
// the creation of other utility classes.
// Function multiplyExact(long, int) calls multiplyExact(long, long) for example.
while (!methodProviders.isEmpty()) {
DexMethod method = methodProviders.keySet().iterator().next();
MethodProvider provider = methodProviders.remove(method);
assert provider.requiresGenerationOfCode();
// The utility class could have been synthesized, e.g., running R8 then D8,
// or if already processed in this while loop.
if (appView.definitionFor(method.holder) != null) {
continue;
}
Code code = provider.generateTemplateMethod(appView.options(), method);
DexEncodedMethod dexEncodedMethod =
new DexEncodedMethod(
method,
flags,
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
true);
boolean addToMainDexList =
referencingClasses.stream()
.anyMatch(clazz -> appView.appInfo().isInMainDexList(clazz.type));
DexProgramClass utilityClass =
synthesizeClassWithUniqueMethod(
builder,
classAccessFlags,
method.holder,
dexEncodedMethod,
"java8 methods utility class",
addToMainDexList,
appView);
// The following may add elements to methodsProviders.
converter.optimizeSynthesizedClass(utilityClass, executorService);
}
}
static DexProgramClass synthesizeClassWithUniqueMethod(
Builder<?> builder,
ClassAccessFlags accessFlags,
DexType type,
DexEncodedMethod uniqueMethod,
String origin,
boolean addToMainDexList,
AppView<?> appView) {
DexItemFactory factory = appView.dexItemFactory();
DexProgramClass newClass =
new DexProgramClass(
type,
null,
new SynthesizedOrigin(origin, BackportedMethodRewriter.class),
accessFlags,
factory.objectType,
DexTypeList.empty(),
null,
null,
Collections.emptyList(),
null,
Collections.emptyList(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
uniqueMethod.isStatic()
? new DexEncodedMethod[] {uniqueMethod}
: DexEncodedMethod.EMPTY_ARRAY,
uniqueMethod.isStatic()
? DexEncodedMethod.EMPTY_ARRAY
: new DexEncodedMethod[] {uniqueMethod},
factory.getSkipNameValidationForTesting(),
getChecksumSupplier(uniqueMethod, appView));
appView.appInfo().addSynthesizedClass(newClass);
builder.addSynthesizedClass(newClass, addToMainDexList);
return newClass;
}
private static ChecksumSupplier getChecksumSupplier(DexEncodedMethod method, AppView<?> appView) {
if (!appView.options().encodeChecksums) {
return DexProgramClass::invalidChecksumRequest;
}
return c -> method.method.hashCode();
}
private MethodProvider getMethodProviderOrNull(DexMethod method) {
DexMethod original = appView.graphLense().getOriginalMethodSignature(method);
assert original != null;
return rewritableMethods.getProvider(original);
}
private static final class RewritableMethods {
// Map backported method to a provider for creating the actual target method (with code).
private final Map<DexMethod, MethodProvider> rewritable = new IdentityHashMap<>();
RewritableMethods(InternalOptions options, AppView<?> appView) {
if (!options.shouldBackportMethods()) {
return;
}
DexItemFactory factory = options.itemFactory;
if (options.minApiLevel < AndroidApiLevel.K.getLevel()) {
initializeAndroidKMethodProviders(factory);
}
if (options.minApiLevel < AndroidApiLevel.N.getLevel()) {
initializeAndroidNMethodProviders(factory);
}
if (options.minApiLevel < AndroidApiLevel.O.getLevel()) {
initializeAndroidOMethodProviders(factory);
}
// The following providers are currently not implemented at any API level in Android.
// They however require the Optional/Stream class to be present, either through desugared
// libraries or natively. If Optional/Stream class is not present, we do not desugar to
// avoid confusion in error messages.
if (appView.rewritePrefix.hasRewrittenType(factory.optionalType, appView)
|| options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
initializeJava9OptionalMethodProviders(factory);
initializeJava10OptionalMethodProviders(factory);
initializeJava11OptionalMethodProviders(factory);
}
if (appView.rewritePrefix.hasRewrittenType(factory.streamType, appView)
|| options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
initializeStreamMethodProviders(factory);
}
// These are currently not implemented at any API level in Android.
initializeJava9MethodProviders(factory);
initializeJava10MethodProviders(factory);
initializeJava11MethodProviders(factory);
}
boolean isEmpty() {
return rewritable.isEmpty();
}
public void visit(Consumer<DexMethod> consumer) {
rewritable.keySet().forEach(consumer);
}
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));
// 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)
name = factory.createString("requireNonNull");
proto = factory.createProto(factory.objectType, factory.objectType, factory.stringType);
method = factory.createMethod(type, proto, name);
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"));
// 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 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));
// Objects
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));
// Math & StrictMath, which have some symmetric, binary-compatible APIs
DexType[] mathTypes = {factory.mathType, factory.strictMathType};
for (int i = 0; i < mathTypes.length; i++) {
type = mathTypes[i];
// int {Math,StrictMath}.addExact(int, int)
name = factory.createString("addExact");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
method = factory.createMethod(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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(type, 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 initializeJava9MethodProviders(DexItemFactory factory) {
// Math & StrictMath, which have some symmetric, binary-compatible APIs
DexType[] mathTypes = {factory.mathType, factory.strictMathType};
for (int i = 0; i < mathTypes.length; i++) {
DexType type = mathTypes[i];
// long {Math,StrictMath}.multiplyExact(long, int)
DexString name = factory.createString("multiplyExact");
DexProto proto = factory.createProto(factory.longType, factory.longType, factory.intType);
DexMethod method = factory.createMethod(type, 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(type, 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(type, 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(type, 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(type, proto, name);
addProvider(
new MethodGenerator(
method, BackportedMethods::MathMethods_floorModLongInt, "floorModLongInt"));
}
// Byte
DexType type = factory.boxedByteType;
// int Byte.compareUnsigned(byte, byte)
DexString name = factory.createString("compareUnsigned");
DexProto proto = factory.createProto(factory.intType, factory.byteType, factory.byteType);
DexMethod 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));
// 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));
// T Objects.requireNonNullElseGet(T, Supplier<? extends T>)
name = factory.createString("requireNonNullElseGet");
proto = factory.createProto(factory.objectType, factory.objectType, factory.supplierType);
method = factory.createMethod(type, proto, name);
addProvider(
new MethodGenerator(method, BackportedMethods::ObjectsMethods_requireNonNullElseGet));
// 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));
// 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,
(options, methodArg) ->
CollectionMethodGenerators.generateMapOf(options, 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 initializeJava10MethodProviders(DexItemFactory factory) {
// List
DexType type = factory.listType;
// List List.copyOf(Collection)
DexString name = factory.createString("copyOf");
DexProto proto = factory.createProto(factory.listType, factory.collectionType);
DexMethod 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"));
// Set
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 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"));
// String
type = factory.stringType;
// String String.repeat(int)
name = factory.createString("repeat");
proto = factory.createProto(factory.stringType, factory.intType);
method = factory.createMethod(type, proto, name);
addProvider(
new StatifyingMethodGenerator(
method, BackportedMethods::StringMethods_repeat, "repeat", type));
// boolean String.isBlank()
name = factory.createString("isBlank");
proto = factory.createProto(factory.booleanType);
method = factory.createMethod(type, proto, name);
addProvider(
new StatifyingMethodGenerator(
method, BackportedMethods::StringMethods_isBlank, "isBlank", type));
// String String.strip()
name = factory.createString("strip");
proto = factory.createProto(factory.stringType);
method = factory.createMethod(type, proto, name);
addProvider(
new StatifyingMethodGenerator(
method, BackportedMethods::StringMethods_strip, "strip", type));
// String String.stripLeading()
name = factory.createString("stripLeading");
proto = factory.createProto(factory.stringType);
method = factory.createMethod(type, proto, name);
addProvider(
new StatifyingMethodGenerator(
method, BackportedMethods::StringMethods_stripLeading, "stripLeading", type));
// String String.stripTrailing()
name = factory.createString("stripTrailing");
proto = factory.createProto(factory.stringType);
method = factory.createMethod(type, proto, name);
addProvider(
new StatifyingMethodGenerator(
method, BackportedMethods::StringMethods_stripTrailing, "stripTrailing", type));
}
private void initializeJava9OptionalMethodProviders(DexItemFactory factory) {
// Optional
DexType optionalType = factory.optionalType;
// Optional.or(supplier)
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));
// Optional{void,Int,Long,Double}.stream()
DexType[] optionalTypes =
new DexType[] {
optionalType,
factory.optionalDoubleType,
factory.optionalLongType,
factory.optionalIntType,
};
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,
};
name = factory.createString("stream");
for (int i = 0; i < optionalTypes.length; i++) {
DexType optional = optionalTypes[i];
DexType streamReturnType = streamReturnTypes[i];
proto = factory.createProto(streamReturnType);
method = factory.createMethod(optional, proto, name);
addProvider(
new StatifyingMethodGenerator(method, streamMethodFactories[i], "stream", optional));
}
// Optional{void,Int,Long,Double}.ifPresentOrElse(consumer,runnable)
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];
name = factory.createString("ifPresentOrElse");
proto = factory.createProto(factory.voidType, consumer, factory.runnableType);
method = factory.createMethod(optional, proto, name);
addProvider(
new StatifyingMethodGenerator(method, methodFactories[i], "ifPresentOrElse", optional));
}
}
private void initializeJava10OptionalMethodProviders(DexItemFactory factory) {
// Optional{void,Int,Long,Double}.orElseThrow()
DexType[] optionalTypes =
new DexType[] {
factory.optionalType,
factory.optionalDoubleType,
factory.optionalLongType,
factory.optionalIntType,
};
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]));
}
}
private void initializeJava11OptionalMethodProviders(DexItemFactory factory) {
// Optional{void,Int,Long,Double}.isEmpty()
DexType[] optionalTypes =
new DexType[] {
factory.optionalType,
factory.optionalDoubleType,
factory.optionalLongType,
factory.optionalIntType,
};
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++) {
DexType optionalType = optionalTypes[i];
DexProto proto = factory.createProto(factory.booleanType);
DexMethod method = factory.createMethod(optionalType, proto, name);
addProvider(
new StatifyingMethodGenerator(method, methodFactories[i], "isEmpty", optionalType));
}
}
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 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 void rewriteInvoke(
InvokeMethod invoke,
InstructionListIterator iterator,
IRCode code,
AppView<?> appView,
Set<Value> affectedValues);
public abstract DexMethod provideMethod(AppView<?> appView);
public abstract Code generateTemplateMethod(InternalOptions options, DexMethod method);
public abstract boolean requiresGenerationOfCode();
}
private static final class InvokeRewriter extends MethodProvider {
private final MethodInvokeRewriter rewriter;
InvokeRewriter(DexMethod method, MethodInvokeRewriter rewriter) {
super(method);
this.rewriter = rewriter;
}
@Override
public void rewriteInvoke(
InvokeMethod invoke,
InstructionListIterator iterator,
IRCode code,
AppView<?> appView,
Set<Value> affectedValues) {
rewriter.rewrite(invoke, iterator, appView.dexItemFactory(), affectedValues);
assert code.isConsistentSSA();
}
@Override
public boolean requiresGenerationOfCode() {
return false;
}
@Override
public DexMethod provideMethod(AppView<?> appView) {
throw new Unreachable();
}
@Override
public Code generateTemplateMethod(InternalOptions options, DexMethod method) {
throw new Unreachable();
}
}
private static class MethodGenerator extends MethodProvider {
private final TemplateMethodFactory factory;
private final String methodName;
DexMethod generatedMethod;
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;
}
@Override
public void rewriteInvoke(
InvokeMethod invoke,
InstructionListIterator iterator,
IRCode code,
AppView<?> appView,
Set<Value> affectedValues) {
iterator.replaceCurrentInstruction(
new InvokeStatic(provideMethod(appView), invoke.outValue(), invoke.inValues()));
}
@Override
public DexMethod provideMethod(AppView<?> appView) {
if (generatedMethod != null) {
return generatedMethod;
}
DexItemFactory factory = appView.dexItemFactory();
String unqualifiedName = method.holder.getName();
// Avoid duplicate class names between core lib dex file and program dex files.
String descriptor =
"L"
+ appView.options().synthesizedClassPrefix
+ UTILITY_CLASS_NAME_PREFIX
+ '$'
+ unqualifiedName
+ '$'
+ method.proto.parameters.size()
+ '$'
+ methodName
+ ';';
DexType type = factory.createType(descriptor);
generatedMethod = factory.createMethod(type, method.proto, method.name);
return generatedMethod;
}
@Override
public Code generateTemplateMethod(InternalOptions options, DexMethod method) {
return factory.create(options, method);
}
@Override
public boolean requiresGenerationOfCode() {
return true;
}
}
// 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 DexMethod provideMethod(AppView<?> appView) {
if (generatedMethod != null) {
return generatedMethod;
}
super.provideMethod(appView);
assert generatedMethod != null;
DexProto newProto = appView.dexItemFactory().prependTypeToProto(receiverType, method.proto);
generatedMethod =
appView.dexItemFactory().createMethod(generatedMethod.holder, newProto, method.name);
return generatedMethod;
}
}
private interface TemplateMethodFactory {
Code create(InternalOptions options, DexMethod method);
}
private interface MethodInvokeRewriter {
void rewrite(
InvokeMethod invoke,
InstructionListIterator iterator,
DexItemFactory factory,
Set<Value> affectedValues);
}
}