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':