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, + } + }, }