Merge commit 'edf8439d2bc78bfc7e9b6a5052edffda49bcaba3' into dev-release
diff --git a/build.gradle b/build.gradle index c47befd..b7c7830 100644 --- a/build.gradle +++ b/build.gradle
@@ -1939,6 +1939,11 @@ systemProperty 'runtimes', project.property('runtimes') } + if (project.hasProperty('horizontalClassMerging')) { + println "NOTE: Running with horizontal class merging" + systemProperty 'com.android.tools.r8.horizontalClassMerging', 'true' + } + if (project.hasProperty('slow_tests')) { systemProperty 'slow_tests', project.property('slow_tests') }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index 9e462dd..84bc9b3 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -398,6 +398,7 @@ // Assert some of R8 optimizations are disabled. assert !internal.enableInlining; assert !internal.enableClassInlining; + assert !internal.enableHorizontalClassMerging; assert !internal.enableStaticClassMerging; assert !internal.enableVerticalClassMerging; assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java index db24f23..221df4c 100644 --- a/src/main/java/com/android/tools/r8/L8Command.java +++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -167,6 +167,7 @@ // Assert some of R8 optimizations are disabled. assert !internal.enableInlining; assert !internal.enableClassInlining; + assert !internal.enableHorizontalClassMerging; assert !internal.enableStaticClassMerging; assert !internal.enableVerticalClassMerging; assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java index c0efa2c..2f53841 100644 --- a/src/main/java/com/android/tools/r8/PrintUses.java +++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -207,7 +207,10 @@ addType(field.holder); Set<DexField> typeFields = fields.get(field.holder); if (typeFields != null) { - assert baseField != null; + if (baseField == null) { + System.out.println(field.toSourceString()); + } + assert baseField != null : field.toSourceString(); if (!allowObfuscation) { noObfuscationTypes.add(field.holder); }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index 7c78db1..0375b6b 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -845,6 +845,7 @@ ? LineNumberOptimization.ON : LineNumberOptimization.OFF; + assert proguardConfiguration.isOptimizing() || !internal.enableHorizontalClassMerging; assert internal.enableStaticClassMerging || !proguardConfiguration.isOptimizing(); assert !internal.enableTreeShakingOfLibraryMethodOverrides; assert internal.enableVerticalClassMerging || !proguardConfiguration.isOptimizing(); @@ -854,6 +855,7 @@ internal.getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true; internal.enableInlining = false; internal.enableClassInlining = false; + internal.enableHorizontalClassMerging = false; internal.enableStaticClassMerging = false; internal.enableVerticalClassMerging = false; internal.enableClassStaticizer = false;
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java index 15c2dce..1cd6718 100644 --- a/src/main/java/com/android/tools/r8/StringConsumer.java +++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -145,6 +145,7 @@ if (failedToCreateDelegate) { return; } + ensureDelegate(handler); if (delegate != null) { delegate.finished(handler); delegate = null;
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java index f079277..ddc81b6 100644 --- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java +++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.compatproguard.CompatProguard; import com.android.tools.r8.dexsplitter.DexSplitter; import com.android.tools.r8.relocator.RelocatorCommandLine; +import com.android.tools.r8.tracereferences.TraceReferences; import java.util.Arrays; /** @@ -75,6 +76,9 @@ case "relocator": RelocatorCommandLine.main(shift((args))); break; + case "tracereferences": + TraceReferences.main(shift((args))); + break; default: runDefault(args); break;
diff --git a/src/main/java/com/android/tools/r8/experimental/graphinfo/ClassGraphNode.java b/src/main/java/com/android/tools/r8/experimental/graphinfo/ClassGraphNode.java index edbffbe..fa55609 100644 --- a/src/main/java/com/android/tools/r8/experimental/graphinfo/ClassGraphNode.java +++ b/src/main/java/com/android/tools/r8/experimental/graphinfo/ClassGraphNode.java
@@ -24,7 +24,7 @@ @Override public boolean equals(Object o) { return this == o - || (o instanceof ClassGraphNode && ((ClassGraphNode) o).reference == reference); + || (o instanceof ClassGraphNode && ((ClassGraphNode) o).reference.equals(reference)); } @Override
diff --git a/src/main/java/com/android/tools/r8/experimental/graphinfo/FieldGraphNode.java b/src/main/java/com/android/tools/r8/experimental/graphinfo/FieldGraphNode.java index b091613..836058f 100644 --- a/src/main/java/com/android/tools/r8/experimental/graphinfo/FieldGraphNode.java +++ b/src/main/java/com/android/tools/r8/experimental/graphinfo/FieldGraphNode.java
@@ -24,7 +24,7 @@ @Override public boolean equals(Object o) { return this == o - || (o instanceof FieldGraphNode && ((FieldGraphNode) o).reference == reference); + || (o instanceof FieldGraphNode && ((FieldGraphNode) o).reference.equals(reference)); } @Override
diff --git a/src/main/java/com/android/tools/r8/experimental/graphinfo/MethodGraphNode.java b/src/main/java/com/android/tools/r8/experimental/graphinfo/MethodGraphNode.java index 94fe8e7..7a92a91 100644 --- a/src/main/java/com/android/tools/r8/experimental/graphinfo/MethodGraphNode.java +++ b/src/main/java/com/android/tools/r8/experimental/graphinfo/MethodGraphNode.java
@@ -24,7 +24,7 @@ @Override public boolean equals(Object o) { return this == o - || (o instanceof MethodGraphNode && ((MethodGraphNode) o).reference == reference); + || (o instanceof MethodGraphNode && ((MethodGraphNode) o).reference.equals(reference)); } @Override
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java index 4bb68b9..ed19598 100644 --- a/src/main/java/com/android/tools/r8/graph/AccessControl.java +++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -15,62 +15,53 @@ public class AccessControl { public static OptionalBool isClassAccessible( - DexClass clazz, ProgramMethod context, AppView<? extends AppInfoWithClassHierarchy> appView) { - return isClassAccessible( - clazz, context.getHolder(), appView.appInfo().getClassToFeatureSplitMap()); + DexClass clazz, + ProgramDefinition context, + AppView<? extends AppInfoWithClassHierarchy> appView) { + return isClassAccessible(clazz, context, appView.appInfo().getClassToFeatureSplitMap()); } public static OptionalBool isClassAccessible( - DexClass clazz, DexProgramClass context, ClassToFeatureSplitMap classToFeatureSplitMap) { - if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getType())) { + DexClass clazz, ProgramDefinition context, ClassToFeatureSplitMap classToFeatureSplitMap) { + if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getContextType())) { return OptionalBool.FALSE; } if (clazz.isProgramClass() - && !classToFeatureSplitMap.isInBaseOrSameFeatureAs(clazz.asProgramClass(), context)) { + && !classToFeatureSplitMap.isInBaseOrSameFeatureAs( + clazz.asProgramClass(), context.getContextClass())) { return OptionalBool.UNKNOWN; } return OptionalBool.TRUE; } - public static OptionalBool isMethodAccessible( - DexEncodedMethod method, - DexClass holder, - ProgramMethod context, + /** Intentionally package-private, use {@link MemberResolutionResult#isAccessibleFrom}. */ + static OptionalBool isMemberAccessible( + SuccessfulMemberResolutionResult<?, ?> resolutionResult, + ProgramDefinition context, + AppInfoWithClassHierarchy appInfo) { + return isMemberAccessible( + resolutionResult.getResolutionPair(), + resolutionResult.getInitialResolutionHolder(), + context, + appInfo); + } + + public static OptionalBool isMemberAccessible( + DexClassAndMember<?, ?> member, + DexClass initialResolutionHolder, + ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) { - return isMethodAccessible(method, holder, context.getHolder(), appView.appInfo()); + return isMemberAccessible(member, initialResolutionHolder, context, appView.appInfo()); } - public static OptionalBool isMethodAccessible( - DexEncodedMethod method, - DexClass holder, - DexProgramClass context, + public static OptionalBool isMemberAccessible( + DexClassAndMember<?, ?> member, + DexClass initialResolutionHolder, + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { - return isMemberAccessible(method.accessFlags, holder, context, appInfo); - } - - public static OptionalBool isFieldAccessible( - DexEncodedField field, - DexClass holder, - ProgramMethod context, - AppView<? extends AppInfoWithClassHierarchy> appView) { - return isFieldAccessible(field, holder, context.getHolder(), appView.appInfo()); - } - - public static OptionalBool isFieldAccessible( - DexEncodedField field, - DexClass holder, - DexProgramClass context, - AppInfoWithClassHierarchy appInfo) { - return isMemberAccessible(field.accessFlags, holder, context, appInfo); - } - - private static OptionalBool isMemberAccessible( - AccessFlags<?> memberFlags, - DexClass holder, - DexProgramClass context, - AppInfoWithClassHierarchy appInfo) { + AccessFlags<?> memberFlags = member.getDefinition().getAccessFlags(); OptionalBool classAccessibility = - isClassAccessible(holder, context, appInfo.getClassToFeatureSplitMap()); + isClassAccessible(initialResolutionHolder, context, appInfo.getClassToFeatureSplitMap()); if (classAccessibility.isFalse()) { return OptionalBool.FALSE; } @@ -78,15 +69,16 @@ return classAccessibility; } if (memberFlags.isPrivate()) { - if (!isNestMate(holder, context)) { + if (!isNestMate(member.getHolder(), context.getContextClass())) { return OptionalBool.FALSE; } return classAccessibility; } - if (holder.getType().isSamePackage(context.getType())) { + if (member.getHolderType().isSamePackage(context.getContextType())) { return classAccessibility; } - if (memberFlags.isProtected() && appInfo.isSubtype(context.getType(), holder.getType())) { + if (memberFlags.isProtected() + && appInfo.isSubtype(context.getContextType(), member.getHolderType())) { return classAccessibility; } return OptionalBool.FALSE;
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 cde4638..dede4e0 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -54,7 +54,7 @@ private EnclosingMethodAttribute enclosingMethod; /** InnerClasses table. If this class is an inner class, it will have an entry here. */ - private final List<InnerClassAttribute> innerClasses; + private List<InnerClassAttribute> innerClasses; private NestHostClassAttribute nestHost; private final List<NestMemberClassAttribute> nestMembers; @@ -723,6 +723,10 @@ return innerClasses; } + public void setInnerClasses(List<InnerClassAttribute> innerClasses) { + this.innerClasses = innerClasses; + } + public EnclosingMethodAttribute getEnclosingMethodAttribute() { return enclosingMethod; }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java index 24776d0..c30f69c 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -509,6 +509,11 @@ return accessFlags.isPublic(); } + public boolean isProtectedMethod() { + checkIfObsolete(); + return accessFlags.isProtected(); + } + public boolean isPrivateMethod() { checkIfObsolete(); return accessFlags.isPrivate();
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 68da447..b5fe6b8 100644 --- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -494,6 +494,11 @@ } @Override + public DexProgramClass getContextClass() { + return this; + } + + @Override public DexType getContextType() { return getType(); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java index 500da1e..f21e965 100644 --- a/src/main/java/com/android/tools/r8/graph/DexType.java +++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.horizontalclassmerging.SyntheticArgumentClass; -import com.android.tools.r8.ir.desugar.BackportedMethodRewriter; import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter; import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring; import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter; @@ -42,7 +41,8 @@ // Bundletool is merging classes that may originate from a build with an old version of R8. // Allow merging of classes that use names from older versions of R8. - private static List<String> OLD_SYNTHESIZED_NAMES = ImmutableList.of("$r8$java8methods$utility"); + private static List<String> OLD_SYNTHESIZED_NAMES = + ImmutableList.of("$r8$backportedMethods$utility", "$r8$java8methods$utility"); public final DexString descriptor; private String toStringCache = null; @@ -327,7 +327,6 @@ || name.contains(OutlineOptions.CLASS_NAME) // Global singleton. || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME) // Global singleton. || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME) // Global singleton. - || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX) // Shared on reference. || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME); // Global singleton. }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java index 486996e..ecb09ca 100644 --- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -7,7 +7,7 @@ import com.android.tools.r8.utils.OptionalBool; public abstract class FieldResolutionResult - implements MemberResolutionResult<DexEncodedField, DexField> { + extends MemberResolutionResult<DexEncodedField, DexField> { public static FailedFieldResolutionResult failure() { return FailedFieldResolutionResult.INSTANCE; @@ -21,9 +21,6 @@ return null; } - public abstract OptionalBool isAccessibleFrom( - ProgramMethod context, AppInfoWithClassHierarchy appInfo); - public boolean isSuccessfulResolution() { return false; } @@ -85,14 +82,15 @@ return resolvedField; } + @Override public DexClassAndField getResolutionPair() { return DexClassAndField.create(resolvedHolder, resolvedField); } @Override - public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) { - return AccessControl.isFieldAccessible( - resolvedField, initialResolutionHolder, context.getHolder(), appInfo); + public OptionalBool isAccessibleFrom( + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { + return AccessControl.isMemberAccessible(this, context, appInfo); } @Override @@ -121,7 +119,8 @@ private static final FailedFieldResolutionResult INSTANCE = new FailedFieldResolutionResult(); @Override - public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) { + public OptionalBool isAccessibleFrom( + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { return OptionalBool.FALSE; } @@ -140,7 +139,8 @@ private static final UnknownFieldResolutionResult INSTANCE = new UnknownFieldResolutionResult(); @Override - public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) { + public OptionalBool isAccessibleFrom( + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { return OptionalBool.FALSE; }
diff --git a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java index da70e09..01294be 100644 --- a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
@@ -4,10 +4,20 @@ package com.android.tools.r8.graph; -public interface MemberResolutionResult< +import com.android.tools.r8.utils.OptionalBool; + +public abstract class MemberResolutionResult< D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> { - boolean isSuccessfulMemberResolutionResult(); + public abstract boolean isSuccessfulMemberResolutionResult(); - SuccessfulMemberResolutionResult<D, R> asSuccessfulMemberResolutionResult(); + public abstract SuccessfulMemberResolutionResult<D, R> asSuccessfulMemberResolutionResult(); + + public abstract OptionalBool isAccessibleFrom( + ProgramDefinition context, AppInfoWithClassHierarchy appInfo); + + public final OptionalBool isAccessibleFrom( + ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) { + return isAccessibleFrom(context, appView.appInfo()); + } }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java index 91edb06..883b0a6 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -6,6 +6,8 @@ public interface ProgramDefinition { + DexProgramClass getContextClass(); + DexType getContextType(); DexDefinition getDefinition();
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMember.java b/src/main/java/com/android/tools/r8/graph/ProgramMember.java index 328c71f..fc55509 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramMember.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
@@ -8,7 +8,14 @@ extends ProgramDefinition { @Override + default DexProgramClass getContextClass() { + return getHolder(); + } + + @Override D getDefinition(); + DexProgramClass getHolder(); + DexType getHolderType(); }
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java index 82b02c4..4db00fc 100644 --- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -18,8 +18,7 @@ import java.util.function.BiPredicate; import java.util.function.Consumer; -public abstract class ResolutionResult - implements MemberResolutionResult<DexEncodedMethod, DexMethod> { +public abstract class ResolutionResult extends MemberResolutionResult<DexEncodedMethod, DexMethod> { /** * Returns true if resolution succeeded *and* the resolved method has a known definition. @@ -75,21 +74,8 @@ return null; } - public abstract OptionalBool isAccessibleFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo); - - public final OptionalBool isAccessibleFrom( - ProgramMethod context, AppInfoWithClassHierarchy appInfo) { - return isAccessibleFrom(context.getHolder(), appInfo); - } - public abstract OptionalBool isAccessibleForVirtualDispatchFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo); - - public final OptionalBool isAccessibleForVirtualDispatchFrom( - ProgramMethod context, AppInfoWithClassHierarchy appInfo) { - return isAccessibleForVirtualDispatchFrom(context.getHolder(), appInfo); - } + ProgramDefinition context, AppInfoWithClassHierarchy appInfo); public abstract boolean isVirtualTarget(); @@ -183,6 +169,7 @@ : null; } + @Override public DexClassAndMethod getResolutionPair() { return DexClassAndMethod.create(resolvedHolder, resolvedMethod); } @@ -210,14 +197,13 @@ @Override public OptionalBool isAccessibleFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo) { - return AccessControl.isMethodAccessible( - resolvedMethod, initialResolutionHolder, context, appInfo); + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { + return AccessControl.isMemberAccessible(this, context, appInfo); } @Override public OptionalBool isAccessibleForVirtualDispatchFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo) { + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { if (resolvedMethod.isVirtualMethod()) { return isAccessibleFrom(context, appInfo); } @@ -781,13 +767,13 @@ @Override public OptionalBool isAccessibleFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo) { + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { return OptionalBool.TRUE; } @Override public OptionalBool isAccessibleForVirtualDispatchFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo) { + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { return OptionalBool.TRUE; } @@ -816,13 +802,13 @@ @Override public OptionalBool isAccessibleFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo) { + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { return OptionalBool.FALSE; } @Override public OptionalBool isAccessibleForVirtualDispatchFrom( - DexProgramClass context, AppInfoWithClassHierarchy appInfo) { + ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { return OptionalBool.FALSE; }
diff --git a/src/main/java/com/android/tools/r8/graph/SuccessfulMemberResolutionResult.java b/src/main/java/com/android/tools/r8/graph/SuccessfulMemberResolutionResult.java index 84d89d0..4a94338 100644 --- a/src/main/java/com/android/tools/r8/graph/SuccessfulMemberResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/SuccessfulMemberResolutionResult.java
@@ -12,4 +12,6 @@ DexClass getResolvedHolder(); D getResolvedMember(); + + DexClassAndMember<D, R> getResolutionPair(); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java index 37e1690..345905b 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -98,13 +98,15 @@ DexEncodedMethod firstConstructor = constructors.iterator().next(); DexProto oldProto = firstConstructor.getProto(); - if (isTrivialMerge()) { + if (isTrivialMerge() && syntheticArgumentClass == null) { return oldProto; } List<DexType> parameters = new ArrayList<>(); Collections.addAll(parameters, oldProto.parameters.values); - parameters.add(dexItemFactory.intType); + if (!isTrivialMerge()) { + parameters.add(dexItemFactory.intType); + } if (syntheticArgumentClass != null) { parameters.add(syntheticArgumentClass.getArgumentClass()); } @@ -171,7 +173,15 @@ if (isTrivialMerge()) { // The constructor does not require the additional argument, just map it like a regular // method. - lensBuilder.mapMethod(constructors.iterator().next().method, newConstructorReference); + DexEncodedMethod oldConstructor = constructors.iterator().next(); + if (addExtraNull) { + List<ExtraParameter> extraParameters = new LinkedList<>(); + extraParameters.add(new ExtraUnusedNullParameter()); + lensBuilder.mapMergedConstructor( + oldConstructor.method, newConstructorReference, extraParameters); + } else { + lensBuilder.mapMethod(oldConstructor.method, newConstructorReference); + } } else { // Map each old constructor to the newly synthesized constructor in the graph lens. for (DexEncodedMethod oldConstructor : constructors) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java index d33b15d..1886a48 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -9,6 +9,8 @@ import com.android.tools.r8.graph.DirectMappedDexApplication; import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses; import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations; +import com.android.tools.r8.horizontalclassmerging.policies.NoClassesOrMembersWithAnnotations; +import com.android.tools.r8.horizontalclassmerging.policies.NoClassesWithInterfaces; import com.android.tools.r8.horizontalclassmerging.policies.NoFields; import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses; import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces; @@ -17,6 +19,7 @@ import com.android.tools.r8.horizontalclassmerging.policies.NoStaticClassInitializer; import com.android.tools.r8.horizontalclassmerging.policies.NotEntryPoint; import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging; +import com.android.tools.r8.horizontalclassmerging.policies.NotVerticallyMergedIntoSubtype; import com.android.tools.r8.horizontalclassmerging.policies.PreventChangingVisibility; import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoMainDex; import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries; @@ -50,10 +53,13 @@ new NoFields(), // TODO(b/166071504): Allow merging of classes that implement interfaces. new NoInterfaces(), + new NoClassesWithInterfaces(), new NoAnnotations(), + new NoClassesOrMembersWithAnnotations(), new NoInnerClasses(), new NoStaticClassInitializer(), new NoKeepRules(appView), + new NotVerticallyMergedIntoSubtype(appView), new NoRuntimeTypeChecks(classMergingEnqueuerExtension), new NotEntryPoint(appView.dexItemFactory()), new PreventMergeIntoMainDex(appView, mainDexTracingResult),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotations.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotations.java index f96a787..9190247 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotations.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotations.java
@@ -10,6 +10,6 @@ public class NoAnnotations extends SingleClassPolicy { @Override public boolean canMerge(DexProgramClass program) { - return !program.hasClassOrMemberAnnotations(); + return !program.isAnnotation(); } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java new file mode 100644 index 0000000..f9d2106 --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java
@@ -0,0 +1,15 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.horizontalclassmerging.policies; + +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy; + +public class NoClassesOrMembersWithAnnotations extends SingleClassPolicy { + @Override + public boolean canMerge(DexProgramClass program) { + return !program.hasClassOrMemberAnnotations(); + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesWithInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesWithInterfaces.java new file mode 100644 index 0000000..56e6756 --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesWithInterfaces.java
@@ -0,0 +1,16 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.horizontalclassmerging.policies; + +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy; + +public class NoClassesWithInterfaces extends SingleClassPolicy { + + @Override + public boolean canMerge(DexProgramClass program) { + return program.interfaces.isEmpty(); + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java index 7b194a2..63df0de 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
@@ -11,6 +11,6 @@ @Override public boolean canMerge(DexProgramClass program) { - return program.interfaces.isEmpty(); + return !program.isInterface(); } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java new file mode 100644 index 0000000..67afbc3 --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java
@@ -0,0 +1,26 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.horizontalclassmerging.policies; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public class NotVerticallyMergedIntoSubtype extends SingleClassPolicy { + private final AppView<AppInfoWithLiveness> appView; + + public NotVerticallyMergedIntoSubtype(AppView<AppInfoWithLiveness> appView) { + this.appView = appView; + } + + @Override + public boolean canMerge(DexProgramClass program) { + if (appView.verticallyMergedClasses() == null) { + return true; + } + return !appView.verticallyMergedClasses().hasBeenMergedIntoSubtype(program.type); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java index b7e359e..3c9bd86 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -6,7 +6,6 @@ import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; -import com.android.tools.r8.graph.AccessControl; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; @@ -14,6 +13,7 @@ import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; +import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; @@ -80,12 +80,9 @@ @Override public boolean isMaterializableInContext( AppView<AppInfoWithLiveness> appView, ProgramMethod context) { - return AccessControl.isFieldAccessible( - appView.appInfo().resolveField(field).getResolvedField(), - appView.definitionForHolder(field), - context.getHolder(), - appView.appInfo()) - .isTrue(); + SuccessfulFieldResolutionResult resolutionResult = + appView.appInfo().resolveField(field).asSuccessfulResolution(); + return resolutionResult != null && resolutionResult.isAccessibleFrom(context, appView).isTrue(); } @Override
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 da1a799..6dc372e 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
@@ -56,10 +56,6 @@ public final class BackportedMethodRewriter { - // Don't change this name, at least not without adding special-casing in DexType to support - // merging old dex code in Bundletool. - public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$backportedMethods$utility"; - private final AppView<?> appView; private final RewritableMethods rewritableMethods; private final boolean enabled;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java index 7f697a9..e58eeba 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -1002,18 +1002,26 @@ // classes if needed. AppInfo appInfo = appView.appInfo(); MainDexClasses mainDexClasses = appInfo.getMainDexClasses(); - processInterfaces(builder, flavour) - .forEach( - (interfaceClass, synthesizedClass) -> { - // Don't need to optimize synthesized class since all of its methods - // are just moved from interfaces and don't need to be re-processed. - builder.addSynthesizedClass(synthesizedClass); - boolean addToMainDexClasses = - interfaceClass.isProgramClass() - && mainDexClasses.contains(interfaceClass.asProgramClass()); - appInfo.addSynthesizedClass(synthesizedClass, addToMainDexClasses); - }); - + InterfaceProcessorNestedGraphLens.Builder graphLensBuilder = + InterfaceProcessorNestedGraphLens.builder(); + Map<DexClass, DexProgramClass> classMapping = + processInterfaces(builder, flavour, graphLensBuilder); + InterfaceProcessorNestedGraphLens graphLens = + graphLensBuilder.build(appView.dexItemFactory(), appView.graphLens()); + if (appView.enableWholeProgramOptimizations() && graphLens != null) { + appView.setGraphLens(graphLens); + } + classMapping.forEach( + (interfaceClass, synthesizedClass) -> { + // Don't need to optimize synthesized class since all of its methods + // are just moved from interfaces and don't need to be re-processed. + builder.addSynthesizedClass(synthesizedClass); + boolean addToMainDexClasses = + interfaceClass.isProgramClass() + && mainDexClasses.contains(interfaceClass.asProgramClass()); + appInfo.addSynthesizedClass(synthesizedClass, addToMainDexClasses); + }); + new InterfaceMethodRewriterFixup(appView, graphLens).run(); if (appView.options().isDesugaredLibraryCompilation()) { renameEmulatedInterfaces(); } @@ -1036,9 +1044,10 @@ && clazz.isInterface() == mustBeInterface; } - private Map<DexClass, DexProgramClass> processInterfaces(Builder<?> builder, Flavor flavour) { - InterfaceProcessorNestedGraphLens.Builder graphLensBuilder = - InterfaceProcessorNestedGraphLens.builder(); + private Map<DexClass, DexProgramClass> processInterfaces( + Builder<?> builder, + Flavor flavour, + InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) { InterfaceProcessor processor = new InterfaceProcessor(appView, this); for (DexProgramClass clazz : builder.getProgramClasses()) { if (shouldProcess(clazz, flavour, true)) { @@ -1049,9 +1058,6 @@ DexProgramClass dispatchClass = processor.process(entry.getKey(), entry.getValue()); dispatchClass.forEachProgramMethod(synthesizedMethods::add); } - if (appView.enableWholeProgramOptimizations()) { - appView.setGraphLens(graphLensBuilder.build(appView.dexItemFactory(), appView.graphLens())); - } return processor.syntheticClasses; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriterFixup.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriterFixup.java new file mode 100644 index 0000000..995f693 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriterFixup.java
@@ -0,0 +1,63 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.ir.desugar; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.EnclosingMethodAttribute; +import com.android.tools.r8.graph.InnerClassAttribute; +import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens; + +class InterfaceMethodRewriterFixup { + private final AppView<?> appView; + private final InterfaceProcessorNestedGraphLens graphLens; + + InterfaceMethodRewriterFixup(AppView<?> appView, InterfaceProcessorNestedGraphLens graphLens) { + this.appView = appView; + this.graphLens = graphLens; + } + + void run() { + if (graphLens == null) { + return; + } + for (DexProgramClass clazz : appView.appInfo().classes()) { + if (clazz.getEnclosingMethodAttribute() != null + && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) { + // Only relevant to rewrite for local or anonymous classes, as the interface + // desugaring only moves methods to the companion class. + InnerClassAttribute innerClassAttributeForThisClass = + clazz.getInnerClassAttributeForThisClass(); + if (innerClassAttributeForThisClass != null + && innerClassAttributeForThisClass.getOuter() == null) { + clazz.setEnclosingMethodAttribute( + fixupEnclosingMethodAttribute(clazz.getEnclosingMethodAttribute())); + } + } + } + } + + private EnclosingMethodAttribute fixupEnclosingMethodAttribute( + EnclosingMethodAttribute enclosingMethodAttribute) { + DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod(); + DexMethod mappedEnclosingMethod = fixupDexMethodForEnclosingMethod(enclosingMethod); + return mappedEnclosingMethod != enclosingMethod + ? new EnclosingMethodAttribute(mappedEnclosingMethod) + : enclosingMethodAttribute; + } + + private DexMethod fixupDexMethodForEnclosingMethod(DexMethod method) { + if (method == null) { + return null; + } + // Map default methods to their companion methods. + DexMethod mappedMethod = graphLens.getExtraOriginalMethodSignatures().inverse().get(method); + if (mappedMethod != null) { + return mappedMethod; + } + // For other methods moved to the companion class use the lens mapping. + return graphLens.getRenamedMethodSignature(method); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java index 67d5b2a..20c3ae4 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -428,6 +428,10 @@ this.extraOriginalMethodSignatures = tmp; } + public BiMap<DexMethod, DexMethod> getExtraOriginalMethodSignatures() { + return extraOriginalMethodSignatures; + } + @Override public boolean isInterfaceProcessorLens() { return true; @@ -476,11 +480,12 @@ } @Override - public GraphLens build(DexItemFactory dexItemFactory, GraphLens previousLens) { + public InterfaceProcessorNestedGraphLens build( + DexItemFactory dexItemFactory, GraphLens previousLens) { if (originalFieldSignatures.isEmpty() && originalMethodSignatures.isEmpty() && extraOriginalMethodSignatures.isEmpty()) { - return previousLens; + return null; } return new InterfaceProcessorNestedGraphLens( typeMap,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index ca93336..273f7b7 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -65,7 +65,6 @@ import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.IteratorUtils; import com.android.tools.r8.utils.ListUtils; -import com.android.tools.r8.utils.OptionalBool; import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.collections.ProgramMethodSet; @@ -983,7 +982,8 @@ .appInfo() .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit()) .asSingleResolution(); - if (resolutionResult == null) { + if (resolutionResult == null + || resolutionResult.isAccessibleFrom(context, appView).isPossiblyFalse()) { continue; } @@ -994,16 +994,6 @@ continue; } - OptionalBool methodAccessible = - AccessControl.isMethodAccessible( - singleTarget.getDefinition(), - singleTarget.getHolder().asDexClass(), - context, - appView); - if (!methodAccessible.isTrue()) { - continue; - } - DexEncodedMethod singleTargetMethod = singleTarget.getDefinition(); WhyAreYouNotInliningReporter whyAreYouNotInliningReporter = oracle.isForcedInliningOracle() @@ -1022,6 +1012,16 @@ continue; } + DexType downcastTypeOrNull = getDowncastTypeIfNeeded(strategy, invoke, singleTarget); + if (downcastTypeOrNull != null) { + DexClass downcastClass = appView.definitionFor(downcastTypeOrNull, context); + if (downcastClass == null + || AccessControl.isClassAccessible(downcastClass, context, appView) + .isPossiblyFalse()) { + continue; + } + } + if (!inlineeStack.isEmpty() && !strategy.allowInliningOfInvokeInInlinee( action, inlineeStack.size(), whyAreYouNotInliningReporter)) { @@ -1069,12 +1069,7 @@ iterator.previous(); strategy.markInlined(inlinee); iterator.inlineInvoke( - appView, - code, - inlinee.code, - blockIterator, - blocksToRemove, - getDowncastTypeIfNeeded(strategy, invoke, singleTarget)); + appView, code, inlinee.code, blockIterator, blocksToRemove, downcastTypeOrNull); if (inlinee.reason == Reason.SINGLE_CALLER) { feedback.markInlinedIntoSingleCallSite(singleTargetMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java index ec1f830..b917507 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -60,7 +60,6 @@ import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter; import com.android.tools.r8.kotlin.KotlinClassLevelInfo; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.OptionalBool; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.Timing; @@ -292,7 +291,8 @@ .appInfo() .resolveMethod(invokeMethod.getInvokedMethod(), invokeMethod.getInterfaceBit()) .asSingleResolution(); - if (resolutionResult == null) { + if (resolutionResult == null + || resolutionResult.isAccessibleFrom(method, appView).isPossiblyFalse()) { return user; // Not eligible. } @@ -311,13 +311,11 @@ return user; // Not eligible. } - OptionalBool methodAccessible = - AccessControl.isMethodAccessible( - singleTargetMethod, singleTarget.getHolder().asDexClass(), method, appView); - - if (!methodAccessible.isTrue()) { - return user; // Not eligible. + if (AccessControl.isClassAccessible(singleTarget.getHolder(), method, appView) + .isPossiblyFalse()) { + continue; } + // Eligible constructor call (for new instance roots only). if (user.isInvokeDirect()) { InvokeDirect invoke = user.asInvokeDirect();
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java index b8022b9..5d3f09b 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AccessControl; import com.android.tools.r8.graph.AppView; 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; @@ -330,8 +331,8 @@ return; } - DexEncodedField resolvedField = resolutionResult.getResolvedField(); - if (resolvedField.field == field) { + DexClassAndField resolvedField = resolutionResult.getResolutionPair(); + if (resolvedField.getReference() == field) { assert false; return; } @@ -341,7 +342,7 @@ boolean accessibleInAllContexts = true; for (ProgramMethod context : contexts) { boolean inaccessibleInContext = - AccessControl.isFieldAccessible( + AccessControl.isMemberAccessible( resolvedField, resolutionResult.getResolvedHolder(), context, appView) .isPossiblyFalse(); if (inaccessibleInContext) { @@ -353,7 +354,8 @@ if (accessibleInAllContexts) { builder.map( field, - lens.lookupField(validTargetFor(resolvedField.field, field, DexClass::lookupField))); + lens.lookupField( + validTargetFor(resolvedField.getReference(), field, DexClass::lookupField))); } }
diff --git a/src/main/java/com/android/tools/r8/references/ArrayReference.java b/src/main/java/com/android/tools/r8/references/ArrayReference.java index 7946c2f..919bbd7 100644 --- a/src/main/java/com/android/tools/r8/references/ArrayReference.java +++ b/src/main/java/com/android/tools/r8/references/ArrayReference.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.Keep; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.utils.DescriptorUtils; +import java.util.Objects; /** Reference to an array type. */ @Keep @@ -13,7 +14,8 @@ private final int dimensions; private final TypeReference baseType; - private String descriptor; + // Consider removing the descriptor field as dimensions and baseType encode the same information. + private final String descriptor; private ArrayReference(int dimensions, TypeReference baseType, String descriptor) { assert dimensions > 0; @@ -71,11 +73,18 @@ @Override public boolean equals(Object o) { - return this == o; + if (this == o) { + return true; + } + if (!(o instanceof ArrayReference)) { + return false; + } + ArrayReference other = (ArrayReference) o; + return dimensions == other.dimensions && baseType.equals(other.baseType); } @Override public int hashCode() { - return System.identityHashCode(this); + return Objects.hash(dimensions, baseType); } }
diff --git a/src/main/java/com/android/tools/r8/references/ClassReference.java b/src/main/java/com/android/tools/r8/references/ClassReference.java index 9c0f479..3e30319 100644 --- a/src/main/java/com/android/tools/r8/references/ClassReference.java +++ b/src/main/java/com/android/tools/r8/references/ClassReference.java
@@ -41,12 +41,18 @@ @Override public boolean equals(Object o) { - return this == o; + if (this == o) { + return true; + } + if (!(o instanceof ClassReference)) { + return false; + } + return descriptor.equals(((ClassReference) o).descriptor); } @Override public int hashCode() { - return System.identityHashCode(this); + return descriptor.hashCode(); } @Override
diff --git a/src/main/java/com/android/tools/r8/references/FieldReference.java b/src/main/java/com/android/tools/r8/references/FieldReference.java index c0c5120..d4d74fd 100644 --- a/src/main/java/com/android/tools/r8/references/FieldReference.java +++ b/src/main/java/com/android/tools/r8/references/FieldReference.java
@@ -13,19 +13,15 @@ * type of the field. */ @Keep -public class FieldReference { +public final class FieldReference { private final ClassReference holderClass; private final String fieldName; private final TypeReference fieldType; - boolean isUnknown() { - return false; - } - FieldReference(ClassReference holderClass, String fieldName, TypeReference fieldType) { assert holderClass != null; assert fieldName != null; - assert fieldType != null || isUnknown(); + assert fieldType != null; this.holderClass = holderClass; this.fieldName = fieldName; this.fieldType = fieldType; @@ -69,16 +65,4 @@ public String toString() { return getHolderClass().toString() + getFieldName() + ":" + getFieldType().getDescriptor(); } - - public static final class UnknownFieldReference extends FieldReference { - - public UnknownFieldReference(ClassReference holderClass, String fieldName) { - super(holderClass, fieldName, null); - } - - @Override - boolean isUnknown() { - return true; - } - } }
diff --git a/src/main/java/com/android/tools/r8/references/MethodReference.java b/src/main/java/com/android/tools/r8/references/MethodReference.java index 7462a57..9ff0649 100644 --- a/src/main/java/com/android/tools/r8/references/MethodReference.java +++ b/src/main/java/com/android/tools/r8/references/MethodReference.java
@@ -17,7 +17,7 @@ * full list of formal parameters. */ @Keep -public class MethodReference { +public final class MethodReference { private final ClassReference holderClass; private final String methodName; private final List<TypeReference> formalTypes; @@ -30,17 +30,12 @@ TypeReference returnType) { assert holderClass != null; assert methodName != null; - assert formalTypes != null || isUnknown(); this.holderClass = holderClass; this.methodName = methodName; this.formalTypes = formalTypes; this.returnType = returnType; } - public boolean isUnknown() { - return false; - } - public ClassReference getHolderClass() { return holderClass; } @@ -90,16 +85,4 @@ public String toString() { return getHolderClass().toString() + getMethodName() + getMethodDescriptor(); } - - public static final class UnknownMethodReference extends MethodReference { - - @Override - public boolean isUnknown() { - return true; - } - - public UnknownMethodReference(ClassReference holderClass, String methodName) { - super(holderClass, methodName, null, null); - } - } }
diff --git a/src/main/java/com/android/tools/r8/references/PackageReference.java b/src/main/java/com/android/tools/r8/references/PackageReference.java index 1c54921..b2474fe 100644 --- a/src/main/java/com/android/tools/r8/references/PackageReference.java +++ b/src/main/java/com/android/tools/r8/references/PackageReference.java
@@ -33,7 +33,7 @@ if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof PackageReference)) { return false; } PackageReference that = (PackageReference) o;
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 444eee3..3a30ec4 100644 --- a/src/main/java/com/android/tools/r8/references/Reference.java +++ b/src/main/java/com/android/tools/r8/references/Reference.java
@@ -6,21 +6,18 @@ import com.android.tools.r8.Keep; import com.android.tools.r8.utils.DescriptorUtils; import com.google.common.collect.ImmutableList; -import com.google.common.collect.MapMaker; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; -import java.util.concurrent.ConcurrentMap; /** * Reference provider/factory. * * <p>The reference class provides a single point for creating and managing reference objects that - * represent types, methods and fields in a JVM/DEX application. The objects are interned/shared so - * that allocation is reduced and equality is constant time. Internally, the objects are weakly - * stored to avoid memory pressure. + * represent types, methods and fields in a JVM/DEX application. The objects may or may not be + * interned/shared. * * <p>No guarantees are made on identity and all references must be compared by {@code equals}. */ @@ -36,39 +33,10 @@ public static PrimitiveReference LONG = PrimitiveReference.LONG; public static PrimitiveReference DOUBLE = PrimitiveReference.DOUBLE; - private static Reference instance; - - // Weak map of classes. The values are weak and the descriptor is used as key. - private final ConcurrentMap<String, ClassReference> classes = - new MapMaker().weakValues().makeMap(); - - // Weak map of arrays. The values are weak and the descriptor is used as key. - private final ConcurrentMap<String, ArrayReference> arrays = - new MapMaker().weakValues().makeMap(); - - // Weak map of method references. Keys are strong and must not be identical to the value. - private final ConcurrentMap<MethodReference, MethodReference> methods = - new MapMaker().weakValues().makeMap(); - - // Weak map of field references. Keys are strong and must not be identical to the value. - private final ConcurrentMap<FieldReference, FieldReference> fields = - new MapMaker().weakValues().makeMap(); - private Reference() { // Intentionally hidden. } - private static Reference getInstance() { - if (instance == null) { - synchronized (Reference.class) { - if (instance == null) { - instance = new Reference(); - } - } - } - return instance; - } - public static TypeReference returnTypeFromDescriptor(String descriptor) { return descriptor.equals("V") ? null : typeFromDescriptor(descriptor); } @@ -99,7 +67,7 @@ /** Get a class reference from a JVM descriptor. */ public static ClassReference classFromDescriptor(String descriptor) { - return getInstance().classes.computeIfAbsent(descriptor, ClassReference::fromDescriptor); + return ClassReference.fromDescriptor(descriptor); } /** @@ -127,17 +95,12 @@ /** Get an array reference from a JVM descriptor. */ public static ArrayReference arrayFromDescriptor(String descriptor) { - return getInstance().arrays.computeIfAbsent(descriptor, ArrayReference::fromDescriptor); + return ArrayReference.fromDescriptor(descriptor); } /** Get an array reference from a base type and dimensions. */ public static ArrayReference array(TypeReference baseType, int dimensions) { - String arrayDescriptor = - DescriptorUtils.toArrayDescriptor(dimensions, baseType.getDescriptor()); - return getInstance() - .arrays - .computeIfAbsent( - arrayDescriptor, descriptor -> ArrayReference.fromBaseType(baseType, dimensions)); + return ArrayReference.fromBaseType(baseType, dimensions); } /** Get a method reference from its full reference specification. */ @@ -146,15 +109,8 @@ String methodName, List<TypeReference> formalTypes, TypeReference returnType) { - MethodReference key = new MethodReference( + return new MethodReference( holderClass, methodName, ImmutableList.copyOf(formalTypes), returnType); - return getInstance().methods.computeIfAbsent(key, - // Allocate a distinct value for the canonical reference so the key is not a strong pointer. - k -> new MethodReference( - k.getHolderClass(), - k.getMethodName(), - ImmutableList.copyOf(k.getFormalTypes()), - k.getReturnType())); } /** Get a method reference from a Java reflection method. */ @@ -208,10 +164,7 @@ /** Get a field reference from its full reference specification. */ public static FieldReference field( ClassReference holderClass, String fieldName, TypeReference fieldType) { - FieldReference key = new FieldReference(holderClass, fieldName, fieldType); - return getInstance().fields.computeIfAbsent(key, - // Allocate a distinct value for the canonical reference so the key is not a strong pointer. - k -> new FieldReference(k.getHolderClass(), k.getFieldName(), k.getFieldType())); + return new FieldReference(holderClass, fieldName, fieldType); } /** Get a field reference from a Java reflection field. */
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java index e13c832..4ca10b6 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -92,9 +92,11 @@ return Stream.of( new Element( this, - mapper == null ? obfuscatedReference : Reference.classFromTypeName(mapper.originalName), - mapper, - false)); + RetracedClass.create( + mapper == null + ? obfuscatedReference + : Reference.classFromTypeName(mapper.originalName)), + mapper)); } @Override @@ -115,20 +117,19 @@ public static class Element { private final RetraceClassResult classResult; - private final ClassReference classReference; + private final RetracedClass classReference; private final ClassNamingForNameMapper mapper; public Element( RetraceClassResult classResult, - ClassReference classReference, - ClassNamingForNameMapper mapper, - boolean isAmbiguous) { + RetracedClass classReference, + ClassNamingForNameMapper mapper) { this.classResult = classResult; this.classReference = classReference; this.mapper = mapper; } - public ClassReference getClassReference() { + public RetracedClass getRetracedClass() { return classReference; }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java index 5477eaa..265b3b2 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -7,9 +7,6 @@ import com.android.tools.r8.Keep; import com.android.tools.r8.naming.MemberNaming; import com.android.tools.r8.naming.MemberNaming.FieldSignature; -import com.android.tools.r8.references.ClassReference; -import com.android.tools.r8.references.FieldReference; -import com.android.tools.r8.references.FieldReference.UnknownFieldReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.DescriptorUtils; import java.util.List; @@ -51,14 +48,14 @@ return memberNamings.size() > 1; } + public RetracedField getUnknownReference() { + return RetracedField.createUnknown(classElement.getRetracedClass(), obfuscatedName); + } + @Override public Stream<Element> stream() { if (!hasRetraceResult()) { - return Stream.of( - new Element( - this, - classElement, - new UnknownFieldReference(classElement.getClassReference(), obfuscatedName))); + return Stream.of(new Element(this, classElement, getUnknownReference())); } assert !memberNamings.isEmpty(); return memberNamings.stream() @@ -67,21 +64,24 @@ assert memberNaming.isFieldNaming(); FieldSignature fieldSignature = memberNaming.getOriginalSignature().asFieldSignature(); - ClassReference holder = + RetracedClass holder = fieldSignature.isQualified() - ? Reference.classFromDescriptor( - DescriptorUtils.javaTypeToDescriptor( - fieldSignature.toHolderFromQualified())) - : classElement.getClassReference(); + ? RetracedClass.create( + Reference.classFromDescriptor( + DescriptorUtils.javaTypeToDescriptor( + fieldSignature.toHolderFromQualified()))) + : classElement.getRetracedClass(); return new Element( this, classElement, - Reference.field( + RetracedField.create( holder, - fieldSignature.isQualified() - ? fieldSignature.toUnqualifiedName() - : fieldSignature.name, - Reference.typeFromTypeName(fieldSignature.type))); + Reference.field( + holder.getClassReference(), + fieldSignature.isQualified() + ? fieldSignature.toUnqualifiedName() + : fieldSignature.name, + Reference.typeFromTypeName(fieldSignature.type)))); }); } @@ -93,25 +93,29 @@ public static class Element { - private final FieldReference fieldReference; + private final RetracedField fieldReference; private final RetraceFieldResult retraceFieldResult; private final RetraceClassResult.Element classElement; private Element( RetraceFieldResult retraceFieldResult, RetraceClassResult.Element classElement, - FieldReference fieldReference) { + RetracedField fieldReference) { this.classElement = classElement; this.fieldReference = fieldReference; this.retraceFieldResult = retraceFieldResult; } - public FieldReference getFieldReference() { + public boolean isUnknown() { + return fieldReference.isUnknown(); + } + + public RetracedField getField() { return fieldReference; } public RetraceFieldResult getRetraceFieldResult() { - return getRetraceFieldResult(); + return retraceFieldResult; } public RetraceClassResult.Element getClassElement() {
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java index 7977858..93c87e8 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
@@ -9,9 +9,6 @@ import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.naming.Range; -import com.android.tools.r8.references.ClassReference; -import com.android.tools.r8.references.MethodReference; -import com.android.tools.r8.references.MethodReference.UnknownMethodReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.references.TypeReference; import com.android.tools.r8.utils.DescriptorUtils; @@ -41,8 +38,8 @@ assert classElement != null; } - public UnknownMethodReference getUnknownReference() { - return new UnknownMethodReference(classElement.getClassReference(), obfuscatedName); + public RetracedMethod getUnknownReference() { + return RetracedMethod.createUnknown(classElement.getRetracedClass(), obfuscatedName); } private boolean hasRetraceResult() { @@ -102,12 +99,13 @@ .map( mappedRange -> { MethodSignature signature = mappedRange.signature; - ClassReference holder = + RetracedClass holder = mappedRange.signature.isQualified() - ? Reference.classFromDescriptor( - DescriptorUtils.javaTypeToDescriptor( - mappedRange.signature.toHolderFromQualified())) - : classElement.getClassReference(); + ? RetracedClass.create( + Reference.classFromDescriptor( + DescriptorUtils.javaTypeToDescriptor( + mappedRange.signature.toHolderFromQualified()))) + : classElement.getRetracedClass(); List<TypeReference> formalTypes = new ArrayList<>(signature.parameters.length); for (String parameter : signature.parameters) { formalTypes.add(Reference.typeFromTypeName(parameter)); @@ -115,12 +113,14 @@ TypeReference returnType = Reference.returnTypeFromDescriptor( DescriptorUtils.javaTypeToDescriptor(signature.type)); - MethodReference retracedMethod = - Reference.method( + RetracedMethod retracedMethod = + RetracedMethod.create( holder, - signature.isQualified() ? signature.toUnqualifiedName() : signature.name, - formalTypes, - returnType); + Reference.method( + holder.getClassReference(), + signature.isQualified() ? signature.toUnqualifiedName() : signature.name, + formalTypes, + returnType)); return new Element(this, classElement, retracedMethod, mappedRange); }); } @@ -133,7 +133,7 @@ public static class Element { - private final MethodReference methodReference; + private final RetracedMethod methodReference; private final RetraceMethodResult retraceMethodResult; private final RetraceClassResult.Element classElement; private final MappedRange mappedRange; @@ -141,7 +141,7 @@ private Element( RetraceMethodResult retraceMethodResult, RetraceClassResult.Element classElement, - MethodReference methodReference, + RetracedMethod methodReference, MappedRange mappedRange) { this.classElement = classElement; this.retraceMethodResult = retraceMethodResult; @@ -149,7 +149,23 @@ this.mappedRange = mappedRange; } - public MethodReference getMethodReference() { + public boolean isUnknown() { + return methodReference.isUnknown(); + } + + public boolean hasNoLineNumberRange() { + return mappedRange == null || mappedRange.minifiedRange == null; + } + + public boolean containsMinifiedLineNumber(int linePosition) { + if (hasNoLineNumberRange()) { + return false; + } + return mappedRange.minifiedRange.from <= linePosition + && linePosition <= mappedRange.minifiedRange.to; + } + + public RetracedMethod getMethod() { return methodReference; } @@ -165,18 +181,6 @@ return mappedRange != null ? mappedRange.getOriginalLineNumber(linePosition) : linePosition; } - public boolean containsMinifiedLineNumber(int linePosition) { - if (hasNoLineNumberRange()) { - return false; - } - return mappedRange.minifiedRange.from <= linePosition - && linePosition <= mappedRange.minifiedRange.to; - } - - public boolean hasNoLineNumberRange() { - return mappedRange == null || mappedRange.minifiedRange == null; - } - public int getFirstLineNumberOfOriginalRange() { if (hasNoLineNumberRange()) { return 0; @@ -188,5 +192,6 @@ return RetraceUtils.getSourceFile( classElement, methodReference.getHolderClass(), sourceFile, retraceMethodResult.retracer); } + } }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java index 40b3f41..8606324 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -7,11 +7,11 @@ import com.android.tools.r8.DiagnosticsHandler; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.references.ClassReference; -import com.android.tools.r8.references.FieldReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.references.TypeReference; import com.android.tools.r8.retrace.RetraceClassResult.Element; import com.android.tools.r8.retrace.RetraceRegularExpression.ClassNameGroup.ClassNameGroupHandler; +import com.android.tools.r8.retrace.RetracedField.KnownRetracedField; import com.android.tools.r8.utils.Box; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.StringDiagnostic; @@ -101,7 +101,7 @@ if (isAmbiguous) { retracedStrings.sort(new RetraceLineComparator()); } - ClassReference previousContext = null; + RetracedClass previousContext = null; for (RetraceString retracedString : retracedStrings) { String finalString = retracedString.builder.build(string); if (!isAmbiguous) { @@ -109,7 +109,7 @@ continue; } assert retracedString.getClassContext() != null; - ClassReference currentContext = retracedString.getClassContext().getClassReference(); + RetracedClass currentContext = retracedString.getClassContext().getRetracedClass(); if (currentContext.equals(previousContext)) { int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(finalString); finalString = @@ -131,9 +131,9 @@ (line, t) -> { switch (t) { case CLASS: - return line.getClassContext().getClassReference().getTypeName(); + return line.getClassContext().getRetracedClass().getTypeName(); case METHOD: - return line.getMethodContext().getMethodReference().getMethodName(); + return line.getMethodContext().getMethod().getMethodName(); case SOURCE: return line.getSource(); case LINE: @@ -241,7 +241,7 @@ static class RetraceStringContext { private final Element classContext; - private final ClassReference qualifiedContext; + private final RetracedClass qualifiedContext; private final String methodName; private final RetraceMethodResult.Element methodContext; private final int minifiedLineNumber; @@ -251,7 +251,7 @@ private RetraceStringContext( Element classContext, - ClassReference qualifiedContext, + RetracedClass qualifiedContext, String methodName, RetraceMethodResult.Element methodContext, int minifiedLineNumber, @@ -273,7 +273,7 @@ } private RetraceStringContext withClassContext( - Element classContext, ClassReference qualifiedContext) { + Element classContext, RetracedClass qualifiedContext) { return new RetraceStringContext( classContext, qualifiedContext, @@ -299,7 +299,7 @@ private RetraceStringContext withMethodContext( RetraceMethodResult.Element methodContext, - ClassReference qualifiedContext, + RetracedClass qualifiedContext, boolean isAmbiguous) { return new RetraceStringContext( classContext, @@ -312,7 +312,7 @@ isAmbiguous); } - private RetraceStringContext withQualifiedContext(ClassReference qualifiedContext) { + private RetraceStringContext withQualifiedContext(RetracedClass qualifiedContext) { return new RetraceStringContext( classContext, qualifiedContext, @@ -485,7 +485,7 @@ abstract static class ClassNameGroup extends RegularExpressionGroup { - abstract String getClassName(ClassReference classReference); + abstract String getClassName(RetracedClass classReference); abstract ClassReference classFromMatch(String match); @@ -525,13 +525,13 @@ element -> { final RetraceString newRetraceString = retraceString.updateContext( - context -> context.withClassContext(element, element.getClassReference())); + context -> context.withClassContext(element, element.getRetracedClass())); retraceStrings.add(newRetraceString); if (qualifiedHandler == null) { // If there is no qualified handler, commit right away. newRetraceString.builder.appendRetracedString( original, - classNameGroup.getClassName(element.getClassReference()), + classNameGroup.getClassName(element.getRetracedClass()), startOfGroup, matcher.end(captureGroup)); } @@ -543,7 +543,7 @@ void commitClassName( String original, RetraceString retraceString, - ClassReference qualifiedContext, + RetracedClass qualifiedContext, Matcher matcher) { if (matcher.start(captureGroup) == NO_MATCH) { return; @@ -568,7 +568,7 @@ assert !retraceClassResult.isAmbiguous(); Box<RetraceStringContext> box = new Box<>(); retraceClassResult.forEach( - element -> box.set(context.withClassContext(element, element.getClassReference()))); + element -> box.set(context.withClassContext(element, element.getRetracedClass()))); return box.get(); } @@ -597,7 +597,7 @@ } @Override - String getClassName(ClassReference classReference) { + String getClassName(RetracedClass classReference) { return classReference.getTypeName(); } @@ -615,7 +615,7 @@ } @Override - String getClassName(ClassReference classReference) { + String getClassName(RetracedClass classReference) { return classReference.getBinaryName(); } @@ -672,18 +672,15 @@ final RetraceString newRetraceString = retraceString.duplicate(newContext); if (classNameGroupHandler != null) { classNameGroupHandler.commitClassName( - original, - newRetraceString, - element.getMethodReference().getHolderClass(), - matcher); + original, newRetraceString, element.getMethod().getHolderClass(), matcher); } retracedStrings.add( newRetraceString.appendRetracedString( original, printVerbose ? RetraceUtils.methodDescriptionFromMethodReference( - element.getMethodReference(), false, true) - : element.getMethodReference().getMethodName(), + element.getMethod(), false, true) + : element.getMethod().getMethodName(), startOfGroup, matcher.end(captureGroup))); }); @@ -722,7 +719,7 @@ element, retraceString.context.withMethodContext( element, - element.getMethodReference().getHolderClass(), + element.getMethod().getHolderClass(), element.getRetraceMethodResult().isAmbiguous()))); } } @@ -778,20 +775,16 @@ element -> { if (classNameGroupHandler != null) { classNameGroupHandler.commitClassName( - original, - retraceString, - element.getFieldReference().getHolderClass(), - matcher); + original, retraceString, element.getField().getHolderClass(), matcher); } retracedStrings.add( retraceString .updateContext( context -> - context.withQualifiedContext( - element.getFieldReference().getHolderClass())) + context.withQualifiedContext(element.getField().getHolderClass())) .appendRetracedString( original, - getFieldString(element.getFieldReference()), + getFieldString(element.getField()), startOfGroup, matcher.end(captureGroup))); }); @@ -801,11 +794,13 @@ }; } - private String getFieldString(FieldReference fieldReference) { - if (!printVerbose) { + private String getFieldString(RetracedField fieldReference) { + if (!printVerbose || fieldReference.isUnknown()) { return fieldReference.getFieldName(); } - return fieldReference.getFieldType().getTypeName() + " " + fieldReference.getFieldName(); + assert fieldReference.isKnown(); + KnownRetracedField knownRef = fieldReference.asKnown(); + return knownRef.getFieldType().getTypeName() + " " + fieldReference.getFieldName(); } } @@ -917,8 +912,7 @@ continue; } // If the method context is unknown, do nothing. - if (methodContext.getMethodReference().isUnknown() - || methodContext.hasNoLineNumberRange()) { + if (methodContext.isUnknown() || methodContext.hasNoLineNumberRange()) { retracedStrings.add(retraceString); continue; } @@ -997,10 +991,10 @@ for (RetraceString retraceString : strings) { retracedType.forEach( element -> { - TypeReference retracedReference = element.getTypeReference(); + RetracedType retracedReference = element.getType(); retraceString.appendRetracedString( original, - retracedReference == null ? "void" : retracedReference.getTypeName(), + retracedReference.isVoid() ? "void" : retracedReference.getTypeName(), startOfGroup, matcher.end(captureGroup)); }); @@ -1039,8 +1033,8 @@ final RetraceTypeResult retraceResult = retracer.retrace(Reference.returnTypeFromDescriptor(descriptor)); assert !retraceResult.isAmbiguous(); - final Box<TypeReference> elementBox = new Box<>(); - retraceResult.forEach(element -> elementBox.set(element.getTypeReference())); + final Box<RetracedType> elementBox = new Box<>(); + retraceResult.forEach(element -> elementBox.set(element.getType())); return elementBox.get().getTypeName(); }) .filter(Objects::nonNull)
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java index 4b21be1..e68e5c9 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
@@ -17,11 +17,11 @@ this.synthesized = synthesized; } - public String getFilename() { - return filename; - } - public boolean isSynthesized() { return synthesized; } + + public String getFilename() { + return filename; + } }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java index fd28127..e2e3616 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.DiagnosticsHandler; import com.android.tools.r8.references.ClassReference; -import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.StringUtils; @@ -235,7 +234,7 @@ new ExceptionLine( initialWhiteSpace, description, - element.getClassReference().getTypeName(), + element.getRetracedClass().getTypeName(), message))); return exceptionLines; } @@ -407,7 +406,7 @@ .forEach( classElement -> { retraceClassAndMethods( - retracer, verbose, lines, classElement.getClassReference().getTypeName()); + retracer, verbose, lines, classElement.getRetracedClass().getTypeName()); }); } else { retraceClassAndMethods(retracer, verbose, lines, retraceClassLoaderName); @@ -425,7 +424,7 @@ } retraceResult.forEach( methodElement -> { - MethodReference methodReference = methodElement.getMethodReference(); + RetracedMethod methodReference = methodElement.getMethod(); lines.add( new AtLine( startingWhitespace, @@ -568,7 +567,7 @@ exceptionLines.add( new CircularReferenceLine( startWhitespace, - element.getClassReference().getTypeName(), + element.getRetracedClass().getTypeName(), endBracketAndWhitespace))); return exceptionLines; }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java index b70602d..4f7ec0a 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.retrace; -import com.android.tools.r8.references.Reference; import com.android.tools.r8.references.TypeReference; import com.android.tools.r8.retrace.RetraceTypeResult.Element; import java.util.function.Consumer; @@ -24,15 +23,17 @@ public Stream<Element> stream() { // Handle void and primitive types as single element results. if (obfuscatedType == null || obfuscatedType.isPrimitive()) { - return Stream.of(new Element(obfuscatedType)); + return Stream.of(new Element(RetracedType.create(obfuscatedType))); } if (obfuscatedType.isArray()) { int dimensions = obfuscatedType.asArray().getDimensions(); return retracer.retrace(obfuscatedType.asArray().getBaseType()).stream() - .map(base -> new Element(Reference.array(base.getTypeReference(), dimensions))); + .map( + baseElement -> + new Element(RetracedType.create(baseElement.retracedType.toArray(dimensions)))); } return retracer.retrace(obfuscatedType.asClass()).stream() - .map(clazz -> new Element(clazz.getClassReference())); + .map(classElement -> new Element(classElement.getRetracedClass().getRetracedType())); } public boolean isAmbiguous() { @@ -47,14 +48,14 @@ public static class Element { - private final TypeReference typeReference; + private final RetracedType retracedType; - public Element(TypeReference typeReference) { - this.typeReference = typeReference; + public Element(RetracedType retracedType) { + this.retracedType = retracedType; } - public TypeReference getTypeReference() { - return typeReference; + public RetracedType getType() { + return retracedType; } } }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java index fe0bac2..335059e 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
@@ -4,9 +4,8 @@ package com.android.tools.r8.retrace; -import com.android.tools.r8.references.ClassReference; -import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.references.TypeReference; +import com.android.tools.r8.retrace.RetracedMethod.KnownRetracedMethod; import com.android.tools.r8.utils.Box; import com.google.common.collect.Sets; import com.google.common.io.Files; @@ -18,7 +17,7 @@ Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source", "PG"); public static String methodDescriptionFromMethodReference( - MethodReference methodReference, boolean appendHolder, boolean verbose) { + RetracedMethod methodReference, boolean appendHolder, boolean verbose) { StringBuilder sb = new StringBuilder(); if (appendHolder) { sb.append(methodReference.getHolderClass().getTypeName()); @@ -27,15 +26,14 @@ if (!verbose || methodReference.isUnknown()) { return sb.append(methodReference.getMethodName()).toString(); } - sb.append( - methodReference.getReturnType() == null - ? "void" - : methodReference.getReturnType().getTypeName()); + assert methodReference.isKnown(); + KnownRetracedMethod knownRef = methodReference.asKnown(); + sb.append(knownRef.isVoid() ? "void" : knownRef.getReturnType().getTypeName()); sb.append(" "); sb.append(methodReference.getMethodName()); sb.append("("); boolean seenFirstIndex = false; - for (TypeReference formalType : methodReference.getFormalTypes()) { + for (TypeReference formalType : knownRef.getFormalTypes()) { if (seenFirstIndex) { sb.append(","); } @@ -63,17 +61,17 @@ static RetraceSourceFileResult getSourceFile( RetraceClassResult.Element classElement, - ClassReference context, + RetracedClass context, String sourceFile, RetraceApi retracer) { // If no context is specified always retrace using the found class element. if (context == null) { return classElement.retraceSourceFile(sourceFile); } - if (context.equals(classElement.getClassReference())) { + if (context.equals(classElement.getRetracedClass())) { return classElement.retraceSourceFile(sourceFile); } else { - RetraceClassResult contextClassResult = retracer.retrace(context); + RetraceClassResult contextClassResult = retracer.retrace(context.getClassReference()); assert !contextClassResult.isAmbiguous(); if (contextClassResult.hasRetraceResult()) { Box<RetraceSourceFileResult> retraceSourceFile = new Box<>(); @@ -84,7 +82,7 @@ return new RetraceSourceFileResult( synthesizeFileName( context.getTypeName(), - classElement.getClassReference().getTypeName(), + classElement.getRetracedClass().getTypeName(), sourceFile, true), true);
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedClass.java b/src/main/java/com/android/tools/r8/retrace/RetracedClass.java new file mode 100644 index 0000000..af064fc --- /dev/null +++ b/src/main/java/com/android/tools/r8/retrace/RetracedClass.java
@@ -0,0 +1,55 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.retrace; + +import com.android.tools.r8.Keep; +import com.android.tools.r8.references.ClassReference; + +@Keep +public final class RetracedClass { + + private final ClassReference classReference; + + private RetracedClass(ClassReference classReference) { + assert classReference != null; + this.classReference = classReference; + } + + public static RetracedClass create(ClassReference classReference) { + return new RetracedClass(classReference); + } + + public String getTypeName() { + return classReference.getTypeName(); + } + + public String getBinaryName() { + return classReference.getBinaryName(); + } + + public RetracedType getRetracedType() { + return RetracedType.create(classReference); + } + + ClassReference getClassReference() { + return classReference; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return classReference.equals(((RetracedClass) o).classReference); + } + + @Override + public int hashCode() { + return classReference.hashCode(); + } +}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedField.java b/src/main/java/com/android/tools/r8/retrace/RetracedField.java new file mode 100644 index 0000000..b809cab --- /dev/null +++ b/src/main/java/com/android/tools/r8/retrace/RetracedField.java
@@ -0,0 +1,119 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.retrace; + +import com.android.tools.r8.Keep; +import com.android.tools.r8.references.FieldReference; +import com.android.tools.r8.references.TypeReference; +import java.util.Objects; + +@Keep +public abstract class RetracedField { + + private RetracedField() {} + + public boolean isUnknown() { + return true; + } + + public final boolean isKnown() { + return !isUnknown(); + } + + public KnownRetracedField asKnown() { + return null; + } + + public abstract RetracedClass getHolderClass(); + + public abstract String getFieldName(); + + public static final class KnownRetracedField extends RetracedField { + + private final RetracedClass classReference; + private final FieldReference fieldReference; + + private KnownRetracedField(RetracedClass classReference, FieldReference fieldReference) { + this.classReference = classReference; + this.fieldReference = fieldReference; + } + + @Override + public boolean isUnknown() { + return false; + } + + @Override + public KnownRetracedField asKnown() { + return this; + } + + @Override + public RetracedClass getHolderClass() { + return classReference; + } + + @Override + public String getFieldName() { + return fieldReference.getFieldName(); + } + + public TypeReference getFieldType() { + return fieldReference.getFieldType(); + } + + public FieldReference getFieldReference() { + return fieldReference; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + KnownRetracedField that = (KnownRetracedField) o; + assert !fieldReference.equals(that.fieldReference) + || classReference.equals(that.classReference); + return fieldReference.equals(that.fieldReference); + } + + @Override + public int hashCode() { + return Objects.hash(classReference, fieldReference); + } + } + + public static final class UnknownRetracedField extends RetracedField { + + private final RetracedClass classReference; + private final String name; + + private UnknownRetracedField(RetracedClass classReference, String name) { + this.classReference = classReference; + this.name = name; + } + + @Override + public RetracedClass getHolderClass() { + return classReference; + } + + @Override + public String getFieldName() { + return name; + } + } + + static RetracedField create(RetracedClass classReference, FieldReference fieldReference) { + return new KnownRetracedField(classReference, fieldReference); + } + + static RetracedField createUnknown(RetracedClass classReference, String name) { + return new UnknownRetracedField(classReference, name); + } +}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java b/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java new file mode 100644 index 0000000..ee3b792 --- /dev/null +++ b/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java
@@ -0,0 +1,134 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.retrace; + +import com.android.tools.r8.Keep; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.references.TypeReference; +import java.util.List; +import java.util.Objects; + +@Keep +public abstract class RetracedMethod { + + private RetracedMethod() {} + + public boolean isUnknown() { + return true; + } + + public final boolean isKnown() { + return !isUnknown(); + } + + public KnownRetracedMethod asKnown() { + return null; + } + + public abstract RetracedClass getHolderClass(); + + public abstract String getMethodName(); + + public static final class KnownRetracedMethod extends RetracedMethod { + + private final MethodReference methodReference; + private final RetracedClass classReference; + + private KnownRetracedMethod(RetracedClass classReference, MethodReference methodReference) { + assert methodReference != null; + this.classReference = classReference; + this.methodReference = methodReference; + } + + @Override + public boolean isUnknown() { + return false; + } + + public boolean isVoid() { + return methodReference.getReturnType() == null; + } + + @Override + public KnownRetracedMethod asKnown() { + return this; + } + + @Override + public RetracedClass getHolderClass() { + return classReference; + } + + @Override + public String getMethodName() { + return methodReference.getMethodName(); + } + + public TypeReference getReturnType() { + assert !isVoid(); + return methodReference.getReturnType(); + } + + public List<TypeReference> getFormalTypes() { + return methodReference.getFormalTypes(); + } + + public MethodReference getClassReference() { + return methodReference; + } + + public boolean equalsMethodReference(MethodReference reference) { + return methodReference.equals(reference); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + KnownRetracedMethod that = (KnownRetracedMethod) o; + assert !methodReference.equals(that.methodReference) + || classReference.equals(that.classReference); + return methodReference.equals(that.methodReference); + } + + @Override + public int hashCode() { + return Objects.hash(methodReference, classReference); + } + } + + public static final class UnknownRetracedMethod extends RetracedMethod { + + private final RetracedClass classReference; + private final String name; + + private UnknownRetracedMethod(RetracedClass classReference, String name) { + this.classReference = classReference; + this.name = name; + } + + @Override + public RetracedClass getHolderClass() { + return classReference; + } + + @Override + public String getMethodName() { + return name; + } + } + + static RetracedMethod create(RetracedClass classReference, MethodReference methodReference) { + return new KnownRetracedMethod(classReference, methodReference); + } + + static RetracedMethod createUnknown(RetracedClass classReference, String name) { + return new UnknownRetracedMethod(classReference, name); + } +}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedType.java b/src/main/java/com/android/tools/r8/retrace/RetracedType.java new file mode 100644 index 0000000..016c8df --- /dev/null +++ b/src/main/java/com/android/tools/r8/retrace/RetracedType.java
@@ -0,0 +1,57 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.retrace; + +import com.android.tools.r8.Keep; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.references.TypeReference; +import java.util.Objects; + +@Keep +public final class RetracedType { + + private final TypeReference typeReference; + + private RetracedType(TypeReference typeReference) { + this.typeReference = typeReference; + } + + static RetracedType create(TypeReference typeReference) { + return new RetracedType(typeReference); + } + + public boolean isVoid() { + return typeReference == null; + } + + public TypeReference toArray(int dimensions) { + return Reference.array(typeReference, dimensions); + } + + public String getTypeName() { + assert !isVoid(); + return typeReference.getTypeName(); + } + + public TypeReference getTypeReference() { + return typeReference; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return typeReference.equals(((RetracedType) o).typeReference); + } + + @Override + public int hashCode() { + return Objects.hash(typeReference); + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java new file mode 100644 index 0000000..f259b01 --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java
@@ -0,0 +1,84 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.utils.StringUtils; +import java.util.List; + +class KeepRuleFormatter extends ResultFormatter { + final boolean allowObfuscation; + + KeepRuleFormatter( + StringConsumer output, DiagnosticsHandler diagnosticsHandler, boolean allowObfuscation) { + super(output, diagnosticsHandler); + this.allowObfuscation = allowObfuscation; + } + + @Override + protected void printTypeHeader(DexClass dexClass) { + append(allowObfuscation ? "-keep,allowobfuscation" : "-keep"); + if (dexClass.isInterface()) { + append(" interface " + dexClass.type.toSourceString() + " {" + System.lineSeparator()); + } else if (dexClass.accessFlags.isEnum()) { + append(" enum " + dexClass.type.toSourceString() + " {" + System.lineSeparator()); + } else { + append(" class " + dexClass.type.toSourceString() + " {" + System.lineSeparator()); + } + } + + @Override + protected void printConstructorName(DexEncodedMethod encodedMethod) { + append("<init>"); + } + + @Override + protected void printField(DexClass dexClass, DexField field) { + append( + " " + + field.type.toSourceString() + + " " + + field.name.toString() + + ";" + + System.lineSeparator()); + } + + @Override + protected void printMethod(DexEncodedMethod encodedMethod, String typeName) { + // Static initializers do not require keep rules - it is kept by keeping the class. + if (encodedMethod.accessFlags.isConstructor() && encodedMethod.accessFlags.isStatic()) { + return; + } + append(" "); + if (encodedMethod.isPublicMethod()) { + append("public "); + } else if (encodedMethod.isPrivateMethod()) { + append("private "); + } else if (encodedMethod.isProtectedMethod()) { + append("protected "); + } + if (encodedMethod.isStatic()) { + append("static "); + } + printNameAndReturn(encodedMethod); + printArguments(encodedMethod.method); + appendLine(";"); + } + + @Override + protected void printPackageNames(List<String> packageNames) { + if (!packageNames.isEmpty()) { + append("-keeppackagenames " + StringUtils.join(packageNames, ",") + System.lineSeparator()); + } + } + + @Override + protected void printTypeFooter() { + appendLine("}"); + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java new file mode 100644 index 0000000..12b2e6b --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
@@ -0,0 +1,60 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; +import java.util.List; + +class PrintUsesFormatter extends ResultFormatter { + + PrintUsesFormatter(StringConsumer output, DiagnosticsHandler diagnosticsHandler) { + super(output, diagnosticsHandler); + } + + @Override + protected void printConstructorName(DexEncodedMethod encodedMethod) { + if (encodedMethod.accessFlags.isStatic()) { + append("<clinit>"); + } else { + String holderName = encodedMethod.holder().toSourceString(); + String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1); + append(constructorName); + } + } + + @Override + protected void printMethod(DexEncodedMethod encodedMethod, String typeName) { + append(typeName + ": "); + printNameAndReturn(encodedMethod); + printArguments(encodedMethod.method); + appendLine(""); + } + + @Override + protected void printPackageNames(List<String> packageNames) { + // No need to print package names for text output. + } + + @Override + protected void printTypeHeader(DexClass dexClass) { + appendLine(dexClass.type.toSourceString()); + } + + @Override + protected void printTypeFooter() {} + + @Override + protected void printField(DexClass dexClass, DexField field) { + appendLine( + dexClass.type.toSourceString() + + ": " + + field.type.toSourceString() + + " " + + field.name.toString()); + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Result.java b/src/main/java/com/android/tools/r8/tracereferences/Result.java new file mode 100644 index 0000000..6889f0c --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/Result.java
@@ -0,0 +1,36 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import com.android.tools.r8.graph.DexApplication; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.DexType; +import java.util.Map; +import java.util.Set; + +class Result { + final DexApplication application; + final Set<DexType> types; + final Set<String> keepPackageNames; + final Map<DexType, Set<DexField>> fields; + final Map<DexType, Set<DexMethod>> methods; + final Set<DexReference> missingDefinition; + + Result( + DexApplication application, + Set<DexType> types, + Set<String> keepPackageNames, + Map<DexType, Set<DexField>> fields, + Map<DexType, Set<DexMethod>> methods, + Set<DexReference> missingDefinition) { + this.application = application; + this.types = types; + this.keepPackageNames = keepPackageNames; + this.fields = fields; + this.methods = methods; + this.missingDefinition = missingDefinition; + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java new file mode 100644 index 0000000..2fd8934 --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java
@@ -0,0 +1,142 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.graph.DexApplication; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.DexType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +abstract class ResultFormatter { + + private final StringConsumer output; + private final DiagnosticsHandler diagnosticsHandler; + + ResultFormatter(StringConsumer output, DiagnosticsHandler diagnosticsHandler) { + this.output = output; + this.diagnosticsHandler = diagnosticsHandler; + } + + protected void append(String string) { + output.accept(string, diagnosticsHandler); + } + + protected void appendLine(String string) { + output.accept(string + System.lineSeparator(), diagnosticsHandler); + } + + protected void printArguments(DexMethod method) { + append("("); + for (int i = 0; i < method.getArity(); i++) { + if (i != 0) { + append(","); + } + append(method.proto.parameters.values[i].toSourceString()); + } + append(")"); + } + + protected abstract void printConstructorName(DexEncodedMethod encodedMethod); + + private void printError(String message) { + appendLine("# Error: " + message); + } + + protected abstract void printField(DexClass dexClass, DexField field); + + protected abstract void printMethod(DexEncodedMethod encodedMethod, String typeName); + + protected abstract void printPackageNames(List<String> packageNames); + + protected void printNameAndReturn(DexEncodedMethod encodedMethod) { + if (encodedMethod.accessFlags.isConstructor()) { + printConstructorName(encodedMethod); + } else { + DexMethod method = encodedMethod.method; + append(method.proto.returnType.toSourceString()); + append(" "); + append(method.name.toSourceString()); + } + } + + protected abstract void printTypeHeader(DexClass dexClass); + + protected abstract void printTypeFooter(); + + void format(Result result) { + int errors = + print( + result.application, + result.types, + result.keepPackageNames, + result.fields, + result.methods, + result.missingDefinition); + output.finished(diagnosticsHandler); + assert errors == result.missingDefinition.size(); + } + + private int print( + DexApplication application, + Set<DexType> types, + Set<String> keepPackageNames, + Map<DexType, Set<DexField>> fields, + Map<DexType, Set<DexMethod>> methods, + Set<DexReference> missingDefinition) { + int errors = 0; + List<DexType> sortedTypes = new ArrayList<>(types); + sortedTypes.sort(Comparator.comparing(DexType::toSourceString)); + for (DexType type : sortedTypes) { + DexClass dexClass = application.definitionFor(type); + if (missingDefinition.contains(type)) { + assert dexClass == null; + printError("Could not find definition for type " + type.toSourceString()); + errors++; + continue; + } + printTypeHeader(dexClass); + List<DexEncodedMethod> methodDefinitions = new ArrayList<>(methods.size()); + for (DexMethod method : methods.get(type)) { + DexEncodedMethod encodedMethod = dexClass.lookupMethod(method); + if (missingDefinition.contains(method)) { + assert encodedMethod == null; + printError("Could not find definition for method " + method.toSourceString()); + errors++; + continue; + } + methodDefinitions.add(encodedMethod); + } + methodDefinitions.sort(Comparator.comparing(x -> x.method.name.toSourceString())); + for (DexEncodedMethod encodedMethod : methodDefinitions) { + printMethod(encodedMethod, dexClass.type.toSourceString()); + } + List<DexField> sortedFields = new ArrayList<>(fields.get(type)); + sortedFields.sort(Comparator.comparing(DexField::toSourceString)); + for (DexField field : sortedFields) { + if (missingDefinition.contains(field)) { + printError("Could not find definition for field " + field.toSourceString()); + errors++; + continue; + } + printField(dexClass, field); + } + printTypeFooter(); + } + ArrayList<String> packageNamesToKeep = new ArrayList<>(keepPackageNames); + Collections.sort(packageNamesToKeep); + printPackageNames(packageNamesToKeep); + return errors; + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java new file mode 100644 index 0000000..4808947 --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -0,0 +1,101 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import static com.android.tools.r8.utils.ExceptionUtils.STATUS_ERROR; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.Keep; +import com.android.tools.r8.ProgramResource; +import com.android.tools.r8.ProgramResourceProvider; +import com.android.tools.r8.Version; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ExceptionDiagnostic; +import java.util.HashSet; +import java.util.Set; + +@Keep +public class TraceReferences { + + public static void run(TraceReferencesCommand command) throws CompilationFailedException { + try { + runInternal(command); + } catch (TraceReferencesAbortException e) { + throw new CompilationFailedException(); + } catch (Exception e) { + command.getDiagnosticsHandler().error(new ExceptionDiagnostic(e)); + throw new CompilationFailedException(); + } + } + + private static void runInternal(TraceReferencesCommand command) throws Exception { + if (command.getLibrary().isEmpty()) { + throw new TraceReferencesException("No library specified"); + } + if (command.getTarget().isEmpty()) { + throw new TraceReferencesException("No target specified"); + } + if (command.getSource().isEmpty()) { + throw new TraceReferencesException("No source specified"); + } + if (command.getOutput() == null) { + throw new TraceReferencesException("No output specified"); + } + AndroidApp.Builder builder = AndroidApp.builder(); + command.getLibrary().forEach(builder::addLibraryResourceProvider); + command.getTarget().forEach(builder::addLibraryResourceProvider); + command.getSource().forEach(builder::addProgramResourceProvider); + Set<String> tagetDescriptors = new HashSet<>(); + command + .getTarget() + .forEach(provider -> tagetDescriptors.addAll(provider.getClassDescriptors())); + for (ProgramResourceProvider provider : command.getSource()) { + for (ProgramResource programResource : provider.getProgramResources()) { + tagetDescriptors.removeAll(programResource.getClassDescriptors()); + } + } + Tracer tracer = new Tracer(tagetDescriptors, builder.build()); + Result result = tracer.run(); + ResultFormatter formatter; + switch (command.getOutputFormat()) { + case PRINTUSAGE: + formatter = new PrintUsesFormatter(command.getOutput(), command.getDiagnosticsHandler()); + break; + case KEEP_RULES: + formatter = + new KeepRuleFormatter(command.getOutput(), command.getDiagnosticsHandler(), false); + break; + case KEEP_RULES_WITH_ALLOWOBFUSCATION: + formatter = + new KeepRuleFormatter(command.getOutput(), command.getDiagnosticsHandler(), true); + break; + default: + throw new TraceReferencesException("Unexpected format " + command.getOutputFormat().name()); + } + formatter.format(result); + } + + public static void main(String... args) { + try { + TraceReferencesCommand command = TraceReferencesCommand.parse(args, Origin.root()).build(); + if (command.isPrintHelp()) { + System.out.println(TraceReferencesCommandParser.USAGE_MESSAGE); + return; + } + if (command.isPrintVersion()) { + System.out.println("referencetrace " + Version.getVersionString()); + return; + } + run(command); + } catch (CompilationFailedException e) { + System.exit(STATUS_ERROR); + } catch (Throwable e) { + System.err.println("ReferenceTrace failed with an internal error."); + Throwable cause = e.getCause() == null ? e : e.getCause(); + cause.printStackTrace(); + System.exit(STATUS_ERROR); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesAbortException.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesAbortException.java new file mode 100644 index 0000000..8afab19 --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesAbortException.java
@@ -0,0 +1,6 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +public class TraceReferencesAbortException extends RuntimeException {}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java new file mode 100644 index 0000000..f6fcb69 --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
@@ -0,0 +1,254 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import static com.android.tools.r8.utils.FileUtils.isArchive; + +import com.android.tools.r8.ArchiveClassFileProvider; +import com.android.tools.r8.ClassFileResourceProvider; +import com.android.tools.r8.Diagnostic; +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.Keep; +import com.android.tools.r8.ProgramResourceProvider; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.origin.PathOrigin; +import com.android.tools.r8.tracereferences.TraceReferencesCommandParser.OutputFormat; +import com.android.tools.r8.utils.ArchiveResourceProvider; +import com.android.tools.r8.utils.ExceptionDiagnostic; +import com.android.tools.r8.utils.StringDiagnostic; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +@Keep +public class TraceReferencesCommand { + private final boolean printHelp; + private final boolean printVersion; + private final DiagnosticsHandler diagnosticsHandler; + private final ImmutableList<ClassFileResourceProvider> library; + private final ImmutableList<ClassFileResourceProvider> traceTarget; + private final ImmutableList<ProgramResourceProvider> traceSource; + private final StringConsumer output; + private final OutputFormat outputFormat; + + TraceReferencesCommand( + boolean printHelp, + boolean printVersion, + DiagnosticsHandler diagnosticsHandler, + ImmutableList<ClassFileResourceProvider> library, + ImmutableList<ClassFileResourceProvider> traceTarget, + ImmutableList<ProgramResourceProvider> traceSource, + StringConsumer output, + OutputFormat outputFormat) { + this.printHelp = printHelp; + this.printVersion = printVersion; + this.diagnosticsHandler = diagnosticsHandler; + this.library = library; + this.traceTarget = traceTarget; + this.traceSource = traceSource; + this.output = output; + this.outputFormat = outputFormat; + } + + /** + * Utility method for obtaining a <code>ReferenceTraceCommand.Builder</code>. + * + * @param diagnosticsHandler The diagnostics handler for consuming messages. + */ + public static Builder builder(DiagnosticsHandler diagnosticsHandler) { + return new Builder(diagnosticsHandler); + } + + /** + * Utility method for obtaining a <code>ReferenceTraceCommand.Builder</code> with a default + * diagnostics handler. + */ + public static Builder builder() { + return new Builder(new DiagnosticsHandler() {}); + } + + public static Builder parse(String[] args, Origin origin) { + return TraceReferencesCommandParser.parse(args, origin); + } + + public static Builder parse(String[] args, Origin origin, DiagnosticsHandler diagnosticsHandler) { + return TraceReferencesCommandParser.parse(args, origin, diagnosticsHandler); + } + + public boolean isPrintHelp() { + return printHelp; + } + + public boolean isPrintVersion() { + return printVersion; + } + + public static class Builder { + + private boolean printHelp = false; + private boolean printVersion = false; + private final DiagnosticsHandler diagnosticsHandler; + private final ImmutableList.Builder<ClassFileResourceProvider> libraryBuilder = + ImmutableList.builder(); + private final ImmutableList.Builder<ClassFileResourceProvider> traceTargetBuilder = + ImmutableList.builder(); + private final ImmutableList.Builder<ProgramResourceProvider> traceSourceBuilder = + ImmutableList.builder(); + private StringConsumer output; + private OutputFormat outputFormat = TraceReferencesCommandParser.OutputFormat.PRINTUSAGE; + + private Builder(DiagnosticsHandler diagnosticsHandler) { + this.diagnosticsHandler = diagnosticsHandler; + } + + public Builder setPrintHelp(boolean printHelp) { + this.printHelp = printHelp; + return this; + } + + /** True if the print-version flag is enabled. */ + public boolean isPrintVersion() { + return printVersion; + } + + /** Set the value of the print-version flag. */ + public Builder setPrintVersion(boolean printVersion) { + this.printVersion = printVersion; + return this; + } + + private void addLibraryOrTargetFile( + Path file, ImmutableList.Builder<ClassFileResourceProvider> builder) { + if (!Files.exists(file)) { + PathOrigin pathOrigin = new PathOrigin(file); + NoSuchFileException noSuchFileException = new NoSuchFileException(file.toString()); + error(new ExceptionDiagnostic(noSuchFileException, pathOrigin)); + } + if (isArchive(file)) { + try { + ArchiveClassFileProvider provider = new ArchiveClassFileProvider(file); + builder.add(provider); + } catch (IOException e) { + error(new ExceptionDiagnostic(e, new PathOrigin(file))); + } + } else { + error(new StringDiagnostic("Unsupported source file type", new PathOrigin(file))); + } + } + + private void addSourceFile(Path file) { + if (!Files.exists(file)) { + PathOrigin pathOrigin = new PathOrigin(file); + NoSuchFileException noSuchFileException = new NoSuchFileException(file.toString()); + error(new ExceptionDiagnostic(noSuchFileException, pathOrigin)); + } + if (isArchive(file)) { + traceSourceBuilder.add(ArchiveResourceProvider.fromArchive(file, false)); + } else { + error(new StringDiagnostic("Unsupported source file type", new PathOrigin(file))); + } + } + + public Builder addLibraryResourceProvider(ClassFileResourceProvider provider) { + libraryBuilder.add(provider); + return this; + } + + public Builder addLibraryFiles(Path... files) { + addLibraryFiles(Arrays.asList(files)); + return this; + } + + public Builder addLibraryFiles(Collection<Path> files) { + for (Path file : files) { + addLibraryOrTargetFile(file, libraryBuilder); + } + return this; + } + + public Builder addTargetFiles(Path... files) { + addTargetFiles(Arrays.asList(files)); + return this; + } + + public Builder addTargetFiles(Collection<Path> files) { + for (Path file : files) { + addLibraryOrTargetFile(file, traceTargetBuilder); + } + return this; + } + + public Builder addSourceFiles(Path... files) { + addSourceFiles(Arrays.asList(files)); + return this; + } + + public Builder addSourceFiles(Collection<Path> files) { + for (Path file : files) { + addSourceFile(file); + } + return this; + } + + Builder setOutputPath(Path output) { + this.output = new StringConsumer.FileConsumer(output); + return this; + } + + Builder setOutputFormat(OutputFormat outputFormat) { + this.outputFormat = outputFormat; + return this; + } + + public final TraceReferencesCommand build() { + ImmutableList<ClassFileResourceProvider> traceTarget = traceTargetBuilder.build(); + ImmutableList<ProgramResourceProvider> traceSource = traceSourceBuilder.build(); + return new TraceReferencesCommand( + printHelp, + printVersion, + diagnosticsHandler, + libraryBuilder.build(), + traceTarget, + traceSource, + output, + outputFormat); + } + + void error(Diagnostic diagnostic) { + diagnosticsHandler.error(diagnostic); + // For now all errors are fatal. + throw new TraceReferencesAbortException(); + } + } + + DiagnosticsHandler getDiagnosticsHandler() { + return diagnosticsHandler; + } + + List<ClassFileResourceProvider> getLibrary() { + return library; + } + + List<ClassFileResourceProvider> getTarget() { + return traceTarget; + } + + List<ProgramResourceProvider> getSource() { + return traceSource; + } + + StringConsumer getOutput() { + return output; + } + + OutputFormat getOutputFormat() { + return outputFormat; + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java new file mode 100644 index 0000000..79de8e2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
@@ -0,0 +1,166 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.JdkClassFileProvider; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.ExceptionDiagnostic; +import com.android.tools.r8.utils.FlagFile; +import com.android.tools.r8.utils.StringDiagnostic; +import com.google.common.collect.ImmutableSet; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Set; + +class TraceReferencesCommandParser { + + enum OutputFormat { + /** Format used with the -printusage flag */ + PRINTUSAGE, + /** Keep rules keeping each of the traced references */ + KEEP_RULES, + /** + * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references + */ + KEEP_RULES_WITH_ALLOWOBFUSCATION + } + + private static final Set<String> OPTIONS_WITH_PARAMETER = + ImmutableSet.of("--lib", "--target", "--source", "--format", "--output"); + + static final String USAGE_MESSAGE = + String.join( + "\n", + Arrays.asList( + "Usage: referencetrace [options] [@<argfile>]", + " Each <argfile> is a file containing additional arguments (one per line)", + " and options are:", + " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.", + " --target <file> # Add <file> as a classpath resource.", + " --source <file> # Add <file> as a classpath resource.", + " --output <file> # Output result in <outfile>.", + " --version # Print the version of referencetrace.", + " --help # Print this message.")); + /** + * Parse the referencetrace command-line. + * + * <p>Parsing will set the supplied options or their default value if they have any. + * + * @param args Command-line arguments array. + * @param origin Origin description of the command-line arguments. + * @return referencetrace command builder with state set up according to parsed command line. + */ + static TraceReferencesCommand.Builder parse(String[] args, Origin origin) { + return new TraceReferencesCommandParser().parse(args, origin, TraceReferencesCommand.builder()); + } + + /** + * Parse the referencetrace command-line. + * + * <p>Parsing will set the supplied options or their default value if they have any. + * + * @param args Command-line arguments array. + * @param origin Origin description of the command-line arguments. + * @param handler Custom defined diagnostics handler. + * @return referencetrace command builder with state set up according to parsed command line. + */ + static TraceReferencesCommand.Builder parse( + String[] args, Origin origin, DiagnosticsHandler handler) { + return new TraceReferencesCommandParser() + .parse(args, origin, TraceReferencesCommand.builder(handler)); + } + + private TraceReferencesCommand.Builder parse( + String[] args, Origin origin, TraceReferencesCommand.Builder builder) { + String[] expandedArgs = FlagFile.expandFlagFiles(args, builder::error); + for (int i = 0; i < expandedArgs.length; i++) { + String arg = expandedArgs[i].trim(); + String nextArg = null; + if (OPTIONS_WITH_PARAMETER.contains(arg)) { + if (++i < expandedArgs.length) { + nextArg = expandedArgs[i]; + } else { + builder.error( + new StringDiagnostic("Missing parameter for " + expandedArgs[i - 1] + ".", origin)); + break; + } + } + if (arg.length() == 0) { + continue; + } else if (arg.equals("--help")) { + builder.setPrintHelp(true); + } else if (arg.equals("--version")) { + builder.setPrintVersion(true); + } else if (arg.equals("--lib")) { + addLibraryArgument(builder, origin, nextArg); + } else if (arg.equals("--target")) { + builder.addTargetFiles(Paths.get(nextArg)); + } else if (arg.equals("--source")) { + builder.addSourceFiles(Paths.get(nextArg)); + } else if (arg.equals("--format")) { + OutputFormat format = null; + if (nextArg.equals("printuses")) { + format = OutputFormat.PRINTUSAGE; + } + if (nextArg.equals("keep")) { + format = OutputFormat.KEEP_RULES; + } + if (nextArg.equals("keepallowobfuscation")) { + format = OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION; + } + if (format == null) { + builder.error(new StringDiagnostic("Unsupported format '" + nextArg + "'")); + } + builder.setOutputFormat(format); + } else if (arg.equals("--output")) { + builder.setOutputPath(Paths.get(nextArg)); + } else if (arg.startsWith("@")) { + builder.error(new StringDiagnostic("Recursive @argfiles are not supported: ", origin)); + } else { + builder.error(new StringDiagnostic("Unsupported argument '" + arg + "'")); + } + } + return builder; + } + + /** + * This method must match the lookup in {@link + * com.android.tools.r8.JdkClassFileProvider#fromJdkHome}. + */ + private static boolean isJdkHome(Path home) { + Path jrtFsJar = home.resolve("lib").resolve("jrt-fs.jar"); + if (Files.exists(jrtFsJar)) { + return true; + } + // JDK has rt.jar in jre/lib/rt.jar. + Path rtJar = home.resolve("jre").resolve("lib").resolve("rt.jar"); + if (Files.exists(rtJar)) { + return true; + } + // JRE has rt.jar in lib/rt.jar. + rtJar = home.resolve("lib").resolve("rt.jar"); + if (Files.exists(rtJar)) { + return true; + } + return false; + } + + static void addLibraryArgument( + TraceReferencesCommand.Builder builder, Origin origin, String arg) { + Path path = Paths.get(arg); + if (isJdkHome(path)) { + try { + builder.addLibraryResourceProvider(JdkClassFileProvider.fromJdkHome(path)); + } catch (IOException e) { + builder.error(new ExceptionDiagnostic(e, origin)); + } + } else { + builder.addLibraryFiles(path); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesException.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesException.java new file mode 100644 index 0000000..57fcc4e --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesException.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +class TraceReferencesException extends Exception { + TraceReferencesException(String message) { + super(message); + } +}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java new file mode 100644 index 0000000..5ad1cfa --- /dev/null +++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -0,0 +1,291 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; + +import com.android.tools.r8.dex.ApplicationReader; +import com.android.tools.r8.features.ClassToFeatureSplitMap; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.DexAnnotation; +import com.android.tools.r8.graph.DexCallSite; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedField; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; +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; +import com.android.tools.r8.graph.DexValue; +import com.android.tools.r8.graph.DexValue.DexValueArray; +import com.android.tools.r8.graph.DirectMappedDexApplication; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.ResolutionResult; +import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.ir.desugar.LambdaDescriptor; +import com.android.tools.r8.shaking.MainDexClasses; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.Timing; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.List; +import java.util.Map; +import java.util.Set; + +class Tracer { + + private final Set<String> descriptors; + private Set<DexType> types = Sets.newIdentityHashSet(); + private Map<DexType, Set<DexMethod>> methods = Maps.newIdentityHashMap(); + private Map<DexType, Set<DexField>> fields = Maps.newIdentityHashMap(); + private Set<String> keepPackageNames = Sets.newHashSet(); + private Set<DexReference> missingDefinitions = Sets.newHashSet(); + private final DirectMappedDexApplication application; + private final AppInfoWithClassHierarchy appInfo; + + Tracer(Set<String> descriptors, AndroidApp inputApp) throws Exception { + this.descriptors = descriptors; + InternalOptions options = new InternalOptions(); + application = + new ApplicationReader(inputApp, options, new Timing("ReferenceTrace")).read().toDirect(); + appInfo = + AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy( + application, + ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(), + MainDexClasses.createEmptyMainDexClasses()); + } + + Result run() { + UseCollector useCollector = new UseCollector(appInfo.dexItemFactory()); + for (DexProgramClass clazz : application.classes()) { + useCollector.setContext(clazz); + useCollector.registerSuperType(clazz, clazz.superType); + for (DexType implementsType : clazz.interfaces.values) { + useCollector.registerSuperType(clazz, implementsType); + } + clazz.forEachProgramMethod(useCollector::registerMethod); + clazz.forEachField(useCollector::registerField); + } + + return new Result(application, types, keepPackageNames, fields, methods, missingDefinitions); + } + + private boolean isTargetType(DexType type) { + return descriptors.contains(type.toDescriptorString()); + } + + private void addType(DexType type) { + if (isTargetType(type) && types.add(type)) { + DexClass clazz = appInfo.definitionFor(type); + if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) { + keepPackageNames.add(clazz.type.getPackageName()); + } + methods.put(type, Sets.newIdentityHashSet()); + fields.put(type, Sets.newIdentityHashSet()); + } + } + + private void addField(DexField field) { + addType(field.type); + DexEncodedField baseField = appInfo.resolveField(field).getResolvedField(); + if (baseField != null && baseField.holder() != field.holder) { + field = baseField.field; + } + addType(field.holder); + if (isTargetType(field.holder)) { + Set<DexField> typeFields = fields.get(field.holder); + assert typeFields != null; + if (baseField != null) { + if (baseField.accessFlags.isVisibilityDependingOnPackage()) { + keepPackageNames.add(baseField.holder().getPackageName()); + } + } else { + missingDefinitions.add(field); + } + typeFields.add(field); + } + } + + private void addMethod(DexMethod method) { + addType(method.holder); + for (DexType parameterType : method.proto.parameters.values) { + addType(parameterType); + } + addType(method.proto.returnType); + if (isTargetType(method.holder)) { + Set<DexMethod> typeMethods = methods.get(method.holder); + assert typeMethods != null; + DexClass holder = appInfo.definitionForHolder(method); + DexEncodedMethod definition = method.lookupOnClass(holder); + if (definition != null) { + if (definition.accessFlags.isVisibilityDependingOnPackage()) { + keepPackageNames.add(definition.holder().getPackageName()); + } + } else { + missingDefinitions.add(method); + } + typeMethods.add(method); + } + } + + class UseCollector extends UseRegistry { + + private DexProgramClass context; + + UseCollector(DexItemFactory factory) { + super(factory); + } + + public void setContext(DexProgramClass context) { + this.context = context; + } + + @Override + public void registerInitClass(DexType clazz) { + addType(clazz); + } + + @Override + public void registerInvokeVirtual(DexMethod method) { + ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method); + DexEncodedMethod target = + resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null; + if (target != null && target.method != method) { + addType(method.holder); + addMethod(target.method); + } else { + addMethod(method); + } + } + + @Override + public void registerInvokeDirect(DexMethod method) { + addMethod(method); + } + + @Override + public void registerInvokeStatic(DexMethod method) { + DexEncodedMethod target = appInfo.unsafeResolveMethodDueToDexFormat(method).getSingleTarget(); + if (target != null && target.method != method) { + addType(method.holder); + addMethod(target.method); + } else { + addMethod(method); + } + } + + @Override + public void registerInvokeInterface(DexMethod method) { + registerInvokeVirtual(method); + } + + @Override + public void registerInvokeSuper(DexMethod method) { + DexEncodedMethod superTarget = appInfo.lookupSuperTarget(method, context); + if (superTarget != null) { + addMethod(superTarget.method); + } else { + addMethod(method); + } + } + + @Override + public void registerInstanceFieldWrite(DexField field) { + addField(field); + } + + @Override + public void registerInstanceFieldRead(DexField field) { + addField(field); + } + + @Override + public void registerNewInstance(DexType type) { + addType(type); + } + + @Override + public void registerStaticFieldRead(DexField field) { + addField(field); + } + + @Override + public void registerStaticFieldWrite(DexField field) { + addField(field); + } + + @Override + public void registerTypeReference(DexType type) { + addType(type); + } + + @Override + public void registerInstanceOf(DexType type) { + addType(type); + } + + private void registerField(DexEncodedField field) { + registerTypeReference(field.field.type); + } + + private void registerMethod(ProgramMethod method) { + DexEncodedMethod superTarget = + appInfo + .resolveMethodOn(method.getHolder(), method.getReference()) + .lookupInvokeSpecialTarget(context, appInfo); + if (superTarget != null) { + addMethod(superTarget.method); + } + for (DexType type : method.getDefinition().parameters().values) { + registerTypeReference(type); + } + for (DexAnnotation annotation : method.getDefinition().annotations().annotations) { + if (annotation.annotation.type == appInfo.dexItemFactory().annotationThrows) { + DexValueArray dexValues = annotation.annotation.elements[0].value.asDexValueArray(); + for (DexValue dexValType : dexValues.getValues()) { + registerTypeReference(dexValType.asDexValueType().value); + } + } + } + registerTypeReference(method.getDefinition().returnType()); + method.registerCodeReferences(this); + } + + private void registerSuperType(DexProgramClass clazz, DexType superType) { + registerTypeReference(superType); + // If clazz overrides any methods in superType, we should keep those as well. + clazz.forEachMethod( + method -> { + ResolutionResult resolutionResult = + appInfo.resolveMethodOn(superType, method.method, superType != clazz.superType); + DexEncodedMethod dexEncodedMethod = resolutionResult.getSingleTarget(); + if (dexEncodedMethod != null) { + addMethod(dexEncodedMethod.method); + } + }); + } + + @Override + public void registerCallSite(DexCallSite callSite) { + super.registerCallSite(callSite); + + // For Lambda's, in order to find the correct use, we need to register the method for the + // functional interface. + List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo); + if (directInterfaces != null) { + for (DexType directInterface : directInterfaces) { + DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(directInterface)); + if (clazz != null) { + clazz.forEachProgramVirtualMethodMatching( + definition -> definition.getReference().name.equals(callSite.methodName), + this::registerMethod); + } + } + } + } + } +}
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 cdd3ca3..a7481b7 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -186,6 +186,7 @@ enableClassStaticizer = false; enableDevirtualization = false; enableLambdaMerging = false; + enableHorizontalClassMerging = false; enableStaticClassMerging = false; enableVerticalClassMerging = false; enableEnumUnboxing = false; @@ -220,7 +221,8 @@ public boolean enableFieldBitAccessAnalysis = System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null; public boolean enableStaticClassMerging = true; - public boolean enableHorizontalClassMerging = false; + public boolean enableHorizontalClassMerging = + System.getProperty("com.android.tools.r8.horizontalClassMerging") != null; public boolean enableVerticalClassMerging = true; public boolean enableArgumentRemoval = true; public boolean enableUnusedInterfaceRemoval = true;
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java index 71400bd..f75d170 100644 --- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -24,12 +24,17 @@ 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.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -75,14 +80,26 @@ } public static void zip(Path zipFile, Path inputDirectory) throws IOException { + List<Path> files = + Files.walk(inputDirectory) + .filter(path -> !Files.isDirectory(path)) + .collect(Collectors.toList()); + zip(zipFile, inputDirectory, files); + } + + public static void zip(Path zipFile, Path basePath, Collection<Path> filesToZip) + throws IOException { try (ZipOutputStream stream = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zipFile)))) { - List<Path> files = - Files.walk(inputDirectory) - .filter(path -> !Files.isDirectory(path)) - .collect(Collectors.toList()); - for (Path path : files) { - ZipEntry zipEntry = new ZipEntry(inputDirectory.relativize(path).toString()); + for (Path path : filesToZip) { + ZipEntry zipEntry = + new ZipEntry( + StreamSupport.stream( + Spliterators.spliteratorUnknownSize( + basePath.relativize(path).iterator(), Spliterator.ORDERED), + false) + .map(Path::toString) + .collect(Collectors.joining("/"))); stream.putNextEntry(zipEntry); Files.copy(path, stream); stream.closeEntry(); @@ -90,6 +107,10 @@ } } + public static void zip(Path zipFile, Path basePath, Path... filesToZip) throws IOException { + zip(zipFile, basePath, Arrays.asList(filesToZip)); + } + public static List<File> unzip(String zipFile, File outDirectory) throws IOException { return unzip(zipFile, outDirectory, (entry) -> true); }
diff --git a/src/main/keep-applymapping.txt b/src/main/keep-applymapping.txt index 6ce5283..c404d21 100644 --- a/src/main/keep-applymapping.txt +++ b/src/main/keep-applymapping.txt
@@ -19,6 +19,3 @@ -keep class com.android.tools.r8.joptsimple.OptionDescriptor { java.lang.String argumentDescription(); } - -# We should support reserved names and compute the set based on the tests. --keep class com.android.tools.r8.shaking.** { *; } \ No newline at end of file
diff --git a/src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NestHost.java b/src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NestHost.java new file mode 100644 index 0000000..5c1453e --- /dev/null +++ b/src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NestHost.java
@@ -0,0 +1,11 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091.examples; + +public class NestHost { + /*private*/ int f; + + public static class NestMember extends NestHost {} +}
diff --git a/src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NonNestMember.java b/src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NonNestMember.java new file mode 100644 index 0000000..02fd5e7 --- /dev/null +++ b/src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NonNestMember.java
@@ -0,0 +1,7 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091.examples; + +public class NonNestMember extends NestHost.NestMember {}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java index 6154959..f5c75aa 100644 --- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java +++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1609,11 +1609,6 @@ return builder; } - protected void runArtTest(CompilerUnderTest compilerUnderTest) throws Throwable { - // Use the default dex VM specified. - runArtTest(ToolHelper.getDexVm(), compilerUnderTest); - } - private static class CompilationOptions implements Consumer<InternalOptions> { private final boolean disableInlining;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java index bb8cb6e..b76e08f 100644 --- a/src/test/java/com/android/tools/r8/TestBase.java +++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -337,6 +337,11 @@ return ClassFileTransformer.create(clazz); } + public static ClassFileTransformer transformer(Path path, ClassReference classReference) + throws IOException { + return ClassFileTransformer.create(Files.readAllBytes(path), classReference); + } + public static ClassFileTransformer transformer(byte[] bytes, ClassReference classReference) { return ClassFileTransformer.create(bytes, classReference); }
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java index 06fd76e..20ea852 100644 --- a/src/test/java/com/android/tools/r8/TestParameters.java +++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -29,7 +29,9 @@ public boolean canUseDefaultAndStaticInterfaceMethods() { assert isCfRuntime() || isDexRuntime(); - return isCfRuntime() || getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N); + return isCfRuntime() + || getApiLevel() + .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport()); } // Convenience predicates.
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java new file mode 100644 index 0000000..3d13626 --- /dev/null +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -0,0 +1,115 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.classmerging.horizontal; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static com.android.tools.r8.utils.codeinspector.Matchers.readsInstanceField; +import static com.android.tools.r8.utils.codeinspector.Matchers.writesInstanceField; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNot.not; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.horizontalclassmerging.ClassMerger; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; + +public class ConstructorMergingTrivialOverlapTest extends HorizontalClassMergingTestBase { + + public ConstructorMergingTrivialOverlapTest( + TestParameters parameters, boolean enableHorizontalClassMerging) { + super(parameters, enableHorizontalClassMerging); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addOptionsModification( + options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + .enableInliningAnnotations() + .enableNeverClassInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("7", "42", "13", "print a", "print b") + .inspect( + codeInspector -> { + if (enableHorizontalClassMerging) { + ClassSubject aClassSubject = codeInspector.clazz(A.class); + assertThat(aClassSubject, isPresent()); + FieldSubject classIdFieldSubject = + aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME); + assertThat(classIdFieldSubject, isPresent()); + + MethodSubject firstInitSubject = aClassSubject.init("int"); + assertThat(firstInitSubject, isPresent()); + assertThat( + firstInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference())); + + ClassSubject synthesizedClass = getSynthesizedArgumentClassSubject(codeInspector); + + MethodSubject otherInitSubject = + aClassSubject.init("int", synthesizedClass.getFinalName()); + assertThat(otherInitSubject, isPresent()); + assertThat( + otherInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference())); + + MethodSubject printSubject = aClassSubject.method("void", "print"); + assertThat(printSubject, isPresent()); + assertThat( + printSubject, readsInstanceField(classIdFieldSubject.getFieldReference())); + + assertThat(codeInspector.clazz(B.class), not(isPresent())); + + // TODO(b/165517236): Explicitly check classes have been merged. + } else { + assertThat(codeInspector.clazz(A.class), isPresent()); + assertThat(codeInspector.clazz(B.class), isPresent()); + } + }); + } + + @NeverClassInline + public static class A { + public A() { + System.out.println(7); + } + + @NeverInline + public void print() { + System.out.println("print a"); + } + } + + @NeverClassInline + public static class B { + public B() { + this(42); + } + + public B(int x) { + System.out.println(x); + } + + @NeverInline + public void print() { + System.out.println("print b"); + } + } + + public static class Main { + public static void main(String[] args) { + A a = new A(); + B b = new B(); + b = new B(13); + a.print(); + b.print(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAnnotationsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java similarity index 63% rename from src/test/java/com/android/tools/r8/classmerging/horizontal/NoAnnotationsTest.java rename to src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java index b7fc7a1..d17fa75 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAnnotationsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
@@ -11,14 +11,16 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestParameters; import com.android.tools.r8.shaking.ProguardKeepAttributes; +import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.junit.Test; -public class NoAnnotationsTest extends HorizontalClassMergingTestBase { - public NoAnnotationsTest(TestParameters parameters, boolean enableHorizontalClassMerging) { +public class NoClassesOrMembersWithAnnotationsTest extends HorizontalClassMergingTestBase { + public NoClassesOrMembersWithAnnotationsTest( + TestParameters parameters, boolean enableHorizontalClassMerging) { super(parameters, enableHorizontalClassMerging); } @@ -27,7 +29,6 @@ testForR8(parameters.getBackend()) .addInnerClasses(getClass()) .addKeepMainRule(Main.class) - .addKeepClassRules(TypeAnnotation.class, MethodAnnotation.class) .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) .addOptionsModification( options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) @@ -35,9 +36,12 @@ .enableInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines("a", "b", "c", "foo") + .assertSuccessWithOutputLines( + "a", "b", "c", "foo", "null", "annotation 2", "annotation 1", "annotation 2") .inspect( codeInspector -> { + assertThat(codeInspector.clazz(TypeAnnotation.class), isPresent()); + assertThat(codeInspector.clazz(MethodAnnotation.class), isPresent()); assertThat(codeInspector.clazz(A.class), isPresent()); assertThat(codeInspector.clazz(B.class), isPresent()); assertThat(codeInspector.clazz(C.class), isPresent()); @@ -81,11 +85,37 @@ } static class Main { - public static void main(String[] args) { + @NeverInline + public static void foo(TypeAnnotation annotation) { + System.out.println(annotation); + } + + @NeverInline + public static void foo2(MethodAnnotation annotation) { + System.out.println(annotation.toString().replaceFirst(".*@.*", "annotation 2")); + } + + public static void main(String[] args) throws NoSuchMethodException { A a = new A(); B b = new B("b"); C c = new C("c"); c.foo(); + foo(null); + foo2( + new MethodAnnotation() { + @Override + public Class<? extends Annotation> annotationType() { + return null; + } + }); + System.out.println( + b.getClass().getAnnotations()[0].toString().replaceFirst(".*", "annotation 1")); + System.out.println( + c.getClass() + .getMethods()[0] + .getAnnotations()[0] + .toString() + .replaceFirst(".*", "annotation 2")); } } }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoInterfacesTest.java new file mode 100644 index 0000000..f286bfd --- /dev/null +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoInterfacesTest.java
@@ -0,0 +1,86 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.classmerging.horizontal; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestParameters; +import org.junit.Test; + +public class NoInterfacesTest extends HorizontalClassMergingTestBase { + public NoInterfacesTest(TestParameters parameters, boolean enableHorizontalClassMerging) { + super(parameters, enableHorizontalClassMerging); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addOptionsModification( + options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + .enableInliningAnnotations() + .enableNeverClassInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("bar", "foo y", "foo z") + .inspect( + codeInspector -> { + assertThat(codeInspector.clazz(I.class), isPresent()); + assertThat(codeInspector.clazz(X.class), isPresent()); + assertThat(codeInspector.clazz(Y.class), isPresent()); + assertThat(codeInspector.clazz(Z.class), isPresent()); + }); + } + + public interface I { + void foo(); + } + + @NeverClassInline + public static class X { + @NeverInline + public static void bar() { + System.out.println("bar"); + } + } + + @NeverClassInline + public static class Y implements I { + @NeverInline + @Override + public void foo() { + System.out.println("foo y"); + } + } + + @NeverClassInline + public static class Z implements I { + @NeverInline + @Override + public void foo() { + System.out.println("foo z"); + } + } + + public static class Main { + @NeverInline + public static void foo(I i) { + i.foo(); + } + + public static void main(String[] args) { + X x = new X(); + x.bar(); + Y y = new Y(); + Z z = new Z(); + foo(y); + foo(z); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java new file mode 100644 index 0000000..e40ff51 --- /dev/null +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
@@ -0,0 +1,77 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.classmerging.horizontal; + +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.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.classmerging.horizontal.ConstructorMergingOverlapTest.Main; +import org.junit.Test; + +public class VerticallyMergedClassTest extends HorizontalClassMergingTestBase { + public VerticallyMergedClassTest( + TestParameters parameters, boolean enableHorizontalClassMerging) { + super(parameters, enableHorizontalClassMerging); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addOptionsModification( + options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + .enableNoHorizontalClassMergingAnnotations() + .enableInliningAnnotations() + .enableNeverClassInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("b", "a", "c") + .inspect( + codeInspector -> { + assertThat(codeInspector.clazz(A.class), not(isPresent())); + assertThat(codeInspector.clazz(B.class), isPresent()); + assertThat(codeInspector.clazz(C.class), isPresent()); + }); + } + + public static class A { + public void print() { + System.out.println("a"); + } + } + + @NoHorizontalClassMerging + @NeverClassInline + public static class B extends A { + public B() { + System.out.println("b"); + } + } + + @NeverClassInline + public static class C { + public C() { + System.out.println("c"); + } + } + + public static class Main { + @NeverInline + static void printA(A a) { + a.print(); + } + + public static void main(String[] args) { + printA(new B()); + new C(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java new file mode 100644 index 0000000..9a7802f --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
@@ -0,0 +1,168 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.desugar; + +import com.android.tools.r8.DesugarTestConfiguration; +import com.android.tools.r8.R8TestRunResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter; +import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.concurrent.Callable; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class DesugarInnerClassesInInterfaces extends TestBase { + + private List<String> EXPECTED_RESULT_WITHOUT_DESUGARING = + ImmutableList.of( + WithAnonymousInner.class.getName(), "true", WithLocalInner.class.getName(), "true"); + + private List<String> EXPECTED_RESULT_WITH_DESUGARING = + ImmutableList.of( + WithAnonymousInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX, + "true", + WithLocalInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX, + "true"); + + private List<String> EXPECTED_RESULT_WITH_DESUGARING_B168697955 = + ImmutableList.of( + WithAnonymousInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX, + "false", + WithLocalInner.class.getName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX, + "false"); + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); + } + + private final TestParameters parameters; + + public DesugarInnerClassesInInterfaces(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testDesugar() throws Exception { + testForDesugaring(parameters) + .addInnerClasses(DesugarInnerClassesInInterfaces.class) + .run(parameters.getRuntime(), TestClass.class) + .applyIf( + DesugarTestConfiguration::isNotDesugared, + r -> r.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING)) + .applyIf( + DesugarTestConfiguration::isDesugared, + r -> + r.assertSuccessWithOutputLines( + parameters + .getApiLevel() + .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport()) + ? EXPECTED_RESULT_WITHOUT_DESUGARING + : EXPECTED_RESULT_WITH_DESUGARING)); + } + + @Test + public void testR8() throws Exception { + R8TestRunResult result = + testForR8(parameters.getBackend()) + .addInnerClasses(DesugarInnerClassesInInterfaces.class) + .setMinApi(parameters.getApiLevel()) + .addKeepRules("-keep class * { *; }") + .addKeepAttributes("InnerClasses", "EnclosingMethod") + .compile() + .run(parameters.getRuntime(), TestClass.class); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING); + } else { + // The static method which is moved to the companion class is inlined and causing + // this different output. The rule "-keep class * { *; }" does not keep the static + // method from being inlined after it has moved. Turning off inlining produces the + // expected result. The inlining cause the getEnclosingClass() to return null. + // See b/168697955. + result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING_B168697955); + } + } + + @Test + public void testR8_B168697955() throws Exception { + R8TestRunResult result = + testForR8(parameters.getBackend()) + .addInnerClasses(DesugarInnerClassesInInterfaces.class) + .setMinApi(parameters.getApiLevel()) + .addKeepRules("-keep class * { *; }") + .addKeepAttributes("InnerClasses", "EnclosingMethod") + // With inlining turned off we get the expected result. + .addOptionsModification(options -> options.enableInlining = false) + .compile() + .run(parameters.getRuntime(), TestClass.class); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING); + } else { + result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING); + } + } + + interface WithAnonymousInner { + static Callable<Class<?>> staticOuter() { + return new Callable<Class<?>>() { + @Override + public Class<?> call() throws Exception { + return getClass().getEnclosingClass(); + } + }; + } + + default Callable<Class<?>> defaultOuter() { + return new Callable<Class<?>>() { + @Override + public Class<?> call() throws Exception { + return getClass().getEnclosingClass(); + } + }; + } + } + + interface WithLocalInner { + static Callable<Class<?>> staticOuter() { + class Local implements Callable<Class<?>> { + @Override + public Class<?> call() throws Exception { + return getClass().getEnclosingClass(); + } + } + return new Local(); + } + + default Callable<Class<?>> defaultOuter() { + class Local implements Callable<Class<?>> { + @Override + public Class<?> call() throws Exception { + return getClass().getEnclosingClass(); + } + } + return new Local(); + } + } + + public static class TestClass { + + public static void main(String[] args) throws Exception { + System.out.println(new WithAnonymousInner() {}.defaultOuter().call().getName()); + System.out.println( + new WithAnonymousInner() {}.defaultOuter() + .call() + .equals(WithAnonymousInner.staticOuter().call())); + System.out.println(new WithLocalInner() {}.defaultOuter().call().getName()); + System.out.println( + new WithLocalInner() {}.defaultOuter() + .call() + .equals(WithLocalInner.staticOuter().call())); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java b/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java index acf084b..dfed133 100644 --- a/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java +++ b/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java
@@ -40,8 +40,7 @@ static class Main { public static void main(String[] args) { OuterImpl.register(args); - InnerClass inner = new InnerClass(); - inner.foobar(); + new InnerClass().foobar(); System.out.println("42"); } }
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java index ed64145..3920cc8 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
@@ -5,9 +5,7 @@ package com.android.tools.r8.resolution.packageprivate; import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationFailedException; @@ -29,10 +27,7 @@ import com.android.tools.r8.resolution.packageprivate.a.NonAbstractExtendingA; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.transformers.ClassTransformer; -import com.google.common.collect.ImmutableSet; import java.io.IOException; -import java.util.HashSet; -import java.util.Set; import java.util.concurrent.ExecutionException; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,14 +69,9 @@ ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method); DexProgramClass context = appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory())); + assertTrue(resolutionResult.isAccessibleFrom(context, appView).isFalse()); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo); - assertTrue(lookupResult.isLookupResultSuccess()); - Set<String> targets = new HashSet<>(); - lookupResult.forEach( - target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail()); - // TODO(b/148591377): The set should be empty. - ImmutableSet<String> expected = ImmutableSet.of(Abstract.class.getTypeName() + ".foo"); - assertEquals(expected, targets); + assertTrue(lookupResult.isLookupResultFailure()); } @Test
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java index f0c3022..c688182 100644 --- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java +++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
@@ -55,7 +55,7 @@ static class A { private static String buildClassName(String className) { - return com.android.tools.r8.shaking.A.class.getPackage().getName() + "." + className; + return A.class.getPackage().getName() + "." + className; } public static void main(String[] args) { @@ -179,7 +179,7 @@ "Options with file names are not supported"); } - class TestProvider implements ProgramResourceProvider, DataResourceProvider { + static class TestProvider implements ProgramResourceProvider, DataResourceProvider { @Override public Collection<ProgramResource> getProgramResources() throws ResourceException {
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java index bee167f..6f6ad5a 100644 --- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java +++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -35,6 +35,10 @@ import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; @RunWith(Parameterized.class) public class NonVirtualOverrideTest extends TestBase { @@ -190,4 +194,899 @@ assertThat(classSubject.method("void", "m4", ImmutableList.of()), not(isPresent())); } } + + static class NonVirtualOverrideTestClass { + + public static void main(String[] args) { + A a = new B(); + a.m1(); + a.m2(); + a.m3(); + a.m4(); + + a = new C(); + a.m1(); + a.m2(); + a.m3(); + a.m4(); + + B b = new B(); + try { + b.m1(); + } catch (IllegalAccessError exception) { + System.out.println("Caught IllegalAccessError when calling B.m1()"); + } + try { + b.m3(); + } catch (IncompatibleClassChangeError exception) { + System.out.println("Caught IncompatibleClassChangeError when calling B.m3()"); + } + + try { + b = new C(); + b.m1(); + } catch (IllegalAccessError exception) { + System.out.println("Caught IllegalAccessError when calling B.m1()"); + } + try { + b = new C(); + b.m3(); + } catch (IncompatibleClassChangeError exception) { + System.out.println("Caught IncompatibleClassChangeError when calling B.m3()"); + } + + C c = new C(); + c.m1(); + c.m3(); + } + } + + static class A { + + public void m1() { + System.out.println("In A.m1()"); + } + + public void m2() { + System.out.println("In A.m2()"); + } + + public void m3() { + System.out.println("In A.m3()"); + } + + public void m4() { + System.out.println("In A.m4()"); + } + } + + static class B extends A { + + // Made private in the dump below. This method is targeted and can therefore not be removed. + @Override + public void m1() { + System.out.println("In B.m1()"); + } + + // Made private in the dump below. Ends up being dead code because the method is never called. + @Override + public void m2() { + System.out.println("In B.m2()"); + } + + // Made static in the dump below. This method is targeted and can therefore not be removed. + @Override + public void m3() { + System.out.println("In B.m3()"); + } + + // Made static in the dump below. Ends up being dead code because the method is never called. + @Override + public void m4() { + System.out.println("In B.m4()"); + } + } + + static class C extends B { + + @Override + public void m1() { + System.out.println("In C.m1()"); + } + + @Override + public void m3() { + System.out.println("In C.m3()"); + } + } + + /* Below are dumps from the classes above with the changes to B as described */ + + static class NonVirtualOverrideTestClassDump implements Opcodes { + + public static byte[] dump() { + + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + + classWriter.visit( + V1_8, + ACC_SUPER, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$NonVirtualOverrideTestClass", + null, + "java/lang/Object", + null); + + classWriter.visitSource("NonVirtualOverrideTest.java", null); + + { + methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(7, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + methodVisitor.visitInsn(RETURN); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$NonVirtualOverrideTestClass;", + null, + label0, + label1, + 0); + methodVisitor.visitMaxs(1, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = + classWriter.visitMethod( + ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + Label label1 = new Label(); + Label label2 = new Label(); + methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/IllegalAccessError"); + Label label3 = new Label(); + Label label4 = new Label(); + Label label5 = new Label(); + methodVisitor.visitTryCatchBlock( + label3, label4, label5, "java/lang/IncompatibleClassChangeError"); + Label label6 = new Label(); + Label label7 = new Label(); + Label label8 = new Label(); + methodVisitor.visitTryCatchBlock(label6, label7, label8, "java/lang/IllegalAccessError"); + Label label9 = new Label(); + Label label10 = new Label(); + Label label11 = new Label(); + methodVisitor.visitTryCatchBlock( + label9, label10, label11, "java/lang/IncompatibleClassChangeError"); + Label label12 = new Label(); + methodVisitor.visitLabel(label12); + methodVisitor.visitLineNumber(10, label12); + methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/NonVirtualOverrideTest$B"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + "<init>", + "()V", + false); + methodVisitor.visitVarInsn(ASTORE, 1); + Label label13 = new Label(); + methodVisitor.visitLabel(label13); + methodVisitor.visitLineNumber(11, label13); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m1", + "()V", + false); + Label label14 = new Label(); + methodVisitor.visitLabel(label14); + methodVisitor.visitLineNumber(12, label14); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m2", + "()V", + false); + Label label15 = new Label(); + methodVisitor.visitLabel(label15); + methodVisitor.visitLineNumber(13, label15); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m3", + "()V", + false); + Label label16 = new Label(); + methodVisitor.visitLabel(label16); + methodVisitor.visitLineNumber(14, label16); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m4", + "()V", + false); + Label label17 = new Label(); + methodVisitor.visitLabel(label17); + methodVisitor.visitLineNumber(16, label17); + methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/NonVirtualOverrideTest$C"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$C", + "<init>", + "()V", + false); + methodVisitor.visitVarInsn(ASTORE, 1); + Label label18 = new Label(); + methodVisitor.visitLabel(label18); + methodVisitor.visitLineNumber(17, label18); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m1", + "()V", + false); + Label label19 = new Label(); + methodVisitor.visitLabel(label19); + methodVisitor.visitLineNumber(18, label19); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m2", + "()V", + false); + Label label20 = new Label(); + methodVisitor.visitLabel(label20); + methodVisitor.visitLineNumber(19, label20); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m3", + "()V", + false); + Label label21 = new Label(); + methodVisitor.visitLabel(label21); + methodVisitor.visitLineNumber(20, label21); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "m4", + "()V", + false); + Label label22 = new Label(); + methodVisitor.visitLabel(label22); + methodVisitor.visitLineNumber(22, label22); + methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/NonVirtualOverrideTest$B"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + "<init>", + "()V", + false); + methodVisitor.visitVarInsn(ASTORE, 2); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(24, label0); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + "m1", + "()V", + false); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(27, label1); + methodVisitor.visitJumpInsn(GOTO, label3); + methodVisitor.visitLabel(label2); + methodVisitor.visitLineNumber(25, label2); + methodVisitor.visitFrame( + Opcodes.F_FULL, + 3, + new Object[] { + "[Ljava/lang/String;", + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B" + }, + 1, + new Object[] {"java/lang/IllegalAccessError"}); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label23 = new Label(); + methodVisitor.visitLabel(label23); + methodVisitor.visitLineNumber(26, label23); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("Caught IllegalAccessError when calling B.m1()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + methodVisitor.visitLabel(label3); + methodVisitor.visitLineNumber(29, label3); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + "m3", + "()V", + false); + methodVisitor.visitLabel(label4); + methodVisitor.visitLineNumber(32, label4); + methodVisitor.visitJumpInsn(GOTO, label6); + methodVisitor.visitLabel(label5); + methodVisitor.visitLineNumber(30, label5); + methodVisitor.visitFrame( + Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/IncompatibleClassChangeError"}); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label24 = new Label(); + methodVisitor.visitLabel(label24); + methodVisitor.visitLineNumber(31, label24); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("Caught IncompatibleClassChangeError when calling B.m3()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + methodVisitor.visitLabel(label6); + methodVisitor.visitLineNumber(35, label6); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/NonVirtualOverrideTest$C"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$C", + "<init>", + "()V", + false); + methodVisitor.visitVarInsn(ASTORE, 2); + Label label25 = new Label(); + methodVisitor.visitLabel(label25); + methodVisitor.visitLineNumber(36, label25); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + "m1", + "()V", + false); + methodVisitor.visitLabel(label7); + methodVisitor.visitLineNumber(39, label7); + methodVisitor.visitJumpInsn(GOTO, label9); + methodVisitor.visitLabel(label8); + methodVisitor.visitLineNumber(37, label8); + methodVisitor.visitFrame( + Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/IllegalAccessError"}); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label26 = new Label(); + methodVisitor.visitLabel(label26); + methodVisitor.visitLineNumber(38, label26); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("Caught IllegalAccessError when calling B.m1()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + methodVisitor.visitLabel(label9); + methodVisitor.visitLineNumber(41, label9); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/NonVirtualOverrideTest$C"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$C", + "<init>", + "()V", + false); + methodVisitor.visitVarInsn(ASTORE, 2); + Label label27 = new Label(); + methodVisitor.visitLabel(label27); + methodVisitor.visitLineNumber(42, label27); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + "m3", + "()V", + false); + methodVisitor.visitLabel(label10); + methodVisitor.visitLineNumber(45, label10); + Label label28 = new Label(); + methodVisitor.visitJumpInsn(GOTO, label28); + methodVisitor.visitLabel(label11); + methodVisitor.visitLineNumber(43, label11); + methodVisitor.visitFrame( + Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/IncompatibleClassChangeError"}); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label29 = new Label(); + methodVisitor.visitLabel(label29); + methodVisitor.visitLineNumber(44, label29); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("Caught IncompatibleClassChangeError when calling B.m3()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + methodVisitor.visitLabel(label28); + methodVisitor.visitLineNumber(47, label28); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/NonVirtualOverrideTest$C"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$C", + "<init>", + "()V", + false); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label30 = new Label(); + methodVisitor.visitLabel(label30); + methodVisitor.visitLineNumber(48, label30); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$C", + "m1", + "()V", + false); + Label label31 = new Label(); + methodVisitor.visitLabel(label31); + methodVisitor.visitLineNumber(49, label31); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$C", + "m3", + "()V", + false); + Label label32 = new Label(); + methodVisitor.visitLabel(label32); + methodVisitor.visitLineNumber(50, label32); + methodVisitor.visitInsn(RETURN); + Label label33 = new Label(); + methodVisitor.visitLabel(label33); + methodVisitor.visitLocalVariable( + "exception", "Ljava/lang/IllegalAccessError;", null, label23, label3, 3); + methodVisitor.visitLocalVariable( + "exception", "Ljava/lang/IncompatibleClassChangeError;", null, label24, label6, 3); + methodVisitor.visitLocalVariable( + "exception", "Ljava/lang/IllegalAccessError;", null, label26, label9, 3); + methodVisitor.visitLocalVariable( + "exception", "Ljava/lang/IncompatibleClassChangeError;", null, label29, label28, 3); + methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label12, label33, 0); + methodVisitor.visitLocalVariable( + "a", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$A;", + null, + label13, + label33, + 1); + methodVisitor.visitLocalVariable( + "b", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$B;", + null, + label0, + label33, + 2); + methodVisitor.visitLocalVariable( + "c", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$C;", + null, + label30, + label33, + 3); + methodVisitor.visitMaxs(2, 4); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } + } + + static class ADump implements Opcodes { + + public static byte[] dump() { + + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + + classWriter.visit( + V1_8, + ACC_SUPER, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + null, + "java/lang/Object", + null); + + classWriter.visitSource("NonVirtualOverrideTest.java", null); + + { + methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(53, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + methodVisitor.visitInsn(RETURN); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$A;", + null, + label0, + label1, + 0); + methodVisitor.visitMaxs(1, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m1", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(56, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In A.m1()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(57, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$A;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m2", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(60, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In A.m2()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(61, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$A;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m3", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(64, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In A.m3()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(65, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$A;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m4", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(68, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In A.m4()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(69, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$A;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } + } + + static class BDump implements Opcodes { + + public static byte[] dump() { + + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + + classWriter.visit( + V1_8, + ACC_SUPER, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + null, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + null); + + classWriter.visitSource("NonVirtualOverrideTest.java", null); + + { + methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(72, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$A", + "<init>", + "()V", + false); + methodVisitor.visitInsn(RETURN); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$B;", + null, + label0, + label1, + 0); + methodVisitor.visitMaxs(1, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "m1", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(76, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In B.m1()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(77, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$B;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "m2", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(81, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In B.m2()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(82, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$B;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_STATIC, "m3", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(86, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In B.m3()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(87, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$B;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_STATIC, "m4", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(91, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In B.m4()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(92, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$B;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } + } + + static class CDump implements Opcodes { + + public static byte[] dump() { + + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + + classWriter.visit( + V1_8, + ACC_SUPER, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$C", + null, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + null); + + classWriter.visitSource("NonVirtualOverrideTest.java", null); + + { + methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(95, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitMethodInsn( + INVOKESPECIAL, + "com/android/tools/r8/shaking/NonVirtualOverrideTest$B", + "<init>", + "()V", + false); + methodVisitor.visitInsn(RETURN); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$C;", + null, + label0, + label1, + 0); + methodVisitor.visitMaxs(1, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m1", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(98, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In C.m1()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(99, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$C;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m3", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(102, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("In C.m3()"); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(103, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Lcom/android/tools/r8/shaking/NonVirtualOverrideTest$C;", + null, + label0, + label2, + 0); + methodVisitor.visitMaxs(2, 1); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } + } }
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTestDump.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTestDump.java deleted file mode 100644 index e8f54b3..0000000 --- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTestDump.java +++ /dev/null
@@ -1,726 +0,0 @@ -// 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.shaking; - -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -class NonVirtualOverrideTestClass { - - public static void main(String[] args) { - A a = new B(); - a.m1(); - a.m2(); - a.m3(); - a.m4(); - - a = new C(); - a.m1(); - a.m2(); - a.m3(); - a.m4(); - - B b = new B(); - try { - b.m1(); - } catch (IllegalAccessError exception) { - System.out.println("Caught IllegalAccessError when calling B.m1()"); - } - try { - b.m3(); - } catch (IncompatibleClassChangeError exception) { - System.out.println("Caught IncompatibleClassChangeError when calling B.m3()"); - } - - try { - b = new C(); - b.m1(); - } catch (IllegalAccessError exception) { - System.out.println("Caught IllegalAccessError when calling B.m1()"); - } - try { - b = new C(); - b.m3(); - } catch (IncompatibleClassChangeError exception) { - System.out.println("Caught IncompatibleClassChangeError when calling B.m3()"); - } - - C c = new C(); - c.m1(); - c.m3(); - } -} - -class A { - - public void m1() { - System.out.println("In A.m1()"); - } - - public void m2() { - System.out.println("In A.m2()"); - } - - public void m3() { - System.out.println("In A.m3()"); - } - - public void m4() { - System.out.println("In A.m4()"); - } -} - -class B extends A { - - // Made private in the dump below. This method is targeted and can therefore not be removed. - public void m1() { - System.out.println("In B.m1()"); - } - - // Made private in the dump below. Ends up being dead code because the method is never called. - public void m2() { - System.out.println("In B.m2()"); - } - - // Made static in the dump below. This method is targeted and can therefore not be removed. - public void m3() { - System.out.println("In B.m3()"); - } - - // Made static in the dump below. Ends up being dead code because the method is never called. - public void m4() { - System.out.println("In B.m4()"); - } -} - -class C extends B { - - public void m1() { - System.out.println("In C.m1()"); - } - - public void m3() { - System.out.println("In C.m3()"); - } -} - -/* Below are dumps from the classes above with the changes to B as described */ - -class NonVirtualOverrideTestClassDump implements Opcodes { - - public static byte[] dump() { - - ClassWriter classWriter = new ClassWriter(0); - MethodVisitor methodVisitor; - - classWriter.visit( - V1_8, - ACC_SUPER, - "com/android/tools/r8/shaking/NonVirtualOverrideTestClass", - null, - "java/lang/Object", - null); - - classWriter.visitSource("NonVirtualOverrideTestDump.java", null); - - { - methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(7, label0); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); - methodVisitor.visitInsn(RETURN); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLocalVariable( - "this", - "Lcom/android/tools/r8/shaking/NonVirtualOverrideTestClass;", - null, - label0, - label1, - 0); - methodVisitor.visitMaxs(1, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = - classWriter.visitMethod( - ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - Label label1 = new Label(); - Label label2 = new Label(); - methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/IllegalAccessError"); - Label label3 = new Label(); - Label label4 = new Label(); - Label label5 = new Label(); - methodVisitor.visitTryCatchBlock( - label3, label4, label5, "java/lang/IncompatibleClassChangeError"); - Label label6 = new Label(); - Label label7 = new Label(); - Label label8 = new Label(); - methodVisitor.visitTryCatchBlock(label6, label7, label8, "java/lang/IllegalAccessError"); - Label label9 = new Label(); - Label label10 = new Label(); - Label label11 = new Label(); - methodVisitor.visitTryCatchBlock( - label9, label10, label11, "java/lang/IncompatibleClassChangeError"); - Label label12 = new Label(); - methodVisitor.visitLabel(label12); - methodVisitor.visitLineNumber(10, label12); - methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/B"); - methodVisitor.visitInsn(DUP); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/B", "<init>", "()V", false); - methodVisitor.visitVarInsn(ASTORE, 1); - Label label13 = new Label(); - methodVisitor.visitLabel(label13); - methodVisitor.visitLineNumber(11, label13); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m1", "()V", false); - Label label14 = new Label(); - methodVisitor.visitLabel(label14); - methodVisitor.visitLineNumber(12, label14); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m2", "()V", false); - Label label15 = new Label(); - methodVisitor.visitLabel(label15); - methodVisitor.visitLineNumber(13, label15); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m3", "()V", false); - Label label16 = new Label(); - methodVisitor.visitLabel(label16); - methodVisitor.visitLineNumber(14, label16); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m4", "()V", false); - Label label17 = new Label(); - methodVisitor.visitLabel(label17); - methodVisitor.visitLineNumber(16, label17); - methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/C"); - methodVisitor.visitInsn(DUP); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/C", "<init>", "()V", false); - methodVisitor.visitVarInsn(ASTORE, 1); - Label label18 = new Label(); - methodVisitor.visitLabel(label18); - methodVisitor.visitLineNumber(17, label18); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m1", "()V", false); - Label label19 = new Label(); - methodVisitor.visitLabel(label19); - methodVisitor.visitLineNumber(18, label19); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m2", "()V", false); - Label label20 = new Label(); - methodVisitor.visitLabel(label20); - methodVisitor.visitLineNumber(19, label20); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m3", "()V", false); - Label label21 = new Label(); - methodVisitor.visitLabel(label21); - methodVisitor.visitLineNumber(20, label21); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/A", "m4", "()V", false); - Label label22 = new Label(); - methodVisitor.visitLabel(label22); - methodVisitor.visitLineNumber(22, label22); - methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/B"); - methodVisitor.visitInsn(DUP); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/B", "<init>", "()V", false); - methodVisitor.visitVarInsn(ASTORE, 2); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(24, label0); - methodVisitor.visitVarInsn(ALOAD, 2); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/B", "m1", "()V", false); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(27, label1); - methodVisitor.visitJumpInsn(GOTO, label3); - methodVisitor.visitLabel(label2); - methodVisitor.visitLineNumber(25, label2); - methodVisitor.visitFrame( - Opcodes.F_FULL, - 3, - new Object[] { - "[Ljava/lang/String;", - "com/android/tools/r8/shaking/A", - "com/android/tools/r8/shaking/B" - }, - 1, - new Object[] {"java/lang/IllegalAccessError"}); - methodVisitor.visitVarInsn(ASTORE, 3); - Label label23 = new Label(); - methodVisitor.visitLabel(label23); - methodVisitor.visitLineNumber(26, label23); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("Caught IllegalAccessError when calling B.m1()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - methodVisitor.visitLabel(label3); - methodVisitor.visitLineNumber(29, label3); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitVarInsn(ALOAD, 2); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/B", "m3", "()V", false); - methodVisitor.visitLabel(label4); - methodVisitor.visitLineNumber(32, label4); - methodVisitor.visitJumpInsn(GOTO, label6); - methodVisitor.visitLabel(label5); - methodVisitor.visitLineNumber(30, label5); - methodVisitor.visitFrame( - Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/IncompatibleClassChangeError"}); - methodVisitor.visitVarInsn(ASTORE, 3); - Label label24 = new Label(); - methodVisitor.visitLabel(label24); - methodVisitor.visitLineNumber(31, label24); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("Caught IncompatibleClassChangeError when calling B.m3()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - methodVisitor.visitLabel(label6); - methodVisitor.visitLineNumber(35, label6); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/C"); - methodVisitor.visitInsn(DUP); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/C", "<init>", "()V", false); - methodVisitor.visitVarInsn(ASTORE, 2); - Label label25 = new Label(); - methodVisitor.visitLabel(label25); - methodVisitor.visitLineNumber(36, label25); - methodVisitor.visitVarInsn(ALOAD, 2); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/B", "m1", "()V", false); - methodVisitor.visitLabel(label7); - methodVisitor.visitLineNumber(39, label7); - methodVisitor.visitJumpInsn(GOTO, label9); - methodVisitor.visitLabel(label8); - methodVisitor.visitLineNumber(37, label8); - methodVisitor.visitFrame( - Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/IllegalAccessError"}); - methodVisitor.visitVarInsn(ASTORE, 3); - Label label26 = new Label(); - methodVisitor.visitLabel(label26); - methodVisitor.visitLineNumber(38, label26); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("Caught IllegalAccessError when calling B.m1()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - methodVisitor.visitLabel(label9); - methodVisitor.visitLineNumber(41, label9); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/C"); - methodVisitor.visitInsn(DUP); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/C", "<init>", "()V", false); - methodVisitor.visitVarInsn(ASTORE, 2); - Label label27 = new Label(); - methodVisitor.visitLabel(label27); - methodVisitor.visitLineNumber(42, label27); - methodVisitor.visitVarInsn(ALOAD, 2); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/B", "m3", "()V", false); - methodVisitor.visitLabel(label10); - methodVisitor.visitLineNumber(45, label10); - Label label28 = new Label(); - methodVisitor.visitJumpInsn(GOTO, label28); - methodVisitor.visitLabel(label11); - methodVisitor.visitLineNumber(43, label11); - methodVisitor.visitFrame( - Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/IncompatibleClassChangeError"}); - methodVisitor.visitVarInsn(ASTORE, 3); - Label label29 = new Label(); - methodVisitor.visitLabel(label29); - methodVisitor.visitLineNumber(44, label29); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("Caught IncompatibleClassChangeError when calling B.m3()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - methodVisitor.visitLabel(label28); - methodVisitor.visitLineNumber(47, label28); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitTypeInsn(NEW, "com/android/tools/r8/shaking/C"); - methodVisitor.visitInsn(DUP); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/C", "<init>", "()V", false); - methodVisitor.visitVarInsn(ASTORE, 3); - Label label30 = new Label(); - methodVisitor.visitLabel(label30); - methodVisitor.visitLineNumber(48, label30); - methodVisitor.visitVarInsn(ALOAD, 3); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/C", "m1", "()V", false); - Label label31 = new Label(); - methodVisitor.visitLabel(label31); - methodVisitor.visitLineNumber(49, label31); - methodVisitor.visitVarInsn(ALOAD, 3); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "com/android/tools/r8/shaking/C", "m3", "()V", false); - Label label32 = new Label(); - methodVisitor.visitLabel(label32); - methodVisitor.visitLineNumber(50, label32); - methodVisitor.visitInsn(RETURN); - Label label33 = new Label(); - methodVisitor.visitLabel(label33); - methodVisitor.visitLocalVariable( - "exception", "Ljava/lang/IllegalAccessError;", null, label23, label3, 3); - methodVisitor.visitLocalVariable( - "exception", "Ljava/lang/IncompatibleClassChangeError;", null, label24, label6, 3); - methodVisitor.visitLocalVariable( - "exception", "Ljava/lang/IllegalAccessError;", null, label26, label9, 3); - methodVisitor.visitLocalVariable( - "exception", "Ljava/lang/IncompatibleClassChangeError;", null, label29, label28, 3); - methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label12, label33, 0); - methodVisitor.visitLocalVariable( - "a", "Lcom/android/tools/r8/shaking/A;", null, label13, label33, 1); - methodVisitor.visitLocalVariable( - "b", "Lcom/android/tools/r8/shaking/B;", null, label0, label33, 2); - methodVisitor.visitLocalVariable( - "c", "Lcom/android/tools/r8/shaking/C;", null, label30, label33, 3); - methodVisitor.visitMaxs(2, 4); - methodVisitor.visitEnd(); - } - classWriter.visitEnd(); - - return classWriter.toByteArray(); - } -} - -class ADump implements Opcodes { - - public static byte[] dump() { - - ClassWriter classWriter = new ClassWriter(0); - MethodVisitor methodVisitor; - - classWriter.visit( - V1_8, ACC_SUPER, "com/android/tools/r8/shaking/A", null, "java/lang/Object", null); - - classWriter.visitSource("NonVirtualOverrideTestDump.java", null); - - { - methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(53, label0); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); - methodVisitor.visitInsn(RETURN); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/A;", null, label0, label1, 0); - methodVisitor.visitMaxs(1, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m1", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(56, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In A.m1()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(57, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/A;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m2", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(60, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In A.m2()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(61, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/A;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m3", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(64, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In A.m3()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(65, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/A;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m4", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(68, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In A.m4()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(69, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/A;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - classWriter.visitEnd(); - - return classWriter.toByteArray(); - } -} - -class BDump implements Opcodes { - - public static byte[] dump() { - - ClassWriter classWriter = new ClassWriter(0); - MethodVisitor methodVisitor; - - classWriter.visit( - V1_8, - ACC_SUPER, - "com/android/tools/r8/shaking/B", - null, - "com/android/tools/r8/shaking/A", - null); - - classWriter.visitSource("NonVirtualOverrideTestDump.java", null); - - { - methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(72, label0); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/A", "<init>", "()V", false); - methodVisitor.visitInsn(RETURN); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/B;", null, label0, label1, 0); - methodVisitor.visitMaxs(1, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "m1", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(76, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In B.m1()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(77, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/B;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "m2", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(81, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In B.m2()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(82, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/B;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_STATIC, "m3", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(86, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In B.m3()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(87, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/B;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_STATIC, "m4", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(91, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In B.m4()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(92, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/B;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - classWriter.visitEnd(); - - return classWriter.toByteArray(); - } -} - -class CDump implements Opcodes { - - public static byte[] dump() { - - ClassWriter classWriter = new ClassWriter(0); - MethodVisitor methodVisitor; - - classWriter.visit( - V1_8, - ACC_SUPER, - "com/android/tools/r8/shaking/C", - null, - "com/android/tools/r8/shaking/B", - null); - - classWriter.visitSource("NonVirtualOverrideTestDump.java", null); - - { - methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(95, label0); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn( - INVOKESPECIAL, "com/android/tools/r8/shaking/B", "<init>", "()V", false); - methodVisitor.visitInsn(RETURN); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/C;", null, label0, label1, 0); - methodVisitor.visitMaxs(1, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m1", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(98, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In C.m1()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(99, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/C;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "m3", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(102, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("In C.m3()"); - methodVisitor.visitMethodInsn( - INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(103, label1); - methodVisitor.visitInsn(RETURN); - Label label2 = new Label(); - methodVisitor.visitLabel(label2); - methodVisitor.visitLocalVariable( - "this", "Lcom/android/tools/r8/shaking/C;", null, label0, label2, 0); - methodVisitor.visitMaxs(2, 1); - methodVisitor.visitEnd(); - } - classWriter.visitEnd(); - - return classWriter.toByteArray(); - } -}
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java b/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java new file mode 100644 index 0000000..29a7280 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java
@@ -0,0 +1,142 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.graph.AppView; +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.shaking.AppInfoWithLiveness; +import com.android.tools.r8.shaking.b169045091.testclasses.HelloGreeter; +import com.android.tools.r8.shaking.b169045091.testclasses.WorldGreeterBase; +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class B169045091 extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public B169045091(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getProgramClasses()) + .addProgramClassFileData(getWorldGreeterClassFileData()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(getProgramClasses()) + .addProgramClassFileData(getWorldGreeterClassFileData()) + .addKeepMainRule(TestClass.class) + .enableNeverClassInliningAnnotations() + .enableInliningAnnotations() + .enableNoVerticalClassMergingAnnotations() + .setMinApi(parameters.getApiLevel()) + .noMinification() + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + private List<Class<?>> getProgramClasses() { + return ImmutableList.of( + TestClass.class, HelloGreeter.class, HelloGreeterBase.class, WorldGreeterBase.class); + } + + private byte[] getWorldGreeterClassFileData() throws Exception { + return transformer(WorldGreeter.class) + .removeMethods( + (int access, String name, String descriptor, String signature, String[] exceptions) -> + name.equals("world")) + .transform(); + } + + @Test + public void testAccessibility() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AppView<AppInfoWithLiveness> appView = + computeAppViewWithLiveness( + buildClasses(getProgramClasses()) + .addClassProgramData(getWorldGreeterClassFileData()) + .build(), + TestClass.class); + AppInfoWithLiveness appInfo = appView.appInfo(); + DexItemFactory dexItemFactory = appView.dexItemFactory(); + + DexProgramClass context = + appView + .contextIndependentDefinitionFor(buildType(TestClass.class, dexItemFactory)) + .asProgramClass(); + + // Test that HelloGreeter.greet() is accessible to TestClass. + DexMethod helloReference = buildNullaryVoidMethod(HelloGreeter.class, "hello", dexItemFactory); + assertTrue( + appInfo.resolveMethodOnClass(helloReference).isAccessibleFrom(context, appView).isTrue()); + + // Test that WorldGreeter.greet() is inaccessible to TestClass. + DexMethod worldReference = buildNullaryVoidMethod(WorldGreeter.class, "world", dexItemFactory); + assertTrue( + appInfo.resolveMethodOnClass(worldReference).isAccessibleFrom(context, appView).isFalse()); + } + + public static class TestClass { + + public static void main(String[] args) { + // HelloGreeterBase.greet() is accessible to TestClass because they are in the same package. + new HelloGreeter().hello(); + + try { + // WorldGreeterBase.world() is inaccessible to TestClass. + new WorldGreeter().world(); + throw new RuntimeException(); + } catch (IllegalAccessError e) { + System.out.println(" world!"); + } + } + } + + @NoVerticalClassMerging + public static class HelloGreeterBase { + @NeverInline + protected void hello() { + System.out.print("Hello"); + } + } + + @NeverClassInline + public static class WorldGreeter extends WorldGreeterBase { + + // Removed by a transformer. + @Override + public void world() { + super.world(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/NestMemberAccessibilityTest.java b/src/test/java/com/android/tools/r8/shaking/b169045091/NestMemberAccessibilityTest.java new file mode 100644 index 0000000..62923a7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/b169045091/NestMemberAccessibilityTest.java
@@ -0,0 +1,131 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091; + +import static com.android.tools.r8.references.Reference.INT; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.shaking.b169045091.B169045091.TestClass; +import com.android.tools.r8.shaking.b169045091.examples.NestHost; +import com.android.tools.r8.shaking.b169045091.examples.NestHost.NestMember; +import com.android.tools.r8.shaking.b169045091.examples.NonNestMember; +import com.android.tools.r8.utils.DescriptorUtils; +import com.google.common.collect.ImmutableList; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +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 NestMemberAccessibilityTest extends TestBase { + + private final Path TEST_DIRECTORY = + Paths.get(ToolHelper.EXAMPLES_JAVA11_BUILD_DIR) + .resolve( + DescriptorUtils.getBinaryNameFromJavaType(NestHost.class.getPackage().getName())); + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return TestBase.getTestParameters().withNoneRuntime().build(); + } + + public NestMemberAccessibilityTest(TestParameters parameters) { + parameters.assertNoneRuntime(); + } + + @Test + public void testAccessibility() throws Exception { + AppView<AppInfoWithLiveness> appView = + computeAppViewWithLiveness( + buildClasses() + .addProgramFiles(getProgramFiles()) + .addClassProgramData(getNestHostClassFileData()) + .build(), + TestClass.class); + AppInfoWithLiveness appInfo = appView.appInfo(); + DexItemFactory dexItemFactory = appView.dexItemFactory(); + + DexProgramClass hostContext = + appView + .contextIndependentDefinitionFor(buildType(NestHost.class, dexItemFactory)) + .asProgramClass(); + + DexProgramClass memberContext = + appView + .contextIndependentDefinitionFor(buildType(NestMember.class, dexItemFactory)) + .asProgramClass(); + + DexProgramClass nonMemberContext = + appView + .contextIndependentDefinitionFor(buildType(NonNestMember.class, dexItemFactory)) + .asProgramClass(); + + // Test that NestHost.f is accessible to NestHost and NestMember but not NonNestMember. + DexField hostFieldReference = + buildField( + Reference.field(Reference.classFromClass(NestHost.class), "f", INT), dexItemFactory); + assertTrue( + appInfo.resolveField(hostFieldReference).isAccessibleFrom(hostContext, appView).isTrue()); + assertTrue( + appInfo.resolveField(hostFieldReference).isAccessibleFrom(memberContext, appView).isTrue()); + assertTrue( + appInfo + .resolveField(hostFieldReference) + .isAccessibleFrom(nonMemberContext, appView) + .isFalse()); + + // Test that NestMember.f is accessible to NestMember but not NonNestMember. + DexField memberFieldReference = + buildField( + Reference.field(Reference.classFromClass(NestMember.class), "f", INT), dexItemFactory); + assertTrue( + appInfo + .resolveField(memberFieldReference) + .isAccessibleFrom(memberContext, appView) + .isTrue()); + assertTrue( + appInfo + .resolveField(memberFieldReference) + .isAccessibleFrom(nonMemberContext, appView) + .isFalse()); + + // Test that NonNestMember.f is inaccessible to NonNestMember. + DexField nonMemberFieldReference = + buildField( + Reference.field(Reference.classFromClass(NonNestMember.class), "f", INT), + dexItemFactory); + assertTrue( + appInfo + .resolveField(nonMemberFieldReference) + .isAccessibleFrom(nonMemberContext, appView) + .isFalse()); + } + + private List<Path> getProgramFiles() { + return ImmutableList.of( + TEST_DIRECTORY.resolve("NestHost$NestMember.class"), + TEST_DIRECTORY.resolve("NonNestMember.class")); + } + + private byte[] getNestHostClassFileData() throws Exception { + return transformer( + TEST_DIRECTORY.resolve("NestHost.class"), Reference.classFromClass(NestHost.class)) + .setPrivate(NestHost.class.getDeclaredField("f")) + .transform(); + } +}
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/examples/NestHost.java b/src/test/java/com/android/tools/r8/shaking/b169045091/examples/NestHost.java new file mode 100644 index 0000000..872e37b --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/b169045091/examples/NestHost.java
@@ -0,0 +1,15 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091.examples; + +/** + * Mirror of src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NestHost.java + */ +public class NestHost { + + public int f; + + public static class NestMember {} +}
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/examples/NonNestMember.java b/src/test/java/com/android/tools/r8/shaking/b169045091/examples/NonNestMember.java new file mode 100644 index 0000000..26de699 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/b169045091/examples/NonNestMember.java
@@ -0,0 +1,11 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091.examples; + +/** + * Mirror of + * src/test/examplesJava11/com/android/tools/r8/shaking/b169045091/examples/NonNestMember.java. + */ +public class NonNestMember {}
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/testclasses/HelloGreeter.java b/src/test/java/com/android/tools/r8/shaking/b169045091/testclasses/HelloGreeter.java new file mode 100644 index 0000000..edc59a7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/b169045091/testclasses/HelloGreeter.java
@@ -0,0 +1,11 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091.testclasses; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.shaking.b169045091.B169045091.HelloGreeterBase; + +@NeverClassInline +public class HelloGreeter extends HelloGreeterBase {}
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/testclasses/WorldGreeterBase.java b/src/test/java/com/android/tools/r8/shaking/b169045091/testclasses/WorldGreeterBase.java new file mode 100644 index 0000000..9dc0b62 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/b169045091/testclasses/WorldGreeterBase.java
@@ -0,0 +1,16 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.b169045091.testclasses; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoVerticalClassMerging; + +@NoVerticalClassMerging +public class WorldGreeterBase { + @NeverInline + protected void world() { + System.out.println(" world!"); + } +}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java new file mode 100644 index 0000000..489d8b2 --- /dev/null +++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -0,0 +1,355 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.tracereferences; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.DiagnosticsChecker; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.tracereferences.TraceReferencesCommandParser.OutputFormat; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.FileUtils; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.ZipUtils; +import com.google.common.collect.ImmutableList; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import kotlin.text.Charsets; +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 TraceReferencesCommandTest extends TestBase { + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public TraceReferencesCommandTest(TestParameters parameters) {} + + @Test + public void emptyBuilder() throws Throwable { + verifyEmptyCommand(TraceReferencesCommand.builder().build()); + } + + private void verifyEmptyCommand(TraceReferencesCommand command) { + assertEquals(0, command.getLibrary().size()); + assertEquals(0, command.getTarget().size()); + assertEquals(0, command.getSource().size()); + assertEquals(TraceReferencesCommandParser.OutputFormat.PRINTUSAGE, command.getOutputFormat()); + assertNull(command.getOutput()); + } + + @Test(expected = CompilationFailedException.class) + public void emptyRun() throws Throwable { + DiagnosticsChecker.checkErrorsContains( + "No library specified", + handler -> { + TraceReferences.run(TraceReferencesCommand.builder(handler).build()); + }); + } + + @Test(expected = CompilationFailedException.class) + public void emptyRunCommandLine() throws Throwable { + DiagnosticsChecker.checkErrorsContains( + "No library specified", + handler -> { + TraceReferences.run( + TraceReferencesCommand.parse(new String[] {""}, Origin.unknown(), handler).build()); + }); + } + + @Test(expected = CompilationFailedException.class) + public void onlyLibrarySpecified() throws Throwable { + DiagnosticsChecker.checkErrorsContains( + "No target specified", + handler -> { + TraceReferences.run( + TraceReferencesCommand.builder(handler) + .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P)) + .build()); + }); + } + + @Test(expected = CompilationFailedException.class) + public void onlyLibrarySpecifiedCommandLine() throws Throwable { + DiagnosticsChecker.checkErrorsContains( + "No target specified", + handler -> { + TraceReferences.run( + TraceReferencesCommand.parse( + new String[] { + "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString() + }, + Origin.unknown(), + handler) + .build()); + }); + } + + private String formatName(OutputFormat format) { + if (format == TraceReferencesCommandParser.OutputFormat.PRINTUSAGE) { + return "printuses"; + } + if (format == TraceReferencesCommandParser.OutputFormat.KEEP_RULES) { + return "keep"; + } + assertSame(format, TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION); + return "keepallowobfuscation"; + } + + public void runAndCheckOutput( + Path targetJar, Path sourceJar, OutputFormat format, String expected) throws Throwable { + Path dir = temp.newFolder().toPath(); + Path output = dir.resolve("output.txt"); + TraceReferences.run( + TraceReferencesCommand.builder() + .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P)) + .addTargetFiles(targetJar) + .addSourceFiles(sourceJar) + .setOutputPath(output) + .setOutputFormat(format) + .build()); + assertEquals(expected, FileUtils.readTextFile(output, Charsets.UTF_8)); + + TraceReferences.run( + TraceReferencesCommand.parse( + new String[] { + "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(), + "--target", targetJar.toString(), + "--source", sourceJar.toString(), + "--output", output.toString(), + "--format", formatName(format) + }, + Origin.unknown()) + .build()); + assertEquals(expected, FileUtils.readTextFile(output, Charsets.UTF_8)); + } + + public void runAndCheckOutput( + List<Class<?>> targetClasses, + List<Class<?>> sourceClasses, + OutputFormat format, + String expected) + throws Throwable { + Path dir = temp.newFolder().toPath(); + Path targetJar = dir.resolve("target.jar"); + Path sourceJar = dir.resolve("source.jar"); + ZipUtils.zip( + targetJar, + ToolHelper.getClassPathForTests(), + targetClasses.stream() + .map(ToolHelper::getClassFileForTestClass) + .collect(Collectors.toList())); + ZipUtils.zip( + sourceJar, + ToolHelper.getClassPathForTests(), + sourceClasses.stream() + .map(ToolHelper::getClassFileForTestClass) + .collect(Collectors.toList())); + runAndCheckOutput(targetJar, sourceJar, format, expected); + } + + @Test + public void test_printUses() throws Throwable { + runAndCheckOutput( + ImmutableList.of(Target.class), + ImmutableList.of(Source.class), + TraceReferencesCommandParser.OutputFormat.PRINTUSAGE, + StringUtils.lines( + ImmutableList.of( + "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target", + "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: void" + + " target(int)", + "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: int" + + " field"))); + } + + @Test + public void test_keepRules() throws Throwable { + runAndCheckOutput( + ImmutableList.of(Target.class), + ImmutableList.of(Source.class), + TraceReferencesCommandParser.OutputFormat.KEEP_RULES, + StringUtils.lines( + ImmutableList.of( + "-keep class com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target" + + " {", + " public static void target(int);", + " int field;", + "}", + "-keeppackagenames com.android.tools.r8.tracereferences"))); + } + + @Test + public void test_keepRulesAllowObfuscation() throws Throwable { + runAndCheckOutput( + ImmutableList.of(Target.class), + ImmutableList.of(Source.class), + TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION, + StringUtils.lines( + ImmutableList.of( + "-keep,allowobfuscation class" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target {", + " public static void target(int);", + " int field;", + "}", + "-keeppackagenames com.android.tools.r8.tracereferences"))); + } + + @Test + public void testNoReferences_printUses() throws Throwable { + runAndCheckOutput( + ImmutableList.of(OtherTarget.class), + ImmutableList.of(Source.class), + TraceReferencesCommandParser.OutputFormat.PRINTUSAGE, + StringUtils.lines(ImmutableList.of())); + } + + @Test + public void testMissingReference_keepRules() throws Throwable { + runAndCheckOutput( + ImmutableList.of(OtherTarget.class), + ImmutableList.of(Source.class), + TraceReferencesCommandParser.OutputFormat.KEEP_RULES, + StringUtils.lines(ImmutableList.of())); + } + + @Test + public void testNoReferences_keepRulesAllowObfuscation() throws Throwable { + runAndCheckOutput( + ImmutableList.of(OtherTarget.class), + ImmutableList.of(Source.class), + TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION, + StringUtils.lines(ImmutableList.of())); + } + + public static void zip(Path zipFile, String path, byte[] data) throws IOException { + try (ZipOutputStream stream = + new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zipFile)))) { + ZipEntry zipEntry = new ZipEntry(path); + stream.putNextEntry(zipEntry); + stream.write(data); + stream.closeEntry(); + } + } + + @Test + public void testMissingDefinition_printUses() throws Throwable { + Path dir = temp.newFolder().toPath(); + Path targetJar = dir.resolve("target.jar"); + Path sourceJar = dir.resolve("source.jar"); + zip(targetJar, DescriptorUtils.getPathFromJavaType(Target.class), getClassWithTargetRemoved()); + ZipUtils.zip( + sourceJar, + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(Source.class)); + runAndCheckOutput( + targetJar, + sourceJar, + TraceReferencesCommandParser.OutputFormat.PRINTUSAGE, + StringUtils.lines( + ImmutableList.of( + "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target", + "# Error: Could not find definition for method void" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target" + + ".target(int)", + "# Error: Could not find definition for field int" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target" + + ".field"))); + } + + @Test + public void testMissingDefinition_keepRules() throws Throwable { + Path dir = temp.newFolder().toPath(); + Path targetJar = dir.resolve("target.jar"); + Path sourceJar = dir.resolve("source.jar"); + zip(targetJar, DescriptorUtils.getPathFromJavaType(Target.class), getClassWithTargetRemoved()); + ZipUtils.zip( + sourceJar, + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(Source.class)); + runAndCheckOutput( + targetJar, + sourceJar, + TraceReferencesCommandParser.OutputFormat.KEEP_RULES, + StringUtils.lines( + ImmutableList.of( + "-keep class com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target" + + " {", + "# Error: Could not find definition for method void" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target" + + ".target(int)", + "# Error: Could not find definition for field int" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target" + + ".field", + "}", + "-keeppackagenames com.android.tools.r8.tracereferences"))); + } + + @Test + public void testMissingDefinition_keepRulesAllowObfuscation() throws Throwable { + Path dir = temp.newFolder().toPath(); + Path targetJar = dir.resolve("target.jar"); + Path sourceJar = dir.resolve("source.jar"); + zip(targetJar, DescriptorUtils.getPathFromJavaType(Target.class), getClassWithTargetRemoved()); + ZipUtils.zip( + sourceJar, + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(Source.class)); + runAndCheckOutput( + targetJar, + sourceJar, + TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION, + StringUtils.lines( + ImmutableList.of( + "-keep,allowobfuscation class" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target {", + "# Error: Could not find definition for method void" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target.target(int)", + "# Error: Could not find definition for field int" + + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target.field", + "}", + "-keeppackagenames com.android.tools.r8.tracereferences"))); + } + + private byte[] getClassWithTargetRemoved() throws IOException { + return transformer(Target.class) + .removeMethods((access, name, descriptor, signature, exceptions) -> name.equals("target")) + .removeFields((access, name, descriptor, signature, value) -> name.equals("field")) + .transform(); + } + + static class Target { + public static int field; + + public static void target(int i) {} + } + + static class OtherTarget { + public static void target() {} + } + + static class Source { + public static void source() { + Target.target(Target.field); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java index 84621b6..fa627ef 100644 --- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java +++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -13,15 +13,18 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.AccessFlags; import com.android.tools.r8.graph.ClassAccessFlags; +import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.references.ClassReference; +import com.android.tools.r8.references.FieldReference; import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.transformers.MethodTransformer.MethodContext; import com.android.tools.r8.utils.DescriptorUtils; import java.io.IOException; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -364,6 +367,16 @@ return setAccessFlags(method, MethodAccessFlags::setBridge); } + public ClassFileTransformer setPrivate(Field field) { + return setAccessFlags( + field, + accessFlags -> { + accessFlags.setPrivate(); + accessFlags.unsetProtected(); + accessFlags.unsetPublic(); + }); + } + public ClassFileTransformer setPublic(Method method) { return setAccessFlags( method, @@ -393,11 +406,34 @@ return setAccessFlags(Reference.methodFromMethod(constructor), setter); } + public ClassFileTransformer setAccessFlags(Field field, Consumer<FieldAccessFlags> setter) { + return setAccessFlags(Reference.fieldFromField(field), setter); + } + public ClassFileTransformer setAccessFlags(Method method, Consumer<MethodAccessFlags> setter) { return setAccessFlags(Reference.methodFromMethod(method), setter); } private ClassFileTransformer setAccessFlags( + FieldReference fieldReference, Consumer<FieldAccessFlags> setter) { + return addClassTransformer( + new ClassTransformer() { + + @Override + public FieldVisitor visitField( + int access, String name, String descriptor, String signature, Object value) { + FieldAccessFlags accessFlags = FieldAccessFlags.fromCfAccessFlags(access); + if (name.equals(fieldReference.getFieldName()) + && descriptor.equals(fieldReference.getFieldType().getDescriptor())) { + setter.accept(accessFlags); + } + return super.visitField( + accessFlags.getAsCfAccessFlags(), name, descriptor, signature, value); + } + }); + } + + private ClassFileTransformer setAccessFlags( MethodReference methodReference, Consumer<MethodAccessFlags> setter) { return addClassTransformer( new ClassTransformer() { @@ -425,6 +461,11 @@ boolean test(int access, String name, String descriptor, String signature, String[] exceptions); } + @FunctionalInterface + public interface FieldPredicate { + boolean test(int access, String name, String descriptor, String signature, Object value); + } + public ClassFileTransformer removeInnerClasses() { return addClassTransformer( new ClassTransformer() { @@ -460,6 +501,19 @@ }); } + public ClassFileTransformer removeFields(FieldPredicate predicate) { + return addClassTransformer( + new ClassTransformer() { + @Override + public FieldVisitor visitField( + int access, String name, String descriptor, String signature, Object value) { + return predicate.test(access, name, descriptor, signature, value) + ? null + : super.visitField(access, name, descriptor, signature, value); + } + }); + } + /** Abstraction of the MethodVisitor.visitMethodInsn method with its sub visitor. */ @FunctionalInterface public interface MethodInsnTransform {
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 4f4c340..934c06f 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
@@ -15,6 +15,7 @@ import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.retrace.RetraceMethodResult; import com.android.tools.r8.retrace.RetraceMethodResult.Element; +import com.android.tools.r8.retrace.RetracedMethod; import com.android.tools.r8.utils.Box; import com.android.tools.r8.utils.Visibility; import com.google.common.collect.ImmutableList; @@ -344,7 +345,7 @@ .appendValue(subject.getOriginalName()) .appendText(" was "); if (subject.isPresent()) { - AccessFlags accessFlags = + AccessFlags<?> accessFlags = subject.isMethodSubject() ? subject.asMethodSubject().getMethod().accessFlags : subject.asFieldSubject().getField().accessFlags; @@ -463,7 +464,12 @@ returnValue.set(false); return; } - sameMethod = element.getMethodReference().equals(currentInline.methodReference); + sameMethod = + element.getMethod().isKnown() + && element + .getMethod() + .asKnown() + .equalsMethodReference(currentInline.methodReference); boolean samePosition = element.getOriginalLineNumber(currentInline.minifiedPosition) == currentInline.originalPosition; @@ -495,7 +501,7 @@ for (int i = 0; i < retraceElements.size(); i++) { Element retraceElement = retraceElements.get(i); StackTraceLine stackTraceLine = stackTrace.get(i); - MethodReference methodReference = retraceElement.getMethodReference(); + RetracedMethod methodReference = retraceElement.getMethod(); if (!stackTraceLine.methodName.equals(methodReference.getMethodName()) || !stackTraceLine.className.equals(methodReference.getHolderClass().getTypeName()) || stackTraceLine.lineNumber
diff --git a/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java b/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java index 8d6dd4b..b08651f 100644 --- a/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java +++ b/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java
@@ -31,8 +31,8 @@ import com.google.common.collect.ImmutableSet; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; -import java.util.IdentityHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -629,9 +629,9 @@ this.inspector = inspector; Set<GraphNode> targets = consumer.getTargets(); - classes = new IdentityHashMap<>(targets.size()); - methods = new IdentityHashMap<>(targets.size()); - fields = new IdentityHashMap<>(targets.size()); + classes = new HashMap<>(targets.size()); + methods = new HashMap<>(targets.size()); + fields = new HashMap<>(targets.size()); for (GraphNode target : targets) { if (target instanceof ClassGraphNode) {
diff --git a/tools/create_art_tests.py b/tools/create_art_tests.py index 248f893..978b086 100755 --- a/tools/create_art_tests.py +++ b/tools/create_art_tests.py
@@ -24,24 +24,38 @@ import static com.android.tools.r8.R8RunArtTestsTest.DexTool.$testGeneratingToolchainEnum; import com.android.tools.r8.R8RunArtTestsTest; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; /** * Auto-generated test for the art $name test using the $testGeneratingToolchain toolchain. * * DO NOT EDIT THIS FILE. EDIT THE HERE DOCUMENT TEMPLATE IN tools/create_art_tests.py INSTEAD! */ +@RunWith(Parameterized.class) public class $testClassName extends R8RunArtTestsTest { - public $testClassName() { + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return TestBase.getTestParameters().withDexRuntimes().build(); + } + + private final TestParameters parameters; + + public $testClassName(TestParameters parameters) { super("$name", $testGeneratingToolchainEnum); + this.parameters = parameters; } @Test - public void run$testClassName() throws Throwable { - // For testing with other Art VMs than the default pass the VM version as a argument to - // runArtTest, e.g. runArtTest(DexVm.ART_4_4_4_HOST, CompilerUnderTest.$compilerUnderTestEnum). - runArtTest(CompilerUnderTest.$compilerUnderTestEnum); + public void test() throws Throwable { + runArtTest(parameters.getRuntime().asDex().getVm(), CompilerUnderTest.$compilerUnderTestEnum); } } """)
diff --git a/tools/r8_release.py b/tools/r8_release.py index 7f27da8..a048c5c 100755 --- a/tools/r8_release.py +++ b/tools/r8_release.py
@@ -7,14 +7,12 @@ import datetime import os.path import re -import shutil import subprocess import sys import urllib import xml -import xml.etree.ElementTree as et import zipfile -import archive_desugar_jdk_libs + import utils R8_DEV_BRANCH = '2.2' @@ -248,17 +246,18 @@ return release_maven -def git_message_dev(version): +# ------------------------------------------------------ column 70 --v +def git_message_dev(version, bugs): return """Update D8 R8 to %s -This is a development snapshot, it's fine to use for studio canary build, but -not for BETA or release, for those we would need a release version of R8 -binaries. -This build IS suitable for preview release but IS NOT suitable for public release. +This is a development snapshot, it's fine to use for studio canary +build, but not for BETA or release, for those we would need a release +version of R8 binaries. This build IS suitable for preview release +but IS NOT suitable for public release. Built here: go/r8-releases/raw/%s Test: ./gradlew check -Bug: """ % (version, version) +Bug: %s""" % (version, version, '\nBug: '.join(bugs)) def git_message_release(version, bugs): @@ -267,7 +266,7 @@ Built here: go/r8-releases/raw/%s/ Test: ./gradlew check -Bug: %s """ % (version, version, '\nBug: '.join(bugs)) +Bug: %s""" % (version, version, '\nBug: '.join(bugs)) def prepare_studio(args): @@ -281,7 +280,7 @@ return 'DryRun: omitting studio release for %s' % options.version if 'dev' in options.version: - git_message = git_message_dev(options.version) + git_message = git_message_dev(options.version, options.bug) r8_checkout = utils.REPO_ROOT return release_studio_or_aosp( r8_checkout, args.studio, options, git_message)
diff --git a/tools/test.py b/tools/test.py index fbc1985..27f15a8 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -163,6 +163,11 @@ (options, args) = ParseOptions() if utils.is_bot(): + if options.horizontal_class_merging: + # This flag is in preparation of running horizontal class merging + # but currently is the same as the default tests. Don't run to + # save resources on the bots. + return 0 gradle.RunGradle(['--no-daemon', 'clean']) gradle_args = ['--stacktrace'] @@ -291,6 +296,9 @@ return_code = gradle.RunGradle(gradle_args, throw_on_failure=False) return archive_and_return(return_code, options) + if options.horizontal_class_merging: + gradle_args.append('-PhorizontalClassMerging') + # Now run tests on selected runtime(s). if options.runtimes: if options.dex_vm != 'default':