Version 1.7.1-dev
Merge commit '706f75519ffa158611a9934893ba79ad638b9b81' into 1.7
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index ef7e2a6..52963e9 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.7.0-dev";
+ public static final String LABEL = "1.7.1-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java b/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
index 14833f5..f645d0b 100644
--- a/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
+++ b/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
@@ -31,6 +31,8 @@
IsLibraryMethod,
OverridingMethod,
MethodHandleUseFrom,
+ CompanionClass,
+ CompanionMethod,
Unknown
}
@@ -79,6 +81,10 @@
return "defined in library method overridden by";
case MethodHandleUseFrom:
return "referenced by method handle";
+ case CompanionClass:
+ return "companion class for";
+ case CompanionMethod:
+ return "companion method for";
default:
assert false : "Unknown edge kind: " + edgeKind();
// fall through
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 8b2e770..e77f932 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -58,6 +58,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
@@ -67,6 +68,7 @@
import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
+import com.android.tools.r8.utils.DequeUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -322,6 +324,7 @@
private final GraphReporter graphReporter;
private final GraphConsumer keptGraphConsumer;
+ private CollectingGraphConsumer verificationGraphConsumer = null;
Enqueuer(
AppView<? extends AppInfoWithSubtyping> appView,
@@ -335,7 +338,7 @@
this.compatibility = compatibility;
this.forceProguardCompatibility = options.forceProguardCompatibility;
this.graphReporter = new GraphReporter();
- this.keptGraphConsumer = keptGraphConsumer;
+ this.keptGraphConsumer = recordKeptGraph(options, keptGraphConsumer);
this.mode = mode;
this.options = options;
this.workList = EnqueuerWorklist.createWorklist(appView);
@@ -1669,8 +1672,10 @@
private void markStaticFieldAsLive(DexEncodedField encodedField, KeepReason reason) {
// Mark the type live here, so that the class exists at runtime.
DexField field = encodedField.field;
- markTypeAsLive(field.holder, reason);
- markTypeAsLive(field.type, reason);
+ markTypeAsLive(
+ field.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
+ markTypeAsLive(
+ field.type, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
DexProgramClass clazz = getProgramClassOrNull(field.holder);
if (clazz == null) {
@@ -1804,8 +1809,10 @@
Log.verbose(getClass(), "Marking instance field `%s` as reachable.", field);
}
- markTypeAsLive(field.holder, reason);
- markTypeAsLive(field.type, reason);
+ markTypeAsLive(
+ field.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
+ markTypeAsLive(
+ field.type, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
DexProgramClass clazz = getProgramClassOrNull(field.holder);
if (clazz == null) {
@@ -1820,7 +1827,6 @@
markStaticFieldAsLive(encodedField, reason);
} else {
if (isInstantiatedOrHasInstantiatedSubtype(clazz)) {
- // We have at least one live subtype, so mark it as live.
markInstanceFieldAsLive(encodedField, reason);
} else {
// Add the field to the reachable set if the type later becomes instantiated.
@@ -2110,9 +2116,51 @@
trace(executorService, timing);
options.reporter.failIfPendingErrors();
analyses.forEach(EnqueuerAnalysis::done);
+ assert verifyKeptGraph();
return createAppInfo(appInfo);
}
+ private GraphConsumer recordKeptGraph(InternalOptions options, GraphConsumer consumer) {
+ if (options.testing.verifyKeptGraphInfo) {
+ verificationGraphConsumer = new CollectingGraphConsumer(consumer);
+ return verificationGraphConsumer;
+ }
+ return consumer;
+ }
+
+ private boolean verifyKeptGraph() {
+ if (options.testing.verifyKeptGraphInfo) {
+ assert verificationGraphConsumer != null;
+ for (DexProgramClass liveType : liveTypes.items) {
+ assert verifyRootedPath(liveType, verificationGraphConsumer);
+ }
+ }
+ return true;
+ }
+
+ private boolean verifyRootedPath(DexProgramClass liveType, CollectingGraphConsumer graph) {
+ ClassGraphNode node = getClassGraphNode(liveType.type);
+ Set<GraphNode> seen = Sets.newIdentityHashSet();
+ Deque<GraphNode> targets = DequeUtils.newArrayDeque(node);
+ while (!targets.isEmpty()) {
+ GraphNode item = targets.pop();
+ if (item instanceof KeepRuleGraphNode) {
+ KeepRuleGraphNode rule = (KeepRuleGraphNode) item;
+ if (rule.getPreconditions().isEmpty()) {
+ return true;
+ }
+ }
+ if (seen.add(item)) {
+ Map<GraphNode, Set<GraphEdgeInfo>> sources = graph.getSourcesTargeting(item);
+ assert sources != null : "No sources set for " + item;
+ assert !sources.isEmpty() : "Empty sources set for " + item;
+ targets.addAll(sources.keySet());
+ }
+ }
+ assert false : "No rooted path to " + liveType.type;
+ return true;
+ }
+
private AppInfoWithLiveness createAppInfo(AppInfoWithSubtyping appInfo) {
ImmutableSortedSet.Builder<DexType> builder =
ImmutableSortedSet.orderedBy(PresortedComparable::slowCompareTo);
@@ -2367,8 +2415,11 @@
DexEncodedMethod implementation = target.getDefaultInterfaceMethodImplementation();
if (implementation != null) {
DexProgramClass companion = getProgramClassOrNull(implementation.method.holder);
- markTypeAsLive(companion, reason);
- markVirtualMethodAsLive(companion, implementation, reason);
+ markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion));
+ markVirtualMethodAsLive(
+ companion,
+ implementation,
+ graphReporter.reportCompanionMethod(target, implementation));
}
}
}
@@ -3128,6 +3179,40 @@
return KeepReasonWitness.INSTANCE;
}
+ public KeepReasonWitness reportClassReferencedFrom(
+ DexProgramClass clazz, DexEncodedField field) {
+ if (keptGraphConsumer != null) {
+ FieldGraphNode source = getFieldGraphNode(field.field);
+ ClassGraphNode target = getClassGraphNode(clazz.type);
+ return reportEdge(source, target, EdgeKind.ReferencedFrom);
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ private KeepReason reportCompanionClass(DexProgramClass iface, DexProgramClass companion) {
+ assert iface.isInterface();
+ assert InterfaceMethodRewriter.isCompanionClassType(companion.type);
+ if (keptGraphConsumer == null) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return reportEdge(
+ getClassGraphNode(iface.type),
+ getClassGraphNode(companion.type),
+ EdgeKind.CompanionClass);
+ }
+
+ private KeepReason reportCompanionMethod(
+ DexEncodedMethod definition, DexEncodedMethod implementation) {
+ assert InterfaceMethodRewriter.isCompanionClassType(implementation.method.holder);
+ if (keptGraphConsumer == null) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return reportEdge(
+ getMethodGraphNode(definition.method),
+ getMethodGraphNode(implementation.method),
+ EdgeKind.CompanionMethod);
+ }
+
private KeepReasonWitness reportEdge(
GraphNode source, GraphNode target, GraphEdgeInfo.EdgeKind kind) {
assert keptGraphConsumer != null;
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 d202fd8..2e45d5f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -972,6 +972,7 @@
// Flag to turn on/off JDK11+ nest-access control even when not required (Cf backend)
public boolean enableForceNestBasedAccessDesugaringForTest = false;
+ public boolean verifyKeptGraphInfo = false;
public boolean desugarLambdasThroughLensCodeRewriter() {
return enableStatefulLambdaCreateInstanceMethod;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
index c3dd579..4dba762 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
@@ -84,6 +84,10 @@
private void configure(InternalOptions options) {
options.enableVerticalClassMerging = enableVerticalClassMerging;
+ // TODO(b/141093535): The precondition set for conditionals is currently based on the syntactic
+ // form, when merging is enabled, if the precondition is merged to a differently named type, the
+ // rule will still fire, but the reported precondition type is incorrect.
+ options.testing.verifyKeptGraphInfo = !enableVerticalClassMerging;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
index 0c06960..a43e42c 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
@@ -82,10 +82,11 @@
inspector.method(mainMethod).assertNotRenamed().assertKeptBy(keepMain);
// The field is primarily kept by the reflective lookup in main.
- inspector.field(fooField).assertRenamed().assertReflectedFrom(mainMethod);
+ QueryNode fooNode = inspector.field(fooField).assertRenamed();
+ fooNode.assertReflectedFrom(mainMethod);
// The field is also kept by the write in Foo.<init>.
// We may want to change that behavior. See b/124428834.
- inspector.field(fooField).assertRenamed().assertKeptBy(inspector.method(fooInit));
+ fooNode.assertKeptBy(inspector.method(fooInit));
}
}
diff --git a/tools/archive.py b/tools/archive.py
index 32c5c32..1041b40 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -59,18 +59,18 @@
# commits to archive these to the hash based location
if len(branches) == 0:
return True
- if not version.endswith('-dev'):
+ if not version == 'master':
# Sanity check, we don't want to archive on top of release builds EVER
# Note that even though we branch, we never push the bots to build the same
# commit as master on a branch since we always change the version to
- # not have dev (or we crash here :-)).
+ # not be just 'master' (or we crash here :-)).
if 'origin/master' in branches:
raise Exception('We are seeing origin/master in a commit that '
- 'don\'t have -dev in version')
+ 'don\'t have \'master\' as version')
return False
if not 'origin/master' in branches:
raise Exception('We are not seeing origin/master '
- 'in a commit that have -dev in version')
+ 'in a commit that have \'master\' as version')
return True
def GetStorageDestination(storage_prefix,
@@ -239,9 +239,10 @@
if options.dry_run:
print('Dry run, not actually creating maven repo for '
+ 'desugar configuration.')
- shutil.copyfile(
- desugar_jdk_libs_configuration_jar,
- os.path.join(options.dry_run_output, jar_name))
+ if options.dry_run_output:
+ shutil.copyfile(
+ desugar_jdk_libs_configuration_jar,
+ os.path.join(options.dry_run_output, jar_name))
else:
utils.upload_file_to_cloud_storage(
desugar_jdk_libs_configuration_jar, maven_dst)