blob: 140d8d545e67af4f1fac6f224d8a4c2de2f2559b [file]
// Copyright (c) 2020, 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.synthesis;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.PRIVATE_METHOD_PREFIX;
import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_GET_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_PUT_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_METHOD_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_METHOD_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.synthesis.SyntheticNaming.Phase;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.hamcrest.Matcher;
public abstract class SyntheticItemsTestUtils {
// Private copy of the synthetic namings. This is not the compiler instance, but checking on the
// id/descriptor content is safe.
private static final SyntheticNaming naming = new SyntheticNaming();
public static String syntheticFileNameD8() {
return "D8$$SyntheticClass";
}
public static String syntheticMethodName() {
return SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_NAME;
}
public static ClassReference syntheticCompanionClass(Class<?> clazz) {
return syntheticCompanionClass(Reference.classFromClass(clazz));
}
public static ClassReference syntheticCompanionClass(ClassReference clazz) {
return Reference.classFromDescriptor(
InterfaceDesugaringForTesting.getCompanionClassDescriptor(clazz.getDescriptor()));
}
public static MethodReference syntheticInvokeSpecialMethod(Method method) {
MethodReference originalMethod = Reference.methodFromMethod(method);
return Reference.method(
originalMethod.getHolderClass(),
InvokeSpecialToSelfDesugaring.INVOKE_SPECIAL_BRIDGE_PREFIX + method.getName(),
originalMethod.getFormalTypes(),
originalMethod.getReturnType());
}
public static boolean isLambdaMethodAnnotationDescriptor(String descriptor) {
return descriptor.equals(DexItemFactory.lambdaMethodAnnotationDescriptor);
}
public final MethodReference syntheticBackportMethod(Class<?> clazz, int id, Method method) {
return syntheticBackportMethod(Reference.classFromClass(clazz), id, method);
}
public final MethodReference syntheticBackportMethod(
ClassReference classReference, int id, Method method) {
MethodReference originalMethod = Reference.methodFromMethod(method);
return syntheticBackportMethod(classReference, originalMethod, id);
}
public final MethodReference syntheticBackportWithForwardingMethod(
ClassReference clazz, int id, MethodReference method) {
// For backports with forwarding the backported method is not static, so the original method
// signature has the receiver type pre-pended.
ImmutableList.Builder<TypeReference> builder = ImmutableList.builder();
builder.add(method.getHolderClass()).addAll(method.getFormalTypes());
MethodReference methodWithReceiverForForwarding =
Reference.method(
method.getHolderClass(),
method.getMethodName(),
builder.build(),
method.getReturnType());
ClassReference syntheticBackportClass = syntheticBackportWithForwardingClass(clazz, id);
if (syntheticBackportClass != null) {
return Reference.methodFromDescriptor(
syntheticBackportClass,
syntheticMethodName(),
methodWithReceiverForForwarding.getMethodDescriptor());
}
return null;
}
public final ClassReference syntheticBottomUpOutlineClass(Class<?> clazz, int id) {
return syntheticBottomUpOutlineClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticBottomUpOutlineClass(ClassReference clazz, int id);
public final ClassReference syntheticOutlineClass(Class<?> clazz, int id) {
return syntheticOutlineClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticOutlineClass(ClassReference clazz, int id);
public final ClassReference syntheticLambdaClass(Class<?> clazz, int id) {
return syntheticLambdaClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticLambdaClass(ClassReference clazz, int id);
public final ClassReference syntheticApiConversionClass(Class<?> clazz, int id) {
return syntheticApiConversionClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticApiConversionClass(ClassReference classReference, int id);
public final ClassReference syntheticApiOutlineClass(Class<?> clazz, int id) {
return syntheticApiOutlineClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticApiOutlineClass(ClassReference classReference, int id);
public final ClassReference syntheticBackportClass(Class<?> clazz, int id) {
return syntheticBackportClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticBackportClass(ClassReference classReference, int id);
public abstract MethodReference syntheticBackportMethod(
ClassReference classReference, MethodReference originalMethod, int id);
public final ClassReference syntheticBackportWithForwardingClass(Class<?> clazz, int id) {
return syntheticBackportWithForwardingClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticBackportWithForwardingClass(
ClassReference classReference, int id);
public static ClassReference syntheticRecordTagClass() {
return Reference.classFromDescriptor(DexItemFactory.recordTagDescriptorString);
}
public abstract ClassReference syntheticRecordHelperClass(ClassReference reference, int id);
public abstract ClassReference syntheticTwrCloseResourceClass(ClassReference reference, int id);
public abstract ClassReference syntheticAutoCloseableDispatcherClass(
ClassReference classReference, int id);
public abstract ClassReference syntheticAutoCloseableForwarderClass(
ClassReference classReference, int id);
public abstract ClassReference syntheticThrowIAEClass(ClassReference classReference, int id);
public final MethodReference syntheticLambdaMethod(Class<?> clazz, int id, Method method) {
ClassReference syntheticHolder = syntheticLambdaClass(clazz, id);
MethodReference originalMethod = Reference.methodFromMethod(method);
return Reference.methodFromDescriptor(
syntheticHolder.getDescriptor(),
originalMethod.getMethodName(),
originalMethod.getMethodDescriptor());
}
public static ClassReference syntheticNestConstructorArgumentClass(
ClassReference classReference) {
return Reference.classFromDescriptor(
SyntheticNaming.createDescriptor(
"", naming.INIT_TYPE_ARGUMENT, classReference.getBinaryName(), ""));
}
public static MethodReference syntheticNestInstanceFieldGetter(Field field) {
return syntheticNestInstanceFieldGetter(Reference.fieldFromField(field));
}
public static MethodReference syntheticNestInstanceFieldGetter(FieldReference fieldReference) {
return Reference.method(
fieldReference.getHolderClass(),
NEST_ACCESS_FIELD_GET_NAME_PREFIX + fieldReference.getFieldName(),
Collections.emptyList(),
fieldReference.getFieldType());
}
public static MethodReference syntheticNestInstanceFieldSetter(Field field) {
FieldReference fieldReference = Reference.fieldFromField(field);
return Reference.method(
fieldReference.getHolderClass(),
NEST_ACCESS_FIELD_PUT_NAME_PREFIX + field.getName(),
ImmutableList.of(fieldReference.getFieldType()),
null);
}
public static MethodReference syntheticNestInstanceMethodAccessor(Method method) {
MethodReference originalMethod = Reference.methodFromMethod(method);
return Reference.methodFromDescriptor(
originalMethod.getHolderClass(),
NEST_ACCESS_METHOD_NAME_PREFIX + method.getName(),
originalMethod.getMethodDescriptor());
}
public static MethodReference syntheticNestStaticFieldGetter(Field field) {
FieldReference fieldReference = Reference.fieldFromField(field);
return Reference.method(
fieldReference.getHolderClass(),
NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX + field.getName(),
Collections.emptyList(),
fieldReference.getFieldType());
}
public static MethodReference syntheticNestStaticFieldSetter(Field field) {
FieldReference fieldReference = Reference.fieldFromField(field);
return Reference.method(
fieldReference.getHolderClass(),
NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX + field.getName(),
ImmutableList.of(fieldReference.getFieldType()),
null);
}
public static MethodReference syntheticNestStaticMethodAccessor(Method method) {
MethodReference originalMethod = Reference.methodFromMethod(method);
return Reference.methodFromDescriptor(
originalMethod.getHolderClass(),
NEST_ACCESS_STATIC_METHOD_NAME_PREFIX + method.getName(),
originalMethod.getMethodDescriptor());
}
public final ClassReference syntheticNonStartupInStartupOutlineClass(Class<?> clazz, int id) {
return syntheticNonStartupInStartupOutlineClass(Reference.classFromClass(clazz), id);
}
public abstract ClassReference syntheticNonStartupInStartupOutlineClass(
ClassReference reference, int id);
public static MethodReference syntheticPrivateInterfaceMethodAsCompanionMethod(Method method) {
MethodReference originalMethod = Reference.methodFromMethod(method);
ClassReference companionClassReference =
syntheticCompanionClass(originalMethod.getHolderClass());
return Reference.methodFromDescriptor(
companionClassReference,
PRIVATE_METHOD_PREFIX + method.getName(),
originalMethod.getMethodDescriptor());
}
public static MethodReference syntheticStaticInterfaceMethodAsCompanionMethod(Method method) {
MethodReference originalMethod = Reference.methodFromMethod(method);
ClassReference companionClassReference =
syntheticCompanionClass(originalMethod.getHolderClass());
return Reference.methodFromDescriptor(
companionClassReference, method.getName(), originalMethod.getMethodDescriptor());
}
public static ClassReference syntheticEnumUnboxingLocalUtilityClass(Class<?> clazz) {
return Reference.classFromTypeName(
clazz.getTypeName() + naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS.getDescriptor());
}
public static ClassReference syntheticEnumUnboxingSharedUtilityClass(Class<?> clazz) {
return Reference.classFromTypeName(
clazz.getTypeName() + naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS.getDescriptor());
}
public static boolean isEnumUnboxingSharedUtilityClass(ClassReference reference) {
return SyntheticNaming.isSynthetic(reference, null, naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS);
}
public static boolean isInternalLambda(ClassReference reference) {
return SyntheticNaming.isSynthetic(reference, Phase.INTERNAL, naming.LAMBDA);
}
public abstract boolean isExternalLambda(ClassReference reference);
public abstract boolean isExternalStaticInterfaceCall(ClassReference reference);
public abstract boolean isExternalTwrCloseMethod(ClassReference reference);
public final boolean isMaybeExternalSuppressedExceptionMethod(ClassReference reference) {
// The suppressed exception methods are grouped with the backports.
return isExternalBackportClass(reference);
}
public abstract boolean isExternalBackportClass(ClassReference reference);
public abstract boolean isExternalOutlineClass(ClassReference reference);
public abstract boolean isExternalApiOutlineClass(ClassReference reference);
public static boolean isInitializerTypeArgument(ClassReference reference) {
return SyntheticNaming.isSynthetic(reference, null, naming.INIT_TYPE_ARGUMENT);
}
public abstract boolean isExternalNonFixedInitializerTypeArgument(ClassReference reference);
public static boolean isWrapper(ClassReference reference) {
return SyntheticNaming.isSynthetic(reference, null, naming.WRAPPER)
|| SyntheticNaming.isSynthetic(reference, null, naming.VIVIFIED_WRAPPER);
}
public static Matcher<String> containsInternalSyntheticReference() {
return containsString(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
}
public static boolean isInternalThrowNSME(MethodReference method) {
return SyntheticNaming.isSynthetic(method.getHolderClass(), Phase.INTERNAL, naming.THROW_NSME);
}
public static class Builder extends SyntheticItemsTestUtils {
private final Map<ClassReference, Map<SyntheticKind, Int2ObjectMap<Set<ClassReference>>>>
syntheticClasses = new HashMap<>();
private final Map<ClassReference, Map<SyntheticKind, Int2ObjectMap<Set<MethodReference>>>>
syntheticMethods = new HashMap<>();
public void add(
SyntheticKind kind, int id, DexType syntheticContext, DexReference externalReference) {
externalReference.accept(
clazz -> addClass(kind, id, syntheticContext, clazz),
field -> {
assert false;
},
method -> addMethod(kind, id, syntheticContext, method));
}
private void addClass(
SyntheticKind kind, int id, DexType syntheticContext, DexType externalClass) {
Map<SyntheticKind, Int2ObjectMap<Set<ClassReference>>> syntheticsForContextMap =
syntheticClasses.computeIfAbsent(
syntheticContext.asClassReference(), ignoreKey(HashMap::new));
Int2ObjectMap<Set<ClassReference>> idToExternalMethodsMap =
syntheticsForContextMap.computeIfAbsent(kind, ignoreKey(Int2ObjectOpenHashMap::new));
Set<ClassReference> externalClasses =
idToExternalMethodsMap.computeIfAbsent(id, ignoreKey(HashSet::new));
externalClasses.add(externalClass.asClassReference());
}
private void addMethod(
SyntheticKind kind, int id, DexType syntheticContext, DexMethod externalMethod) {
Map<SyntheticKind, Int2ObjectMap<Set<MethodReference>>> syntheticsForContextMap =
syntheticMethods.computeIfAbsent(
syntheticContext.asClassReference(), ignoreKey(HashMap::new));
Int2ObjectMap<Set<MethodReference>> idToExternalMethodsMap =
syntheticsForContextMap.computeIfAbsent(kind, ignoreKey(Int2ObjectOpenHashMap::new));
Set<MethodReference> externalMethods =
idToExternalMethodsMap.computeIfAbsent(id, ignoreKey(HashSet::new));
externalMethods.add(externalMethod.asMethodReference());
}
private ClassReference lookupClass(ClassReference classReference, int id, SyntheticKind kind) {
return internalLookup(classReference, id, kind, syntheticClasses);
}
private ClassReference lookupMethod(ClassReference classReference, int id, SyntheticKind kind) {
MethodReference methodReference = internalLookup(classReference, id, kind, syntheticMethods);
return methodReference != null ? methodReference.getHolderClass() : null;
}
private static <S> S internalLookup(
ClassReference classReference,
int id,
SyntheticKind kind,
Map<ClassReference, Map<SyntheticKind, Int2ObjectMap<Set<S>>>> synthetics) {
Set<S> references =
synthetics
.getOrDefault(classReference, Collections.emptyMap())
.getOrDefault(kind, Int2ObjectMaps.emptyMap())
.getOrDefault(id, Collections.emptySet());
if (references.size() == 1) {
return references.iterator().next();
}
assert references.isEmpty();
return null;
}
private MethodReference lookupMethod(
ClassReference classReference,
MethodReference methodReference,
int id,
SyntheticKind kind) {
Set<MethodReference> references =
syntheticMethods
.getOrDefault(classReference, Collections.emptyMap())
.getOrDefault(kind, Int2ObjectMaps.emptyMap())
.getOrDefault(id, Collections.emptySet());
for (MethodReference reference : references) {
if (reference.getMethodDescriptor().equals(methodReference.getMethodDescriptor())) {
return reference;
}
}
return null;
}
@Override
public boolean isExternalApiOutlineClass(ClassReference classReference) {
return isSyntheticMethodOfKind(classReference, naming.API_MODEL_OUTLINE);
}
@Override
public boolean isExternalBackportClass(ClassReference classReference) {
return isSyntheticMethodOfKind(classReference, naming.BACKPORT);
}
@Override
public boolean isExternalLambda(ClassReference classReference) {
return isSyntheticClassOfKind(classReference, naming.LAMBDA);
}
@Override
public boolean isExternalNonFixedInitializerTypeArgument(ClassReference classReference) {
return isSyntheticClassOfKind(classReference, naming.NON_FIXED_INIT_TYPE_ARGUMENT);
}
@Override
public boolean isExternalOutlineClass(ClassReference classReference) {
return isSyntheticMethodOfKind(classReference, naming.OUTLINE);
}
@Override
public boolean isExternalStaticInterfaceCall(ClassReference classReference) {
return isSyntheticMethodOfKind(classReference, naming.STATIC_INTERFACE_CALL);
}
@Override
public boolean isExternalTwrCloseMethod(ClassReference classReference) {
return isSyntheticMethodOfKind(classReference, naming.TWR_CLOSE_RESOURCE);
}
private boolean isSyntheticClassOfKind(ClassReference classReference, SyntheticKind kind) {
return internalIsSyntheticOfKind(classReference, kind, syntheticClasses, Function.identity());
}
private boolean isSyntheticMethodOfKind(ClassReference classReference, SyntheticKind kind) {
return internalIsSyntheticOfKind(
classReference, kind, syntheticMethods, MethodReference::getHolderClass);
}
private static <S> boolean internalIsSyntheticOfKind(
ClassReference classReference,
SyntheticKind kind,
Map<ClassReference, Map<SyntheticKind, Int2ObjectMap<Set<S>>>> synthetics,
Function<S, ClassReference> toContext) {
for (Map<SyntheticKind, Int2ObjectMap<Set<S>>> map : synthetics.values()) {
Int2ObjectMap<Set<S>> referencesOfKind = map.getOrDefault(kind, Int2ObjectMaps.emptyMap());
for (Set<S> references : referencesOfKind.values()) {
if (Iterables.any(
references, reference -> toContext.apply(reference).equals(classReference))) {
return true;
}
}
}
return false;
}
@Override
public ClassReference syntheticApiConversionClass(ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.API_CONVERSION);
}
@Override
public ClassReference syntheticApiOutlineClass(ClassReference classReference, int id) {
ClassReference result = lookupMethod(classReference, id, naming.API_MODEL_OUTLINE);
return result != null
? result
: lookupMethod(classReference, id, naming.API_MODEL_OUTLINE_WITHOUT_GLOBAL_MERGING);
}
@Override
public ClassReference syntheticAutoCloseableDispatcherClass(
ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.AUTOCLOSEABLE_DISPATCHER);
}
@Override
public ClassReference syntheticAutoCloseableForwarderClass(
ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.AUTOCLOSEABLE_FORWARDER);
}
@Override
public ClassReference syntheticBackportClass(ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.BACKPORT);
}
@Override
public MethodReference syntheticBackportMethod(
ClassReference classReference, MethodReference originalMethod, int id) {
return lookupMethod(classReference, originalMethod, id, naming.BACKPORT);
}
@Override
public ClassReference syntheticBackportWithForwardingClass(
ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.BACKPORT_WITH_FORWARDING);
}
@Override
public ClassReference syntheticBottomUpOutlineClass(ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.BOTTOM_UP_OUTLINE);
}
@Override
public ClassReference syntheticLambdaClass(ClassReference classReference, int id) {
return lookupClass(classReference, id, naming.LAMBDA);
}
@Override
public ClassReference syntheticNonStartupInStartupOutlineClass(
ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.NON_STARTUP_IN_STARTUP_OUTLINE);
}
@Override
public ClassReference syntheticOutlineClass(ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.OUTLINE);
}
@Override
public ClassReference syntheticRecordHelperClass(ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.RECORD_HELPER);
}
@Override
public ClassReference syntheticThrowIAEClass(ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.THROW_IAE);
}
@Override
public ClassReference syntheticTwrCloseResourceClass(ClassReference classReference, int id) {
return lookupMethod(classReference, id, naming.TWR_CLOSE_RESOURCE);
}
}
}