blob: 11a92d610364125b189d793d0698cda85fdec555 [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 com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
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.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.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.RewritableMethods.MethodGenerator;
import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public final class BackportedMethodRewriter {
public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$backportedMethods$utility";
private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L" + UTILITY_CLASS_NAME_PREFIX;
private final Set<DexType> holders = Sets.newConcurrentHashSet();
private final AppView<?> appView;
private final IRConverter converter;
private final DexItemFactory factory;
private final RewritableMethods rewritableMethods;
private Map<DexMethod, MethodGenerator> methodGenerators = new ConcurrentHashMap<>();
public BackportedMethodRewriter(AppView<?> appView, IRConverter converter) {
this.appView = appView;
this.converter = converter;
this.factory = appView.dexItemFactory();
this.rewritableMethods = new RewritableMethods(factory, appView.options());
}
public void desugar(IRCode code) {
if (rewritableMethods.isEmpty()) {
return; // Nothing to do!
}
InstructionIterator iterator = code.instructionIterator();
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
if (!instruction.isInvokeStatic()) {
continue;
}
InvokeStatic invoke = instruction.asInvokeStatic();
MethodGenerator generator = getMethodGeneratorOrNull(invoke.getInvokedMethod());
if (generator == null) {
continue;
}
iterator.replaceCurrentInstruction(
new InvokeStatic(generator.generateMethod(factory),
invoke.outValue(), invoke.inValues()));
methodGenerators.putIfAbsent(generator.generateMethod(factory), generator);
holders.add(code.method.method.holder);
}
}
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().startsWith(UTILITY_CLASS_DESCRIPTOR_PREFIX);
}
public void synthesizeUtilityClass(
Builder<?> builder, ExecutorService executorService, InternalOptions options)
throws ExecutionException {
if (holders.isEmpty()) {
return;
}
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);
for (MethodGenerator generator : methodGenerators.values()) {
DexMethod method = generator.generateMethod(factory);
// The utility class could have been synthesized, e.g., running R8 then D8.
if (appView.definitionFor(method.holder) != null) {
continue;
}
TemplateMethodCode code = generator.generateTemplateMethod(options, method);
DexEncodedMethod dexEncodedMethod= new DexEncodedMethod(method,
flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
DexProgramClass utilityClass =
new DexProgramClass(
method.holder,
null,
new SynthesizedOrigin("java8 methods utility class", getClass()),
classAccessFlags,
factory.objectType,
DexTypeList.empty(),
null,
null,
Collections.emptyList(),
null,
Collections.emptyList(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
new DexEncodedMethod[] {dexEncodedMethod},
DexEncodedMethod.EMPTY_ARRAY,
factory.getSkipNameValidationForTesting(),
referencingClasses);
code.setUpContext(utilityClass);
AppInfo appInfo = appView.appInfo();
boolean addToMainDexList =
referencingClasses.stream().anyMatch(clazz -> appInfo.isInMainDexList(clazz.type));
appInfo.addSynthesizedClass(utilityClass);
converter.optimizeSynthesizedClass(utilityClass, executorService);
builder.addSynthesizedClass(utilityClass, addToMainDexList);
}
}
private MethodGenerator getMethodGeneratorOrNull(DexMethod method) {
DexMethod original = appView.graphLense().getOriginalMethodSignature(method);
assert original != null;
return rewritableMethods.getGenerator(
original.holder.descriptor, original.name, original.proto);
}
private static final class ByteMethods extends TemplateMethodCode {
ByteMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(byte i) {
return Byte.valueOf(i).hashCode();
}
public static int compareImpl(byte a, byte b) {
return Byte.valueOf(a).compareTo(Byte.valueOf(b));
}
public static int toUnsignedIntImpl(byte value) {
return value & 0xff;
}
public static long toUnsignedLongImpl(byte value) {
return value & 0xffL;
}
}
private static final class ShortMethods extends TemplateMethodCode {
ShortMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(short i) {
return Short.valueOf(i).hashCode();
}
public static int compareImpl(short a, short b) {
return Short.valueOf(a).compareTo(Short.valueOf(b));
}
public static int toUnsignedIntImpl(short value) {
return value & 0xffff;
}
public static long toUnsignedLongImpl(short value) {
return value & 0xffffL;
}
}
private static final class IntegerMethods extends TemplateMethodCode {
IntegerMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(int i) {
return Integer.valueOf(i).hashCode();
}
public static int compareImpl(int a, int b) {
return Integer.valueOf(a).compareTo(Integer.valueOf(b));
}
public static int maxImpl(int a, int b) {
return java.lang.Math.max(a, b);
}
public static int minImpl(int a, int b) {
return java.lang.Math.min(a, b);
}
public static int sumImpl(int a, int b) {
return a + b;
}
public static int divideUnsignedImpl(int dividend, int divisor) {
long dividendLong = dividend & 0xffffffffL;
long divisorLong = divisor & 0xffffffffL;
return (int) (dividendLong / divisorLong);
}
public static int remainderUnsignedImpl(int dividend, int divisor) {
long dividendLong = dividend & 0xffffffffL;
long divisorLong = divisor & 0xffffffffL;
return (int) (dividendLong % divisorLong);
}
public static int compareUnsignedImpl(int a, int b) {
int aFlipped = a ^ Integer.MIN_VALUE;
int bFlipped = b ^ Integer.MIN_VALUE;
return Integer.compare(aFlipped, bFlipped);
}
public static long toUnsignedLongImpl(int value) {
return value & 0xffffffffL;
}
}
private static final class DoubleMethods extends TemplateMethodCode {
DoubleMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(double d) {
return Double.valueOf(d).hashCode();
}
public static double maxImpl(double a, double b) {
return java.lang.Math.max(a, b);
}
public static double minImpl(double a, double b) {
return java.lang.Math.min(a, b);
}
public static double sumImpl(double a, double b) {
return a + b;
}
public static boolean isFiniteImpl(double d) {
Double boxed = Double.valueOf(d);
return !boxed.isInfinite() && !boxed.isNaN();
}
}
private static final class FloatMethods extends TemplateMethodCode {
FloatMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(float d) {
return Float.valueOf(d).hashCode();
}
public static float maxImpl(float a, float b) {
return java.lang.Math.max(a, b);
}
public static float minImpl(float a, float b) {
return java.lang.Math.min(a, b);
}
public static float sumImpl(float a, float b) {
return a + b;
}
public static boolean isFiniteImpl(float d) {
Float boxed = Float.valueOf(d);
return !boxed.isInfinite() && !boxed.isNaN();
}
}
private static final class BooleanMethods extends TemplateMethodCode {
BooleanMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(boolean b) {
return Boolean.valueOf(b).hashCode();
}
public static int compareImpl(boolean a, boolean b) {
return Boolean.valueOf(a).compareTo(Boolean.valueOf(b));
}
public static boolean logicalAndImpl(boolean a, boolean b) {
return a && b;
}
public static boolean logicalOrImpl(boolean a, boolean b) {
return a || b;
}
public static boolean logicalXorImpl(boolean a, boolean b) {
return a ^ b;
}
}
private static final class LongMethods extends TemplateMethodCode {
LongMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(long i) {
return Long.valueOf(i).hashCode();
}
public static long maxImpl(long a, long b) {
return java.lang.Math.max(a, b);
}
public static long minImpl(long a, long b) {
return java.lang.Math.min(a, b);
}
public static long sumImpl(long a, long b) {
return a + b;
}
public static long divideUnsignedImpl(long dividend, long divisor) {
// This implementation is adapted from Guava's UnsignedLongs.java and Longs.java.
if (divisor < 0) { // i.e., divisor >= 2^63:
// Reference implementation calls UnsignedLongs.compare(dividend, divisor) whose
// implementation is Longs.compare(UnsignedLong.flip(a), UnsignedLong.flip(b)). The
// implementations of flip() and compare() are inlined here instead.
long dividendFlipped = dividend ^ Long.MIN_VALUE;
long divisorFlipped = divisor ^ Long.MIN_VALUE;
if (dividendFlipped < divisorFlipped) {
return 0; // dividend < divisor
} else {
return 1; // dividend >= divisor
}
}
// Optimization - use signed division if dividend < 2^63
if (dividend >= 0) {
return dividend / divisor;
}
// Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is
// guaranteed to be either exact or one less than the correct value. This follows from the
// fact that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is
// not quite trivial.
long quotient = ((dividend >>> 1) / divisor) << 1;
long rem = dividend - quotient * divisor;
// Reference implementation calls UnsignedLongs.compare(rem, divisor) whose
// implementation is Longs.compare(UnsignedLong.flip(a), UnsignedLong.flip(b)). The
// implementations of flip() and compare() are inlined here instead.
long remFlipped = rem ^ Long.MIN_VALUE;
long divisorFlipped = divisor ^ Long.MIN_VALUE;
return quotient + (remFlipped >= divisorFlipped ? 1 : 0);
}
public static long remainderUnsignedImpl(long dividend, long divisor) {
// This implementation is adapted from Guava's UnsignedLongs.java and Longs.java.
if (divisor < 0) { // i.e., divisor >= 2^63:
// Reference implementation calls UnsignedLongs.compare(dividend, divisor) whose
// implementation is Longs.compare(UnsignedLong.flip(a), UnsignedLong.flip(b)). The
// implementations of flip() and compare() are inlined here instead.
long dividendFlipped = dividend ^ Long.MIN_VALUE;
long divisorFlipped = divisor ^ Long.MIN_VALUE;
if (dividendFlipped < divisorFlipped) {
return dividend; // dividend < divisor
} else {
return dividend - divisor; // dividend >= divisor
}
}
// Optimization - use signed modulus if dividend < 2^63
if (dividend >= 0) {
return dividend % divisor;
}
// Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is
// guaranteed to be either exact or one less than the correct value. This follows from the
// fact that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is
// not quite trivial.
long quotient = ((dividend >>> 1) / divisor) << 1;
long rem = dividend - quotient * divisor;
// Reference implementation calls UnsignedLongs.compare(rem, divisor) whose
// implementation is Longs.compare(UnsignedLong.flip(a), UnsignedLong.flip(b)). The
// implementations of flip() and compare() are inlined here instead.
long remFlipped = rem ^ Long.MIN_VALUE;
long divisorFlipped = divisor ^ Long.MIN_VALUE;
return rem - (remFlipped >= divisorFlipped ? divisor : 0);
}
public static int compareUnsignedImpl(long a, long b) {
long aFlipped = a ^ Long.MIN_VALUE;
long bFlipped = b ^ Long.MIN_VALUE;
return Long.compare(aFlipped, bFlipped);
}
}
private static final class CharacterMethods extends TemplateMethodCode {
CharacterMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static int hashCodeImpl(char i) {
return Character.valueOf(i).hashCode();
}
public static int compareImpl(char a, char b) {
return Character.valueOf(a).compareTo(Character.valueOf(b));
}
}
private static final class ObjectsMethods extends TemplateMethodCode {
ObjectsMethods(InternalOptions options, DexMethod method, String methodName) {
super(options, method, methodName, method.proto.toDescriptorString());
}
public static <T> int compareImpl(T a, T b, Comparator<? super T> c) {
return a == b ? 0 : c.compare(a, b);
}
public static boolean deepEqualsImpl(Object a, Object b) {
if (a == b) return true;
if (a == null) return false;
if (a instanceof boolean[]) {
return b instanceof boolean[] && Arrays.equals((boolean[]) a, (boolean[]) b);
}
if (a instanceof byte[]) {
return b instanceof byte[] && Arrays.equals((byte[]) a, (byte[]) b);
}
if (a instanceof char[]) {
return b instanceof char[] && Arrays.equals((char[]) a, (char[]) b);
}
if (a instanceof double[]) {
return b instanceof double[] && Arrays.equals((double[]) a, (double[]) b);
}
if (a instanceof float[]) {
return b instanceof float[] && Arrays.equals((float[]) a, (float[]) b);
}
if (a instanceof int[]) {
return b instanceof int[] && Arrays.equals((int[]) a, (int[]) b);
}
if (a instanceof long[]) {
return b instanceof long[] && Arrays.equals((long[]) a, (long[]) b);
}
if (a instanceof short[]) {
return b instanceof short[] && Arrays.equals((short[]) a, (short[]) b);
}
if (a instanceof Object[]) {
return b instanceof Object[] && Arrays.deepEquals((Object[]) a, (Object[]) b);
}
return a.equals(b);
}
public static boolean equalsImpl(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
public static int hashImpl(Object[] o) {
return Arrays.hashCode(o);
}
public static int hashCodeImpl(Object o) {
return o == null ? 0 : o.hashCode();
}
public static boolean isNullImpl(Object o) {
return o == null;
}
public static boolean nonNullImpl(Object o) {
return o != null;
}
public static <T> T requireNonNullMessageImpl(T obj, String message) {
if (obj == null) {
throw new NullPointerException(message);
}
return obj;
}
public static String toStringImpl(Object o) {
return Objects.toString(o, "null");
}
public static String toStringDefaultImpl(Object o, String nullDefault) {
return o == null ? nullDefault : o.toString();
}
}
public static final class RewritableMethods {
// Map class, method, proto to a generator for creating the code and method.
private final Map<DexString, Map<DexString, Map<DexProto, MethodGenerator>>> rewritable =
new HashMap<>();
public RewritableMethods(DexItemFactory factory, InternalOptions options) {
if (!options.canUseJava7CompareAndObjectsOperations()) {
initializeJava7CompareOperations(factory);
}
if (!options.canUseJava8SignedOperations()) {
initializeJava8SignedOperations(factory);
}
if (!options.canUseJava8UnsignedOperations()) {
initializeJava8UnsignedOperations(factory);
}
}
boolean isEmpty() {
return rewritable.isEmpty();
}
private void initializeJava7CompareOperations(DexItemFactory factory) {
// Note: Long.compare rewriting is handled by CodeRewriter since there is a dedicated
// bytecode which supports the operation.
// Byte
DexString clazz = factory.boxedByteDescriptor;
// int Byte.compare(byte a, byte b)
DexString method = factory.createString("compare");
DexProto proto = factory.createProto(factory.intType, factory.byteType, factory.byteType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ByteMethods::new, "compareImpl", clazz, method, proto));
// Short
clazz = factory.boxedShortDescriptor;
// int Short.compare(short a, short b)
method = factory.createString("compare");
proto = factory.createProto(factory.intType, factory.shortType, factory.shortType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ShortMethods::new, "compareImpl", clazz, method, proto));
// Integer
clazz = factory.boxedIntDescriptor;
// int Integer.compare(int a, int b)
method = factory.createString("compare");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "compareImpl", clazz, method, proto));
// Boolean
clazz = factory.boxedBooleanDescriptor;
// int Boolean.compare(boolean a, boolean b)
method = factory.createString("compare");
proto = factory.createProto(factory.intType, factory.booleanType, factory.booleanType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(BooleanMethods::new, "compareImpl", clazz, method, proto));
// Character
clazz = factory.boxedCharDescriptor;
// int Character.compare(char a, char b)
method = factory.createString("compare");
proto = factory.createProto(factory.intType, factory.charType, factory.charType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(CharacterMethods::new, "compareImpl", clazz, method, proto));
// Objects
clazz = factory.objectsDescriptor;
// int Objects.compare(T a, T b, Comparator<? super T> c)
method = factory.createString("compare");
proto = factory.createProto(factory.intType, factory.objectType, factory.objectType,
factory.comparatorType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "compareImpl", clazz, method, proto));
// boolean Objects.deepEquals(Object a, Object b)
method = factory.createString("deepEquals");
proto = factory.createProto(factory.booleanType, factory.objectType, factory.objectType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "deepEqualsImpl", clazz, method, proto));
// boolean Objects.equals(Object a, Object b)
method = factory.createString("equals");
proto = factory.createProto(factory.booleanType, factory.objectType, factory.objectType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "equalsImpl", clazz, method, proto));
// int Objects.hash(Object... o)
method = factory.createString("hash");
proto = factory.createProto(factory.intType, factory.objectArrayType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "hashImpl", clazz, method, proto));
// int Objects.hashCode(Object o)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.objectType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "hashCodeImpl", clazz, method, proto));
// Note: Objects.requireNonNull(T) rewriting is handled by CodeRewriter for now.
// T Objects.requireNonNull(T obj, String message)
method = factory.createString("requireNonNull");
proto = factory.createProto(factory.objectType, factory.objectType, factory.stringType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "requireNonNullMessageImpl", clazz, method,
proto));
// String Objects.toString(Object o)
method = factory.createString("toString");
proto = factory.createProto(factory.stringType, factory.objectType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "toStringImpl", clazz, method, proto));
// String Objects.toString(Object o, String nullDefault);
method = factory.createString("toString");
proto = factory.createProto(factory.stringType, factory.objectType, factory.stringType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "toStringDefaultImpl", clazz, method, proto));
}
private void initializeJava8SignedOperations(DexItemFactory factory) {
// Byte
DexString clazz = factory.boxedByteDescriptor;
// int Byte.hashCode(byte i)
DexString method = factory.createString("hashCode");
DexProto proto = factory.createProto(factory.intType, factory.byteType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ByteMethods::new, "hashCodeImpl", clazz, method, proto));
// Short
clazz = factory.boxedShortDescriptor;
// int Short.hashCode(short i)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.shortType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ShortMethods::new, "hashCodeImpl", clazz, method, proto));
// Integer
clazz = factory.boxedIntDescriptor;
// int Integer.hashCode(int i)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "hashCodeImpl", clazz, method, proto));
// int Integer.max(int a, int b)
method = factory.createString("max");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "maxImpl", clazz, method, proto));
// int Integer.min(int a, int b)
method = factory.createString("min");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "minImpl", clazz, method, proto));
// int Integer.sum(int a, int b)
method = factory.createString("sum");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "sumImpl", clazz, method, proto));
// Double
clazz = factory.boxedDoubleDescriptor;
// int Double.hashCode(double d)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.doubleType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(DoubleMethods::new, "hashCodeImpl", clazz, method, proto));
// double Double.max(double a, double b)
method = factory.createString("max");
proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(DoubleMethods::new, "maxImpl", clazz, method, proto));
// double Double.min(double a, double b)
method = factory.createString("min");
proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(DoubleMethods::new, "minImpl", clazz, method, proto));
// double Double.sum(double a, double b)
method = factory.createString("sum");
proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(DoubleMethods::new, "sumImpl", clazz, method, proto));
// boolean Double.isFinite(double a)
method = factory.createString("isFinite");
proto = factory.createProto(factory.booleanType, factory.doubleType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(DoubleMethods::new, "isFiniteImpl", clazz, method, proto));
// Float
clazz = factory.boxedFloatDescriptor;
// int Float.hashCode(float d)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.floatType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(FloatMethods::new, "hashCodeImpl", clazz, method, proto));
// float Float.max(float a, float b)
method = factory.createString("max");
proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(FloatMethods::new, "maxImpl", clazz, method, proto));
// float Float.min(float a, float b)
method = factory.createString("min");
proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(FloatMethods::new, "minImpl", clazz, method, proto));
// float Float.sum(float a, float b)
method = factory.createString("sum");
proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(FloatMethods::new, "sumImpl", clazz, method, proto));
// boolean Float.isFinite(float a)
method = factory.createString("isFinite");
proto = factory.createProto(factory.booleanType, factory.floatType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(FloatMethods::new, "isFiniteImpl", clazz, method, proto));
// Boolean
clazz = factory.boxedBooleanDescriptor;
// int Boolean.hashCode(boolean b)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.booleanType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(BooleanMethods::new, "hashCodeImpl", clazz, method, proto));
// boolean Boolean.logicalAnd(boolean a, boolean b)
method = factory.createString("logicalAnd");
proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(BooleanMethods::new, "logicalAndImpl", clazz, method, proto));
// boolean Boolean.logicalOr(boolean a, boolean b)
method = factory.createString("logicalOr");
proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(BooleanMethods::new, "logicalOrImpl", clazz, method, proto));
// boolean Boolean.logicalXor(boolean a, boolean b)
method = factory.createString("logicalXor");
proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(BooleanMethods::new, "logicalXorImpl", clazz, method, proto));
// Long
clazz = factory.boxedLongDescriptor;
// int Long.hashCode(long i)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.longType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(LongMethods::new, "hashCodeImpl", clazz, method, proto));
// long Long.max(long a, long b)
method = factory.createString("max");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(LongMethods::new, "maxImpl", clazz, method, proto));
// long Long.min(long a, long b)
method = factory.createString("min");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(LongMethods::new, "minImpl", clazz, method, proto));
// long Long.sum(long a, long b)
method = factory.createString("sum");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(LongMethods::new, "sumImpl", clazz, method, proto));
// Character
clazz = factory.boxedCharDescriptor;
// int Character.hashCode(char i)
method = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.charType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(CharacterMethods::new, "hashCodeImpl", clazz, method, proto));
// Objects
clazz = factory.objectsDescriptor;
// boolean Objects.isNull(Object o)
method = factory.createString("isNull");
proto = factory.createProto(factory.booleanType, factory.objectType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "isNullImpl", clazz, method, proto));
// boolean Objects.nonNull(Object a)
method = factory.createString("nonNull");
proto = factory.createProto(factory.booleanType, factory.objectType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ObjectsMethods::new, "nonNullImpl", clazz, method, proto));
}
private void initializeJava8UnsignedOperations(DexItemFactory factory) {
// Byte
DexString clazz = factory.boxedByteDescriptor;
// int Byte.toUnsignedInt(byte value)
DexString method = factory.createString("toUnsignedInt");
DexProto proto = factory.createProto(factory.intType, factory.byteType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ByteMethods::new, "toUnsignedIntImpl", clazz, method, proto));
// long Byte.toUnsignedLong(byte value)
method = factory.createString("toUnsignedLong");
proto = factory.createProto(factory.longType, factory.byteType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ByteMethods::new, "toUnsignedLongImpl", clazz, method, proto));
// Short
clazz = factory.boxedShortDescriptor;
// int Short.toUnsignedInt(short value)
method = factory.createString("toUnsignedInt");
proto = factory.createProto(factory.intType, factory.shortType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ShortMethods::new, "toUnsignedIntImpl", clazz, method, proto));
// long Short.toUnsignedLong(short value)
method = factory.createString("toUnsignedLong");
proto = factory.createProto(factory.longType, factory.shortType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(ShortMethods::new, "toUnsignedLongImpl", clazz, method, proto));
// Integer
clazz = factory.boxedIntDescriptor;
// int Integer.divideUnsigned(int a, int b)
method = factory.createString("divideUnsigned");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "divideUnsignedImpl", clazz, method, proto));
// int Integer.remainderUnsigned(int a, int b)
method = factory.createString("remainderUnsigned");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "remainderUnsignedImpl", clazz, method, proto));
// int Integer.compareUnsigned(int a, int b)
method = factory.createString("compareUnsigned");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "compareUnsignedImpl", clazz, method, proto));
// long Integer.toUnsignedLong(int value)
method = factory.createString("toUnsignedLong");
proto = factory.createProto(factory.longType, factory.intType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(IntegerMethods::new, "toUnsignedLongImpl", clazz, method, proto));
// Long
clazz = factory.boxedLongDescriptor;
// long Long.divideUnsigned(long a, long b)
method = factory.createString("divideUnsigned");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(LongMethods::new, "divideUnsignedImpl", clazz, method, proto));
// long Long.remainderUnsigned(long a, long b)
method = factory.createString("remainderUnsigned");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(LongMethods::new, "remainderUnsignedImpl", clazz, method, proto));
// int Long.compareUnsigned(long a, long b)
method = factory.createString("compareUnsigned");
proto = factory.createProto(factory.intType, factory.longType, factory.longType);
addOrGetMethod(clazz, method).put(proto,
new MethodGenerator(LongMethods::new, "compareUnsignedImpl", clazz, method, proto));
}
private Map<DexString, Map<DexProto, MethodGenerator>> addOrGetClass(DexString clazz) {
return rewritable.computeIfAbsent(clazz, k -> new HashMap<>());
}
private Map<DexProto, MethodGenerator> addOrGetMethod(
DexString clazz, DexString method) {
return addOrGetClass(clazz).computeIfAbsent(method, k -> new HashMap<>());
}
public MethodGenerator getGenerator(DexString clazz, DexString method, DexProto proto) {
Map<DexString, Map<DexProto, MethodGenerator>> classMap = rewritable.get(clazz);
if (classMap != null) {
Map<DexProto, MethodGenerator> methodMap = classMap.get(method);
if (methodMap != null) {
return methodMap.get(proto);
}
}
return null;
}
public static class MethodGenerator {
private final TemplateMethodFactory factory;
private final String methodName;
private final DexString clazz;
private final DexString method;
private final DexProto proto;
private DexMethod dexMethod;
public MethodGenerator(TemplateMethodFactory factory,
String methodName, DexString clazz, DexString method, DexProto proto) {
this.factory = factory;
this.methodName = methodName;
this.clazz = clazz;
this.method = method;
this.proto = proto;
}
public DexMethod generateMethod(DexItemFactory factory) {
if (dexMethod != null) {
return dexMethod;
}
String unqualifiedName =
DescriptorUtils.getUnqualifiedClassNameFromDescriptor(clazz.toString());
String descriptor =
UTILITY_CLASS_DESCRIPTOR_PREFIX + '$' + unqualifiedName + '$' + methodName + ';';
DexType clazz = factory.createType(descriptor);
dexMethod = factory.createMethod(clazz, proto, method);
return dexMethod;
}
public TemplateMethodCode generateTemplateMethod(InternalOptions options, DexMethod method) {
return factory.create(options, method, methodName);
}
}
private interface TemplateMethodFactory {
TemplateMethodCode create(InternalOptions options, DexMethod method, String name);
}
}
}