Merge commit 'c225205824ce58503ff38eec05c3b31ade65cfb5' into dev-release
diff --git a/.gitignore b/.gitignore
index 28f7415..b929f34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -106,6 +106,8 @@
third_party/kotlin/kotlin-compiler-1.3.72
third_party/kotlin/kotlin-compiler-1.4.20.tar.gz
third_party/kotlin/kotlin-compiler-1.4.20
+third_party/kotlin/kotlin-compiler-1.5.0-M2.tar.gz
+third_party/kotlin/kotlin-compiler-1.5.0-M2
third_party/kotlinx-coroutines-1.3.6.tar.gz
third_party/kotlinx-coroutines-1.3.6
third_party/nest/*
diff --git a/build.gradle b/build.gradle
index 30c839c..4feb96d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -46,7 +46,7 @@
// The kotlin version is only here to specify the kotlin language level,
// all kotlin compilations are done in tests.
kotlinVersion = '1.3.72'
- kotlinExtMetadataJVMVersion = '0.1.0'
+ kotlinExtMetadataJVMVersion = '0.2.0'
smaliVersion = '2.2b4'
errorproneVersion = '2.3.2'
testngVersion = '6.10'
@@ -327,6 +327,7 @@
"kotlin/kotlin-compiler-1.3.41",
"kotlin/kotlin-compiler-1.3.72",
"kotlin/kotlin-compiler-1.4.20",
+ "kotlin/kotlin-compiler-1.5.0-M2",
"kotlinx-coroutines-1.3.6",
"openjdk/openjdk-rt-1.8",
"openjdk/desugar_jdk_libs",
@@ -441,7 +442,8 @@
"youtube/youtube.android_14.44",
"youtube/youtube.android_15.08",
"youtube/youtube.android_15.09",
- "youtube/youtube.android_15.33"
+ "youtube/youtube.android_15.33",
+ "youtube/youtube.android_16.12"
],
]
@@ -731,7 +733,7 @@
task repackageDepsNew(type: ShadowJar) {
configurations = [project.configurations.runtimeClasspath]
mergeServiceFiles(it)
- exclude { it.getRelativePath().getPathString() == "module-info.class" }
+ exclude { it.getRelativePath().getPathString().endsWith("module-info.class") }
exclude { it.getRelativePath().getPathString().startsWith("META-INF/maven/") }
baseName 'deps_all'
}
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index b782dbe..f819ee4 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -150,7 +150,6 @@
acl_sets: "default"
triggering_policy: {
max_concurrent_invocations: 1
- max_batch_size: 1
}
buildbucket {
server: "cr-buildbucket.appspot.com"
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index f12d33c..8470275 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -97,6 +97,8 @@
// Desugar to class file format and turn off switch optimizations, as the final
// compilation with D8 or R8 will do that.
options.cfToCfDesugar = true;
+ assert !options.forceAnnotateSynthetics;
+ options.forceAnnotateSynthetics = true;
assert options.enableSwitchRewriting;
options.enableSwitchRewriting = false;
assert options.enableStringSwitchConversion;
@@ -105,6 +107,7 @@
desugar(app, options, executorService);
options.cfToCfDesugar = false;
+ options.forceAnnotateSynthetics = false;
options.enableSwitchRewriting = true;
options.enableStringSwitchConversion = true;
});
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a6f1b9c..546fad4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -374,7 +374,7 @@
shrinker -> shrinker.run(Mode.INITIAL_TREE_SHAKING));
TreePruner pruner = new TreePruner(appViewWithLiveness);
- DirectMappedDexApplication prunedApp = pruner.run();
+ DirectMappedDexApplication prunedApp = pruner.run(executorService);
// Recompute the subtyping information.
Set<DexType> removedClasses = pruner.getRemovedClasses();
@@ -609,7 +609,7 @@
DefaultTreePrunerConfiguration.getInstance());
TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration);
- DirectMappedDexApplication application = pruner.run();
+ DirectMappedDexApplication application = pruner.run(executorService);
Set<DexType> removedClasses = pruner.getRemovedClasses();
if (options.usageInformationConsumer != null) {
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index a69cc03..47eebe9 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
+import java.util.Arrays;
+import java.util.stream.Collectors;
import org.objectweb.asm.Opcodes;
public final class CfVersion implements StructuralItem<CfVersion> {
@@ -30,6 +32,24 @@
private final int version;
+ private static CfVersion[] versions = {
+ CfVersion.V1_1,
+ CfVersion.V1_2,
+ CfVersion.V1_3,
+ CfVersion.V1_4,
+ CfVersion.V1_5,
+ CfVersion.V1_6,
+ CfVersion.V1_7,
+ CfVersion.V1_8,
+ CfVersion.V9,
+ CfVersion.V10,
+ CfVersion.V11,
+ CfVersion.V12,
+ CfVersion.V13,
+ CfVersion.V14,
+ CfVersion.V15
+ };
+
// Private constructor in case we want to canonicalize versions.
private CfVersion(int version) {
this.version = version;
@@ -55,6 +75,14 @@
spec.withInt(CfVersion::major).withInt(CfVersion::minor);
}
+ public static Iterable<CfVersion> rangeInclusive(CfVersion from, CfVersion to) {
+ assert from.isLessThanOrEqualTo(to);
+ return Arrays.stream(versions)
+ .filter(version -> version.isGreaterThanOrEqualTo(from))
+ .filter(version -> version.isLessThanOrEqualTo(to))
+ .collect(Collectors.toList());
+ }
+
@Override
public CfVersion self() {
return this;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 775a909..a80dc16 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -157,6 +157,10 @@
return false;
}
+ public boolean isInvokeVirtual() {
+ return false;
+ }
+
public boolean isInvokeInterface() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index b725c80..b19a183 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -185,6 +185,7 @@
return opcode == Opcodes.INVOKESTATIC;
}
+ @Override
public boolean isInvokeVirtual() {
return opcode == Opcodes.INVOKEVIRTUAL;
}
diff --git a/src/main/java/com/android/tools/r8/errors/Unreachable.java b/src/main/java/com/android/tools/r8/errors/Unreachable.java
index 0eb71d2..0232c99 100644
--- a/src/main/java/com/android/tools/r8/errors/Unreachable.java
+++ b/src/main/java/com/android/tools/r8/errors/Unreachable.java
@@ -8,6 +8,14 @@
*/
public class Unreachable extends InternalCompilerError {
+ public static Unreachable raise() {
+ throw new Unreachable();
+ }
+
+ public static Unreachable raise(Object... ignore) {
+ throw new Unreachable();
+ }
+
public Unreachable() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 2ee3013..c2c02ad 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -504,15 +504,15 @@
* <p>The result is the field that will be hit at runtime, if such field is known. A result of
* null indicates that the field is either undefined or not a static field.
*/
- public DexEncodedField lookupStaticTargetOn(DexType type, DexField field) {
+ public DexClassAndField lookupStaticTargetOn(DexType type, DexField field) {
assert checkIfObsolete();
assert type.isClassType();
- DexEncodedField result = resolveFieldOn(type, field).getResolvedField();
- return result == null || !result.accessFlags.isStatic() ? null : result;
+ DexClassAndField result = resolveFieldOn(type, field).getResolutionPair();
+ return result == null || !result.getAccessFlags().isStatic() ? null : result;
}
- public DexEncodedField lookupStaticTarget(DexField field) {
- return lookupStaticTargetOn(field.holder, field);
+ public DexClassAndField lookupStaticTarget(DexField field) {
+ return lookupStaticTargetOn(field.getHolderType(), field);
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index b2bc948..0074673 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -221,6 +221,10 @@
return Iterables.filter(directMethods(), predicate::test);
}
+ public void addDirectMethod(DexEncodedMethod method) {
+ methodCollection.addDirectMethod(method);
+ }
+
public void addDirectMethods(Collection<DexEncodedMethod> methods) {
methodCollection.addDirectMethods(methods);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 6ab995d..58862d7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -48,6 +48,10 @@
return getReference().asMethodReference();
}
+ public DexType getParameter(int index) {
+ return getReference().getParameter(index);
+ }
+
public DexTypeList getParameters() {
return getReference().getParameters();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 412a66d..ceb543f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -390,6 +390,8 @@
public final DexType stringBuilderType = createStaticallyKnownType(stringBuilderDescriptor);
public final DexType stringBufferType = createStaticallyKnownType(stringBufferDescriptor);
+ public final DexType javaLangReflectArrayType =
+ createStaticallyKnownType("Ljava/lang/reflect/Array;");
public final DexType javaLangSystemType = createStaticallyKnownType(javaLangSystemDescriptor);
public final DexType javaIoPrintStreamType = createStaticallyKnownType("Ljava/io/PrintStream;");
@@ -557,6 +559,8 @@
public final ClassMethods classMethods = new ClassMethods();
public final ConstructorMethods constructorMethods = new ConstructorMethods();
public final EnumMembers enumMembers = new EnumMembers();
+ public final JavaLangReflectArrayMembers javaLangReflectArrayMembers =
+ new JavaLangReflectArrayMembers();
public final JavaLangSystemMethods javaLangSystemMethods = new JavaLangSystemMethods();
public final NullPointerExceptionMethods npeMethods = new NullPointerExceptionMethods();
public final IllegalArgumentExceptionMethods illegalArgumentExceptionMethods =
@@ -1453,6 +1457,17 @@
}
}
+ public class JavaLangReflectArrayMembers {
+
+ public final DexMethod newInstanceMethodWithDimensions =
+ createMethod(
+ javaLangReflectArrayType,
+ createProto(objectType, classType, intArrayType),
+ "newInstance");
+
+ private JavaLangReflectArrayMembers() {}
+ }
+
public class JavaLangSystemMethods {
public final DexMethod identityHashCode;
@@ -2568,12 +2583,15 @@
if (type.isClassType()) {
if (!appView.enableWholeProgramOptimizations()) {
// Don't reason at the level of interfaces in D8.
- return ClassTypeElement.create(type, nullability, InterfaceCollection.empty());
+ return ClassTypeElement.createForD8(type, nullability);
}
assert appView.appInfo().hasClassHierarchy();
if (appView.isInterface(type).isTrue()) {
return ClassTypeElement.create(
- objectType, nullability, InterfaceCollection.singleton(type));
+ objectType,
+ nullability,
+ appView.withClassHierarchy(),
+ InterfaceCollection.singleton(type));
}
// In theory, `interfaces` is the least upper bound of implemented interfaces.
// It is expensive to walk through type hierarchy; collect implemented interfaces;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 881b0e9..31a24ff 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -595,10 +595,6 @@
methodCollection.addVirtualMethod(virtualMethod);
}
- public void addDirectMethod(DexEncodedMethod directMethod) {
- methodCollection.addDirectMethod(directMethod);
- }
-
public void replaceVirtualMethod(
DexMethod virtualMethod, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
methodCollection.replaceVirtualMethod(virtualMethod, replacement);
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
index 1af8be2..f56cc34 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
@@ -4,8 +4,9 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.utils.ConsumerUtils;
-import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
@@ -89,24 +90,31 @@
public MethodAccessInfoCollection rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLens lens) {
- return new MethodAccessInfoCollection(
- rewriteInvokesWithLens(directInvokes, definitions, lens),
- rewriteInvokesWithLens(interfaceInvokes, definitions, lens),
- rewriteInvokesWithLens(staticInvokes, definitions, lens),
- rewriteInvokesWithLens(superInvokes, definitions, lens),
- rewriteInvokesWithLens(virtualInvokes, definitions, lens));
+ MethodAccessInfoCollection.Builder<?> builder = identityBuilder();
+ rewriteInvokesWithLens(builder, directInvokes, definitions, lens, Type.DIRECT);
+ rewriteInvokesWithLens(builder, interfaceInvokes, definitions, lens, Type.INTERFACE);
+ rewriteInvokesWithLens(builder, staticInvokes, definitions, lens, Type.STATIC);
+ rewriteInvokesWithLens(builder, superInvokes, definitions, lens, Type.SUPER);
+ rewriteInvokesWithLens(builder, virtualInvokes, definitions, lens, Type.VIRTUAL);
+ return builder.build();
}
- private static Map<DexMethod, ProgramMethodSet> rewriteInvokesWithLens(
- Map<DexMethod, ProgramMethodSet> invokes, DexDefinitionSupplier definitions, GraphLens lens) {
- return MapUtils.map(
- invokes,
- IdentityHashMap::new,
- lens::getRenamedMethodSignature,
- methods -> methods.rewrittenWithLens(definitions, lens),
- (methods, other) -> {
- methods.addAll(other);
- return methods;
+ private static void rewriteInvokesWithLens(
+ MethodAccessInfoCollection.Builder<?> builder,
+ Map<DexMethod, ProgramMethodSet> invokes,
+ DexDefinitionSupplier definitions,
+ GraphLens lens,
+ Type type) {
+ invokes.forEach(
+ (reference, contexts) -> {
+ ProgramMethodSet newContexts = contexts.rewrittenWithLens(definitions, lens);
+ for (ProgramMethod newContext : newContexts) {
+ MethodLookupResult methodLookupResult =
+ lens.lookupMethod(reference, newContext.getReference(), type);
+ DexMethod newReference = methodLookupResult.getReference();
+ Type newType = methodLookupResult.getType();
+ builder.registerInvokeInContext(newReference, newContext, newType);
+ }
});
}
@@ -151,6 +159,25 @@
return virtualInvokes;
}
+ public boolean registerInvokeInContext(
+ DexMethod invokedMethod, ProgramMethod context, Type type) {
+ switch (type) {
+ case DIRECT:
+ return registerInvokeDirectInContext(invokedMethod, context);
+ case INTERFACE:
+ return registerInvokeInterfaceInContext(invokedMethod, context);
+ case STATIC:
+ return registerInvokeStaticInContext(invokedMethod, context);
+ case SUPER:
+ return registerInvokeSuperInContext(invokedMethod, context);
+ case VIRTUAL:
+ return registerInvokeVirtualInContext(invokedMethod, context);
+ default:
+ assert false;
+ return false;
+ }
+ }
+
public boolean registerInvokeDirectInContext(DexMethod invokedMethod, ProgramMethod context) {
return registerInvokeMethodInContext(invokedMethod, context, directInvokes);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
index f33a1d4..78332d5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Function;
public class ArrayTypeElement extends ReferenceTypeElement {
@@ -134,10 +135,12 @@
@Override
public ArrayTypeElement fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ Function<DexType, DexType> mapping,
+ Set<DexType> prunedTypes) {
if (memberTypeLattice.isReferenceType()) {
TypeElement substitutedMemberType =
- memberTypeLattice.fixupClassTypeReferences(mapping, appView);
+ memberTypeLattice.fixupClassTypeReferences(appView, mapping, prunedTypes);
if (substitutedMemberType != memberTypeLattice) {
return ArrayTypeElement.create(substitutedMemberType, nullability);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 144f465..083c27f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -23,6 +23,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
+import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -37,11 +38,16 @@
private final DexType type;
public static ClassTypeElement create(
- DexType classType, Nullability nullability, InterfaceCollection interfaces) {
+ DexType classType,
+ Nullability nullability,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ InterfaceCollection interfaces) {
+ assert appView != null;
+ assert appView.enableWholeProgramOptimizations();
assert interfaces != null;
return NullabilityVariants.create(
nullability,
- (variants) -> new ClassTypeElement(classType, nullability, interfaces, variants, null));
+ variants -> new ClassTypeElement(classType, nullability, interfaces, variants, appView));
}
public static ClassTypeElement create(
@@ -49,9 +55,18 @@
Nullability nullability,
AppView<? extends AppInfoWithClassHierarchy> appView) {
assert appView != null;
+ assert appView.enableWholeProgramOptimizations();
return NullabilityVariants.create(
nullability,
- (variants) -> new ClassTypeElement(classType, nullability, null, variants, appView));
+ variants -> new ClassTypeElement(classType, nullability, null, variants, appView));
+ }
+
+ public static ClassTypeElement createForD8(DexType classType, Nullability nullability) {
+ return NullabilityVariants.create(
+ nullability,
+ variants ->
+ new ClassTypeElement(
+ classType, nullability, InterfaceCollection.empty(), variants, null));
}
private ClassTypeElement(
@@ -61,11 +76,13 @@
NullabilityVariants<ClassTypeElement> variants,
AppView<? extends AppInfoWithClassHierarchy> appView) {
super(nullability);
+ assert appView != null
+ ? appView.enableWholeProgramOptimizations()
+ : (interfaces != null && interfaces.isEmpty());
assert classType.isClassType();
- assert interfaces != null || appView != null;
- type = classType;
+ this.type = classType;
this.appView = appView;
- lazyInterfaces = interfaces;
+ this.lazyInterfaces = interfaces;
this.variants = variants;
}
@@ -75,12 +92,13 @@
public InterfaceCollection getInterfaces() {
if (lazyInterfaces == null) {
+ // Class type interfaces are cached on DexItemFactory.
assert appView != null;
- lazyInterfaces =
- appView.dexItemFactory()
- .getOrComputeLeastUpperBoundOfImplementedInterfaces(type, appView);
+ assert appView.enableWholeProgramOptimizations();
+ return appView
+ .dexItemFactory()
+ .getOrComputeLeastUpperBoundOfImplementedInterfaces(type, appView);
}
- assert lazyInterfaces != null;
return lazyInterfaces;
}
@@ -90,11 +108,6 @@
return new ClassTypeElement(type, nullability, lazyInterfaces, variants, appView);
}
- public boolean isRelatedTo(ClassTypeElement other, AppView<?> appView) {
- return lessThanOrEqualUpToNullability(other, appView)
- || other.lessThanOrEqualUpToNullability(this, appView);
- }
-
@Override
public ClassTypeElement getOrCreateVariant(Nullability nullability) {
ClassTypeElement variant = variants.get(nullability);
@@ -156,17 +169,21 @@
@Override
public TypeElement fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> ignore,
+ Function<DexType, DexType> mapping,
+ Set<DexType> prunedTypes) {
+ assert appView != null;
+ assert appView.enableWholeProgramOptimizations();
+
DexType mappedType = mapping.apply(type);
if (mappedType.isPrimitiveType()) {
return PrimitiveTypeElement.fromDexType(mappedType, false);
}
- if (mappedType != type) {
- return create(mappedType, nullability, appView);
- }
- // If the mapped type is not object and no computation of interfaces, we can return early.
- if (mappedType != appView.dexItemFactory().objectType && lazyInterfaces == null) {
- return this;
+
+ // If there are no interfaces, then just return the mapped type element. Otherwise, the list of
+ // interfaces require rewriting.
+ if (lazyInterfaces == null || lazyInterfaces.isEmpty()) {
+ return mappedType == type ? this : create(mappedType, nullability, appView);
}
// For most types there will not have been a change thus we iterate without allocating a new
@@ -176,6 +193,9 @@
getInterfaces()
.forEach(
(iface, isKnown) -> {
+ if (prunedTypes.contains(iface)) {
+ return;
+ }
DexType substitutedType = mapping.apply(iface);
if (iface != substitutedType) {
hasChangedInterfaces.set();
@@ -196,7 +216,7 @@
if (hasChangedInterfaces.get()) {
if (interfaceToClassChange.isSet()) {
assert !interfaceToClassChange.get().isInterface();
- assert type == appView.dexItemFactory().objectType;
+ assert mappedType == appView.dexItemFactory().objectType;
return create(interfaceToClassChange.get().type, nullability, appView);
} else {
Builder builder = InterfaceCollection.builder();
@@ -206,38 +226,53 @@
assert iface == rewritten || isKnown : "Rewritten implies program types thus known.";
builder.addInterface(rewritten, isKnown);
});
- return create(mappedType, nullability, builder.build());
+ return create(mappedType, nullability, appView, builder.build());
}
}
- return this;
+ return mappedType == type ? this : create(mappedType, nullability, appView, getInterfaces());
}
ClassTypeElement join(ClassTypeElement other, AppView<?> appView) {
- Nullability nullability = nullability().join(other.nullability());
if (!appView.enableWholeProgramOptimizations()) {
- assert lazyInterfaces != null && lazyInterfaces.isEmpty();
- assert other.lazyInterfaces != null && other.lazyInterfaces.isEmpty();
- return ClassTypeElement.create(
+ assert lazyInterfaces != null;
+ assert lazyInterfaces.isEmpty();
+ assert other.lazyInterfaces != null;
+ assert other.lazyInterfaces.isEmpty();
+ return ClassTypeElement.createForD8(
getClassType() == other.getClassType()
? getClassType()
: appView.dexItemFactory().objectType,
- nullability,
- InterfaceCollection.empty());
+ nullability().join(other.nullability()));
}
+ return joinWithClassHierarchy(other);
+ }
+
+ private ClassTypeElement joinWithClassHierarchy(ClassTypeElement other) {
+ assert appView != null;
+ assert appView.enableWholeProgramOptimizations();
DexType lubType =
- computeLeastUpperBoundOfClasses(
- appView.appInfo().withClassHierarchy(), getClassType(), other.getClassType());
+ computeLeastUpperBoundOfClasses(appView.appInfo(), getClassType(), other.getClassType());
InterfaceCollection c1lubItfs = getInterfaces();
InterfaceCollection c2lubItfs = other.getInterfaces();
- InterfaceCollection lubItfs = null;
- if (c1lubItfs.equals(c2lubItfs)) {
- lubItfs = c1lubItfs;
- }
- if (lubItfs == null) {
- lubItfs =
- computeLeastUpperBoundOfInterfaces(appView.withClassHierarchy(), c1lubItfs, c2lubItfs);
- }
- return ClassTypeElement.create(lubType, nullability, lubItfs);
+ InterfaceCollection lubItfs =
+ c1lubItfs.equals(c2lubItfs)
+ ? c1lubItfs
+ : computeLeastUpperBoundOfInterfaces(appView, c1lubItfs, c2lubItfs);
+ InterfaceCollection lubItfsDefault =
+ appView
+ .dexItemFactory()
+ .getOrComputeLeastUpperBoundOfImplementedInterfaces(lubType, appView);
+ Nullability lubNullability = nullability().join(other.nullability());
+
+ // If the computed interfaces are identical to the interfaces of `lubType`, then do not include
+ // the interfaces in the ClassTypeElement. This canonicalization of interfaces reduces memory,
+ // but also reduces the amount of work involved in lens rewriting class type elements (the null
+ // element does not require any rewriting).
+ //
+ // From a correctness point of view, both solutions should work.
+ return lubItfs.equals(lubItfsDefault)
+ ? create(lubType, lubNullability, appView)
+ : create(lubType, lubNullability, appView, lubItfs);
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
index a89c486..88d8b66 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
@@ -85,7 +85,7 @@
|| operandType.isPrimitiveType()
|| operandType.isNullType()
|| (operandType.isReferenceType()
- && operandType.fixupClassTypeReferences(mapping, appView) == operandType);
+ && operandType.fixupClassTypeReferences(appView, mapping) == operandType);
}
}
}
@@ -98,7 +98,7 @@
BasicBlock block = blocks.next();
for (Phi phi : block.getPhis()) {
TypeElement phiType = phi.getType();
- TypeElement substituted = phiType.fixupClassTypeReferences(mapping, appView);
+ TypeElement substituted = phiType.fixupClassTypeReferences(appView, mapping);
assert substituted == phiType || affectedPhis.contains(phi);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index 9736b13..25cb05d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -3,13 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.analysis.type;
+import static java.util.Collections.emptySet;
+
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
import java.util.function.Function;
/** The base abstraction of lattice elements for local type analysis. */
@@ -67,11 +71,30 @@
return ReferenceTypeElement.getNullType();
}
+ public final TypeElement fixupClassTypeReferences(
+ AppView<? extends AppInfoWithClassHierarchy> appView, Function<DexType, DexType> mapping) {
+ return fixupClassTypeReferences(appView, mapping, emptySet());
+ }
+
public TypeElement fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ Function<DexType, DexType> mapping,
+ Set<DexType> prunedTypes) {
return this;
}
+ public final TypeElement rewrittenWithLens(
+ AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens graphLens) {
+ return rewrittenWithLens(appView, graphLens, emptySet());
+ }
+
+ public final TypeElement rewrittenWithLens(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ GraphLens graphLens,
+ Set<DexType> prunedTypes) {
+ return fixupClassTypeReferences(appView, graphLens::lookupType, prunedTypes);
+ }
+
public boolean isNullable() {
return nullability().isNullable();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 4c1d942..eb5a275 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1578,7 +1578,11 @@
}
public B setFreshOutValue(ValueFactory factory, TypeElement type) {
- return setOutValue(factory.createValue(type));
+ return setFreshOutValue(factory, type, null);
+ }
+
+ public B setFreshOutValue(ValueFactory factory, TypeElement type, DebugLocalInfo localInfo) {
+ return setOutValue(factory.createValue(type, localInfo));
}
public B setPosition(Position position) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 5793ec1..6fe8c6f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.Opcodes;
@@ -160,6 +161,11 @@
return arguments().get(index);
}
+ public Value getArgumentForParameter(int index) {
+ int offset = BooleanUtils.intValue(!isInvokeStatic());
+ return getArgument(index + offset);
+ }
+
public Value getFirstArgument() {
return getArgument(0);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 1ef9cf5..508b115 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -93,7 +93,8 @@
assert verifyLambdaInterfaces(returnType, lambdaInterfaceSet, objectType);
- return ClassTypeElement.create(objectType, Nullability.maybeNull(), lambdaInterfaceSet);
+ return ClassTypeElement.create(
+ objectType, Nullability.maybeNull(), appView.withClassHierarchy(), lambdaInterfaceSet);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 70f0502..b4caedd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -37,6 +38,10 @@
super(field, dest, (Value) null);
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
public static StaticGet copyOf(IRCode code, StaticGet original) {
Value newValue =
new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
@@ -243,4 +248,28 @@
return holder != context.getHolderType();
}
}
+
+ public static class Builder extends BuilderBase<Builder, StaticGet> {
+
+ private DexField field;
+
+ public Builder setField(DexClassAndField field) {
+ return setField(field.getReference());
+ }
+
+ public Builder setField(DexField field) {
+ this.field = field;
+ return this;
+ }
+
+ @Override
+ public StaticGet build() {
+ return amend(new StaticGet(outValue, field));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 3fb79c6..0437f8d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -1523,20 +1523,6 @@
null /* sourceString */);
}
}
- if (type == Type.VIRTUAL) {
- // If an invoke-virtual targets a private method in the current class overriding will
- // not apply (see jvm spec on method resolution 5.4.3.3 and overriding 5.4.5) and
- // therefore we use an invoke-direct instead. We need to do this as the Android Runtime
- // will not allow invoke-virtual of a private method.
- DexMethod invocationMethod = (DexMethod) item;
- if (invocationMethod.holder == method.getHolderType()) {
- DexEncodedMethod directTarget = method.getHolder().lookupDirectMethod(invocationMethod);
- if (directTarget != null && !directTarget.isStatic()) {
- assert invocationMethod.holder == directTarget.getHolderType();
- type = Type.DIRECT;
- }
- }
- }
add(Invoke.create(type, item, callSiteProto, null, arguments, itf));
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 5a9ee16..a241a05 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1545,10 +1545,6 @@
timing.end();
}
- if (enumUnboxer != null && methodProcessor.isPrimaryMethodProcessor()) {
- enumUnboxer.analyzeEnums(code);
- }
-
assert code.verifyTypes(appView);
deadCodeRemover.run(code, timing);
@@ -1597,6 +1593,10 @@
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
Timing timing) {
+ if (enumUnboxer != null && methodProcessor.isPrimaryMethodProcessor()) {
+ enumUnboxer.analyzeEnums(code);
+ }
+
if (libraryMethodOverrideAnalysis != null) {
timing.begin("Analyze library method overrides");
libraryMethodOverrideAnalysis.analyze(code);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 66e7ff7..b02d5e7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -122,8 +122,7 @@
private Value makeOutValue(Instruction insn, IRCode code) {
if (insn.outValue() != null) {
TypeElement oldType = insn.getOutType();
- TypeElement newType =
- oldType.fixupClassTypeReferences(appView.graphLens()::lookupType, appView);
+ TypeElement newType = oldType.rewrittenWithLens(appView, appView.graphLens());
return code.createValue(newType, insn.getLocalInfo());
}
return null;
@@ -622,8 +621,7 @@
Assume assume = current.asAssume();
if (assume.hasOutValue()) {
TypeElement type = assume.getOutType();
- TypeElement substituted =
- type.fixupClassTypeReferences(graphLens::lookupType, appView);
+ TypeElement substituted = type.rewrittenWithLens(appView, graphLens);
if (substituted != type) {
assert type.isArrayType() || type.isClassType();
if (substituted.isPrimitiveType()) {
@@ -659,8 +657,7 @@
if (current.hasOutValue()) {
// For all other instructions, substitute any changed type.
TypeElement type = current.getOutType();
- TypeElement substituted =
- type.fixupClassTypeReferences(graphLens::lookupType, appView);
+ TypeElement substituted = type.rewrittenWithLens(appView, graphLens);
if (substituted != type) {
current.outValue().setType(substituted);
affectedPhis.addAll(current.outValue().uniquePhiUsers());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 8c7528c..7bb5741 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -70,7 +70,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
if (!instruction.isInvoke()) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
index 0f264b0..ea1cd46 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
@@ -37,7 +37,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
if (!isInvokeCandidate(instruction)) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
index 864a7b4..11c18d0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.ProgramMethod;
import java.util.Collection;
@@ -26,7 +27,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext);
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory);
/**
* Returns true if the given instruction needs desugaring.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
new file mode 100644
index 0000000..7fd31a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, 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.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+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.ProgramMethod;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * If an invoke-virtual targets a private method in the current class overriding will not apply (see
+ * JVM 11 spec on method selection 5.4.6. In previous jvm specs this was not explicitly stated, but
+ * derived from method resolution 5.4.3.3 and overriding 5.4.5).
+ *
+ * <p>An invoke-interface can in the same way target a private method.
+ *
+ * <p>For desugaring we use invoke-direct instead. We need to do this as the Android Runtime will
+ * not allow invoke-virtual of a private method.
+ */
+public class InvokeToPrivateRewriter implements CfInstructionDesugaring {
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
+ if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
+ return null;
+ }
+ CfInvoke invoke = instruction.asInvoke();
+ DexMethod method = invoke.getMethod();
+ DexEncodedMethod privateMethod = privateMethodInvokedOnSelf(invoke, context);
+ if (privateMethod == null) {
+ return null;
+ }
+ return ImmutableList.of(new CfInvoke(Opcodes.INVOKESPECIAL, method, invoke.isInterface()));
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
+ return false;
+ }
+ return isInvokingPrivateMethodOnSelf(instruction.asInvoke(), context);
+ }
+
+ private DexEncodedMethod privateMethodInvokedOnSelf(CfInvoke invoke, ProgramMethod context) {
+ DexMethod method = invoke.getMethod();
+ if (method.getHolderType() != context.getHolderType()) {
+ return null;
+ }
+ DexEncodedMethod directTarget = context.getHolder().lookupDirectMethod(method);
+ if (directTarget != null && !directTarget.isStatic()) {
+ assert method.holder == directTarget.getHolderType();
+ return directTarget;
+ }
+ return null;
+ }
+
+ private boolean isInvokingPrivateMethodOnSelf(CfInvoke invoke, ProgramMethod context) {
+ return privateMethodInvokedOnSelf(invoke, context) != null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 04b40b3..26a2c5c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -45,6 +45,7 @@
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
desugarings.add(new LambdaInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
+ desugarings.add(new InvokeToPrivateRewriter());
desugarings.add(new StringConcatInstructionDesugaring(appView));
desugarings.add(new BufferCovariantReturnTypeRewriter(appView));
if (appView.options().enableBackportedMethodRewriting()) {
@@ -181,7 +182,8 @@
localStackAllocator,
eventConsumer,
context,
- methodProcessingContext);
+ methodProcessingContext,
+ appView.dexItemFactory());
if (replacement != null) {
assert verifyNoOtherDesugaringNeeded(
instruction, context, methodProcessingContext, iterator);
@@ -215,7 +217,7 @@
desugarings, desugaring -> desugaring.needsDesugaring(instruction, context));
}
- private static boolean verifyNoOtherDesugaringNeeded(
+ private boolean verifyNoOtherDesugaringNeeded(
CfInstruction instruction,
ProgramMethod context,
MethodProcessingContext methodProcessingContext,
@@ -234,7 +236,8 @@
},
CfInstructionDesugaringEventConsumer.createForDesugaredCode(),
context,
- methodProcessingContext)
+ methodProcessingContext,
+ appView.dexItemFactory())
!= null)
== null;
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
index d8d47e8..5770e22 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
@@ -125,7 +125,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
assert !instruction.isInitClass();
if (instruction.isInvokeDynamic() && needsDesugaring(instruction.asInvokeDynamic(), context)) {
return desugarInvokeDynamicOnRecord(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
index 0b9c80e..dfb797c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
@@ -72,7 +72,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
if (instruction.isInvokeSpecial()) {
return desugarInvokeInstruction(instruction.asInvoke(), eventConsumer, context);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 0661c1d..0899b2d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -25,12 +25,10 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -39,10 +37,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.MethodCollection;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -918,45 +913,20 @@
return;
}
- // If the companion class does not exist, then synthesize it on the classpath.
- DexClass companionClass = appView.definitionFor(rewritten.getHolderType());
- if (companionClass == null) {
- companionClass =
- synthesizeEmptyCompanionClass(appView, rewritten.getHolderType(), method.getHolder());
- }
-
- // If the companion class is a classpath class, then synthesize the companion class method if it
- // does not exist.
- if (companionClass.isClasspathClass()) {
- synthetizeCompanionClassMethodIfNotPresent(companionClass.asClasspathClass(), rewritten);
- }
- }
-
- private static DexClasspathClass synthesizeEmptyCompanionClass(
- AppView<?> appView, DexType type, DexClass context) {
- return appView
+ appView
.getSyntheticItems()
- .createClasspathClass(
- SyntheticKind.COMPANION_CLASS, type, context, appView.dexItemFactory());
- }
-
- private static void synthetizeCompanionClassMethodIfNotPresent(
- DexClasspathClass companionClass, DexMethod method) {
- MethodCollection methodCollection = companionClass.getMethodCollection();
- synchronized (methodCollection) {
- if (methodCollection.getMethod(method) == null) {
- boolean d8R8Synthesized = true;
- methodCollection.addDirectMethod(
- new DexEncodedMethod(
- method,
- MethodAccessFlags.createPublicStaticSynthetic(),
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- DexEncodedMethod.buildEmptyThrowingCfCode(method),
- d8R8Synthesized));
- }
- }
+ .ensureDirectMethodOnSyntheticClasspathClassWhileMigrating(
+ SyntheticKind.COMPANION_CLASS,
+ rewritten.getHolderType(),
+ method.getHolder(),
+ appView,
+ rewritten,
+ builder ->
+ builder
+ .setName(rewritten.name)
+ .setProto(rewritten.proto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(DexEncodedMethod::buildEmptyThrowingCfCode));
}
/**
@@ -1029,14 +999,6 @@
}
}
- private void process(InterfaceDesugaringProcessor processor, Builder<?> builder, Flavor flavour) {
- for (DexProgramClass clazz : builder.getProgramClasses()) {
- if (shouldProcess(clazz, flavour)) {
- processor.process(clazz, synthesizedMethods);
- }
- }
- }
-
final boolean isDefaultMethod(DexEncodedMethod method) {
assert !method.accessFlags.isConstructor();
assert !method.accessFlags.isStatic();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 83f051d..206a3ed 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -89,7 +89,43 @@
if (!iface.isInterface()) {
return;
}
+ analyzeBridges(iface);
+ if (needsCompanionClass(iface)) {
+ synthesizeCompanionClass(iface, synthesizedMethods);
+ }
+ }
+ private void analyzeBridges(DexProgramClass iface) {
+ for (ProgramMethod method : iface.virtualProgramMethods()) {
+ DexEncodedMethod virtual = method.getDefinition();
+ if (!interfaceMethodRemovalChangesApi(virtual, iface)) {
+ getPostProcessingInterfaceInfo(iface).setHasBridgesToRemove();
+ return;
+ }
+ }
+ }
+
+ private boolean needsCompanionClass(DexProgramClass iface) {
+ if (hasStaticMethodThatTriggersNonTrivialClassInitializer(iface)) {
+ return true;
+ }
+ for (ProgramMethod method : iface.virtualProgramMethods()) {
+ DexEncodedMethod virtual = method.getDefinition();
+ if (rewriter.isDefaultMethod(virtual)) {
+ return true;
+ }
+ }
+ for (ProgramMethod method : iface.directProgramMethods()) {
+ DexEncodedMethod definition = method.getDefinition();
+ if (!definition.isInitializer()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void synthesizeCompanionClass(
+ DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
// The list of methods to be created in companion class.
List<DexEncodedMethod> companionMethods = new ArrayList<>();
@@ -102,9 +138,7 @@
// make private instance methods public static.
processDirectInterfaceMethods(iface, companionMethods);
- if (companionMethods.isEmpty()) {
- return; // No methods to create, companion class not needed.
- }
+ assert !companionMethods.isEmpty();
ClassAccessFlags companionClassFlags = iface.accessFlags.copy();
companionClassFlags.unsetAbstract();
@@ -260,20 +294,14 @@
getPostProcessingInterfaceInfo(iface)
.mapDefaultMethodToCompanionMethod(virtual, implMethod);
}
-
- if (!interfaceMethodRemovalChangesApi(virtual, iface)) {
- getPostProcessingInterfaceInfo(iface).setHasBridgesToRemove();
- }
}
}
private void processDirectInterfaceMethods(
DexProgramClass iface, List<DexEncodedMethod> companionMethods) {
- DexEncodedMethod clinit = null;
for (ProgramMethod method : iface.directProgramMethods()) {
DexEncodedMethod definition = method.getDefinition();
if (definition.isClassInitializer()) {
- clinit = definition;
continue;
}
if (definition.isInstanceInitializer()) {
@@ -283,6 +311,8 @@
continue;
}
+ getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
+
MethodAccessFlags originalFlags = method.getAccessFlags();
MethodAccessFlags newFlags = originalFlags.copy();
if (originalFlags.isPrivate()) {
@@ -343,12 +373,6 @@
companionMethods.add(implMethod);
getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companionMethod);
}
-
- boolean hasNonClinitDirectMethods =
- iface.getMethodCollection().size() != (clinit == null ? 0 : 1);
- if (hasNonClinitDirectMethods) {
- getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
- }
}
private void clearDirectMethods(DexProgramClass iface) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index 59411a7..667397b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
@@ -73,7 +74,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
if (instruction.isInvokeDynamic()) {
return desugarInvokeDynamicInstruction(
instruction.asInvokeDynamic(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index cec4cb1..a3bdc56 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -150,7 +150,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
if (instruction.isFieldInstruction()) {
return desugarFieldInstruction(instruction.asFieldInstruction(), context, eventConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
index cae3e54..4f4d963 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
@@ -75,7 +75,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
if (instruction.isInvokeDynamic()) {
// We are interested in bootstrap methods StringConcatFactory::makeConcat
// and StringConcatFactory::makeConcatWthConstants, both are static.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrCloseResourceInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrCloseResourceInstructionDesugaring.java
index 076c9b3..30e7702 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrCloseResourceInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrCloseResourceInstructionDesugaring.java
@@ -44,7 +44,8 @@
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
if (!instruction.isInvokeStatic()) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 2d36f99..ae48979 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.google.common.base.Predicates.alwaysTrue;
+import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -39,6 +40,7 @@
import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfoLookup;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardMemberRuleReturnValue;
+import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
@@ -105,7 +107,7 @@
DexField field = returnValueRule.getField();
assert instruction.getOutType() == TypeElement.fromDexType(field.type, maybeNull(), appView);
- DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field);
+ DexClassAndField staticField = appView.appInfo().lookupStaticTarget(field);
if (staticField == null) {
if (warnedFields.add(field)) {
reporter.warning(
@@ -118,8 +120,19 @@
return null;
}
+ if (AccessControl.isMemberAccessible(
+ staticField, staticField.getHolder(), code.context(), appView)
+ .isTrue()) {
+ return StaticGet.builder()
+ .setField(field)
+ .setFreshOutValue(code, field.getTypeElement(appView), instruction.getLocalInfo())
+ .build();
+ }
+
Instruction replacement =
- staticField.valueAsConstInstruction(code, instruction.getLocalInfo(), appView);
+ staticField
+ .getDefinition()
+ .valueAsConstInstruction(code, instruction.getLocalInfo(), appView);
if (replacement == null) {
reporter.warning(
new StringDiagnostic(
@@ -179,11 +192,24 @@
}
replacement.setPosition(position);
if (block.hasCatchHandlers()) {
- iterator.split(code, blocks).listIterator(code).add(replacement);
+ BasicBlock splitBlock = iterator.split(code, blocks);
+ splitBlock.listIterator(code).add(replacement);
+
+ // Process the materialized value.
+ blocks.previous();
+ assert !iterator.hasNext();
+ assert IteratorUtils.peekNext(blocks) == splitBlock;
+
+ return true;
} else {
iterator.add(replacement);
}
}
+
+ // Process the materialized value.
+ iterator.previous();
+ assert iterator.peekNext() == replacement;
+
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 802fdb3..683cf92 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -42,12 +43,9 @@
}
Set<Value> affectedValues = Sets.newIdentityHashSet();
ProgramMethod context = code.context();
- for (BasicBlock block : code.blocks) {
- // Conservatively bail out if the containing block has catch handlers.
- // TODO(b/118509730): unless join of all catch types is ClassNotFoundException ?
- if (block.hasCatchHandlers()) {
- continue;
- }
+ BasicBlockIterator blockIterator = code.listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
InstructionListIterator it = block.listIterator(code);
while (it.hasNext()) {
InvokeMethod invoke = it.nextUntil(x -> x.isInvokeStatic() || x.isInvokeVirtual());
@@ -61,14 +59,14 @@
context,
invoke.asInvokeStatic(),
rewriteSingleGetClassOrForNameToConstClass(
- appView, code, it, invoke, affectedValues));
+ appView, code, blockIterator, it, invoke, affectedValues));
} else {
applyTypeForGetClassTo(
appView,
context,
invoke.asInvokeVirtual(),
rewriteSingleGetClassOrForNameToConstClass(
- appView, code, it, invoke, affectedValues));
+ appView, code, blockIterator, it, invoke, affectedValues));
}
}
}
@@ -82,10 +80,12 @@
private static BiConsumer<DexType, DexClass> rewriteSingleGetClassOrForNameToConstClass(
AppView<AppInfoWithLiveness> appView,
IRCode code,
+ BasicBlockIterator blockIterator,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
Set<Value> affectedValues) {
return (type, baseClass) -> {
+ InitClass initClass = null;
if (invoke.getInvokedMethod().match(appView.dexItemFactory().classMethods.forName)) {
// Bail-out if the optimization could increase the size of the main dex.
if (baseClass.isProgramClass()
@@ -106,25 +106,42 @@
return;
}
- instructionIterator.addBefore(
+ initClass =
InitClass.builder()
.setFreshOutValue(code, TypeElement.getInt())
.setType(type)
.setPosition(invoke)
- .build());
+ .build();
}
}
// If there are no users of the const-class then simply remove the instruction.
if (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers()) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
+ if (initClass != null) {
+ instructionIterator.replaceCurrentInstruction(initClass);
+ } else {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
return;
}
// Otherwise insert a const-class instruction.
+ BasicBlock block = invoke.getBlock();
affectedValues.addAll(invoke.outValue().affectedValues());
instructionIterator.replaceCurrentInstructionWithConstClass(
appView, code, type, invoke.getLocalInfo());
+
+ if (initClass != null) {
+ if (block.hasCatchHandlers()) {
+ instructionIterator
+ .splitCopyCatchHandlers(code, blockIterator, appView.options())
+ .listIterator(code)
+ .add(initClass);
+ } else {
+ instructionIterator.add(initClass);
+ }
+ }
+
if (appView.options().isGeneratingClassFiles()) {
code.method()
.upgradeClassFileVersion(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index f4c8028..bbbb543 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -77,31 +77,47 @@
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldOrdinalData;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldUnknownData;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
-import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.IllegalInvokeWithImpreciseParameterTypeReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingContentsForEnumValuesArrayReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingEnumStaticFieldValuesReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingInstanceFieldValueForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingObjectStateForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedInstanceFieldValueForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedLibraryInvokeReason;
+import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.shaking.KeepInfoCollection;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.OptionalInt;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
import java.util.function.Predicate;
public class EnumUnboxer {
@@ -121,7 +137,7 @@
private EnumUnboxingRewriter enumUnboxerRewriter;
private final boolean debugLogEnabled;
- private final Map<DexType, Reason> debugLogs;
+ private final Map<DexType, List<Reason>> debugLogs;
public EnumUnboxer(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
@@ -149,10 +165,20 @@
return ordinal + 1;
}
- private void markEnumAsUnboxable(Reason reason, DexProgramClass enumClass) {
+ /**
+ * Returns true if {@param enumClass} was marked as being unboxable.
+ *
+ * <p>Note that, if debug logging is enabled, {@param enumClass} is not marked unboxable until the
+ * enum unboxing analysis has finished. This is to ensure completeness of the reason reporting.
+ */
+ private boolean markEnumAsUnboxable(Reason reason, DexProgramClass enumClass) {
assert enumClass.isEnum();
- reportFailure(enumClass.type, reason);
- enumUnboxingCandidatesInfo.removeCandidate(enumClass.type);
+ if (!reportFailure(enumClass, reason)) {
+ // The failure was not reported, meaning debug logging is disabled.
+ enumUnboxingCandidatesInfo.removeCandidate(enumClass);
+ return true;
+ }
+ return false;
}
private DexProgramClass getEnumUnboxingCandidateOrNull(TypeElement lattice) {
@@ -265,6 +291,19 @@
}
private void analyzeCheckCast(CheckCast checkCast, Set<DexType> eligibleEnums) {
+ // Casts to enum array types are fine as long all enum array creations are valid and have valid
+ // usages. Since creations of enum arrays are rewritten to primitive int arrays, enum array
+ // casts will continue to work after rewriting to int[] casts. Casts that failed with
+ // ClassCastException: "T[] cannot be cast to MyEnum[]" will continue to fail, but with "T[]
+ // cannot be cast to int[]".
+ //
+ // Note that strictly speaking, the rewriting from MyEnum[] to int[] could change the semantics
+ // of code that would fail with "int[] cannot be cast to MyEnum[]" in the input. However, javac
+ // does not allow such code ("incompatible types"), so we should generally not see such code.
+ if (checkCast.getType().isArrayType()) {
+ return;
+ }
+
// We are doing a type check, which typically means the in-value is of an upper
// type and cannot be dealt with.
// If the cast is on a dynamically typed object, the checkCast can be simply removed.
@@ -291,6 +330,9 @@
ConstClass constClass, Set<DexType> eligibleEnums, ProgramMethod context) {
// We are using the ConstClass of an enum, which typically means the enum cannot be unboxed.
// We however allow unboxing if the ConstClass is used only:
+ // - as an argument to java.lang.reflect.Array#newInstance(java.lang.Class, int[]), to allow
+ // unboxing of:
+ // MyEnum[][] a = new MyEnum[x][y];
// - as an argument to Enum#valueOf, to allow unboxing of:
// MyEnum a = Enum.valueOf(MyEnum.class, "A");
// - as a receiver for a name method, to allow unboxing of:
@@ -315,11 +357,17 @@
}
if (user.isInvokeStatic()) {
DexClassAndMethod singleTarget = user.asInvokeStatic().lookupSingleTarget(appView, context);
- if (singleTarget != null && singleTarget.getReference() == factory.enumMembers.valueOf) {
- // The name data is required for the correct mapping from the enum name to the ordinal in
- // the valueOf utility method.
- addRequiredNameData(enumType);
- continue;
+ if (singleTarget != null) {
+ if (singleTarget.getReference() == factory.enumMembers.valueOf) {
+ // The name data is required for the correct mapping from the enum name to the ordinal
+ // in the valueOf utility method.
+ addRequiredNameData(enumClass);
+ continue;
+ }
+ if (singleTarget.getReference()
+ == factory.javaLangReflectArrayMembers.newInstanceMethodWithDimensions) {
+ continue;
+ }
}
}
markEnumAsUnboxable(Reason.CONST_CLASS, enumClass);
@@ -328,9 +376,9 @@
eligibleEnums.add(enumType);
}
- private void addRequiredNameData(DexType enumType) {
+ private void addRequiredNameData(DexProgramClass enumClass) {
enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(
- enumType, factory.enumMembers.nameField);
+ enumClass, factory.enumMembers.nameField);
}
private boolean isUnboxableNameMethod(DexMethod method) {
@@ -370,23 +418,30 @@
}
private Reason validateEnumUsages(IRCode code, Value value, DexProgramClass enumClass) {
+ Reason result = Reason.ELIGIBLE;
for (Instruction user : value.uniqueUsers()) {
Reason reason = instructionAllowEnumUnboxing(user, code, enumClass, value);
if (reason != Reason.ELIGIBLE) {
- markEnumAsUnboxable(reason, enumClass);
- return reason;
+ if (markEnumAsUnboxable(reason, enumClass)) {
+ return reason;
+ }
+ // Record that the enum is ineligible, and continue analysis to collect all reasons for
+ // debugging.
+ result = reason;
}
}
for (Phi phi : value.uniquePhiUsers()) {
for (Value operand : phi.getOperands()) {
if (!operand.getType().isNullType()
&& getEnumUnboxingCandidateOrNull(operand.getType()) != enumClass) {
+ // All reported reasons from here will be the same (INVALID_PHI), so just return
+ // immediately.
markEnumAsUnboxable(Reason.INVALID_PHI, enumClass);
return Reason.INVALID_PHI;
}
}
}
- return Reason.ELIGIBLE;
+ return result;
}
public void unboxEnums(
@@ -441,31 +496,19 @@
executorService,
new OptimizationInfoFixer() {
@Override
- public void fixup(DexEncodedField field) {
- FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
- if (optimizationInfo.isMutableFieldOptimizationInfo()) {
- optimizationInfo
- .asMutableFieldOptimizationInfo()
- .fixupClassTypeReferences(appView.graphLens()::lookupType, appView)
- .fixupAbstractValue(appView, appView.graphLens());
- } else {
- assert optimizationInfo.isDefaultFieldOptimizationInfo();
- }
+ public void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) {
+ optimizationInfo
+ .fixupClassTypeReferences(appView, appView.graphLens())
+ .fixupAbstractValue(appView, appView.graphLens());
}
@Override
- public void fixup(DexEncodedMethod method) {
- MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
- if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
- UpdatableMethodOptimizationInfo updatableOptimizationInfo =
- optimizationInfo.asUpdatableMethodOptimizationInfo();
- updatableOptimizationInfo
- .fixupClassTypeReferences(appView.graphLens()::lookupType, appView)
- .fixupAbstractReturnValue(appView, appView.graphLens())
- .fixupInstanceInitializerInfo(appView, appView.graphLens());
- } else {
- assert optimizationInfo.isDefaultMethodOptimizationInfo();
- }
+ public void fixup(
+ DexEncodedMethod method, UpdatableMethodOptimizationInfo optimizationInfo) {
+ optimizationInfo
+ .fixupClassTypeReferences(appView, appView.graphLens())
+ .fixupAbstractReturnValue(appView, appView.graphLens())
+ .fixupInstanceInitializerInfo(appView, appView.graphLens());
}
});
}
@@ -479,29 +522,38 @@
analyzeInitializers();
analyzeAccessibility();
EnumDataMap enumDataMap = analyzeEnumInstances();
- assert enumDataMap.getUnboxedEnums().size() == enumUnboxingCandidatesInfo.candidates().size();
if (debugLogEnabled) {
+ // Remove all enums that have been reported as being unboxable.
+ debugLogs.keySet().forEach(enumUnboxingCandidatesInfo::removeCandidate);
reportEnumsAnalysis();
}
+ assert enumDataMap.getUnboxedEnums().size() == enumUnboxingCandidatesInfo.candidates().size();
return enumDataMap;
}
private EnumDataMap analyzeEnumInstances() {
ImmutableMap.Builder<DexType, EnumData> builder = ImmutableMap.builder();
enumUnboxingCandidatesInfo.forEachCandidateAndRequiredInstanceFieldData(
- (enumClass, fields) -> {
- EnumData data = buildData(enumClass, fields);
+ (enumClass, instanceFields) -> {
+ EnumData data = buildData(enumClass, instanceFields);
if (data == null) {
- markEnumAsUnboxable(Reason.MISSING_INSTANCE_FIELD_DATA, enumClass);
+ // Reason is already reported at this point.
+ enumUnboxingCandidatesInfo.removeCandidate(enumClass);
return;
}
- builder.put(enumClass.type, data);
+ if (!debugLogEnabled || !debugLogs.containsKey(enumClass.getType())) {
+ builder.put(enumClass.type, data);
+ }
});
staticFieldValuesMap.clear();
return new EnumDataMap(builder.build());
}
- private EnumData buildData(DexProgramClass enumClass, Set<DexField> fields) {
+ private EnumData buildData(DexProgramClass enumClass, Set<DexField> instanceFields) {
+ if (!enumClass.hasStaticFields()) {
+ return new EnumData(ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of(), -1);
+ }
+
// This map holds all the accessible fields to their unboxed value, so we can remap the field
// read to the unboxed value.
ImmutableMap.Builder<DexField, Integer> unboxedValues = ImmutableMap.builder();
@@ -514,6 +566,10 @@
EnumValuesObjectState valuesContents = null;
EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
+ if (enumStaticFieldValues == null) {
+ reportFailure(enumClass, new MissingEnumStaticFieldValuesReason());
+ return null;
+ }
// Step 1: We iterate over the field to find direct enum instance information and the values
// fields.
@@ -527,10 +583,16 @@
continue;
}
// We could not track the content of that field. We bail out.
+ reportFailure(
+ enumClass, new MissingObjectStateForEnumInstanceReason(staticField.getReference()));
return null;
}
OptionalInt optionalOrdinal = getOrdinal(enumState);
if (!optionalOrdinal.isPresent()) {
+ reportFailure(
+ enumClass,
+ new MissingInstanceFieldValueForEnumInstanceReason(
+ staticField.getReference(), factory.enumMembers.ordinalField));
return null;
}
int ordinal = optionalOrdinal.getAsInt();
@@ -547,6 +609,8 @@
// We could not track the content of that field. We bail out.
// We could not track the content of that field, and the field could be a values field.
// We conservatively bail out.
+ reportFailure(
+ enumClass, new MissingContentsForEnumValuesArrayReason(staticField.getReference()));
return null;
}
assert valuesState.isEnumValuesObjectState();
@@ -577,32 +641,41 @@
// The ordinalToObjectState map may have holes at this point, if some enum instances are never
// used ($VALUES unused or removed, and enum instance field unused or removed), it contains
// only data for reachable enum instance, that is what we're interested in.
- ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> instanceFieldBuilder =
- ImmutableMap.builder();
- for (DexField instanceField : fields) {
- EnumInstanceFieldData fieldData =
- computeEnumFieldData(instanceField, enumClass, ordinalToObjectState);
- if (fieldData.isUnknown()) {
- return null;
- }
- instanceFieldBuilder.put(instanceField, fieldData.asEnumFieldKnownData());
+ ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldsData =
+ computeRequiredEnumInstanceFieldsData(enumClass, instanceFields, ordinalToObjectState);
+ if (instanceFieldsData == null) {
+ return null;
}
return new EnumData(
- instanceFieldBuilder.build(),
+ instanceFieldsData,
unboxedValues.build(),
valuesField.build(),
valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
}
- private boolean isFinalFieldInitialized(DexEncodedField staticField, DexProgramClass enumClass) {
- assert staticField.isFinal();
- return appView
- .appInfo()
- .isFieldOnlyWrittenInMethodIgnoringPinning(staticField, enumClass.getClassInitializer());
+ private ImmutableMap<DexField, EnumInstanceFieldKnownData> computeRequiredEnumInstanceFieldsData(
+ DexProgramClass enumClass,
+ Set<DexField> instanceFields,
+ Int2ReferenceMap<ObjectState> ordinalToObjectState) {
+ ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> builder = ImmutableMap.builder();
+ for (DexField instanceField : instanceFields) {
+ EnumInstanceFieldData fieldData =
+ computeRequiredEnumInstanceFieldData(instanceField, enumClass, ordinalToObjectState);
+ if (fieldData.isUnknown()) {
+ if (!debugLogEnabled) {
+ return null;
+ }
+ builder = null;
+ }
+ if (builder != null) {
+ builder.put(instanceField, fieldData.asEnumFieldKnownData());
+ }
+ }
+ return builder != null ? builder.build() : null;
}
- private EnumInstanceFieldData computeEnumFieldData(
+ private EnumInstanceFieldData computeRequiredEnumInstanceFieldData(
DexField instanceField,
DexProgramClass enumClass,
Int2ReferenceMap<ObjectState> ordinalToObjectState) {
@@ -615,7 +688,15 @@
for (Integer ordinal : ordinalToObjectState.keySet()) {
ObjectState state = ordinalToObjectState.get(ordinal);
AbstractValue fieldValue = state.getAbstractFieldValue(encodedInstanceField);
+ if (!fieldValue.isSingleValue()) {
+ reportFailure(
+ enumClass, new MissingInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
+ return EnumInstanceFieldUnknownData.getInstance();
+ }
if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) {
+ reportFailure(
+ enumClass,
+ new UnsupportedInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
return EnumInstanceFieldUnknownData.getInstance();
}
data.put(ordinalToUnboxedInt(ordinal), fieldValue);
@@ -888,29 +969,19 @@
private void analyzeInitializers() {
enumUnboxingCandidatesInfo.forEachCandidate(
enumClass -> {
- boolean hasInstanceInitializer = false;
for (DexEncodedMethod directMethod : enumClass.directMethods()) {
if (directMethod.isInstanceInitializer()) {
- hasInstanceInitializer = true;
if (directMethod
.getOptimizationInfo()
.getContextInsensitiveInstanceInitializerInfo()
.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
- markEnumAsUnboxable(Reason.INVALID_INIT, enumClass);
- break;
+ if (markEnumAsUnboxable(Reason.INVALID_INIT, enumClass)) {
+ break;
+ }
}
}
}
- if (!hasInstanceInitializer) {
- // This case typically happens when a programmer uses EnumSet/EnumMap without using the
- // enum keep rules. The code is incorrect in this case (EnumSet/EnumMap won't work).
- // We bail out.
- markEnumAsUnboxable(Reason.NO_INIT, enumClass);
- return;
- }
-
if (enumClass.classInitializationMayHaveSideEffects(appView)) {
- enumClass.classInitializationMayHaveSideEffects(appView);
markEnumAsUnboxable(Reason.INVALID_CLINIT, enumClass);
}
});
@@ -1079,7 +1150,7 @@
Value enumValue) {
assert instanceGet.getField().holder == enumClass.type;
DexField field = instanceGet.getField();
- enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(enumClass.type, field);
+ enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(enumClass, field);
return Reason.ELIGIBLE;
}
@@ -1101,10 +1172,11 @@
if (singleTarget == null) {
return Reason.INVALID_INVOKE;
}
- DexClass dexClass = singleTarget.getHolder();
- if (dexClass.isProgramClass()) {
- if (dexClass.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
- if (code.method().getHolderType() == dexClass.type && code.method().isClassInitializer()) {
+ DexMethod singleTargetReference = singleTarget.getReference();
+ DexClass targetHolder = singleTarget.getHolder();
+ if (targetHolder.isProgramClass()) {
+ if (targetHolder.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
+ if (code.context().getHolder() == targetHolder && code.method().isClassInitializer()) {
// The enum instance initializer is allowed to be called only from the enum clinit.
return Reason.ELIGIBLE;
} else {
@@ -1113,28 +1185,28 @@
}
// Check that the enum-value only flows into parameters whose type exactly matches the
// enum's type.
- int offset = BooleanUtils.intValue(!singleTarget.getDefinition().isStatic());
- for (int i = 0; i < singleTarget.getReference().getParameters().size(); i++) {
- if (invoke.getArgument(offset + i) == enumValue) {
- if (singleTarget.getReference().getParameter(i).toBaseType(factory) != enumClass.type) {
- return Reason.GENERIC_INVOKE;
- }
+ for (int i = 0; i < singleTarget.getParameters().size(); i++) {
+ if (invoke.getArgumentForParameter(i) == enumValue
+ && singleTarget.getParameter(i).toBaseType(factory) != enumClass.getType()) {
+ return new IllegalInvokeWithImpreciseParameterTypeReason(singleTargetReference);
}
}
if (invoke.isInvokeMethodWithReceiver()) {
Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
- if (receiver == enumValue && dexClass.isInterface()) {
+ if (receiver == enumValue && targetHolder.isInterface()) {
return Reason.DEFAULT_METHOD_INVOKE;
}
}
return Reason.ELIGIBLE;
}
- if (dexClass.isClasspathClass()) {
- return Reason.INVALID_INVOKE;
+
+ if (targetHolder.isClasspathClass()) {
+ return Reason.INVALID_INVOKE_CLASSPATH;
}
- assert dexClass.isLibraryClass();
- DexMethod singleTargetReference = singleTarget.getReference();
- if (dexClass.type != factory.enumType) {
+
+ assert targetHolder.isLibraryClass();
+
+ if (targetHolder.getType() != factory.enumType) {
// System.identityHashCode(Object) is supported for proto enums.
// Object#getClass without outValue and Objects.requireNonNull are supported since R8
// rewrites explicit null checks to such instructions.
@@ -1142,7 +1214,7 @@
return Reason.ELIGIBLE;
}
if (singleTargetReference == factory.stringMembers.valueOf) {
- addRequiredNameData(enumClass.type);
+ addRequiredNameData(enumClass);
return Reason.ELIGIBLE;
}
if (singleTargetReference == factory.objectMembers.getClass
@@ -1154,7 +1226,7 @@
|| singleTargetReference == factory.objectsMethods.requireNonNullWithMessage) {
return Reason.ELIGIBLE;
}
- return Reason.UNSUPPORTED_LIBRARY_CALL;
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
// TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
if (singleTargetReference == factory.enumMembers.compareTo) {
@@ -1164,7 +1236,7 @@
} else if (singleTargetReference == factory.enumMembers.nameMethod
|| singleTargetReference == factory.enumMembers.toString) {
assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
- addRequiredNameData(enumClass.type);
+ addRequiredNameData(enumClass);
return Reason.ELIGIBLE;
} else if (singleTargetReference == factory.enumMembers.ordinalMethod) {
return Reason.ELIGIBLE;
@@ -1172,12 +1244,11 @@
return Reason.ELIGIBLE;
} else if (singleTargetReference == factory.enumMembers.constructor) {
// Enum constructor call is allowed only if called from an enum initializer.
- if (code.method().isInstanceInitializer()
- && code.method().getHolderType() == enumClass.type) {
+ if (code.method().isInstanceInitializer() && code.context().getHolder() == enumClass) {
return Reason.ELIGIBLE;
}
}
- return Reason.UNSUPPORTED_LIBRARY_CALL;
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
// Return is used for valueOf methods.
@@ -1196,30 +1267,102 @@
private void reportEnumsAnalysis() {
assert debugLogEnabled;
- Reporter reporter = appView.options().reporter;
+ Reporter reporter = appView.reporter();
Set<DexType> candidates = enumUnboxingCandidatesInfo.candidates();
reporter.info(
new StringDiagnostic(
- "Unboxed enums (Unboxing succeeded "
- + candidates.size()
- + "): "
- + Arrays.toString(candidates.toArray())));
- StringBuilder sb = new StringBuilder();
- sb.append("Boxed enums (Unboxing failed ").append(debugLogs.size()).append("):\n");
- for (DexType enumType : debugLogs.keySet()) {
- sb.append("- ")
- .append(enumType)
- .append(": ")
- .append(debugLogs.get(enumType).toString())
- .append('\n');
+ "Unboxed " + candidates.size() + " enums: " + Arrays.toString(candidates.toArray())));
+
+ StringBuilder sb =
+ new StringBuilder("Unable to unbox ")
+ .append(debugLogs.size())
+ .append(" enums.")
+ .append(System.lineSeparator())
+ .append(System.lineSeparator());
+
+ // Sort by the number of reasons that prevent enum unboxing.
+ TreeMap<DexType, List<Reason>> sortedDebugLogs =
+ new TreeMap<>(
+ Comparator.<DexType>comparingInt(x -> debugLogs.get(x).size())
+ .thenComparing(Function.identity()));
+ sortedDebugLogs.putAll(debugLogs);
+
+ // Print the pinned enums and remove them from further reporting.
+ List<DexType> pinned = new ArrayList<>();
+ Iterator<Entry<DexType, List<Reason>>> sortedDebugLogIterator =
+ sortedDebugLogs.entrySet().iterator();
+ while (sortedDebugLogIterator.hasNext()) {
+ Entry<DexType, List<Reason>> entry = sortedDebugLogIterator.next();
+ List<Reason> reasons = entry.getValue();
+ if (reasons.size() > 1) {
+ break;
+ }
+ if (reasons.get(0) == Reason.PINNED) {
+ pinned.add(entry.getKey());
+ sortedDebugLogIterator.remove();
+ }
}
+ if (!pinned.isEmpty()) {
+ sb.append("Pinned: ").append(Arrays.toString(pinned.toArray()));
+ }
+
+ // Print the reasons for each unboxable enum.
+ sortedDebugLogs.forEach(
+ (type, reasons) -> {
+ sb.append(type).append(" (").append(reasons.size()).append(" reasons):");
+ HashMultiset.create(reasons)
+ .forEachEntry(
+ (reason, count) ->
+ sb.append(System.lineSeparator())
+ .append(" - ")
+ .append(reason)
+ .append(" (")
+ .append(count)
+ .append(")"));
+ sb.append(System.lineSeparator());
+ });
+
+ sb.append(System.lineSeparator());
+
+ // Print information about how often a given Reason kind prevents enum unboxing.
+ Object2IntMap<Object> reasonKindCount = new Object2IntOpenHashMap<>();
+ debugLogs.forEach(
+ (type, reasons) ->
+ reasons.forEach(
+ reason ->
+ reasonKindCount.put(reason.getKind(), reasonKindCount.getInt(reason) + 1)));
+ List<Object> differentReasonKinds = new ArrayList<>(reasonKindCount.keySet());
+ differentReasonKinds.sort(
+ (reasonKind, other) -> {
+ int freq = reasonKindCount.getInt(reasonKind) - reasonKindCount.getInt(other);
+ return freq != 0
+ ? freq
+ : System.identityHashCode(reasonKind) - System.identityHashCode(other);
+ });
+ differentReasonKinds.forEach(
+ reasonKind ->
+ sb.append(reasonKind)
+ .append(" (")
+ .append(reasonKindCount.getInt(reasonKind))
+ .append(")")
+ .append(System.lineSeparator()));
+
reporter.info(new StringDiagnostic(sb.toString()));
}
- void reportFailure(DexType enumType, Reason reason) {
+ boolean reportFailure(DexProgramClass enumClass, Reason reason) {
+ return reportFailure(enumClass.getType(), reason);
+ }
+
+ /** Returns true if the failure was reported. */
+ boolean reportFailure(DexType enumType, Reason reason) {
if (debugLogEnabled) {
- debugLogs.put(enumType, reason);
+ debugLogs
+ .computeIfAbsent(enumType, ignore -> Collections.synchronizedList(new ArrayList<>()))
+ .add(reason);
+ return true;
}
+ return false;
}
public Set<Phi> rewriteCode(IRCode code) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 9196ca3..62b05a9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedStaticFieldReason;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -53,50 +54,62 @@
if (!clazz.isEnum()) {
return false;
}
+
+ boolean result = true;
if (clazz.superType != factory.enumType || !clazz.isEffectivelyFinal(appView)) {
- enumUnboxer.reportFailure(clazz.type, Reason.SUBTYPES);
- return false;
+ if (!enumUnboxer.reportFailure(clazz, Reason.SUBTYPES)) {
+ return false;
+ }
+ // Record that `clazz` is ineligible, and continue analysis to ensure all reasons are reported
+ // for debugging.
+ result = false;
}
if (clazz.instanceFields().size() > MAX_INSTANCE_FIELDS_FOR_UNBOXING) {
- enumUnboxer.reportFailure(clazz.type, Reason.MANY_INSTANCE_FIELDS);
- return false;
+ if (!enumUnboxer.reportFailure(clazz, Reason.MANY_INSTANCE_FIELDS)) {
+ return false;
+ }
+ result = false;
}
if (!enumHasBasicStaticFields(clazz)) {
- enumUnboxer.reportFailure(clazz.type, Reason.UNEXPECTED_STATIC_FIELD);
- return false;
+ result = false;
}
- return true;
+ return result;
}
// The enum should have the $VALUES static field and only fields directly referencing the enum
// instances.
private boolean enumHasBasicStaticFields(DexProgramClass clazz) {
+ boolean result = true;
for (DexEncodedField staticField : clazz.staticFields()) {
- if (isEnumField(staticField, clazz.type)) {
+ if (isEnumField(staticField, clazz)) {
// Enum field, valid, do nothing.
- } else if (matchesValuesField(staticField, clazz.type, factory)) {
+ } else if (matchesValuesField(staticField, clazz, factory)) {
// Field $VALUES, valid, do nothing.
} else if (appView.appInfo().isFieldRead(staticField)) {
// Only non read static fields are valid, and they are assumed unused.
- return false;
+ if (!enumUnboxer.reportFailure(
+ clazz, new UnsupportedStaticFieldReason(staticField.getReference()))) {
+ return false;
+ }
+ result = false;
}
}
- return true;
+ return result;
}
- static boolean isEnumField(DexEncodedField staticField, DexType enumType) {
- return staticField.getReference().type == enumType
- && staticField.accessFlags.isEnum()
- && staticField.accessFlags.isFinal();
+ static boolean isEnumField(DexEncodedField staticField, DexProgramClass enumClass) {
+ return staticField.getType() == enumClass.getType()
+ && staticField.isEnum()
+ && staticField.isFinal();
}
static boolean matchesValuesField(
- DexEncodedField staticField, DexType enumType, DexItemFactory factory) {
- return staticField.getReference().type.isArrayType()
- && staticField.getReference().type.toArrayElementType(factory) == enumType
- && staticField.accessFlags.isSynthetic()
- && staticField.accessFlags.isFinal()
- && staticField.getReference().name == factory.enumValuesFieldName;
+ DexEncodedField staticField, DexProgramClass enumClass, DexItemFactory factory) {
+ return staticField.getType().isArrayType()
+ && staticField.getType().toArrayElementType(factory) == enumClass.getType()
+ && staticField.isSynthetic()
+ && staticField.isFinal()
+ && staticField.getName() == factory.enumValuesFieldName;
}
private void removeEnumsInAnnotations() {
@@ -116,8 +129,9 @@
|| appView.options().testing.allowInjectedAnnotationMethods;
DexType valueType = method.returnType().toBaseType(appView.dexItemFactory());
if (enumToUnboxCandidates.isCandidate(valueType)) {
- enumUnboxer.reportFailure(valueType, Reason.ANNOTATION);
- enumToUnboxCandidates.removeCandidate(valueType);
+ if (!enumUnboxer.reportFailure(valueType, Reason.ANNOTATION)) {
+ enumToUnboxCandidates.removeCandidate(valueType);
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
index 947cc6d..7d85b8d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -26,6 +26,10 @@
enumTypeToInfo.put(enumClass.type, new EnumUnboxingCandidateInfo(enumClass));
}
+ public void removeCandidate(DexProgramClass enumClass) {
+ removeCandidate(enumClass.getType());
+ }
+
public void removeCandidate(DexType enumType) {
enumTypeToInfo.remove(enumType);
}
@@ -77,11 +81,11 @@
info.addMethodDependency(programMethod);
}
- public void addRequiredEnumInstanceFieldData(DexType enumType, DexField field) {
+ public void addRequiredEnumInstanceFieldData(DexProgramClass enumClass, DexField field) {
// The enumType may be removed concurrently map from enumTypeToInfo. It means in that
// case the enum is no longer a candidate, and dependencies don't need to be recorded
// anymore.
- EnumUnboxingCandidateInfo info = enumTypeToInfo.get(enumType);
+ EnumUnboxingCandidateInfo info = enumTypeToInfo.get(enumClass.getType());
if (info == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
index 2cd2098..429bdae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -4,46 +4,236 @@
package com.android.tools.r8.ir.optimize.enums.eligibility;
-public class Reason {
- public static final Reason ELIGIBLE = new Reason("ELIGIBLE");
- public static final Reason ACCESSIBILITY = new Reason("ACCESSIBILITY");
- public static final Reason ANNOTATION = new Reason("ANNOTATION");
- public static final Reason PINNED = new Reason("PINNED");
- public static final Reason DOWN_CAST = new Reason("DOWN_CAST");
- public static final Reason SUBTYPES = new Reason("SUBTYPES");
- public static final Reason MANY_INSTANCE_FIELDS = new Reason("MANY_INSTANCE_FIELDS");
- public static final Reason GENERIC_INVOKE = new Reason("GENERIC_INVOKE");
- public static final Reason DEFAULT_METHOD_INVOKE = new Reason("DEFAULT_METHOD_INVOKE");
- public static final Reason UNEXPECTED_STATIC_FIELD = new Reason("UNEXPECTED_STATIC_FIELD");
- public static final Reason UNRESOLVABLE_FIELD = new Reason("UNRESOLVABLE_FIELD");
- public static final Reason CONST_CLASS = new Reason("CONST_CLASS");
- public static final Reason INVALID_PHI = new Reason("INVALID_PHI");
- public static final Reason NO_INIT = new Reason("NO_INIT");
- public static final Reason INVALID_INIT = new Reason("INVALID_INIT");
- public static final Reason INVALID_CLINIT = new Reason("INVALID_CLINIT");
- public static final Reason INVALID_INVOKE = new Reason("INVALID_INVOKE");
- public static final Reason INVALID_INVOKE_ON_ARRAY = new Reason("INVALID_INVOKE_ON_ARRAY");
- public static final Reason IMPLICIT_UP_CAST_IN_RETURN = new Reason("IMPLICIT_UP_CAST_IN_RETURN");
- public static final Reason UNSUPPORTED_LIBRARY_CALL = new Reason("UNSUPPORTED_LIBRARY_CALL");
- public static final Reason MISSING_INSTANCE_FIELD_DATA =
- new Reason("MISSING_INSTANCE_FIELD_DATA");
- public static final Reason INVALID_FIELD_PUT = new Reason("INVALID_FIELD_PUT");
- public static final Reason INVALID_ARRAY_PUT = new Reason("INVALID_ARRAY_PUT");
- public static final Reason TYPE_MISMATCH_FIELD_PUT = new Reason("TYPE_MISMATCH_FIELD_PUT");
- public static final Reason INVALID_IF_TYPES = new Reason("INVALID_IF_TYPES");
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.ImmutableList;
+
+public abstract class Reason {
+ public static final Reason ELIGIBLE = new StringReason("ELIGIBLE");
+ public static final Reason ACCESSIBILITY = new StringReason("ACCESSIBILITY");
+ public static final Reason ANNOTATION = new StringReason("ANNOTATION");
+ public static final Reason PINNED = new StringReason("PINNED");
+ public static final Reason DOWN_CAST = new StringReason("DOWN_CAST");
+ public static final Reason SUBTYPES = new StringReason("SUBTYPES");
+ public static final Reason MANY_INSTANCE_FIELDS = new StringReason("MANY_INSTANCE_FIELDS");
+ public static final Reason DEFAULT_METHOD_INVOKE = new StringReason("DEFAULT_METHOD_INVOKE");
+ public static final Reason UNRESOLVABLE_FIELD = new StringReason("UNRESOLVABLE_FIELD");
+ public static final Reason CONST_CLASS = new StringReason("CONST_CLASS");
+ public static final Reason INVALID_PHI = new StringReason("INVALID_PHI");
+ public static final Reason NO_INIT = new StringReason("NO_INIT");
+ public static final Reason INVALID_INIT = new StringReason("INVALID_INIT");
+ public static final Reason INVALID_CLINIT = new StringReason("INVALID_CLINIT");
+ public static final Reason INVALID_INVOKE = new StringReason("INVALID_INVOKE");
+ public static final Reason INVALID_INVOKE_CLASSPATH =
+ new StringReason("INVALID_INVOKE_CLASSPATH");
+ public static final Reason INVALID_INVOKE_ON_ARRAY = new StringReason("INVALID_INVOKE_ON_ARRAY");
+ public static final Reason IMPLICIT_UP_CAST_IN_RETURN =
+ new StringReason("IMPLICIT_UP_CAST_IN_RETURN");
+ public static final Reason INVALID_FIELD_PUT = new StringReason("INVALID_FIELD_PUT");
+ public static final Reason INVALID_ARRAY_PUT = new StringReason("INVALID_ARRAY_PUT");
+ public static final Reason TYPE_MISMATCH_FIELD_PUT = new StringReason("TYPE_MISMATCH_FIELD_PUT");
+ public static final Reason INVALID_IF_TYPES = new StringReason("INVALID_IF_TYPES");
public static final Reason ENUM_METHOD_CALLED_WITH_NULL_RECEIVER =
- new Reason("ENUM_METHOD_CALLED_WITH_NULL_RECEIVER");
+ new StringReason("ENUM_METHOD_CALLED_WITH_NULL_RECEIVER");
public static final Reason OTHER_UNSUPPORTED_INSTRUCTION =
- new Reason("OTHER_UNSUPPORTED_INSTRUCTION");
+ new StringReason("OTHER_UNSUPPORTED_INSTRUCTION");
- private final String message;
-
- public Reason(String message) {
- this.message = message;
- }
+ public abstract Object getKind();
@Override
- public String toString() {
- return message;
+ public abstract String toString();
+
+ public static class StringReason extends Reason {
+
+ private final String message;
+
+ public StringReason(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public Object getKind() {
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return message;
+ }
+ }
+
+ public static class IllegalInvokeWithImpreciseParameterTypeReason extends Reason {
+
+ private final DexMethod invokedMethod;
+
+ public IllegalInvokeWithImpreciseParameterTypeReason(DexMethod invokedMethod) {
+ this.invokedMethod = invokedMethod;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "IllegalInvokeWithImpreciseParameterType(" + invokedMethod.toSourceString() + ")";
+ }
+ }
+
+ public static class MissingEnumStaticFieldValuesReason extends Reason {
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "MissingEnumStaticFieldValues";
+ }
+ }
+
+ public static class MissingContentsForEnumValuesArrayReason extends Reason {
+
+ private final DexField valuesField;
+
+ public MissingContentsForEnumValuesArrayReason(DexField valuesField) {
+ this.valuesField = valuesField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "MissingContentsForEnumValuesArray(" + valuesField.toSourceString() + ")";
+ }
+ }
+
+ public static class MissingInstanceFieldValueForEnumInstanceReason extends Reason {
+
+ private final DexField enumField;
+ private final int ordinal;
+ private final DexField instanceField;
+
+ public MissingInstanceFieldValueForEnumInstanceReason(
+ DexField enumField, DexField instanceField) {
+ this.enumField = enumField;
+ this.ordinal = -1;
+ this.instanceField = instanceField;
+ }
+
+ public MissingInstanceFieldValueForEnumInstanceReason(int ordinal, DexField instanceField) {
+ this.enumField = null;
+ this.ordinal = ordinal;
+ this.instanceField = instanceField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ if (enumField != null) {
+ return "MissingInstanceFieldValueForEnumInstance(enum field="
+ + enumField.toSourceString()
+ + ", instance field="
+ + instanceField.toSourceString()
+ + ")";
+ }
+ assert ordinal >= 0;
+ return "MissingInstanceFieldValueForEnumInstance(ordinal="
+ + ordinal
+ + ", instance field="
+ + instanceField.toSourceString()
+ + ")";
+ }
+ }
+
+ public static class MissingObjectStateForEnumInstanceReason extends Reason {
+
+ private final DexField enumField;
+
+ public MissingObjectStateForEnumInstanceReason(DexField enumField) {
+ this.enumField = enumField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "MissingObjectStateForEnumInstance(" + enumField + ")";
+ }
+ }
+
+ public static class UnsupportedInstanceFieldValueForEnumInstanceReason extends Reason {
+
+ private final int ordinal;
+ private final DexField instanceField;
+
+ public UnsupportedInstanceFieldValueForEnumInstanceReason(int ordinal, DexField instanceField) {
+ this.ordinal = ordinal;
+ this.instanceField = instanceField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "UnsupportedInstanceFieldValueForEnumInstance(ordinal="
+ + ordinal
+ + ", instance field="
+ + instanceField.toSourceString()
+ + ")";
+ }
+ }
+
+ public static class UnsupportedLibraryInvokeReason extends Reason {
+
+ private final DexMethod invokedMethod;
+
+ public UnsupportedLibraryInvokeReason(DexMethod invokedMethod) {
+ this.invokedMethod = invokedMethod;
+ }
+
+ @Override
+ public Object getKind() {
+ return ImmutableList.of(getClass(), invokedMethod);
+ }
+
+ @Override
+ public String toString() {
+ return "UnsupportedLibraryInvoke(" + invokedMethod.toSourceString() + ")";
+ }
+ }
+
+ public static class UnsupportedStaticFieldReason extends Reason {
+
+ private final DexField field;
+
+ public UnsupportedStaticFieldReason(DexField field) {
+ this.field = field;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "UnsupportedStaticField(" + field.toSourceString() + ")";
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index 41ee658..b4141c6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.info;
+import static java.util.Collections.emptySet;
+
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
@@ -13,7 +15,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.function.Function;
+import java.util.Set;
/**
* Optimization info for fields.
@@ -35,13 +37,20 @@
private TypeElement dynamicUpperBoundType = null;
public MutableFieldOptimizationInfo fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+ return fixupClassTypeReferences(appView, lens, emptySet());
+ }
+
+ public MutableFieldOptimizationInfo fixupClassTypeReferences(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ GraphLens lens,
+ Set<DexType> prunedTypes) {
if (dynamicUpperBoundType != null) {
- dynamicUpperBoundType = dynamicUpperBoundType.fixupClassTypeReferences(mapping, appView);
+ dynamicUpperBoundType = dynamicUpperBoundType.rewrittenWithLens(appView, lens, prunedTypes);
}
if (dynamicLowerBoundType != null) {
TypeElement dynamicLowerBoundType =
- this.dynamicLowerBoundType.fixupClassTypeReferences(mapping, appView);
+ this.dynamicLowerBoundType.rewrittenWithLens(appView, lens);
if (dynamicLowerBoundType.isClassType()) {
this.dynamicLowerBoundType = dynamicLowerBoundType.asClassType();
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
index 46cff7b..b384693 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.conversion.FieldOptimizationFeedback;
import com.android.tools.r8.ir.conversion.MethodOptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
@@ -20,19 +21,41 @@
public interface OptimizationInfoFixer {
- void fixup(DexEncodedField field);
+ void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo);
- void fixup(DexEncodedMethod method);
+ void fixup(DexEncodedMethod method, UpdatableMethodOptimizationInfo optimizationInfo);
}
public void fixupOptimizationInfos(
AppView<?> appView, ExecutorService executorService, OptimizationInfoFixer fixer)
throws ExecutionException {
+ fixupOptimizationInfos(appView.appInfo().classes(), executorService, fixer);
+ }
+
+ public void fixupOptimizationInfos(
+ Iterable<DexProgramClass> classes,
+ ExecutorService executorService,
+ OptimizationInfoFixer fixer)
+ throws ExecutionException {
ThreadUtils.processItems(
- appView.appInfo().classes(),
+ classes,
clazz -> {
- clazz.fields().forEach(fixer::fixup);
- clazz.methods().forEach(fixer::fixup);
+ for (DexEncodedField field : clazz.fields()) {
+ FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
+ if (optimizationInfo.isMutableFieldOptimizationInfo()) {
+ fixer.fixup(field, optimizationInfo.asMutableFieldOptimizationInfo());
+ } else {
+ assert optimizationInfo.isDefaultFieldOptimizationInfo();
+ }
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
+ if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
+ fixer.fixup(method, optimizationInfo.asUpdatableMethodOptimizationInfo());
+ } else {
+ assert optimizationInfo.isDefaultMethodOptimizationInfo();
+ }
+ }
},
executorService);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index d6bdd6b..03eee35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.info;
+import static java.util.Collections.emptySet;
+
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
@@ -23,7 +25,6 @@
import com.android.tools.r8.utils.BooleanUtils;
import java.util.BitSet;
import java.util.Set;
-import java.util.function.Function;
public class UpdatableMethodOptimizationInfo extends MethodOptimizationInfo {
@@ -147,14 +148,21 @@
}
public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+ return fixupClassTypeReferences(appView, lens, emptySet());
+ }
+
+ public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ GraphLens lens,
+ Set<DexType> prunedTypes) {
if (returnsObjectWithUpperBoundType != null) {
returnsObjectWithUpperBoundType =
- returnsObjectWithUpperBoundType.fixupClassTypeReferences(mapping, appView);
+ returnsObjectWithUpperBoundType.rewrittenWithLens(appView, lens, prunedTypes);
}
if (returnsObjectWithLowerBoundType != null) {
TypeElement returnsObjectWithLowerBoundType =
- this.returnsObjectWithLowerBoundType.fixupClassTypeReferences(mapping, appView);
+ this.returnsObjectWithLowerBoundType.rewrittenWithLens(appView, lens, prunedTypes);
if (returnsObjectWithLowerBoundType.isClassType()) {
this.returnsObjectWithLowerBoundType = returnsObjectWithLowerBoundType.asClassType();
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index 262671f..708c84a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -62,12 +62,9 @@
}
return new InstanceFieldTypeInitializationInfo(
dynamicLowerBoundType != null
- ? dynamicLowerBoundType
- .fixupClassTypeReferences(lens::lookupType, appView.withClassHierarchy())
- .asClassType()
+ ? dynamicLowerBoundType.rewrittenWithLens(appView, lens).asClassType()
: null,
- dynamicUpperBoundType.fixupClassTypeReferences(
- lens::lookupType, appView.withClassHierarchy()));
+ dynamicUpperBoundType.rewrittenWithLens(appView, lens));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 7761bfa..f600a41 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -519,8 +520,8 @@
}
// Allow single assignment to a singleton field.
StaticPut staticPut = instruction.asStaticPut();
- DexEncodedField fieldAccessed = appView.appInfo().lookupStaticTarget(staticPut.getField());
- return fieldAccessed == info.singletonField;
+ DexClassAndField fieldAccessed = appView.appInfo().lookupStaticTarget(staticPut.getField());
+ return fieldAccessed != null && fieldAccessed.getDefinition() == info.singletonField;
}
// Only allow a very trivial pattern: load the singleton field and return it, which looks like:
@@ -536,8 +537,8 @@
for (Instruction instr : code.instructions()) {
if (instr.isStaticGet()) {
staticGet = instr.asStaticGet();
- DexEncodedField fieldAccessed = appView.appInfo().lookupStaticTarget(staticGet.getField());
- if (fieldAccessed != info.singletonField) {
+ DexClassAndField fieldAccessed = appView.appInfo().lookupStaticTarget(staticGet.getField());
+ if (fieldAccessed == null || fieldAccessed.getDefinition() != info.singletonField) {
return null;
}
instructions.add(instr);
@@ -568,7 +569,8 @@
return null;
}
- assert candidateInfo.singletonField == appView.appInfo().lookupStaticTarget(field)
+ assert candidateInfo.singletonField
+ == appView.appInfo().lookupStaticTarget(field).getDefinition()
: "Added reference after collectCandidates(...)?";
Value singletonValue = staticGet.dest();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
index 8c6c2b8..256ee1e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
@@ -15,6 +15,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
// This is a parser for the Kotlin-generated source debug extensions, which is a stratified map.
// The kotlin-parser for this data is can be found here in the kotlin compiler:
@@ -80,13 +81,21 @@
int linesInBlock,
ThrowingConsumer<List<String>, KotlinSourceDebugExtensionParserException> callback)
throws IOException, KotlinSourceDebugExtensionParserException {
- if (terminator.equals(readLine)) {
+ readUntil(terminator::equals, linesInBlock, callback);
+ }
+
+ void readUntil(
+ Predicate<String> terminator,
+ int linesInBlock,
+ ThrowingConsumer<List<String>, KotlinSourceDebugExtensionParserException> callback)
+ throws IOException, KotlinSourceDebugExtensionParserException {
+ if (terminator.test(readLine)) {
return;
}
List<String> readStrings = new ArrayList<>();
readStrings.add(readNextLine());
int linesLeft = linesInBlock;
- while (!terminator.equals(readLine) && !isEOF()) {
+ while (!terminator.test(readLine) && !isEOF()) {
if (linesLeft == 1) {
assert readStrings.size() == linesInBlock;
callback.accept(readStrings);
@@ -97,7 +106,7 @@
}
readStrings.add(readNextLine());
}
- if (readStrings.size() > 0 && !readStrings.get(0).equals(terminator)) {
+ if (readStrings.size() > 0 && !terminator.test(readStrings.get(0))) {
throw new KotlinSourceDebugExtensionParserException(
"Block size does not match linesInBlock = " + linesInBlock);
}
@@ -125,7 +134,7 @@
// *L
// <from>#<file>,<to>:<debug-line-position>
// <from>#<file>:<debug-line-position>
- // *E
+ // *E <-- This is an error in versions prior to kotlin 1.5 and is not present in kotlin 1.5.
// *S KotlinDebug
// ***
// *E
@@ -157,10 +166,12 @@
// or
// <from>#<file>:<debug-line-position>
reader.readUntil(
- SMAP_END_IDENTIFIER, 1, block -> addDebugEntryToBuilder(block.get(0), builder));
+ line -> line.equals(SMAP_END_IDENTIFIER) || line.startsWith(SMAP_SECTION_START),
+ 1,
+ block -> addDebugEntryToBuilder(block.get(0), builder));
- // Ensure that we read the end section *E.
- if (reader.isEOF()) {
+ // Ensure that we read the end section and it is terminated.
+ if (reader.isEOF() && !reader.readLine.equals(SMAP_END_IDENTIFIER)) {
throw new KotlinSourceDebugExtensionParserException(
"Unexpected EOF when parsing SMAP debug entries");
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 0583a2f..26742bd 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.naming.ClassNameMapper.MissingFileAction.MISSING_FILE_IS_ERROR;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
-import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexField;
@@ -15,16 +14,12 @@
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.naming.mappinginformation.MappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
import com.android.tools.r8.position.Position;
-import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharSource;
import java.io.BufferedReader;
@@ -32,14 +27,11 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.function.Consumer;
public class ClassNameMapper implements ProguardMap {
@@ -50,7 +42,6 @@
public static class Builder extends ProguardMap.Builder {
private final Map<String, ClassNamingForNameMapper.Builder> mapping = new HashMap<>();
- private final Map<ScopeReference, List<MappingInformation>> scopedMappingInfo = new HashMap<>();
private Builder() {
@@ -66,42 +57,11 @@
}
@Override
- public void addMappingInformation(
- ScopeReference reference,
- MappingInformation info,
- Consumer<MappingInformation> onProhibitedAddition) {
- List<MappingInformation> additionalMappings =
- scopedMappingInfo.computeIfAbsent(reference, ignoreArgument(ArrayList::new));
- for (MappingInformation existing : additionalMappings) {
- if (!existing.allowOther(info)) {
- onProhibitedAddition.accept(existing);
- }
- }
- additionalMappings.add(info);
- }
-
- @Override
public ClassNameMapper build() {
- return new ClassNameMapper(buildClassNameMappings(), buildScopedMappingInfo());
- }
-
- private ImmutableMap<ScopeReference, ImmutableList<MappingInformation>>
- buildScopedMappingInfo() {
- ImmutableMap.Builder<ScopeReference, ImmutableList<MappingInformation>> builder =
- ImmutableMap.builder();
- scopedMappingInfo.forEach((ref, infos) -> builder.put(ref, ImmutableList.copyOf(infos)));
- return builder.build();
+ return new ClassNameMapper(buildClassNameMappings());
}
private ImmutableMap<String, ClassNamingForNameMapper> buildClassNameMappings() {
- // Ensure that all scoped references have at least the identity in the final mapping.
- for (ScopeReference reference : scopedMappingInfo.keySet()) {
- if (!reference.isGlobalScope()) {
- mapping.computeIfAbsent(
- reference.getHolderReference().getTypeName(),
- t -> ClassNamingForNameMapper.builder(t, t));
- }
- }
ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
mapping.forEach(
@@ -166,26 +126,17 @@
}
private final ImmutableMap<String, ClassNamingForNameMapper> classNameMappings;
- private final ImmutableMap<ScopeReference, ImmutableList<MappingInformation>>
- additionalMappingInfo;
private BiMapContainer<String, String> nameMapping;
private final Map<Signature, Signature> signatureMap = new HashMap<>();
- private ClassNameMapper(
- ImmutableMap<String, ClassNamingForNameMapper> classNameMappings,
- ImmutableMap<ScopeReference, ImmutableList<MappingInformation>> additionalMappingInfo) {
+ private ClassNameMapper(ImmutableMap<String, ClassNamingForNameMapper> classNameMappings) {
this.classNameMappings = classNameMappings;
- this.additionalMappingInfo = additionalMappingInfo;
}
public Map<String, ClassNamingForNameMapper> getClassNameMappings() {
return classNameMappings;
}
- public List<MappingInformation> getAdditionalMappingInfo(ScopeReference reference) {
- return additionalMappingInfo.getOrDefault(reference, ImmutableList.of());
- }
-
private Signature canonicalizeSignature(Signature signature) {
Signature result = signatureMap.get(signature);
if (result != null) {
@@ -255,7 +206,7 @@
ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
classNameMappings.forEach(builder::put);
- return new ClassNameMapper(builder.build(), additionalMappingInfo);
+ return new ClassNameMapper(builder.build());
}
public boolean verifyIsSorted() {
@@ -277,10 +228,7 @@
// deterministic (and easy to navigate manually).
assert verifyIsSorted();
for (ClassNamingForNameMapper naming : getClassNameMappings().values()) {
- List<MappingInformation> additionalMappingInfo =
- getAdditionalMappingInfo(
- ScopeReference.fromClassReference(Reference.classFromTypeName(naming.renamedName)));
- naming.write(consumer, additionalMappingInfo);
+ naming.write(consumer);
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
index 429ed45..c9de8ab 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -3,8 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.function.Consumer;
/**
* Stores name information for a class.
@@ -20,11 +23,14 @@
public abstract ClassNaming build();
/** This is an optional method, may be implemented as no-op */
- public abstract void addMappedRange(
+ public abstract MappedRange addMappedRange(
Range obfuscatedRange,
MemberNaming.MethodSignature originalSignature,
Object originalRange,
String obfuscatedName);
+
+ public abstract void addMappingInformation(
+ MappingInformation info, Consumer<MappingInformation> onProhibitedAddition);
}
MemberNaming lookup(Signature renamedSignature);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
index c8e2f2b..a222acb 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
@@ -6,10 +6,12 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -20,6 +22,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
/**
* Stores name information for a class.
@@ -82,12 +85,19 @@
}
@Override
- /** No-op */
- public void addMappedRange(
+ public MappedRange addMappedRange(
Range obfuscatedRange,
MemberNaming.MethodSignature originalSignature,
Object originalRange,
- String obfuscatedName) {}
+ String obfuscatedName) {
+ return null;
+ }
+
+ @Override
+ public void addMappingInformation(
+ MappingInformation info, Consumer<MappingInformation> onProhibitedAddition) {
+ // Intentionally empty.
+ }
}
static Builder builder(
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 523184b..1d837b3 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.Consumer;
/**
* Stores name information for a class.
@@ -36,6 +37,7 @@
private final Map<FieldSignature, MemberNaming> fieldMembers = Maps.newHashMap();
private final Map<String, List<MappedRange>> mappedRangesByName = Maps.newHashMap();
private final Map<String, List<MemberNaming>> mappedFieldNamingsByName = Maps.newHashMap();
+ private final List<MappingInformation> additionalMappingInfo = new ArrayList<>();
private Builder(String renamedName, String originalName) {
this.originalName = originalName;
@@ -69,19 +71,38 @@
}
return new ClassNamingForNameMapper(
- renamedName, originalName, methodMembers, fieldMembers, map, mappedFieldNamingsByName);
+ renamedName,
+ originalName,
+ methodMembers,
+ fieldMembers,
+ map,
+ mappedFieldNamingsByName,
+ additionalMappingInfo);
}
/** The parameters are forwarded to MappedRange constructor, see explanation there. */
@Override
- public void addMappedRange(
+ public MappedRange addMappedRange(
Range minifiedRange,
MemberNaming.MethodSignature originalSignature,
Object originalRange,
String renamedName) {
- mappedRangesByName
- .computeIfAbsent(renamedName, k -> new ArrayList<>())
- .add(new MappedRange(minifiedRange, originalSignature, originalRange, renamedName));
+ MappedRange range =
+ new MappedRange(minifiedRange, originalSignature, originalRange, renamedName);
+ mappedRangesByName.computeIfAbsent(renamedName, k -> new ArrayList<>()).add(range);
+ return range;
+ }
+
+ @Override
+ public void addMappingInformation(
+ MappingInformation info, Consumer<MappingInformation> onProhibitedAddition) {
+ for (MappingInformation existing : additionalMappingInfo) {
+ if (!existing.allowOther(info)) {
+ onProhibitedAddition.accept(existing);
+ return;
+ }
+ }
+ additionalMappingInfo.add(info);
}
}
@@ -200,19 +221,27 @@
public final Map<String, List<MemberNaming>> mappedFieldNamingsByName;
+ private final List<MappingInformation> additionalMappingInfo;
+
private ClassNamingForNameMapper(
String renamedName,
String originalName,
Map<MethodSignature, MemberNaming> methodMembers,
Map<FieldSignature, MemberNaming> fieldMembers,
Map<String, MappedRangesOfName> mappedRangesByRenamedName,
- Map<String, List<MemberNaming>> mappedFieldNamingsByName) {
+ Map<String, List<MemberNaming>> mappedFieldNamingsByName,
+ List<MappingInformation> additionalMappingInfo) {
this.renamedName = renamedName;
this.originalName = originalName;
this.methodMembers = ImmutableMap.copyOf(methodMembers);
this.fieldMembers = ImmutableMap.copyOf(fieldMembers);
this.mappedRangesByRenamedName = mappedRangesByRenamedName;
this.mappedFieldNamingsByName = mappedFieldNamingsByName;
+ this.additionalMappingInfo = additionalMappingInfo;
+ }
+
+ public List<MappingInformation> getAdditionalMappingInfo() {
+ return Collections.unmodifiableList(additionalMappingInfo);
}
public MappedRangesOfName getMappedRangesForRenamedName(String renamedName) {
@@ -297,7 +326,7 @@
return methodMembers.values();
}
- void write(ChainableStringConsumer consumer, List<MappingInformation> additionalMappingInfo) {
+ void write(ChainableStringConsumer consumer) {
consumer.accept(originalName).accept(" -> ").accept(renamedName).accept(":\n");
// Print all additional mapping information.
@@ -315,13 +344,16 @@
mappedRangesSorted.sort(Comparator.comparingInt(range -> range.sequenceNumber));
for (MappedRange range : mappedRangesSorted) {
consumer.accept(" ").accept(range.toString()).accept("\n");
+ for (MappingInformation info : range.additionalMappingInfo) {
+ consumer.accept(" # ").accept(info.serialize()).accept("\n");
+ }
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
- write(ChainableStringConsumer.wrap(builder::append), Collections.emptyList());
+ write(ChainableStringConsumer.wrap(builder::append));
return builder.toString();
}
@@ -390,6 +422,8 @@
*/
private final int sequenceNumber = getNextSequenceNumber();
+ private List<MappingInformation> additionalMappingInfo = new ArrayList<>();
+
private MappedRange(
Range minifiedRange, MethodSignature signature, Object originalRange, String renamedName) {
@@ -403,6 +437,26 @@
this.renamedName = renamedName;
}
+ public void addMappingInformation(
+ MappingInformation info, Consumer<MappingInformation> onProhibitedAddition) {
+ for (MappingInformation existing : additionalMappingInfo) {
+ if (!existing.allowOther(info)) {
+ onProhibitedAddition.accept(existing);
+ return;
+ }
+ }
+ additionalMappingInfo.add(info);
+ }
+
+ public boolean isCompilerSynthesized() {
+ for (MappingInformation info : additionalMappingInfo) {
+ if (info.isCompilerSynthesizedMappingInformation()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public int getOriginalLineNumber(int lineNumberAfterMinification) {
if (minifiedRange == null) {
// General mapping without concrete line numbers: "a() -> b"
@@ -479,6 +533,10 @@
result = 31 * result + renamedName.hashCode();
return result;
}
+
+ public List<MappingInformation> getAdditionalMappingInfo() {
+ return additionalMappingInfo;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 0f50ecb..6ca2736 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -9,16 +9,24 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.Function;
/**
@@ -87,8 +95,10 @@
// from the method name minifier to the interface method name minifier.
class State {
- void putRenaming(DexEncodedMethod key, DexString value) {
- renaming.put(key.getReference(), value);
+ void putRenaming(DexEncodedMethod key, DexString newName) {
+ if (newName != key.getName()) {
+ renaming.put(key.getReference(), newName);
+ }
}
MethodReservationState<?> getReservationState(DexType type) {
@@ -173,7 +183,9 @@
}
}
- MethodRenaming computeRenaming(Iterable<DexClass> interfaces, Timing timing) {
+ MethodRenaming computeRenaming(
+ Iterable<DexClass> interfaces, ExecutorService executorService, Timing timing)
+ throws ExecutionException {
// Phase 1: Reserve all the names that need to be kept and allocate linked state in the
// library part.
timing.begin("Phase 1");
@@ -193,6 +205,9 @@
timing.begin("Phase 4");
assignNamesToClassesMethods(appView.dexItemFactory().objectType, rootNamingState);
timing.end();
+ timing.begin("Phase 5: non-rebound references");
+ renameNonReboundReferences(executorService);
+ timing.end();
return new MethodRenaming(renaming);
}
@@ -322,6 +337,60 @@
return reservationState;
}
+ private void renameNonReboundReferences(ExecutorService executorService)
+ throws ExecutionException {
+ Map<DexMethod, DexString> nonReboundRenamings = new ConcurrentHashMap<>();
+ MethodAccessInfoCollection methodAccessInfoCollection =
+ appView.appInfo().getMethodAccessInfoCollection();
+ ThreadUtils.processItems(
+ methodAccessInfoCollection::forEachMethodReference,
+ method -> renameNonReboundMethodReference(method, nonReboundRenamings),
+ executorService);
+ renaming.putAll(nonReboundRenamings);
+ }
+
+ private void renameNonReboundMethodReference(
+ DexMethod method, Map<DexMethod, DexString> nonReboundRenamings) {
+ if (method.getHolderType().isArrayType()) {
+ return;
+ }
+
+ DexClass holder = appView.contextIndependentDefinitionFor(method.getHolderType());
+ if (holder == null) {
+ return;
+ }
+
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethodOn(holder, method);
+ if (resolutionResult.isSingleResolution()) {
+ DexEncodedMethod resolvedMethod = resolutionResult.getSingleTarget();
+ if (resolvedMethod.getReference() == method) {
+ return;
+ }
+
+ DexString newName = renaming.get(resolvedMethod.getReference());
+ if (newName != null) {
+ assert newName != resolvedMethod.getName();
+ nonReboundRenamings.put(method, newName);
+ }
+ return;
+ }
+
+ // If resolution fails, the method must be renamed consistently with the targets that give rise
+ // to the failure.
+ assert resolutionResult.isFailedResolution();
+
+ List<DexEncodedMethod> targets = new ArrayList<>();
+ resolutionResult.asFailedResolution().forEachFailureDependency(targets::add);
+ if (!targets.isEmpty()) {
+ DexString newName = renaming.get(targets.get(0).getReference());
+ assert targets.stream().allMatch(target -> renaming.get(target.getReference()) == newName);
+ if (newName != null) {
+ assert newName != targets.get(0).getName();
+ nonReboundRenamings.put(method, newName);
+ }
+ }
+ }
+
// Shuffles the given methods if assertions are enabled and deterministic debugging is disabled.
// Used to ensure that the generated output is deterministic.
private static Iterable<DexEncodedMethod> shuffleMethods(
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index a51edde..c44d311 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -20,9 +20,7 @@
import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
-import java.util.ArrayList;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
class MinifiedRenaming extends NonIdentityNamingLens {
@@ -89,36 +87,12 @@
@Override
public DexString lookupName(DexMethod method) {
- DexString renamed = renaming.get(method);
- if (renamed != null) {
- return renamed;
- }
- // If the method does not have a direct renaming, return the resolutions mapping.
- ResolutionResult resolutionResult = appView.appInfo().unsafeResolveMethodDueToDexFormat(method);
- if (resolutionResult.isSingleResolution()) {
- return renaming.getOrDefault(resolutionResult.getSingleTarget().getReference(), method.name);
- }
- // If resolution fails, the method must be renamed consistently with the targets that give rise
- // to the failure.
- if (resolutionResult.isFailedResolution()) {
- List<DexEncodedMethod> targets = new ArrayList<>();
- resolutionResult.asFailedResolution().forEachFailureDependency(targets::add);
- if (!targets.isEmpty()) {
- DexString firstRename = renaming.get(targets.get(0).getReference());
- assert targets.stream()
- .allMatch(target -> renaming.get(target.getReference()) == firstRename);
- if (firstRename != null) {
- return firstRename;
- }
- }
- }
- // If no renaming can be found the default is the methods name.
- return method.name;
+ return renaming.getOrDefault(method, method.getName());
}
@Override
public DexString lookupName(DexField field) {
- return renaming.getOrDefault(field, field.name);
+ return renaming.getOrDefault(field, field.getName());
}
/**
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index c5b8ced..e7106a6 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -67,7 +67,7 @@
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
new MethodNameMinifier(appView, subtypingInfo, minifyMembers)
- .computeRenaming(interfaces, timing);
+ .computeRenaming(interfaces, executorService, timing);
timing.end();
assert new MinifiedRenaming(appView, classRenaming, methodRenaming, FieldRenaming.empty())
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMap.java b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
index 94615a0..221f064 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMap.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
@@ -4,10 +4,7 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.naming.mappinginformation.MappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
import com.android.tools.r8.position.Position;
-import java.util.function.Consumer;
public interface ProguardMap {
@@ -16,11 +13,6 @@
String renamedName, String originalName, Position position);
abstract ProguardMap build();
-
- abstract void addMappingInformation(
- ScopeReference scope,
- MappingInformation MappingInformation,
- Consumer<MappingInformation> onProhibitedAddition);
}
boolean hasMapping(DexType type);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 92a507b..74737eb 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -145,7 +145,7 @@
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
new MethodNameMinifier(appView, subtypingInfo, nameStrategy)
- .computeRenaming(interfaces, timing);
+ .computeRenaming(interfaces, executorService, timing);
// Amend the method renamings with the default interface methods.
methodRenaming.renaming.putAll(defaultInterfaceMethodImplementationNames);
methodRenaming.renaming.putAll(additionalMethodNamings);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 0efcb53..ae65b10 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
@@ -11,10 +12,7 @@
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
import com.android.tools.r8.naming.mappinginformation.MetaInfMappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference.ClassScopeReference;
import com.android.tools.r8.position.TextPosition;
-import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.gson.JsonObject;
@@ -25,7 +23,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* Parses a Proguard mapping file and produces mappings from obfuscated class names to the original
@@ -90,7 +88,6 @@
private int lineOffset = 0;
private String line;
private MapVersion version = MapVersion.MapVersionNone;
- private ScopeReference implicitSingletonScope = ScopeReference.globalScope();
private int peekCodePoint() {
return lineOffset < line.length() ? line.codePointAt(lineOffset) : '\n';
@@ -226,16 +223,12 @@
// Parsing of entries
private void parseClassMappings(ProguardMap.Builder mapBuilder) throws IOException {
- assert implicitSingletonScope == ScopeReference.globalScope();
while (hasLine()) {
skipWhitespace();
if (isCommentLineWithJsonBrace()) {
parseMappingInformation(
- (reference, info) -> {
- if (!reference.isGlobalScope()) {
- diagnosticsHandler.error(
- MappingInformationDiagnostics.invalidScopeFor(lineNo, reference, info));
- }
+ info -> {
+ assert info.isMetaInfMappingInformation();
});
// Skip reading the rest of the line.
lineOffset = line.length();
@@ -259,7 +252,6 @@
expect(':');
ClassNaming.Builder currentClassBuilder =
mapBuilder.classNamingBuilder(after, before, getPosition());
- implicitSingletonScope = new ClassScopeReference(Reference.classFromTypeName(after));
skipWhitespace();
if (nextLine()) {
parseMemberMappings(mapBuilder, currentClassBuilder);
@@ -267,20 +259,18 @@
}
}
- private void parseMappingInformation(
- BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
+ private void parseMappingInformation(Consumer<MappingInformation> onMappingInfo) {
MappingInformation.fromJsonObject(
version,
parseJsonInComment(),
diagnosticsHandler,
lineNo,
- implicitSingletonScope,
- (reference, info) -> {
+ info -> {
MetaInfMappingInformation generatorInfo = info.asMetaInfMappingInformation();
if (generatorInfo != null) {
version = generatorInfo.getMapVersion();
}
- onMappingInfo.accept(reference, info);
+ onMappingInfo.accept(info);
});
}
@@ -288,21 +278,33 @@
throws IOException {
MemberNaming lastAddedNaming = null;
MemberNaming activeMemberNaming = null;
+ MappedRange activeMappedRange = null;
Range previousMappedRange = null;
do {
Object originalRange = null;
Range mappedRange = null;
// Try to parse any information added in comments above member namings
if (isCommentLineWithJsonBrace()) {
+ final MemberNaming currentMember = activeMemberNaming;
+ final MappedRange currentRange = activeMappedRange;
parseMappingInformation(
- (reference, mappingInfo) ->
- mapBuilder.addMappingInformation(
- reference,
- mappingInfo,
+ info -> {
+ if (currentMember == null) {
+ classNamingBuilder.addMappingInformation(
+ info,
conflictingInfo ->
diagnosticsHandler.warning(
MappingInformationDiagnostics.notAllowedCombination(
- reference, mappingInfo, conflictingInfo, lineNo))));
+ info, conflictingInfo, lineNo)));
+ } else if (currentRange != null) {
+ currentRange.addMappingInformation(
+ info,
+ conflictingInfo ->
+ diagnosticsHandler.warning(
+ MappingInformationDiagnostics.notAllowedCombination(
+ info, conflictingInfo, lineNo)));
+ }
+ });
// Skip reading the rest of the line.
lineOffset = line.length();
continue;
@@ -344,8 +346,9 @@
String renamedName = parseMethodName();
if (signature instanceof MethodSignature) {
- classNamingBuilder.addMappedRange(
- mappedRange, (MethodSignature) signature, originalRange, renamedName);
+ activeMappedRange =
+ classNamingBuilder.addMappedRange(
+ mappedRange, (MethodSignature) signature, originalRange, renamedName);
}
assert mappedRange == null || signature instanceof MethodSignature;
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 6516e22..73ae362 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -103,10 +103,7 @@
// Turn off linting of the mapping file in some build systems.
builder.append("# common_typos_disable" + "\n");
// Emit the R8 specific map-file version.
- MapVersion mapVersion =
- options.testing.enableExperimentalMapFileVersion
- ? MapVersion.MapVersionExperimental
- : MapVersion.STABLE;
+ MapVersion mapVersion = options.getMapFileVersion();
if (mapVersion.isGreaterThan(MapVersion.MapVersionNone)) {
builder
.append("# ")
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index e595d42..927cf6e 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -9,8 +9,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.naming.mappinginformation.MappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableMap;
@@ -25,7 +23,6 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Mappings read from the given ProGuard map.
@@ -64,14 +61,6 @@
}
@Override
- void addMappingInformation(
- ScopeReference scope,
- MappingInformation MappingInformation,
- Consumer<MappingInformation> onProhibitedAddition) {
- // Not needed.
- }
-
- @Override
SeedMapper build() {
reporter.failIfPendingErrors();
return new SeedMapper(ImmutableMap.copyOf(map), mappedToDescriptorNames, reporter);
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
index eb675c3..0c49e28 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
@@ -8,10 +8,11 @@
import com.android.tools.r8.naming.MapVersion;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public class CompilerSynthesizedMappingInformation extends MappingInformation {
+ public static final MapVersion SUPPORTED_VERSION = MapVersion.MapVersionExperimental;
public static final String ID = "com.android.tools.r8.synthesized";
public static class Builder {
@@ -21,6 +22,10 @@
}
}
+ public static boolean isSupported(MapVersion version) {
+ return version.isGreaterThanOrEqualTo(SUPPORTED_VERSION);
+ }
+
private CompilerSynthesizedMappingInformation() {}
public static Builder builder() {
@@ -59,16 +64,9 @@
JsonObject object,
DiagnosticsHandler diagnosticsHandler,
int lineNumber,
- ScopeReference implicitSingletonScope,
- BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
- if (version.isLessThan(MapVersion.MapVersionExperimental)) {
- return;
- }
- CompilerSynthesizedMappingInformation info = builder().build();
- for (ScopeReference reference :
- ScopedMappingInformation.deserializeScope(
- object, implicitSingletonScope, diagnosticsHandler, lineNumber, version)) {
- onMappingInfo.accept(reference, info);
+ Consumer<MappingInformation> onMappingInfo) {
+ if (isSupported(version)) {
+ onMappingInfo.accept(builder().build());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
index a9c7811..e4f3490 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
@@ -6,11 +6,10 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MapVersion;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference.ClassScopeReference;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public class FileNameInformation extends MappingInformation {
@@ -64,15 +63,12 @@
JsonObject object,
DiagnosticsHandler diagnosticsHandler,
int lineNumber,
- ScopeReference implicitSingletonScope,
- BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
- assert implicitSingletonScope instanceof ClassScopeReference;
+ Consumer<MappingInformation> onMappingInfo) {
try {
JsonElement fileName =
getJsonElementFromObject(object, diagnosticsHandler, lineNumber, FILE_NAME_KEY, ID);
if (fileName != null) {
- onMappingInfo.accept(
- implicitSingletonScope, new FileNameInformation(fileName.getAsString()));
+ onMappingInfo.accept(new FileNameInformation(fileName.getAsString()));
}
} catch (UnsupportedOperationException | IllegalStateException ignored) {
diagnosticsHandler.info(
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index 034b537..145080f 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.naming.MapVersion;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public abstract class MappingInformation {
@@ -49,8 +49,7 @@
JsonObject object,
DiagnosticsHandler diagnosticsHandler,
int lineNumber,
- ScopeReference implicitSingletonScope,
- BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
+ Consumer<MappingInformation> onMappingInfo) {
if (object == null) {
diagnosticsHandler.info(MappingInformationDiagnostics.notValidJson(lineNumber));
return;
@@ -73,7 +72,6 @@
object,
diagnosticsHandler,
lineNumber,
- implicitSingletonScope,
onMappingInfo);
}
@@ -83,8 +81,7 @@
JsonObject object,
DiagnosticsHandler diagnosticsHandler,
int lineNumber,
- ScopeReference implicitSingletonScope,
- BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
+ Consumer<MappingInformation> onMappingInfo) {
switch (id) {
case MetaInfMappingInformation.ID:
MetaInfMappingInformation.deserialize(
@@ -92,11 +89,11 @@
return;
case FileNameInformation.ID:
FileNameInformation.deserialize(
- version, object, diagnosticsHandler, lineNumber, implicitSingletonScope, onMappingInfo);
+ version, object, diagnosticsHandler, lineNumber, onMappingInfo);
return;
case CompilerSynthesizedMappingInformation.ID:
CompilerSynthesizedMappingInformation.deserialize(
- version, object, diagnosticsHandler, lineNumber, implicitSingletonScope, onMappingInfo);
+ version, object, diagnosticsHandler, lineNumber, onMappingInfo);
return;
default:
diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, id));
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java
index 261a544..1c4242a 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java
@@ -36,14 +36,6 @@
this.position = position;
}
- public static MappingInformationDiagnostics invalidScopeFor(
- int lineNumber, ScopeReference reference, MappingInformation info) {
- return new MappingInformationDiagnostics(
- String.format(
- "Cannot use scope %s for mapping information %s", reference.toString(), info.getId()),
- new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
- }
-
static MappingInformationDiagnostics noHandlerFor(int lineNumber, String value) {
return new MappingInformationDiagnostics(
String.format("Could not find a handler for %s", value),
@@ -102,14 +94,9 @@
}
public static MappingInformationDiagnostics notAllowedCombination(
- ScopeReference reference, MappingInformation one, MappingInformation other, int lineNumber) {
+ MappingInformation one, MappingInformation other, int lineNumber) {
return new MappingInformationDiagnostics(
- "The mapping '"
- + one.serialize()
- + "' is not allowed in combination with '"
- + other.serialize()
- + "' in the mapping for "
- + reference.toString(),
+ "The mapping '" + one + "' is not allowed in combination with '" + other + "'",
new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
index f3becb8..ea3824a 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.naming.MapVersion;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public class MetaInfMappingInformation extends MappingInformation {
@@ -57,22 +57,22 @@
}
public static void deserialize(
- MapVersion version,
+ MapVersion ignoredCurrentMapVersion,
JsonObject object,
DiagnosticsHandler diagnosticsHandler,
int lineNumber,
- BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
+ Consumer<MappingInformation> onMappingInfo) {
// Parsing the generator information must support parsing at all map versions as it itself is
// what establishes the version.
- String mapVersion = object.get(MAP_VERSION_KEY).getAsString();
- if (mapVersion == null) {
+ String mapVersionString = object.get(MAP_VERSION_KEY).getAsString();
+ if (mapVersionString == null) {
noKeyForObjectWithId(lineNumber, MAP_VERSION_KEY, MAPPING_ID_KEY, ID);
return;
}
- MapVersion mapVersion1 = MapVersion.fromName(mapVersion);
- if (mapVersion1 == null) {
+ MapVersion mapVersion = MapVersion.fromName(mapVersionString);
+ if (mapVersion == null) {
return;
}
- onMappingInfo.accept(ScopeReference.globalScope(), new MetaInfMappingInformation(mapVersion1));
+ onMappingInfo.accept(new MetaInfMappingInformation(mapVersion));
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopeReference.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopeReference.java
deleted file mode 100644
index 1ce5efa..0000000
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopeReference.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (c) 2021, 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.naming.mappinginformation;
-
-import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.DescriptorUtils;
-
-/** Abstraction for the items referenced in a scope. */
-public abstract class ScopeReference {
-
- public static ScopeReference globalScope() {
- return GlobalScopeReference.INSTANCE;
- }
-
- public static ScopeReference fromClassReference(ClassReference reference) {
- return new ClassScopeReference(reference);
- }
-
- // Method for reading in the serialized reference format.
- public static ScopeReference fromReferenceString(String referenceString) {
- if (DescriptorUtils.isClassDescriptor(referenceString)) {
- return fromClassReference(Reference.classFromDescriptor(referenceString));
- }
- throw new Unimplemented("No support for reference: " + referenceString);
- }
-
- public boolean isGlobalScope() {
- return equals(ScopeReference.globalScope());
- }
-
- public abstract String toReferenceString();
-
- public abstract ClassReference getHolderReference();
-
- @Override
- public abstract boolean equals(Object other);
-
- @Override
- public abstract int hashCode();
-
- @Override
- public String toString() {
- return toReferenceString();
- }
-
- public static class GlobalScopeReference extends ScopeReference {
- private static final GlobalScopeReference INSTANCE = new GlobalScopeReference();
-
- @Override
- public String toReferenceString() {
- throw new Unreachable();
- }
-
- @Override
- public String toString() {
- return "<global-scope>";
- }
-
- @Override
- public ClassReference getHolderReference() {
- throw new Unreachable();
- }
-
- @Override
- public boolean equals(Object other) {
- return this == other;
- }
-
- @Override
- public int hashCode() {
- return System.identityHashCode(this);
- }
- }
-
- public static class ClassScopeReference extends ScopeReference {
- private final ClassReference reference;
-
- public ClassScopeReference(ClassReference reference) {
- assert reference != null;
- this.reference = reference;
- }
-
- @Override
- public String toReferenceString() {
- return reference.getDescriptor();
- }
-
- @Override
- public ClassReference getHolderReference() {
- return reference;
- }
-
- @Override
- public boolean equals(Object other) {
- return other instanceof ClassScopeReference
- && reference.equals(((ClassScopeReference) other).reference);
- }
-
- @Override
- public int hashCode() {
- return reference.hashCode();
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopedMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopedMappingInformation.java
deleted file mode 100644
index 4a5b3d3..0000000
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopedMappingInformation.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2021, 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.naming.mappinginformation;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.naming.MapVersion;
-import com.google.common.collect.ImmutableList;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import java.util.Collections;
-import java.util.List;
-
-public abstract class ScopedMappingInformation {
-
- private static final MapVersion SCOPE_SUPPORTED = MapVersion.MapVersionExperimental;
- public static final String SCOPE_KEY = "scope";
-
- public static List<ScopeReference> deserializeScope(
- JsonObject object,
- ScopeReference implicitSingletonScope,
- DiagnosticsHandler diagnosticsHandler,
- int lineNumber,
- MapVersion version) {
- // Prior to support, the scope is always the implicit scope.
- if (version.isLessThan(SCOPE_SUPPORTED)) {
- return Collections.singletonList(implicitSingletonScope);
- }
- // If the scope key is absent, the implicit scope is assumed.
- JsonArray scopeArray = object.getAsJsonArray(SCOPE_KEY);
- if (scopeArray == null) {
- return Collections.singletonList(implicitSingletonScope);
- }
- ImmutableList.Builder<ScopeReference> builder = ImmutableList.builder();
- for (JsonElement element : scopeArray) {
- builder.add(ScopeReference.fromReferenceString(element.getAsString()));
- }
- return builder.build();
- }
-
- public static void serializeScope(
- JsonObject object,
- ScopeReference currentImplicitScope,
- List<ScopeReference> scopeReferences,
- MapVersion version) {
- assert !scopeReferences.isEmpty();
- // If emitting a non-experimental version the scope is always implicit.
- if (version.isLessThan(SCOPE_SUPPORTED)) {
- assert currentImplicitScope.equals(scopeReferences.get(0));
- return;
- }
- // If the scope matches the implicit scope don't add it explicitly.
- if (scopeReferences.size() == 1 && scopeReferences.get(0).equals(currentImplicitScope)) {
- return;
- }
- JsonArray scopeArray = new JsonArray();
- scopeReferences.forEach(ref -> scopeArray.add(ref.toReferenceString()));
- object.add(SCOPE_KEY, scopeArray);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/references/Reference.java b/src/main/java/com/android/tools/r8/references/Reference.java
index 3a30ec4..795c3bb 100644
--- a/src/main/java/com/android/tools/r8/references/Reference.java
+++ b/src/main/java/com/android/tools/r8/references/Reference.java
@@ -41,6 +41,10 @@
return descriptor.equals("V") ? null : typeFromDescriptor(descriptor);
}
+ public static TypeReference returnTypeFromTypeName(String typename) {
+ return typename.equals("void") ? null : typeFromTypeName(typename);
+ }
+
public static TypeReference typeFromDescriptor(String descriptor) {
switch (descriptor.charAt(0)) {
case 'L':
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
index 6070723..cb2ac8c 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
@@ -11,4 +11,6 @@
public interface RetraceElement<R extends RetraceResult<?>> {
R getRetraceResultContext();
+
+ boolean isCompilerSynthesized();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
index de298f0..5d8332e 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
@@ -16,7 +16,9 @@
RetraceClassElement getClassElement();
- void visitFrames(BiConsumer<RetracedMethodReference, Integer> consumer);
+ void visitAllFrames(BiConsumer<RetracedMethodReference, Integer> consumer);
+
+ void visitNonCompilerSynthesizedFrames(BiConsumer<RetracedMethodReference, Integer> consumer);
RetraceSourceFileResult retraceSourceFile(RetracedClassMemberReference frame, String sourceFile);
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index 825ac52..4502782 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -73,7 +73,10 @@
private void joinAmbiguousLines(
List<List<String>> retracedResult, Consumer<String> joinedConsumer) {
- assert !retracedResult.isEmpty();
+ if (retracedResult.isEmpty()) {
+ // The result is empty, likely it maps to compiler synthesized items.
+ return;
+ }
List<String> initialResult = retracedResult.get(0);
initialResult.forEach(joinedConsumer);
if (retracedResult.size() <= 1) {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
index 613e869..fd8ebb6 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
@@ -225,14 +224,25 @@
}
@Override
+ public boolean isCompilerSynthesized() {
+ if (classResult.mapper != null) {
+ for (MappingInformation info : classResult.mapper.getAdditionalMappingInfo()) {
+ if (info.isCompilerSynthesizedMappingInformation()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
public RetraceSourceFileResultImpl retraceSourceFile(String sourceFile) {
- for (MappingInformation info :
- classResult
- .getRetracerImpl()
- .getAdditionalMappingInfo(
- ScopeReference.fromClassReference(classResult.obfuscatedReference))) {
- if (info.isFileNameInformation()) {
- return new RetraceSourceFileResultImpl(info.asFileNameInformation().getFileName(), false);
+ if (classResult.mapper != null) {
+ for (MappingInformation info : classResult.mapper.getAdditionalMappingInfo()) {
+ if (info.isFileNameInformation()) {
+ return new RetraceSourceFileResultImpl(
+ info.asFileNameInformation().getFileName(), false);
+ }
}
}
return new RetraceSourceFileResultImpl(
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index 34c5206..331298e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.retrace.internal;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.references.Reference;
@@ -107,6 +108,11 @@
}
@Override
+ public boolean isCompilerSynthesized() {
+ throw new Unimplemented("b/172014416");
+ }
+
+ @Override
public boolean isUnknown() {
return fieldReference.isUnknown();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index 178bf9e..b5dae19 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.retrace.RetracedMethodReference;
import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -152,6 +153,23 @@
this.obfuscatedPosition = obfuscatedPosition;
}
+ private boolean isOuterMostFrameCompilerSynthesized() {
+ if (mappedRanges == null || mappedRanges.isEmpty()) {
+ return false;
+ }
+ return ListUtils.last(mappedRanges).isCompilerSynthesized();
+ }
+
+ /**
+ * Predicate determines if the *entire* frame is to be considered synthetic.
+ *
+ * <p>That is only true for a frame that has just one entry and that entry is synthetic.
+ */
+ @Override
+ public boolean isCompilerSynthesized() {
+ return getOuterFrames().isEmpty() && isOuterMostFrameCompilerSynthesized();
+ }
+
@Override
public RetraceFrameResult getRetraceResultContext() {
return retraceFrameResult;
@@ -173,7 +191,7 @@
}
@Override
- public void visitFrames(BiConsumer<RetracedMethodReference, Integer> consumer) {
+ public void visitAllFrames(BiConsumer<RetracedMethodReference, Integer> consumer) {
int counter = 0;
consumer.accept(getTopFrame(), counter++);
for (RetracedMethodReferenceImpl outerFrame : getOuterFrames()) {
@@ -182,6 +200,22 @@
}
@Override
+ public void visitNonCompilerSynthesizedFrames(
+ BiConsumer<RetracedMethodReference, Integer> consumer) {
+ int index = 0;
+ RetracedMethodReferenceImpl prev = getTopFrame();
+ for (RetracedMethodReferenceImpl next : getOuterFrames()) {
+ consumer.accept(prev, index++);
+ prev = next;
+ }
+ // We expect only the last frame, i.e., the outer-most caller to potentially be synthesized.
+ // If not include it too.
+ if (!isOuterMostFrameCompilerSynthesized()) {
+ consumer.accept(prev, index);
+ }
+ }
+
+ @Override
public RetraceSourceFileResult retraceSourceFile(
RetracedClassMemberReference frame, String sourceFile) {
return RetraceUtils.getSourceFile(
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 167390d..7e66ec1 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.retrace.internal;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
import com.android.tools.r8.references.MethodReference;
@@ -140,6 +141,11 @@
}
@Override
+ public boolean isCompilerSynthesized() {
+ throw new Unimplemented("b/172014416");
+ }
+
+ @Override
public boolean isUnknown() {
return methodReference.isUnknown();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
index d14df13..33a7d80 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
@@ -6,8 +6,6 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.mappinginformation.MappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -16,7 +14,6 @@
import com.android.tools.r8.retrace.ProguardMapProducer;
import com.android.tools.r8.retrace.Retracer;
import java.io.BufferedReader;
-import java.util.Collection;
/** A default implementation for the retrace api using the ClassNameMapper defined in R8. */
public class RetracerImpl implements Retracer {
@@ -72,8 +69,4 @@
public RetraceTypeResultImpl retraceType(TypeReference typeReference) {
return RetraceTypeResultImpl.create(typeReference, this);
}
-
- public Collection<MappingInformation> getAdditionalMappingInfo(ScopeReference reference) {
- return classNameMapper.getAdditionalMappingInfo(reference);
- }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index 937bf8e..793282a 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -99,15 +99,16 @@
frameElement -> {
List<RetraceStackTraceProxy<T, ST>> retracedProxies =
new ArrayList<>();
- frameElement.visitFrames(
+ frameElement.visitNonCompilerSynthesizedFrames(
(frame, index) -> {
+ boolean isTopFrame = retracedProxies.isEmpty();
RetraceStackTraceProxyImpl.Builder<T, ST> proxy =
RetraceStackTraceProxyImpl.builder(element)
.setRetracedClass(frame.getHolderClass())
.setRetracedMethod(frame)
.setAmbiguous(
- frameResult.isAmbiguous() && index == 0)
- .setTopFrame(index == 0);
+ frameResult.isAmbiguous() && isTopFrame)
+ .setTopFrame(isTopFrame);
if (element.hasLineNumber()) {
proxy.setLineNumber(
frame.getOriginalPositionOrDefault(
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 6974a61..ef2f149 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -17,6 +17,11 @@
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -29,6 +34,8 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.Predicate;
public class TreePruner {
@@ -56,15 +63,18 @@
: UnusedItemsPrinter.DONT_PRINT;
}
- public DirectMappedDexApplication run() {
+ public DirectMappedDexApplication run(ExecutorService executorService) throws ExecutionException {
DirectMappedDexApplication application = appView.appInfo().app().asDirect();
Timing timing = application.timing;
timing.begin("Pruning application...");
try {
DirectMappedDexApplication.Builder builder = removeUnused(application);
- return prunedTypes.isEmpty() && !appView.options().configurationDebugging
- ? application
- : builder.build();
+ DirectMappedDexApplication newApplication =
+ prunedTypes.isEmpty() && !appView.options().configurationDebugging
+ ? application
+ : builder.build();
+ fixupOptimizationInfo(newApplication, executorService);
+ return newApplication;
} finally {
timing.end();
}
@@ -92,7 +102,7 @@
&& !options.forceProguardCompatibility) {
// The class is only needed as a type but never instantiated. Make it abstract to reflect
// this.
- if (clazz.accessFlags.isFinal()) {
+ if (clazz.isFinal()) {
// We cannot mark this class abstract, as it is final (not supported on Android).
// However, this might extend an abstract class and we might have removed the
// corresponding methods in this class. This might happen if we only keep this
@@ -120,7 +130,7 @@
private void pruneUnusedInterfaces(DexProgramClass clazz) {
Set<DexType> reachableInterfaces = new LinkedHashSet<>();
- for (DexType type : clazz.interfaces.values) {
+ for (DexType type : clazz.getInterfaces()) {
retainReachableInterfacesFrom(type, reachableInterfaces);
}
if (!reachableInterfaces.isEmpty()) {
@@ -374,6 +384,33 @@
return Collections.unmodifiableCollection(methodsToKeepForConfigurationDebugging);
}
+ private void fixupOptimizationInfo(
+ DirectMappedDexApplication application, ExecutorService executorService)
+ throws ExecutionException {
+ // Clear the type elements cache due to redundant interface removal.
+ appView.dexItemFactory().clearTypeElementsCache();
+
+ OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
+ feedback.fixupOptimizationInfos(
+ application.classes(),
+ executorService,
+ new OptimizationInfoFixer() {
+ @Override
+ public void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) {
+ optimizationInfo.fixupClassTypeReferences(appView, appView.graphLens(), prunedTypes);
+ }
+
+ @Override
+ public void fixup(
+ DexEncodedMethod method, UpdatableMethodOptimizationInfo optimizationInfo) {
+ optimizationInfo.fixupClassTypeReferences(appView, appView.graphLens(), prunedTypes);
+ }
+ });
+
+ // Verify that the fixup did not lead to the caching of any elements.
+ assert appView.dexItemFactory().verifyNoCachedTypeElements();
+ }
+
private boolean verifyNoDeadFields(DexProgramClass clazz) {
for (DexEncodedField field : clazz.fields()) {
// Pinned field which type is never instantiated are always null, they are marked as dead
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index ed9d843..8f883ea 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -453,10 +453,10 @@
private static boolean shouldAnnotateSynthetics(InternalOptions options) {
// Only intermediate builds have annotated synthetics to allow later sharing.
- // This is currently also disabled on CF to CF desugaring to avoid missing class references to
- // the annotated classes.
+ // This is currently also disabled on non-L8 CF to CF desugaring to avoid missing class
+ // references to the annotated classes.
// TODO(b/147485959): Find an alternative encoding for synthetics to avoid missing-class refs.
- return options.intermediate && !options.cfToCfDesugar;
+ return options.intermediate && (!options.cfToCfDesugar || options.forceAnnotateSynthetics);
}
private <T extends SyntheticDefinition<?, T, ?>>
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 65c53d5..d6c93aa 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClasspathClass;
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.DexReference;
import com.android.tools.r8.graph.DexType;
@@ -360,18 +361,6 @@
return clazz;
}
- public DexClasspathClass createClasspathClass(
- SyntheticKind kind, DexType type, DexClass context, DexItemFactory factory) {
- // Obtain the outer synthesizing context in the case the context itself is synthetic.
- // This is to ensure a flat input-type -> synthetic-item mapping.
- SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
- SyntheticClasspathClassBuilder classBuilder =
- new SyntheticClasspathClassBuilder(type, outerContext, factory);
- DexClasspathClass clazz = classBuilder.build();
- addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
- return clazz;
- }
-
// TODO(b/172194101): Make this take a unique context.
public DexProgramClass createFixedClass(
SyntheticKind kind,
@@ -390,6 +379,91 @@
return clazz;
}
+ // This is a temporary API for migration to the hygienic synthetic, the classes created behave
+ // like a hygienic synthetic, but use the legacyType passed as parameter instead of the
+ // hygienic type.
+ public DexProgramClass ensureFixedClassWhileMigrating(
+ SyntheticKind kind,
+ DexType legacyType,
+ DexProgramClass context,
+ AppView<?> appView,
+ Consumer<SyntheticProgramClassBuilder> fn) {
+ synchronized (context) {
+ DexClass dexClass = appView.definitionFor(legacyType);
+ if (dexClass != null) {
+ assert dexClass.isProgramClass();
+ return dexClass.asProgramClass();
+ }
+ // Obtain the outer synthesizing context in the case the context itself is synthetic.
+ // This is to ensure a flat input-type -> synthetic-item mapping.
+ SynthesizingContext outerContext = getSynthesizingContext(context);
+ SyntheticProgramClassBuilder classBuilder =
+ new SyntheticProgramClassBuilder(legacyType, outerContext, appView.dexItemFactory());
+ fn.accept(classBuilder);
+ DexProgramClass clazz = classBuilder.build();
+ addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz));
+ return clazz;
+ }
+ }
+
+ public DexClasspathClass createFixedClasspathClass(
+ SyntheticKind kind, DexClasspathClass context, DexItemFactory factory) {
+ // Obtain the outer synthesizing context in the case the context itself is synthetic.
+ // This is to ensure a flat input-type -> synthetic-item mapping.
+ SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
+ DexType type = SyntheticNaming.createFixedType(kind, outerContext, factory);
+ SyntheticClasspathClassBuilder classBuilder =
+ new SyntheticClasspathClassBuilder(type, outerContext, factory);
+ DexClasspathClass clazz = classBuilder.build();
+ addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
+ return clazz;
+ }
+
+ // This is a temporary API for migration to the hygienic synthetic, the classes created behave
+ // like a hygienic synthetic, but use the legacyType passed as parameter instead of the
+ // hygienic type.
+ private DexClasspathClass ensureFixedClasspathClassWhileMigrating(
+ SyntheticKind kind, DexType legacyType, DexClass context, AppView<?> appView) {
+ synchronized (context) {
+ DexClass dexClass = appView.definitionFor(legacyType);
+ if (dexClass != null) {
+ assert dexClass.isClasspathClass();
+ return dexClass.asClasspathClass();
+ }
+ // Obtain the outer synthesizing context in the case the context itself is synthetic.
+ // This is to ensure a flat input-type -> synthetic-item mapping.
+ SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
+ SyntheticClasspathClassBuilder classBuilder =
+ new SyntheticClasspathClassBuilder(legacyType, outerContext, appView.dexItemFactory());
+ DexClasspathClass clazz = classBuilder.build();
+ addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
+ return clazz;
+ }
+ }
+
+ // This is a temporary API for migration to the hygienic synthetic, the classes created behave
+ // like a hygienic synthetic, but use the legacyType passed as parameter instead of the
+ // hygienic type.
+ public void ensureDirectMethodOnSyntheticClasspathClassWhileMigrating(
+ SyntheticKind kind,
+ DexType legacyType,
+ DexClass context,
+ AppView<?> appView,
+ DexMethod method,
+ Consumer<SyntheticMethodBuilder> builderConsumer) {
+ DexClasspathClass syntheticClass =
+ ensureFixedClasspathClassWhileMigrating(kind, legacyType, context, appView);
+ synchronized (syntheticClass) {
+ if (syntheticClass.lookupMethod(method) != null) {
+ return;
+ }
+ SyntheticMethodBuilder syntheticMethodBuilder =
+ new SyntheticMethodBuilder(appView.dexItemFactory(), syntheticClass.type);
+ builderConsumer.accept(syntheticMethodBuilder);
+ syntheticClass.addDirectMethod(syntheticMethodBuilder.build());
+ }
+ }
+
public DexProgramClass createFixedClassFromType(
SyntheticKind kind,
DexType contextType,
@@ -407,19 +481,6 @@
return clazz;
}
- public DexClasspathClass createFixedClasspathClass(
- SyntheticKind kind, DexClasspathClass context, DexItemFactory factory) {
- // Obtain the outer synthesizing context in the case the context itself is synthetic.
- // This is to ensure a flat input-type -> synthetic-item mapping.
- SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
- DexType type = SyntheticNaming.createFixedType(kind, outerContext, factory);
- SyntheticClasspathClassBuilder classBuilder =
- new SyntheticClasspathClassBuilder(type, outerContext, factory);
- DexClasspathClass clazz = classBuilder.build();
- addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
- return clazz;
- }
-
/** Create a single synthetic method item. */
public ProgramMethod createMethod(
SyntheticKind kind,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index ab4cd49..487cb53 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -7,9 +7,11 @@
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
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.DexProto;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -20,7 +22,8 @@
Code generate(DexMethod method);
}
- private final SyntheticClassBuilder<?, ?> parent;
+ private final DexItemFactory factory;
+ private final DexType holderType;
private DexString name = null;
private DexProto proto = null;
private CfVersion classFileVersion;
@@ -28,11 +31,17 @@
private MethodAccessFlags accessFlags = null;
SyntheticMethodBuilder(SyntheticClassBuilder<?, ?> parent) {
- this.parent = parent;
+ this.factory = parent.getFactory();
+ this.holderType = parent.getType();
+ }
+
+ SyntheticMethodBuilder(DexItemFactory factory, DexType holderType) {
+ this.factory = factory;
+ this.holderType = holderType;
}
public SyntheticMethodBuilder setName(String name) {
- return setName(parent.getFactory().createString(name));
+ return setName(factory.createString(name));
}
public SyntheticMethodBuilder setName(DexString name) {
@@ -95,7 +104,7 @@
}
private DexMethod getMethodSignature() {
- return parent.getFactory().createMethod(parent.getType(), proto, name);
+ return factory.createMethod(holderType, proto, name);
}
private MethodAccessFlags getAccessFlags() {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 79e63c0..210ae9b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
+import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.Reference;
@@ -269,6 +270,7 @@
public boolean encodeChecksums = false;
public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
public boolean cfToCfDesugar = false;
+ public boolean forceAnnotateSynthetics = false;
public int callGraphLikelySpuriousCallEdgeThreshold = 50;
@@ -1398,6 +1400,12 @@
public boolean enableExperimentalMapFileVersion = false;
}
+ public MapVersion getMapFileVersion() {
+ return testing.enableExperimentalMapFileVersion
+ ? MapVersion.MapVersionExperimental
+ : MapVersion.STABLE;
+ }
+
@VisibleForTesting
public void disableNameReflectionOptimization() {
// Use this util to disable get*Name() computation if the main intention of tests is checking
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 050519f..3663ec2 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -43,6 +43,7 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ClassNaming;
import com.android.tools.r8.naming.ClassNaming.Builder;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -50,8 +51,7 @@
import com.android.tools.r8.naming.Range;
import com.android.tools.r8.naming.mappinginformation.CompilerSynthesizedMappingInformation;
import com.android.tools.r8.naming.mappinginformation.FileNameInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
-import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.retrace.internal.RetraceUtils;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
@@ -294,28 +294,22 @@
com.android.tools.r8.position.Position.UNKNOWN));
// Check if source file should be added to the map
- ScopeReference classScope =
- ScopeReference.fromClassReference(
- Reference.classFromDescriptor(renamedDescriptor.toString()));
if (clazz.sourceFile != null) {
String sourceFile = clazz.sourceFile.toString();
if (!RetraceUtils.hasPredictableSourceFileName(clazz.toSourceString(), sourceFile)) {
- classNameMapperBuilder.addMappingInformation(
- classScope,
- FileNameInformation.build(sourceFile),
- conflictingInfo -> {
- throw new Unreachable();
- });
+ onDemandClassNamingBuilder
+ .get()
+ .addMappingInformation(FileNameInformation.build(sourceFile), Unreachable::raise);
}
}
- if (isSyntheticClass && appView.options().testing.enableExperimentalMapFileVersion) {
- classNameMapperBuilder.addMappingInformation(
- classScope,
- CompilerSynthesizedMappingInformation.builder().build(),
- conflictingInfo -> {
- throw new Unreachable();
- });
+ if (isSyntheticClass
+ && CompilerSynthesizedMappingInformation.isSupported(
+ appView.options().getMapFileVersion())) {
+ onDemandClassNamingBuilder
+ .get()
+ .addMappingInformation(
+ CompilerSynthesizedMappingInformation.builder().build(), Unreachable::raise);
}
// If the class is renamed add it to the classNamingBuilder.
@@ -384,15 +378,32 @@
DexString obfuscatedNameDexString = namingLens.lookupName(method.getReference());
String obfuscatedName = obfuscatedNameDexString.toString();
+ List<MappingInformation> methodMappingInfo = new ArrayList<>();
+ if (method.isD8R8Synthesized()
+ && CompilerSynthesizedMappingInformation.isSupported(
+ appView.options().getMapFileVersion())) {
+ methodMappingInfo.add(CompilerSynthesizedMappingInformation.builder().build());
+ }
+
+ // Don't emit pure identity mappings.
+ if (mappedPositions.isEmpty()
+ && methodMappingInfo.isEmpty()
+ && obfuscatedNameDexString == originalMethod.name
+ && originalMethod.holder == originalType) {
+ continue;
+ }
+
+ MemberNaming memberNaming = new MemberNaming(originalSignature, obfuscatedName);
+ onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
+
// Add simple "a() -> b" mapping if we won't have any other with concrete line numbers
if (mappedPositions.isEmpty()) {
- // But only if it's been renamed.
- if (obfuscatedNameDexString != originalMethod.name
- || originalMethod.holder != originalType) {
- onDemandClassNamingBuilder
- .get()
- .addMappedRange(null, originalSignature, null, obfuscatedName);
- }
+ MappedRange range =
+ onDemandClassNamingBuilder
+ .get()
+ .addMappedRange(null, originalSignature, null, obfuscatedName);
+ methodMappingInfo.forEach(
+ info -> range.addMappingInformation(info, Unreachable::raise));
continue;
}
@@ -403,9 +414,6 @@
signatures.computeIfAbsent(
m, key -> MethodSignature.fromDexMethod(m, m.holder != clazz.getType()));
- MemberNaming memberNaming = new MemberNaming(originalSignature, obfuscatedName);
- onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
-
// Update memberNaming with the collected positions, merging multiple positions into a
// single region whenever possible.
for (int i = 0; i < mappedPositions.size(); /* updated in body */ ) {
@@ -449,20 +457,25 @@
Range originalRange = new Range(firstPosition.originalLine, lastPosition.originalLine);
ClassNaming.Builder classNamingBuilder = onDemandClassNamingBuilder.get();
- classNamingBuilder.addMappedRange(
- obfuscatedRange,
- getOriginalMethodSignature.apply(firstPosition.method),
- originalRange,
- obfuscatedName);
+ MappedRange lastMappedRange =
+ classNamingBuilder.addMappedRange(
+ obfuscatedRange,
+ getOriginalMethodSignature.apply(firstPosition.method),
+ originalRange,
+ obfuscatedName);
Position caller = firstPosition.caller;
while (caller != null) {
- classNamingBuilder.addMappedRange(
- obfuscatedRange,
- getOriginalMethodSignature.apply(caller.method),
- Math.max(caller.line, 0), // Prevent against "no-position".
- obfuscatedName);
+ lastMappedRange =
+ classNamingBuilder.addMappedRange(
+ obfuscatedRange,
+ getOriginalMethodSignature.apply(caller.method),
+ Math.max(caller.line, 0), // Prevent against "no-position".
+ obfuscatedName);
caller = caller.callerPosition;
}
+ for (MappingInformation info : methodMappingInfo) {
+ lastMappedRange.addMappingInformation(info, Unreachable::raise);
+ }
i = j;
}
} // for each method of the group
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index d299224..822a5bd 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -82,9 +82,12 @@
@Override
public D8TestBuilder enableCoreLibraryDesugaring(
- AndroidApiLevel minApiLevel, KeepRuleConsumer keepRuleConsumer) {
+ AndroidApiLevel minApiLevel,
+ KeepRuleConsumer keepRuleConsumer,
+ StringResource desugaredLibraryConfiguration) {
if (minApiLevel.getLevel() < AndroidApiLevel.O.getLevel()) {
- super.enableCoreLibraryDesugaring(minApiLevel, keepRuleConsumer);
+ super.enableCoreLibraryDesugaring(
+ minApiLevel, keepRuleConsumer, desugaredLibraryConfiguration);
builder.setDesugaredLibraryKeepRuleConsumer(keepRuleConsumer);
}
return self();
diff --git a/src/test/java/com/android/tools/r8/D8TestRunResult.java b/src/test/java/com/android/tools/r8/D8TestRunResult.java
index d0fe6fb..c4427d1 100644
--- a/src/test/java/com/android/tools/r8/D8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestRunResult.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertNotNull;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
@@ -31,4 +32,12 @@
assertNotNull(app);
return proguardMap == null ? new CodeInspector(app) : new CodeInspector(app, proguardMap);
}
+
+ @Override
+ public StackTrace getStackTrace() {
+ if (proguardMap == null) {
+ return super.getStackTrace();
+ }
+ return super.getStackTrace().retrace(proguardMap);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index f19059b..ec2b927 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -30,7 +30,8 @@
public enum KotlinCompilerVersion {
KOTLINC_1_3_72("kotlin-compiler-1.3.72"),
- KOTLINC_1_4_20("kotlin-compiler-1.4.20");
+ KOTLINC_1_4_20("kotlin-compiler-1.4.20"),
+ KOTLINC_1_5_0_M2("kotlin-compiler-1.5.0-M2");
private final String folder;
@@ -77,6 +78,10 @@
return compilerVersion == version;
}
+ public boolean isNot(KotlinCompilerVersion version) {
+ return !is(version);
+ }
+
public KotlinCompilerVersion getCompilerVersion() {
return compilerVersion;
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 4b095bb..95198ec 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -845,6 +845,11 @@
DexVm.Version.V4_4_4,
DexVm.Version.V5_1_1,
DexVm.Version.V6_0_1)))
+ // Class.forName() that fails due to verification error is removed.
+ .put(
+ "412-new-array",
+ TestCondition.match(
+ TestCondition.compilers(CompilerUnderTest.R8, CompilerUnderTest.R8_AFTER_D8)))
// Array index out of bounds exception.
.put("449-checker-bce", TestCondition.any())
// Fails: get_vreg_jni.cc:46] Check failed: value == 42u (value=314630384, 42u=42)
@@ -887,6 +892,11 @@
.put("575-checker-string-init-alias", TestCondition.any())
// Runtime exception.
.put("577-profile-foreign-dex", TestCondition.any())
+ // Class.forName() that fails due to linkage error is removed.
+ .put(
+ "587-inline-class-error",
+ TestCondition.match(
+ TestCondition.compilers(CompilerUnderTest.R8, CompilerUnderTest.R8_AFTER_D8)))
// Array index out of bounds exception.
.put("602-deoptimizeable", TestCondition.any())
// Array index out of bounds exception.
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 0d90a34..19b414a 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -618,9 +618,12 @@
@Override
public T enableCoreLibraryDesugaring(
- AndroidApiLevel minApiLevel, KeepRuleConsumer keepRuleConsumer) {
+ AndroidApiLevel minApiLevel,
+ KeepRuleConsumer keepRuleConsumer,
+ StringResource desugaredLibraryConfiguration) {
if (minApiLevel.getLevel() < AndroidApiLevel.O.getLevel()) {
- super.enableCoreLibraryDesugaring(minApiLevel, keepRuleConsumer);
+ super.enableCoreLibraryDesugaring(
+ minApiLevel, keepRuleConsumer, desugaredLibraryConfiguration);
builder.setDesugaredLibraryKeepRuleConsumer(keepRuleConsumer);
}
return self();
diff --git a/src/test/java/com/android/tools/r8/SingleTestRunResult.java b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
index 07e0736..17546ee 100644
--- a/src/test/java/com/android/tools/r8/SingleTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
@@ -50,6 +50,11 @@
return result.stdout;
}
+ public <E extends Throwable> RR inspectStdOut(ThrowingConsumer<String, E> consumer) throws E {
+ consumer.accept(getStdOut());
+ return self();
+ }
+
public String getStdErr() {
return result.stderr;
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 766fb0f..cd584e0 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -432,9 +432,18 @@
public T enableCoreLibraryDesugaring(
AndroidApiLevel minApiLevel, KeepRuleConsumer keepRuleConsumer) {
- assert minApiLevel.getLevel() < AndroidApiLevel.O.getLevel();
- builder.addDesugaredLibraryConfiguration(
+ return enableCoreLibraryDesugaring(
+ minApiLevel,
+ keepRuleConsumer,
StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()));
+ }
+
+ public T enableCoreLibraryDesugaring(
+ AndroidApiLevel minApiLevel,
+ KeepRuleConsumer keepRuleConsumer,
+ StringResource desugaredLibraryConfiguration) {
+ assert minApiLevel.getLevel() < AndroidApiLevel.O.getLevel();
+ builder.addDesugaredLibraryConfiguration(desugaredLibraryConfiguration);
// TODO(b/158543446): This should not be setting an implicit library file. Doing so causes
// inconsistent library setup depending on the api level and makes tests hard to read and
// reason about.
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 34cc0b0..23e2e5c 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -53,8 +53,51 @@
public <S extends Throwable, T extends Throwable> RR applyIf(
boolean condition, ThrowingConsumer<RR, S> thenConsumer, ThrowingConsumer<RR, T> elseConsumer)
throws S, T {
- if (condition) {
- thenConsumer.accept(self());
+ return applyIf(
+ condition,
+ thenConsumer,
+ true,
+ elseConsumer,
+ r -> {
+ assert false;
+ });
+ }
+
+ public <S extends Throwable, T extends Throwable, U extends Throwable> RR applyIf(
+ boolean condition1,
+ ThrowingConsumer<RR, S> thenConsumer1,
+ boolean condition2,
+ ThrowingConsumer<RR, T> thenConsumer2,
+ ThrowingConsumer<RR, U> elseConsumer)
+ throws S, T, U {
+ return applyIf(
+ condition1,
+ thenConsumer1,
+ condition2,
+ thenConsumer2,
+ true,
+ elseConsumer,
+ r -> {
+ assert false;
+ });
+ }
+
+ public <S extends Throwable, T extends Throwable, U extends Throwable, V extends Throwable>
+ RR applyIf(
+ boolean condition1,
+ ThrowingConsumer<RR, S> thenConsumer1,
+ boolean condition2,
+ ThrowingConsumer<RR, T> thenConsumer2,
+ boolean condition3,
+ ThrowingConsumer<RR, U> thenConsumer3,
+ ThrowingConsumer<RR, V> elseConsumer)
+ throws S, T, U, V {
+ if (condition1) {
+ thenConsumer1.accept(self());
+ } else if (condition2) {
+ thenConsumer2.accept(self());
+ } else if (condition3) {
+ thenConsumer3.accept(self());
} else {
elseConsumer.accept(self());
}
diff --git a/src/test/java/com/android/tools/r8/TestRunResultCollection.java b/src/test/java/com/android/tools/r8/TestRunResultCollection.java
index 4912ef8..e166be4 100644
--- a/src/test/java/com/android/tools/r8/TestRunResultCollection.java
+++ b/src/test/java/com/android/tools/r8/TestRunResultCollection.java
@@ -57,10 +57,59 @@
return inspectIf(Predicates.alwaysTrue(), consumer);
}
- public RR applyIf(Predicate<C> filter, Consumer<TestRunResult<?>> fn) {
+ public RR applyIf(Predicate<C> filter, Consumer<TestRunResult<?>> thenConsumer) {
+ return applyIf(filter, thenConsumer, r -> {});
+ }
+
+ public RR applyIf(
+ Predicate<C> filter,
+ Consumer<TestRunResult<?>> thenConsumer,
+ Consumer<TestRunResult<?>> elseConsumer) {
+ return applyIf(
+ filter,
+ thenConsumer,
+ c -> true,
+ elseConsumer,
+ r -> {
+ assert false;
+ });
+ }
+
+ public RR applyIf(
+ Predicate<C> filter1,
+ Consumer<TestRunResult<?>> thenConsumer1,
+ Predicate<C> filter2,
+ Consumer<TestRunResult<?>> thenConsumer2,
+ Consumer<TestRunResult<?>> elseConsumer) {
+ return applyIf(
+ filter1,
+ thenConsumer1,
+ filter2,
+ thenConsumer2,
+ c -> true,
+ elseConsumer,
+ r -> {
+ assert false;
+ });
+ }
+
+ public RR applyIf(
+ Predicate<C> filter1,
+ Consumer<TestRunResult<?>> thenConsumer1,
+ Predicate<C> filter2,
+ Consumer<TestRunResult<?>> thenConsumer2,
+ Predicate<C> filter3,
+ Consumer<TestRunResult<?>> thenConsumer3,
+ Consumer<TestRunResult<?>> elseConsumer) {
for (Pair<C, TestRunResult<?>> run : runs) {
- if (filter.test(run.getFirst())) {
- fn.accept(run.getSecond());
+ if (filter1.test(run.getFirst())) {
+ thenConsumer1.accept(run.getSecond());
+ } else if (filter2.test(run.getFirst())) {
+ thenConsumer2.accept(run.getSecond());
+ } else if (filter3.test(run.getFirst())) {
+ thenConsumer3.accept(run.getSecond());
+ } else {
+ elseConsumer.accept(run.getSecond());
}
}
return self();
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index 20bf4b8..5aef2ad 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -323,6 +323,10 @@
return Objects.hash(vm, home);
}
+ public boolean isOlderThan(CfVm version) {
+ return vm.lessThan(version);
+ }
+
public boolean isNewerThan(CfVm version) {
return !vm.lessThanOrEqual(version);
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index e9a47e3..c3949d9 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0_M2;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.isDexFile;
import static org.junit.Assert.assertEquals;
@@ -2152,8 +2153,12 @@
return new KotlinCompiler(KOTLINC_1_4_20);
}
+ public static KotlinCompiler getKotlinC_1_5_0_m2() {
+ return new KotlinCompiler(KOTLINC_1_5_0_M2);
+ }
+
public static KotlinCompiler[] getKotlinCompilers() {
- return new KotlinCompiler[] {getKotlinC_1_3_72(), getKotlinC_1_4_20()};
+ return new KotlinCompiler[] {getKotlinC_1_3_72(), getKotlinC_1_4_20(), getKotlinC_1_5_0_m2()};
}
public static void disassemble(AndroidApp app, PrintStream ps) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
index 550e21f..8540d13 100644
--- a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -9,13 +9,11 @@
import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
@@ -77,15 +75,10 @@
}
private void inspectSourceDebugExtension(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz("retrace.InlineFunctionKt");
+ ClassSubject clazz = inspector.clazz("retrace.MainKt");
assertThat(clazz, isPresent());
AnnotationSubject sourceDebugExtensions =
clazz.annotation("dalvik.annotation.SourceDebugExtension");
- // TODO(b/179866574): This is somehow not present
- if (kotlinCompiler.is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
- assertThat(sourceDebugExtensions, not(isPresent()));
- } else {
- assertThat(sourceDebugExtensions, isPresent());
- }
+ assertThat(sourceDebugExtensions, isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index b385fd6..a8d57c0 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.graph.GenericSignatureIdentityTest.testParseSignaturesInJar;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static com.google.common.io.ByteStreams.toByteArray;
-import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -106,14 +105,7 @@
.setMode(mode)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
- .allowDiagnosticInfoMessages(mode == CompilationMode.DEBUG)
.compile()
- .assertAllInfoMessagesMatch(
- anyOf(
- containsString("Stripped invalid locals information from 1 method."),
- containsString("Methods with invalid locals information:"),
- containsString(
- "Some warnings are typically a sign of using an outdated Java toolchain.")))
.apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
.writeToZip(jar);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerIndirectReflectiveNameTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerIndirectReflectiveNameTest.java
index f52ff7c..0dd71da 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerIndirectReflectiveNameTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerIndirectReflectiveNameTest.java
@@ -4,11 +4,6 @@
package com.android.tools.r8.classmerging.vertical;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThrows;
-
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
@@ -48,26 +43,15 @@
@Test
public void testR8() throws Exception {
- // TODO(b/173099479): This should not throw an assertion-error.
- assertThrows(
- CompilationFailedException.class,
- () ->
- testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, A.class, B.class)
- .addKeepMainRule(Main.class)
- .setMinApi(parameters.getApiLevel())
- .enableInliningAnnotations()
- .enableNeverClassInliningAnnotations()
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorsMatch(
- diagnosticMessage(
- containsString(
- "Expected vertically merged class"
- + " `com.android.tools.r8.classmerging.vertical."
- + "VerticalClassMergerIndirectReflectiveNameTest$A`"
- + " to be absent")));
- }));
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class, B.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A::foo", "B::foo");
}
public static class A {
diff --git a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
new file mode 100644
index 0000000..86c5440
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
@@ -0,0 +1,164 @@
+// Copyright (c) 2021, 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.desugar;
+
+import static com.android.tools.r8.utils.InternalOptions.EXPERIMENTAL_CF_VERSION;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class InterfaceInvokePrivateTest extends TestBase implements Opcodes {
+
+ private final TestParameters parameters;
+ private final CfVersion inputCfVersion;
+
+ @Parameterized.Parameters(name = "{0}, Input CfVersion = {1}")
+ public static Iterable<?> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevelsAlsoForCf().build(),
+ CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15));
+ }
+
+ public InterfaceInvokePrivateTest(TestParameters parameters, CfVersion inputCfVersion) {
+ this.parameters = parameters;
+ this.inputCfVersion = inputCfVersion;
+ }
+
+ private boolean isNotDesugaredAndCfRuntimeOlderThanJDK11(DesugarTestConfiguration configuration) {
+ return DesugarTestConfiguration.isNotDesugared(configuration)
+ && parameters.getRuntime().isCf()
+ && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11);
+ }
+
+ private boolean isInputCfVersionSupported() {
+ return inputCfVersion.isLessThanOrEqualTo(
+ CfVersion.fromRaw(parameters.getRuntime().asCf().getVm().getClassfileVersion()));
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.getRuntime().isCf());
+ assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+ testForJvm()
+ .addProgramClassFileData(transformIToPrivate(inputCfVersion))
+ .addProgramClasses(TestRunner.class)
+ .run(parameters.getRuntime(), TestRunner.class)
+ .applyIf(
+ // Fails if input CF version is not supported.
+ !isInputCfVersionSupported(),
+ r ->
+ r.assertFailureWithErrorThatMatches(
+ containsString(
+ "more recent version of the Java Runtime (class file version "
+ + inputCfVersion.toString())),
+ // Fails with ICCE on VMs prior to JDK 11.
+ parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11),
+ r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
+ // Succeeds on VMs from JDK 11 regardless of the CF version of the input.
+ r -> r.assertSuccessWithOutputLines("Hello, world!"));
+ }
+
+ @Test
+ public void testDesugar() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClassFileData(transformIToPrivate(inputCfVersion))
+ .addProgramClasses(TestRunner.class)
+ .run(parameters.getRuntime(), TestRunner.class)
+ .applyIf(
+ // Running un-desugared on a JVM with too high a CF version fails.
+ c ->
+ parameters.getRuntime().isCf()
+ && !isInputCfVersionSupported()
+ && DesugarTestConfiguration.isNotDesugared(c),
+ r ->
+ r.assertFailureWithErrorThatMatches(
+ containsString(
+ "more recent version of the Java Runtime (class file version "
+ + inputCfVersion.toString())),
+ // Running un-desugared on a JVM with a supported CF version fails on pre JVM 11. On
+ // JVM 11 and above this succeeds even of the input CF version is below 55.
+ this::isNotDesugaredAndCfRuntimeOlderThanJDK11,
+ r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
+ // All other conditions succeed.
+ r -> r.assertSuccessWithOutputLines("Hello, world!"));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(transformIToPrivate(inputCfVersion))
+ .addProgramClasses(TestRunner.class)
+ .addKeepMainRule(TestRunner.class)
+ // TODO(b/185463156): Not keeping I and its members will "fix" the ICCE for all runtimes.
+ .addKeepClassAndMembersRules(I.class)
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages(
+ inputCfVersion.isGreaterThanOrEqualTo(EXPERIMENTAL_CF_VERSION))
+ .compile()
+ .run(parameters.getRuntime(), TestRunner.class)
+ .applyIf(
+ // Running un-desugared on a JVM with too high a CF version fails.
+ parameters.getRuntime().isCf() && !isInputCfVersionSupported(),
+ r ->
+ r.assertFailureWithErrorThatMatches(
+ containsString(
+ "more recent version of the Java Runtime (class file version "
+ + inputCfVersion.toString())),
+ // Running on a JVM with a supported CF version fails on pre JVM 11. On
+ // JVM 11 and above this succeeds even of the input CF version is below 55.
+ parameters.getRuntime().isCf()
+ && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11),
+ r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
+ // All other conditions succeed.
+ r -> r.assertSuccessWithOutputLines("Hello, world!"));
+ }
+
+ private byte[] transformIToPrivate(CfVersion version) throws NoSuchMethodException, IOException {
+ return transformer(I.class)
+ .setVersion(version)
+ .setPrivate(I.class.getDeclaredMethod("privateHello"))
+ .transformMethodInsnInMethod(
+ "hello",
+ ((opcode, owner, name, descriptor, isInterface, continuation) -> {
+ continuation.visitMethodInsn(
+ name.equals("privateHello") ? Opcodes.INVOKEINTERFACE : opcode,
+ owner,
+ name,
+ descriptor,
+ isInterface);
+ }))
+ .transform();
+ }
+
+ interface I {
+ /* private */ default String privateHello() {
+ return "Hello, world!";
+ }
+
+ default String hello() {
+ // The private method "privateHello" is called with invokeinterface.
+ return privateHello();
+ }
+ }
+
+ public static class TestRunner implements I {
+
+ public static void main(String[] args) {
+ System.out.println(new TestRunner().hello());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/VirtualInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/VirtualInvokePrivateTest.java
new file mode 100644
index 0000000..55de73b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/VirtualInvokePrivateTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2021, 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.desugar;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class VirtualInvokePrivateTest extends TestBase implements Opcodes {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimes()
+ .withDexRuntimes()
+ .withAllApiLevelsAlsoForCf()
+ .build();
+ }
+
+ public VirtualInvokePrivateTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private boolean isNotDesugaredAndCfRuntimeNewerThanOrEqualToJDK11(
+ DesugarTestConfiguration configuration) {
+ return DesugarTestConfiguration.isNotDesugared(configuration)
+ && parameters.getRuntime().isCf()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11);
+ }
+
+ private void inspectNotDesugared(CodeInspector inspector) {
+ MethodSubject main = inspector.clazz(TestRunner.class).uniqueMethodWithName("main");
+ assertEquals(2, main.streamInstructions().filter(InstructionSubject::isInvokeVirtual).count());
+ }
+
+ private void inspectDesugared(CodeInspector inspector) {
+ MethodSubject main = inspector.clazz(TestRunner.class).uniqueMethodWithName("main");
+ assertEquals(1, main.streamInstructions().filter(InstructionSubject::isInvokeVirtual).count());
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.getRuntime().isCf());
+ assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+ testForJvm()
+ .addProgramClassFileData(transformInvokeSpecialToInvokeVirtual())
+ .run(parameters.getRuntime(), TestRunner.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testDesugar() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClassFileData(transformInvokeSpecialToInvokeVirtual())
+ .run(parameters.getRuntime(), TestRunner.class)
+ .inspectIf(
+ this::isNotDesugaredAndCfRuntimeNewerThanOrEqualToJDK11, this::inspectNotDesugared)
+ .inspectIf(DesugarTestConfiguration::isDesugared, this::inspectDesugared)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(transformInvokeSpecialToInvokeVirtual())
+ .addKeepMainRule(TestRunner.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestRunner.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private byte[] transformInvokeSpecialToInvokeVirtual() throws IOException {
+ return transformer(TestRunner.class)
+ .setVersion(CfVersion.V1_8)
+ .transformMethodInsnInMethod(
+ "main",
+ ((opcode, owner, name, descriptor, isInterface, continuation) -> {
+ continuation.visitMethodInsn(
+ name.equals("hello") ? Opcodes.INVOKEVIRTUAL : opcode,
+ owner,
+ name,
+ descriptor,
+ isInterface);
+ }))
+ .transform();
+ }
+
+ public static class TestRunner {
+ private String hello() {
+ return "Hello, world!";
+ }
+
+ public static void main(String[] args) {
+ // The private method "hello" is called with invokevirtual.
+ System.out.println(new TestRunner().hello());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 8f1d451..3b9a63d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.errors.Unreachable;
@@ -27,21 +28,25 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.junit.BeforeClass;
+import org.junit.rules.TemporaryFolder;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
@@ -94,6 +99,158 @@
return parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel();
}
+ public static class L8TestBuilder {
+
+ private final AndroidApiLevel apiLevel;
+ private final TemporaryFolder temp;
+
+ private CompilationMode mode = CompilationMode.RELEASE;
+ private List<String> keepRules = new ArrayList<>();
+ private List<Path> additionalProgramFiles = new ArrayList<>();
+ private Consumer<InternalOptions> optionsModifier = ConsumerUtils.emptyConsumer();
+ private Path desugarJDKLibs = ToolHelper.getDesugarJDKLibs();
+ private Path desugarJDKLibsConfiguration = ToolHelper.DESUGAR_LIB_CONVERSIONS;
+ private StringResource desugaredLibraryConfiguration =
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting());
+
+ public static L8TestBuilder builder(AndroidApiLevel apiLevel, TemporaryFolder temp) {
+ return new L8TestBuilder(apiLevel, temp);
+ }
+
+ private L8TestBuilder(AndroidApiLevel apiLevel, TemporaryFolder temp) {
+ this.apiLevel = apiLevel;
+ this.temp = temp;
+ }
+
+ public L8TestBuilder addProgramFiles(Collection<Path> programFiles) {
+ this.additionalProgramFiles.addAll(programFiles);
+ return this;
+ }
+
+ public L8TestBuilder addKeepRules(String keepRules) {
+ if (!keepRules.trim().isEmpty()) {
+ this.keepRules.add(keepRules);
+ }
+ return this;
+ }
+
+ public L8TestBuilder addKeepRuleFiles(Collection<Path> keepRuleFiles) throws IOException {
+ for (Path keepRuleFile : keepRuleFiles) {
+ addKeepRules(FileUtils.readTextFile(keepRuleFile, StandardCharsets.UTF_8));
+ }
+ return this;
+ }
+
+ public L8TestBuilder addOptionsModifier(Consumer<InternalOptions> optionsModifier) {
+ this.optionsModifier = this.optionsModifier.andThen(optionsModifier);
+ return this;
+ }
+
+ public L8TestBuilder applyIf(boolean condition, ThrowableConsumer<L8TestBuilder> thenConsumer) {
+ return applyIf(condition, thenConsumer, ThrowableConsumer.empty());
+ }
+
+ public L8TestBuilder applyIf(
+ boolean condition,
+ ThrowableConsumer<L8TestBuilder> thenConsumer,
+ ThrowableConsumer<L8TestBuilder> elseConsumer) {
+ if (condition) {
+ thenConsumer.acceptWithRuntimeException(this);
+ } else {
+ elseConsumer.acceptWithRuntimeException(this);
+ }
+ return this;
+ }
+
+ public L8TestBuilder setDebug() {
+ this.mode = CompilationMode.DEBUG;
+ return this;
+ }
+
+ public L8TestBuilder setDesugarJDKLibs(Path desugarJDKLibs) {
+ this.desugarJDKLibs = desugarJDKLibs;
+ return this;
+ }
+
+ public L8TestBuilder setDesugarJDKLibsConfiguration(Path desugarJDKLibsConfiguration) {
+ this.desugarJDKLibsConfiguration = desugarJDKLibsConfiguration;
+ return this;
+ }
+
+ public L8TestBuilder setDesugaredLibraryConfiguration(Path path) {
+ this.desugaredLibraryConfiguration = StringResource.fromFile(path);
+ return this;
+ }
+
+ public Path compile() {
+ // We wrap exceptions in a RuntimeException to call this from a lambda.
+ try {
+ // If we compile extended library here, it means we use TestNG.
+ // TestNG requires annotations, hence we disable AnnotationRemoval.
+ // This implies that extra warning are generated if this is set.
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
+ L8Command.Builder l8Builder =
+ L8Command.builder(diagnosticsHandler)
+ .addProgramFiles(getProgramFiles())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMode(mode)
+ .addDesugaredLibraryConfiguration(desugaredLibraryConfiguration)
+ .setMinApiLevel(apiLevel.getLevel())
+ .setOutput(desugaredLib, OutputMode.DexIndexed);
+ Path mapping = null;
+ if (!keepRules.isEmpty()) {
+ mapping = temp.newFolder().toPath().resolve("mapping.txt");
+ l8Builder.addProguardConfiguration(
+ ImmutableList.<String>builder()
+ .addAll(keepRules)
+ .add("-printmapping " + mapping)
+ .build(),
+ Origin.unknown());
+ }
+ ToolHelper.runL8(
+ l8Builder.build(),
+ options -> {
+ if (!additionalProgramFiles.isEmpty()) {
+ options.testing.disableL8AnnotationRemoval = true;
+ }
+ optionsModifier.accept(options);
+ });
+ if (additionalProgramFiles.isEmpty()) {
+ assertTrue(
+ diagnosticsHandler.getInfos().stream()
+ .noneMatch(
+ string ->
+ string
+ .getDiagnosticMessage()
+ .startsWith(
+ "Invalid parameter counts in MethodParameter attributes.")));
+ }
+ new CodeInspector(desugaredLib, mapping)
+ .forAllClasses(clazz -> assertTrue(clazz.getFinalName().startsWith("j$.")));
+ return desugaredLib;
+ } catch (Exception e) {
+ // Don't wrap assumption violation so junit can catch it.
+ if (e instanceof RuntimeException) {
+ throw ((RuntimeException) e);
+ }
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Collection<Path> getProgramFiles() {
+ return ImmutableList.<Path>builder()
+ .add(desugarJDKLibs)
+ .add(desugarJDKLibsConfiguration)
+ .addAll(additionalProgramFiles)
+ .build();
+ }
+ }
+
+ protected L8TestBuilder testForL8(AndroidApiLevel apiLevel) {
+ return new L8TestBuilder(apiLevel, temp);
+ }
+
protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel) {
return buildDesugaredLibrary(apiLevel, "", false);
}
@@ -126,62 +283,11 @@
boolean shrink,
List<Path> additionalProgramFiles,
Consumer<InternalOptions> optionsModifier) {
- // We wrap exceptions in a RuntimeException to call this from a lambda.
- try {
- // If we compile extended library here, it means we use TestNG.
- // TestNG requires annotations, hence we disable AnnotationRemoval.
- // This implies that extra warning are generated if this is set.
- boolean extraFiles = !additionalProgramFiles.isEmpty();
- ArrayList<Path> extraPaths = new ArrayList<>(additionalProgramFiles);
- TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
- Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
- L8Command.Builder l8Builder =
- L8Command.builder(diagnosticsHandler)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addProgramFiles(ToolHelper.getDesugarJDKLibs())
- .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
- .setMode(shrink ? CompilationMode.RELEASE : CompilationMode.DEBUG)
- .addProgramFiles(extraPaths)
- .addDesugaredLibraryConfiguration(
- StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()))
- .setMinApiLevel(apiLevel.getLevel())
- .setOutput(desugaredLib, OutputMode.DexIndexed);
- Path mapping = null;
- if (shrink) {
- mapping = temp.newFolder().toPath().resolve("mapping.txt");
- List<String> lines =
- new ArrayList<>(Arrays.asList(keepRules.split(System.lineSeparator())));
- lines.add("-printmapping " + mapping);
- l8Builder.addProguardConfiguration(lines, Origin.unknown());
- }
- ToolHelper.runL8(
- l8Builder.build(),
- options -> {
- if (extraFiles) {
- options.testing.disableL8AnnotationRemoval = true;
- }
- optionsModifier.accept(options);
- });
- if (!extraFiles) {
- assertTrue(
- diagnosticsHandler.getInfos().stream()
- .noneMatch(
- string ->
- string
- .getDiagnosticMessage()
- .startsWith(
- "Invalid parameter counts in MethodParameter attributes.")));
- }
- new CodeInspector(desugaredLib, mapping)
- .forAllClasses(clazz -> assertTrue(clazz.getFinalName().startsWith("j$.")));
- return desugaredLib;
- } catch (Exception e) {
- // Don't wrap assumption violation so junit can catch it.
- if (e instanceof RuntimeException) {
- throw ((RuntimeException) e);
- }
- throw new RuntimeException(e);
- }
+ return testForL8(apiLevel)
+ .addProgramFiles(additionalProgramFiles)
+ .applyIf(shrink, builder -> builder.addKeepRules(keepRules), L8TestBuilder::setDebug)
+ .addOptionsModifier(optionsModifier)
+ .compile();
}
protected void assertLines2By2Correct(String stdOut) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index 2d3c243..ef8f61a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.desugar.desugaredlibrary.kotlin;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
@@ -132,7 +132,7 @@
.setMinApi(parameters.getApiLevel())
.allowDiagnosticWarningMessages()
.allowUnusedDontWarnKotlinReflectJvmInternal(
- kotlinParameters.getCompiler().is(KOTLINC_1_4_20));
+ kotlinParameters.getCompiler().isNot(KOTLINC_1_3_72));
KeepRuleConsumer keepRuleConsumer = null;
if (desugarLibrary) {
keepRuleConsumer = createKeepRuleConsumer(parameters);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
index 0bc6b67..6382436 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
@@ -49,26 +49,22 @@
.addKeepRules(enumKeepRules.getKeepRules())
.addKeepClassRules(ClassAnnotationDefault.class)
.addKeepRuntimeVisibleAnnotations()
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector
+ .assertUnboxed(MyEnumParamMethod2.class, MyEnumRetMethod2.class)
+ .assertNotUnboxed(
+ MyEnum.class,
+ MyEnumDefault.class,
+ MyEnumArray.class,
+ MyEnumArrayDefault.class))
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
.enableNoVerticalClassMergingAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(
- m -> {
- assertEnumIsBoxed(MyEnumDefault.class, MyEnumDefault.class.getSimpleName(), m);
- assertEnumIsBoxed(MyEnum.class, MyEnum.class.getSimpleName(), m);
- assertEnumIsBoxed(
- MyEnumArrayDefault.class, MyEnumArrayDefault.class.getSimpleName(), m);
- assertEnumIsBoxed(MyEnumArray.class, MyEnumArray.class.getSimpleName(), m);
- assertEnumIsUnboxed(
- MyEnumRetMethod2.class, MyEnumRetMethod2.class.getSimpleName(), m);
- assertEnumIsUnboxed(
- MyEnumParamMethod2.class, MyEnumParamMethod2.class.getSimpleName(), m);
- })
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("print", "1", "1", "1", "1", "1", "0", "0", "0", "0");
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ClInitSideEffectEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ClInitSideEffectEnumUnboxingTest.java
index 67dfbc0..743a819 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ClInitSideEffectEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ClInitSideEffectEnumUnboxingTest.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -37,23 +36,20 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<Switch> classToTest = Switch.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(ClInitSideEffectEnumUnboxingTest.class)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
- .enableNeverClassInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(MyEnum.class, classToTest.getSimpleName(), m))
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ClInitSideEffectEnumUnboxingTest.class)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ClassAccessEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ClassAccessEnumUnboxingTest.java
index a39e30f..a24cb93 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ClassAccessEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ClassAccessEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Assume;
@@ -37,27 +36,23 @@
@Test
public void testEnumUnboxing() throws Exception {
Assume.assumeTrue("studio rules required to use valueOf", enumKeepRules.isStudio());
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(ClassAccessEnumUnboxingTest.class)
- .addKeepMainRule(Main.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> {
- assertEnumIsUnboxed(ProtoEnumLike.class, ProtoEnumLike.class.getSimpleName(), m);
- assertEnumIsUnboxed(UnboxableEnum.class, UnboxableEnum.class.getSimpleName(), m);
- assertEnumIsBoxed(EscapingEnum1.class, EscapingEnum1.class.getSimpleName(), m);
- assertEnumIsBoxed(EscapingEnum2.class, EscapingEnum2.class.getSimpleName(), m);
- })
- .run(parameters.getRuntime(), Main.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ClassAccessEnumUnboxingTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector
+ .assertUnboxed(ProtoEnumLike.class, UnboxableEnum.class)
+ .assertNotUnboxed(EscapingEnum1.class, EscapingEnum2.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java
index 9873711..e6fdc93 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -19,7 +18,7 @@
@RunWith(Parameterized.class)
public class ComparisonEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?>[] INPUTS = new Class<?>[] {NullCheck.class, EnumComparison.class};
+ private static final Class<?>[] TESTS = new Class<?>[] {NullCheck.class, EnumComparison.class};
private final TestParameters parameters;
private final boolean enumValueOptimization;
@@ -42,12 +41,14 @@
R8TestCompileResult compile =
testForR8(parameters.getBackend())
.addInnerClasses(ComparisonEnumUnboxingTest.class)
- .addKeepMainRules(INPUTS)
+ .addKeepMainRules(TESTS)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertUnboxed(NullCheck.MyEnum.class, EnumComparison.MyEnum.class))
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
@@ -55,14 +56,11 @@
assertEquals(3, inspector.clazz(NullCheck.class).allMethods().size());
assertEquals(2, inspector.clazz(EnumComparison.class).allMethods().size());
});
- for (Class<?> input : INPUTS) {
- R8TestRunResult run =
- compile
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(input.getDeclaredClasses()[0], input.getSimpleName(), m))
- .run(parameters.getRuntime(), input)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ for (Class<?> main : TESTS) {
+ compile
+ .run(parameters.getRuntime(), main)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
index e9a7e5d..0326e0b 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
@@ -7,8 +7,8 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.enumunboxing.DoubleProcessingEnumUnboxingTest.App.AppEnum;
import com.android.tools.r8.enumunboxing.examplelib1.JavaLibrary1;
import com.android.tools.r8.ir.optimize.enums.UnboxedEnumMemberRelocator;
import com.android.tools.r8.references.Reference;
@@ -67,24 +67,21 @@
.compile()
.writeToZip();
// Compile the app with the lib.
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(App.class, App.AppEnum.class)
- .addProgramFiles(javaLibShrunk)
- .addKeepMainRule(App.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::assertUtilityClassPresent)
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(App.AppEnum.class, App.class.getSimpleName(), m))
- .run(parameters.getRuntime(), App.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(App.class, App.AppEnum.class)
+ .addProgramFiles(javaLibShrunk)
+ .addKeepMainRule(App.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(App.AppEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::assertUtilityClassPresent)
+ .run(parameters.getRuntime(), App.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
private void assertUtilityClassPresent(CodeInspector codeInspector) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
index 7cc81c3..44cdff0 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.enumunboxing.examplelib1.JavaLibrary1;
import com.android.tools.r8.enumunboxing.examplelib2.JavaLibrary2;
@@ -58,24 +57,21 @@
Path javaLibShrunk1 = compileLibrary(JavaLibrary1.class, JavaLibrary1.LibEnum1.class);
Path javaLibShrunk2 = compileLibrary(JavaLibrary2.class, JavaLibrary2.LibEnum2.class);
// Compile the app with the lib.
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(App.class, App.AppEnum.class)
- .addProgramFiles(javaLibShrunk1, javaLibShrunk2)
- .addKeepMainRule(App.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::assertUtilityClassPresent)
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(App.AppEnum.class, App.class.getSimpleName(), m))
- .run(parameters.getRuntime(), App.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(App.class, App.AppEnum.class)
+ .addProgramFiles(javaLibShrunk1, javaLibShrunk2)
+ .addKeepMainRule(App.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(App.AppEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::assertUtilityClassPresent)
+ .run(parameters.getRuntime(), App.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
private Path compileLibrary(Class<?> libClass, Class<?> enumLibClass) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EmptyEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EmptyEnumUnboxingTest.java
index ddc0806..34a9d32 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EmptyEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EmptyEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -35,24 +34,20 @@
@Test
public void testEnumUnboxing() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(EmptyEnumUnboxingTest.class)
- .addKeepMainRule(Main.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m ->
- // TODO(b/166532373): Unbox enum with no cases.
- assertEnumIsBoxed(MyEnum.class, Main.class.getSimpleName(), m))
- .run(parameters.getRuntime(), Main.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EmptyEnumUnboxingTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ // TODO(b/166532373): Unbox enum with no cases.
+ .addEnumUnboxingInspector(inspector -> inspector.assertNotUnboxed(MyEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumMissingFieldsUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumMissingFieldsUnboxingTest.java
index f6b55cf..e46a48c 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumMissingFieldsUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumMissingFieldsUnboxingTest.java
@@ -7,7 +7,6 @@
import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.io.IOException;
import java.util.List;
@@ -37,22 +36,19 @@
@Test
public void testEnumUnboxing() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(TestClass.class)
- .addProgramClassFileData(getEnumProgramData())
- .addKeepMainRule(TestClass.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsBoxed(CompilationEnum.class, TestClass.class.getSimpleName(), m))
- .run(parameters.getRuntime(), TestClass.class)
- .assertFailureWithErrorThatMatches(containsString("NoSuchFieldError"));
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addProgramClassFileData(getEnumProgramData())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertNotUnboxed(CompilationEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatMatches(containsString("NoSuchFieldError"))
+ .inspectStdOut(this::assertLines2By2Correct);
}
private byte[] getEnumProgramData() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
index b21cbd0..0d7a5dfc 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
@@ -70,9 +70,7 @@
options.enableEnumUnboxing = enumUnboxing;
options.enableEnumValueOptimization = enumValueOptimization;
options.enableEnumSwitchMapRemoval = enumValueOptimization;
- options.testing.enableEnumUnboxingDebugLogs = enumUnboxing;
})
- .allowDiagnosticInfoMessages(enumUnboxing)
.setMinApi(parameters.getApiLevel())
.compile();
compile
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
index d4b73ff..4776d5f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -18,12 +17,12 @@
@RunWith(Parameterized.class)
public class EnumUnboxingArrayTest extends EnumUnboxingTestBase {
- private static final Class<?>[] SUCCESSES = {
- EnumVarArgs.class,
- EnumArrayReadWriteNoEscape.class,
- EnumArrayReadWrite.class,
+ private static final Class<?>[] TESTS = {
+ Enum2DimArrayReadWrite.class,
EnumArrayNullRead.class,
- Enum2DimArrayReadWrite.class
+ EnumArrayReadWrite.class,
+ EnumArrayReadWriteNoEscape.class,
+ EnumVarArgs.class,
};
private final TestParameters parameters;
@@ -47,24 +46,26 @@
R8TestCompileResult compile =
testForR8(parameters.getBackend())
.addInnerClasses(EnumUnboxingArrayTest.class)
- .addKeepMainRules(SUCCESSES)
+ .addKeepMainRules(TESTS)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertUnboxed(
+ Enum2DimArrayReadWrite.MyEnum.class,
+ EnumArrayNullRead.MyEnum.class,
+ EnumArrayReadWrite.MyEnum.class,
+ EnumArrayReadWriteNoEscape.MyEnum.class,
+ EnumVarArgs.MyEnum.class))
.setMinApi(parameters.getApiLevel())
.compile();
- for (Class<?> success : SUCCESSES) {
- R8TestRunResult run =
- compile
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsUnboxed(
- success.getDeclaredClasses()[0], success.getSimpleName(), m))
- .run(parameters.getRuntime(), success)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ for (Class<?> main : TESTS) {
+ compile
+ .run(parameters.getRuntime(), main)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
index 590cc7c..a8fe3f9 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
@@ -7,7 +7,6 @@
import static org.hamcrest.core.StringContains.containsString;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
import java.nio.file.Path;
@@ -50,18 +49,14 @@
.addProgramFiles(javaLibShrunk)
.addKeepMainRules(ProgramValueOf.class, ProgramStaticMethod.class)
.addKeepRules(enumKeepRules.getKeepRules())
- .addOptionsModification(
- options -> {
- assert options.enableEnumUnboxing;
- options.testing.enableEnumUnboxingDebugLogs = true;
- })
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector
+ .assertUnboxed(Lib.LibEnum.class)
+ .assertUnboxedIf(!missingStaticMethods, Lib.LibEnumStaticMethod.class))
.allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- // The enums cannot be unboxed if static methods are missing,
- // but they should be unboxed otherwise.
- this::assertEnumUnboxedIfStaticMethodsPresent);
+ .compile();
if (missingStaticMethods) {
compile
.run(parameters.getRuntime(), ProgramStaticMethod.class)
@@ -92,36 +87,19 @@
.addKeepRules(missingStaticMethods ? "" : "-keep enum * { static <methods>; }")
.addOptionsModification(
options -> {
+ assert !options.enableEnumUnboxing;
options.enableEnumUnboxing = true;
- options.testing.enableEnumUnboxingDebugLogs = true;
})
.addKeepClassRules(Lib.LibEnumStaticMethod.class)
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertNotUnboxed(Lib.LibEnum.class, Lib.LibEnumStaticMethod.class))
.allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(
- msg -> {
- assertEnumIsBoxed(
- Lib.LibEnumStaticMethod.class,
- Lib.LibEnumStaticMethod.class.getSimpleName(),
- msg);
- assertEnumIsBoxed(Lib.LibEnum.class, Lib.LibEnum.class.getSimpleName(), msg);
- })
.writeToZip();
}
- private void assertEnumUnboxedIfStaticMethodsPresent(TestDiagnosticMessages msg) {
- if (missingStaticMethods) {
- assertEnumIsBoxed(
- Lib.LibEnumStaticMethod.class, Lib.LibEnumStaticMethod.class.getSimpleName(), msg);
- assertEnumIsBoxed(Lib.LibEnum.class, Lib.LibEnum.class.getSimpleName(), msg);
- } else {
- assertEnumIsUnboxed(
- Lib.LibEnumStaticMethod.class, Lib.LibEnumStaticMethod.class.getSimpleName(), msg);
- assertEnumIsUnboxed(Lib.LibEnum.class, Lib.LibEnum.class.getSimpleName(), msg);
- }
- }
-
public static class Lib {
public enum LibEnumStaticMethod {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
index 750c6fe..c531710 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -40,28 +39,21 @@
@Test
public void testEnumUnboxing() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(EnumUnboxingClassStaticizerTest.class)
- .addKeepMainRule(TestClass.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .noMinification() // For assertions.
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::assertClassStaticized)
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsUnboxed(
- UnboxableEnum.class,
- EnumUnboxingClassStaticizerTest.class.getSimpleName(),
- m))
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EnumUnboxingClassStaticizerTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(UnboxableEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .noMinification() // For assertions.
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::assertClassStaticized)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
private void assertClassStaticized(CodeInspector codeInspector) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java
index a0173b5..392807c 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java
@@ -16,7 +16,7 @@
@RunWith(Parameterized.class)
public class EnumUnboxingReturnNullTest extends EnumUnboxingTestBase {
- private static final Class<?> ENUM_CLASS = MyEnum.class;
+ private static final Class<MyEnum> ENUM_CLASS = MyEnum.class;
private static final String EXPECTED_RESULT =
StringUtils.lines(
"print1", "true", "print2", "true", "print2", "false", "0", "print3", "true");
@@ -44,14 +44,12 @@
.addProgramClasses(classToTest, ENUM_CLASS)
.addKeepMainRule(classToTest)
.addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(ENUM_CLASS))
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m))
.run(parameters.getRuntime(), classToTest)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java
index 89621a2..3475ecf 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java
@@ -14,7 +14,7 @@
@RunWith(Parameterized.class)
public class EnumUnboxingSideEffectClInitTest extends EnumUnboxingTestBase {
- private static final Class<?> ENUM_CLASS = MyEnum.class;
+ private static final Class<MyEnum> ENUM_CLASS = MyEnum.class;
private final TestParameters parameters;
private final boolean enumValueOptimization;
private final EnumKeepRules enumKeepRules;
@@ -38,13 +38,11 @@
.addInnerClasses(EnumUnboxingSideEffectClInitTest.class)
.addKeepMainRule(classToTest)
.addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(ENUM_CLASS))
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m))
.run(parameters.getRuntime(), classToTest)
.assertSuccessWithOutputLines("0");
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
index c494152..c36733b 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
@@ -5,11 +5,8 @@
package com.android.tools.r8.enumunboxing;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
-import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -35,6 +32,10 @@
return keepRules;
}
+ public boolean isNone() {
+ return this == NONE;
+ }
+
public boolean isStudio() {
return this == STUDIO;
}
@@ -58,32 +59,6 @@
protected void enableEnumOptions(InternalOptions options, boolean enumValueOptimization) {
options.enableEnumValueOptimization = enumValueOptimization;
options.enableEnumSwitchMapRemoval = enumValueOptimization;
- options.testing.enableEnumUnboxingDebugLogs = true;
- }
-
- protected void assertEnumIsUnboxed(
- Class<?> enumClass, String testName, TestDiagnosticMessages m) {
- assertTrue(enumClass.isEnum());
- assertEnumIsUnboxed(enumClass.getSimpleName(), testName, m);
- }
-
- protected void assertEnumIsUnboxed(String enumClass, String testName, TestDiagnosticMessages m) {
- Diagnostic diagnostic = m.getInfos().get(0);
- assertTrue(diagnostic.getDiagnosticMessage().startsWith("Unboxed enums"));
- assertTrue(
- StringUtils.joinLines(
- "Expected enum to be removed (" + testName + "):",
- m.getInfos().get(1).getDiagnosticMessage()),
- diagnostic.getDiagnosticMessage().contains(enumClass));
- }
-
- void assertEnumIsBoxed(Class<?> enumClass, String testName, TestDiagnosticMessages m) {
- assertTrue(enumClass.isEnum());
- Diagnostic diagnostic = m.getInfos().get(1);
- assertTrue(diagnostic.getDiagnosticMessage().startsWith("Boxed enums"));
- assertTrue(
- "Expected enum NOT to be removed (" + testName + ")",
- diagnostic.getDiagnosticMessage().contains(enumClass.getSimpleName()));
}
static List<Object[]> enumUnboxingTestParameters() {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java
index d8396cb..f240835 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.List;
@@ -37,28 +36,21 @@
@Test
public void testEnumUnboxing() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(EnumUnboxingVerticalClassMergeTest.class)
- .addKeepMainRule(Main.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .noMinification() // For assertions.
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::assertVerticalClassMerged)
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsUnboxed(
- UnboxableEnum.class,
- EnumUnboxingVerticalClassMergeTest.class.getSimpleName(),
- m))
- .run(parameters.getRuntime(), Main.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EnumUnboxingVerticalClassMergeTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(UnboxableEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .noMinification() // For assertions.
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::assertVerticalClassMerged)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
private void assertVerticalClassMerged(CodeInspector codeInspector) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
index 2888ee4..4e38f3f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -35,23 +34,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> success = EnumEqualscompareTo.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(EqualsCompareToEnumUnboxingTest.class)
- .addKeepMainRule(EnumEqualscompareTo.class)
- .enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRules())
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsUnboxed(
- success.getDeclaredClasses()[0], success.getSimpleName(), m))
- .run(parameters.getRuntime(), success)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EqualsCompareToEnumUnboxingTest.class)
+ .addKeepMainRule(EnumEqualscompareTo.class)
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertUnboxed(EnumEqualscompareTo.MyEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), success)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
static class EnumEqualscompareTo {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
index 8bf8034..e3df746 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -34,25 +33,19 @@
@Test
public void testEnumUnboxingFailure() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(FailingEnumUnboxingTest.class)
- .addKeepMainRule(EnumStaticFieldMain.class)
- .enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRules())
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsBoxed(
- EnumStaticFieldMain.EnumStaticField.class,
- EnumStaticFieldMain.class.getSimpleName(),
- m))
- .run(parameters.getRuntime(), EnumStaticFieldMain.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(FailingEnumUnboxingTest.class)
+ .addKeepMainRule(EnumStaticFieldMain.class)
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertNotUnboxed(EnumStaticFieldMain.EnumStaticField.class))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), EnumStaticFieldMain.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
static class EnumStaticFieldMain {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
index 702ce3e..ce3e3f9 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.EnumSet;
@@ -21,7 +21,7 @@
@RunWith(Parameterized.class)
public class FailingMethodEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?>[] FAILURES = {
+ private static final Class<?>[] TESTS = {
InstanceFieldPutObject.class,
StaticFieldPutObject.class,
EnumSetTest.class,
@@ -50,30 +50,31 @@
R8TestCompileResult compile =
testForR8(parameters.getBackend())
.addInnerClasses(FailingMethodEnumUnboxingTest.class)
- .addKeepMainRules(FAILURES)
+ .addKeepMainRules(TESTS)
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertNotUnboxed(
+ InstanceFieldPutObject.MyEnum.class,
+ StaticFieldPutObject.MyEnum.class,
+ EnumSetTest.MyEnum.class,
+ FailingPhi.MyEnum.class,
+ FailingReturnType.MyEnum.class,
+ FailingParameterType.MyEnum.class))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::assertEnumsAsExpected);
- for (Class<?> failure : FAILURES) {
- R8TestRunResult run =
- compile
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsBoxed(
- failure.getDeclaredClasses()[0], failure.getSimpleName(), m))
- .run(parameters.getRuntime(), failure);
- if (failure == EnumSetTest.class && enumKeepRules.getKeepRules().isEmpty()) {
- // EnumSet and EnumMap cannot be used without the enumKeepRules.
- run.assertFailure();
- } else {
- run.assertSuccess();
- assertLines2By2Correct(run.getStdOut());
- }
+ for (Class<?> main : TESTS) {
+ compile
+ .run(parameters.getRuntime(), main)
+ .applyIf(
+ main == EnumSetTest.class && enumKeepRules.getKeepRules().isEmpty(),
+ // EnumSet and EnumMap cannot be used without the enumKeepRules.
+ SingleTestRunResult::assertFailure,
+ result -> result.assertSuccess().inspectStdOut(this::assertLines2By2Correct));
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
index 4d222d5..d7627a6 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -47,7 +46,10 @@
.addKeepMainRules(INPUTS)
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertUnboxed(
+ InstanceFieldPut.MyEnum.class, StaticFieldPut.MyEnum.class))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -62,14 +64,11 @@
1, i.clazz(StaticFieldPut.class).getDexProgramClass().staticFields().size());
});
- for (Class<?> input : INPUTS) {
- R8TestRunResult run =
- compile
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(input.getDeclaredClasses()[0], input.getSimpleName(), m))
- .run(parameters.getRuntime(), input)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ for (Class<?> main : INPUTS) {
+ compile
+ .run(parameters.getRuntime(), main)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
index dc21961..e359c36 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -18,15 +17,11 @@
@RunWith(Parameterized.class)
public class InstanceFieldsEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?>[] FAILURES = {
+ private static final Class<?>[] TESTS = {
FailureIntField.class,
- FailurePrivateIntField.class,
FailureBoxedInnerEnumField.class,
FailureUnboxedEnumField.class,
- FailureTooManyUsedFields.class
- };
-
- private static final Class<?>[] SUCCESSES = {
+ FailureTooManyUsedFields.class,
SuccessUnusedField.class,
SuccessIntField.class,
SuccessDoubleField.class,
@@ -34,6 +29,7 @@
SuccessIntFieldInitializerInit.class,
SuccessStringField.class,
SuccessMultiConstructorIntField.class,
+ SuccessPrivateIntField.class,
};
private final TestParameters parameters;
@@ -57,44 +53,41 @@
R8TestCompileResult compile =
testForR8(parameters.getBackend())
.addInnerClasses(InstanceFieldsEnumUnboxingTest.class)
- .addKeepMainRules(SUCCESSES)
- .addKeepMainRules(FAILURES)
+ .addKeepMainRules(TESTS)
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector
+ .assertUnboxed(
+ SuccessUnusedField.EnumField.class,
+ SuccessIntField.EnumField.class,
+ SuccessDoubleField.EnumField.class,
+ SuccessIntFieldOrdinal.EnumField.class,
+ SuccessIntFieldInitializerInit.EnumField.class,
+ SuccessStringField.EnumField.class,
+ SuccessMultiConstructorIntField.EnumField.class,
+ SuccessPrivateIntField.EnumField.class)
+ .assertNotUnboxed(
+ FailureIntField.EnumField.class,
+ FailureBoxedInnerEnumField.EnumField.class,
+ FailureUnboxedEnumField.EnumField.class,
+ FailureTooManyUsedFields.EnumField.class))
.noMinification()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile();
- for (Class<?> failure : FAILURES) {
- testClass(compile, failure, true);
- }
- for (Class<?> success : SUCCESSES) {
- testClass(compile, success, false);
+ for (Class<?> main : TESTS) {
+ testClass(compile, main);
}
}
- private void testClass(R8TestCompileResult compile, Class<?> testClass, boolean failure)
- throws Exception {
- R8TestRunResult run =
- compile
- .inspectDiagnosticMessages(
- m -> {
- for (Class<?> declaredClass : testClass.getDeclaredClasses()) {
- if (declaredClass.isEnum()
- && !declaredClass.getSimpleName().equals("InnerEnum")) {
- if (failure) {
- assertEnumIsBoxed(declaredClass, testClass.getSimpleName(), m);
- } else {
- assertEnumIsUnboxed(declaredClass, testClass.getSimpleName(), m);
- }
- }
- }
- })
- .run(parameters.getRuntime(), testClass)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ private void testClass(R8TestCompileResult compile, Class<?> testClass) throws Exception {
+ compile
+ .run(parameters.getRuntime(), testClass)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
static class SuccessUnusedField {
@@ -161,7 +154,7 @@
}
}
- static class FailurePrivateIntField {
+ static class SuccessPrivateIntField {
public static void main(String[] args) {
System.out.println(getEnumA().field);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
index 7311ab2..2f27855 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -19,11 +18,9 @@
@RunWith(Parameterized.class)
public class InterfaceEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?>[] FAILURES = {
- FailureDefaultMethodUsed.class, FailureUsedAsInterface.class,
- };
-
- private static final Class<?>[] SUCCESSES = {
+ private static final Class<?>[] TESTS = {
+ FailureDefaultMethodUsed.class,
+ FailureUsedAsInterface.class,
SuccessAbstractMethod.class,
SuccessEmptyInterface.class,
SuccessUnusedDefaultMethod.class,
@@ -52,44 +49,37 @@
R8TestCompileResult compile =
testForR8(parameters.getBackend())
.addInnerClasses(InterfaceEnumUnboxingTest.class)
- .addKeepMainRules(SUCCESSES)
- .addKeepMainRules(FAILURES)
+ .addKeepMainRules(TESTS)
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector
+ .assertUnboxed(
+ SuccessAbstractMethod.EnumInterface.class,
+ SuccessEmptyInterface.EnumInterface.class,
+ SuccessUnusedDefaultMethod.EnumInterface.class,
+ SuccessUnusedDefaultMethodOverride.EnumInterface.class,
+ SuccessUnusedDefaultMethodOverrideEnum.EnumInterface.class)
+ .assertNotUnboxed(
+ FailureDefaultMethodUsed.EnumInterface.class,
+ FailureUsedAsInterface.EnumInterface.class))
.noMinification()
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile();
- for (Class<?> failure : FAILURES) {
- testClass(compile, failure, true);
- }
- for (Class<?> success : SUCCESSES) {
- testClass(compile, success, false);
+ for (Class<?> main : TESTS) {
+ testClass(compile, main);
}
}
- private void testClass(R8TestCompileResult compile, Class<?> testClass, boolean failure)
- throws Exception {
- R8TestRunResult run =
- compile
- .inspectDiagnosticMessages(
- m -> {
- for (Class<?> declaredClass : testClass.getDeclaredClasses()) {
- if (declaredClass.isEnum()) {
- if (failure) {
- assertEnumIsBoxed(declaredClass, testClass.getSimpleName(), m);
- } else {
- assertEnumIsUnboxed(declaredClass, testClass.getSimpleName(), m);
- }
- }
- }
- })
- .run(parameters.getRuntime(), testClass)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ private void testClass(R8TestCompileResult compile, Class<?> testClass) throws Exception {
+ compile
+ .run(parameters.getRuntime(), testClass)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
static class SuccessEmptyInterface {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java b/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java
index 5d4501f..aaf0239 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -15,7 +14,7 @@
@RunWith(Parameterized.class)
public class JavaCGeneratedMethodTest extends EnumUnboxingTestBase {
- private static final Class<?> ENUM_CLASS = MyEnum.class;
+ private static final Class<MyEnum> ENUM_CLASS = MyEnum.class;
private final TestParameters parameters;
private final boolean enumValueOptimization;
@@ -36,23 +35,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> classToTest = Ordinal.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(classToTest, ENUM_CLASS)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .addOptionsModification(
- opt -> opt.testing.enumUnboxingRewriteJavaCGeneratedMethod = true)
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m))
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(classToTest, ENUM_CLASS)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(ENUM_CLASS))
+ .enableNeverClassInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .addOptionsModification(opt -> opt.testing.enumUnboxingRewriteJavaCGeneratedMethod = true)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java
index d6eb6d1..aba1bb8 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java
@@ -51,7 +51,6 @@
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .addOptionsModification(options -> options.testing.enableEnumUnboxingDebugLogs = false)
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/LargeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/LargeEnumUnboxingTest.java
index 66c861a..ca48de2 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/LargeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/LargeEnumUnboxingTest.java
@@ -97,13 +97,11 @@
.addProgramClasses(mainClass, LargeEnum.class)
.addKeepMainRule(mainClass)
.addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(LargeEnum.class))
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(LargeEnum.class, mainClass.getSimpleName(), m))
.run(parameters.getRuntime(), mainClass)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
index 0f313cf..e8cc088 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import java.util.Objects;
@@ -36,28 +35,24 @@
@Test
public void testEnumUnboxing() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(NullCheckEnumUnboxingTest.class)
- .addKeepMainRule(MainNullTest.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> {
- assertEnumIsUnboxed(MyEnum.class, MyEnum.class.getSimpleName(), m);
- // MyEnum19 is always unboxed. If minAPI > 19 the unboxer will identify
- // Objects#requiredNonNull usage. For 19 and prior, the backport code should not
- // prohibit the unboxing either.
- assertEnumIsUnboxed(MyEnum19.class, MyEnum19.class.getSimpleName(), m);
- })
- .run(parameters.getRuntime(), MainNullTest.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(NullCheckEnumUnboxingTest.class)
+ .addKeepMainRule(MainNullTest.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ // MyEnum19 is always unboxed. If minAPI > 19 the unboxer will identify
+ // Objects#requiredNonNull usage. For 19 and prior, the backport code should not
+ // prohibit the unboxing either.
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertUnboxed(MyEnum.class, MyEnum19.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), MainNullTest.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java
index 8e15ad1..80eb9ab 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -17,7 +16,7 @@
@RunWith(Parameterized.class)
public class NullOutValueInvokeEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?> ENUM_CLASS = MyEnum.class;
+ private static final Class<MyEnum> ENUM_CLASS = MyEnum.class;
private final TestParameters parameters;
private final boolean enumValueOptimization;
@@ -38,22 +37,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> classToTest = NullOutValueInvoke.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(classToTest, ENUM_CLASS)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m))
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(classToTest, ENUM_CLASS)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(ENUM_CLASS))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
index 1946116..49b4f76 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -17,7 +16,7 @@
@RunWith(Parameterized.class)
public class OrdinalHashCodeEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?> ENUM_CLASS = MyEnum.class;
+ private static final Class<MyEnum> ENUM_CLASS = MyEnum.class;
private final TestParameters parameters;
private final boolean enumValueOptimization;
@@ -38,22 +37,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> classToTest = OrdinalHashCode.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(classToTest, ENUM_CLASS)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m))
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(classToTest, ENUM_CLASS)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(ENUM_CLASS))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java
index bf038fa..5df98f1 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -36,25 +35,20 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> classToTest = TestClass.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(OverloadingEnumUnboxingTest.class)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> {
- assertEnumIsUnboxed(MyEnum1.class, MyEnum1.class.getSimpleName(), m);
- assertEnumIsUnboxed(MyEnum2.class, MyEnum2.class.getSimpleName(), m);
- })
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(OverloadingEnumUnboxingTest.class)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertUnboxed(MyEnum1.class, MyEnum2.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@SuppressWarnings("SameParameterValue")
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java
index d2851c1..536d066 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -35,22 +34,19 @@
@Test
public void testEnumUnboxing() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(Phi.class, MyEnum.class)
- .addKeepMainRule(Phi.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableInliningAnnotations()
- .enableNeverClassInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(MyEnum.class, Phi.class.getSimpleName(), m))
- .run(parameters.getRuntime(), Phi.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Phi.class, MyEnum.class)
+ .addKeepMainRule(Phi.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Phi.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java
index 1c34891..6391a99 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.enumunboxing.PinnedEnumUnboxingTest.MainWithKeptEnum.MyEnum;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -16,7 +17,7 @@
@RunWith(Parameterized.class)
public class PinnedEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?>[] BOXED = {MainWithKeptEnum.class, MainWithKeptEnumArray.class};
+ private static final Class<?>[] TESTS = {MainWithKeptEnum.class, MainWithKeptEnumArray.class};
private final TestParameters parameters;
private final boolean enumValueOptimization;
@@ -39,21 +40,20 @@
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addInnerClasses(PinnedEnumUnboxingTest.class)
- .addKeepMainRules(BOXED)
- .addKeepClassRules(MainWithKeptEnum.MyEnum.class)
+ .addKeepMainRules(TESTS)
+ .addKeepClassRules(MyEnum.class)
.addKeepMethodRules(MainWithKeptEnumArray.class, "keptMethod()")
.addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertNotUnboxed(
+ MainWithKeptEnum.MyEnum.class, MainWithKeptEnumArray.MyEnum.class))
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile();
- for (Class<?> boxed : BOXED) {
- compileResult
- .inspectDiagnosticMessages(
- m -> assertEnumIsBoxed(boxed.getDeclaredClasses()[0], boxed.getSimpleName(), m))
- .run(parameters.getRuntime(), boxed)
- .assertSuccessWithOutputLines("0");
+ for (Class<?> main : TESTS) {
+ compileResult.run(parameters.getRuntime(), main).assertSuccessWithOutputLines("0");
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java
index 390fe18..43be887 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -36,25 +35,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> classToTest = StaticMethods.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(StaticMethodsEnumUnboxingTest.class)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> {
- assertEnumIsUnboxed(MyEnum.class, classToTest.getSimpleName(), m);
- assertEnumIsUnboxed(MyEnum2.class, classToTest.getSimpleName(), m);
- })
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(StaticMethodsEnumUnboxingTest.class)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class, MyEnum2.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@SuppressWarnings("SameParameterValue")
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
index 1ab86b3..904fc48f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -34,22 +33,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> classToTest = Main.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(classToTest, MyEnum.class)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(MyEnum.class, classToTest.getSimpleName(), m))
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(classToTest, MyEnum.class)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
index f58a069..43dec5d 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -22,7 +21,7 @@
@RunWith(Parameterized.class)
public class SwitchEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?> ENUM_CLASS = MyEnumFewCases.class;
+ private static final Class<MyEnumFewCases> ENUM_CLASS = MyEnumFewCases.class;
private final TestParameters parameters;
private final boolean enumValueOptimization;
@@ -43,24 +42,21 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<Switch> classToTest = Switch.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(SwitchEnumUnboxingTest.class)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableInliningAnnotations()
- .enableNeverClassInliningAnnotations()
- .noMinification() // For assertions.
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::assertSwitchPresentButSwitchMapRemoved)
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m))
- .run(parameters.getRuntime(), classToTest)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(SwitchEnumUnboxingTest.class)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(ENUM_CLASS))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .noMinification() // For assertions.
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::assertSwitchPresentButSwitchMapRemoved)
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
private void assertSwitchPresentButSwitchMapRemoved(CodeInspector i) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
index 50a4d58..99e4e25 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -35,23 +34,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> success = EnumNameToString.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(ToStringEnumUnboxingTest.class)
- .addKeepMainRule(EnumNameToString.class)
- .enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRules())
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsUnboxed(
- success.getDeclaredClasses()[0], success.getSimpleName(), m))
- .run(parameters.getRuntime(), success)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ToStringEnumUnboxingTest.class)
+ .addKeepMainRule(EnumNameToString.class)
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertUnboxed(EnumNameToString.MyEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), success)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
static class EnumNameToString {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
index 8b9b546..d8d115c 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -35,21 +34,19 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> success = EnumNameToString.class;
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(ToStringOverrideEnumUnboxingTest.class)
- .addKeepMainRule(EnumNameToString.class)
- .enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRules())
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsBoxed(success.getDeclaredClasses()[0], success.getSimpleName(), m))
- .run(parameters.getRuntime(), success)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ToStringOverrideEnumUnboxingTest.class)
+ .addKeepMainRule(EnumNameToString.class)
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertNotUnboxed(EnumNameToString.MyEnum.class))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), success)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
static class EnumNameToString {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/UnusedCaseEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/UnusedCaseEnumUnboxingTest.java
index a6b7027..6a35396 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/UnusedCaseEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/UnusedCaseEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.List;
@@ -34,23 +33,20 @@
@Test
public void testEnumUnboxing() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addInnerClasses(UnusedCaseEnumUnboxingTest.class)
- .addKeepMainRule(Main.class)
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRules())
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::assertFieldsRemoved)
- .inspectDiagnosticMessages(
- m -> assertEnumIsUnboxed(MyEnum.class, Main.class.getSimpleName(), m))
- .run(parameters.getRuntime(), Main.class)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(UnusedCaseEnumUnboxingTest.class)
+ .addKeepMainRule(Main.class)
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::assertFieldsRemoved)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
private void assertFieldsRemoved(CodeInspector codeInspector) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
index 1da8c62..15bdcb7 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
@@ -36,14 +36,12 @@
testForR8(parameters.getBackend())
.addInnerClasses(ValueOfEnumUnboxingFailureTest.class)
.addKeepMainRule(success)
+ .addEnumUnboxingInspector(inspector -> inspector.assertNotUnboxed(Main.Enum.class))
.enableNeverClassInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(
- m -> assertEnumIsBoxed(success.getDeclaredClasses()[0], success.getSimpleName(), m))
.run(parameters.getRuntime(), success)
.assertSuccessWithOutput("VALUE1");
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
index 23293f5..eabf368 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -17,7 +16,7 @@
@RunWith(Parameterized.class)
public class ValueOfEnumUnboxingTest extends EnumUnboxingTestBase {
- private static final Class<?>[] SUCCESSES = {
+ private static final Class<?>[] TESTS = {
EnumValueOf.class,
};
@@ -42,23 +41,19 @@
R8TestCompileResult compile =
testForR8(parameters.getBackend())
.addInnerClasses(ValueOfEnumUnboxingTest.class)
- .addKeepMainRules(SUCCESSES)
+ .addKeepMainRules(TESTS)
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertUnboxed(EnumValueOf.MyEnum.class))
.enableNeverClassInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile();
- for (Class<?> success : SUCCESSES) {
- R8TestRunResult run =
- compile
- .inspectDiagnosticMessages(
- m ->
- assertEnumIsUnboxed(
- success.getDeclaredClasses()[0], success.getSimpleName(), m))
- .run(parameters.getRuntime(), success)
- .assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ for (Class<?> main : TESTS) {
+ compile
+ .run(parameters.getRuntime(), main)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
index 6020f1a..4c69310 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
@@ -49,7 +49,6 @@
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.addOptionsModification(options -> enableEnumOptions(options, enumValueOptimization))
- .addOptionsModification(options -> options.testing.enableEnumUnboxingDebugLogs = false)
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
index 52d8688..03e6de9 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
@@ -67,7 +67,6 @@
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
index 3a1fbab..c24a4d9 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
@@ -5,8 +5,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import java.util.List;
import org.junit.Test;
@@ -35,33 +33,28 @@
@Test
public void testEnumUnboxing() throws Exception {
Class<?> classToTest = VirtualMethods.class;
- R8TestCompileResult compile =
- testForR8(parameters.getBackend())
- .addInnerClasses(VirtualMethodsEnumUnboxingTest.class)
- .addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRules())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- m -> {
- assertEnumIsUnboxed(MyEnum.class, classToTest.getSimpleName(), m);
- assertEnumIsUnboxed(MyEnum2.class, classToTest.getSimpleName(), m);
- assertEnumIsUnboxed(MyEnumWithCollisions.class, classToTest.getSimpleName(), m);
- assertEnumIsUnboxed(
- MyEnumWithPackagePrivateCall.class, classToTest.getSimpleName(), m);
- assertEnumIsUnboxed(
- MyEnumWithProtectedCall.class, classToTest.getSimpleName(), m);
- assertEnumIsUnboxed(
- MyEnumWithPackagePrivateFieldAccess.class, classToTest.getSimpleName(), m);
- assertEnumIsUnboxed(
- MyEnumWithPackagePrivateAndPrivateCall.class, classToTest.getSimpleName(), m);
- });
- R8TestRunResult run = compile.run(parameters.getRuntime(), classToTest).assertSuccess();
- assertLines2By2Correct(run.getStdOut());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(VirtualMethodsEnumUnboxingTest.class)
+ .addKeepMainRule(classToTest)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertUnboxed(
+ MyEnum.class,
+ MyEnum2.class,
+ MyEnumWithCollisions.class,
+ MyEnumWithPackagePrivateCall.class,
+ MyEnumWithProtectedCall.class,
+ MyEnumWithPackagePrivateFieldAccess.class,
+ MyEnumWithPackagePrivateAndPrivateCall.class))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), classToTest)
+ .assertSuccess()
+ .inspectStdOut(this::assertLines2By2Correct);
}
@SuppressWarnings("SameParameterValue")
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
index 6f4b565..2d89396 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
@@ -71,19 +71,17 @@
.addKeepRules(enumKeepRules.getKeepRules())
.addKeepRuntimeVisibleAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(PKG + ".Color"))
.allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.inspectDiagnosticMessages(
- messages -> {
- messages
- .assertNoErrors()
- .assertAllWarningsMatch(
- diagnosticMessage(
- containsString("Resource 'META-INF/MANIFEST.MF' already exists.")));
- assertEnumIsUnboxed(
- PKG + ".Color", SimpleKotlinEnumUnboxingTest.class.getSimpleName(), messages);
- })
+ messages ->
+ messages
+ .assertNoErrors()
+ .assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists."))))
.run(parameters.getRuntime(), PKG + ".MainKt")
.assertSuccessWithOutputLines("RED", "GREEN", "BLUE");
}
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index b165f4b..83a8577 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -18,8 +18,8 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -38,9 +38,6 @@
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
-import it.unimi.dsi.fastutil.ints.IntList;
-import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
-import it.unimi.dsi.fastutil.ints.IntSet;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -64,7 +61,7 @@
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
-public abstract class CompilationTestBase extends TestBase {
+public abstract class CompilationTestBase extends DesugaredLibraryTestBase {
protected KeepingDiagnosticHandler handler;
protected Reporter reporter;
diff --git a/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java
index 202878c..f6de3f9 100644
--- a/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import org.junit.Test;
-public class D8YouTubeDeployJarVerificationTest extends YouTubeCompilationBase {
+public class D8YouTubeDeployJarVerificationTest extends YouTubeCompilationTestBase {
public D8YouTubeDeployJarVerificationTest() {
super(12, 17);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
similarity index 66%
rename from src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
rename to src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
index c8be865..b9c868c 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.ToolHelper;
@@ -14,7 +16,7 @@
import java.util.ArrayList;
import java.util.List;
-public abstract class YouTubeCompilationBase extends CompilationTestBase {
+public abstract class YouTubeCompilationTestBase extends CompilationTestBase {
static final String APK = "YouTubeRelease_unsigned.apk";
static final String DEPLOY_JAR = "YouTubeRelease_deploy.jar";
@@ -26,7 +28,7 @@
final String base;
- public YouTubeCompilationBase(int majorVersion, int minorVersion) {
+ public YouTubeCompilationTestBase(int majorVersion, int minorVersion) {
this.base =
"third_party/youtube/youtube.android_"
+ majorVersion
@@ -35,6 +37,33 @@
+ "/";
}
+ protected Path getDesugaredLibraryConfiguration() {
+ Path path = Paths.get(base, "desugar_jdk_libs/full_desugar_jdk_libs.json");
+ assertTrue(path.toFile().exists());
+ return path;
+ }
+
+ protected Path getDesugaredLibraryJDKLibs() {
+ Path path = Paths.get(base, "desugar_jdk_libs/jdk_libs_to_desugar.jar");
+ assertTrue(path.toFile().exists());
+ return path;
+ }
+
+ protected Path getDesugaredLibraryJDKLibsConfiguration() {
+ Path path = Paths.get(base, "desugar_jdk_libs/desugar_jdk_libs_configuration.jar");
+ assertTrue(path.toFile().exists());
+ return path;
+ }
+
+ protected List<Path> getDesugaredLibraryKeepRuleFiles() {
+ ImmutableList<Path> keepRuleFiles =
+ ImmutableList.of(
+ Paths.get(base, "desugar_jdk_libs/base.pgcfg"),
+ Paths.get(base, "desugar_jdk_libs/minify_desugar_jdk_libs.pgcfg"));
+ assertTrue(keepRuleFiles.stream().allMatch(keepRuleFile -> keepRuleFile.toFile().exists()));
+ return keepRuleFiles;
+ }
+
protected List<Path> getKeepRuleFiles() {
ImmutableList.Builder<Path> builder = ImmutableList.builder();
builder.add(Paths.get(base).resolve(PG_CONF));
@@ -49,7 +78,14 @@
}
protected List<Path> getLibraryFiles() {
- return ImmutableList.of(Paths.get(base, "legacy_YouTubeRelease_combined_library_jars.jar"));
+ Path filtered =
+ Paths.get(base).resolve("legacy_YouTubeRelease_combined_library_jars_filtered.jar");
+ if (filtered.toFile().exists()) {
+ return ImmutableList.of(filtered);
+ }
+ Path unfiltered = Paths.get(base, "legacy_YouTubeRelease_combined_library_jars.jar");
+ assertTrue(unfiltered.toFile().exists());
+ return ImmutableList.of(unfiltered);
}
protected List<Path> getMainDexRuleFiles() {
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
index 5bf893d..f6b78f6 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import org.junit.Test;
-public class YouTubeDeployJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeDeployJarVerificationTest extends YouTubeCompilationTestBase {
public YouTubeDeployJarVerificationTest() {
super(12, 17);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
index a01ee01..32b7973 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.CompilationMode;
import org.junit.Test;
-public class YouTubeDexVerificationTest extends YouTubeCompilationBase {
+public class YouTubeDexVerificationTest extends YouTubeCompilationTestBase {
public YouTubeDexVerificationTest() {
super(12, 17);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
index 5092831..ffe4e14 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
@@ -8,7 +8,7 @@
import com.google.common.collect.ImmutableList;
import org.junit.Test;
-public class YouTubeProguardJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeProguardJarVerificationTest extends YouTubeCompilationTestBase {
public YouTubeProguardJarVerificationTest() {
super(12, 17);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1217TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1217TreeShakeJarVerificationTest.java
index 69b4748..5f39a31 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1217TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1217TreeShakeJarVerificationTest.java
@@ -14,7 +14,7 @@
import java.nio.file.Path;
import org.junit.Test;
-public class YouTubeV1217TreeShakeJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeV1217TreeShakeJarVerificationTest extends YouTubeCompilationTestBase {
public YouTubeV1217TreeShakeJarVerificationTest() {
super(12, 17);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
index 15d8928..42fc8f7 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
@@ -23,7 +23,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class YouTubeV1419TreeShakeJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeV1419TreeShakeJarVerificationTest extends YouTubeCompilationTestBase {
private static final int MAX_SIZE = 27500000;
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
index f2afcb4..a998521 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
@@ -23,7 +23,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class YouTubeV1444TreeShakeJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeV1444TreeShakeJarVerificationTest extends YouTubeCompilationTestBase {
private static final boolean DUMP = false;
private static final int MAX_SIZE = 27500000;
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
index 406ff62..eccb336d 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
@@ -23,7 +23,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class YouTubeV1508TreeShakeJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeV1508TreeShakeJarVerificationTest extends YouTubeCompilationTestBase {
private static final boolean DUMP = false;
private static final int MAX_SIZE = 27500000;
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
index 88c7692..1fe6af5 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
@@ -3,10 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
-import static com.android.tools.r8.ToolHelper.isLocalDevelopment;
-import static com.android.tools.r8.ToolHelper.shouldRunSlowTests;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
@@ -23,7 +20,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class YouTubeV1533TreeShakeJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeV1533TreeShakeJarVerificationTest extends YouTubeCompilationTestBase {
private static final boolean DUMP = false;
private static final int MAX_SIZE = 27500000;
@@ -41,8 +38,8 @@
@Test
public void testR8() throws Exception {
// TODO(b/141603168): Enable this on the bots.
- assumeTrue(isLocalDevelopment());
- assumeTrue(shouldRunSlowTests());
+ // assumeTrue(isLocalDevelopment());
+ // assumeTrue(shouldRunSlowTests());
LibrarySanitizer librarySanitizer =
new LibrarySanitizer(temp)
@@ -57,7 +54,9 @@
.addLibraryFiles(librarySanitizer.getSanitizedLibrary())
.addKeepRuleFiles(getKeepRuleFiles())
.addMainDexRuleFiles(getMainDexRuleFiles())
+ .addIgnoreWarnings()
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.setMinApi(AndroidApiLevel.H_MR2)
.compile();
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1612TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1612TreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..0f07300
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1612TreeShakeJarVerificationTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2021, 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.internal;
+
+import static com.android.tools.r8.ToolHelper.isLocalDevelopment;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.analysis.ProtoApplicationStats;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class YouTubeV1612TreeShakeJarVerificationTest extends YouTubeCompilationTestBase {
+
+ private static final boolean DUMP = false;
+ private static final int MAX_SIZE = 30000000;
+
+ private final Path dumpDirectory = Paths.get("YouTubeV1612-" + System.currentTimeMillis());
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public YouTubeV1612TreeShakeJarVerificationTest(TestParameters parameters) {
+ super(16, 12);
+ parameters.assertNoneRuntime();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(isLocalDevelopment());
+
+ KeepRuleConsumer keepRuleConsumer = new PresentKeepRuleConsumer();
+ R8TestCompileResult compileResult =
+ testForR8(Backend.DEX)
+ .addProgramFiles(getProgramFiles())
+ .addLibraryFiles(getLibraryFiles())
+ .addKeepRuleFiles(getKeepRuleFiles())
+ .addIgnoreWarnings()
+ .allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules()
+ .setMinApi(AndroidApiLevel.L)
+ .enableCoreLibraryDesugaring(
+ AndroidApiLevel.L,
+ keepRuleConsumer,
+ StringResource.fromFile(getDesugaredLibraryConfiguration()))
+ .compile();
+
+ if (ToolHelper.isLocalDevelopment()) {
+ if (DUMP) {
+ dumpDirectory.toFile().mkdirs();
+ compileResult.writeToZip(dumpDirectory.resolve("app.zip"));
+ compileResult.writeProguardMap(dumpDirectory.resolve("mapping.txt"));
+ }
+
+ DexItemFactory dexItemFactory = new DexItemFactory();
+ ProtoApplicationStats original =
+ new ProtoApplicationStats(dexItemFactory, new CodeInspector(getProgramFiles()));
+ ProtoApplicationStats actual =
+ new ProtoApplicationStats(dexItemFactory, compileResult.inspector(), original);
+ ProtoApplicationStats baseline =
+ new ProtoApplicationStats(
+ dexItemFactory, new CodeInspector(getReleaseApk(), getReleaseProguardMap()));
+ System.out.println(actual.getStats(baseline));
+ }
+
+ int applicationSize = compileResult.getApp().applicationSize();
+ System.out.println("Dex size (app, excluding desugared library): " + applicationSize);
+
+ Path desugaredLibrary =
+ testForL8(AndroidApiLevel.L)
+ .setDesugaredLibraryConfiguration(getDesugaredLibraryConfiguration())
+ .setDesugarJDKLibs(getDesugaredLibraryJDKLibs())
+ .setDesugarJDKLibsConfiguration(getDesugaredLibraryJDKLibsConfiguration())
+ .addKeepRules(keepRuleConsumer.get())
+ .addKeepRuleFiles(getDesugaredLibraryKeepRuleFiles())
+ .compile();
+
+ byte[] desugaredLibraryDex = ZipUtils.readSingleEntry(desugaredLibrary, "classes.dex");
+
+ if (DUMP) {
+ Files.write(dumpDirectory.resolve("desugared_jdk_libs.dex"), desugaredLibraryDex);
+ }
+
+ int desugaredLibrarySize = desugaredLibraryDex.length;
+ System.out.println("Dex size (desugared library): " + desugaredLibrarySize);
+
+ int totalApplicationSize = applicationSize + desugaredLibrarySize;
+ System.out.println("Dex size (total): " + totalApplicationSize);
+
+ assertTrue(
+ "Expected max size of " + MAX_SIZE + ", got " + totalApplicationSize,
+ totalApplicationSize < MAX_SIZE);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java b/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java
index 7cdab46..dbdeed0 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.internal.LibrarySanitizer;
-import com.android.tools.r8.internal.YouTubeCompilationBase;
+import com.android.tools.r8.internal.YouTubeCompilationTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
@@ -22,7 +22,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class YouTubeV1508ProtoRewritingTest extends YouTubeCompilationBase {
+public class YouTubeV1508ProtoRewritingTest extends YouTubeCompilationTestBase {
@Parameters(name = "{0}")
public static TestParametersCollection data() {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
index c07c431..609c892 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
@@ -76,8 +76,7 @@
public void testReferenceWidth() {
DexItemFactory dexItemFactory = new DexItemFactory();
ClassTypeElement referenceType =
- ClassTypeElement.create(
- dexItemFactory.objectType, Nullability.maybeNull(), InterfaceCollection.empty());
+ ClassTypeElement.createForD8(dexItemFactory.objectType, Nullability.maybeNull());
assertFalse(referenceType.isSinglePrimitive());
assertFalse(referenceType.isWidePrimitive());
assertEquals(1, referenceType.requiredRegisters());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
index 98628aa..3d48fb9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
@@ -70,7 +70,7 @@
private boolean isInvokeSuper(InstructionSubject instruction) {
if (parameters.isCfRuntime()) {
- return instruction.isInvokeSpecial();
+ return instruction.asCfInstruction().isInvokeSpecial();
} else {
return instruction.asDexInstruction().isInvokeSuper();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index f6b24da..65e33d1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0_M2;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -49,11 +50,6 @@
super(parameters, kotlinParameters, true);
}
- private static boolean isLambda(DexClass clazz) {
- return !clazz.getType().getPackageDescriptor().startsWith("kotlin")
- && (isKStyleLambda(clazz) || isJStyleLambda(clazz));
- }
-
private static boolean isKStyleLambda(DexClass clazz) {
return clazz.getSuperType().getTypeName().equals("kotlin.jvm.internal.Lambda");
}
@@ -65,6 +61,8 @@
@Test
public void testJStyleLambdas() throws Exception {
+ // TODO(b/185497606): Unable to class inline j style lambdas.
+ assumeTrue(kotlinc.isNot(KOTLINC_1_5_0_M2));
String mainClassName = "class_inliner_lambda_j_style.MainKt";
runTest(
"class_inliner_lambda_j_style",
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index b0dffa9..548ce4d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
@@ -46,11 +46,11 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
- .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.is(KOTLINC_1_4_20))
- .allowUnusedProguardConfigurationRules(kotlinc.is(KOTLINC_1_4_20))
+ .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
+ .allowUnusedProguardConfigurationRules(kotlinc.isNot(KOTLINC_1_3_72))
.apply(consumer)
.compile()
- .apply(assertUnusedKeepRuleForKotlinMetadata(kotlinc.is(KOTLINC_1_4_20)));
+ .apply(assertUnusedKeepRuleForKotlinMetadata(kotlinc.isNot(KOTLINC_1_3_72)));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 1b2dd74..2b2eed4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0_M2;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestBuilder;
@@ -274,8 +276,12 @@
propertyName, AccessorKind.FROM_COMPANION);
assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
+
+ // kotlinc 1.5 do not generate accessors for public late-init properties.
+ if (kotlinc.isNot(KOTLINC_1_5_0_M2)) {
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ }
});
}
@@ -303,8 +309,12 @@
propertyName, AccessorKind.FROM_COMPANION);
assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
+
+ // kotlinc 1.5 do not generate accessors for public late-init properties.
+ if (kotlinc.isNot(KOTLINC_1_5_0_M2)) {
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ }
});
}
@@ -365,6 +375,8 @@
@Test
public void testAccessorForInnerClassIsRemovedWhenNotUsed() throws Exception {
+ // TODO(b/185493636): Kotlinc 1.5 generated property accessors are not removed.
+ assumeTrue(kotlinc.isNot(KOTLINC_1_5_0_M2));
String mainClass =
addMainToClasspath(
"accessors.PropertyAccessorForInnerClassKt", "noUseOfPropertyAccessorFromInnerClass");
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
index 6fbd439..00d2418 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.lambda;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0_M2;
import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
import static com.android.tools.r8.utils.PredicateUtils.not;
import static junit.framework.TestCase.assertEquals;
@@ -73,6 +74,8 @@
@Test
public void testR8() throws Exception {
+ // TODO(b/185497606): Unable to merge jstyle lambda.
+ assumeTrue(kotlinc.isNot(KOTLINC_1_5_0_M2));
testForR8(parameters.getBackend())
.addProgramFiles(getProgramFiles())
.addKeepMainRule(getMainClassName())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index 401087b..deb4e5e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0_M2;
import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
@@ -139,9 +140,9 @@
KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
assertThat(age, isPresent());
assertThat(age, not(isExtensionProperty()));
- assertEquals(age.fieldSignature().asString(), "a:I");
- assertEquals(age.getterSignature().asString(), "getAge()I");
- assertEquals(age.setterSignature().asString(), "setAge(I)V");
+ assertEquals(kotlinc.is(KOTLINC_1_5_0_M2) ? "b:I" : "a:I", age.fieldSignature().asString());
+ assertEquals("getAge()I", age.getterSignature().asString());
+ assertEquals("setAge(I)V", age.setterSignature().asString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index 90907da..4f5f64f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -163,8 +164,15 @@
.compileRaw();
assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("cannot access"));
- assertThat(kotlinTestCompileResult.stderr, containsString("private in 'Expr'"));
+ if (kotlinc.is(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
+ assertThat(
+ kotlinTestCompileResult.stderr,
+ containsString(
+ "inheritance of sealed classes or interfaces from different module is prohibited"));
+ } else {
+ assertThat(kotlinTestCompileResult.stderr, containsString("cannot access"));
+ assertThat(kotlinTestCompileResult.stderr, containsString("private in 'Expr'"));
+ }
}
private void inspectInvalid(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 0e03e02..6406593 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
import static com.android.tools.r8.ToolHelper.getKotlinReflectJar;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -60,7 +60,7 @@
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
- .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.is(KOTLINC_1_4_20))
+ .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
.compile()
.apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib)
.run(parameters.getRuntime(), mainClassName);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt
index d7ed237..414da89 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt
@@ -28,7 +28,7 @@
val nested = Quux::methodWithNestedAnnotations.returnType.arguments[0].type?.annotations?.get(0) as Nested
println(nested.message)
nested.kept.printAnnoWithClassAndEnum()
- if (nested::class::memberProperties.get().any { it.name.equals("notKept") }) {
+ if (nested::class.memberProperties.any { it.name.equals("notKept") }) {
println("com.android.tools.r8.kotlin.metadata.annotation_lib.Foo")
} else {
println("a.b.c")
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index fcb64b6..4e376ed 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.kotlin.reflection;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
@@ -92,7 +92,7 @@
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
- .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.is(KOTLINC_1_4_20))
+ .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
.compile()
.writeToZip(foo.toPath())
.apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib)
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
index 413a4fd..e25fd15 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
@@ -70,6 +71,9 @@
ClassSubject enumClass = inspector.clazz(ENUM_CLASS_NAME);
assertThat(enumClass, isPresent());
assertEquals(minify, enumClass.isRenamed());
- assertThat(enumClass.clinit(), isAbsent());
+ // TODO(b/179994975): Kotlin enum changed in 1.5.
+ assertThat(
+ enumClass.clinit(),
+ kotlinc.is(KotlinCompilerVersion.KOTLINC_1_5_0_M2) ? isPresent() : isAbsent());
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java b/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
index 1412ed0..c678a70 100644
--- a/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
@@ -10,8 +10,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
-import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import org.junit.Assert;
@@ -81,13 +79,11 @@
String finalName, String originalName, boolean isSynthesized, ClassNameMapper mapper) {
ClassNamingForNameMapper naming = mapper.getClassNaming(finalName);
assertEquals(originalName, naming.originalName);
- Assert.assertEquals(isSynthesized, isCompilerSynthesized(mapper, finalName));
+ Assert.assertEquals(isSynthesized, isCompilerSynthesized(naming));
}
- private boolean isCompilerSynthesized(ClassNameMapper mapper, String finalName) {
- ScopeReference reference =
- ScopeReference.fromClassReference(Reference.classFromTypeName(finalName));
- return mapper.getAdditionalMappingInfo(reference).stream()
+ private boolean isCompilerSynthesized(ClassNamingForNameMapper naming) {
+ return naming.getAdditionalMappingInfo().stream()
.anyMatch(MappingInformation::isCompilerSynthesizedMappingInformation);
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
index 218e037..22a3470 100644
--- a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
@@ -10,7 +10,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
@@ -39,9 +38,10 @@
private static final KotlinCompileMemoizer kotlinJars =
getCompileMemoizer(getKotlinFilesInResource("lambdas_kstyle_generics"))
- .configure(KotlinCompilerTool::includeRuntime);
+ // TODO(b/185465199): This is not really the test for testing shrinking reflect.
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect());
- @Parameters(name = "{0}")
+ @Parameters(name = "{0}, {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 425f3b0..62286f9 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -150,7 +150,8 @@
CodeInspector inspector = new CodeInspector(appView.appInfo().app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
- foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
+ foo.streamInstructions()
+ .anyMatch(i -> i.asCfInstruction().isInvokeSpecial() && i.getMethod() == target));
}
private DexMethod getTargetMethodSignature(
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
index af6e166..83d363b 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -131,7 +130,8 @@
CodeInspector inspector = new CodeInspector(appInfo.app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
- foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == method));
+ foo.streamInstructions()
+ .anyMatch(i -> i.asCfInstruction().isInvokeSpecial() && i.getMethod() == method));
}
private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index c6a2dbf..20575adb 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -125,7 +125,8 @@
CodeInspector inspector = new CodeInspector(appView.appInfo().app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
- foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
+ foo.streamInstructions()
+ .anyMatch(i -> i.asCfInstruction().isInvokeSpecial() && i.getMethod() == target));
}
private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppView<?> appView) {
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index 5498d06..9286245 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -160,7 +160,8 @@
CodeInspector inspector = new CodeInspector(appView.appInfo().app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
- foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
+ foo.streamInstructions()
+ .anyMatch(i -> i.asCfInstruction().isInvokeSpecial() && i.getMethod() == target));
}
private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppView<?> appView) {
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
index ada12fe..b4dab4e 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -122,7 +122,8 @@
CodeInspector inspector = new CodeInspector(appInfo.app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
- foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
+ foo.streamInstructions()
+ .anyMatch(i -> i.asCfInstruction().isInvokeSpecial() && i.getMethod() == target));
}
private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index b65563e..dfe1002 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.retrace;
-import static com.android.tools.r8.Collectors.toSingle;
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
@@ -14,7 +13,6 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
@@ -22,6 +20,7 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition;
@@ -30,6 +29,7 @@
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -69,10 +69,13 @@
}
}
+ private int getObfuscatedLinePosition() {
+ // TODO(b/185358363): This should go away when we correctly retrace.
+ return kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72) ? 43 : 32;
+ }
+
@Test
public void testRuntime() throws Exception {
- // TODO(b/179666509): SMAP has changed.
- assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
testForRuntime(parameters)
.addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
.addRunClasspathFiles(buildOnDexRuntime(parameters, getKotlinStdlibJar(kotlinc)))
@@ -80,13 +83,12 @@
.assertFailureWithErrorThatMatches(containsString("foo"))
.assertFailureWithErrorThatMatches(
containsString(
- "at retrace.InlineFunctionsInSameFileKt.main(InlineFunctionsInSameFile.kt:43"));
+ "at retrace.InlineFunctionsInSameFileKt.main(InlineFunctionsInSameFile.kt:"
+ + getObfuscatedLinePosition()));
}
@Test
public void testRetraceKotlinInlineStaticFunction() throws Exception {
- // TODO(b/179666509): SMAP has changed.
- assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
testForR8(parameters.getBackend())
@@ -116,7 +118,10 @@
8,
FILENAME_INLINE),
LinePosition.create(
- mainSubject.asFoundMethodSubject(), 1, 43, FILENAME_INLINE));
+ mainSubject.asFoundMethodSubject(),
+ 1,
+ getObfuscatedLinePosition(),
+ FILENAME_INLINE));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -128,10 +133,11 @@
LinePosition inlineStack) {
assertThat(mainSubject, isPresent());
RetraceFrameResult retraceResult =
- mainSubject
- .streamInstructions()
- .filter(InstructionSubject::isThrow)
- .collect(toSingle())
+ ListUtils.last(
+ mainSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isThrow)
+ .collect(Collectors.toList()))
.retraceLinePosition(codeInspector.retrace());
assertThat(retraceResult, isInlineFrame());
assertThat(retraceResult, isInlineStack(inlineStack));
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index 17d4dc6..232c1f5 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.retrace;
-import static com.android.tools.r8.Collectors.toSingle;
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
@@ -15,14 +14,13 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -32,6 +30,7 @@
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -96,8 +95,6 @@
@Test
public void testRetraceKotlinInlineStaticFunction() throws Exception {
- // TODO(b/179666509): SMAP has changed.
- assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainKt";
String mainFileName = "Main.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
@@ -128,8 +125,6 @@
@Test
public void testRetraceKotlinInlineInstanceFunction() throws Exception {
- // TODO(b/179666509): SMAP has changed.
- assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainInstanceKt";
String mainFileName = "MainInstance.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
@@ -163,8 +158,6 @@
@Test
public void testRetraceKotlinNestedInlineFunction() throws Exception {
- // TODO(b/179666509): SMAP has changed.
- assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainNestedKt";
String mainFileName = "MainNested.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
@@ -197,8 +190,6 @@
@Test
public void testRetraceKotlinNestedInlineFunctionOnFirstLine() throws Exception {
- // TODO(b/179666509): SMAP has changed.
- assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainNestedFirstLineKt";
String mainFileName = "MainNestedFirstLine.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
@@ -236,10 +227,11 @@
LinePosition inlineStack) {
assertThat(mainSubject, isPresent());
RetraceFrameResult retraceResult =
- mainSubject
- .streamInstructions()
- .filter(InstructionSubject::isThrow)
- .collect(toSingle())
+ ListUtils.last(
+ mainSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isThrow)
+ .collect(Collectors.toList()))
.retraceLinePosition(codeInspector.retrace());
assertThat(retraceResult, isInlineFrame());
assertThat(retraceResult, isInlineStack(inlineStack));
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
index 25cbfbe..38f7725 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.collect.ImmutableList;
@@ -60,41 +59,20 @@
.run(parameters.getRuntime(), Main.class)
.apply(this::checkRunResult)
.apply(this::checkNoOutputSynthetics)
- .inspectStackTrace(
- stackTrace ->
- assertThat(
- stackTrace,
- isSameExceptForFileNameAndLineNumber(
- StackTrace.builder()
- .addWithoutFileNameAndLineNumber(Main.class, JAVAC_LAMBDA_METHOD)
- .addWithoutFileNameAndLineNumber(Main.class, "runIt")
- .addWithoutFileNameAndLineNumber(Main.class, "main")
- .build())));
+ .inspectStackTrace(RetraceLambdaTest::checkExpectedStackTrace);
}
@Test
public void testD8() throws Exception {
testForD8(parameters.getBackend())
.internalEnableMappingOutput()
+ .enableExperimentalMapFileVersion()
.addInnerClasses(getClass())
.setMinApi(parameters.getApiLevel())
- .enableExperimentalMapFileVersion()
.run(parameters.getRuntime(), Main.class)
.apply(this::checkRunResult)
.apply(this::checkOneOutputSynthetic)
- .inspectStackTrace(
- stackTrace ->
- assertThat(
- stackTrace,
- isSameExceptForFileNameAndLineNumber(
- StackTrace.builder()
- .addWithoutFileNameAndLineNumber(Main.class, JAVAC_LAMBDA_METHOD)
- // TODO(b/172014416): Support a D8 mapping and prune the synthetic.
- .addWithoutFileNameAndLineNumber(
- SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 0), "run")
- .addWithoutFileNameAndLineNumber(Main.class, "runIt")
- .addWithoutFileNameAndLineNumber(Main.class, "main")
- .build())));
+ .inspectStackTrace(RetraceLambdaTest::checkExpectedStackTrace);
}
@Test
@@ -140,7 +118,7 @@
stackTrace -> {
int frames = parameters.isCfRuntime() ? 3 : 5;
checkRawStackTraceFrameCount(stackTrace, frames, "Expected nothing to be inlined");
- checkCurrentlyIncorrectStackTrace(stackTrace);
+ checkExpectedStackTrace(stackTrace);
});
}
@@ -184,6 +162,17 @@
assertEquals(message + stackTrace.getOriginalStderr(), expectedFrames, linesFromTest);
}
+ private static void checkExpectedStackTrace(StackTrace stackTrace) {
+ assertThat(
+ stackTrace,
+ isSameExceptForFileNameAndLineNumber(
+ StackTrace.builder()
+ .addWithoutFileNameAndLineNumber(Main.class, JAVAC_LAMBDA_METHOD)
+ .addWithoutFileNameAndLineNumber(Main.class, "runIt")
+ .addWithoutFileNameAndLineNumber(Main.class, "main")
+ .build()));
+ }
+
private void checkCurrentlyIncorrectStackTrace(StackTrace stackTrace) {
assertThat(
stackTrace,
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 22d4f15..a1707c5 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -47,6 +47,8 @@
import com.android.tools.r8.retrace.stacktraces.SourceFileWithNumberAndEmptyStackTrace;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
import com.android.tools.r8.retrace.stacktraces.SuppressedStackTrace;
+import com.android.tools.r8.retrace.stacktraces.SyntheticLambdaMethodStackTrace;
+import com.android.tools.r8.retrace.stacktraces.SyntheticLambdaMethodWithInliningStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnicodeInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnknownSourceStackTrace;
import com.android.tools.r8.utils.BooleanUtils;
@@ -254,6 +256,16 @@
runRetraceTest(new AutoStackTrace());
}
+ @Test
+ public void testRetraceSynthesizedLambda() throws Exception {
+ runRetraceTest(new SyntheticLambdaMethodStackTrace());
+ }
+
+ @Test
+ public void testRetraceSynthesizedLambdaWithInlining() throws Exception {
+ runRetraceTest(new SyntheticLambdaMethodWithInliningStackTrace());
+ }
+
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
@@ -304,7 +316,10 @@
.setStackTrace(stackTraceForTest.obfuscatedStackTrace())
.setRegularExpression(useRegExpParsing ? DEFAULT_REGULAR_EXPRESSION : null)
.setRetracedStackTraceConsumer(
- retraced -> assertEquals(stackTraceForTest.retracedStackTrace(), retraced))
+ retraced ->
+ assertEquals(
+ StringUtils.joinLines(stackTraceForTest.retracedStackTrace()),
+ StringUtils.joinLines(retraced)))
.build();
Retrace.run(retraceCommand);
return diagnosticsHandler;
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodStackTrace.java
new file mode 100644
index 0000000..efe3f0f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodStackTrace.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2019, 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class SyntheticLambdaMethodStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ " at a.a.a(a.java:5)",
+ " at a.b.a(Unknown Source)",
+ " at a.a.b(a.java:3)",
+ " at a.a.c(a.java:2)",
+ " at example.Main.main(Main.java:1)");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ " at example.Foo.lambda$main$0(Foo.java:225)",
+ " at example.Foo.runIt(Foo.java:218)",
+ " at example.Foo.main(Foo.java:223)",
+ " at example.Main.main(Main.java:123)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "# {'id':'com.android.tools.r8.metainf','map-version':'experimental'}",
+ "example.Main -> example.Main:",
+ " 1:1:void main(java.lang.String[]):123 -> main",
+ "example.Foo -> a.a:",
+ " 5:5:void lambda$main$0():225 -> a",
+ " 3:3:void runIt():218 -> b",
+ " 2:2:void main():223 -> c",
+ "example.Foo$$ExternalSyntheticLambda0 -> a.b:",
+ " void run(example.Foo) -> a",
+ " # {'id':'com.android.tools.r8.synthesized'}");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodWithInliningStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodWithInliningStackTrace.java
new file mode 100644
index 0000000..5fda85f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodWithInliningStackTrace.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2019, 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class SyntheticLambdaMethodWithInliningStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ " at a.b.a(Unknown Source:4)",
+ " at a.a.b(a.java:3)",
+ " at a.a.c(a.java:2)",
+ " at example.Main.main(Main.java:1)");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ " at example.Foo.lambda$main$0(Foo.java:225)",
+ " at example.Foo.runIt(Foo.java:218)",
+ " at example.Foo.main(Foo.java:223)",
+ " at example.Main.main(Main.java:123)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "# {'id':'com.android.tools.r8.metainf','map-version':'experimental'}",
+ "example.Main -> example.Main:",
+ " 1:1:void main(java.lang.String[]):123 -> main",
+ "example.Foo -> a.a:",
+ " 3:3:void runIt():218 -> b",
+ " 2:2:void main():223 -> c",
+ "example.Foo$$ExternalSyntheticLambda0 -> a.b:",
+ " 4:4:void example.Foo.lambda$main$0():225 -> a",
+ " 4:4:void run(example.Foo):0 -> a",
+ " # {'id':'com.android.tools.r8.synthesized'}");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesMaterializeFieldReadTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesMaterializeFieldReadTest.java
new file mode 100644
index 0000000..0caf415
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesMaterializeFieldReadTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2021, 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.shaking.assumevalues;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssumeValuesMaterializeFieldReadTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public AssumeValuesMaterializeFieldReadTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(
+ "-assumevalues class " + Main.class.getTypeName() + " {",
+ " static java.lang.Object get() return " + Main.class.getTypeName() + ".field;",
+ "}")
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject getMethodSubject = mainClassSubject.uniqueMethodWithName("get");
+ assertThat(getMethodSubject, isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ static class Main {
+
+ static Object field = new Object();
+
+ public static void main(String[] args) {
+ if (field != get()) {
+ throw new RuntimeException();
+ }
+ }
+
+ @NeverInline
+ public static Object get() {
+ return new Object();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 9324b87..21501d0 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -1682,16 +1682,15 @@
" sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
" new-instance v2, Ljava/util/ArrayList;",
" invoke-direct { v2 }, Ljava/util/ArrayList;-><init>()V",
- " invoke-virtual { v0, v1, v2 }, " +
- " LTest;->method1(Ljava/io/PrintStream;Ljava/util/ArrayList;)Z",
+ " invoke-direct { v0, v1, v2 }, "
+ + "LTest;->method1(Ljava/io/PrintStream;Ljava/util/ArrayList;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " invoke-virtual { v0, v1, v2 }, " +
- " LTest;->method2(Ljava/io/PrintStream;Ljava/util/ArrayList;)Z",
+ " invoke-direct { v0, v1, v2 }, "
+ + "LTest;->method2(Ljava/io/PrintStream;Ljava/util/ArrayList;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " return-void"
- );
+ " return-void");
// Outline 2 times two instructions.
Consumer<InternalOptions> options =
@@ -1759,14 +1758,13 @@
" sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
" new-instance v2, Ljava/util/ArrayList;",
" invoke-direct { v2 }, Ljava/util/ArrayList;-><init>()V",
- " invoke-virtual { v0, v2 }, LTest;->method1(Ljava/util/List;)Z",
+ " invoke-direct { v0, v2 }, LTest;->method1(Ljava/util/List;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " invoke-virtual { v0, v2 }, LTest;->method2(Ljava/util/List;)Z",
+ " invoke-direct { v0, v2 }, LTest;->method2(Ljava/util/List;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " return-void"
- );
+ " return-void");
// Outline 2 times two instructions.
Consumer<InternalOptions> options =
@@ -1836,14 +1834,13 @@
" sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
" new-instance v2, Ljava/util/ArrayList;",
" invoke-direct { v2 }, Ljava/util/ArrayList;-><init>()V",
- " invoke-virtual { v0, v2 }, LTest;->method1(Ljava/util/ArrayList;)Z",
+ " invoke-direct { v0, v2 }, LTest;->method1(Ljava/util/ArrayList;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " invoke-virtual { v0, v2 }, LTest;->method2(Ljava/util/ArrayList;)Z",
+ " invoke-direct { v0, v2 }, LTest;->method2(Ljava/util/ArrayList;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " return-void"
- );
+ " return-void");
// Outline 2 times two instructions.
Consumer<InternalOptions> options =
@@ -1913,14 +1910,13 @@
" sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
" new-instance v2, Ljava/util/ArrayList;",
" invoke-direct { v2 }, Ljava/util/ArrayList;-><init>()V",
- " invoke-virtual { v0, v2 }, LTest;->method1(Ljava/util/ArrayList;)Z",
+ " invoke-direct { v0, v2 }, LTest;->method1(Ljava/util/ArrayList;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " invoke-virtual { v0, v2 }, LTest;->method2(Ljava/util/ArrayList;)Z",
+ " invoke-direct { v0, v2 }, LTest;->method2(Ljava/util/ArrayList;)Z",
" move-result v3",
" invoke-virtual { v1, v3 }, Ljava/io/PrintStream;->print(Z)V",
- " return-void"
- );
+ " return-void");
// Outline 2 times two instructions.
Consumer<InternalOptions> options =
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index f927e62..64b3914 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -53,11 +53,21 @@
}
@Override
+ public boolean isDexInstruction() {
+ return false;
+ }
+
+ @Override
public DexInstructionSubject asDexInstruction() {
return null;
}
@Override
+ public boolean isCfInstruction() {
+ return true;
+ }
+
+ @Override
public CfInstructionSubject asCfInstruction() {
return this;
}
@@ -279,7 +289,6 @@
&& ((CfSwitch) instruction).getKind() == CfSwitch.Kind.LOOKUP;
}
- @Override
public boolean isInvokeSpecial() {
return instruction instanceof CfInvoke
&& ((CfInvoke) instruction).getOpcode() == Opcodes.INVOKESPECIAL;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 8c8ba0f..077af2f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -29,7 +29,6 @@
import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
-import com.android.tools.r8.naming.mappinginformation.ScopeReference;
import com.android.tools.r8.naming.signature.GenericSignatureAction;
import com.android.tools.r8.naming.signature.GenericSignatureParser;
import com.android.tools.r8.origin.Origin;
@@ -292,8 +291,7 @@
}
public Collection<MappingInformation> getAdditionalMappings() {
- return mapper.getAdditionalMappingInfo(
- ScopeReference.fromClassReference(Reference.classFromTypeName(naming.renamedName)));
+ return naming.getAdditionalMappingInfo();
}
public ClassNamingForNameMapper getNaming() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 2ae5f05..2bfadc8 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -116,11 +116,21 @@
}
@Override
+ public boolean isDexInstruction() {
+ return true;
+ }
+
+ @Override
public DexInstructionSubject asDexInstruction() {
return this;
}
@Override
+ public boolean isCfInstruction() {
+ return false;
+ }
+
+ @Override
public CfInstructionSubject asCfInstruction() {
return null;
}
@@ -205,11 +215,6 @@
}
@Override
- public boolean isInvokeSpecial() {
- return false;
- }
-
- @Override
public boolean isInvokeDynamic() {
return isInvokeCustom();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
index 38f7e92..d554d50 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
+import com.android.tools.r8.utils.DescriptorUtils;
public class EnumUnboxingInspector {
@@ -21,8 +22,23 @@
this.unboxedEnums = unboxedEnums;
}
+ public EnumUnboxingInspector assertUnboxed(String typeName) {
+ assertTrue(
+ unboxedEnums.isUnboxedEnum(
+ dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName))));
+ return this;
+ }
+
public EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>> clazz) {
- assertTrue(unboxedEnums.isUnboxedEnum(toDexType(clazz, dexItemFactory)));
+ assertTrue(clazz.getTypeName(), unboxedEnums.isUnboxedEnum(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ @SafeVarargs
+ public final EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>>... classes) {
+ for (Class<? extends Enum<?>> clazz : classes) {
+ assertUnboxed(clazz);
+ }
return this;
}
@@ -36,15 +52,24 @@
}
@SafeVarargs
- public final EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>>... classes) {
+ public final EnumUnboxingInspector assertUnboxedIf(
+ boolean condition, Class<? extends Enum<?>>... classes) {
for (Class<? extends Enum<?>> clazz : classes) {
- assertUnboxed(clazz);
+ assertUnboxedIf(condition, clazz);
}
return this;
}
public EnumUnboxingInspector assertNotUnboxed(Class<? extends Enum<?>> clazz) {
- assertFalse(unboxedEnums.isUnboxedEnum(toDexType(clazz, dexItemFactory)));
+ assertFalse(clazz.getTypeName(), unboxedEnums.isUnboxedEnum(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ @SafeVarargs
+ public final EnumUnboxingInspector assertNotUnboxed(Class<? extends Enum<?>>... classes) {
+ for (Class<? extends Enum<?>> clazz : classes) {
+ assertNotUnboxed(clazz);
+ }
return this;
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index e8818b2..36d9ed5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -17,8 +17,12 @@
DISALLOW
};
+ boolean isDexInstruction();
+
DexInstructionSubject asDexInstruction();
+ boolean isCfInstruction();
+
CfInstructionSubject asCfInstruction();
boolean isFieldAccess();
@@ -41,10 +45,17 @@
boolean isInvokeStatic();
- boolean isInvokeSpecial();
-
boolean isInvokeDynamic();
+ default boolean isInvokeSpecialOrDirect() {
+ if (isCfInstruction()) {
+ return asCfInstruction().isInvokeSpecial();
+ } else {
+ assert isDexInstruction();
+ return asDexInstruction().isInvokeDirect();
+ }
+ }
+
DexMethod getMethod();
boolean isNop();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index aa6d435..5083d42 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -521,9 +521,8 @@
RetraceFrameElement single = item.stream().collect(Collectors.toSingle());
Box<LinePosition> currentPosition = new Box<>(startPosition);
Box<Boolean> returnValue = new Box<>();
- single.visitFrames(
+ single.visitAllFrames(
(method, __) -> {
- boolean sameMethod;
LinePosition currentInline = currentPosition.get();
if (currentInline == null) {
returnValue.set(false);
@@ -533,7 +532,7 @@
returnValue.set(false);
return;
}
- sameMethod =
+ boolean sameMethod =
method.asKnown().getMethodReference().equals(currentInline.methodReference);
boolean samePosition =
method.getOriginalPositionOrDefault(currentInline.minifiedPosition)
@@ -560,7 +559,7 @@
protected boolean matchesSafely(RetraceFrameResult item) {
RetraceFrameElement single = item.stream().collect(Collectors.toSingle());
Box<Boolean> matches = new Box<>(true);
- single.visitFrames(
+ single.visitAllFrames(
(method, index) -> {
StackTraceLine stackTraceLine = stackTrace.get(index);
if (!stackTraceLine.methodName.equals(method.getMethodName())
diff --git a/third_party/kotlin/kotlin-compiler-1.5.0-M2.tar.gz.sha1 b/third_party/kotlin/kotlin-compiler-1.5.0-M2.tar.gz.sha1
new file mode 100644
index 0000000..e0b251e
--- /dev/null
+++ b/third_party/kotlin/kotlin-compiler-1.5.0-M2.tar.gz.sha1
@@ -0,0 +1 @@
+7368d9ee73d01fc609d7ded1bf0506f72aa3ac66
\ No newline at end of file
diff --git a/third_party/youtube/youtube.android_16.12.tar.gz.sha1 b/third_party/youtube/youtube.android_16.12.tar.gz.sha1
new file mode 100644
index 0000000..580b87e
--- /dev/null
+++ b/third_party/youtube/youtube.android_16.12.tar.gz.sha1
@@ -0,0 +1 @@
+1e50d558d85ebd9741cf4bd62e01cf415d4d480f
\ No newline at end of file
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index b58eafb..60cd4f1 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -454,72 +454,62 @@
def should_build(options):
return not options.no_build and not options.golem
-def build_desugared_library_dex(options, quiet, temp, android_java8_libs, inputs, outdir):
- if not input:
- raise Exception("If 'android_java8_libs' is specified the inputs must be explicit"
- + "(not defined using '-injars' in Proguard configuration files)")
+def build_desugared_library_dex(
+ options,
+ quiet,
+ temp,
+ android_java8_libs,
+ desugared_lib_pg_conf,
+ inputs,
+ outdir):
+ if not inputs:
+ raise Exception(
+ "If 'android_java8_libs' is specified the inputs must be explicit"
+ + "(not defined using '-injars' in Proguard configuration files)")
if outdir.endswith('.zip') or outdir.endswith('.jar'):
- raise Exception("If 'android_java8_libs' is specified the output must be a directory")
+ raise Exception(
+ "If 'android_java8_libs' is specified the output must be a directory")
jar = None
main = None
if options.hash:
jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
+ main = 'com.android.tools.r8.R8'
+ # Determine the l8 tool.
assert(options.compiler_build in ['full', 'lib'])
lib_prefix = 'r8lib-' if options.compiler_build == 'lib' else ''
+ tool = lib_prefix + 'l8'
- # Determine the tracereferences tool.
- tool = lib_prefix + 'tracereferences'
- if options.hash:
- main = 'com.android.tools.r8.TraceReferences'
- tracereferences_output = os.path.join(outdir, 'tracereferences.pgcfg')
- args = [
- '--map-diagnostics:MissingDefinitionsDiagnostic', 'error', 'warning',
- '--keep-rules',
- '--lib', 'third_party/android_jar/lib-v30/android.jar',
- '--target', android_java8_libs['library'],
- '--output', tracereferences_output,
- ]
- for source in inputs:
- args.extend(['--source', source])
- exit_code = toolhelper.run(tool, args,
- build=should_build(options),
- debug=not options.no_debug,
- quiet=quiet,
- jar=jar,
- main=main)
- if exit_code != 0:
- raise Exception("tracereferences failed")
-
- # Determine the r8 tool.
- tool = lib_prefix + 'r8'
- if options.compiler_build == 'full':
- tool = ''
- else:
- assert(options.compiler_build == 'lib')
- tool = 'r8lib-r8'
- if options.hash:
- main = 'com.android.tools.r8.R8'
+ # Prepare out directory.
android_java8_libs_output = os.path.join(temp, 'android_java8_libs')
os.makedirs(android_java8_libs_output)
+
+ # Prepare arguments for L8.
args = [
- '--no-desugaring',
- '--lib', '%s/android_jar/lib-v30/android.jar' % utils.THIRD_PARTY,
+ '--desugared-lib', android_java8_libs['config'],
+ '--lib', android_java8_libs['library'],
'--output', android_java8_libs_output,
- '--pg-conf', tracereferences_output,
- android_java8_libs['library']
+ '--pg-conf', desugared_lib_pg_conf,
+ '--release',
]
- for pgconf in android_java8_libs['pgconf']:
- args.extend(['--pg-conf', pgconf])
- exit_code = toolhelper.run(tool, args,
+ if 'pgconf' in android_java8_libs:
+ for pgconf in android_java8_libs['pgconf']:
+ args.extend(['--pg-conf', pgconf])
+ args.extend(android_java8_libs['program'])
+
+ # Run L8.
+ exit_code = toolhelper.run(
+ tool, args,
build=should_build(options),
debug=not options.no_debug,
quiet=quiet,
jar=jar,
main=main)
+
# Copy the desugared library DEX to the output.
- dex_file_name = 'classes' + str(len(glob(os.path.join(outdir, '*.dex'))) + 1) + '.dex'
+ dex_file_name = (
+ 'classes' + str(len(glob(os.path.join(outdir, '*.dex'))) + 1) + '.dex')
shutil.copyfile(
os.path.join(android_java8_libs_output, 'classes.dex'),
os.path.join(outdir, dex_file_name))
@@ -680,6 +670,15 @@
additional_pg_conf = GenerateAdditionalProguardConfiguration(
temp, os.path.abspath(pg_outdir))
args.extend(['--pg-conf', additional_pg_conf])
+
+ android_java8_libs = values.get('android_java8_libs')
+ if android_java8_libs:
+ desugared_lib_pg_conf = os.path.join(
+ temp, 'desugared-lib-pg-conf.txt')
+ args.extend(['--desugared-lib', android_java8_libs['config']])
+ args.extend(
+ ['--desugared-lib-pg-conf-output', desugared_lib_pg_conf])
+
stderr_path = os.path.join(temp, 'stderr')
with open(stderr_path, 'w') as stderr:
jar = None
@@ -722,9 +721,10 @@
.format(options.print_memoryuse,
utils.grep_memoryuse(options.track_memory_to_file)))
- if 'android_java8_libs' in values:
- android_java8_libs = values['android_java8_libs']
- build_desugared_library_dex(options, quiet, temp, android_java8_libs, inputs, outdir)
+ if android_java8_libs:
+ build_desugared_library_dex(
+ options, quiet, temp, android_java8_libs,
+ desugared_lib_pg_conf, inputs, outdir)
if options.print_runtimeraw:
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index b66b70f..02cb549 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -39,6 +39,8 @@
cmd.extend(['-cp', jar, main])
elif tool == 'r8lib-d8':
cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.D8'])
+ elif tool == 'r8lib-l8':
+ cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.L8'])
elif tool == 'r8lib-r8':
cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.R8'])
elif tool == 'r8lib-tracereferences':
diff --git a/tools/youtube_data.py b/tools/youtube_data.py
index eafe5bb..04d9e1f 100644
--- a/tools/youtube_data.py
+++ b/tools/youtube_data.py
@@ -37,6 +37,9 @@
V15_33_BASE = os.path.join(BASE, 'youtube.android_15.33')
V15_33_PREFIX = os.path.join(V15_33_BASE, 'YouTubeRelease')
+V16_12_BASE = os.path.join(BASE, 'youtube.android_16.12')
+V16_12_PREFIX = os.path.join(V16_12_BASE, 'YouTubeRelease')
+
# NOTE: we always use android.jar for SDK v25, later we might want to revise it
# to use proper android.jar version for each of youtube version separately.
ANDROID_JAR = utils.get_android_jar(25)
@@ -283,4 +286,35 @@
'min-api' : ANDROID_L_API,
}
},
+ '16.12': {
+ 'deploy' : {
+ 'sanitize_libraries': False,
+ 'inputs': ['%s_deploy.jar' % V16_12_PREFIX],
+ 'libraries' : [
+ os.path.join(
+ V16_12_BASE,
+ 'legacy_YouTubeRelease_combined_library_jars_filtered.jar')],
+ 'pgconf': [
+ '%s_proguard.config' % V16_12_PREFIX,
+ '%s/proguardsettings/YouTubeRelease_proguard.config' % utils.THIRD_PARTY,
+ utils.IGNORE_WARNINGS_RULES],
+ 'min-api' : ANDROID_L_API,
+ 'android_java8_libs': {
+ 'config': '%s/desugar_jdk_libs/full_desugar_jdk_libs.json' % V16_12_BASE,
+ 'program': [
+ '%s/desugar_jdk_libs/jdk_libs_to_desugar.jar' % V16_12_BASE,
+ '%s/desugar_jdk_libs/desugar_jdk_libs_configuration.jar' % V16_12_BASE],
+ 'library': '%s/android_jar/lib-v30/android.jar' % utils.THIRD_PARTY,
+ 'pgconf': [
+ '%s/desugar_jdk_libs/base.pgcfg' % V16_12_BASE,
+ '%s/desugar_jdk_libs/minify_desugar_jdk_libs.pgcfg' % V16_12_BASE
+ ]
+ }
+ },
+ 'proguarded' : {
+ 'inputs': ['%s_proguard.jar' % V16_12_PREFIX],
+ 'pgmap': '%s_proguard.map' % V16_12_PREFIX,
+ 'min-api' : ANDROID_L_API,
+ }
+ },
}