Merge commit 'c689bb19cc1b3adface24e5f156081ce3cbece52' into dev-release
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 3c31451..fcf4105 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -127,6 +127,7 @@
id: "linux"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -140,6 +141,7 @@
id: "linux_horizontal"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 3
}
buildbucket {
@@ -153,6 +155,7 @@
id: "linux-android-4.0.4"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -180,6 +183,7 @@
id: "linux-android-4.4.4"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -207,6 +211,7 @@
id: "linux-android-5.1.1"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -234,6 +239,7 @@
id: "linux-android-6.0.1"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -261,6 +267,7 @@
id: "linux-android-7.0.0"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -288,6 +295,7 @@
id: "linux-android-8.1.0"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -316,6 +324,7 @@
id: "linux-android-9.0.0"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -343,6 +352,7 @@
id: "linux-android-10.0.0"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 6
}
buildbucket {
@@ -390,6 +400,7 @@
id: "linux-run-on-as-app"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 3
}
buildbucket {
@@ -403,6 +414,7 @@
id: "linux-run-on-as-app-recompilation"
acl_sets: "default"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 3
}
buildbucket {
@@ -430,6 +442,9 @@
job {
id: "linux-jctf"
acl_sets: "default"
+ triggering_policy: {
+ kind: GREEDY_BATCHING
+ }
buildbucket {
server: "cr-buildbucket.appspot.com"
bucket: "luci.r8.ci"
@@ -467,6 +482,9 @@
job {
id: "r8cf-linux-jctf"
acl_sets: "default"
+ triggering_policy: {
+ kind: GREEDY_BATCHING
+ }
buildbucket {
server: "cr-buildbucket.appspot.com"
bucket: "luci.r8.ci"
@@ -490,6 +508,7 @@
job {
id: "windows"
triggering_policy: {
+ kind: GREEDY_BATCHING
max_concurrent_invocations: 3
}
acl_sets: "default"
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 6a2a8c5..f3acbec 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfThrow;
@@ -148,7 +149,7 @@
ParameterAnnotationsList.empty(),
code,
false,
- 50,
+ CfVersion.V1_6,
false);
if (method.isStatic() || method.isDirectMethod()) {
directMethods.add(throwingMethod);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ab78b10..5e8e3a6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -85,7 +85,6 @@
import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ClassInitFieldSynthesizer;
-import com.android.tools.r8.shaking.ClassMergingEnqueuerExtension;
import com.android.tools.r8.shaking.DefaultTreePrunerConfiguration;
import com.android.tools.r8.shaking.DiscardedChecker;
import com.android.tools.r8.shaking.Enqueuer;
@@ -99,6 +98,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationUtils;
import com.android.tools.r8.shaking.RootSetBuilder;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.android.tools.r8.shaking.StaticClassMerger;
import com.android.tools.r8.shaking.TreePruner;
import com.android.tools.r8.shaking.TreePrunerConfiguration;
@@ -324,8 +324,8 @@
timing.begin("Strip unused code");
Set<DexType> classesToRetainInnerClassAttributeFor = null;
Set<DexType> missingClasses = null;
- ClassMergingEnqueuerExtension classMergingEnqueuerExtension =
- new ClassMergingEnqueuerExtension(appView.dexItemFactory());
+ RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder =
+ new RuntimeTypeCheckInfo.Builder(appView.dexItemFactory());
try {
// TODO(b/154849103): Find a better way to determine missing classes.
missingClasses = new SubtypingInfo(appView).getMissingClasses();
@@ -379,7 +379,8 @@
executorService,
appView,
subtypingInfo,
- classMergingEnqueuerExtension);
+ classMergingEnqueuerExtensionBuilder);
+
assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness.appInfo());
@@ -428,6 +429,8 @@
annotationRemover.ensureValid().run();
classesToRetainInnerClassAttributeFor =
annotationRemover.getClassesToRetainInnerClassAttributeFor();
+ new GenericSignatureRewriter(appView, NamingLens.getIdentityLens())
+ .run(appView.appInfo().classes(), executorService);
}
} finally {
timing.end();
@@ -481,7 +484,7 @@
}
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- appView.setGraphLens(new MemberRebindingAnalysis(appViewWithLiveness).run());
+ appView.setGraphLens(new MemberRebindingAnalysis(appViewWithLiveness).run(executorService));
appView.appInfo().withLiveness().getFieldAccessInfoCollection().restrictToProgram(appView);
if (options.shouldDesugarNests()) {
@@ -506,6 +509,7 @@
boolean isKotlinLibraryCompilationWithInlinePassThrough =
options.enableCfByteCodePassThrough && appView.hasCfByteCodePassThroughMethods();
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo = classMergingEnqueuerExtensionBuilder.build();
if (!isKotlinLibraryCompilationWithInlinePassThrough
&& options.getProguardConfiguration().isOptimizing()) {
if (options.enableStaticClassMerging) {
@@ -529,6 +533,7 @@
if (lens != null) {
appView.setVerticallyMergedClasses(verticalClassMerger.getMergedClasses());
appView.rewriteWithLens(lens);
+ runtimeTypeCheckInfo = runtimeTypeCheckInfo.rewriteWithLens(lens);
}
timing.end();
}
@@ -564,7 +569,7 @@
timing.begin("HorizontalClassMerger");
HorizontalClassMerger merger =
new HorizontalClassMerger(
- appViewWithLiveness, mainDexTracingResult, classMergingEnqueuerExtension);
+ appViewWithLiveness, mainDexTracingResult, runtimeTypeCheckInfo);
DirectMappedDexApplication.Builder appBuilder =
appView.appInfo().app().asDirect().builder();
HorizontalClassMergerGraphLens lens = merger.run(appBuilder);
@@ -572,12 +577,13 @@
DirectMappedDexApplication app = appBuilder.build();
appView.removePrunedClasses(app, appView.horizontallyMergedClasses().getSources());
appView.rewriteWithLens(lens);
+
+ // Only required for class merging, clear instance to save memory.
+ runtimeTypeCheckInfo = null;
}
timing.end();
}
- // Only required for class merging, clear instance to save memory.
- classMergingEnqueuerExtension = null;
}
// None of the optimizations above should lead to the creation of type lattice elements.
@@ -998,7 +1004,7 @@
ExecutorService executorService,
AppView<AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
- ClassMergingEnqueuerExtension classMergingEnqueuerExtension)
+ RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder)
throws ExecutionException {
Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder);
@@ -1012,7 +1018,7 @@
}
if (options.isClassMergingExtensionRequired()) {
- classMergingEnqueuerExtension.attach(enqueuer);
+ classMergingEnqueuerExtensionBuilder.attach(enqueuer);
}
AppView<AppInfoWithLiveness> appViewWithLiveness =
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
new file mode 100644
index 0000000..84228b9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -0,0 +1,108 @@
+// 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.cf;
+
+import java.util.Comparator;
+import org.objectweb.asm.Opcodes;
+
+public final class CfVersion implements Comparable<CfVersion> {
+
+ public static final CfVersion V1_1 = new CfVersion(Opcodes.V1_1);
+ public static final CfVersion V1_2 = new CfVersion(Opcodes.V1_2);
+ public static final CfVersion V1_3 = new CfVersion(Opcodes.V1_3);
+ public static final CfVersion V1_4 = new CfVersion(Opcodes.V1_4);
+ public static final CfVersion V1_5 = new CfVersion(Opcodes.V1_5);
+ public static final CfVersion V1_6 = new CfVersion(Opcodes.V1_6);
+ public static final CfVersion V1_7 = new CfVersion(Opcodes.V1_7);
+ public static final CfVersion V1_8 = new CfVersion(Opcodes.V1_8);
+ public static final CfVersion V9 = new CfVersion(Opcodes.V9);
+ public static final CfVersion V10 = new CfVersion(Opcodes.V10);
+ public static final CfVersion V11 = new CfVersion(Opcodes.V11);
+
+ private final int version;
+
+ // Private constructor in case we want to canonicalize versions.
+ private CfVersion(int version) {
+ this.version = version;
+ }
+
+ public static CfVersion fromRaw(int rawVersion) {
+ return new CfVersion(rawVersion);
+ }
+
+ public int major() {
+ return version & 0xFFFF;
+ }
+
+ public int minor() {
+ return version >> 16;
+ }
+
+ public int raw() {
+ return version;
+ }
+
+ public static CfVersion maxAllowNull(CfVersion v1, CfVersion v2) {
+ assert v1 != null || v2 != null;
+ if (v1 == null) {
+ return v2;
+ }
+ if (v2 == null) {
+ return v1;
+ }
+ return v1.max(v2);
+ }
+
+ public CfVersion max(CfVersion other) {
+ return isLessThan(other) ? other : this;
+ }
+
+ public boolean isEqual(CfVersion other) {
+ return version == other.version;
+ }
+
+ public boolean isLessThan(CfVersion other) {
+ return compareTo(other) < 0;
+ }
+
+ public boolean isLessThanOrEqual(CfVersion other) {
+ return compareTo(other) <= 0;
+ }
+
+ public boolean isGreaterThan(CfVersion other) {
+ return compareTo(other) > 0;
+ }
+
+ public boolean isGreaterThanOrEqual(CfVersion other) {
+ return compareTo(other) >= 0;
+ }
+
+ @Override
+ public int compareTo(CfVersion o) {
+ return Comparator.comparingInt(CfVersion::major)
+ .thenComparingInt(CfVersion::minor)
+ .compare(this, o);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CfVersion)) {
+ return false;
+ }
+ return isEqual((CfVersion) o);
+ }
+
+ @Override
+ public int hashCode() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ return minor() != 0 ? ("" + major() + "." + minor()) : ("" + major());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index 1328f67..c05c20d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -193,7 +192,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forBinop();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 232f7c1..70b41e1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -68,7 +67,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forArrayLength();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
index 411ea64..ae086f3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -114,7 +113,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forArrayGet();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index 585b4bc..eb43f6b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -104,7 +103,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forArrayPut();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index 4dfa1c5..4661e68 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -86,7 +85,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forCheckCast(type, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
index cc83558..23b0c8b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -118,7 +117,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forBinop();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index 43a3a4f..51f34fc 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -109,7 +108,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forConstClass(type, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 9a22ae7..259a711 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -87,7 +86,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forConstMethodHandle();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index 632a24c..70417f6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
@@ -85,7 +84,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forConstMethodType();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index f6a720d..1835f18 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -60,7 +59,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forConstInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index 6f7fcf4..e48643e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -161,7 +160,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forConstInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index 1c9305b..ee042dc 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
@@ -90,7 +89,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forConstInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index eed2ef1..e9f11a8 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-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.GraphLens;
@@ -107,7 +106,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forDexItemBasedConstString(item, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 30bb5cc..7c28eff 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.DexClassAndMethod;
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.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -154,7 +153,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
switch (opcode) {
case Opcodes.GETSTATIC:
return inliningConstraints.forStaticGet(field, context);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index dfa633c..cdcb3ec 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -440,7 +439,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index f3babee..0867a89 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -85,7 +84,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forJumpInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index 433d5d9..ddca593 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -121,7 +120,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forJumpInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index bf76936..efedff8 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -122,7 +121,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forJumpInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
index 2d0e0f0..9e59ba6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -80,7 +79,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index cb0d50f..e9c06e0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.DexClassAndMethod;
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.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -95,7 +94,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forInitClass(clazz, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index 46d56b4..1300458 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -94,7 +93,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forInstanceOf(type, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 0616bed..b8874e8 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -226,7 +225,7 @@
}
public abstract ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context);
+ InliningConstraints inliningConstraints, ProgramMethod context);
public abstract void evaluate(
CfFrameVerificationHelper frameBuilder,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 3ad8ed2..4765be5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -249,7 +249,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
GraphLens graphLens = inliningConstraints.getGraphLens();
AppView<?> appView = inliningConstraints.getAppView();
DexMethod target = method;
@@ -259,17 +259,17 @@
case Opcodes.INVOKEINTERFACE:
// Could have changed to an invoke-virtual instruction due to vertical class merging
// (if an interface is merged into a class).
- type = graphLens.lookupMethod(target, null, Type.INTERFACE).getType();
+ type = graphLens.lookupMethod(target, context.getReference(), Type.INTERFACE).getType();
assert type == Type.INTERFACE || type == Type.VIRTUAL;
break;
case Opcodes.INVOKESPECIAL:
if (appView.dexItemFactory().isConstructor(target)) {
type = Type.DIRECT;
- assert noNeedToUseGraphLens(target, type, graphLens);
- } else if (target.holder == context.type) {
+ assert noNeedToUseGraphLens(target, context.getReference(), type, graphLens);
+ } else if (target.holder == context.getHolderType()) {
// The method could have been publicized.
- type = graphLens.lookupMethod(target, null, Type.DIRECT).getType();
+ type = graphLens.lookupMethod(target, context.getReference(), Type.DIRECT).getType();
assert type == Type.DIRECT || type == Type.VIRTUAL;
} else {
// This is a super call. Note that the vertical class merger translates some invoke-super
@@ -280,14 +280,15 @@
// TODO(christofferqa): Consider using graphLens.lookupMethod (to do this, we need the
// context for the graph lens, though).
type = Type.SUPER;
- assert noNeedToUseGraphLens(target, type, graphLens);
+ assert noNeedToUseGraphLens(target, context.getReference(), type, graphLens);
}
break;
case Opcodes.INVOKESTATIC:
{
// Static invokes may have changed as a result of horizontal class merging.
- MethodLookupResult lookup = graphLens.lookupMethod(target, null, Type.STATIC);
+ MethodLookupResult lookup =
+ graphLens.lookupMethod(target, context.getReference(), Type.STATIC);
target = lookup.getReference();
type = lookup.getType();
}
@@ -298,7 +299,7 @@
type = Type.VIRTUAL;
// Instructions that target a private method in the same class translates to
// invoke-direct.
- if (target.holder == context.type) {
+ if (target.holder == context.getHolderType()) {
DexClass clazz = appView.definitionFor(target.holder);
if (clazz != null && clazz.lookupDirectMethod(target) != null) {
type = Type.DIRECT;
@@ -306,7 +307,7 @@
}
// Virtual invokes may have changed to interface invokes as a result of member rebinding.
- MethodLookupResult lookup = graphLens.lookupMethod(target, null, type);
+ MethodLookupResult lookup = graphLens.lookupMethod(target, context.getReference(), type);
target = lookup.getReference();
type = lookup.getType();
}
@@ -343,8 +344,8 @@
}
private static boolean noNeedToUseGraphLens(
- DexMethod method, Invoke.Type type, GraphLens graphLens) {
- assert graphLens.lookupMethod(method, null, type).getType() == type;
+ DexMethod method, DexMethod context, Invoke.Type type, GraphLens graphLens) {
+ assert graphLens.lookupMethod(method, context, type).getType() == type;
return true;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index 2651e10..add492c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
@@ -141,7 +140,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forInvokeCustom();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index eee91ae..ed6c16f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -71,7 +70,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
throw error();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index 5701493..2bfdd93 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -83,7 +82,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index 679e75d..3160d6e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -112,7 +111,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forLoad();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index b3b8a3c..d7f2c7d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -165,7 +164,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forBinop();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index c947e17..8225673 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -82,7 +81,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forMonitor();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 2209a80..5f405d3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -95,7 +94,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forInvokeMultiNewArray(type, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
index 6da10c4..a7b8c32 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -107,7 +106,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forUnop();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index 3913b83..60da1b2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -83,7 +82,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forNewInstance(type, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 593abb4..30671bd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -133,7 +132,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forNewArrayEmpty(type, context);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index f30e52e..87123a4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -64,7 +63,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
index abe5ad1..a7e60e4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -178,7 +177,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forUnop();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 2d8c11d..233a3ef 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -94,7 +93,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index 9d8df3b..8305860 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -100,7 +99,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forReturn();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index de43a8d..687d903 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -74,7 +73,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forReturn();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index 109df7f..c28cb32 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -341,7 +340,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index 51bc1b7..debb573 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -112,7 +111,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forStore();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index 9e883cf..ddfd927 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -137,7 +136,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forJumpInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index b00ef94..b165711 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -81,7 +80,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
- InliningConstraints inliningConstraints, DexProgramClass context) {
+ InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forJumpInstruction();
}
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 77a82df..e3801e6 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import com.android.tools.r8.cf.CfVersion;
+
public class Constants {
public static final byte[] DEX_FILE_MAGIC_PREFIX = {'d', 'e', 'x', '\n'};
@@ -16,7 +18,7 @@
public static final int MAX_VDEX_VERSION = 11;
// We apply Java 6 class file constraints on DEX files.
- public static final int CORRESPONDING_CLASS_FILE_VERSION = 50;
+ public static final CfVersion CORRESPONDING_CLASS_FILE_VERSION = CfVersion.V1_6;
public static final int DEX_MAGIC_SIZE = 8;
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index b4852a3..c915fe7 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -27,6 +27,7 @@
public static final String SHA1 = "sha-1";
public static final String COMPILATION_MODE = "compilation-mode";
public static final String HAS_CHECKSUMS = "has-checksums";
+ public static final String BACKEND = "backend";
public static final String PG_MAP_ID = "pg-map-id";
public static final String R8_MODE = "r8-mode";
private static final String NO_LIBRARY_DESUGARING = "<no-library-desugaring>";
@@ -42,6 +43,11 @@
}
}
+ public enum Backend {
+ CF,
+ DEX
+ }
+
private static final char PREFIX_CHAR = '~';
private static final String PREFIX = "~~";
private static final String D8_PREFIX = PREFIX + Tool.D8 + "{";
@@ -198,6 +204,24 @@
return this;
}
+ public boolean hasBackend() {
+ return jsonObject.has(BACKEND);
+ }
+
+ public String getBackend() {
+ if (!hasBackend()) {
+ // Before adding backend we would always compile to dex if min-api was specified.
+ return hasMinApi() ? "dex" : "cf";
+ }
+ return jsonObject.get(BACKEND).getAsString();
+ }
+
+ public Marker setBackend(Backend backend) {
+ assert !hasBackend();
+ jsonObject.addProperty(BACKEND, backend.name().toLowerCase());
+ return this;
+ }
+
public boolean getHasChecksums() {
return jsonObject.get(HAS_CHECKSUMS).getAsBoolean();
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index a7bd6c1..877f2ba 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
+import com.android.tools.r8.optimize.MemberRebindingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
@@ -598,17 +599,26 @@
assert application.verifyWithLens(lens);
// The application has already been rewritten with the given applied lens. Therefore, we
- // temporarily replace that lens with the identity lens to avoid the overhead of traversing
- // the entire lens chain upon each lookup during the rewriting.
- NonIdentityGraphLens temporaryRootLens = lens;
- while (temporaryRootLens.getPrevious() != appliedLens) {
- GraphLens previousLens = temporaryRootLens.getPrevious();
+ // temporarily replace that lens with a lens that does not have any rewritings to avoid the
+ // overhead of traversing the entire lens chain upon each lookup during the rewriting.
+ NonIdentityGraphLens firstUnappliedLens = lens;
+ while (firstUnappliedLens.getPrevious() != appliedLens) {
+ GraphLens previousLens = firstUnappliedLens.getPrevious();
assert previousLens.isNonIdentityLens();
- temporaryRootLens = previousLens.asNonIdentityLens();
+ firstUnappliedLens = previousLens.asNonIdentityLens();
}
- temporaryRootLens.withAlternativeParentLens(
- GraphLens.getIdentityLens(),
+ // Insert a member rebinding lens above the first unapplied lens.
+ MemberRebindingLens appliedMemberRebindingLens =
+ firstUnappliedLens.findPrevious(GraphLens::isMemberRebindingLens);
+ GraphLens newMemberRebindingLens =
+ appliedMemberRebindingLens != null
+ ? appliedMemberRebindingLens.toRewrittenFieldRebindingLens(
+ appView.dexItemFactory(), appliedLens)
+ : GraphLens.getIdentityLens();
+
+ firstUnappliedLens.withAlternativeParentLens(
+ newMemberRebindingLens,
() -> {
appView.setAppInfo(appView.appInfo().rewrittenWithLens(application, lens));
appView.setAppServices(appView.appServices().rewrittenWithLens(lens));
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index a08d65e..e08f5da 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -7,10 +7,9 @@
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX;
import static com.android.tools.r8.ir.conversion.CfSourceCode.canThrowHelper;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
-import static org.objectweb.asm.Opcodes.V1_5;
-import static org.objectweb.asm.Opcodes.V1_6;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.cf.code.CfFrameVerificationHelper;
@@ -285,7 +284,7 @@
public void write(
ProgramMethod method,
- int classFileVersion,
+ CfVersion classFileVersion,
AppView<?> appView,
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
@@ -305,8 +304,9 @@
}
for (CfInstruction instruction : instructions) {
if (instruction instanceof CfFrame
- && (classFileVersion <= V1_5
- || (classFileVersion == V1_6 && !options.shouldKeepStackMapTable()))) {
+ && (classFileVersion.isLessThan(CfVersion.V1_6)
+ || (classFileVersion.isEqual(CfVersion.V1_6)
+ && !options.shouldKeepStackMapTable()))) {
continue;
}
instruction.write(
@@ -618,7 +618,7 @@
ProgramMethod method,
AppView<AppInfoWithLiveness> appView,
GraphLens graphLens,
- DexProgramClass context) {
+ ProgramMethod context) {
InliningConstraints inliningConstraints = new InliningConstraints(appView, graphLens);
if (appView.options().isInterfaceMethodDesugaringEnabled()) {
// TODO(b/120130831): Conservatively need to say "no" at this point if there are invocations
@@ -687,7 +687,7 @@
stackMapStatus = StackMapStatus.INVALID_OR_NOT_PRESENT;
return true;
}
- if (method.hasClassFileVersion() && method.getClassFileVersion() <= V1_6) {
+ if (method.hasClassFileVersion() && method.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
stackMapStatus = StackMapStatus.INVALID_OR_NOT_PRESENT;
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 36f43ba..e3584ec 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -105,16 +106,17 @@
* Checks whether the constraints from
* https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
*/
- public boolean areValid(int majorVersion, boolean isPackageInfo) {
+ public boolean areValid(CfVersion version, boolean isPackageInfo) {
if (isInterface()) {
// We ignore the super flags prior to JDK 9, as so did the VM.
- if ((majorVersion >= 53) && isSuper()) {
+ if (version.isGreaterThanOrEqual(CfVersion.V9) && isSuper()) {
return false;
}
// When not coming from DEX input we require interfaces to be abstract - except for
// package-info classes - as both old versions of javac and other tools can produce
// package-info classes that are interfaces but not abstract.
- if (((majorVersion > Constants.CORRESPONDING_CLASS_FILE_VERSION) && !isAbstract())
+ if (version.isGreaterThanOrEqual(Constants.CORRESPONDING_CLASS_FILE_VERSION)
+ && !isAbstract()
&& !isPackageInfo) {
return false;
}
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 a972aed..ea4103f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -12,6 +12,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
@@ -67,6 +68,7 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
@@ -149,7 +151,7 @@
private CompilationState compilationState = CompilationState.NOT_PROCESSED;
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.bottom();
- private int classFileVersion;
+ private CfVersion classFileVersion = null;
private KotlinMethodLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
/** Generic signature information if the attribute is present in the input */
private MethodTypeSignature genericSignature;
@@ -241,7 +243,7 @@
parameterAnnotationsList,
code,
false,
- -1);
+ null);
}
public DexEncodedMethod(
@@ -260,7 +262,7 @@
parameterAnnotationsList,
code,
d8R8Synthesized,
- -1);
+ null);
}
public DexEncodedMethod(
@@ -271,7 +273,7 @@
ParameterAnnotationsList parameterAnnotationsList,
Code code,
boolean d8R8Synthesized,
- int classFileVersion) {
+ CfVersion classFileVersion) {
this(
method,
accessFlags,
@@ -292,7 +294,7 @@
ParameterAnnotationsList parameterAnnotationsList,
Code code,
boolean d8R8Synthesized,
- int classFileVersion,
+ CfVersion classFileVersion,
boolean deprecated) {
super(annotations);
this.method = method;
@@ -783,21 +785,21 @@
code = null;
}
- public int getClassFileVersion() {
+ public CfVersion getClassFileVersion() {
checkIfObsolete();
- assert classFileVersion >= 0;
+ assert classFileVersion != null;
return classFileVersion;
}
public boolean hasClassFileVersion() {
checkIfObsolete();
- return classFileVersion >= 0;
+ return classFileVersion != null;
}
- public void upgradeClassFileVersion(int version) {
+ public void upgradeClassFileVersion(CfVersion version) {
checkIfObsolete();
- assert version >= 0;
- classFileVersion = Math.max(classFileVersion, version);
+ assert version != null;
+ classFileVersion = CfVersion.maxAllowNull(classFileVersion, version);
}
public String qualifiedName() {
@@ -1366,6 +1368,10 @@
return method;
}
+ public MethodPosition getPosition() {
+ return new MethodPosition(method.asMethodReference());
+ }
+
@Override
public boolean isDexEncodedMethod() {
checkIfObsolete();
@@ -1423,7 +1429,7 @@
public void copyMetadata(DexEncodedMethod from) {
checkIfObsolete();
- if (from.classFileVersion > classFileVersion) {
+ if (from.hasClassFileVersion()) {
upgradeClassFileVersion(from.getClassFileVersion());
}
}
@@ -1461,7 +1467,7 @@
private CompilationState compilationState;
private MethodOptimizationInfo optimizationInfo;
private KotlinMethodLevelInfo kotlinMemberInfo;
- private final int classFileVersion;
+ private final CfVersion classFileVersion;
private boolean d8R8Synthesized;
private Builder(DexEncodedMethod from) {
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 1a8d506..6474140 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
@@ -20,6 +21,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -47,7 +49,7 @@
private final ProgramResource.Kind originKind;
private final Collection<DexProgramClass> synthesizedFrom;
- private int initialClassFileVersion = -1;
+ private CfVersion initialClassFileVersion = null;
private boolean deprecated = false;
private KotlinClassLevelInfo kotlinInfo = NO_KOTLIN_INFO;
@@ -157,6 +159,18 @@
predicate, method -> consumer.accept(new ProgramMethod(this, method)));
}
+ public Iterable<ProgramMethod> directProgramMethods() {
+ return Iterables.transform(directMethods(), method -> new ProgramMethod(this, method));
+ }
+
+ public Iterable<ProgramMethod> directProgramMethods(Predicate<DexEncodedMethod> predicate) {
+ return Iterables.transform(directMethods(predicate), method -> new ProgramMethod(this, method));
+ }
+
+ public Iterable<ProgramMethod> programInstanceInitializers() {
+ return directProgramMethods(DexEncodedMethod::isInstanceInitializer);
+ }
+
public void forEachProgramDirectMethod(Consumer<ProgramMethod> consumer) {
forEachProgramDirectMethodMatching(alwaysTrue(), consumer);
}
@@ -552,17 +566,18 @@
return this;
}
- public void setInitialClassFileVersion(int initialClassFileVersion) {
- assert this.initialClassFileVersion == -1 && initialClassFileVersion > 0;
+ public void setInitialClassFileVersion(CfVersion initialClassFileVersion) {
+ assert this.initialClassFileVersion == null;
+ assert initialClassFileVersion != null;
this.initialClassFileVersion = initialClassFileVersion;
}
public boolean hasClassFileVersion() {
- return initialClassFileVersion > -1;
+ return initialClassFileVersion != null;
}
- public int getInitialClassFileVersion() {
- assert initialClassFileVersion > -1;
+ public CfVersion getInitialClassFileVersion() {
+ assert initialClassFileVersion != null;
return initialClassFileVersion;
}
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 ecb09ca..977d06d 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -21,6 +21,10 @@
return null;
}
+ public DexField getResolvedFieldReference() {
+ return null;
+ }
+
public boolean isSuccessfulResolution() {
return false;
}
@@ -78,6 +82,11 @@
}
@Override
+ public DexField getResolvedFieldReference() {
+ return resolvedField.toReference();
+ }
+
+ @Override
public DexEncodedField getResolvedMember() {
return resolvedField;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
new file mode 100644
index 0000000..b01445d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -0,0 +1,151 @@
+// 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.graph;
+
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ReturnType;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
+import java.util.List;
+
+public class GenericSignatureEnqueuerAnalysis extends EnqueuerAnalysis {
+
+ private final GenericSignatureVisitor visitor;
+
+ public GenericSignatureEnqueuerAnalysis(DexDefinitionSupplier definitionSupplier) {
+ visitor = new GenericSignatureTypeVisitor(definitionSupplier);
+ }
+
+ @Override
+ public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {
+ visitor.visitClassSignature(clazz.getClassSignature());
+ }
+
+ @Override
+ public void processNewlyLiveField(ProgramField field) {
+ visitor.visitFieldTypeSignature(field.getDefinition().getGenericSignature());
+ }
+
+ @Override
+ public void processNewlyLiveMethod(ProgramMethod method) {
+ visitor.visitMethodSignature(method.getDefinition().getGenericSignature());
+ }
+
+ private static class GenericSignatureTypeVisitor implements GenericSignatureVisitor {
+
+ private final DexDefinitionSupplier definitionSupplier;
+
+ private GenericSignatureTypeVisitor(DexDefinitionSupplier definitionSupplier) {
+ this.definitionSupplier = definitionSupplier;
+ }
+
+ @Override
+ public void visitClassSignature(ClassSignature classSignature) {
+ if (classSignature.hasNoSignature()) {
+ return;
+ }
+ classSignature.visit(this);
+ }
+
+ @Override
+ public void visitMethodSignature(MethodTypeSignature methodSignature) {
+ if (methodSignature.hasNoSignature()) {
+ return;
+ }
+ methodSignature.visit(this);
+ }
+
+ @Override
+ public void visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ if (fieldSignature.hasNoSignature()) {
+ return;
+ }
+ if (fieldSignature.isStar()) {
+ return;
+ }
+ if (fieldSignature.isTypeVariableSignature()) {
+ return;
+ }
+ if (fieldSignature.isArrayTypeSignature()) {
+ fieldSignature.asArrayTypeSignature().visit(this);
+ return;
+ }
+ assert fieldSignature.isClassTypeSignature();
+ visitClassTypeSignature(fieldSignature.asClassTypeSignature());
+ }
+
+ private void visitClassTypeSignature(ClassTypeSignature classTypeSignature) {
+ definitionSupplier.definitionFor(classTypeSignature.type);
+ classTypeSignature.visit(this);
+ }
+
+ @Override
+ public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
+ formalTypeParameters.forEach(formalTypeParameter -> formalTypeParameter.visit(this));
+ }
+
+ @Override
+ public void visitClassBound(FieldTypeSignature fieldSignature) {
+ visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public void visitInterfaceBound(FieldTypeSignature fieldSignature) {
+ visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public void visitSuperClass(ClassTypeSignature classTypeSignature) {
+ visitClassTypeSignature(classTypeSignature);
+ }
+
+ @Override
+ public void visitSuperInterface(ClassTypeSignature classTypeSignature) {
+ visitClassTypeSignature(classTypeSignature);
+ }
+
+ @Override
+ public void visitTypeSignature(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ return;
+ }
+ assert typeSignature.isFieldTypeSignature();
+ visitFieldTypeSignature(typeSignature.asFieldTypeSignature());
+ }
+
+ @Override
+ public void visitSimpleClass(ClassTypeSignature classTypeSignature) {
+ visitClassTypeSignature(classTypeSignature);
+ }
+
+ @Override
+ public void visitReturnType(ReturnType returnType) {
+ if (returnType.isVoidDescriptor()) {
+ return;
+ }
+ visitTypeSignature(returnType.typeSignature);
+ }
+
+ @Override
+ public void visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
+ typeSignatures.forEach(this::visitTypeSignature);
+ }
+
+ @Override
+ public void visitThrowsSignatures(List<TypeSignature> typeSignatures) {
+ typeSignatures.forEach(this::visitTypeSignature);
+ }
+
+ @Override
+ public void visitTypeArguments(List<FieldTypeSignature> typeArguments) {
+ typeArguments.forEach(this::visitFieldTypeSignature);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index 7804c5f..e9b45f1 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -38,21 +38,21 @@
}
public ClassSignature rewrite(ClassSignature classSignature) {
- if (classSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ if (classSignature.hasNoSignature()) {
return classSignature;
}
return new ClassSignatureRewriter().run(classSignature);
}
public FieldTypeSignature rewrite(FieldTypeSignature fieldTypeSignature) {
- if (fieldTypeSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ if (fieldTypeSignature.hasNoSignature()) {
return fieldTypeSignature;
}
return new TypeSignatureRewriter().run(fieldTypeSignature);
}
public MethodTypeSignature rewrite(MethodTypeSignature methodTypeSignature) {
- if (methodTypeSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ if (methodTypeSignature.hasNoSignature()) {
return methodTypeSignature;
}
return new MethodTypeSignatureRewriter().run(methodTypeSignature);
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index f2de6db..ac9b854 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -28,6 +28,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
/**
* A GraphLens implements a virtual view on top of the graph, used to delay global rewrites until
@@ -320,6 +321,13 @@
return newMethod.lookupOnProgramClass(holder);
}
+ // Predicate indicating if a rewritten type is a simple renaming, meaning the move from type to
+ // rewritten is just a renaming of the type to another. In other words, the content of the
+ // definition, including the definition of all of its members is the same modulo the renaming.
+ public boolean isSimpleRenaming(DexType from, DexType to) {
+ return false;
+ }
+
public abstract DexType lookupClassType(DexType type);
public abstract DexType lookupType(DexType type);
@@ -348,15 +356,21 @@
public DexField lookupField(DexField field) {
// Lookup the field using the graph lens and return the (non-rebound) reference from the lookup
// result.
- return internalLookupField(field, FieldLookupResult::getReference);
+ return lookupFieldResult(field).getReference();
}
- protected abstract DexField internalLookupField(
+ /** Lookup a rebound or non-rebound field reference using the current graph lens. */
+ public FieldLookupResult lookupFieldResult(DexField field) {
+ // Lookup the field using the graph lens and return the lookup result.
+ return internalLookupField(field, x -> x);
+ }
+
+ protected abstract FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation);
interface LookupFieldContinuation {
- DexField lookupField(FieldLookupResult previous);
+ FieldLookupResult lookupField(FieldLookupResult previous);
}
public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
@@ -408,6 +422,10 @@
public abstract boolean isIdentityLens();
+ public boolean isMemberRebindingLens() {
+ return false;
+ }
+
public abstract boolean isNonIdentityLens();
public NonIdentityGraphLens asNonIdentityLens() {
@@ -616,6 +634,19 @@
return previousLens;
}
+ @SuppressWarnings("unchecked")
+ public final <T extends GraphLens> T findPrevious(Predicate<NonIdentityGraphLens> predicate) {
+ GraphLens current = getPrevious();
+ while (current.isNonIdentityLens()) {
+ NonIdentityGraphLens nonIdentityGraphLens = current.asNonIdentityLens();
+ if (predicate.test(nonIdentityGraphLens)) {
+ return (T) nonIdentityGraphLens;
+ }
+ current = nonIdentityGraphLens.getPrevious();
+ }
+ return null;
+ }
+
public final void withAlternativeParentLens(GraphLens lens, Action action) {
GraphLens oldParent = getPrevious();
previousLens = lens;
@@ -663,7 +694,7 @@
}
@Override
- protected DexField internalLookupField(
+ protected FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation) {
return previousLens.internalLookupField(
reference, previous -> continuation.lookupField(internalDescribeLookupField(previous)));
@@ -772,7 +803,7 @@
}
@Override
- protected DexField internalLookupField(
+ protected FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation) {
// Passes the field reference back to the next graph lens. The identity lens intentionally
// does not set the rebound field reference, since it does not know what that is.
@@ -847,7 +878,7 @@
}
@Override
- protected DexField internalLookupField(
+ protected FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation) {
return getIdentityLens().internalLookupField(reference, continuation);
}
@@ -981,12 +1012,15 @@
if (previous.hasReboundReference()) {
// Rewrite the rebound reference and then "fixup" the non-rebound reference.
DexField rewrittenReboundReference = previous.getRewrittenReboundReference(fieldMap);
+ DexField rewrittenNonReboundReference =
+ previous.getReference() == previous.getReboundReference()
+ ? rewrittenReboundReference
+ : rewrittenReboundReference.withHolder(
+ internalDescribeLookupClassType(previous.getReference().getHolderType()),
+ dexItemFactory);
return FieldLookupResult.builder(this)
.setReboundReference(rewrittenReboundReference)
- .setReference(
- rewrittenReboundReference.withHolder(
- internalDescribeLookupClassType(previous.getReference().getHolderType()),
- dexItemFactory))
+ .setReference(rewrittenNonReboundReference)
.build();
} else {
// TODO(b/168282032): We should always have the rebound reference, so this should become
@@ -1011,8 +1045,8 @@
internalDescribeLookupClassType(previous.getReference().getHolderType()),
dexItemFactory);
return MethodLookupResult.builder(this)
- .setReboundReference(rewrittenReboundReference)
.setReference(rewrittenReference)
+ .setReboundReference(rewrittenReboundReference)
.setPrototypeChanges(
internalDescribePrototypeChanges(
previous.getPrototypeChanges(), rewrittenReboundReference))
@@ -1090,18 +1124,18 @@
*
* <p>Handle methods moved from interface to class or class to interface.
*/
- protected final Type mapVirtualInterfaceInvocationTypes(
+ public static Type mapVirtualInterfaceInvocationTypes(
DexDefinitionSupplier definitions,
DexMethod newMethod,
DexMethod originalMethod,
Type type) {
if (type == Type.VIRTUAL || type == Type.INTERFACE) {
// Get the invoke type of the actual definition.
- DexClass newTargetClass = definitions.definitionFor(newMethod.holder);
+ DexClass newTargetClass = definitions.definitionFor(newMethod.getHolderType());
if (newTargetClass == null) {
return type;
}
- DexClass originalTargetClass = definitions.definitionFor(originalMethod.holder);
+ DexClass originalTargetClass = definitions.definitionFor(originalMethod.getHolderType());
if (originalTargetClass != null
&& (originalTargetClass.isInterface() ^ (type == Type.INTERFACE))) {
// The invoke was wrong to start with, so we keep it wrong. This is to ensure we get
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index b71bfa0..0902a45 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -8,12 +8,11 @@
import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
-import static org.objectweb.asm.Opcodes.V1_6;
-import static org.objectweb.asm.Opcodes.V9;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
@@ -198,7 +197,7 @@
private final ReparseContext context = new ReparseContext();
// DexClass data.
- private int version;
+ private CfVersion version;
private boolean deprecated;
private DexType type;
private ClassAccessFlags accessFlags;
@@ -238,7 +237,7 @@
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (outerName != null && innerName != null) {
String separator = DescriptorUtils.computeInnerClassSeparator(outerName, name, innerName);
- if (separator == null && getMajorVersion() < V9) {
+ if (separator == null && version.isLessThan(CfVersion.V9)) {
application.options.reporter.info(
new StringDiagnostic(
StringUtils.lines(
@@ -289,43 +288,54 @@
+ name;
}
- private String illegalClassFilePostfix(int version) {
+ private String illegalClassFilePostfix(CfVersion version) {
return "Class file version " + version;
}
private String illegalClassFileMessage(
- ClassAccessFlags accessFlags, String name, int version, String message) {
+ ClassAccessFlags accessFlags, String name, CfVersion version, String message) {
return illegalClassFilePrefix(accessFlags, name)
+ " " + message
+ ". " + illegalClassFilePostfix(version) + ".";
}
@Override
- public void visit(int version, int access, String name, String signature, String superName,
+ public void visit(
+ int rawVersion,
+ int access,
+ String name,
+ String signature,
+ String superName,
String[] interfaces) {
- this.version = version;
- if (InternalOptions.SUPPORTED_CF_MAJOR_VERSION < getMajorVersion()) {
- throw new CompilationError("Unsupported class file version: " + getMajorVersion(), origin);
+ version = CfVersion.fromRaw(rawVersion);
+ if (InternalOptions.SUPPORTED_CF_VERSION.isLessThan(version)) {
+ throw new CompilationError("Unsupported class file version: " + version, origin);
}
this.deprecated = AsmUtils.isDeprecated(access);
accessFlags = ClassAccessFlags.fromCfAccessFlags(cleanAccessFlags(access));
type = application.getTypeFromName(name);
// Check if constraints from
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
- if (!accessFlags.areValid(getMajorVersion(), name.endsWith("/package-info"))) {
+ if (!accessFlags.areValid(version, name.endsWith("/package-info"))) {
throw new CompilationError(
- illegalClassFileMessage(accessFlags, name, version,
- "has invalid access flags. Found: " + accessFlags.toString()), origin);
+ illegalClassFileMessage(
+ accessFlags,
+ name,
+ version,
+ "has invalid access flags. Found: " + accessFlags.toString()),
+ origin);
}
if (superName == null && !name.equals(Constants.JAVA_LANG_OBJECT_NAME)) {
throw new CompilationError(
- illegalClassFileMessage(accessFlags, name, version,
- "is missing a super type"), origin);
+ illegalClassFileMessage(accessFlags, name, version, "is missing a super type"), origin);
}
if (accessFlags.isInterface()
&& !Objects.equals(superName, Constants.JAVA_LANG_OBJECT_NAME)) {
throw new CompilationError(
- illegalClassFileMessage(accessFlags, name, version,
+ illegalClassFileMessage(
+ accessFlags,
+ name,
+ version,
"must extend class java.lang.Object. Found: " + Objects.toString(superName)),
origin);
}
@@ -448,7 +458,7 @@
}
if (enclosingMember == null
&& (clazz.isLocalClass() || clazz.isAnonymousClass())
- && getMajorVersion() > V1_6) {
+ && CfVersion.V1_6.isLessThan(version)) {
application.options.warningMissingEnclosingMember(clazz.type, clazz.origin, version);
}
if (!clazz.isLibraryClass()) {
@@ -526,14 +536,6 @@
return annotations;
}
- private int getMajorVersion() {
- return version & 0xFFFF;
- }
-
- private int getMinorVersion() {
- return ((version >> 16) & 0xFFFF);
- }
-
public boolean isInANest() {
return !nestMembers.isEmpty() || nestHost != null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index a1fe5c7..0765824 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
/** Type representing a method definition in the programs compilation unit and its holder. */
public final class ProgramMethod extends DexClassAndMethod
@@ -81,4 +82,8 @@
assert holder.isProgramClass();
return holder.asProgramClass();
}
+
+ public MethodPosition getPosition() {
+ return getDefinition().getPosition();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 31ea06f..8a44fd0 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -465,7 +465,8 @@
if (parameters.isEmpty()) {
return this;
}
- List<ExtraParameter> newExtraParameters = new ArrayList<>();
+ List<ExtraParameter> newExtraParameters =
+ new ArrayList<>(extraParameters.size() + parameters.size());
newExtraParameters.addAll(extraParameters);
newExtraParameters.addAll(parameters);
return new RewrittenPrototypeDescription(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index f4158c5..cfd5182 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -40,7 +40,6 @@
public class ClassMerger {
public static final String CLASS_ID_FIELD_NAME = "$r8$classId";
- private final AppView<AppInfoWithLiveness> appView;
private final DexProgramClass target;
private final Collection<DexProgramClass> toMergeGroup;
private final DexItemFactory dexItemFactory;
@@ -63,7 +62,6 @@
DexField classIdField,
Collection<VirtualMethodMerger> virtualMethodMergers,
Collection<ConstructorMerger> constructorMergers) {
- this.appView = appView;
this.lensBuilder = lensBuilder;
this.mergedClassesBuilder = mergedClassesBuilder;
this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
@@ -99,6 +97,10 @@
}
void merge(DexProgramClass toMerge) {
+ if (!toMerge.isFinal()) {
+ target.getAccessFlags().demoteFromFinal();
+ }
+
toMerge.forEachProgramDirectMethod(
method -> {
DexEncodedMethod definition = method.getDefinition();
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 b6f3058..9f201bf 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -139,10 +140,11 @@
// Tree map as must be sorted.
Int2ReferenceSortedMap<DexMethod> typeConstructorClassMap = new Int2ReferenceAVLTreeMap<>();
- int classFileVersion = -1;
+ CfVersion classFileVersion = null;
for (DexEncodedMethod constructor : constructors) {
if (constructor.hasClassFileVersion()) {
- classFileVersion = Integer.max(classFileVersion, constructor.getClassFileVersion());
+ classFileVersion =
+ CfVersion.maxAllowNull(classFileVersion, constructor.getClassFileVersion());
}
DexMethod movedConstructor = moveConstructor(constructor);
lensBuilder.mapMethod(movedConstructor, movedConstructor);
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 b1d0dbe..c9a40d7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -7,16 +7,20 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.horizontalclassmerging.policies.DontInlinePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeIntoLessVisible;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses;
+import com.android.tools.r8.horizontalclassmerging.policies.IgnoreSynthetics;
import com.android.tools.r8.horizontalclassmerging.policies.NoAbstractClasses;
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.NoEnums;
import com.android.tools.r8.horizontalclassmerging.policies.NoFields;
import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
+import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
import com.android.tools.r8.horizontalclassmerging.policies.NoRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoStaticClassInitializer;
import com.android.tools.r8.horizontalclassmerging.policies.NotEntryPoint;
@@ -29,9 +33,9 @@
import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost;
import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.ClassMergingEnqueuerExtension;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.shaking.MainDexTracingResult;
+import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
@@ -47,7 +51,7 @@
public HorizontalClassMerger(
AppView<AppInfoWithLiveness> appView,
MainDexTracingResult mainDexTracingResult,
- ClassMergingEnqueuerExtension classMergingEnqueuerExtension) {
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
this.appView = appView;
assert appView.options().enableInlining;
@@ -59,14 +63,18 @@
new NoInterfaces(),
new NoClassesWithInterfaces(),
new NoAnnotations(),
+ new NoEnums(appView),
new NoAbstractClasses(),
+ new IgnoreSynthetics(appView),
new NoClassesOrMembersWithAnnotations(),
new NoInnerClasses(),
new NoStaticClassInitializer(),
+ new NoNativeMethods(),
new NoKeepRules(appView),
new NotVerticallyMergedIntoSubtype(appView),
- new NoRuntimeTypeChecks(classMergingEnqueuerExtension),
+ new NoRuntimeTypeChecks(runtimeTypeCheckInfo),
new NotEntryPoint(appView.dexItemFactory()),
+ new DontInlinePolicy(appView, mainDexTracingResult),
new PreventMergeIntoMainDex(appView, mainDexTracingResult),
new SameParentClass(),
new SameNestHost(),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
new file mode 100644
index 0000000..402ddb1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.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.horizontalclassmerging;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+/**
+ * Calculates the subtyping forrest for all classes. Unlike {@link
+ * com.android.tools.r8.graph.SubtypingInfo}, interfaces are not included in this subtyping
+ * information and only the immediate parents are stored (i.e. the transitive parents are not
+ * calculated). In the following example graph, the roots are A, E and G, and each edge indicates an
+ * entry in {@link SubtypingForrestForClasses#subtypeMap} going from the parent to an entry in the
+ * collection of children. <code>
+ * A E G
+ * / \ |
+ * B C F
+ * |
+ * D
+ * </code>
+ */
+public class SubtypingForrestForClasses {
+ private final AppView<AppInfoWithLiveness> appView;
+
+ private final Collection<DexProgramClass> roots = new ArrayList<>();
+ private final Map<DexProgramClass, Collection<DexProgramClass>> subtypeMap =
+ new IdentityHashMap<>();
+
+ public SubtypingForrestForClasses(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+
+ calculateSubtyping(appView.appInfo().classes());
+ }
+
+ private DexProgramClass superClass(DexProgramClass clazz) {
+ return appView.programDefinitionFor(clazz.superType, clazz);
+ }
+
+ private void calculateSubtyping(Iterable<DexProgramClass> classes) {
+ classes.forEach(this::calculateSubtyping);
+ }
+
+ private void calculateSubtyping(DexProgramClass clazz) {
+ if (clazz.isInterface()) {
+ return;
+ }
+ DexProgramClass superClass = superClass(clazz);
+ if (superClass == null) {
+ roots.add(clazz);
+ } else {
+ subtypeMap.computeIfAbsent(superClass, ignore -> new ArrayList<>()).add(clazz);
+ }
+ }
+
+ public Collection<DexProgramClass> getProgramRoots() {
+ return roots;
+ }
+
+ private Collection<DexProgramClass> getSubtypesFor(DexProgramClass clazz) {
+ return subtypeMap.getOrDefault(clazz, Collections.emptyList());
+ }
+
+ public <T> void traverseNodeDepthFirst(
+ DexProgramClass clazz, T state, BiFunction<DexProgramClass, T, T> consumer) {
+ T newState = consumer.apply(clazz, state);
+ getSubtypesFor(clazz).forEach(subClazz -> traverseNodeDepthFirst(subClazz, newState, consumer));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 7f12cc9..b102b69 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -34,7 +34,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.function.Consumer;
/**
* The tree fixer traverses all program classes and finds and fixes references to old classes which
@@ -53,10 +52,6 @@
private final BiMap<Wrapper<DexMethod>, Wrapper<DexMethod>> reservedInterfaceSignatures =
HashBiMap.create();
- // Store which methods have been renamed in parent classes.
- private final Map<DexType, Map<Wrapper<DexMethod>, DexString>> renamedVirtualMethods =
- new IdentityHashMap<>();
-
public TreeFixer(
AppView<AppInfoWithLiveness> appView,
HorizontallyMergedClasses mergedClasses,
@@ -128,8 +123,13 @@
Iterable<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
- forEachClassTypeTraverseHierarchy(
- Iterables.filter(classes, clazz -> !clazz.isInterface()), this::fixupProgramClass);
+ classes.forEach(this::fixupProgramClassSuperType);
+ SubtypingForrestForClasses subtypingForrest = new SubtypingForrestForClasses(appView);
+ // TODO(b/170078037): parallelize this code segment.
+ for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
+ subtypingForrest.traverseNodeDepthFirst(
+ root, new IdentityHashMap<>(), this::fixupProgramClass);
+ }
lensBuilder.remapMethods(movedMethods);
@@ -139,14 +139,19 @@
return lens;
}
- private void fixupProgramClass(DexProgramClass clazz) {
+ private void fixupProgramClassSuperType(DexProgramClass clazz) {
+ clazz.superType = fixupType(clazz.superType);
+ }
+
+ private Map<Wrapper<DexMethod>, DexString> fixupProgramClass(
+ DexProgramClass clazz, Map<Wrapper<DexMethod>, DexString> remappedVirtualMethods) {
assert !clazz.isInterface();
// TODO(b/169395592): ensure merged classes have been removed using:
// assert !mergedClasses.hasBeenMergedIntoDifferentType(clazz.type);
- Map<Wrapper<DexMethod>, DexString> renamedClassVirtualMethods =
- new HashMap<>(renamedVirtualMethods.getOrDefault(clazz.superType, new HashMap<>()));
+ Map<Wrapper<DexMethod>, DexString> remappedClassVirtualMethods =
+ new HashMap<>(remappedVirtualMethods);
Set<DexMethod> newDirectMethodReferences = new LinkedHashSet<>();
Set<DexMethod> newVirtualMethodReferences = new LinkedHashSet<>();
@@ -155,39 +160,16 @@
.getMethodCollection()
.replaceVirtualMethods(
method ->
- fixupVirtualMethod(renamedClassVirtualMethods, newVirtualMethodReferences, method));
+ fixupVirtualMethod(
+ remappedClassVirtualMethods, newVirtualMethodReferences, method));
clazz
.getMethodCollection()
.replaceDirectMethods(method -> fixupDirectMethod(newDirectMethodReferences, method));
- if (!renamedClassVirtualMethods.isEmpty()) {
- renamedVirtualMethods.put(clazz.type, renamedClassVirtualMethods);
- }
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
- }
- private void traverseUp(
- DexProgramClass clazz, Set<DexProgramClass> seenClasses, Consumer<DexProgramClass> fn) {
- if (clazz == null || !seenClasses.add(clazz)) {
- return;
- }
-
- clazz.superType = mergedClasses.getMergeTargetOrDefault(clazz.superType);
- if (clazz.superType != null) {
- DexProgramClass superClass = appView.programDefinitionFor(clazz.superType, clazz);
- traverseUp(superClass, seenClasses, fn);
- }
-
- fn.accept(clazz);
- }
-
- private void forEachClassTypeTraverseHierarchy(
- Iterable<DexProgramClass> classes, Consumer<DexProgramClass> fn) {
- Set<DexProgramClass> seenClasses = Sets.newIdentityHashSet();
- for (DexProgramClass clazz : classes) {
- traverseUp(clazz, seenClasses, fn);
- }
+ return remappedClassVirtualMethods;
}
private DexEncodedMethod fixupVirtualInterfaceMethod(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 7342124..ec5cb59 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -64,7 +65,7 @@
appView
.withLiveness()
.appInfo()
- .resolveMethodOnClass(template, target.superType)
+ .resolveMethodOnClass(template, target.getSuperType())
.asSingleResolution();
if (resolutionResult == null) {
return null;
@@ -72,7 +73,14 @@
if (resolutionResult.getResolvedMethod().isAbstract()) {
return null;
}
- return resolutionResult.getResolvedMethod().method;
+ if (resolutionResult.getResolvedHolder().isInterface()) {
+ // Ensure that invoke virtual isn't called on an interface method.
+ return resolutionResult
+ .getResolvedMethod()
+ .getReference()
+ .withHolder(target.getSuperType(), appView.dexItemFactory());
+ }
+ return resolutionResult.getResolvedMethod().getReference();
}
public VirtualMethodMerger build(
@@ -147,11 +155,11 @@
Int2ReferenceSortedMap<DexMethod> classIdToMethodMap = new Int2ReferenceAVLTreeMap<>();
- int classFileVersion = -1;
+ CfVersion classFileVersion = null;
for (ProgramMethod method : methods) {
if (method.getDefinition().hasClassFileVersion()) {
- classFileVersion =
- Integer.max(classFileVersion, method.getDefinition().getClassFileVersion());
+ CfVersion methodVersion = method.getDefinition().getClassFileVersion();
+ classFileVersion = CfVersion.maxAllowNull(classFileVersion, methodVersion);
}
DexMethod newMethod = moveMethod(method);
lensBuilder.mapMethod(newMethod, newMethod);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
new file mode 100644
index 0000000..0fb2284
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
@@ -0,0 +1,62 @@
+// 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.CfCode;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.MainDexDirectReferenceTracer;
+import com.android.tools.r8.shaking.MainDexTracingResult;
+import com.google.common.collect.Iterables;
+
+public class DontInlinePolicy extends SingleClassPolicy {
+ private final AppView<AppInfoWithLiveness> appView;
+ private final MainDexTracingResult mainDexTracingResult;
+
+ public DontInlinePolicy(
+ AppView<AppInfoWithLiveness> appView, MainDexTracingResult mainDexTracingResult) {
+ this.appView = appView;
+ this.mainDexTracingResult = mainDexTracingResult;
+ }
+
+ private boolean disallowInlining(ProgramMethod method) {
+ Code code = method.getDefinition().getCode();
+
+ // For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed.
+ if (code == null || !code.isCfCode()) {
+ return true;
+ }
+
+ CfCode cfCode = code.asCfCode();
+
+ ConstraintWithTarget constraint =
+ cfCode.computeInliningConstraint(method, appView, appView.graphLens(), method);
+ if (constraint == ConstraintWithTarget.NEVER) {
+ return true;
+ }
+
+ // Constructors can have references beyond the root main dex classes. This can increase the
+ // size of the main dex dependent classes and we should bail out.
+ if (mainDexTracingResult.getRoots().contains(method.getHolderType())
+ && MainDexDirectReferenceTracer.hasReferencesOutsideFromCode(
+ appView.appInfo(), method, mainDexTracingResult.getRoots())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ return !Iterables.any(
+ program.directProgramMethods(),
+ method -> method.getDefinition().isInstanceInitializer() && disallowInlining(method));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java
new file mode 100644
index 0000000..78875fd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java
@@ -0,0 +1,24 @@
+// 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 IgnoreSynthetics extends SingleClassPolicy {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ public IgnoreSynthetics(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ return !appView.getSyntheticItems().isSyntheticClass(program);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
new file mode 100644
index 0000000..2da0177
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
@@ -0,0 +1,45 @@
+// 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.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
+
+public class NoEnums extends SingleClassPolicy {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final Reference2BooleanMap<DexClass> cache = new Reference2BooleanOpenHashMap<>();
+
+ public NoEnums(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ return !program.isEnum() && !isEnumSubtype(program);
+ }
+
+ private boolean isEnumSubtype(DexClass clazz) {
+ if (cache.containsKey(clazz)) {
+ return cache.getBoolean(clazz);
+ }
+ boolean result;
+ if (clazz.type == appView.dexItemFactory().objectType) {
+ result = false;
+ } else if (clazz.type == appView.dexItemFactory().enumType) {
+ result = true;
+ } else {
+ DexClass superClass = appView.definitionFor(clazz.superType);
+ result = superClass != null && isEnumSubtype(superClass);
+ }
+ cache.put(clazz, result);
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoNativeMethods.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoNativeMethods.java
new file mode 100644
index 0000000..f1a278c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoNativeMethods.java
@@ -0,0 +1,17 @@
+// 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.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.google.common.collect.Iterables;
+
+public class NoNativeMethods extends SingleClassPolicy {
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ return !Iterables.any(program.methods(), DexEncodedMethod::isNative);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
index 23f10b2..010fee8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
@@ -6,18 +6,18 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.shaking.ClassMergingEnqueuerExtension;
+import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
public class NoRuntimeTypeChecks extends SingleClassPolicy {
- private final ClassMergingEnqueuerExtension classMergingEnqueuerExtension;
+ private final RuntimeTypeCheckInfo runtimeTypeCheckInfo;
- public NoRuntimeTypeChecks(ClassMergingEnqueuerExtension classMergingEnqueuerExtension) {
- this.classMergingEnqueuerExtension = classMergingEnqueuerExtension;
+ public NoRuntimeTypeChecks(RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ this.runtimeTypeCheckInfo = runtimeTypeCheckInfo;
}
@Override
public boolean canMerge(DexProgramClass clazz) {
// We currently assume we only merge classes that implement the same set of interfaces.
- return !classMergingEnqueuerExtension.isRuntimeCheckType(clazz);
+ return !runtimeTypeCheckInfo.isRuntimeCheckType(clazz);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
index e4235e6..ea4040c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
@@ -8,12 +8,14 @@
import static com.google.common.base.Predicates.or;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
+import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.Phi;
@@ -33,11 +35,13 @@
public class BasicBlockBehavioralSubsumption {
private final AppView<?> appView;
+ private final IRCode code;
private final ProgramMethod context;
- public BasicBlockBehavioralSubsumption(AppView<?> appView, ProgramMethod context) {
+ public BasicBlockBehavioralSubsumption(AppView<?> appView, IRCode code) {
this.appView = appView;
- this.context = context;
+ this.code = code;
+ this.context = code.context();
}
public boolean isSubsumedBy(BasicBlock block, BasicBlock other) {
@@ -148,7 +152,19 @@
}
private boolean isBlockLocalInstructionWithoutSideEffects(Instruction instruction) {
- return definesBlockLocalValue(instruction) && !instructionMayHaveSideEffects(instruction);
+ if (!definesBlockLocalValue(instruction)) {
+ return false;
+ }
+ if (instruction.instructionMayHaveSideEffects(appView, context)) {
+ return false;
+ }
+ // For constructor calls include field initialization side effects.
+ if (instruction.isInvokeConstructor(appView.dexItemFactory())) {
+ DexEncodedMethod singleTarget =
+ instruction.asInvokeDirect().lookupSingleTarget(appView, context);
+ return singleTarget != null && !singleTarget.getOptimizationInfo().mayHaveSideEffects();
+ }
+ return true;
}
private boolean definesBlockLocalValue(Instruction instruction) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index e4bf2a9..fd8dbf7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -15,20 +15,36 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
+import com.android.tools.r8.ir.optimize.info.field.UnknownInstanceFieldInitializationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DequeUtils;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.ArrayList;
import java.util.Deque;
import java.util.IdentityHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public abstract class FieldValueAnalysis {
+ static class FieldInitializationInfo {
+
+ private final Instruction instruction;
+ private final InstanceFieldInitializationInfo instanceFieldInitializationInfo;
+
+ FieldInitializationInfo(
+ Instruction instruction, InstanceFieldInitializationInfo instanceFieldInitializationInfo) {
+ this.instruction = instruction;
+ this.instanceFieldInitializationInfo = instanceFieldInitializationInfo;
+ }
+ }
+
final AppView<AppInfoWithLiveness> appView;
final IRCode code;
final ProgramMethod context;
@@ -37,7 +53,7 @@
private DominatorTree dominatorTree;
private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
- final Map<DexEncodedField, LinkedList<FieldInstruction>> putsPerField = new IdentityHashMap<>();
+ final Map<DexEncodedField, List<FieldInitializationInfo>> putsPerField = new IdentityHashMap<>();
FieldValueAnalysis(
AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
@@ -61,8 +77,27 @@
return fieldsMaybeReadBeforeBlockInclusiveCache;
}
+ boolean isInstanceFieldValueAnalysis() {
+ return false;
+ }
+
+ InstanceFieldValueAnalysis asInstanceFieldValueAnalysis() {
+ return null;
+ }
+
abstract boolean isSubjectToOptimization(DexEncodedField field);
+ void recordFieldPut(DexEncodedField field, Instruction instruction) {
+ recordFieldPut(field, instruction, UnknownInstanceFieldInitializationInfo.getInstance());
+ }
+
+ void recordFieldPut(
+ DexEncodedField field, Instruction instruction, InstanceFieldInitializationInfo info) {
+ putsPerField
+ .computeIfAbsent(field, ignore -> new ArrayList<>())
+ .add(new FieldInitializationInfo(instruction, info));
+ }
+
/** This method analyzes initializers with the purpose of computing field optimization info. */
void computeFieldOptimizationInfo(ClassInitializerDefaultsResult classInitializerDefaultsResult) {
AppInfoWithLiveness appInfo = appView.appInfo();
@@ -80,31 +115,42 @@
DexField field = fieldPut.getField();
DexEncodedField encodedField = appInfo.resolveField(field).getResolvedField();
if (encodedField != null && isSubjectToOptimization(encodedField)) {
- putsPerField.computeIfAbsent(encodedField, ignore -> new LinkedList<>()).add(fieldPut);
+ recordFieldPut(encodedField, fieldPut);
}
+ } else if (isInstanceFieldValueAnalysis()
+ && instruction.isInvokeConstructor(appView.dexItemFactory())) {
+ InvokeDirect invoke = instruction.asInvokeDirect();
+ asInstanceFieldValueAnalysis().analyzeForwardingConstructorCall(invoke, code.getThis());
}
}
}
List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
- for (Entry<DexEncodedField, LinkedList<FieldInstruction>> entry : putsPerField.entrySet()) {
- DexEncodedField encodedField = entry.getKey();
- LinkedList<FieldInstruction> fieldPuts = entry.getValue();
+ for (Entry<DexEncodedField, List<FieldInitializationInfo>> entry : putsPerField.entrySet()) {
+ DexEncodedField field = entry.getKey();
+ List<FieldInitializationInfo> fieldPuts = entry.getValue();
if (fieldPuts.size() > 1) {
continue;
}
- FieldInstruction fieldPut = fieldPuts.getFirst();
+ FieldInitializationInfo info = ListUtils.first(fieldPuts);
+ Instruction instruction = info.instruction;
+ if (instruction.isInvokeDirect()) {
+ asInstanceFieldValueAnalysis()
+ .recordInstanceFieldIsInitializedWithInfo(field, info.instanceFieldInitializationInfo);
+ continue;
+ }
+ FieldInstruction fieldPut = instruction.asFieldInstruction();
if (!isStraightLineCode) {
if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
continue;
}
}
boolean priorReadsWillReadSameValue =
- !classInitializerDefaultsResult.hasStaticValue(encodedField) && fieldPut.value().isZero();
- if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(encodedField, fieldPut)) {
+ !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
+ if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
continue;
}
- updateFieldOptimizationInfo(encodedField, fieldPut, fieldPut.value());
+ updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index b818cc2..f508479 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -26,8 +27,10 @@
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
+import com.android.tools.r8.ir.optimize.info.field.UnknownInstanceFieldInitializationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
@@ -106,6 +109,16 @@
}
@Override
+ boolean isInstanceFieldValueAnalysis() {
+ return true;
+ }
+
+ @Override
+ InstanceFieldValueAnalysis asInstanceFieldValueAnalysis() {
+ return this;
+ }
+
+ @Override
boolean isSubjectToOptimization(DexEncodedField field) {
return !field.isStatic() && field.holder() == context.getHolderType();
}
@@ -113,11 +126,45 @@
@Override
void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) {
if (fieldNeverWrittenBetweenInstancePutAndMethodExit(field, fieldPut.asInstancePut())) {
- recordFieldIsInitializedWithValue(field, value);
+ recordInstanceFieldIsInitializedWithValue(field, value);
+ }
+ }
+
+ void analyzeForwardingConstructorCall(InvokeDirect invoke, Value thisValue) {
+ if (invoke.getReceiver() != thisValue
+ || invoke.getInvokedMethod().getHolderType() != context.getHolderType()) {
+ // Not a forwarding constructor call.
+ return;
+ }
+
+ ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
+ if (singleTarget == null) {
+ // Failure, should generally not happen.
+ return;
+ }
+
+ InstanceFieldInitializationInfoCollection infos =
+ singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getInstanceInitializerInfo()
+ .fieldInitializationInfos();
+ for (DexEncodedField field : singleTarget.getHolder().instanceFields()) {
+ assert isSubjectToOptimization(field);
+ InstanceFieldInitializationInfo info = infos.get(field);
+ if (info.isArgumentInitializationInfo()) {
+ int argumentIndex = info.asArgumentInitializationInfo().getArgumentIndex();
+ info = getInstanceFieldInitializationInfo(field, invoke.getArgument(argumentIndex));
+ }
+ recordFieldPut(field, invoke, info);
}
}
private void analyzeParentConstructorCall() {
+ if (parentConstructor.getHolderType() == context.getHolderType()) {
+ // Forwarding constructor calls are handled similar to instance-put instructions.
+ return;
+ }
InstanceFieldInitializationInfoCollection infos =
parentConstructor
.getOptimizationInfo()
@@ -129,8 +176,8 @@
if (fieldNeverWrittenBetweenParentConstructorCallAndMethodExit(field)) {
if (info.isArgumentInitializationInfo()) {
int argumentIndex = info.asArgumentInitializationInfo().getArgumentIndex();
- recordFieldIsInitializedWithValue(
- field, parentConstructorCall.arguments().get(argumentIndex));
+ recordInstanceFieldIsInitializedWithValue(
+ field, parentConstructorCall.getArgument(argumentIndex));
} else {
assert info.isSingleValue() || info.isTypeInitializationInfo();
builder.recordInitializationInfo(field, info);
@@ -139,32 +186,39 @@
});
}
- private void recordFieldIsInitializedWithValue(DexEncodedField field, Value value) {
+ private InstanceFieldInitializationInfo getInstanceFieldInitializationInfo(
+ DexEncodedField field, Value value) {
Value root = value.getAliasedValue();
if (root.isDefinedByInstructionSatisfying(Instruction::isArgument)) {
Argument argument = root.definition.asArgument();
- builder.recordInitializationInfo(
- field, factory.createArgumentInitializationInfo(argument.getIndex()));
- return;
+ return factory.createArgumentInitializationInfo(argument.getIndex());
}
-
AbstractValue abstractValue = value.getAbstractValue(appView, context);
if (abstractValue.isSingleValue()) {
- builder.recordInitializationInfo(field, abstractValue.asSingleValue());
- return;
+ return abstractValue.asSingleValue();
}
-
- DexType fieldType = field.field.type;
+ DexType fieldType = field.type();
if (fieldType.isClassType()) {
ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
TypeElement staticFieldType = TypeElement.fromDexType(fieldType, maybeNull(), appView);
if (dynamicLowerBoundType != null || !dynamicUpperBoundType.equals(staticFieldType)) {
- builder.recordInitializationInfo(
- field,
- factory.createTypeInitializationInfo(dynamicLowerBoundType, dynamicUpperBoundType));
+ return factory.createTypeInitializationInfo(dynamicLowerBoundType, dynamicUpperBoundType);
}
}
+ return UnknownInstanceFieldInitializationInfo.getInstance();
+ }
+
+ void recordInstanceFieldIsInitializedWithInfo(
+ DexEncodedField field, InstanceFieldInitializationInfo info) {
+ if (!info.isUnknown()) {
+ builder.recordInitializationInfo(field, info);
+ }
+ }
+
+ void recordInstanceFieldIsInitializedWithValue(DexEncodedField field, Value value) {
+ recordInstanceFieldIsInitializedWithInfo(
+ field, getInstanceFieldInitializationInfo(field, value));
}
private boolean fieldNeverWrittenBetweenInstancePutAndMethodExit(
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 6722cfb..17b49b5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -157,7 +157,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forCheckCast(type, context.getHolder());
+ return inliningConstraints.forCheckCast(type, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 86101ba..7f80abf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -158,7 +158,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forConstClass(clazz, context.getHolder());
+ return inliningConstraints.forConstClass(clazz, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 0753447..2c7313e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -160,7 +160,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forDexItemBasedConstString(item, context.getHolder());
+ return inliningConstraints.forDexItemBasedConstString(item, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index 22e4c4e..61a1882 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -158,7 +158,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInitClass(clazz, context.getHolder());
+ return inliningConstraints.forInitClass(clazz, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 7dad747..090812b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -144,7 +144,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInstanceGet(getField(), context.getHolder());
+ return inliningConstraints.forInstanceGet(getField(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index 23808f4..0530403 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -88,7 +88,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInstanceOf(type, context.getHolder());
+ return inliningConstraints.forInstanceOf(type, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 296b2f9..c38c5c0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -184,7 +184,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInstancePut(getField(), context.getHolder());
+ return inliningConstraints.forInstancePut(getField(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 6a42c80..62c94b8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -1240,6 +1241,10 @@
return null;
}
+ public boolean isInvokeConstructor(DexItemFactory dexItemFactory) {
+ return false;
+ }
+
public boolean isInvokeDirect() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 6d452e1..2a81647 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -109,6 +110,11 @@
}
@Override
+ public boolean isInvokeConstructor(DexItemFactory dexItemFactory) {
+ return getInvokedMethod().isInstanceInitializer(dexItemFactory);
+ }
+
+ @Override
public boolean isInvokeDirect() {
return true;
}
@@ -140,7 +146,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokeDirect(getInvokedMethod(), context.getHolder());
+ return inliningConstraints.forInvokeDirect(getInvokedMethod(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index e95be77..83acc1d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -119,7 +119,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokeInterface(getInvokedMethod(), context.getHolder());
+ return inliningConstraints.forInvokeInterface(getInvokedMethod(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 063592f..7163fdd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -78,7 +78,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokeMultiNewArray(type, context.getHolder());
+ return inliningConstraints.forInvokeMultiNewArray(type, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 25a4b47..cad5e1d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -107,7 +107,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokeNewArray(type, context.getHolder());
+ return inliningConstraints.forInvokeNewArray(type, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 824a283..dbcafdc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -140,7 +140,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokePolymorphic(getInvokedMethod(), context.getHolder());
+ return inliningConstraints.forInvokePolymorphic(getInvokedMethod(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index e89d9b0..696d674 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -130,7 +130,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokeStatic(getInvokedMethod(), context.getHolder());
+ return inliningConstraints.forInvokeStatic(getInvokedMethod(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 4815431..34fa59c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -120,7 +120,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokeSuper(getInvokedMethod(), context.getHolder());
+ return inliningConstraints.forInvokeSuper(getInvokedMethod(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 951a451..e7fb163 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -142,7 +142,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forInvokeVirtual(getInvokedMethod(), context.getHolder());
+ return inliningConstraints.forInvokeVirtual(getInvokedMethod(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index eb12b5a..37fbdf0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -113,7 +113,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forNewArrayEmpty(type, context.getHolder());
+ return inliningConstraints.forNewArrayEmpty(type, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 0c6f609..704de30 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -103,7 +103,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forNewInstance(clazz, context.getHolder());
+ return inliningConstraints.forNewInstance(clazz, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 2a5ffbd..8a3f867 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -170,7 +170,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forStaticGet(getField(), context.getHolder());
+ return inliningConstraints.forStaticGet(getField(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index e268cbe..9df14cc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -180,7 +180,7 @@
@Override
public ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, ProgramMethod context) {
- return inliningConstraints.forStaticPut(getField(), context.getHolder());
+ return inliningConstraints.forStaticPut(getField(), context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 8f2c15e..74191f0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -14,9 +14,9 @@
import static com.android.tools.r8.ir.analysis.type.TypeElement.getNull;
import static com.android.tools.r8.ir.analysis.type.TypeElement.getSingle;
import static com.android.tools.r8.ir.analysis.type.TypeElement.getWide;
-import static org.objectweb.asm.Opcodes.V1_8;
import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.InvalidDebugInfoException;
@@ -2210,7 +2210,7 @@
local,
readType);
} else {
- assert method.getDefinition().getClassFileVersion() < V1_8;
+ assert method.getDefinition().getClassFileVersion().isLessThan(CfVersion.V1_8);
hasIncorrectStackMapTypes = true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index b7964c8..c271af1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -269,10 +269,13 @@
? new InterfaceMethodRewriter(appView, this)
: null;
this.twrCloseResourceRewriter =
- ((options.desugarState == DesugarState.ON) && enableTwrCloseResourceDesugaring())
+ (options.desugarState == DesugarState.ON && enableTwrCloseResourceDesugaring())
? new TwrCloseResourceRewriter(appView, this)
: null;
- this.backportedMethodRewriter = new BackportedMethodRewriter(appView);
+ this.backportedMethodRewriter =
+ (options.desugarState == DesugarState.ON && !appView.enableWholeProgramOptimizations())
+ ? new BackportedMethodRewriter(appView)
+ : null;
this.desugaredLibraryRetargeter =
options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
? null
@@ -655,7 +658,8 @@
}
// Process the application identifying outlining candidates.
- GraphLens graphLensForIR = appView.graphLens();
+ GraphLens initialGraphLensForIR = appView.graphLens();
+ GraphLens graphLensForIR = initialGraphLensForIR;
OptimizationFeedbackDelayed feedback = delayedOptimizationFeedback;
PostMethodProcessor.Builder postMethodProcessorBuilder =
new PostMethodProcessor.Builder(getOptimizationsForPostIRProcessing());
@@ -759,7 +763,7 @@
printPhase("Lambda merging finalization");
// TODO(b/127694949): Adapt to PostOptimization.
- finalizeLambdaMerging(application, feedback, builder, executorService);
+ finalizeLambdaMerging(application, feedback, builder, executorService, initialGraphLensForIR);
printPhase("Desugared library API Conversion finalization");
generateDesugaredLibraryAPIWrappers(builder, executorService);
@@ -934,11 +938,12 @@
DexApplication application,
OptimizationFeedback feedback,
Builder<?> builder,
- ExecutorService executorService)
+ ExecutorService executorService,
+ GraphLens appliedGraphLens)
throws ExecutionException {
if (lambdaMerger != null) {
lambdaMerger.applyLambdaClassMapping(
- application, this, feedback, builder, executorService);
+ application, this, feedback, builder, executorService, appliedGraphLens);
} else {
appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 8b827d3..0512e69 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -32,16 +32,20 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
+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.DexCallSite;
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.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.FieldLookupResult;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
@@ -374,7 +378,7 @@
{
InstanceGet instanceGet = current.asInstanceGet();
DexField field = instanceGet.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupGetFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -399,7 +403,7 @@
{
InstancePut instancePut = current.asInstancePut();
DexField field = instancePut.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupPutFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -421,7 +425,7 @@
{
StaticGet staticGet = current.asStaticGet();
DexField field = staticGet.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupGetFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -445,7 +449,7 @@
{
StaticPut staticPut = current.asStaticPut();
DexField field = staticPut.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupPutFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -581,6 +585,22 @@
assert code.hasNoVerticallyMergedClasses(appView);
}
+ private DexField rewriteFieldReference(
+ DexField reference, ProgramMethod context, GraphLens graphLens) {
+ FieldLookupResult lookup = graphLens.lookupFieldResult(reference);
+ if (lookup.hasReboundReference()) {
+ DexClass holder = appView.definitionFor(lookup.getReboundReference().getHolderType());
+ DexEncodedField definition = lookup.getReboundReference().lookupOnClass(holder);
+ if (definition != null) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ if (AccessControl.isMemberAccessible(field, holder, context, appView).isTrue()) {
+ return lookup.getReboundReference();
+ }
+ }
+ }
+ return lookup.getReference();
+ }
+
// If the initialValue is a default value and its type is rewritten from a reference type to a
// primitive type, then the default value type lattice needs to be changed.
private Value rewriteValueIfDefault(
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 b45fe42..04fb914 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
@@ -37,6 +37,7 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
import java.util.ArrayList;
@@ -106,9 +107,25 @@
}
public boolean desugar(ProgramMethod method, AppInfoWithClassHierarchy appInfo) {
+ return desugar(method, appInfo, synthesizedMethods::add);
+ }
+
+ public boolean desugar(
+ ProgramMethod method, AppInfoWithClassHierarchy appInfo, Consumer<ProgramMethod> consumer) {
if (!enabled) {
return false;
}
+ if (method.getDefinition().getCode().isDexCode()) {
+ appView
+ .options()
+ .reporter
+ .error(
+ new StringDiagnostic(
+ "Unsupported attempt to desugar DEX code",
+ method.getOrigin(),
+ method.getPosition()));
+ return false;
+ }
CfCode code = method.getDefinition().getCode().asCfCode();
ListIterator<CfInstruction> iterator = code.getInstructions().listIterator();
boolean replaced = false;
@@ -127,8 +144,7 @@
iterator = mutableInstructions.listIterator(iterator.previousIndex());
iterator.next();
}
- provider.rewriteInvoke(
- invoke, iterator, method.getHolder(), appInfo, synthesizedMethods::add);
+ provider.rewriteInvoke(invoke, iterator, method.getHolder(), appInfo, consumer);
replaced = true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index f2ae9a1..758830d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -29,9 +30,12 @@
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -79,6 +83,12 @@
boolean isEmpty() {
return signatures.isEmpty();
}
+
+ public MethodSignatures withoutAll(MethodSignatures other) {
+ Set<Wrapper<DexMethod>> merged = new HashSet<>(signatures);
+ merged.removeAll(other.signatures);
+ return signatures.size() == merged.size() ? this : new MethodSignatures(merged);
+ }
}
// Collection of information known at the point of a given (non-library) class.
@@ -86,7 +96,8 @@
// class hierarchy. Thus, in the case of additions the parent pointer will contain prior info.
private static class ClassInfo {
- static final ClassInfo EMPTY = new ClassInfo(null, ImmutableList.of());
+ static final ClassInfo EMPTY =
+ new ClassInfo(null, ImmutableList.of(), EmulatedInterfaceInfo.EMPTY);
final ClassInfo parent;
@@ -94,17 +105,26 @@
// class hierarchy. This set consists of the default interface methods, i.e., the targets of the
// forwarding methods, *not* the forwarding methods themselves.
final ImmutableList<DexEncodedMethod> forwardedMethodTargets;
+ // If the forwarding methods for the emulated interface methods have not been added yet,
+ // this contains the information to add it in the subclasses.
+ final EmulatedInterfaceInfo emulatedInterfaceInfo;
- ClassInfo(ClassInfo parent, ImmutableList<DexEncodedMethod> forwardedMethodTargets) {
+ ClassInfo(
+ ClassInfo parent,
+ ImmutableList<DexEncodedMethod> forwardedMethodTargets,
+ EmulatedInterfaceInfo emulatedInterfaceInfo) {
this.parent = parent;
this.forwardedMethodTargets = forwardedMethodTargets;
+ this.emulatedInterfaceInfo = emulatedInterfaceInfo;
}
static ClassInfo create(
- ClassInfo parent, ImmutableList<DexEncodedMethod> forwardedMethodTargets) {
+ ClassInfo parent,
+ ImmutableList<DexEncodedMethod> forwardedMethodTargets,
+ EmulatedInterfaceInfo emulatedInterfaceInfo) {
return forwardedMethodTargets.isEmpty()
? parent
- : new ClassInfo(parent, forwardedMethodTargets);
+ : new ClassInfo(parent, forwardedMethodTargets, emulatedInterfaceInfo);
}
public boolean isEmpty() {
@@ -117,6 +137,107 @@
}
}
+ // Collection of information on what signatures and what emulated interfaces require
+ // forwarding methods for library classes and interfaces.
+ private static class SignaturesInfo {
+
+ static final SignaturesInfo EMPTY =
+ new SignaturesInfo(MethodSignatures.EMPTY, EmulatedInterfaceInfo.EMPTY);
+
+ final MethodSignatures signatures;
+ final EmulatedInterfaceInfo emulatedInterfaceInfo;
+
+ private SignaturesInfo(
+ MethodSignatures methodsToForward, EmulatedInterfaceInfo emulatedInterfaceInfo) {
+ this.signatures = methodsToForward;
+ this.emulatedInterfaceInfo = emulatedInterfaceInfo;
+ }
+
+ public static SignaturesInfo create(MethodSignatures signatures) {
+ if (signatures.isEmpty()) {
+ return EMPTY;
+ }
+ return new SignaturesInfo(signatures, EmulatedInterfaceInfo.EMPTY);
+ }
+
+ public SignaturesInfo merge(SignaturesInfo other) {
+ if (isEmpty()) {
+ return other;
+ }
+ if (other.isEmpty()) {
+ return this;
+ }
+ return new SignaturesInfo(
+ signatures.merge(other.signatures),
+ emulatedInterfaceInfo.merge(other.emulatedInterfaceInfo));
+ }
+
+ public MethodSignatures emulatedInterfaceSignaturesToForward() {
+ return emulatedInterfaceInfo.signatures.withoutAll(signatures);
+ }
+
+ boolean isEmpty() {
+ return signatures.isEmpty() && emulatedInterfaceInfo.isEmpty();
+ }
+
+ public SignaturesInfo withSignatures(MethodSignatures additions) {
+ if (additions.isEmpty()) {
+ return this;
+ }
+ MethodSignatures newSignatures = signatures.merge(additions);
+ return new SignaturesInfo(newSignatures, emulatedInterfaceInfo);
+ }
+
+ public SignaturesInfo withEmulatedInterfaceInfo(
+ EmulatedInterfaceInfo additionalEmulatedInterfaceInfo) {
+ if (additionalEmulatedInterfaceInfo.isEmpty()) {
+ return this;
+ }
+ return new SignaturesInfo(
+ signatures, emulatedInterfaceInfo.merge(additionalEmulatedInterfaceInfo));
+ }
+ }
+
+ // List of emulated interfaces and corresponding signatures which may require forwarding methods.
+ // If one of the signatures has an override, then the class holding the override is required to
+ // add the forwarding methods for all signatures, and introduce the corresponding emulated
+ // interface in its interfaces attribute for correct emulated dispatch.
+ // If no override is present, then no forwarding methods are required, the class relies on the
+ // default behavior of the emulated dispatch.
+ private static class EmulatedInterfaceInfo {
+
+ static final EmulatedInterfaceInfo EMPTY =
+ new EmulatedInterfaceInfo(MethodSignatures.EMPTY, ImmutableSet.of());
+
+ final MethodSignatures signatures;
+ final ImmutableSet<DexType> emulatedInterfaces;
+
+ private EmulatedInterfaceInfo(
+ MethodSignatures methodsToForward, ImmutableSet<DexType> emulatedInterfaces) {
+ this.signatures = methodsToForward;
+ this.emulatedInterfaces = emulatedInterfaces;
+ }
+
+ public EmulatedInterfaceInfo merge(EmulatedInterfaceInfo other) {
+ if (isEmpty()) {
+ return other;
+ }
+ if (other.isEmpty()) {
+ return this;
+ }
+ ImmutableSet.Builder<DexType> newEmulatedInterfaces = ImmutableSet.builder();
+ newEmulatedInterfaces.addAll(emulatedInterfaces);
+ newEmulatedInterfaces.addAll(other.emulatedInterfaces);
+ return new EmulatedInterfaceInfo(
+ signatures.merge(other.signatures), newEmulatedInterfaces.build());
+ }
+
+ public boolean isEmpty() {
+ assert !emulatedInterfaces.isEmpty() || signatures.isEmpty();
+ return emulatedInterfaces.isEmpty();
+ }
+ }
+
// Helper to keep track of the direct active subclass and nearest program subclass for reporting.
private static class ReportingContext {
@@ -181,10 +302,10 @@
private final Map<DexClass, ClassInfo> classInfo = new IdentityHashMap<>();
// Mapping from library classes to their information summary.
- private final Map<DexLibraryClass, MethodSignatures> libraryClassInfo = new IdentityHashMap<>();
+ private final Map<DexLibraryClass, SignaturesInfo> libraryClassInfo = new IdentityHashMap<>();
// Mapping from arbitrary interfaces to an information summary.
- private final Map<DexClass, MethodSignatures> interfaceInfo = new IdentityHashMap<>();
+ private final Map<DexClass, SignaturesInfo> interfaceInfo = new IdentityHashMap<>();
// Mapping from actual program classes to the synthesized forwarding methods to be created.
private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods =
@@ -228,35 +349,113 @@
}
// Computes the set of method signatures that may need forwarding methods on derived classes.
- private MethodSignatures computeInterfaceInfo(DexClass iface, MethodSignatures signatures) {
+ private SignaturesInfo computeInterfaceInfo(DexClass iface, SignaturesInfo interfaceInfo) {
assert iface.isInterface();
assert iface.superType == dexItemFactory.objectType;
+ assert !rewriter.isEmulatedInterface(iface.type);
// Add non-library default methods as well as those for desugared library classes.
if (!iface.isLibraryClass() || (needsLibraryInfo() && rewriter.isInDesugaredLibrary(iface))) {
- Set<Wrapper<DexMethod>> additions =
- new HashSet<>(iface.getMethodCollection().numberOfVirtualMethods());
- for (DexEncodedMethod method : iface.virtualMethods(DexEncodedMethod::isDefaultMethod)) {
- additions.add(equivalence.wrap(method.method));
- }
- if (!additions.isEmpty()) {
- signatures = signatures.merge(MethodSignatures.create(additions));
- }
+ MethodSignatures signatures = getDefaultMethods(iface);
+ interfaceInfo = interfaceInfo.withSignatures(signatures);
}
- return signatures;
+ return interfaceInfo;
+ }
+
+ private SignaturesInfo computeEmulatedInterfaceInfo(
+ DexClass iface, SignaturesInfo interfaceInfo) {
+ assert iface.isInterface();
+ assert iface.superType == dexItemFactory.objectType;
+ assert rewriter.isEmulatedInterface(iface.type);
+ assert needsLibraryInfo();
+ MethodSignatures signatures = getDefaultMethods(iface);
+ EmulatedInterfaceInfo emulatedInterfaceInfo =
+ new EmulatedInterfaceInfo(signatures, ImmutableSet.of(iface.type));
+ return interfaceInfo.withEmulatedInterfaceInfo(emulatedInterfaceInfo);
+ }
+
+ private MethodSignatures getDefaultMethods(DexClass iface) {
+ assert iface.isInterface();
+ Set<Wrapper<DexMethod>> defaultMethods =
+ new HashSet<>(iface.getMethodCollection().numberOfVirtualMethods());
+ for (DexEncodedMethod method : iface.virtualMethods(DexEncodedMethod::isDefaultMethod)) {
+ defaultMethods.add(equivalence.wrap(method.method));
+ }
+ return MethodSignatures.create(defaultMethods);
}
// Computes the set of signatures of that may need forwarding methods on classes that derive
// from a library class.
- private MethodSignatures computeLibraryClassInfo(
- DexLibraryClass clazz, MethodSignatures signatures) {
+ private SignaturesInfo computeLibraryClassInfo(DexLibraryClass clazz, SignaturesInfo signatures) {
// The result is the identity as the library class does not itself contribute to the set.
return signatures;
}
// The computation of a class information and the insertions of forwarding methods.
private ClassInfo computeClassInfo(
- DexClass clazz, ClassInfo superInfo, MethodSignatures signatures) {
+ DexClass clazz, ClassInfo superInfo, SignaturesInfo signatureInfo) {
Builder<DexEncodedMethod> additionalForwards = ImmutableList.builder();
+ // First we deal with non-emulated interface desugaring.
+ resolveForwardingMethods(clazz, superInfo, signatureInfo.signatures, additionalForwards);
+ // Second we deal with emulated interface, if one method has override in the current class,
+ // we resolve them, else we propagate the emulated interface info down.
+ if (shouldResolveForwardingMethodsForEmulatedInterfaces(
+ clazz, signatureInfo.emulatedInterfaceInfo)) {
+ resolveForwardingMethods(
+ clazz,
+ superInfo,
+ signatureInfo.emulatedInterfaceSignaturesToForward(),
+ additionalForwards);
+ duplicateEmulatedInterfaces(clazz, signatureInfo.emulatedInterfaceInfo.emulatedInterfaces);
+ return ClassInfo.create(superInfo, additionalForwards.build(), EmulatedInterfaceInfo.EMPTY);
+ }
+ return ClassInfo.create(
+ superInfo, additionalForwards.build(), signatureInfo.emulatedInterfaceInfo);
+ }
+
+ // All classes implementing an emulated interface and overriding a default method should now
+ // implement the interface and the emulated one for correct emulated dispatch.
+ // The class signature won't include the correct type parameters for the duplicated interfaces,
+ // i.e., there will be foo.A instead of foo.A<K,V>, but such parameters are unused.
+ private void duplicateEmulatedInterfaces(
+ DexClass clazz, ImmutableSet<DexType> emulatedInterfaces) {
+ if (clazz.isNotProgramClass()) {
+ return;
+ }
+ // We need to introduce them in deterministic order for deterministic compilation.
+ ArrayList<DexType> sortedEmulatedInterfaces = new ArrayList<>(emulatedInterfaces);
+ Collections.sort(sortedEmulatedInterfaces, DexType::slowCompareTo);
+ List<GenericSignature.ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>();
+ for (DexType extraInterface : sortedEmulatedInterfaces) {
+ extraInterfaceSignatures.add(
+ new GenericSignature.ClassTypeSignature(rewriter.getEmulatedInterface(extraInterface)));
+ }
+ clazz.asProgramClass().addExtraInterfaces(extraInterfaceSignatures);
+ }
+
+ // If any of the signature would lead to a different behavior than the default method on the
+ // emulated interface, we need to resolve the forwarding methods.
+ private boolean shouldResolveForwardingMethodsForEmulatedInterfaces(
+ DexClass clazz, EmulatedInterfaceInfo emulatedInterfaceInfo) {
+ AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+ for (Wrapper<DexMethod> signature : emulatedInterfaceInfo.signatures.signatures) {
+ ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(signature.get(), clazz);
+ if (resolutionResult.isFailedResolution()) {
+ return true;
+ }
+ DexClass resolvedHolder = resolutionResult.asSingleResolution().getResolvedHolder();
+ if (!resolvedHolder.isLibraryClass()
+ && !emulatedInterfaceInfo.emulatedInterfaces.contains(resolvedHolder.type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void resolveForwardingMethods(
+ DexClass clazz,
+ ClassInfo superInfo,
+ MethodSignatures signatures,
+ Builder<DexEncodedMethod> additionalForwards) {
for (Wrapper<DexMethod> wrapper : signatures.signatures) {
resolveForwardForSignature(
clazz,
@@ -268,7 +467,6 @@
}
});
}
- return ClassInfo.create(superInfo, additionalForwards.build());
}
// Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
@@ -439,7 +637,10 @@
// a library class or is not, but cannot be both.
ReportingContext thisContext = context.forClass(clazz);
ClassInfo superInfo = visitClassInfo(clazz.superType, thisContext);
- MethodSignatures signatures = visitLibraryClassInfo(clazz.superType);
+ SignaturesInfo signatures = visitLibraryClassInfo(clazz.superType);
+ // The class may inherit emulated interface info from its program superclass if the latter
+ // did not require to resolve the forwarding methods for emualted interfaces.
+ signatures = signatures.withEmulatedInterfaceInfo(superInfo.emulatedInterfaceInfo);
assert superInfo.isEmpty() || signatures.isEmpty();
for (DexType iface : clazz.interfaces.values) {
signatures = signatures.merge(visitInterfaceInfo(iface, thisContext));
@@ -447,24 +648,24 @@
return computeClassInfo(clazz, superInfo, signatures);
}
- private MethodSignatures visitLibraryClassInfo(DexType type) {
+ private SignaturesInfo visitLibraryClassInfo(DexType type) {
// No desugaring required, no library class analysis.
if (ignoreLibraryInfo()) {
- return MethodSignatures.EMPTY;
+ return SignaturesInfo.EMPTY;
}
DexClass clazz = definitionOrNull(type, LibraryReportingContext.LIBRARY_CONTEXT);
- return clazz == null ? MethodSignatures.EMPTY : visitLibraryClassInfo(clazz);
+ return clazz == null ? SignaturesInfo.EMPTY : visitLibraryClassInfo(clazz);
}
- private MethodSignatures visitLibraryClassInfo(DexClass clazz) {
+ private SignaturesInfo visitLibraryClassInfo(DexClass clazz) {
assert !clazz.isInterface();
return clazz.isLibraryClass()
? libraryClassInfo.computeIfAbsent(clazz.asLibraryClass(), this::visitLibraryClassInfoRaw)
- : MethodSignatures.EMPTY;
+ : SignaturesInfo.EMPTY;
}
- private MethodSignatures visitLibraryClassInfoRaw(DexLibraryClass clazz) {
- MethodSignatures signatures = visitLibraryClassInfo(clazz.superType);
+ private SignaturesInfo visitLibraryClassInfoRaw(DexLibraryClass clazz) {
+ SignaturesInfo signatures = visitLibraryClassInfo(clazz.superType);
for (DexType iface : clazz.interfaces.values) {
signatures =
signatures.merge(visitInterfaceInfo(iface, LibraryReportingContext.LIBRARY_CONTEXT));
@@ -472,24 +673,26 @@
return computeLibraryClassInfo(clazz, signatures);
}
- private MethodSignatures visitInterfaceInfo(DexType iface, ReportingContext context) {
+ private SignaturesInfo visitInterfaceInfo(DexType iface, ReportingContext context) {
DexClass definition = definitionOrNull(iface, context);
- return definition == null ? MethodSignatures.EMPTY : visitInterfaceInfo(definition, context);
+ return definition == null ? SignaturesInfo.EMPTY : visitInterfaceInfo(definition, context);
}
- private MethodSignatures visitInterfaceInfo(DexClass iface, ReportingContext context) {
+ private SignaturesInfo visitInterfaceInfo(DexClass iface, ReportingContext context) {
if (iface.isLibraryClass() && ignoreLibraryInfo()) {
- return MethodSignatures.EMPTY;
+ return SignaturesInfo.EMPTY;
}
return interfaceInfo.computeIfAbsent(iface, key -> visitInterfaceInfoRaw(key, context));
}
- private MethodSignatures visitInterfaceInfoRaw(DexClass iface, ReportingContext context) {
+ private SignaturesInfo visitInterfaceInfoRaw(DexClass iface, ReportingContext context) {
ReportingContext thisContext = context.forClass(iface);
- MethodSignatures signatures = MethodSignatures.EMPTY;
+ SignaturesInfo interfaceInfo = SignaturesInfo.EMPTY;
for (DexType superiface : iface.interfaces.values) {
- signatures = signatures.merge(visitInterfaceInfo(superiface, thisContext));
+ interfaceInfo = interfaceInfo.merge(visitInterfaceInfo(superiface, thisContext));
}
- return computeInterfaceInfo(iface, signatures);
+ return rewriter.isEmulatedInterface(iface.type)
+ ? computeEmulatedInterfaceInfo(iface, interfaceInfo)
+ : computeInterfaceInfo(iface, interfaceInfo);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java
deleted file mode 100644
index 6f72091..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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.DexClass;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GenericSignature;
-import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class DesugaredLibraryEmulatedInterfaceDuplicator {
-
- final AppView<?> appView;
- final Map<DexType, DexType> emulatedInterfaces;
-
- public DesugaredLibraryEmulatedInterfaceDuplicator(AppView<?> appView) {
- this.appView = appView;
- emulatedInterfaces =
- appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
- }
-
- public void duplicateEmulatedInterfaces() {
- // All classes implementing an emulated interface now implements the interface and the
- // emulated one, as well as hidden overrides, for correct emulated dispatch.
- // We not that duplicated interfaces won't feature the correct type parameters in the
- // class signature since such signature is expected to be unused.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (clazz.type == appView.dexItemFactory().objectType) {
- continue;
- }
- if (emulatedInterfaces.containsKey(clazz.type)) {
- transformEmulatedInterfaces(clazz);
- } else {
- duplicateEmulatedInterfaces(clazz);
- }
- }
- }
-
- private void transformEmulatedInterfaces(DexProgramClass clazz) {
- List<ClassTypeSignature> newInterfaces = new ArrayList<>();
- GenericSignature.ClassSignature classSignature = clazz.getClassSignature();
- for (int i = 0; i < clazz.interfaces.size(); i++) {
- DexType itf = clazz.interfaces.values[i];
- assert emulatedInterfaces.containsKey(itf);
- List<FieldTypeSignature> typeArguments;
- if (classSignature == null) {
- typeArguments = Collections.emptyList();
- } else {
- ClassTypeSignature classTypeSignature = classSignature.superInterfaceSignatures().get(i);
- assert itf == classTypeSignature.type();
- typeArguments = classTypeSignature.typeArguments();
- }
- newInterfaces.add(new ClassTypeSignature(emulatedInterfaces.get(itf), typeArguments));
- }
- clazz.replaceInterfaces(newInterfaces);
- }
-
- private void duplicateEmulatedInterfaces(DexProgramClass clazz) {
- List<DexType> extraInterfaces = new ArrayList<>();
- LinkedList<DexClass> workList = new LinkedList<>();
- Set<DexType> processed = Sets.newIdentityHashSet();
- workList.add(clazz);
- while (!workList.isEmpty()) {
- DexClass dexClass = workList.removeFirst();
- if (processed.contains(dexClass.type)) {
- continue;
- }
- processed.add(dexClass.type);
- if (dexClass.superType != appView.dexItemFactory().objectType) {
- processSuperType(clazz.superType, extraInterfaces, workList);
- }
- for (DexType itf : dexClass.interfaces) {
- processSuperType(itf, extraInterfaces, workList);
- }
- }
- extraInterfaces = removeDuplicates(extraInterfaces);
- List<ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>();
- for (DexType extraInterface : extraInterfaces) {
- extraInterfaceSignatures.add(new ClassTypeSignature(extraInterface));
- }
- clazz.addExtraInterfaces(extraInterfaceSignatures);
- }
-
- private List<DexType> removeDuplicates(List<DexType> extraInterfaces) {
- if (extraInterfaces.size() <= 1) {
- return extraInterfaces;
- }
- // TODO(b/161399032): It would be nice to remove duplicate based on inheritance, i.e.,
- // if there is ConcurrentMap<K,V> and Map<K,V>, Map<K,V> can be removed.
- return new ArrayList<>(new HashSet<>(extraInterfaces));
- }
-
- void processSuperType(
- DexType superType, List<DexType> extraInterfaces, LinkedList<DexClass> workList) {
- if (emulatedInterfaces.containsKey(superType)) {
- extraInterfaces.add(emulatedInterfaces.get(superType));
- } else {
- DexClass superClass = appView.definitionFor(superType);
- if (shouldProcessSuperclass(superClass)) {
- workList.add(superClass);
- }
- }
- }
-
- private boolean shouldProcessSuperclass(DexClass superclazz) {
- if (appView.options().isDesugaredLibraryCompilation()) {
- return false;
- }
- // TODO(b/161399032): Pay-as-you-go design: stop duplication on library boundaries.
- return superclazz != null && superclazz.isLibraryClass();
- }
-}
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 61a5c56..ee05401 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
@@ -26,8 +26,10 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -207,6 +209,10 @@
return emulatedInterfaces.containsKey(itf);
}
+ DexType getEmulatedInterface(DexType itf) {
+ return emulatedInterfaces.get(itf);
+ }
+
// Rewrites the references to static and default interface methods.
// NOTE: can be called for different methods concurrently.
public void rewriteMethodReferences(DexEncodedMethod encodedMethod, IRCode code) {
@@ -430,8 +436,19 @@
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
DexType dexType = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
if (dexType != null) {
- rewriteCurrentInstructionToEmulatedInterfaceCall(
- dexType, invokedMethod, invokeMethod, instructions);
+ // The call potentially ends up in a library class, in which case we need to rewrite,
+ // since the code may be in the desugared library.
+ SingleResolutionResult resolution =
+ appView
+ .appInfoForDesugaring()
+ .resolveMethod(invokedMethod, invokeMethod.getInterfaceBit())
+ .asSingleResolution();
+ if (resolution != null
+ && (resolution.getResolvedHolder().isLibraryClass()
+ || appView.options().isDesugaredLibraryCompilation())) {
+ rewriteCurrentInstructionToEmulatedInterfaceCall(
+ dexType, invokedMethod, invokeMethod, instructions);
+ }
}
}
}
@@ -448,17 +465,6 @@
if (dexClass == null) {
return null;
}
- // TODO(b/120884788): Make sure program class are looked up before library class.
- // Since program classes are desugared, no need to rewrite invokes which can target only
- // program types.
- if (!appView.options().isDesugaredLibraryCompilation() && !dexClass.isLibraryClass()) {
- return null;
- }
- // Since desugared library classes are desugared, no need to rewrite invokes which can target
- // only such classes program types.
- if (appView.rewritePrefix.hasRewrittenType(dexClass.type, appView)) {
- return null;
- }
DexEncodedMethod singleTarget = null;
if (dexClass.isInterface()) {
// Look for exact method on the interface.
@@ -934,11 +940,11 @@
if (appView.options().isDesugaredLibraryCompilation()) {
generateEmulateInterfaceLibrary(builder);
}
- new DesugaredLibraryEmulatedInterfaceDuplicator(appView).duplicateEmulatedInterfaces();
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
processClasses(builder, flavour, synthesizedMethods::add);
+ transformEmulatedInterfaces();
// Process interfaces, create companion or dispatch class if needed, move static
// methods to companion class, copy default interface methods to companion classes,
@@ -976,6 +982,40 @@
clear();
}
+ private void transformEmulatedInterfaces() {
+ for (DexType dexType : emulatedInterfaces.keySet()) {
+ DexClass dexClass = appView.definitionFor(dexType);
+ if (dexClass != null && dexClass.isProgramClass()) {
+ transformEmulatedInterfaces(dexClass.asProgramClass());
+ }
+ }
+ }
+
+ // The method transforms emulated interface such as they implement the rewritten version
+ // of each emulated interface they implement. Such change should have no effect on the look-up
+ // results, since each class implementing an emulated interface should also implement the
+ // rewritten one.
+ private void transformEmulatedInterfaces(DexProgramClass clazz) {
+ List<GenericSignature.ClassTypeSignature> newInterfaces = new ArrayList<>();
+ GenericSignature.ClassSignature classSignature = clazz.getClassSignature();
+ for (int i = 0; i < clazz.interfaces.size(); i++) {
+ DexType itf = clazz.interfaces.values[i];
+ assert emulatedInterfaces.containsKey(itf);
+ List<GenericSignature.FieldTypeSignature> typeArguments;
+ if (classSignature == null) {
+ typeArguments = Collections.emptyList();
+ } else {
+ GenericSignature.ClassTypeSignature classTypeSignature =
+ classSignature.superInterfaceSignatures().get(i);
+ assert itf == classTypeSignature.type();
+ typeArguments = classTypeSignature.typeArguments();
+ }
+ newInterfaces.add(
+ new GenericSignature.ClassTypeSignature(emulatedInterfaces.get(itf), typeArguments));
+ }
+ clazz.replaceInterfaces(newInterfaces);
+ }
+
private void clear() {
this.cache.clear();
this.synthesizedMethods.clear();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index c9e47c2..9b8f930 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -233,7 +233,7 @@
new DexEncodedMethod(
mainMethod,
MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
+ Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SYNTHETIC, false),
MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
index 7bc5e6b..7f99055 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
@@ -22,7 +22,6 @@
private final DexType nestConstructorType;
private final Map<DexField, DexMethod> getFieldMap;
private final Map<DexField, DexMethod> putFieldMap;
- private final AppView<?> appView;
NestedPrivateMethodLens(
AppView<?> appView,
@@ -43,7 +42,6 @@
assert methodMap instanceof IdentityHashMap;
assert getFieldMap instanceof IdentityHashMap;
assert putFieldMap instanceof IdentityHashMap;
- this.appView = appView;
this.nestConstructorType = nestConstructorType;
this.getFieldMap = getFieldMap;
this.putFieldMap = putFieldMap;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 70efbeb..4c61a1c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1129,10 +1129,9 @@
BasicBlock defaultTarget = theSwitch.fallthroughBlock();
SwitchCaseEliminator eliminator = null;
BasicBlockBehavioralSubsumption behavioralSubsumption =
- new BasicBlockBehavioralSubsumption(appView, code.context());
+ new BasicBlockBehavioralSubsumption(appView, code);
// Compute the set of switch cases that can be removed.
- int alwaysHitCase = -1;
for (int i = 0; i < theSwitch.numberOfKeys(); i++) {
BasicBlock targetBlock = theSwitch.targetBlock(i);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 71684b1..26aa681 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -255,12 +255,21 @@
}
@Override
+ public boolean isDeadIfInValueIsDead() {
+ return true;
+ }
+
+ @Override
public Iterable<Value> getValuesRequiredToBeDead() {
return () -> Iterators.singletonIterator(inValueRequiredToBeDead);
}
};
}
+ public boolean isDeadIfInValueIsDead() {
+ return false;
+ }
+
public boolean isDeadIfOutValueIsDead() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 89681f0..4c7570c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.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.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -116,9 +117,12 @@
}
}
- if (appView.options().testing.enableInvokeSuperToInvokeVirtualRewriting) {
- if (current.isInvokeSuper()) {
- InvokeSuper invoke = current.asInvokeSuper();
+ if (current.isInvokeSuper()) {
+ InvokeSuper invoke = current.asInvokeSuper();
+
+ // Check if the instruction can be rewritten to invoke-super. This allows inlining of the
+ // enclosing method into contexts outside the current class.
+ if (appView.options().testing.enableInvokeSuperToInvokeVirtualRewriting) {
DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget != null) {
DexClass holder = appView.definitionForHolder(singleTarget, context);
@@ -134,10 +138,23 @@
if (newSingleTarget == singleTarget) {
it.replaceCurrentInstruction(
new InvokeVirtual(invokedMethod, invoke.outValue(), invoke.arguments()));
+ continue;
}
}
- continue;
}
+
+ // Rebind the invoke to the most specific target.
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexClassAndMethod reboundTarget = rebindSuperInvokeToMostSpecific(invokedMethod, context);
+ if (reboundTarget != null && reboundTarget.getReference() != invokedMethod) {
+ it.replaceCurrentInstruction(
+ new InvokeSuper(
+ reboundTarget.getReference(),
+ invoke.outValue(),
+ invoke.arguments(),
+ reboundTarget.getHolder().isInterface()));
+ }
+ continue;
}
if (current.isInvokeVirtual()) {
@@ -274,6 +291,33 @@
assert code.isConsistentSSA();
}
+ /** This rebinds invoke-super instructions to their most specific target. */
+ private DexClassAndMethod rebindSuperInvokeToMostSpecific(
+ DexMethod target, ProgramMethod context) {
+ DexEncodedMethod definition = appView.appInfo().lookupSuperTarget(target, context);
+ if (definition == null) {
+ return null;
+ }
+
+ DexClass holder = appView.definitionFor(definition.getHolderType());
+ if (holder == null) {
+ assert false;
+ return null;
+ }
+
+ if (holder.isInterface() && holder.getType() != context.getHolder().superType) {
+ // Not allowed.
+ return null;
+ }
+
+ DexClassAndMethod method = DexClassAndMethod.create(holder, definition);
+ if (AccessControl.isMemberAccessible(method, holder, context, appView).isPossiblyFalse()) {
+ return null;
+ }
+
+ return method;
+ }
+
/**
* This rebinds invoke-virtual instructions to their most specific target.
*
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 273f7b7..9f790ec 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
@@ -365,36 +365,36 @@
}
public static ConstraintWithTarget deriveConstraint(
- DexProgramClass context, DexType targetHolder, AccessFlags<?> flags, AppView<?> appView) {
+ ProgramMethod context, DexType targetHolder, AccessFlags<?> flags, AppView<?> appView) {
if (flags.isPublic()) {
return ALWAYS;
} else if (flags.isPrivate()) {
- if (context.isInANest()) {
- return NestUtils.sameNest(context.getType(), targetHolder, appView)
+ if (context.getHolder().isInANest()) {
+ return NestUtils.sameNest(context.getHolderType(), targetHolder, appView)
? new ConstraintWithTarget(Constraint.SAMENEST, targetHolder)
: NEVER;
}
- return targetHolder == context.type
+ return targetHolder == context.getHolderType()
? new ConstraintWithTarget(Constraint.SAMECLASS, targetHolder)
: NEVER;
} else if (flags.isProtected()) {
- if (targetHolder.isSamePackage(context.type)) {
+ if (targetHolder.isSamePackage(context.getHolderType())) {
// Even though protected, this is visible via the same package from the context.
return new ConstraintWithTarget(Constraint.PACKAGE, targetHolder);
- } else if (appView.isSubtype(context.type, targetHolder).isTrue()) {
+ } else if (appView.isSubtype(context.getHolderType(), targetHolder).isTrue()) {
return new ConstraintWithTarget(Constraint.SUBCLASS, targetHolder);
}
return NEVER;
} else {
/* package-private */
- return targetHolder.isSamePackage(context.type)
+ return targetHolder.isSamePackage(context.getHolderType())
? new ConstraintWithTarget(Constraint.PACKAGE, targetHolder)
: NEVER;
}
}
public static ConstraintWithTarget classIsVisible(
- DexProgramClass context, DexType clazz, AppView<?> appView) {
+ ProgramMethod context, DexType clazz, AppView<?> appView) {
if (clazz.isArrayType()) {
return classIsVisible(context, clazz.toArrayElementType(appView.dexItemFactory()), appView);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 0968624..ff7668a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -44,8 +45,6 @@
private GraphLens graphLens;
public InliningConstraints(AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
- assert graphLens.isContextFreeForMethods();
- assert appView.graphLens() != graphLens || graphLens.isIdentityLens();
this.appView = appView;
this.graphLens = graphLens; // Note: Intentionally *not* appView.graphLens().
}
@@ -90,16 +89,15 @@
return ConstraintWithTarget.ALWAYS;
}
- public ConstraintWithTarget forDexItemBasedConstString(
- DexReference type, DexProgramClass context) {
+ public ConstraintWithTarget forDexItemBasedConstString(DexReference type, ProgramMethod context) {
return ConstraintWithTarget.ALWAYS;
}
- public ConstraintWithTarget forCheckCast(DexType type, DexProgramClass context) {
+ public ConstraintWithTarget forCheckCast(DexType type, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
- public ConstraintWithTarget forConstClass(DexType type, DexProgramClass context) {
+ public ConstraintWithTarget forConstClass(DexType type, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
@@ -127,23 +125,23 @@
return ConstraintWithTarget.ALWAYS;
}
- public ConstraintWithTarget forInitClass(DexType clazz, DexProgramClass context) {
+ public ConstraintWithTarget forInitClass(DexType clazz, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, clazz, appView);
}
- public ConstraintWithTarget forInstanceGet(DexField field, DexProgramClass context) {
+ public ConstraintWithTarget forInstanceGet(DexField field, ProgramMethod context) {
return forFieldInstruction(field, context);
}
- public ConstraintWithTarget forInstanceOf(DexType type, DexProgramClass context) {
+ public ConstraintWithTarget forInstanceOf(DexType type, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
- public ConstraintWithTarget forInstancePut(DexField field, DexProgramClass context) {
+ public ConstraintWithTarget forInstancePut(DexField field, ProgramMethod context) {
return forFieldInstruction(field, context);
}
- public ConstraintWithTarget forInvoke(DexMethod method, Type type, DexProgramClass context) {
+ public ConstraintWithTarget forInvoke(DexMethod method, Type type, ProgramMethod context) {
switch (type) {
case DIRECT:
return forInvokeDirect(method, context);
@@ -169,8 +167,9 @@
return ConstraintWithTarget.NEVER;
}
- public ConstraintWithTarget forInvokeDirect(DexMethod method, DexProgramClass context) {
- DexMethod lookup = graphLens.lookupMethod(method);
+ public ConstraintWithTarget forInvokeDirect(DexMethod method, ProgramMethod context) {
+ DexMethod lookup =
+ graphLens.lookupMethod(method, context.getReference(), Type.DIRECT).getReference();
if (lookup.holder.isArrayType()) {
return ConstraintWithTarget.ALWAYS;
}
@@ -181,25 +180,27 @@
return forResolvedMember(resolutionResult.getInitialResolutionHolder(), context, target);
}
- public ConstraintWithTarget forInvokeInterface(DexMethod method, DexProgramClass context) {
- DexMethod lookup = graphLens.lookupMethod(method);
+ public ConstraintWithTarget forInvokeInterface(DexMethod method, ProgramMethod context) {
+ DexMethod lookup =
+ graphLens.lookupMethod(method, context.getReference(), Type.INTERFACE).getReference();
return forVirtualInvoke(lookup, context, true);
}
- public ConstraintWithTarget forInvokeMultiNewArray(DexType type, DexProgramClass context) {
+ public ConstraintWithTarget forInvokeMultiNewArray(DexType type, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
- public ConstraintWithTarget forInvokeNewArray(DexType type, DexProgramClass context) {
+ public ConstraintWithTarget forInvokeNewArray(DexType type, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
- public ConstraintWithTarget forInvokePolymorphic(DexMethod method, DexProgramClass context) {
+ public ConstraintWithTarget forInvokePolymorphic(DexMethod method, ProgramMethod context) {
return ConstraintWithTarget.NEVER;
}
- public ConstraintWithTarget forInvokeStatic(DexMethod method, DexProgramClass context) {
- DexMethod lookup = graphLens.lookupMethod(method);
+ public ConstraintWithTarget forInvokeStatic(DexMethod method, ProgramMethod context) {
+ DexMethod lookup =
+ graphLens.lookupMethod(method, context.getReference(), Type.STATIC).getReference();
if (lookup.holder.isArrayType()) {
return ConstraintWithTarget.ALWAYS;
}
@@ -220,19 +221,21 @@
@SuppressWarnings("ConstantConditions")
private DexEncodedMethod singleTargetWhileVerticalClassMerging(
ResolutionResult resolutionResult,
- DexProgramClass context,
+ ProgramMethod context,
TriFunction<ResolutionResult, DexProgramClass, AppInfoWithClassHierarchy, DexEncodedMethod>
lookup) {
if (!resolutionResult.isSingleResolution()) {
return null;
}
- DexEncodedMethod dexEncodedMethod = lookup.apply(resolutionResult, context, appView.appInfo());
+ DexEncodedMethod dexEncodedMethod =
+ lookup.apply(resolutionResult, context.getHolder(), appView.appInfo());
if (!isVerticalClassMerging() || dexEncodedMethod != null) {
return dexEncodedMethod;
}
assert isVerticalClassMerging();
- assert graphLens.lookupType(context.superType) == context.type;
- DexProgramClass superContext = appView.programDefinitionFor(context.superType, context);
+ assert graphLens.lookupType(context.getHolder().superType) == context.getHolderType();
+ DexProgramClass superContext =
+ appView.programDefinitionFor(context.getHolder().superType, context);
if (superContext == null) {
return null;
}
@@ -245,13 +248,14 @@
return null;
}
- public ConstraintWithTarget forInvokeSuper(DexMethod method, DexProgramClass context) {
+ public ConstraintWithTarget forInvokeSuper(DexMethod method, ProgramMethod context) {
// The semantics of invoke super depend on the context.
- return new ConstraintWithTarget(Constraint.SAMECLASS, context.type);
+ return new ConstraintWithTarget(Constraint.SAMECLASS, context.getHolderType());
}
- public ConstraintWithTarget forInvokeVirtual(DexMethod method, DexProgramClass context) {
- DexMethod lookup = graphLens.lookupMethod(method);
+ public ConstraintWithTarget forInvokeVirtual(DexMethod method, ProgramMethod context) {
+ DexMethod lookup =
+ graphLens.lookupMethod(method, context.getReference(), Type.VIRTUAL).getReference();
return forVirtualInvoke(lookup, context, false);
}
@@ -275,7 +279,7 @@
return ConstraintWithTarget.ALWAYS;
}
- public ConstraintWithTarget forNewArrayEmpty(DexType type, DexProgramClass context) {
+ public ConstraintWithTarget forNewArrayEmpty(DexType type, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
@@ -283,7 +287,7 @@
return ConstraintWithTarget.ALWAYS;
}
- public ConstraintWithTarget forNewInstance(DexType type, DexProgramClass context) {
+ public ConstraintWithTarget forNewInstance(DexType type, ProgramMethod context) {
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
@@ -299,11 +303,11 @@
return ConstraintWithTarget.ALWAYS;
}
- public ConstraintWithTarget forStaticGet(DexField field, DexProgramClass context) {
+ public ConstraintWithTarget forStaticGet(DexField field, ProgramMethod context) {
return forFieldInstruction(field, context);
}
- public ConstraintWithTarget forStaticPut(DexField field, DexProgramClass context) {
+ public ConstraintWithTarget forStaticPut(DexField field, ProgramMethod context) {
return forFieldInstruction(field, context);
}
@@ -331,7 +335,7 @@
return ConstraintWithTarget.NEVER;
}
- private ConstraintWithTarget forFieldInstruction(DexField field, DexProgramClass context) {
+ private ConstraintWithTarget forFieldInstruction(DexField field, ProgramMethod context) {
DexField lookup = graphLens.lookupField(field);
FieldResolutionResult fieldResolutionResult = appView.appInfo().resolveField(lookup);
return forResolvedMember(
@@ -341,7 +345,7 @@
}
private ConstraintWithTarget forVirtualInvoke(
- DexMethod method, DexProgramClass context, boolean isInterface) {
+ DexMethod method, ProgramMethod context, boolean isInterface) {
if (method.holder.isArrayType()) {
return ConstraintWithTarget.ALWAYS;
}
@@ -359,7 +363,7 @@
private ConstraintWithTarget forResolvedMember(
DexClass initialResolutionHolder,
- DexProgramClass context,
+ ProgramMethod context,
DexEncodedMember<?, ?> resolvedMember) {
if (resolvedMember == null) {
// This will fail at runtime.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index f9f4d58..4e89840 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -125,7 +125,7 @@
}
// Make sure the target (base) type is visible.
ConstraintWithTarget constraints =
- ConstraintWithTarget.classIsVisible(context.getHolder(), baseType, appView);
+ ConstraintWithTarget.classIsVisible(context, baseType, appView);
if (constraints == ConstraintWithTarget.NEVER) {
return null;
}
@@ -201,7 +201,7 @@
}
// Make sure the (base) type is visible.
ConstraintWithTarget constraints =
- ConstraintWithTarget.classIsVisible(context.getHolder(), baseType, appView);
+ ConstraintWithTarget.classIsVisible(context, baseType, appView);
if (constraints == ConstraintWithTarget.NEVER) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index d815826..c076942 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -69,7 +70,7 @@
public class EnumUnboxingRewriter {
public static final String ENUM_UNBOXING_UTILITY_METHOD_PREFIX = "$enumboxing$";
- private static final int REQUIRED_CLASS_FILE_VERSION = 52;
+ private static final CfVersion REQUIRED_CLASS_FILE_VERSION = CfVersion.V1_8;
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory factory;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index ee35087..9ccbbbc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
@@ -339,7 +340,8 @@
IRConverter converter,
OptimizationFeedback feedback,
Builder<?> builder,
- ExecutorService executorService)
+ ExecutorService executorService,
+ GraphLens appliedGraphLens)
throws ExecutionException {
if (lambdas.isEmpty()) {
appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
@@ -389,7 +391,7 @@
// Rewrite lambda class references into lambda group class
// references inside methods from the processing queue.
- rewriteLambdaReferences(converter, executorService);
+ rewriteLambdaReferences(converter, executorService, appliedGraphLens);
this.mode = null;
appView.setHorizontallyMergedLambdaClasses(
@@ -454,12 +456,13 @@
}
}
- private void rewriteLambdaReferences(IRConverter converter, ExecutorService executorService)
+ private void rewriteLambdaReferences(
+ IRConverter converter, ExecutorService executorService, GraphLens appliedGraphLens)
throws ExecutionException {
if (methodsToReprocess.isEmpty()) {
return;
}
- SortedProgramMethodSet methods = methodsToReprocess.build(appView);
+ SortedProgramMethodSet methods = methodsToReprocess.build(appView, appliedGraphLens);
converter.processMethodsConcurrently(methods, executorService);
assert methods.stream()
.map(DexClassAndMethod::getDefinition)
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index b914a14..d96b5d4 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -4,11 +4,10 @@
package com.android.tools.r8.jar;
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
-import static org.objectweb.asm.Opcodes.V1_6;
-import static org.objectweb.asm.Opcodes.V1_8;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.errors.CodeSizeOverflowDiagnostic;
@@ -85,6 +84,8 @@
public final ProguardMapSupplier proguardMapSupplier;
+ private static final CfVersion MIN_VERSION_FOR_COMPILER_GENERATED_CODE = CfVersion.V1_6;
+
public CfApplicationWriter(
AppView<?> appView,
Marker marker,
@@ -168,8 +169,8 @@
}
String sourceDebug = getSourceDebugExtension(clazz.annotations());
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug);
- int version = getClassFileVersion(clazz);
- if (version >= V1_8) {
+ CfVersion version = getClassFileVersion(clazz);
+ if (version.isGreaterThanOrEqual(CfVersion.V1_8)) {
// JDK8 and after ignore ACC_SUPER so unset it.
clazz.accessFlags.unsetSuper();
} else {
@@ -193,7 +194,7 @@
for (int i = 0; i < clazz.interfaces.values.length; i++) {
interfaces[i] = namingLens.lookupInternalName(clazz.interfaces.values[i]);
}
- writer.visit(version, access, name, signature, superName, interfaces);
+ writer.visit(version.raw(), access, name, signature, superName, interfaces);
writeAnnotations(writer::visitAnnotation, clazz.annotations().annotations);
ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations());
@@ -238,7 +239,7 @@
options.reporter, handler -> consumer.accept(ByteDataView.of(result), desc, handler));
}
- private int getClassFileVersion(DexEncodedMethod method) {
+ private CfVersion getClassFileVersion(DexEncodedMethod method) {
if (!method.hasClassFileVersion()) {
// In this case bridges have been introduced for the Cf back-end,
// which do not have class file version.
@@ -247,18 +248,22 @@
|| options.cfToCfDesugar;
// TODO(b/146424042): We may call static methods on interface classes so we have to go for
// Java 8.
- return options.cfToCfDesugar ? V1_8 : 0;
+ assert MIN_VERSION_FOR_COMPILER_GENERATED_CODE.isLessThan(CfVersion.V1_8);
+ return options.cfToCfDesugar ? CfVersion.V1_8 : MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
}
return method.getClassFileVersion();
}
- private int getClassFileVersion(DexProgramClass clazz) {
- int version = clazz.hasClassFileVersion() ? clazz.getInitialClassFileVersion() : V1_6;
+ private CfVersion getClassFileVersion(DexProgramClass clazz) {
+ CfVersion version =
+ clazz.hasClassFileVersion()
+ ? clazz.getInitialClassFileVersion()
+ : MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
for (DexEncodedMethod method : clazz.directMethods()) {
- version = Math.max(version, getClassFileVersion(method));
+ version = version.max(getClassFileVersion(method));
}
for (DexEncodedMethod method : clazz.virtualMethods()) {
- version = Math.max(version, getClassFileVersion(method));
+ version = version.max(getClassFileVersion(method));
}
return version;
}
@@ -335,7 +340,7 @@
private void writeMethod(
ProgramMethod method,
- int classFileVersion,
+ CfVersion classFileVersion,
LensCodeRewriterUtils rewriter,
ClassWriter writer,
ImmutableMap<DexString, DexValue> defaults) {
@@ -497,7 +502,7 @@
private void writeCode(
ProgramMethod method,
- int classFileVersion,
+ CfVersion classFileVersion,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
CfCode code = method.getDefinition().getCode().asCfCode();
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
new file mode 100644
index 0000000..7e66078
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -0,0 +1,128 @@
+// Copyright (c) 2018, 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.optimize;
+
+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.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * This lens is used to populate the rebound field reference during lookup, such that both the
+ * non-rebound and rebound field references are available to all descendants of this lens.
+ *
+ * <p>TODO(b/157616970): All uses of this should be replaced by {@link MemberRebindingIdentityLens}.
+ */
+public class FieldRebindingIdentityLens extends NonIdentityGraphLens {
+
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap;
+
+ private FieldRebindingIdentityLens(
+ Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap,
+ DexItemFactory dexItemFactory,
+ GraphLens previousLens) {
+ super(dexItemFactory, previousLens);
+ this.nonReboundFieldReferenceToDefinitionMap = nonReboundFieldReferenceToDefinitionMap;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public boolean hasCodeRewritings() {
+ return false;
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ assert previous.getReboundReference() == null;
+ return FieldLookupResult.builder(this)
+ .setReference(previous.getReference())
+ .setReboundReference(getReboundFieldReference(previous.getReference()))
+ .build();
+ }
+
+ private DexField getReboundFieldReference(DexField field) {
+ return nonReboundFieldReferenceToDefinitionMap.getOrDefault(field, field);
+ }
+
+ @Override
+ public DexType getOriginalType(DexType type) {
+ return getPrevious().getOriginalType(type);
+ }
+
+ @Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return getPrevious().getOriginalFieldSignature(field);
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return getPrevious().getOriginalMethodSignature(method);
+ }
+
+ @Override
+ public DexField getRenamedFieldSignature(DexField originalField) {
+ return getPrevious().getRenamedFieldSignature(originalField);
+ }
+
+ @Override
+ public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ return getPrevious().getRenamedMethodSignature(originalMethod, applied);
+ }
+
+ @Override
+ public final DexType internalDescribeLookupClassType(DexType previous) {
+ return previous;
+ }
+
+ @Override
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ return previous;
+ }
+
+ @Override
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ }
+
+ @Override
+ public boolean isContextFreeForMethods() {
+ return getPrevious().isContextFreeForMethods();
+ }
+
+ public static class Builder {
+
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
+ new IdentityHashMap<>();
+
+ private Builder() {}
+
+ void recordDefinitionForNonReboundFieldReference(
+ DexField nonReboundFieldReference, DexField reboundFieldReference) {
+ nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
+ }
+
+ FieldRebindingIdentityLens build(DexItemFactory dexItemFactory) {
+ // This intentionally does not return null when the map is empty. In this case there are no
+ // non-rebound field references, but the member rebinding lens is still needed to populate the
+ // rebound reference during field lookup.
+ return new FieldRebindingIdentityLens(
+ nonReboundFieldReferenceToDefinitionMap, dexItemFactory, GraphLens.getIdentityLens());
+ }
+ }
+}
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 5d3f09b..9d4121c 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -3,28 +3,31 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
-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;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BiForEachable;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -34,14 +37,14 @@
private final GraphLens lens;
private final InternalOptions options;
- private final MemberRebindingLens.Builder builder;
+ private final MemberRebindingLens.Builder lensBuilder;
public MemberRebindingAnalysis(AppView<AppInfoWithLiveness> appView) {
assert appView.graphLens().isContextFreeForMethods();
this.appView = appView;
this.lens = appView.graphLens();
this.options = appView.options();
- this.builder = MemberRebindingLens.builder(appView);
+ this.lensBuilder = MemberRebindingLens.builder(appView);
}
private DexMethod validTargetFor(DexMethod target, DexMethod original) {
@@ -192,7 +195,8 @@
method, target, originalClass, targetClass, lookupTarget);
}
}
- builder.map(method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
+ lensBuilder.map(
+ method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
});
}
@@ -248,11 +252,9 @@
return false;
}
ConstraintWithTarget classVisibility =
- ConstraintWithTarget.deriveConstraint(
- context.getHolder(), holderType, holder.accessFlags, appView);
+ ConstraintWithTarget.deriveConstraint(context, holderType, holder.accessFlags, appView);
ConstraintWithTarget methodVisibility =
- ConstraintWithTarget.deriveConstraint(
- context.getHolder(), holderType, method.accessFlags, appView);
+ ConstraintWithTarget.deriveConstraint(context, holderType, method.accessFlags, appView);
// We may need bridge for visibility if the target class is not visible while the target method
// is visible from the calling context.
return classVisibility == ConstraintWithTarget.NEVER
@@ -312,59 +314,132 @@
return null;
}
- private void computeFieldRebinding() {
+ private void recordNonReboundFieldAccesses(ExecutorService executorService)
+ throws ExecutionException {
+ assert verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(executorService);
FieldAccessInfoCollection<?> fieldAccessInfoCollection =
appView.appInfo().getFieldAccessInfoCollection();
- fieldAccessInfoCollection.forEach(this::computeFieldRebindingForIndirectAccesses);
+ fieldAccessInfoCollection.forEach(lensBuilder::recordNonReboundFieldAccesses);
}
- private void computeFieldRebindingForIndirectAccesses(FieldAccessInfo fieldAccessInfo) {
- fieldAccessInfo.forEachIndirectAccessWithContexts(
- this::computeFieldRebindingForIndirectAccessWithContexts);
- }
-
- private void computeFieldRebindingForIndirectAccessWithContexts(
- DexField field, ProgramMethodSet contexts) {
- SuccessfulFieldResolutionResult resolutionResult =
- appView.appInfo().resolveField(field).asSuccessfulResolution();
- if (resolutionResult == null) {
- return;
- }
-
- DexClassAndField resolvedField = resolutionResult.getResolutionPair();
- if (resolvedField.getReference() == field) {
- assert false;
- return;
- }
-
- // Rebind to the lowest library class or program class. Do not rebind accesses to fields that
- // are not visible from the access context.
- boolean accessibleInAllContexts = true;
- for (ProgramMethod context : contexts) {
- boolean inaccessibleInContext =
- AccessControl.isMemberAccessible(
- resolvedField, resolutionResult.getResolvedHolder(), context, appView)
- .isPossiblyFalse();
- if (inaccessibleInContext) {
- accessibleInAllContexts = false;
- break;
- }
- }
-
- if (accessibleInAllContexts) {
- builder.map(
- field,
- lens.lookupField(
- validTargetFor(resolvedField.getReference(), field, DexClass::lookupField)));
- }
- }
-
- public GraphLens run() {
+ public MemberRebindingLens run(ExecutorService executorService) throws ExecutionException {
AppInfoWithLiveness appInfo = appView.appInfo();
computeMethodRebinding(appInfo.getMethodAccessInfoCollection());
- computeFieldRebinding();
- GraphLens lens = builder.build(this.lens);
+ recordNonReboundFieldAccesses(executorService);
appInfo.getFieldAccessInfoCollection().flattenAccessContexts();
- return lens;
+ return lensBuilder.build();
+ }
+
+ private boolean verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(
+ ExecutorService executorService) throws ExecutionException {
+ Set<DexField> nonReboundFieldReferences = computeNonReboundFieldReferences(executorService);
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection =
+ appView.appInfo().getFieldAccessInfoCollection();
+ fieldAccessInfoCollection.forEach(
+ info -> {
+ DexField reboundFieldReference = info.getField();
+ info.forEachIndirectAccess(
+ nonReboundFieldReference -> {
+ assert reboundFieldReference != nonReboundFieldReference;
+ assert reboundFieldReference
+ == appView
+ .appInfo()
+ .resolveField(nonReboundFieldReference)
+ .getResolvedFieldReference();
+ nonReboundFieldReferences.remove(nonReboundFieldReference);
+ });
+ });
+ assert nonReboundFieldReferences.isEmpty();
+ return true;
+ }
+
+ private Set<DexField> computeNonReboundFieldReferences(ExecutorService executorService)
+ throws ExecutionException {
+ Set<DexField> nonReboundFieldReferences = Sets.newConcurrentHashSet();
+ ThreadUtils.processItems(
+ appView.appInfo()::forEachMethod,
+ method -> {
+ if (method.getDefinition().hasCode()) {
+ method.registerCodeReferences(
+ new UseRegistry(appView.dexItemFactory()) {
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ registerFieldReference(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ registerFieldReference(field);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ registerFieldReference(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ registerFieldReference(field);
+ }
+
+ private void registerFieldReference(DexField field) {
+ SuccessfulFieldResolutionResult resolutionResult =
+ appView.appInfo().resolveField(field).asSuccessfulResolution();
+ if (resolutionResult != null
+ && resolutionResult.getResolvedField().toReference() != field) {
+ nonReboundFieldReferences.add(field);
+ }
+ }
+
+ @Override
+ public void registerInitClass(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeDirect(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInstanceOf(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ // Intentionally empty.
+ }
+ });
+ }
+ },
+ executorService);
+ return nonReboundFieldReferences;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 944af37..8e3f4c1 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -165,12 +165,14 @@
synchronized (fieldAccessInfo) {
// Record the fact that there is a non-rebound access to the given field. We don't
// distinguish between non-rebound reads and writes, so we just record it as a read.
- ConcreteAccessContexts accessContexts =
- fieldAccessInfo.getReadsWithContexts().isConcrete()
- ? fieldAccessInfo.getReadsWithContexts().asConcrete()
- : new ConcreteAccessContexts();
+ if (fieldAccessInfo.getReadsWithContexts().isBottom()) {
+ fieldAccessInfo.setReadsWithContexts(new ConcreteAccessContexts());
+ } else {
+ assert fieldAccessInfo.getReadsWithContexts().isConcrete();
+ }
// For the purpose of member rebinding, we don't care about the access contexts, so we
// simply use the empty set.
+ ConcreteAccessContexts accessContexts = fieldAccessInfo.getReadsWithContexts().asConcrete();
accessContexts.getAccessesWithContexts().put(field, ProgramMethodSet.empty());
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index a6a6021..9e23dd0 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -4,88 +4,104 @@
package com.android.tools.r8.optimize;
+import static com.android.tools.r8.graph.GraphLens.NestedGraphLens.mapVirtualInterfaceInvocationTypes;
+
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.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.google.common.collect.ImmutableMap;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
-public class MemberRebindingLens extends NestedGraphLens {
+public class MemberRebindingLens extends NonIdentityGraphLens {
- public static class Builder {
-
- private final AppView<?> appView;
-
- private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
- private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps = new IdentityHashMap<>();
-
- protected Builder(AppView<?> appView) {
- this.appView = appView;
- }
-
- public void map(DexField from, DexField to) {
- if (from == to) {
- assert !fieldMap.containsKey(from);
- return;
- }
- fieldMap.put(from, to);
- }
-
- public void map(DexMethod from, DexMethod to, Invoke.Type type) {
- if (from == to) {
- assert !methodMaps.containsKey(type) || methodMaps.get(type).getOrDefault(from, to) == to;
- return;
- }
- Map<DexMethod, DexMethod> methodMap =
- methodMaps.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
- assert methodMap.getOrDefault(from, to) == to;
- methodMap.put(from, to);
- }
-
- public GraphLens build(GraphLens previousLens) {
- if (fieldMap.isEmpty() && methodMaps.isEmpty()) {
- return previousLens;
- }
- return new MemberRebindingLens(appView, methodMaps, fieldMap, previousLens);
- }
- }
-
- private final AppView<?> appView;
+ private final AppView<AppInfoWithLiveness> appView;
private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps;
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap;
public MemberRebindingLens(
- AppView<?> appView,
+ AppView<AppInfoWithLiveness> appView,
Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps,
- Map<DexField, DexField> fieldMap,
- GraphLens previousLens) {
- super(
- ImmutableMap.of(),
- ImmutableMap.of(),
- fieldMap,
- null,
- null,
- previousLens,
- appView.dexItemFactory());
+ Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap) {
+ super(appView.dexItemFactory(), appView.graphLens());
this.appView = appView;
this.methodMaps = methodMaps;
+ this.nonReboundFieldReferenceToDefinitionMap = nonReboundFieldReferenceToDefinitionMap;
}
- public static Builder builder(AppView<?> appView) {
+ public static Builder builder(AppView<AppInfoWithLiveness> appView) {
return new Builder(appView);
}
@Override
- public boolean isLegitimateToHaveEmptyMappings() {
+ public boolean isMemberRebindingLens() {
return true;
}
@Override
+ public DexType getOriginalType(DexType type) {
+ return getPrevious().getOriginalType(type);
+ }
+
+ @Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return getPrevious().getOriginalFieldSignature(field);
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return getPrevious().getOriginalMethodSignature(method);
+ }
+
+ @Override
+ public DexField getRenamedFieldSignature(DexField originalField) {
+ return getPrevious().getRenamedFieldSignature(originalField);
+ }
+
+ @Override
+ public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ return this != applied
+ ? getPrevious().getRenamedMethodSignature(originalMethod, applied)
+ : originalMethod;
+ }
+
+ @Override
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ }
+
+ @Override
+ public boolean isContextFreeForMethods() {
+ return getPrevious().isContextFreeForMethods();
+ }
+
+ @Override
+ protected DexType internalDescribeLookupClassType(DexType previous) {
+ return previous;
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ assert previous.getReboundReference() == null;
+ return FieldLookupResult.builder(this)
+ .setReference(previous.getReference())
+ .setReboundReference(getReboundFieldReference(previous.getReference()))
+ .build();
+ }
+
+ private DexField getReboundFieldReference(DexField field) {
+ return nonReboundFieldReferenceToDefinitionMap.getOrDefault(field, field);
+ }
+
+ @Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context) {
Map<DexMethod, DexMethod> methodMap =
@@ -97,12 +113,68 @@
return MethodLookupResult.builder(this)
.setReference(newMethod)
.setPrototypeChanges(previous.getPrototypeChanges())
- .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+ .setType(
+ mapVirtualInterfaceInvocationTypes(
+ appView, newMethod, previous.getReference(), previous.getType()))
.build();
}
@Override
- protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
- return super.mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
+ DexItemFactory dexItemFactory, GraphLens lens) {
+ FieldRebindingIdentityLens.Builder builder = FieldRebindingIdentityLens.builder();
+ nonReboundFieldReferenceToDefinitionMap.forEach(
+ (nonReboundFieldReference, reboundFieldReference) -> {
+ DexField rewrittenReboundFieldReference = lens.lookupField(reboundFieldReference);
+ DexField rewrittenNonReboundFieldReference =
+ rewrittenReboundFieldReference.withHolder(
+ lens.lookupType(nonReboundFieldReference.getHolderType()), dexItemFactory);
+ builder.recordDefinitionForNonReboundFieldReference(
+ rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
+ });
+ return builder.build(dexItemFactory);
+ }
+
+ public static class Builder {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps = new IdentityHashMap<>();
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
+ new IdentityHashMap<>();
+
+ private Builder(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ public void map(DexMethod from, DexMethod to, Invoke.Type type) {
+ if (from == to) {
+ assert !methodMaps.containsKey(type) || methodMaps.get(type).getOrDefault(from, to) == to;
+ return;
+ }
+ Map<DexMethod, DexMethod> methodMap =
+ methodMaps.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
+ assert methodMap.getOrDefault(from, to) == to;
+ methodMap.put(from, to);
+ }
+
+ void recordNonReboundFieldAccesses(FieldAccessInfo info) {
+ DexField reboundFieldReference = info.getField();
+ info.forEachIndirectAccess(
+ nonReboundFieldReference ->
+ recordNonReboundFieldAccess(nonReboundFieldReference, reboundFieldReference));
+ }
+
+ private void recordNonReboundFieldAccess(
+ DexField nonReboundFieldReference, DexField reboundFieldReference) {
+ nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
+ }
+
+ public MemberRebindingLens build() {
+ return new MemberRebindingLens(appView, methodMaps, nonReboundFieldReferenceToDefinitionMap);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
index 5d2fef0..52e7b3d 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
@@ -40,6 +40,11 @@
}
@Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ return previous;
+ }
+
+ @Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context) {
if (previous.getType() == Type.DIRECT && publicizedMethods.contains(previous.getReference())) {
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
index 7da1116..aea3e82 100644
--- a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
@@ -149,7 +149,6 @@
.addAdaptResourceFilenames(ProguardPathList.builder().addFileName("**").build())
.build(),
getReporter());
- assert options.threadCount == ThreadUtils.NOT_SPECIFIED;
options.relocatorCompilation = true;
options.threadCount = getThreadCount();
options.programConsumer = consumer;
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
index a0f6645..d3ceea5 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
@@ -39,6 +39,11 @@
return getPrevious().getOriginalType(previous);
}
+ @Override
+ public boolean isSimpleRenaming(DexType from, DexType to) {
+ return originalTypes.get(to) == from || super.isSimpleRenaming(from, to);
+ }
+
public static class Builder {
protected final BiMap<DexType, DexType> originalTypes = HashBiMap.create();
diff --git a/src/main/java/com/android/tools/r8/retrace/Definition.java b/src/main/java/com/android/tools/r8/retrace/Definition.java
new file mode 100644
index 0000000..f955ac0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/Definition.java
@@ -0,0 +1,14 @@
+// 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.references.ClassReference;
+
+interface Definition {
+
+ String getName();
+
+ ClassReference getHolderClass();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/FieldDefinition.java b/src/main/java/com/android/tools/r8/retrace/FieldDefinition.java
new file mode 100644
index 0000000..3dcf1c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/FieldDefinition.java
@@ -0,0 +1,108 @@
+// 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.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import java.util.Objects;
+
+/** Internal encoding of a field that allows for having either basic info or full info. */
+abstract class FieldDefinition implements Definition {
+
+ static FieldDefinition create(ClassReference obfuscatedReference, String fieldName) {
+ return new BaseFieldDefinition(obfuscatedReference, fieldName);
+ }
+
+ public static FieldDefinition create(FieldReference field) {
+ return new FullFieldDefinition(field);
+ }
+
+ abstract FieldDefinition substituteHolder(ClassReference newHolder);
+
+ static class BaseFieldDefinition extends FieldDefinition {
+ private final ClassReference classReference;
+ private final String name;
+
+ private BaseFieldDefinition(ClassReference classReference, String name) {
+ this.classReference = classReference;
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public ClassReference getHolderClass() {
+ return classReference;
+ }
+
+ @Override
+ FieldDefinition substituteHolder(ClassReference newHolder) {
+ return FieldDefinition.create(classReference, name);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ BaseFieldDefinition that = (BaseFieldDefinition) o;
+ return classReference.equals(that.classReference) && name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(classReference, name);
+ }
+ }
+
+ static class FullFieldDefinition extends FieldDefinition {
+
+ private final FieldReference fieldReference;
+
+ private FullFieldDefinition(FieldReference fieldReference) {
+ this.fieldReference = fieldReference;
+ }
+
+ @Override
+ public String getName() {
+ return fieldReference.getFieldName();
+ }
+
+ @Override
+ public ClassReference getHolderClass() {
+ return fieldReference.getHolderClass();
+ }
+
+ @Override
+ FieldDefinition substituteHolder(ClassReference newHolder) {
+ return create(
+ Reference.field(newHolder, fieldReference.getFieldName(), fieldReference.getFieldType()));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ FullFieldDefinition that = (FullFieldDefinition) o;
+ return fieldReference.equals(that.fieldReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return fieldReference.hashCode();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MethodDefinition.java b/src/main/java/com/android/tools/r8/retrace/MethodDefinition.java
new file mode 100644
index 0000000..8641f13
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MethodDefinition.java
@@ -0,0 +1,135 @@
+// 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.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import java.util.Objects;
+
+/** Internal encoding of a method that allows for having either basic info or full info. */
+abstract class MethodDefinition implements Definition {
+
+ static MethodDefinition create(ClassReference classReference, String methodName) {
+ return new BaseMethodDefinition(classReference, methodName);
+ }
+
+ static MethodDefinition create(MethodReference methodReference) {
+ return new FullMethodDefinition(methodReference);
+ }
+
+ boolean isFullMethodDefinition() {
+ return false;
+ }
+
+ FullMethodDefinition asFullMethodDefinition() {
+ return null;
+ }
+
+ abstract MethodDefinition substituteHolder(ClassReference newHolder);
+
+ static class BaseMethodDefinition extends MethodDefinition {
+
+ private final ClassReference classReference;
+ private final String name;
+
+ private BaseMethodDefinition(ClassReference classReference, String name) {
+ this.classReference = classReference;
+ this.name = name;
+ }
+
+ @Override
+ public ClassReference getHolderClass() {
+ return classReference;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ MethodDefinition substituteHolder(ClassReference newHolder) {
+ return MethodDefinition.create(newHolder, name);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ BaseMethodDefinition that = (BaseMethodDefinition) o;
+ return classReference.equals(that.classReference) && name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(classReference, name);
+ }
+ }
+
+ static class FullMethodDefinition extends MethodDefinition {
+
+ private final MethodReference methodReference;
+
+ private FullMethodDefinition(MethodReference methodReference) {
+ this.methodReference = methodReference;
+ }
+
+ @Override
+ public ClassReference getHolderClass() {
+ return methodReference.getHolderClass();
+ }
+
+ @Override
+ public String getName() {
+ return methodReference.getMethodName();
+ }
+
+ @Override
+ boolean isFullMethodDefinition() {
+ return true;
+ }
+
+ @Override
+ FullMethodDefinition asFullMethodDefinition() {
+ return this;
+ }
+
+ @Override
+ MethodDefinition substituteHolder(ClassReference newHolder) {
+ return MethodDefinition.create(
+ Reference.method(
+ newHolder,
+ methodReference.getMethodName(),
+ methodReference.getFormalTypes(),
+ methodReference.getReturnType()));
+ }
+
+ MethodReference getMethodReference() {
+ return methodReference;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ FullMethodDefinition that = (FullMethodDefinition) o;
+ return methodReference.equals(that.methodReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return methodReference.hashCode();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/PlainStackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/PlainStackTraceVisitor.java
new file mode 100644
index 0000000..1234b4d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/PlainStackTraceVisitor.java
@@ -0,0 +1,229 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import static com.google.common.base.Predicates.not;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.retrace.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public final class PlainStackTraceVisitor
+ implements StackTraceVisitor<StackTraceElementStringProxy> {
+
+ private final List<String> stackTrace;
+ private final DiagnosticsHandler diagnosticsHandler;
+
+ PlainStackTraceVisitor(List<String> stackTrace, DiagnosticsHandler diagnosticsHandler) {
+ this.stackTrace = stackTrace;
+ this.diagnosticsHandler = diagnosticsHandler;
+ }
+
+ @Override
+ public void forEach(Consumer<StackTraceElementStringProxy> consumer) {
+ for (int i = 0; i < stackTrace.size(); i++) {
+ consumer.accept(parseLine(i + 1, stackTrace.get(i)));
+ }
+ }
+
+ static int firstNonWhiteSpaceCharacterFromIndex(String line, int index) {
+ return firstFromIndex(line, index, not(Character::isWhitespace));
+ }
+
+ static int firstCharFromIndex(String line, int index, char ch) {
+ return firstFromIndex(line, index, c -> c == ch);
+ }
+
+ static int firstFromIndex(String line, int index, Predicate<Character> predicate) {
+ for (int i = index; i < line.length(); i++) {
+ if (predicate.test(line.charAt(i))) {
+ return i;
+ }
+ }
+ return line.length();
+ }
+
+ /**
+ * Captures a stack trace line of the following formats:
+ *
+ * <ul>
+ * <li>com.android.r8.R8Exception
+ * <li>com.android.r8.R8Exception: Problem when compiling program
+ * <li>Caused by: com.android.r8.R8InnerException: You have to write the program first
+ * <li>com.android.r8.R8InnerException: You have to write the program first
+ * </ul>
+ *
+ * <p>This will also contains false positives, such as
+ *
+ * <pre>
+ * W( 8207) VFY: unable to resolve static method 11: Lprivateinterfacemethods/I$-CC;....
+ * </pre>
+ *
+ * <p>The only invalid chars for type-identifiers for a java type-name is ';', '[' and '/', so we
+ * cannot really disregard the above line.
+ *
+ * <p>Caused by and Suppressed seems to not change based on locale, so we use these as markers.
+ */
+ private static class ExceptionLine {
+
+ private static final String CAUSED_BY = "Caused by: ";
+ private static final String SUPPRESSED = "Suppressed: ";
+
+ private static StackTraceElementStringProxy tryParse(String line) {
+ if (line.isEmpty()) {
+ return null;
+ }
+ int firstNonWhiteSpaceChar = firstNonWhiteSpaceCharacterFromIndex(line, 0);
+ String description = "";
+ if (line.startsWith(CAUSED_BY, firstNonWhiteSpaceChar)) {
+ description = CAUSED_BY;
+ } else if (line.startsWith(SUPPRESSED, firstNonWhiteSpaceChar)) {
+ description = SUPPRESSED;
+ }
+ int exceptionStartIndex = firstNonWhiteSpaceChar + description.length();
+ int messageStartIndex = firstCharFromIndex(line, exceptionStartIndex, ':');
+ String className = line.substring(exceptionStartIndex, messageStartIndex);
+ if (!DescriptorUtils.isValidJavaType(className)) {
+ return null;
+ }
+ return StackTraceElementStringProxy.builder(line)
+ .registerClassName(exceptionStartIndex, messageStartIndex)
+ .build();
+ }
+ }
+
+ /**
+ * Captures a stack trace line on the following form
+ *
+ * <ul>
+ * <li>at dalvik.system.NativeStart.main(NativeStart.java:99)
+ * <li>at dalvik.system.NativeStart.main(:99)
+ * <li>at dalvik.system.NativeStart.main(Foo.java:)
+ * <li>at dalvik.system.NativeStart.main(Native Method)
+ * <li>at classloader/named_module@version/foo.bar.baz(:20)
+ * <li>at classloader//foo.bar.baz(:20)
+ * </ul>
+ *
+ * <p>Empirical evidence suggests that the "at" string is never localized.
+ */
+ private static class AtLine {
+
+ private static StackTraceElementStringProxy tryParse(String line) {
+ // Check that the line is indented with some amount of white space.
+ if (line.length() == 0 || !Character.isWhitespace(line.charAt(0))) {
+ return null;
+ }
+ // Find the first non-white space character and check that we have the sequence 'a', 't', ' '.
+ int firstNonWhiteSpace = firstNonWhiteSpaceCharacterFromIndex(line, 0);
+ if (firstNonWhiteSpace + 2 >= line.length()
+ || line.charAt(firstNonWhiteSpace) != 'a'
+ || line.charAt(firstNonWhiteSpace + 1) != 't'
+ || line.charAt(firstNonWhiteSpace + 2) != ' ') {
+ return null;
+ }
+ int classClassLoaderOrModuleStartIndex =
+ firstNonWhiteSpaceCharacterFromIndex(line, firstNonWhiteSpace + 2);
+ if (classClassLoaderOrModuleStartIndex >= line.length()
+ || classClassLoaderOrModuleStartIndex != firstNonWhiteSpace + 3) {
+ return null;
+ }
+ int parensStart = firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '(');
+ if (parensStart >= line.length()) {
+ return null;
+ }
+ int parensEnd = firstCharFromIndex(line, parensStart, ')');
+ if (parensEnd >= line.length()) {
+ return null;
+ }
+ if (firstNonWhiteSpaceCharacterFromIndex(line, parensEnd) == line.length()) {
+ return null;
+ }
+ int methodSeparator = line.lastIndexOf('.', parensStart);
+ if (methodSeparator <= classClassLoaderOrModuleStartIndex) {
+ return null;
+ }
+ int classStartIndex = classClassLoaderOrModuleStartIndex;
+ int classLoaderOrModuleEndIndex =
+ firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '/');
+ if (classLoaderOrModuleEndIndex < methodSeparator) {
+ int moduleEndIndex = firstCharFromIndex(line, classLoaderOrModuleEndIndex + 1, '/');
+ if (moduleEndIndex < methodSeparator) {
+ // The stack trace contains both a class loader and module
+ classStartIndex = moduleEndIndex + 1;
+ } else {
+ classStartIndex = classLoaderOrModuleEndIndex + 1;
+ }
+ }
+ StackTraceElementStringProxyBuilder builder =
+ StackTraceElementStringProxy.builder(line)
+ .registerClassName(classStartIndex, methodSeparator)
+ .registerMethodName(methodSeparator + 1, parensStart);
+ // Check if we have a filename and position.
+ int separatorIndex = firstCharFromIndex(line, parensStart, ':');
+ if (separatorIndex < parensEnd) {
+ builder.registerSourceFile(parensStart + 1, separatorIndex);
+ builder.registerLineNumber(separatorIndex + 1, parensEnd);
+ } else {
+ builder.registerSourceFile(parensStart + 1, parensEnd);
+ }
+ return builder.build();
+ }
+ }
+
+ static class CircularReferenceLine {
+
+ private static final String CIRCULAR_REFERENCE = "[CIRCULAR REFERENCE:";
+
+ static StackTraceElementStringProxy tryParse(String line) {
+ // Check that the line is indented with some amount of white space.
+ if (line.length() == 0 || !Character.isWhitespace(line.charAt(0))) {
+ return null;
+ }
+ // Find the first non-white space character and check that we have the sequence
+ // '[CIRCULAR REFERENCE:Exception]'.
+ int firstNonWhiteSpace = firstNonWhiteSpaceCharacterFromIndex(line, 0);
+ if (!line.startsWith(CIRCULAR_REFERENCE, firstNonWhiteSpace)) {
+ return null;
+ }
+ int exceptionStartIndex = firstNonWhiteSpace + CIRCULAR_REFERENCE.length();
+ int lastBracketPosition = firstCharFromIndex(line, exceptionStartIndex, ']');
+ if (lastBracketPosition == line.length()) {
+ return null;
+ }
+ int onlyWhitespaceFromLastBracket =
+ firstNonWhiteSpaceCharacterFromIndex(line, lastBracketPosition + 1);
+ if (onlyWhitespaceFromLastBracket != line.length()) {
+ return null;
+ }
+ return StackTraceElementStringProxy.builder(line)
+ .registerClassName(exceptionStartIndex, lastBracketPosition)
+ .build();
+ }
+ }
+
+ private StackTraceElementStringProxy parseLine(int lineNumber, String line) {
+ if (line == null) {
+ diagnosticsHandler.error(RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
+ throw new Retrace.RetraceAbortException();
+ }
+ // Most lines are 'at lines' so attempt to parse it first.
+ StackTraceElementStringProxy parsedLine = AtLine.tryParse(line);
+ if (parsedLine != null) {
+ return parsedLine;
+ }
+ parsedLine = ExceptionLine.tryParse(line);
+ if (parsedLine != null) {
+ return parsedLine;
+ }
+ parsedLine = CircularReferenceLine.tryParse(line);
+ if (parsedLine != null) {
+ return parsedLine;
+ }
+ return StackTraceElementStringProxy.builder(line).build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/Result.java b/src/main/java/com/android/tools/r8/retrace/Result.java
index 12e92d3..692ca9b 100644
--- a/src/main/java/com/android/tools/r8/retrace/Result.java
+++ b/src/main/java/com/android/tools/r8/retrace/Result.java
@@ -14,4 +14,6 @@
public abstract Stream<R> stream();
public abstract RR forEach(Consumer<R> resultConsumer);
+
+ public abstract boolean isAmbiguous();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index adf9583..b8d94e6 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -49,7 +49,8 @@
public static final String USAGE_MESSAGE =
StringUtils.lines(
- "Usage: retrace <proguard-map> [stack-trace-file] [--regex <regexp>, --verbose, --info]",
+ "Usage: retrace <proguard-map> [stack-trace-file] "
+ + "[--regex <regexp>, --verbose, --info, --quiet]",
" where <proguard-map> is an r8 generated mapping file.");
private static Builder parseArguments(String[] args, DiagnosticsHandler diagnosticsHandler) {
@@ -58,6 +59,7 @@
boolean hasSetProguardMap = false;
boolean hasSetStackTrace = false;
boolean hasSetRegularExpression = false;
+ boolean hasSetQuiet = false;
while (context.head() != null) {
Boolean help = OptionsParsing.tryParseBoolean(context, "--help");
if (help != null) {
@@ -77,6 +79,11 @@
builder.setVerbose(true);
continue;
}
+ Boolean quiet = OptionsParsing.tryParseBoolean(context, "--quiet");
+ if (quiet != null) {
+ hasSetQuiet = true;
+ continue;
+ }
String regex = OptionsParsing.tryParseSingle(context, "--regex", "r");
if (regex != null && !regex.isEmpty()) {
builder.setRegularExpression(regex);
@@ -104,7 +111,7 @@
throw new RetraceAbortException();
}
if (!hasSetStackTrace) {
- builder.setStackTrace(getStackTraceFromStandardInput());
+ builder.setStackTrace(getStackTraceFromStandardInput(hasSetQuiet));
}
if (!hasSetRegularExpression) {
builder.setRegularExpression(DEFAULT_REGULAR_EXPRESSION);
@@ -165,10 +172,27 @@
command.isVerbose)
.retrace();
} else {
- result =
- new RetraceStackTrace(
- retracer, command.stackTrace, command.diagnosticsHandler, command.isVerbose)
- .retrace();
+ PlainStackTraceVisitor plainStackTraceVisitor =
+ new PlainStackTraceVisitor(command.stackTrace, command.diagnosticsHandler);
+ StackTraceElementProxyRetracer<StackTraceElementStringProxy> proxyRetracer =
+ new StackTraceElementProxyRetracer<>(retracer);
+ List<String> retracedStrings = new ArrayList<>();
+ plainStackTraceVisitor.forEach(
+ stackTraceElement -> {
+ List<String> retracedStringsForElement = new ArrayList<>();
+ proxyRetracer
+ .retrace(stackTraceElement)
+ .forEach(
+ retracedElement -> {
+ StackTraceElementStringProxy originalItem =
+ retracedElement.getOriginalItem();
+ retracedStringsForElement.add(
+ originalItem.toRetracedItem(
+ retracedElement, !retracedStringsForElement.isEmpty()));
+ });
+ retracedStrings.addAll(retracedStringsForElement);
+ });
+ result = new RetraceCommandLineResult(retracedStrings);
}
timing.end();
timing.begin("Report result");
@@ -238,8 +262,10 @@
withMainProgramHandler(() -> run(args));
}
- private static List<String> getStackTraceFromStandardInput() {
- System.out.println("Waiting for stack-trace input...");
+ private static List<String> getStackTraceFromStandardInput(boolean printWaitingMessage) {
+ if (!printWaitingMessage) {
+ System.out.println("Waiting for stack-trace input...");
+ }
Scanner sc = new Scanner(new InputStreamReader(System.in, Charsets.UTF_8));
List<String> readLines = new ArrayList<>();
while (sc.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceApi.java b/src/main/java/com/android/tools/r8/retrace/RetraceApi.java
index 7bb9232..7a1eb4e 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceApi.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceApi.java
@@ -14,11 +14,15 @@
@Keep
public interface RetraceApi {
+ // TODO(b/170711681): Rename these to not have overloads.
+
+ RetraceClassResult retrace(ClassReference classReference);
+
RetraceMethodResult retrace(MethodReference methodReference);
RetraceFieldResult retrace(FieldReference fieldReference);
- RetraceClassResult retrace(ClassReference classReference);
+ RetraceFrameResult retrace(MethodReference methodReference, int position);
RetraceTypeResult retrace(TypeReference typeReference);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassMemberElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassMemberElement.java
new file mode 100644
index 0000000..c21a100
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassMemberElement.java
@@ -0,0 +1,28 @@
+// 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 java.util.function.BiConsumer;
+
+public interface RetraceClassMemberElement<T extends RetracedClassMember> {
+
+ boolean isUnknown();
+
+ default boolean isFrameElement() {
+ return false;
+ }
+
+ default RetraceFrameResult.Element asFrameElement() {
+ return null;
+ }
+
+ RetraceClassResult.Element getClassElement();
+
+ T getMember();
+
+ void visitFrames(BiConsumer<T, Integer> consumer);
+
+ RetraceSourceFileResult retraceSourceFile(RetracedClassMember frame, String sourceFile);
+}
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 945aa0f..795311e 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -14,8 +14,11 @@
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.references.ClassReference;
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.utils.Box;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -41,8 +44,28 @@
}
public RetraceFieldResult lookupField(String fieldName) {
+ return lookupField(FieldDefinition.create(obfuscatedReference, fieldName));
+ }
+
+ public RetraceFieldResult lookupField(String fieldName, TypeReference fieldType) {
+ return lookupField(
+ FieldDefinition.create(Reference.field(obfuscatedReference, fieldName, fieldType)));
+ }
+
+ public RetraceMethodResult lookupMethod(String methodName) {
+ return lookupMethod(MethodDefinition.create(obfuscatedReference, methodName));
+ }
+
+ public RetraceMethodResult lookupMethod(
+ String methodName, List<TypeReference> formalTypes, TypeReference returnType) {
+ return lookupMethod(
+ MethodDefinition.create(
+ Reference.method(obfuscatedReference, methodName, formalTypes, returnType)));
+ }
+
+ private RetraceFieldResult lookupField(FieldDefinition fieldDefinition) {
return lookup(
- fieldName,
+ fieldDefinition,
(mapper, name) -> {
List<MemberNaming> memberNamings = mapper.mappedFieldNamingsByName.get(name);
if (memberNamings == null || memberNamings.isEmpty()) {
@@ -53,34 +76,37 @@
RetraceFieldResult::new);
}
- public RetraceMethodResult lookupMethod(String methodName) {
+ private RetraceMethodResult lookupMethod(MethodDefinition methodDefinition) {
return lookup(
- methodName,
+ methodDefinition,
(mapper, name) -> {
MappedRangesOfName mappedRanges = mapper.mappedRangesByRenamedName.get(name);
if (mappedRanges == null || mappedRanges.getMappedRanges().isEmpty()) {
return null;
}
- return mappedRanges;
+ return mappedRanges.getMappedRanges();
},
RetraceMethodResult::new);
}
- private <T, R> R lookup(
- String name,
+ private <T, R, D extends Definition> R lookup(
+ D definition,
BiFunction<ClassNamingForNameMapper, String, T> lookupFunction,
- ResultConstructor<T, R> constructor) {
- Box<R> elementBox = new Box<>();
+ ResultConstructor<T, R, D> constructor) {
+ List<Pair<Element, T>> mappings = new ArrayList<>();
forEach(
element -> {
- assert !elementBox.isSet();
- T mappedRangesForT = null;
- if (element.mapper != null) {
- mappedRangesForT = lookupFunction.apply(element.mapper, name);
+ if (mapper != null) {
+ assert element.mapper != null;
+ T mappedElements = lookupFunction.apply(element.mapper, definition.getName());
+ if (mappedElements != null) {
+ mappings.add(new Pair<>(element, mappedElements));
+ return;
+ }
}
- elementBox.set(constructor.create(element, mappedRangesForT, name, retracer));
+ mappings.add(new Pair<>(element, null));
});
- return elementBox.get();
+ return constructor.create(this, mappings, definition, retracer);
}
boolean hasRetraceResult() {
@@ -105,10 +131,15 @@
return this;
}
- private interface ResultConstructor<T, R> {
- R create(Element element, T mappings, String obfuscatedName, RetraceApi retraceApi);
+ private interface ResultConstructor<T, R, D> {
+ R create(
+ RetraceClassResult classResult,
+ List<Pair<Element, T>> mappings,
+ D definition,
+ RetraceApi retraceApi);
}
+ @Override
public boolean isAmbiguous() {
// Currently we have no way of producing ambiguous class results.
return false;
@@ -160,8 +191,12 @@
}
public RetraceFieldResult lookupField(String fieldName) {
+ return lookupField(FieldDefinition.create(classReference.getClassReference(), fieldName));
+ }
+
+ private RetraceFieldResult lookupField(FieldDefinition fieldDefinition) {
return lookup(
- fieldName,
+ fieldDefinition,
(mapper, name) -> {
List<MemberNaming> memberNamings = mapper.mappedFieldNamingsByName.get(name);
if (memberNamings == null || memberNamings.isEmpty()) {
@@ -173,27 +208,37 @@
}
public RetraceMethodResult lookupMethod(String methodName) {
+ return lookupMethod(MethodDefinition.create(classReference.getClassReference(), methodName));
+ }
+
+ private RetraceMethodResult lookupMethod(MethodDefinition methodDefinition) {
return lookup(
- methodName,
+ methodDefinition,
(mapper, name) -> {
MappedRangesOfName mappedRanges = mapper.mappedRangesByRenamedName.get(name);
if (mappedRanges == null || mappedRanges.getMappedRanges().isEmpty()) {
return null;
}
- return mappedRanges;
+ return mappedRanges.getMappedRanges();
},
RetraceMethodResult::new);
}
- private <T, R> R lookup(
- String name,
+ private <T, R, D extends Definition> R lookup(
+ D definition,
BiFunction<ClassNamingForNameMapper, String, T> lookupFunction,
- ResultConstructor<T, R> constructor) {
- return constructor.create(
- this,
- mapper != null ? lookupFunction.apply(mapper, name) : null,
- name,
- classResult.retracer);
+ ResultConstructor<T, R, D> constructor) {
+ List<Pair<Element, T>> mappings = ImmutableList.of();
+ if (mapper != null) {
+ T result = lookupFunction.apply(mapper, definition.getName());
+ if (result != null) {
+ mappings = ImmutableList.of(new Pair<>(this, result));
+ }
+ }
+ if (mappings.isEmpty()) {
+ mappings = ImmutableList.of(new Pair<>(this, null));
+ }
+ return constructor.create(classResult, mappings, definition, classResult.retracer);
}
}
}
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 265b3b2..46f247d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -9,79 +9,83 @@
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.Pair;
import java.util.List;
-import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Stream;
@Keep
public class RetraceFieldResult extends Result<RetraceFieldResult.Element, RetraceFieldResult> {
- private final RetraceClassResult.Element classElement;
- private final List<MemberNaming> memberNamings;
- private final String obfuscatedName;
+ private final RetraceClassResult classResult;
+ private final List<Pair<RetraceClassResult.Element, List<MemberNaming>>> memberNamings;
+ private final FieldDefinition fieldDefinition;
private final RetraceApi retracer;
RetraceFieldResult(
- RetraceClassResult.Element classElement,
- List<MemberNaming> memberNamings,
- String obfuscatedName,
+ RetraceClassResult classResult,
+ List<Pair<RetraceClassResult.Element, List<MemberNaming>>> memberNamings,
+ FieldDefinition fieldDefinition,
RetraceApi retracer) {
- this.classElement = classElement;
+ this.classResult = classResult;
this.memberNamings = memberNamings;
- this.obfuscatedName = obfuscatedName;
+ this.fieldDefinition = fieldDefinition;
this.retracer = retracer;
- assert classElement != null;
- assert memberNamings == null
- || (!memberNamings.isEmpty() && memberNamings.stream().allMatch(Objects::nonNull));
+ assert classResult != null;
+ assert !memberNamings.isEmpty();
}
- private boolean hasRetraceResult() {
- return memberNamings != null;
- }
-
+ @Override
public boolean isAmbiguous() {
- if (!hasRetraceResult()) {
+ if (memberNamings.size() > 1) {
+ return true;
+ }
+ List<MemberNaming> mappings = memberNamings.get(0).getSecond();
+ if (mappings == null) {
return false;
}
- assert memberNamings != null;
- return memberNamings.size() > 1;
- }
-
- public RetracedField getUnknownReference() {
- return RetracedField.createUnknown(classElement.getRetracedClass(), obfuscatedName);
+ return mappings.size() > 1;
}
@Override
public Stream<Element> stream() {
- if (!hasRetraceResult()) {
- return Stream.of(new Element(this, classElement, getUnknownReference()));
- }
- assert !memberNamings.isEmpty();
return memberNamings.stream()
- .map(
- memberNaming -> {
- assert memberNaming.isFieldNaming();
- FieldSignature fieldSignature =
- memberNaming.getOriginalSignature().asFieldSignature();
- RetracedClass holder =
- fieldSignature.isQualified()
- ? RetracedClass.create(
- Reference.classFromDescriptor(
- DescriptorUtils.javaTypeToDescriptor(
- fieldSignature.toHolderFromQualified())))
- : classElement.getRetracedClass();
- return new Element(
- this,
- classElement,
- RetracedField.create(
- holder,
- Reference.field(
- holder.getClassReference(),
- fieldSignature.isQualified()
- ? fieldSignature.toUnqualifiedName()
- : fieldSignature.name,
- Reference.typeFromTypeName(fieldSignature.type))));
+ .flatMap(
+ mappedNamePair -> {
+ RetraceClassResult.Element classElement = mappedNamePair.getFirst();
+ List<MemberNaming> memberNamings = mappedNamePair.getSecond();
+ if (memberNamings == null) {
+ return Stream.of(
+ new RetraceFieldResult.Element(
+ this,
+ classElement,
+ RetracedField.create(
+ fieldDefinition.substituteHolder(
+ classElement.getRetracedClass().getClassReference()))));
+ }
+ return memberNamings.stream()
+ .map(
+ memberNaming -> {
+ FieldSignature fieldSignature =
+ memberNaming.getOriginalSignature().asFieldSignature();
+ RetracedClass holder =
+ fieldSignature.isQualified()
+ ? RetracedClass.create(
+ Reference.classFromDescriptor(
+ DescriptorUtils.javaTypeToDescriptor(
+ fieldSignature.toHolderFromQualified())))
+ : classElement.getRetracedClass();
+ return new Element(
+ this,
+ classElement,
+ RetracedField.create(
+ Reference.field(
+ holder.getClassReference(),
+ fieldSignature.isQualified()
+ ? fieldSignature.toUnqualifiedName()
+ : fieldSignature.name,
+ Reference.typeFromTypeName(fieldSignature.type))));
+ });
});
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFrameResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFrameResult.java
new file mode 100644
index 0000000..95dc9d5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFrameResult.java
@@ -0,0 +1,164 @@
+// 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 static com.android.tools.r8.retrace.RetraceUtils.methodReferenceFromMappedRange;
+
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+public class RetraceFrameResult extends Result<RetraceFrameResult.Element, RetraceFrameResult> {
+
+ private final RetraceClassResult classResult;
+ private final MethodDefinition methodDefinition;
+ private final int obfuscatedPosition;
+ private final List<Pair<RetraceClassResult.Element, List<MappedRange>>> mappedRanges;
+ private final RetraceApi retracer;
+
+ public RetraceFrameResult(
+ RetraceClassResult classResult,
+ List<Pair<RetraceClassResult.Element, List<MappedRange>>> mappedRanges,
+ MethodDefinition methodDefinition,
+ int obfuscatedPosition,
+ RetraceApi retracer) {
+ this.classResult = classResult;
+ this.methodDefinition = methodDefinition;
+ this.obfuscatedPosition = obfuscatedPosition;
+ this.mappedRanges = mappedRanges;
+ this.retracer = retracer;
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ return mappedRanges.size() > 1;
+ }
+
+ @Override
+ public Stream<Element> stream() {
+ return mappedRanges.stream()
+ .map(
+ mappedRangePair -> {
+ RetraceClassResult.Element classElement = mappedRangePair.getFirst();
+ List<MappedRange> mappedRanges = mappedRangePair.getSecond();
+ if (mappedRanges == null || mappedRanges.isEmpty()) {
+ return new Element(
+ this,
+ classElement,
+ RetracedMethod.create(
+ methodDefinition.substituteHolder(
+ classElement.getRetracedClass().getClassReference())),
+ ImmutableList.of(),
+ obfuscatedPosition);
+ }
+ MappedRange mappedRange = mappedRanges.get(0);
+ MethodReference methodReference =
+ methodReferenceFromMappedRange(
+ mappedRange, classElement.getRetracedClass().getClassReference());
+ RetracedMethod retracedMethod =
+ RetracedMethod.create(
+ methodReference, mappedRange.getOriginalLineNumber(obfuscatedPosition));
+ return new Element(
+ this, classElement, retracedMethod, mappedRanges, obfuscatedPosition);
+ });
+ }
+
+ @Override
+ public RetraceFrameResult forEach(Consumer<Element> resultConsumer) {
+ stream().forEach(resultConsumer);
+ return this;
+ }
+
+ public static class Element implements RetraceClassMemberElement<RetracedMethod> {
+
+ private final RetracedMethod methodReference;
+ private final RetraceFrameResult retraceFrameResult;
+ private final RetraceClassResult.Element classElement;
+ private final List<MappedRange> mappedRanges;
+ private final int obfuscatedPosition;
+
+ public Element(
+ RetraceFrameResult retraceFrameResult,
+ RetraceClassResult.Element classElement,
+ RetracedMethod methodReference,
+ List<MappedRange> mappedRanges,
+ int obfuscatedPosition) {
+ this.methodReference = methodReference;
+ this.retraceFrameResult = retraceFrameResult;
+ this.classElement = classElement;
+ this.mappedRanges = mappedRanges;
+ this.obfuscatedPosition = obfuscatedPosition;
+ }
+
+ @Override
+ public boolean isUnknown() {
+ return methodReference.isUnknown();
+ }
+
+ @Override
+ public boolean isFrameElement() {
+ return true;
+ }
+
+ @Override
+ public Element asFrameElement() {
+ return this;
+ }
+
+ @Override
+ public RetracedMethod getMember() {
+ return methodReference;
+ }
+
+ public RetracedMethod getTopFrame() {
+ return methodReference;
+ }
+
+ @Override
+ public RetraceClassResult.Element getClassElement() {
+ return classElement;
+ }
+
+ @Override
+ public void visitFrames(BiConsumer<RetracedMethod, Integer> consumer) {
+ int counter = 0;
+ consumer.accept(methodReference, counter++);
+ for (RetracedMethod outerFrame : getOuterFrames()) {
+ consumer.accept(outerFrame, counter++);
+ }
+ }
+
+ @Override
+ public RetraceSourceFileResult retraceSourceFile(RetracedClassMember frame, String sourceFile) {
+ return RetraceUtils.getSourceFile(
+ classElement, frame.getHolderClass(), sourceFile, retraceFrameResult.retracer);
+ }
+
+ public List<RetracedMethod> getOuterFrames() {
+ if (mappedRanges == null) {
+ return Collections.emptyList();
+ }
+ List<RetracedMethod> outerFrames = new ArrayList<>();
+ for (int i = 1; i < mappedRanges.size(); i++) {
+ MappedRange mappedRange = mappedRanges.get(i);
+ MethodReference methodReference =
+ methodReferenceFromMappedRange(
+ mappedRange, classElement.getRetracedClass().getClassReference());
+ outerFrames.add(
+ RetracedMethod.create(
+ MethodDefinition.create(methodReference),
+ mappedRange.getOriginalLineNumber(obfuscatedPosition)));
+ }
+ return outerFrames;
+ }
+ }
+}
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 93c87e8..0906420 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
@@ -7,121 +7,116 @@
import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
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.Reference;
-import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;
@Keep
public class RetraceMethodResult extends Result<RetraceMethodResult.Element, RetraceMethodResult> {
- private final String obfuscatedName;
- private final RetraceClassResult.Element classElement;
- private final MappedRangesOfName mappedRanges;
+ private final MethodDefinition methodDefinition;
+ private final RetraceClassResult classResult;
+ private final List<Pair<RetraceClassResult.Element, List<MappedRange>>> mappedRanges;
private final RetraceApi retracer;
- private Boolean isAmbiguousCached = null;
RetraceMethodResult(
- RetraceClassResult.Element classElement,
- MappedRangesOfName mappedRanges,
- String obfuscatedName,
+ RetraceClassResult classResult,
+ List<Pair<RetraceClassResult.Element, List<MappedRange>>> mappedRanges,
+ MethodDefinition methodDefinition,
RetraceApi retracer) {
- this.classElement = classElement;
+ this.classResult = classResult;
this.mappedRanges = mappedRanges;
- this.obfuscatedName = obfuscatedName;
+ this.methodDefinition = methodDefinition;
this.retracer = retracer;
- assert classElement != null;
+ assert classResult != null;
+ assert !mappedRanges.isEmpty();
}
- public RetracedMethod getUnknownReference() {
- return RetracedMethod.createUnknown(classElement.getRetracedClass(), obfuscatedName);
- }
-
- private boolean hasRetraceResult() {
- return mappedRanges != null && mappedRanges.getMappedRanges().size() > 0;
- }
-
+ @Override
public boolean isAmbiguous() {
- if (isAmbiguousCached != null) {
- return isAmbiguousCached;
+ if (mappedRanges.size() > 1) {
+ return true;
}
- if (!hasRetraceResult()) {
+ List<MappedRange> methodRanges = mappedRanges.get(0).getSecond();
+ if (methodRanges == null || methodRanges.isEmpty()) {
return false;
}
- assert mappedRanges != null;
- Range minifiedRange = null;
- boolean seenNull = false;
- for (MappedRange mappedRange : mappedRanges.getMappedRanges()) {
- if (minifiedRange != null && !minifiedRange.equals(mappedRange.minifiedRange)) {
- isAmbiguousCached = true;
+ MappedRange lastRange = methodRanges.get(0);
+ for (MappedRange mappedRange : methodRanges) {
+ if (mappedRange != lastRange
+ && (mappedRange.minifiedRange == null
+ || !mappedRange.minifiedRange.equals(lastRange.minifiedRange))) {
return true;
- } else if (minifiedRange == null) {
- if (seenNull) {
- isAmbiguousCached = true;
- return true;
- }
- seenNull = true;
}
- minifiedRange = mappedRange.minifiedRange;
}
- isAmbiguousCached = false;
return false;
}
- public RetraceMethodResult narrowByLine(int linePosition) {
- if (!hasRetraceResult()) {
- return this;
- }
- List<MappedRange> narrowedRanges = this.mappedRanges.allRangesForLine(linePosition, false);
- if (narrowedRanges.isEmpty()) {
- narrowedRanges = new ArrayList<>();
- for (MappedRange mappedRange : this.mappedRanges.getMappedRanges()) {
- if (mappedRange.minifiedRange == null) {
- narrowedRanges.add(mappedRange);
+ public RetraceFrameResult narrowByPosition(int position) {
+ List<Pair<RetraceClassResult.Element, List<MappedRange>>> narrowedRanges = new ArrayList<>();
+ List<Pair<RetraceClassResult.Element, List<MappedRange>>> noMappingRanges = new ArrayList<>();
+ for (Pair<RetraceClassResult.Element, List<MappedRange>> mappedRange : mappedRanges) {
+ if (mappedRange.getSecond() == null) {
+ noMappingRanges.add(new Pair<>(mappedRange.getFirst(), null));
+ continue;
+ }
+ List<MappedRange> ranges =
+ new MappedRangesOfName(mappedRange.getSecond()).allRangesForLine(position, false);
+ boolean hasAddedRanges = false;
+ if (!ranges.isEmpty()) {
+ narrowedRanges.add(new Pair<>(mappedRange.getFirst(), ranges));
+ hasAddedRanges = true;
+ } else {
+ narrowedRanges = new ArrayList<>();
+ for (MappedRange mapped : mappedRange.getSecond()) {
+ if (mapped.minifiedRange == null) {
+ narrowedRanges.add(new Pair<>(mappedRange.getFirst(), ImmutableList.of(mapped)));
+ hasAddedRanges = true;
+ }
}
}
+ if (!hasAddedRanges) {
+ narrowedRanges.add(new Pair<>(mappedRange.getFirst(), null));
+ }
}
- return new RetraceMethodResult(
- classElement, new MappedRangesOfName(narrowedRanges), obfuscatedName, retracer);
+ return new RetraceFrameResult(
+ classResult,
+ narrowedRanges.isEmpty() ? noMappingRanges : narrowedRanges,
+ methodDefinition,
+ position,
+ retracer);
}
@Override
public Stream<Element> stream() {
- if (!hasRetraceResult()) {
- return Stream.of(new Element(this, classElement, getUnknownReference(), null));
- }
- return mappedRanges.getMappedRanges().stream()
- .map(
- mappedRange -> {
- MethodSignature signature = mappedRange.signature;
- RetracedClass holder =
- mappedRange.signature.isQualified()
- ? 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));
+ return mappedRanges.stream()
+ .flatMap(
+ mappedRangePair -> {
+ RetraceClassResult.Element classElement = mappedRangePair.getFirst();
+ List<MappedRange> mappedRanges = mappedRangePair.getSecond();
+ if (mappedRanges == null || mappedRanges.isEmpty()) {
+ return Stream.of(
+ new Element(
+ this,
+ classElement,
+ RetracedMethod.create(
+ methodDefinition.substituteHolder(
+ classElement.getRetracedClass().getClassReference()))));
}
- TypeReference returnType =
- Reference.returnTypeFromDescriptor(
- DescriptorUtils.javaTypeToDescriptor(signature.type));
- RetracedMethod retracedMethod =
- RetracedMethod.create(
- holder,
- Reference.method(
- holder.getClassReference(),
- signature.isQualified() ? signature.toUnqualifiedName() : signature.name,
- formalTypes,
- returnType));
- return new Element(this, classElement, retracedMethod, mappedRange);
+ return mappedRanges.stream()
+ .map(
+ mappedRange -> {
+ MethodReference methodReference =
+ RetraceUtils.methodReferenceFromMappedRange(
+ mappedRange, classElement.getRetracedClass().getClassReference());
+ return new Element(
+ this, classElement, RetracedMethod.create(methodReference));
+ });
});
}
@@ -131,41 +126,28 @@
return this;
}
- public static class Element {
+ public static class Element implements RetraceClassMemberElement<RetracedMethod> {
private final RetracedMethod methodReference;
private final RetraceMethodResult retraceMethodResult;
private final RetraceClassResult.Element classElement;
- private final MappedRange mappedRange;
private Element(
RetraceMethodResult retraceMethodResult,
RetraceClassResult.Element classElement,
- RetracedMethod methodReference,
- MappedRange mappedRange) {
+ RetracedMethod methodReference) {
this.classElement = classElement;
this.retraceMethodResult = retraceMethodResult;
this.methodReference = methodReference;
- this.mappedRange = mappedRange;
}
+ @Override
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() {
+ @Override
+ public RetracedMethod getMember() {
return methodReference;
}
@@ -173,25 +155,20 @@
return retraceMethodResult;
}
+ @Override
public RetraceClassResult.Element getClassElement() {
return classElement;
}
- public int getOriginalLineNumber(int linePosition) {
- return mappedRange != null ? mappedRange.getOriginalLineNumber(linePosition) : linePosition;
+ @Override
+ public void visitFrames(BiConsumer<RetracedMethod, Integer> consumer) {
+ consumer.accept(methodReference, 0);
}
- public int getFirstLineNumberOfOriginalRange() {
- if (hasNoLineNumberRange()) {
- return 0;
- }
- return mappedRange.getFirstLineNumberOfOriginalRange();
- }
-
- public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
+ @Override
+ public RetraceSourceFileResult retraceSourceFile(RetracedClassMember frame, String sourceFile) {
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 8606324..910db1a 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.retrace;
+import static com.android.tools.r8.retrace.RetraceUtils.methodDescriptionFromRetraceMethod;
+
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.references.ClassReference;
@@ -14,7 +16,6 @@
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;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.Lists;
import java.util.ArrayList;
@@ -133,7 +134,7 @@
case CLASS:
return line.getClassContext().getRetracedClass().getTypeName();
case METHOD:
- return line.getMethodContext().getMethod().getMethodName();
+ return line.getMethodContext().getMember().getMethodName();
case SOURCE:
return line.getSource();
case LINE:
@@ -241,9 +242,10 @@
static class RetraceStringContext {
private final Element classContext;
- private final RetracedClass qualifiedContext;
+ private final RetraceClassMemberElement<? extends RetracedMethod> methodContext;
+ private final RetracedClass qualifiedClassContext;
+ private final RetracedMethod qualifiedMethodContext;
private final String methodName;
- private final RetraceMethodResult.Element methodContext;
private final int minifiedLineNumber;
private final int originalLineNumber;
private final String source;
@@ -251,17 +253,19 @@
private RetraceStringContext(
Element classContext,
- RetracedClass qualifiedContext,
+ RetraceClassMemberElement<? extends RetracedMethod> methodContext,
+ RetracedClass qualifiedClassContext,
+ RetracedMethod qualifiedMethodContext,
String methodName,
- RetraceMethodResult.Element methodContext,
int minifiedLineNumber,
int originalLineNumber,
String source,
boolean isAmbiguous) {
this.classContext = classContext;
- this.qualifiedContext = qualifiedContext;
- this.methodName = methodName;
this.methodContext = methodContext;
+ this.qualifiedClassContext = qualifiedClassContext;
+ this.qualifiedMethodContext = qualifiedMethodContext;
+ this.methodName = methodName;
this.minifiedLineNumber = minifiedLineNumber;
this.originalLineNumber = originalLineNumber;
this.source = source;
@@ -269,16 +273,18 @@
}
private static RetraceStringContext empty() {
- return new RetraceStringContext(null, null, null, null, NO_MATCH, NO_MATCH, null, false);
+ return new RetraceStringContext(
+ null, null, null, null, null, NO_MATCH, NO_MATCH, null, false);
}
private RetraceStringContext withClassContext(
Element classContext, RetracedClass qualifiedContext) {
return new RetraceStringContext(
classContext,
- qualifiedContext,
- methodName,
methodContext,
+ qualifiedContext,
+ qualifiedMethodContext,
+ methodName,
minifiedLineNumber,
originalLineNumber,
source,
@@ -288,9 +294,10 @@
private RetraceStringContext withMethodName(String methodName) {
return new RetraceStringContext(
classContext,
- qualifiedContext,
- methodName,
methodContext,
+ qualifiedClassContext,
+ qualifiedMethodContext,
+ methodName,
minifiedLineNumber,
originalLineNumber,
source,
@@ -298,26 +305,39 @@
}
private RetraceStringContext withMethodContext(
- RetraceMethodResult.Element methodContext,
- RetracedClass qualifiedContext,
- boolean isAmbiguous) {
+ RetraceClassMemberElement<? extends RetracedMethod> methodContext, boolean isAmbiguous) {
return new RetraceStringContext(
classContext,
- qualifiedContext,
- methodName,
methodContext,
+ qualifiedClassContext,
+ qualifiedMethodContext,
+ methodName,
minifiedLineNumber,
originalLineNumber,
source,
isAmbiguous);
}
- private RetraceStringContext withQualifiedContext(RetracedClass qualifiedContext) {
+ private RetraceStringContext withQualifiedClassContext(RetracedClass qualifiedContext) {
return new RetraceStringContext(
classContext,
+ methodContext,
+ qualifiedContext,
+ qualifiedMethodContext,
+ methodName,
+ minifiedLineNumber,
+ originalLineNumber,
+ source,
+ isAmbiguous);
+ }
+
+ private RetraceStringContext withQualifiedMethodContext(RetracedMethod qualifiedContext) {
+ return new RetraceStringContext(
+ classContext,
+ methodContext,
+ qualifiedContext.getHolderClass(),
qualifiedContext,
methodName,
- methodContext,
minifiedLineNumber,
originalLineNumber,
source,
@@ -327,9 +347,10 @@
public RetraceStringContext withSource(String source) {
return new RetraceStringContext(
classContext,
- qualifiedContext,
- methodName,
methodContext,
+ qualifiedClassContext,
+ qualifiedMethodContext,
+ methodName,
minifiedLineNumber,
originalLineNumber,
source,
@@ -339,9 +360,10 @@
public RetraceStringContext withLineNumbers(int minifiedLineNumber, int originalLineNumber) {
return new RetraceStringContext(
classContext,
- qualifiedContext,
- methodName,
methodContext,
+ qualifiedClassContext,
+ qualifiedMethodContext,
+ methodName,
minifiedLineNumber,
originalLineNumber,
source,
@@ -385,7 +407,7 @@
return context.classContext;
}
- private RetraceMethodResult.Element getMethodContext() {
+ private RetraceClassMemberElement<? extends RetracedMethod> getMethodContext() {
return context.methodContext;
}
@@ -523,7 +545,7 @@
for (RetraceString retraceString : strings) {
retraceResult.forEach(
element -> {
- final RetraceString newRetraceString =
+ RetraceString newRetraceString =
retraceString.updateContext(
context -> context.withClassContext(element, element.getRetracedClass()));
retraceStrings.add(newRetraceString);
@@ -657,7 +679,7 @@
if (classNameGroupHandler != null) {
for (RetraceString string : strings) {
classNameGroupHandler.commitClassName(
- original, string, string.context.qualifiedContext, matcher);
+ original, string, string.context.qualifiedClassContext, matcher);
}
}
return strings;
@@ -669,20 +691,23 @@
retraceString,
methodName,
(element, newContext) -> {
- final RetraceString newRetraceString = retraceString.duplicate(newContext);
- if (classNameGroupHandler != null) {
- classNameGroupHandler.commitClassName(
- original, newRetraceString, element.getMethod().getHolderClass(), matcher);
- }
- retracedStrings.add(
- newRetraceString.appendRetracedString(
- original,
- printVerbose
- ? RetraceUtils.methodDescriptionFromMethodReference(
- element.getMethod(), false, true)
- : element.getMethod().getMethodName(),
- startOfGroup,
- matcher.end(captureGroup)));
+ element.visitFrames(
+ (method, ignoredPosition) -> {
+ RetraceString newRetraceString =
+ retraceString.duplicate(newContext.withQualifiedMethodContext(method));
+ if (classNameGroupHandler != null) {
+ classNameGroupHandler.commitClassName(
+ original, newRetraceString, method.getHolderClass(), matcher);
+ }
+ retracedStrings.add(
+ newRetraceString.appendRetracedString(
+ original,
+ printVerbose
+ ? methodDescriptionFromRetraceMethod(method, false, true)
+ : method.getMethodName(),
+ startOfGroup,
+ matcher.end(captureGroup)));
+ });
});
}
return retracedStrings;
@@ -703,24 +728,25 @@
private static void retraceMethodForString(
RetraceString retraceString,
String methodName,
- BiConsumer<RetraceMethodResult.Element, RetraceStringContext> process) {
+ BiConsumer<RetraceClassMemberElement<? extends RetracedMethod>, RetraceStringContext>
+ process) {
if (retraceString.context.classContext == null) {
return;
}
RetraceMethodResult retraceMethodResult =
retraceString.getClassContext().lookupMethod(methodName);
+ Result<? extends RetraceClassMemberElement<RetracedMethod>, ?> retraceResult;
if (retraceString.context.minifiedLineNumber > NO_MATCH) {
- retraceMethodResult =
- retraceMethodResult.narrowByLine(retraceString.context.minifiedLineNumber);
+ retraceResult =
+ retraceMethodResult.narrowByPosition(retraceString.context.minifiedLineNumber);
+ } else {
+ retraceResult = retraceMethodResult;
}
- retraceMethodResult.forEach(
+ retraceResult.forEach(
element ->
process.accept(
element,
- retraceString.context.withMethodContext(
- element,
- element.getMethod().getHolderClass(),
- element.getRetraceMethodResult().isAmbiguous())));
+ retraceString.context.withMethodContext(element, retraceResult.isAmbiguous())));
}
}
@@ -756,12 +782,12 @@
if (classNameGroupHandler != null) {
for (RetraceString string : strings) {
classNameGroupHandler.commitClassName(
- original, string, string.context.qualifiedContext, matcher);
+ original, string, string.context.qualifiedClassContext, matcher);
}
}
return strings;
}
- String methodName = matcher.group(captureGroup);
+ String fieldName = matcher.group(captureGroup);
List<RetraceString> retracedStrings = new ArrayList<>();
for (RetraceString retraceString : strings) {
if (retraceString.getClassContext() == null) {
@@ -769,7 +795,7 @@
return strings;
}
final RetraceFieldResult retraceFieldResult =
- retraceString.getClassContext().lookupField(methodName);
+ retraceString.getClassContext().lookupField(fieldName);
assert !retraceFieldResult.isAmbiguous();
retraceFieldResult.forEach(
element -> {
@@ -781,7 +807,8 @@
retraceString
.updateContext(
context ->
- context.withQualifiedContext(element.getField().getHolderClass()))
+ context.withQualifiedClassContext(
+ element.getField().getHolderClass()))
.appendRetracedString(
original,
getFieldString(element.getField()),
@@ -829,10 +856,12 @@
}
RetraceSourceFileResult sourceFileResult =
retraceString.getMethodContext() != null
- ? retraceString.getMethodContext().retraceSourceFile(fileName)
+ ? retraceString
+ .getMethodContext()
+ .retraceSourceFile(retraceString.context.qualifiedMethodContext, fileName)
: RetraceUtils.getSourceFile(
retraceString.getClassContext(),
- retraceString.context.qualifiedContext,
+ retraceString.context.qualifiedClassContext,
fileName,
retracer);
retracedStrings.add(
@@ -873,7 +902,8 @@
int lineNumber = Integer.parseInt(lineNumberAsString);
List<RetraceString> retracedStrings = new ArrayList<>();
for (RetraceString retraceString : strings) {
- RetraceMethodResult.Element methodContext = retraceString.context.methodContext;
+ RetraceClassMemberElement<? extends RetracedMethod> methodContext =
+ retraceString.context.methodContext;
if (methodContext == null) {
if (retraceString.context.classContext == null
|| retraceString.context.methodName == null) {
@@ -889,34 +919,33 @@
(element, newContext) -> {
// The same method can be represented multiple times if it has multiple
// mappings.
- if (element.hasNoLineNumberRange()
- || !element.containsMinifiedLineNumber(lineNumber)) {
- diagnosticsHandler.info(
- new StringDiagnostic(
- "Pruning "
- + retraceString.builder.retracedString.toString()
- + " from result because method is not in range on line number "
- + lineNumber));
- }
- final int originalLineNumber = element.getOriginalLineNumber(lineNumber);
- retracedStrings.add(
- retraceString
- .updateContext(
- context -> context.withLineNumbers(lineNumber, originalLineNumber))
- .appendRetracedString(
- original,
- originalLineNumber + "",
- startOfGroup,
- matcher.end(captureGroup)));
+ element.visitFrames(
+ (method, ignoredPosition) -> {
+ int originalPosition = method.getOriginalPositionOrDefault(lineNumber);
+ retracedStrings.add(
+ retraceString
+ .duplicate(
+ retraceString
+ .context
+ .withQualifiedMethodContext(method)
+ .withLineNumbers(lineNumber, originalPosition))
+ .appendRetracedString(
+ original,
+ originalPosition + "",
+ startOfGroup,
+ matcher.end(captureGroup)));
+ });
});
continue;
}
// If the method context is unknown, do nothing.
- if (methodContext.isUnknown() || methodContext.hasNoLineNumberRange()) {
+ if (methodContext.isUnknown()) {
retracedStrings.add(retraceString);
continue;
}
- int originalLineNumber = methodContext.getOriginalLineNumber(lineNumber);
+ int originalLineNumber =
+ retraceString.context.qualifiedMethodContext.getOriginalPositionOrDefault(
+ lineNumber);
retracedStrings.add(
retraceString
.updateContext(
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
deleted file mode 100644
index e2e3616..0000000
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ /dev/null
@@ -1,625 +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.retrace;
-
-import static com.android.tools.r8.retrace.RetraceUtils.methodDescriptionFromMethodReference;
-import static com.google.common.base.Predicates.not;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Predicate;
-
-public final class RetraceStackTrace {
-
- public static class StackTraceNode {
-
- private final List<StackTraceLine> lines;
-
- StackTraceNode(List<StackTraceLine> lines) {
- this.lines = lines;
- assert !lines.isEmpty();
- assert lines.size() == 1 || lines.stream().allMatch(StackTraceLine::isAtLine);
- }
-
- public void append(List<String> strings) {
- assert !lines.isEmpty();
- if (lines.size() == 1) {
- strings.add(lines.get(0).toString());
- return;
- }
- // We must have an inlining or ambiguous match here, thus all lines are at-lines.
- assert lines.stream().allMatch(StackTraceLine::isAtLine);
- assert lines.stream()
- .allMatch(line -> line.asAtLine().isAmbiguous == lines.get(0).asAtLine().isAmbiguous);
- if (lines.get(0).asAtLine().isAmbiguous) {
- lines.sort(new AtStackTraceLineComparator());
- }
- boolean shouldPrintOr = false;
- for (StackTraceLine line : lines) {
- assert line.isAtLine();
- AtLine atLine = line.asAtLine();
- if (atLine.isAmbiguous && shouldPrintOr) {
- String atLineString = atLine.toString();
- int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(atLineString);
- strings.add(
- atLineString.substring(0, firstNonWhitespaceCharacter)
- + "<OR> "
- + atLineString.substring(firstNonWhitespaceCharacter));
- } else {
- strings.add(atLine.toString());
- }
- shouldPrintOr = true;
- }
- }
- }
-
- static class AtStackTraceLineComparator extends AmbiguousComparator<StackTraceLine> {
-
- AtStackTraceLineComparator() {
- super(
- (line, t) -> {
- assert line.isAtLine();
- AtLine atLine = line.asAtLine();
- switch (t) {
- case CLASS:
- return atLine.clazz;
- case METHOD:
- return atLine.method;
- case SOURCE:
- return atLine.fileName;
- case LINE:
- return atLine.linePosition + "";
- default:
- assert false;
- }
- throw new RuntimeException("Comparator key is unknown");
- });
- }
- }
-
- private final RetraceApi retracer;
- private final List<String> stackTrace;
- private final DiagnosticsHandler diagnosticsHandler;
- private final boolean verbose;
-
- RetraceStackTrace(
- RetraceApi retracer,
- List<String> stackTrace,
- DiagnosticsHandler diagnosticsHandler,
- boolean verbose) {
- this.retracer = retracer;
- this.stackTrace = stackTrace;
- this.diagnosticsHandler = diagnosticsHandler;
- this.verbose = verbose;
- }
-
- public RetraceCommandLineResult retrace() {
- ArrayList<StackTraceNode> result = new ArrayList<>();
- retraceLine(stackTrace, 0, result);
- List<String> retracedStrings = new ArrayList<>();
- for (StackTraceNode node : result) {
- node.append(retracedStrings);
- }
- return new RetraceCommandLineResult(retracedStrings);
- }
-
- private void retraceLine(List<String> stackTrace, int index, List<StackTraceNode> result) {
- if (stackTrace.size() <= index) {
- return;
- }
- StackTraceLine stackTraceLine = parseLine(index + 1, stackTrace.get(index));
- List<StackTraceLine> retraced = stackTraceLine.retrace(retracer, verbose);
- StackTraceNode node = new StackTraceNode(retraced);
- result.add(node);
- retraceLine(stackTrace, index + 1, result);
- }
-
- abstract static class StackTraceLine {
-
- abstract List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose);
-
- static int firstNonWhiteSpaceCharacterFromIndex(String line, int index) {
- return firstFromIndex(line, index, not(Character::isWhitespace));
- }
-
- static int firstCharFromIndex(String line, int index, char ch) {
- return firstFromIndex(line, index, c -> c == ch);
- }
-
- static int firstFromIndex(String line, int index, Predicate<Character> predicate) {
- for (int i = index; i < line.length(); i++) {
- if (predicate.test(line.charAt(i))) {
- return i;
- }
- }
- return line.length();
- }
-
- boolean isAtLine() {
- return false;
- }
-
- AtLine asAtLine() {
- return null;
- }
-
- boolean isExceptionLine() {
- return false;
- }
-
- ExceptionLine asExceptionLine() {
- return null;
- }
- }
-
- /**
- * Captures a stack trace line of the following formats:
- *
- * <ul>
- * <li>com.android.r8.R8Exception
- * <li>com.android.r8.R8Exception: Problem when compiling program
- * <li>Caused by: com.android.r8.R8InnerException: You have to write the program first
- * <li>com.android.r8.R8InnerException: You have to write the program first
- * </ul>
- *
- * <p>This will also contains false positives, such as
- *
- * <pre>
- * W( 8207) VFY: unable to resolve static method 11: Lprivateinterfacemethods/I$-CC;....
- * </pre>
- *
- * <p>The only invalid chars for type-identifiers for a java type-name is ';', '[' and '/', so we
- * cannot really disregard the above line.
- *
- * <p>Caused by and Suppressed seems to not change based on locale, so we use these as markers.
- */
- static class ExceptionLine extends StackTraceLine {
-
- private static final String CAUSED_BY = "Caused by: ";
- private static final String SUPPRESSED = "Suppressed: ";
-
- private final String initialWhiteSpace;
- private final String description;
- private final String exceptionClass;
- private final String message;
-
- ExceptionLine(
- String initialWhiteSpace, String description, String exceptionClass, String message) {
- this.initialWhiteSpace = initialWhiteSpace;
- this.description = description;
- this.exceptionClass = exceptionClass;
- this.message = message;
- }
-
- static ExceptionLine tryParse(String line) {
- if (line.isEmpty()) {
- return null;
- }
- int firstNonWhiteSpaceChar = firstNonWhiteSpaceCharacterFromIndex(line, 0);
- String description = "";
- if (line.startsWith(CAUSED_BY, firstNonWhiteSpaceChar)) {
- description = CAUSED_BY;
- } else if (line.startsWith(SUPPRESSED, firstNonWhiteSpaceChar)) {
- description = SUPPRESSED;
- }
- int exceptionStartIndex = firstNonWhiteSpaceChar + description.length();
- int messageStartIndex = firstCharFromIndex(line, exceptionStartIndex, ':');
- String className = line.substring(exceptionStartIndex, messageStartIndex);
- if (!DescriptorUtils.isValidJavaType(className)) {
- return null;
- }
- return new ExceptionLine(
- line.substring(0, firstNonWhiteSpaceChar),
- description,
- className,
- line.substring(messageStartIndex));
- }
-
- @Override
- List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
- List<StackTraceLine> exceptionLines = new ArrayList<>();
- retracer
- .retrace(Reference.classFromTypeName(exceptionClass))
- .forEach(
- element ->
- exceptionLines.add(
- new ExceptionLine(
- initialWhiteSpace,
- description,
- element.getRetracedClass().getTypeName(),
- message)));
- return exceptionLines;
- }
-
- @Override
- public String toString() {
- return initialWhiteSpace + description + exceptionClass + message;
- }
-
- @Override
- boolean isExceptionLine() {
- return true;
- }
-
- @Override
- ExceptionLine asExceptionLine() {
- return this;
- }
- }
-
- /**
- * Captures a stack trace line on the following form
- *
- * <ul>
- * <li>at dalvik.system.NativeStart.main(NativeStart.java:99)
- * <li>at dalvik.system.NativeStart.main(:99)
- * <li>at dalvik.system.NativeStart.main(Foo.java:)
- * <li>at dalvik.system.NativeStart.main(Native Method)
- * <li>at classloader/named_module@version/foo.bar.baz(:20)
- * <li>at classloader//foo.bar.baz(:20)
- * </ul>
- *
- * <p>Empirical evidence suggests that the "at" string is never localized.
- */
- static class AtLine extends StackTraceLine {
-
- private static final int NO_POSITION = -2;
- private static final int INVALID_POSITION = -1;
-
- private final String startingWhitespace;
- private final String at;
- private final String classLoaderName;
- private final String moduleName;
- private final String clazz;
- private final String method;
- private final String methodAsString;
- private final String fileName;
- private final int linePosition;
- private final boolean isAmbiguous;
-
- private AtLine(
- String startingWhitespace,
- String at,
- String classLoaderName,
- String moduleName,
- String clazz,
- String method,
- String methodAsString,
- String fileName,
- int linePosition,
- boolean isAmbiguous) {
- this.startingWhitespace = startingWhitespace;
- this.at = at;
- this.classLoaderName = classLoaderName;
- this.moduleName = moduleName;
- this.clazz = clazz;
- this.method = method;
- this.methodAsString = methodAsString;
- this.fileName = fileName;
- this.linePosition = linePosition;
- this.isAmbiguous = isAmbiguous;
- }
-
- static AtLine tryParse(String line) {
- // Check that the line is indented with some amount of white space.
- if (line.length() == 0 || !Character.isWhitespace(line.charAt(0))) {
- return null;
- }
- // Find the first non-white space character and check that we have the sequence 'a', 't', ' '.
- int firstNonWhiteSpace = firstNonWhiteSpaceCharacterFromIndex(line, 0);
- if (firstNonWhiteSpace + 2 >= line.length()
- || line.charAt(firstNonWhiteSpace) != 'a'
- || line.charAt(firstNonWhiteSpace + 1) != 't'
- || line.charAt(firstNonWhiteSpace + 2) != ' ') {
- return null;
- }
- int classClassLoaderOrModuleStartIndex =
- firstNonWhiteSpaceCharacterFromIndex(line, firstNonWhiteSpace + 2);
- if (classClassLoaderOrModuleStartIndex >= line.length()
- || classClassLoaderOrModuleStartIndex != firstNonWhiteSpace + 3) {
- return null;
- }
- int parensStart = firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '(');
- if (parensStart >= line.length()) {
- return null;
- }
- int parensEnd = firstCharFromIndex(line, parensStart, ')');
- if (parensEnd >= line.length()) {
- return null;
- }
- if (firstNonWhiteSpaceCharacterFromIndex(line, parensEnd) == line.length()) {
- return null;
- }
- int methodSeparator = line.lastIndexOf('.', parensStart);
- if (methodSeparator <= classClassLoaderOrModuleStartIndex) {
- return null;
- }
- // Check if we have a filename and position.
- String fileName = "";
- int position = NO_POSITION;
- int separatorIndex = firstCharFromIndex(line, parensStart, ':');
- if (separatorIndex < parensEnd) {
- fileName = line.substring(parensStart + 1, separatorIndex);
- try {
- String positionAsString = line.substring(separatorIndex + 1, parensEnd);
- position = Integer.parseInt(positionAsString);
- } catch (NumberFormatException e) {
- position = INVALID_POSITION;
- }
- } else {
- fileName = line.substring(parensStart + 1, parensEnd);
- }
- String classLoaderName = null;
- String moduleName = null;
- int classStartIndex = classClassLoaderOrModuleStartIndex;
- int classLoaderOrModuleEndIndex =
- firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '/');
- if (classLoaderOrModuleEndIndex < methodSeparator) {
- int moduleEndIndex = firstCharFromIndex(line, classLoaderOrModuleEndIndex + 1, '/');
- if (moduleEndIndex < methodSeparator) {
- // The stack trace contains both a class loader and module
- classLoaderName =
- line.substring(classClassLoaderOrModuleStartIndex, classLoaderOrModuleEndIndex);
- moduleName = line.substring(classLoaderOrModuleEndIndex + 1, moduleEndIndex);
- classStartIndex = moduleEndIndex + 1;
- } else {
- moduleName =
- line.substring(classClassLoaderOrModuleStartIndex, classLoaderOrModuleEndIndex);
- classStartIndex = classLoaderOrModuleEndIndex + 1;
- }
- }
- String className = line.substring(classStartIndex, methodSeparator);
- String methodName = line.substring(methodSeparator + 1, parensStart);
- return new AtLine(
- line.substring(0, firstNonWhiteSpace),
- line.substring(firstNonWhiteSpace, classClassLoaderOrModuleStartIndex),
- classLoaderName,
- moduleName,
- className,
- methodName,
- className + "." + methodName,
- fileName,
- position,
- false);
- }
-
- private boolean hasLinePosition() {
- return linePosition > -1;
- }
-
- @Override
- List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
- List<StackTraceLine> lines = new ArrayList<>();
- String retraceClassLoaderName = classLoaderName;
- if (retraceClassLoaderName != null) {
- ClassReference classLoaderReference = Reference.classFromTypeName(retraceClassLoaderName);
- retracer
- .retrace(classLoaderReference)
- .forEach(
- classElement -> {
- retraceClassAndMethods(
- retracer, verbose, lines, classElement.getRetracedClass().getTypeName());
- });
- } else {
- retraceClassAndMethods(retracer, verbose, lines, retraceClassLoaderName);
- }
- return lines;
- }
-
- private void retraceClassAndMethods(
- RetraceApi retracer, boolean verbose, List<StackTraceLine> lines, String classLoaderName) {
- ClassReference classReference = Reference.classFromTypeName(clazz);
- RetraceClassResult classResult = retracer.retrace(classReference);
- RetraceMethodResult retraceResult = classResult.lookupMethod(method);
- if (linePosition != NO_POSITION && linePosition != INVALID_POSITION) {
- retraceResult = retraceResult.narrowByLine(linePosition);
- }
- retraceResult.forEach(
- methodElement -> {
- RetracedMethod methodReference = methodElement.getMethod();
- lines.add(
- new AtLine(
- startingWhitespace,
- at,
- classLoaderName,
- moduleName,
- methodReference.getHolderClass().getTypeName(),
- methodReference.getMethodName(),
- methodDescriptionFromMethodReference(methodReference, true, verbose),
- methodElement.retraceSourceFile(fileName).getFilename(),
- hasLinePosition()
- ? methodElement.getOriginalLineNumber(linePosition)
- : linePosition,
- methodElement.getRetraceMethodResult().isAmbiguous()));
- });
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(startingWhitespace);
- sb.append(at);
- if (classLoaderName != null) {
- sb.append(classLoaderName);
- sb.append("/");
- }
- if (moduleName != null) {
- sb.append(moduleName);
- sb.append("/");
- }
- sb.append(methodAsString);
- sb.append("(");
- sb.append(fileName);
- if (linePosition != NO_POSITION) {
- sb.append(":");
- }
- if (linePosition > INVALID_POSITION) {
- sb.append(linePosition);
- }
- sb.append(")");
- return sb.toString();
- }
-
- @Override
- boolean isAtLine() {
- return true;
- }
-
- @Override
- AtLine asAtLine() {
- return this;
- }
- }
-
- static class MoreLine extends StackTraceLine {
- private final String line;
-
- MoreLine(String line) {
- this.line = line;
- }
-
- static StackTraceLine tryParse(String line) {
- int dotsSeen = 0;
- boolean isWhiteSpaceAllowed = true;
- for (int i = 0; i < line.length(); i++) {
- char ch = line.charAt(i);
- if (Character.isWhitespace(ch) && isWhiteSpaceAllowed) {
- continue;
- }
- isWhiteSpaceAllowed = false;
- if (ch != '.') {
- return null;
- }
- if (++dotsSeen == 3) {
- return new MoreLine(line);
- }
- }
- return null;
- }
-
- @Override
- List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
- return ImmutableList.of(new MoreLine(line));
- }
-
- @Override
- public String toString() {
- return line;
- }
- }
-
- static class CircularReferenceLine extends StackTraceLine {
-
- private final String startWhitespace;
- private final String exceptionClass;
- private final String endBracketAndWhitespace;
-
- private static final String CIRCULAR_REFERENCE = "[CIRCULAR REFERENCE:";
-
- public CircularReferenceLine(
- String startWhitespace, String exceptionClass, String endBracketAndWhitespace) {
- this.startWhitespace = startWhitespace;
- this.exceptionClass = exceptionClass;
- this.endBracketAndWhitespace = endBracketAndWhitespace;
- }
-
- static StackTraceLine tryParse(String line) {
- // Check that the line is indented with some amount of white space.
- if (line.length() == 0 || !Character.isWhitespace(line.charAt(0))) {
- return null;
- }
- // Find the first non-white space character and check that we have the sequence
- // '[CIRCULAR REFERENCE:Exception]'.
- int firstNonWhiteSpace = firstNonWhiteSpaceCharacterFromIndex(line, 0);
- if (!line.startsWith(CIRCULAR_REFERENCE, firstNonWhiteSpace)) {
- return null;
- }
- int exceptionStartIndex = firstNonWhiteSpace + CIRCULAR_REFERENCE.length();
- int lastBracketPosition = firstCharFromIndex(line, exceptionStartIndex, ']');
- if (lastBracketPosition == line.length()) {
- return null;
- }
- int onlyWhitespaceFromLastBracket =
- firstNonWhiteSpaceCharacterFromIndex(line, lastBracketPosition + 1);
- if (onlyWhitespaceFromLastBracket != line.length()) {
- return null;
- }
- return new CircularReferenceLine(
- line.substring(0, firstNonWhiteSpace),
- line.substring(exceptionStartIndex, lastBracketPosition),
- line.substring(lastBracketPosition));
- }
-
- @Override
- List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
- List<StackTraceLine> exceptionLines = new ArrayList<>();
- retracer
- .retrace(Reference.classFromTypeName(exceptionClass))
- .forEach(
- element ->
- exceptionLines.add(
- new CircularReferenceLine(
- startWhitespace,
- element.getRetracedClass().getTypeName(),
- endBracketAndWhitespace)));
- return exceptionLines;
- }
-
- @Override
- public String toString() {
- return startWhitespace + CIRCULAR_REFERENCE + exceptionClass + endBracketAndWhitespace;
- }
- }
-
- static class UnknownLine extends StackTraceLine {
- private final String line;
-
- UnknownLine(String line) {
- this.line = line;
- }
-
- @Override
- List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
- return ImmutableList.of(new UnknownLine(line));
- }
-
- @Override
- public String toString() {
- return line;
- }
- }
-
- private StackTraceLine parseLine(int lineNumber, String line) {
- if (line == null) {
- diagnosticsHandler.error(RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
- throw new Retrace.RetraceAbortException();
- }
- // Most lines are 'at lines' so attempt to parse it first.
- StackTraceLine parsedLine = AtLine.tryParse(line);
- if (parsedLine != null) {
- return parsedLine;
- }
- parsedLine = ExceptionLine.tryParse(line);
- if (parsedLine != null) {
- return parsedLine;
- }
- parsedLine = CircularReferenceLine.tryParse(line);
- if (parsedLine != null) {
- return parsedLine;
- }
- parsedLine = MoreLine.tryParse(line);
- if (parsedLine == null) {
- diagnosticsHandler.warning(
- RetraceInvalidStackTraceLineDiagnostics.createParse(lineNumber, line));
- }
- parsedLine = new UnknownLine(line);
- return parsedLine;
- }
-}
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 4f7ec0a..d546d11 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -14,11 +14,15 @@
private final TypeReference obfuscatedType;
private final RetraceApi retracer;
- RetraceTypeResult(TypeReference obfuscatedType, RetraceApi retracer) {
+ private RetraceTypeResult(TypeReference obfuscatedType, RetraceApi retracer) {
this.obfuscatedType = obfuscatedType;
this.retracer = retracer;
}
+ static RetraceTypeResult create(TypeReference obfuscatedType, RetraceApi retracer) {
+ return new RetraceTypeResult(obfuscatedType, retracer);
+ }
+
@Override
public Stream<Element> stream() {
// Handle void and primitive types as single element results.
@@ -36,6 +40,7 @@
.map(classElement -> new Element(classElement.getRetracedClass().getRetracedType()));
}
+ @Override
public boolean isAmbiguous() {
return false;
}
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 335059e..e402a80 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
@@ -4,11 +4,19 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+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.references.TypeReference;
import com.android.tools.r8.retrace.RetracedMethod.KnownRetracedMethod;
import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
public class RetraceUtils {
@@ -16,7 +24,7 @@
private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source", "PG");
- public static String methodDescriptionFromMethodReference(
+ public static String methodDescriptionFromRetraceMethod(
RetracedMethod methodReference, boolean appendHolder, boolean verbose) {
StringBuilder sb = new StringBuilder();
if (appendHolder) {
@@ -115,4 +123,25 @@
String newFileName = getClassSimpleName(retracedClassName);
return newFileName + "." + extension;
}
+
+ static MethodReference methodReferenceFromMappedRange(
+ MappedRange mappedRange, ClassReference classReference) {
+ MethodSignature signature = mappedRange.signature;
+ ClassReference holder =
+ signature.isQualified()
+ ? Reference.classFromDescriptor(
+ DescriptorUtils.javaTypeToDescriptor(signature.toHolderFromQualified()))
+ : classReference;
+ List<TypeReference> formalTypes = new ArrayList<>(signature.parameters.length);
+ for (String parameter : signature.parameters) {
+ formalTypes.add(Reference.typeFromTypeName(parameter));
+ }
+ TypeReference returnType =
+ Reference.returnTypeFromDescriptor(DescriptorUtils.javaTypeToDescriptor(signature.type));
+ return Reference.method(
+ holder,
+ signature.isQualified() ? signature.toUnqualifiedName() : signature.name,
+ formalTypes,
+ returnType);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java b/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java
new file mode 100644
index 0000000..e05be47
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.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.retrace;
+
+public interface RetracedClassMember {
+
+ RetracedClass getHolderClass();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedField.java b/src/main/java/com/android/tools/r8/retrace/RetracedField.java
index b809cab..132d0c7 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracedField.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedField.java
@@ -10,7 +10,7 @@
import java.util.Objects;
@Keep
-public abstract class RetracedField {
+public abstract class RetracedField implements RetracedClassMember {
private RetracedField() {}
@@ -26,17 +26,13 @@
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;
+ private KnownRetracedField(FieldReference fieldReference) {
this.fieldReference = fieldReference;
}
@@ -52,7 +48,7 @@
@Override
public RetracedClass getHolderClass() {
- return classReference;
+ return RetracedClass.create(fieldReference.getHolderClass());
}
@Override
@@ -77,43 +73,56 @@
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);
+ return Objects.hash(fieldReference);
}
}
public static final class UnknownRetracedField extends RetracedField {
- private final RetracedClass classReference;
- private final String name;
+ private final FieldDefinition fieldDefinition;
- private UnknownRetracedField(RetracedClass classReference, String name) {
- this.classReference = classReference;
- this.name = name;
+ private UnknownRetracedField(FieldDefinition fieldDefinition) {
+ this.fieldDefinition = fieldDefinition;
}
@Override
public RetracedClass getHolderClass() {
- return classReference;
+ return RetracedClass.create(fieldDefinition.getHolderClass());
}
@Override
public String getFieldName() {
- return name;
+ return fieldDefinition.getName();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ UnknownRetracedField that = (UnknownRetracedField) o;
+ return fieldDefinition.equals(that.fieldDefinition);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fieldDefinition);
}
}
- static RetracedField create(RetracedClass classReference, FieldReference fieldReference) {
- return new KnownRetracedField(classReference, fieldReference);
+ static RetracedField create(FieldReference fieldReference) {
+ return new KnownRetracedField(fieldReference);
}
- static RetracedField createUnknown(RetracedClass classReference, String name) {
- return new UnknownRetracedField(classReference, name);
+ static RetracedField create(FieldDefinition fieldDefinition) {
+ return new UnknownRetracedField(fieldDefinition);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java b/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java
index ee3b792..937ded1 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java
@@ -9,9 +9,12 @@
import com.android.tools.r8.references.TypeReference;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
@Keep
-public abstract class RetracedMethod {
+public abstract class RetracedMethod implements RetracedClassMember {
+
+ private static final int NO_POSITION = -1;
private RetracedMethod() {}
@@ -27,19 +30,21 @@
return null;
}
- public abstract RetracedClass getHolderClass();
-
public abstract String getMethodName();
+ public abstract boolean hasPosition();
+
+ public abstract int getOriginalPositionOrDefault(int defaultPosition);
+
public static final class KnownRetracedMethod extends RetracedMethod {
private final MethodReference methodReference;
- private final RetracedClass classReference;
+ private final int position;
- private KnownRetracedMethod(RetracedClass classReference, MethodReference methodReference) {
+ private KnownRetracedMethod(MethodReference methodReference, int position) {
assert methodReference != null;
- this.classReference = classReference;
this.methodReference = methodReference;
+ this.position = position;
}
@Override
@@ -58,7 +63,7 @@
@Override
public RetracedClass getHolderClass() {
- return classReference;
+ return RetracedClass.create(methodReference.getHolderClass());
}
@Override
@@ -66,6 +71,16 @@
return methodReference.getMethodName();
}
+ @Override
+ public boolean hasPosition() {
+ return position != NO_POSITION;
+ }
+
+ @Override
+ public int getOriginalPositionOrDefault(int defaultPosition) {
+ return hasPosition() ? position : defaultPosition;
+ }
+
public TypeReference getReturnType() {
assert !isVoid();
return methodReference.getReturnType();
@@ -75,14 +90,10 @@
return methodReference.getFormalTypes();
}
- public MethodReference getClassReference() {
+ public MethodReference getMethodReference() {
return methodReference;
}
- public boolean equalsMethodReference(MethodReference reference) {
- return methodReference.equals(reference);
- }
-
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -92,43 +103,70 @@
return false;
}
KnownRetracedMethod that = (KnownRetracedMethod) o;
- assert !methodReference.equals(that.methodReference)
- || classReference.equals(that.classReference);
- return methodReference.equals(that.methodReference);
+ return position == that.position && methodReference.equals(that.methodReference);
}
@Override
public int hashCode() {
- return Objects.hash(methodReference, classReference);
+ return Objects.hash(methodReference, position);
}
}
public static final class UnknownRetracedMethod extends RetracedMethod {
- private final RetracedClass classReference;
- private final String name;
+ private final MethodDefinition methodDefinition;
+ private final int position;
- private UnknownRetracedMethod(RetracedClass classReference, String name) {
- this.classReference = classReference;
- this.name = name;
+ private UnknownRetracedMethod(MethodDefinition methodDefinition, int position) {
+ this.methodDefinition = methodDefinition;
+ this.position = position;
}
@Override
public RetracedClass getHolderClass() {
- return classReference;
+ return RetracedClass.create(methodDefinition.getHolderClass());
}
@Override
public String getMethodName() {
- return name;
+ return methodDefinition.getName();
+ }
+
+ @Override
+ public boolean hasPosition() {
+ return position != NO_POSITION;
+ }
+
+ @Override
+ public int getOriginalPositionOrDefault(int defaultPosition) {
+ return hasPosition() ? position : defaultPosition;
+ }
+
+ public Optional<MethodReference> getMethodReference() {
+ if (!methodDefinition.isFullMethodDefinition()) {
+ return Optional.empty();
+ }
+ return Optional.of(methodDefinition.asFullMethodDefinition().getMethodReference());
}
}
- static RetracedMethod create(RetracedClass classReference, MethodReference methodReference) {
- return new KnownRetracedMethod(classReference, methodReference);
+ static RetracedMethod create(MethodDefinition methodDefinition) {
+ return create(methodDefinition, NO_POSITION);
}
- static RetracedMethod createUnknown(RetracedClass classReference, String name) {
- return new UnknownRetracedMethod(classReference, name);
+ static RetracedMethod create(MethodDefinition methodDefinition, int position) {
+ if (methodDefinition.isFullMethodDefinition()) {
+ return new KnownRetracedMethod(
+ methodDefinition.asFullMethodDefinition().getMethodReference(), position);
+ }
+ return new UnknownRetracedMethod(methodDefinition, position);
+ }
+
+ static RetracedMethod create(MethodReference methodReference) {
+ return create(methodReference, NO_POSITION);
+ }
+
+ static RetracedMethod create(MethodReference methodReference, int position) {
+ return new KnownRetracedMethod(methodReference, position);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index 583ca7c..279253c 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -50,6 +50,13 @@
}
@Override
+ public RetraceFrameResult retrace(MethodReference methodReference, int position) {
+ return retrace(methodReference.getHolderClass())
+ .lookupMethod(methodReference.getMethodName())
+ .narrowByPosition(position);
+ }
+
+ @Override
public RetraceClassResult retrace(ClassReference classReference) {
return RetraceClassResult.create(
classReference, classNameMapper.getClassNaming(classReference.getTypeName()), this);
@@ -57,6 +64,6 @@
@Override
public RetraceTypeResult retrace(TypeReference typeReference) {
- return new RetraceTypeResult(typeReference, this);
+ return RetraceTypeResult.create(typeReference, this);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
new file mode 100644
index 0000000..efaab05
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
@@ -0,0 +1,27 @@
+// 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;
+
+@Keep
+public abstract class StackTraceElementProxy<T> {
+
+ public abstract boolean hasClassName();
+
+ public abstract boolean hasMethodName();
+
+ public abstract boolean hasFileName();
+
+ public abstract boolean hasLineNumber();
+
+ public abstract String className();
+
+ public abstract String methodName();
+
+ public abstract String fileName();
+
+ public abstract int lineNumber();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
new file mode 100644
index 0000000..042b1f8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
@@ -0,0 +1,195 @@
+// 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 java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+@Keep
+public class StackTraceElementProxyRetracer<T extends StackTraceElementProxy<?>> {
+
+ private final RetraceApi retracer;
+
+ public StackTraceElementProxyRetracer(RetraceApi retracer) {
+ this.retracer = retracer;
+ }
+
+ public Stream<RetraceStackTraceProxy<T>> retrace(T element) {
+ if (!element.hasClassName()) {
+ return Stream.of(RetraceStackTraceProxy.builder(element).build());
+ }
+ RetraceClassResult classResult =
+ retracer.retrace(Reference.classFromTypeName(element.className()));
+ List<RetraceStackTraceProxy<T>> retracedProxies = new ArrayList<>();
+ if (!element.hasMethodName()) {
+ classResult.forEach(
+ classElement -> {
+ RetraceStackTraceProxy.Builder<T> proxy =
+ RetraceStackTraceProxy.builder(element)
+ .setRetracedClassElement(classElement.getRetracedClass())
+ .setAmbiguous(classResult.isAmbiguous());
+ if (element.hasFileName()) {
+ proxy.setSourceFile(classElement.retraceSourceFile(element.fileName()).getFilename());
+ }
+ retracedProxies.add(proxy.build());
+ });
+ } else {
+ RetraceMethodResult retraceMethodResult = classResult.lookupMethod(element.methodName());
+ Result<? extends RetraceClassMemberElement<RetracedMethod>, ?> methodResult;
+ if (element.hasLineNumber()) {
+ methodResult = retraceMethodResult.narrowByPosition(element.lineNumber());
+ } else {
+ methodResult = retraceMethodResult;
+ }
+ methodResult.forEach(
+ methodElement -> {
+ methodElement.visitFrames(
+ (frame, index) -> {
+ RetraceStackTraceProxy.Builder<T> proxy =
+ RetraceStackTraceProxy.builder(element)
+ .setRetracedClassElement(frame.getHolderClass())
+ .setRetracedMethodElement(frame)
+ .setAmbiguous(methodResult.isAmbiguous() && index == 0);
+ if (element.hasLineNumber()) {
+ proxy.setLineNumber(frame.getOriginalPositionOrDefault(element.lineNumber()));
+ }
+ if (element.hasFileName()) {
+ proxy.setSourceFile(
+ methodElement.retraceSourceFile(frame, element.fileName()).getFilename());
+ }
+ retracedProxies.add(proxy.build());
+ });
+ });
+ }
+ return retracedProxies.stream();
+ }
+
+ @Keep
+ public static class RetraceStackTraceProxy<T extends StackTraceElementProxy<?>> {
+
+ private final T originalItem;
+ private final RetracedClass retracedClass;
+ private final RetracedMethod retracedMethod;
+ private final String sourceFile;
+ private final int lineNumber;
+ private final boolean isAmbiguous;
+
+ private RetraceStackTraceProxy(
+ T originalItem,
+ RetracedClass retracedClass,
+ RetracedMethod retracedMethod,
+ String sourceFile,
+ int lineNumber,
+ boolean isAmbiguous) {
+ assert originalItem != null;
+ this.originalItem = originalItem;
+ this.retracedClass = retracedClass;
+ this.retracedMethod = retracedMethod;
+ this.sourceFile = sourceFile;
+ this.lineNumber = lineNumber;
+ this.isAmbiguous = isAmbiguous;
+ }
+
+ public boolean isAmbiguous() {
+ return isAmbiguous;
+ }
+
+ public boolean hasRetracedClass() {
+ return retracedClass != null;
+ }
+
+ public boolean hasRetracedMethod() {
+ return retracedMethod != null;
+ }
+
+ public boolean hasSourceFile() {
+ return sourceFile != null;
+ }
+
+ public boolean hasLineNumber() {
+ return lineNumber != -1;
+ }
+
+ public T getOriginalItem() {
+ return originalItem;
+ }
+
+ public RetracedClass getRetracedClass() {
+ return retracedClass;
+ }
+
+ public RetracedMethod getRetracedMethod() {
+ return retracedMethod;
+ }
+
+ public String getSourceFile() {
+ return sourceFile;
+ }
+
+ private static <T extends StackTraceElementProxy<?>> Builder<T> builder(T originalElement) {
+ return new Builder<>(originalElement);
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ private static class Builder<T extends StackTraceElementProxy<?>> {
+
+ private final T originalElement;
+ private RetracedClass classContext;
+ private RetracedMethod methodContext;
+ private String sourceFile;
+ private int lineNumber = -1;
+ private boolean isAmbiguous;
+
+ private Builder(T originalElement) {
+ this.originalElement = originalElement;
+ }
+
+ private Builder<T> setRetracedClassElement(RetracedClass retracedClass) {
+ this.classContext = retracedClass;
+ return this;
+ }
+
+ private Builder<T> setRetracedMethodElement(RetracedMethod methodElement) {
+ this.methodContext = methodElement;
+ return this;
+ }
+
+ private Builder<T> setSourceFile(String sourceFile) {
+ this.sourceFile = sourceFile;
+ return this;
+ }
+
+ private Builder<T> setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ return this;
+ }
+
+ private Builder<T> setAmbiguous(boolean ambiguous) {
+ this.isAmbiguous = ambiguous;
+ return this;
+ }
+
+ private RetraceStackTraceProxy<T> build() {
+ RetracedClass retracedClass = classContext;
+ if (methodContext != null) {
+ retracedClass = methodContext.getHolderClass();
+ }
+ return new RetraceStackTraceProxy<>(
+ originalElement,
+ retracedClass,
+ methodContext,
+ sourceFile != null ? sourceFile : null,
+ lineNumber,
+ isAmbiguous);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementStringProxy.java
new file mode 100644
index 0000000..dc21cbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementStringProxy.java
@@ -0,0 +1,228 @@
+// 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 static com.android.tools.r8.retrace.PlainStackTraceVisitor.firstNonWhiteSpaceCharacterFromIndex;
+import static com.android.tools.r8.retrace.StackTraceElementStringProxy.StringIndex.noIndex;
+
+import com.android.tools.r8.retrace.StackTraceElementProxyRetracer.RetraceStackTraceProxy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+
+public final class StackTraceElementStringProxy extends StackTraceElementProxy<String> {
+
+ private final String line;
+ private final List<StringIndex> orderedIndices;
+ private final StringIndex className;
+ private final StringIndex methodName;
+ private final StringIndex sourceFile;
+ private final StringIndex lineNumber;
+
+ private StackTraceElementStringProxy(
+ String line,
+ List<StringIndex> orderedIndices,
+ StringIndex className,
+ StringIndex methodName,
+ StringIndex sourceFile,
+ StringIndex lineNumber) {
+ this.line = line;
+ this.orderedIndices = orderedIndices;
+ this.className = className;
+ this.methodName = methodName;
+ this.sourceFile = sourceFile;
+ this.lineNumber = lineNumber;
+ }
+
+ static StackTraceElementStringProxyBuilder builder(String line) {
+ return new StackTraceElementStringProxyBuilder(line);
+ }
+
+ @Override
+ public boolean hasClassName() {
+ return className.hasIndex();
+ }
+
+ @Override
+ public boolean hasMethodName() {
+ return methodName.hasIndex();
+ }
+
+ @Override
+ public boolean hasFileName() {
+ return sourceFile.hasIndex();
+ }
+
+ @Override
+ public boolean hasLineNumber() {
+ return lineNumber.hasIndex();
+ }
+
+ @Override
+ public String className() {
+ return hasClassName() ? getEntryInLine(className) : null;
+ }
+
+ @Override
+ public String methodName() {
+ return hasMethodName() ? getEntryInLine(methodName) : null;
+ }
+
+ @Override
+ public String fileName() {
+ return hasFileName() ? getEntryInLine(sourceFile) : null;
+ }
+
+ @Override
+ public int lineNumber() {
+ if (!hasLineNumber()) {
+ return -1;
+ }
+ try {
+ return Integer.parseInt(getEntryInLine(lineNumber));
+ } catch (NumberFormatException nfe) {
+ return -1;
+ }
+ }
+
+ public String toRetracedItem(
+ RetraceStackTraceProxy<StackTraceElementStringProxy> retracedProxy, boolean printAmbiguous) {
+ StringBuilder sb = new StringBuilder();
+ int lastSeenIndex = 0;
+ if (retracedProxy.isAmbiguous() && printAmbiguous) {
+ lastSeenIndex = firstNonWhiteSpaceCharacterFromIndex(line, 0);
+ sb.append(line, 0, lastSeenIndex);
+ sb.append("<OR> ");
+ }
+ for (StringIndex index : orderedIndices) {
+ sb.append(line, lastSeenIndex, index.startIndex);
+ sb.append(index.retracedString.apply(retracedProxy, this));
+ lastSeenIndex = index.endIndex;
+ }
+ sb.append(line, lastSeenIndex, line.length());
+ return sb.toString();
+ }
+
+ public String lineNumberAsString() {
+ return getEntryInLine(lineNumber);
+ }
+
+ private String getEntryInLine(StringIndex index) {
+ assert index != noIndex();
+ return line.substring(index.startIndex, index.endIndex);
+ }
+
+ public static class StackTraceElementStringProxyBuilder {
+
+ private final String line;
+ private final List<StringIndex> orderedIndices = new ArrayList<>();
+ private StringIndex className = noIndex();
+ private StringIndex methodName = noIndex();
+ private StringIndex sourceFile = noIndex();
+ private StringIndex lineNumber = noIndex();
+ private int lastSeenStartIndex = -1;
+
+ private StackTraceElementStringProxyBuilder(String line) {
+ this.line = line;
+ }
+
+ public StackTraceElementStringProxyBuilder registerClassName(int startIndex, int endIndex) {
+ ensureLineIndexIncreases(startIndex);
+ className =
+ new StringIndex(
+ startIndex,
+ endIndex,
+ (retraced, original) -> {
+ assert retraced.hasRetracedClass();
+ return retraced.getRetracedClass().getTypeName();
+ });
+ orderedIndices.add(className);
+ return this;
+ }
+
+ public StackTraceElementStringProxyBuilder registerMethodName(int startIndex, int endIndex) {
+ methodName =
+ new StringIndex(
+ startIndex,
+ endIndex,
+ (retraced, original) ->
+ retraced.hasRetracedMethod()
+ ? retraced.getRetracedMethod().getMethodName()
+ : original.methodName());
+ orderedIndices.add(methodName);
+ return this;
+ }
+
+ public StackTraceElementStringProxyBuilder registerSourceFile(int startIndex, int endIndex) {
+ sourceFile =
+ new StringIndex(
+ startIndex,
+ endIndex,
+ (retraced, original) ->
+ retraced.hasSourceFile() ? retraced.getSourceFile() : original.fileName());
+ orderedIndices.add(sourceFile);
+ return this;
+ }
+
+ public StackTraceElementStringProxyBuilder registerLineNumber(int startIndex, int endIndex) {
+ lineNumber =
+ new StringIndex(
+ startIndex,
+ endIndex,
+ (retraced, original) ->
+ retraced.hasLineNumber()
+ ? retraced.getLineNumber() + ""
+ : original.lineNumberAsString());
+ orderedIndices.add(lineNumber);
+ return this;
+ }
+
+ public StackTraceElementStringProxy build() {
+ return new StackTraceElementStringProxy(
+ line, orderedIndices, className, methodName, sourceFile, lineNumber);
+ }
+
+ private void ensureLineIndexIncreases(int newStartIndex) {
+ if (lastSeenStartIndex >= newStartIndex) {
+ throw new RuntimeException("Parsing has to be incremental in the order of characters.");
+ }
+ lastSeenStartIndex = newStartIndex;
+ }
+ }
+
+ static final class StringIndex {
+
+ private static final StringIndex NO_INDEX = new StringIndex(-1, -1, null);
+
+ static StringIndex noIndex() {
+ return NO_INDEX;
+ }
+
+ private final int startIndex;
+ private final int endIndex;
+ private final BiFunction<
+ RetraceStackTraceProxy<StackTraceElementStringProxy>,
+ StackTraceElementStringProxy,
+ String>
+ retracedString;
+
+ private StringIndex(
+ int startIndex,
+ int endIndex,
+ BiFunction<
+ RetraceStackTraceProxy<StackTraceElementStringProxy>,
+ StackTraceElementStringProxy,
+ String>
+ retracedString) {
+ this.startIndex = startIndex;
+ this.endIndex = endIndex;
+ this.retracedString = retracedString;
+ }
+
+ boolean hasIndex() {
+ return this != NO_INDEX;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java
new file mode 100644
index 0000000..fd9c822
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java
@@ -0,0 +1,12 @@
+// 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 java.util.function.Consumer;
+
+public interface StackTraceVisitor<T extends StackTraceElementProxy<?>> {
+
+ void forEach(Consumer<T> consumer);
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 3b68071..d5bc317 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexCallSite;
@@ -568,29 +569,30 @@
return definition;
}
- private int largestInputCfVersion = -1;
+ private CfVersion largestInputCfVersion = null;
public boolean canUseConstClassInstructions(InternalOptions options) {
if (!options.isGeneratingClassFiles()) {
return true;
}
- if (largestInputCfVersion == -1) {
+ if (largestInputCfVersion == null) {
computeLargestCfVersion();
}
return options.canUseConstClassInstructions(largestInputCfVersion);
}
private synchronized void computeLargestCfVersion() {
- if (largestInputCfVersion != -1) {
+ if (largestInputCfVersion != null) {
return;
}
for (DexProgramClass clazz : classes()) {
// Skip synthetic classes which may not have a specified version.
if (clazz.hasClassFileVersion()) {
- largestInputCfVersion = Math.max(largestInputCfVersion, clazz.getInitialClassFileVersion());
+ largestInputCfVersion =
+ CfVersion.maxAllowNull(largestInputCfVersion, clazz.getInitialClassFileVersion());
}
}
- assert largestInputCfVersion != -1;
+ assert largestInputCfVersion != null;
}
public boolean isLiveProgramClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java b/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
deleted file mode 100644
index 6a2151b..0000000
--- a/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
-import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
-import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
-import com.google.common.collect.Sets;
-import java.util.Set;
-
-public class ClassMergingEnqueuerExtension
- implements EnqueuerInstanceOfAnalysis,
- EnqueuerCheckCastAnalysis,
- EnqueuerExceptionGuardAnalysis {
-
- private final Set<DexType> instanceOfTypes = Sets.newIdentityHashSet();
- private final Set<DexType> checkCastTypes = Sets.newIdentityHashSet();
- private final Set<DexType> exceptionGuardTypes = Sets.newIdentityHashSet();
- private final DexItemFactory factory;
-
- public ClassMergingEnqueuerExtension(DexItemFactory factory) {
- this.factory = factory;
- }
-
- @Override
- public void traceCheckCast(DexType type, ProgramMethod context) {
- checkCastTypes.add(type.toBaseType(factory));
- }
-
- @Override
- public void traceInstanceOf(DexType type, ProgramMethod context) {
- instanceOfTypes.add(type.toBaseType(factory));
- }
-
- @Override
- public void traceExceptionGuard(DexType guard, ProgramMethod context) {
- exceptionGuardTypes.add(guard);
- }
-
- public boolean isCheckCastType(DexProgramClass clazz) {
- return checkCastTypes.contains(clazz.type);
- }
-
- public boolean isInstanceOfType(DexProgramClass clazz) {
- return instanceOfTypes.contains(clazz.type);
- }
-
- public boolean isExceptionGuardType(DexProgramClass clazz) {
- return exceptionGuardTypes.contains(clazz.type);
- }
-
- public boolean isRuntimeCheckType(DexProgramClass clazz) {
- return isInstanceOfType(clazz) || isCheckCastType(clazz) || isExceptionGuardType(clazz);
- }
-
- public void attach(Enqueuer enqueuer) {
- enqueuer
- .registerInstanceOfAnalysis(this)
- .registerCheckCastAnalysis(this)
- .registerExceptionGuardAnalysis(this);
- }
-}
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 ec4e675..a7fdc85 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupLambdaTarget;
@@ -78,6 +79,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.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
@@ -340,11 +342,13 @@
private final GraphReporter graphReporter;
private final LambdaRewriter lambdaRewriter;
+ private final BackportedMethodRewriter backportRewriter;
private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis;
private final Map<DexType, Pair<LambdaClass, ProgramMethod>> lambdaClasses =
new IdentityHashMap<>();
private final Map<DexEncodedMethod, Map<DexCallSite, LambdaClass>> lambdaCallSites =
new IdentityHashMap<>();
+ private final Map<DexMethod, ProgramMethod> methodsWithBackports = new IdentityHashMap<>();
private final Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
Enqueuer(
@@ -383,6 +387,8 @@
liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
liveFields = new LiveFieldsSet(graphReporter::registerField);
lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
+ backportRewriter =
+ options.desugarState == DesugarState.ON ? new BackportedMethodRewriter(appView) : null;
objectAllocationInfoCollection =
ObjectAllocationInfoCollectionImpl.builder(mode.isInitialTreeShaking(), graphReporter);
@@ -1077,6 +1083,10 @@
private void traceInvokeDirect(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
+ if (registerBackportInvoke(invokedMethod, context)) {
+ return;
+ }
+
if (!registerMethodWithTargetAndContext(
methodAccessInfoCollection::registerInvokeDirectInContext, invokedMethod, context)) {
return;
@@ -1098,6 +1108,10 @@
private void traceInvokeInterface(
DexMethod method, ProgramMethod context, KeepReason keepReason) {
+ if (registerBackportInvoke(method, context)) {
+ return;
+ }
+
if (!registerMethodWithTargetAndContext(
methodAccessInfoCollection::registerInvokeInterfaceInContext, method, context)) {
return;
@@ -1117,8 +1131,20 @@
traceInvokeStatic(invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context));
}
+ private boolean registerBackportInvoke(DexMethod invokedMethod, ProgramMethod context) {
+ if (backportRewriter != null && backportRewriter.needsDesugaring(invokedMethod)) {
+ methodsWithBackports.putIfAbsent(context.getReference(), context);
+ return true;
+ }
+ return false;
+ }
+
private void traceInvokeStatic(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
+ if (registerBackportInvoke(invokedMethod, context)) {
+ return;
+ }
+
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
|| dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
@@ -1150,6 +1176,9 @@
}
void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
+ if (registerBackportInvoke(invokedMethod, context)) {
+ return;
+ }
// We have to revisit super invokes based on the context they are found in. The same
// method descriptor will hit different targets, depending on the context it is used in.
DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, context);
@@ -1174,6 +1203,10 @@
private void traceInvokeVirtual(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
+ if (registerBackportInvoke(invokedMethod, context)) {
+ return;
+ }
+
if (invokedMethod == appView.dexItemFactory().classMethods.newInstance
|| invokedMethod == appView.dexItemFactory().constructorMethods.newInstance) {
pendingReflectiveUses.add(context);
@@ -2698,6 +2731,10 @@
new KotlinMetadataEnqueuerExtension(
appView, enqueuerDefinitionSupplier, initialPrunedTypes));
}
+ if (appView.options().getProguardConfiguration() != null
+ && appView.options().getProguardConfiguration().getKeepAttributes().signature) {
+ registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
+ }
if (mode.isInitialTreeShaking()) {
// This is simulating the effect of the "root set" applied rules.
// This is done only in the initial pass, in subsequent passes the "rules" are reapplied
@@ -2892,6 +2929,7 @@
synthesizeInterfaceMethodBridges(additions);
synthesizeLambdas(additions);
synthesizeLibraryConversionWrappers(additions);
+ synthesizeBackports(additions);
if (additions.isEmpty()) {
return;
}
@@ -2923,6 +2961,12 @@
syntheticInterfaceMethodBridges.clear();
}
+ private void synthesizeBackports(SyntheticAdditions additions) {
+ for (ProgramMethod method : methodsWithBackports.values()) {
+ backportRewriter.desugar(method, appInfo, additions::addLiveMethod);
+ }
+ }
+
private void synthesizeLambdas(SyntheticAdditions additions) {
if (lambdaRewriter == null || lambdaClasses.isEmpty()) {
assert lambdaCallSites.isEmpty();
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index 624ee95..765fe31 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -13,4 +13,8 @@
boolean isAccessModificationEnabled();
boolean isRepackagingEnabled();
+
+ boolean isForceProguardCompatibilityEnabled();
+
+ boolean isKeepAttributesSignatureEnabled();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 4cb677f..7d75f05 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -82,6 +82,12 @@
return allowAccessModification;
}
+ public boolean isAllowSignatureAttributeRemovalAllowed(
+ GlobalKeepInfoConfiguration configuration) {
+ return !configuration.isKeepAttributesSignatureEnabled()
+ || !(isPinned() || configuration.isForceProguardCompatibilityEnabled());
+ }
+
public abstract boolean isTop();
public abstract boolean isBottom();
diff --git a/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
new file mode 100644
index 0000000..ddfea43
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
@@ -0,0 +1,95 @@
+// 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;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
+import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+public class RuntimeTypeCheckInfo {
+
+ private final Set<DexType> instanceOfTypes;
+ private final Set<DexType> checkCastTypes;
+ private final Set<DexType> exceptionGuardTypes;
+
+ public RuntimeTypeCheckInfo(
+ Set<DexType> instanceOfTypes, Set<DexType> checkCastTypes, Set<DexType> exceptionGuardTypes) {
+ this.instanceOfTypes = instanceOfTypes;
+ this.checkCastTypes = checkCastTypes;
+ this.exceptionGuardTypes = exceptionGuardTypes;
+ }
+
+ public static class Builder
+ implements EnqueuerInstanceOfAnalysis,
+ EnqueuerCheckCastAnalysis,
+ EnqueuerExceptionGuardAnalysis {
+ private final DexItemFactory factory;
+
+ private final Set<DexType> instanceOfTypes = Sets.newIdentityHashSet();
+ private final Set<DexType> checkCastTypes = Sets.newIdentityHashSet();
+ private final Set<DexType> exceptionGuardTypes = Sets.newIdentityHashSet();
+
+ public Builder(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
+ public RuntimeTypeCheckInfo build() {
+ return new RuntimeTypeCheckInfo(instanceOfTypes, checkCastTypes, exceptionGuardTypes);
+ }
+
+ @Override
+ public void traceCheckCast(DexType type, ProgramMethod context) {
+ checkCastTypes.add(type.toBaseType(factory));
+ }
+
+ @Override
+ public void traceInstanceOf(DexType type, ProgramMethod context) {
+ instanceOfTypes.add(type.toBaseType(factory));
+ }
+
+ @Override
+ public void traceExceptionGuard(DexType guard, ProgramMethod context) {
+ exceptionGuardTypes.add(guard);
+ }
+
+ public void attach(Enqueuer enqueuer) {
+ enqueuer
+ .registerInstanceOfAnalysis(this)
+ .registerCheckCastAnalysis(this)
+ .registerExceptionGuardAnalysis(this);
+ }
+ }
+
+ public boolean isCheckCastType(DexProgramClass clazz) {
+ return checkCastTypes.contains(clazz.type);
+ }
+
+ public boolean isInstanceOfType(DexProgramClass clazz) {
+ return instanceOfTypes.contains(clazz.type);
+ }
+
+ public boolean isExceptionGuardType(DexProgramClass clazz) {
+ return exceptionGuardTypes.contains(clazz.type);
+ }
+
+ public boolean isRuntimeCheckType(DexProgramClass clazz) {
+ return isInstanceOfType(clazz) || isCheckCastType(clazz) || isExceptionGuardType(clazz);
+ }
+
+ public RuntimeTypeCheckInfo rewriteWithLens(GraphLens.NonIdentityGraphLens graphLens) {
+ return new RuntimeTypeCheckInfo(
+ SetUtils.mapIdentityHashSet(instanceOfTypes, graphLens::lookupType),
+ SetUtils.mapIdentityHashSet(checkCastTypes, graphLens::lookupType),
+ SetUtils.mapIdentityHashSet(exceptionGuardTypes, graphLens::lookupType));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index abd2a99..974fdb1 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -248,6 +248,12 @@
}
private MergeGroup getMergeGroup(DexProgramClass clazz) {
+ if (appView.getSyntheticItems().isSyntheticClass(clazz)) {
+ // In principle it would be valid to merge synthetic classes into program classes.
+ // Doing so may however increase size as static methods will not be de-duplicated
+ // at synthetic finalization.
+ return MergeGroup.DONT_MERGE;
+ }
if (appView.appInfo().getNoStaticClassMergingSet().contains(clazz.type)) {
return MergeGroup.DONT_MERGE;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 9462690..6100000 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -380,21 +380,26 @@
// * Have access to the no-arg constructor of its first non-serializable superclass
return false;
}
- TraversalContinuation result =
- sourceClass.traverseProgramInstanceInitializers(
- method -> {
- AbortReason reason = disallowInlining(method, targetClass);
- if (reason != null) {
- // Cannot guarantee that markForceInline() will work.
- if (Log.ENABLED) {
- reason.printLogMessageForClass(sourceClass);
+
+ // If there is a constructor in the target, make sure that all source constructors can be
+ // inlined.
+ if (!Iterables.isEmpty(targetClass.programInstanceInitializers())) {
+ TraversalContinuation result =
+ sourceClass.traverseProgramInstanceInitializers(
+ method -> {
+ AbortReason reason = disallowInlining(method, targetClass);
+ if (reason != null) {
+ // Cannot guarantee that markForceInline() will work.
+ if (Log.ENABLED) {
+ reason.printLogMessageForClass(sourceClass);
+ }
+ return TraversalContinuation.BREAK;
}
- return TraversalContinuation.BREAK;
- }
- return TraversalContinuation.CONTINUE;
- });
- if (result.shouldBreak()) {
- return false;
+ return TraversalContinuation.CONTINUE;
+ });
+ if (result.shouldBreak()) {
+ return false;
+ }
}
if (sourceClass.getEnclosingMethodAttribute() != null
|| !sourceClass.getInnerClasses().isEmpty()) {
@@ -1263,7 +1268,7 @@
ParameterAnnotationsList.empty(),
code,
true,
- method.hasClassFileVersion() ? method.getClassFileVersion() : -1);
+ method.hasClassFileVersion() ? method.getClassFileVersion() : null);
bridge.setLibraryMethodOverride(method.isLibraryMethodOverride());
if (method.accessFlags.isPromotedToPublic()) {
// The bridge is now the public method serving the role of the original method, and should
@@ -1688,7 +1693,7 @@
method,
appView,
new SingleTypeMapperGraphLens(method.getHolderType(), context),
- context);
+ context.programInstanceInitializers().iterator().next());
if (constraint == ConstraintWithTarget.NEVER) {
return AbortReason.UNSAFE_INLINING;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 2848d3f..a95cfb4 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -50,7 +50,7 @@
interface GraphLensLookupResultProvider {
- abstract MethodLookupResult get(RewrittenPrototypeDescription prototypeChanges);
+ MethodLookupResult get(RewrittenPrototypeDescription prototypeChanges);
}
private final AppView<?> appView;
@@ -133,7 +133,7 @@
@Override
protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
- return super.mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
+ return mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 93d1248..3625fbb 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -3,6 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
+import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
+
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -91,6 +95,33 @@
return synthesizingContextType;
}
+ void registerPrefixRewriting(DexType hygienicType, AppView<?> appView) {
+ assert hygienicType.toSourceString().startsWith(synthesizingContextType.toSourceString());
+ if (!appView.options().isDesugaredLibraryCompilation()) {
+ return;
+ }
+ DexType rewrittenContext =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .get(synthesizingContextType);
+ if (rewrittenContext == null) {
+ return;
+ }
+ String contextPrefix =
+ getBinaryNameFromDescriptor(synthesizingContextType.toDescriptorString());
+ String rewrittenPrefix = getBinaryNameFromDescriptor(rewrittenContext.toDescriptorString());
+ String suffix =
+ getBinaryNameFromDescriptor(hygienicType.toDescriptorString())
+ .substring(contextPrefix.length());
+ DexType rewrittenType =
+ appView
+ .dexItemFactory()
+ .createType(getDescriptorFromClassBinaryName(rewrittenPrefix + suffix));
+ appView.rewritePrefix.rewriteType(hygienicType, rewrittenType);
+ }
+
void addIfDerivedFromMainDexClass(
DexProgramClass externalSyntheticClass,
MainDexClasses mainDexClasses,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 9dd8bee..2872d1c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -111,12 +111,11 @@
List<DexProgramClass> finalSyntheticClasses = new ArrayList<>();
Set<DexType> derivedMainDexTypesToIgnore = Sets.newIdentityHashSet();
buildLensAndProgram(
- application,
+ appView,
equivalences,
syntheticItems::containsKey,
mainDexClasses,
lensBuilder,
- options,
newProgramClasses,
finalSyntheticClasses,
derivedMainDexTypesToIgnore);
@@ -241,19 +240,19 @@
}
private static void buildLensAndProgram(
- DexApplication app,
+ AppView<?> appView,
Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
Predicate<DexType> isSyntheticType,
MainDexClasses mainDexClasses,
Builder lensBuilder,
- InternalOptions options,
List<DexProgramClass> normalClasses,
List<DexProgramClass> newSyntheticClasses,
Set<DexType> derivedMainDexTypesToIgnore) {
- DexItemFactory factory = options.itemFactory;
+ DexItemFactory factory = appView.dexItemFactory();
- for (DexProgramClass clazz : app.classes()) {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
if (!isSyntheticType.test(clazz.type)) {
+ assert SyntheticItems.verifyNotInternalSynthetic(clazz.type);
normalClasses.add(clazz);
}
}
@@ -264,10 +263,11 @@
mainDexType -> {
derivedMainDexTypes.add(mainDexType);
DexProgramClass mainDexClass =
- DexProgramClass.asProgramClassOrNull(app.definitionFor(mainDexType));
+ DexProgramClass.asProgramClassOrNull(
+ appView.appInfo().definitionForWithoutExistenceAssert(mainDexType));
if (mainDexClass != null) {
derivedMainDexTypes.addAll(
- DexAnnotation.readAnnotationSynthesizedClassMap(mainDexClass, options.itemFactory));
+ DexAnnotation.readAnnotationSynthesizedClassMap(mainDexClass, factory));
}
});
@@ -275,6 +275,7 @@
(syntheticType, syntheticGroup) -> {
SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
SynthesizingContext context = representative.getContext();
+ context.registerPrefixRewriting(syntheticType, appView);
SyntheticClassBuilder builder =
new SyntheticClassBuilder(syntheticType, context, factory);
// TODO(b/158159959): Support grouping multiple methods per synthetic class.
@@ -287,7 +288,7 @@
.setCode(m -> definition.getCode());
});
DexProgramClass externalSyntheticClass = builder.build();
- if (shouldAnnotateSynthetics(options)) {
+ if (shouldAnnotateSynthetics(appView.options())) {
externalSyntheticClass.setAnnotations(
externalSyntheticClass
.annotations()
@@ -313,7 +314,10 @@
// TODO(b/168584485): Remove this once class-mapping support is removed.
DexProgramClass from =
DexProgramClass.asProgramClassOrNull(
- app.definitionFor(member.getContext().getSynthesizingContextType()));
+ appView
+ .appInfo()
+ .definitionForWithoutExistenceAssert(
+ member.getContext().getSynthesizingContextType()));
if (from != null) {
externalSyntheticClass.addSynthesizedFrom(from);
}
@@ -418,6 +422,10 @@
SyntheticMethodDefinition method = (SyntheticMethodDefinition) definition;
if (SyntheticMethodBuilder.isValidSyntheticMethod(method.getMethod().getDefinition())) {
methods.add(method);
+ } else {
+ // Failing this check indicates that an optimization has modified the synthetic in a
+ // disruptive way.
+ assert false;
}
}
return methods;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 6f5c49d..c6726e1 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -308,7 +308,9 @@
ImmutableMap.Builder<DexType, SyntheticReference> rewrittenItems = ImmutableMap.builder();
for (SyntheticReference reference : nonLecacySyntheticItems.values()) {
SyntheticReference rewritten = reference.rewrite(lens);
- rewrittenItems.put(rewritten.getHolder(), rewritten);
+ if (rewritten != null) {
+ rewrittenItems.put(rewritten.getHolder(), rewritten);
+ }
}
// No pending item should need rewriting.
assert legacyPendingClasses.keySet().equals(lens.rewriteTypes(legacyPendingClasses.keySet()));
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
index 86704ea..28913d0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -24,6 +25,11 @@
}
@Override
+ DexReference getReference() {
+ return method;
+ }
+
+ @Override
DexType getHolder() {
return method.holder;
}
@@ -35,14 +41,23 @@
return null;
}
assert clazz.isProgramClass();
- ProgramMethod definition = clazz.asProgramClass().lookupProgramMethod(this.method);
- return new SyntheticMethodDefinition(getContext(), definition);
+ ProgramMethod definition = clazz.asProgramClass().lookupProgramMethod(method);
+ return definition != null ? new SyntheticMethodDefinition(getContext(), definition) : null;
}
@Override
SyntheticReference rewrite(NonIdentityGraphLens lens) {
- SynthesizingContext context = getContext().rewrite(lens);
DexMethod rewritten = lens.lookupMethod(method);
+ // If the reference has been non-trivially rewritten the compiler has changed it and it can no
+ // longer be considered a synthetic. The context may or may not have changed.
+ if (method != rewritten && !lens.isSimpleRenaming(method.holder, rewritten.holder)) {
+ // If the referenced item is rewritten, it should be moved to another holder as the
+ // synthetic holder is no longer part of the synthetic collection.
+ assert method.holder != rewritten.holder;
+ assert SyntheticItems.verifyNotInternalSynthetic(rewritten.holder);
+ return null;
+ }
+ SynthesizingContext context = getContext().rewrite(lens);
return context == getContext() && rewritten == method
? this
: new SyntheticMethodReference(context, rewritten);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
index 408324e..6618378 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.synthesis;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import java.util.function.Function;
@@ -22,6 +23,8 @@
abstract SyntheticDefinition lookupDefinition(Function<DexType, DexClass> definitions);
+ abstract DexReference getReference();
+
final SynthesizingContext getContext() {
return context;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
index 8d66524..7259af1 100644
--- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -7,9 +7,18 @@
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Function;
public class ConsumerUtils {
+ public static <S, T> Function<S, Consumer<T>> curry(BiConsumer<S, T> function) {
+ return arg -> arg2 -> function.accept(arg, arg2);
+ }
+
+ public static <S, T> Consumer<T> apply(BiConsumer<S, T> function, S arg) {
+ return curry(function).apply(arg);
+ }
+
public static <T> Consumer<T> acceptIfNotSeen(Consumer<T> consumer, Set<T> seen) {
return element -> {
if (seen.add(element)) {
diff --git a/src/main/java/com/android/tools/r8/utils/FunctionUtils.java b/src/main/java/com/android/tools/r8/utils/FunctionUtils.java
index 64e8537..b7c7a87 100644
--- a/src/main/java/com/android/tools/r8/utils/FunctionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FunctionUtils.java
@@ -4,11 +4,20 @@
package com.android.tools.r8.utils;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
public class FunctionUtils {
+ public static <S, T, R> Function<S, Function<T, R>> curry(BiFunction<S, T, R> function) {
+ return arg -> arg2 -> function.apply(arg, arg2);
+ }
+
+ public static <S, T, R> Function<T, R> apply(BiFunction<S, T, R> function, S arg) {
+ return curry(function).apply(arg);
+ }
+
public static <T, R> void forEachApply(
Iterable<T> list, Function<T, Consumer<R>> func, R argument) {
for (T t : list) {
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 8bc1a55..c1fe00e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -15,7 +15,9 @@
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Backend;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
@@ -103,7 +105,7 @@
ON
}
- public static final int SUPPORTED_CF_MAJOR_VERSION = Opcodes.V11;
+ public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V11;
public static final int SUPPORTED_DEX_VERSION =
AndroidApiLevel.LATEST.getDexVersion().getIntValue();
@@ -180,7 +182,8 @@
protoShrinking.enableGeneratedMessageLiteShrinking = true;
protoShrinking.enableGeneratedMessageLiteBuilderShrinking = true;
protoShrinking.enableGeneratedExtensionRegistryShrinking = true;
- protoShrinking.enableEnumLiteProtoShrinking = true;
+ // TODO(b/170798502): Reland enum unboxing for proto enums.
+ // protoShrinking.enableEnumLiteProtoShrinking = true;
}
void disableAllOptimizations() {
@@ -347,6 +350,7 @@
new Marker(tool)
.setVersion(Version.LABEL)
.setCompilationMode(debug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
+ .setBackend(isGeneratingClassFiles() ? Backend.CF : Backend.DEX)
.setHasChecksums(encodeChecksums);
// Compiling with D8 and L8 is always with a min API level and desugaring to that level. If
// desugaring is explicitly turned off for D8 the input is expected to already have been
@@ -546,6 +550,16 @@
&& (isShrinking() || isMinifying());
}
+ @Override
+ public boolean isForceProguardCompatibilityEnabled() {
+ return forceProguardCompatibility;
+ }
+
+ @Override
+ public boolean isKeepAttributesSignatureEnabled() {
+ return proguardConfiguration.getKeepAttributes().signature;
+ }
+
/**
* If any non-static class merging is enabled, information about types referred to by instanceOf
* and check cast instructions needs to be collected.
@@ -702,10 +716,10 @@
private static class TypeVersionPair {
- final int version;
+ final CfVersion version;
final DexType type;
- public TypeVersionPair(int version, DexType type) {
+ public TypeVersionPair(CfVersion version, DexType type) {
this.version = version;
this.type = type;
}
@@ -951,7 +965,7 @@
}
}
- public void warningMissingEnclosingMember(DexType clazz, Origin origin, int version) {
+ public void warningMissingEnclosingMember(DexType clazz, Origin origin, CfVersion version) {
TypeVersionPair pair = new TypeVersionPair(version, clazz);
synchronized (missingEnclosingMembers) {
missingEnclosingMembers.computeIfAbsent(origin, k -> new ArrayList<>()).add(pair);
@@ -1065,7 +1079,7 @@
builder.append(", ");
}
builder.append(pair.type);
- printOutdatedToolchain |= pair.version < 49;
+ printOutdatedToolchain |= pair.version.isLessThan(CfVersion.V1_5);
}
reporter.info(new StringDiagnostic(builder.toString(), origin));
}
@@ -1387,14 +1401,14 @@
return result;
}
- public boolean canUseConstClassInstructions(int cfVersion) {
+ public boolean canUseConstClassInstructions(CfVersion cfVersion) {
assert isGeneratingClassFiles();
- return cfVersion >= requiredCfVersionForConstClassInstructions();
+ return cfVersion.isGreaterThanOrEqual(requiredCfVersionForConstClassInstructions());
}
- public int requiredCfVersionForConstClassInstructions() {
+ public CfVersion requiredCfVersionForConstClassInstructions() {
assert isGeneratingClassFiles();
- return Opcodes.V1_5;
+ return CfVersion.V1_5;
}
public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
index 8297a9f..f4115a4 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -60,10 +60,10 @@
return build(appView, null);
}
- public T build(AppView<AppInfoWithLiveness> appView, GraphLens applied) {
+ public T build(AppView<AppInfoWithLiveness> appView, GraphLens appliedGraphLens) {
T result = factory.apply(methods.size());
for (DexMethod oldMethod : methods) {
- DexMethod method = appView.graphLens().getRenamedMethodSignature(oldMethod, applied);
+ DexMethod method = appView.graphLens().getRenamedMethodSignature(oldMethod, appliedGraphLens);
DexProgramClass holder = appView.definitionForHolder(method).asProgramClass();
result.createAndAdd(holder, holder.lookupMethod(method));
}
diff --git a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
index 008ff1f..7cbcf63 100644
--- a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.utils.BooleanUtils;
import java.nio.file.Paths;
import java.util.Collection;
@@ -120,4 +122,24 @@
.build());
assertTrue(testExecuted[0]);
}
+
+ @Test
+ public void backendValueCompatibilityTestForMinApi() {
+ DexItemFactory factory = new DexItemFactory();
+ DexString markerString =
+ factory.createString(
+ "~~D8{\"compilation-mode\":\"debug\",\"has-checksums\":false,"
+ + "\"min-api\":21,\"sha-1\":\"engineering\",\"version\":\"master\" }");
+ assertEquals("dex", Marker.parse(markerString).getBackend());
+ }
+
+ @Test
+ public void backendValueCompatibilityTestForR8Cf() {
+ DexItemFactory factory = new DexItemFactory();
+ DexString markerString =
+ factory.createString(
+ "~~R8{\"compilation-mode\":\"release\",\"has-checksums\":true,"
+ + "\"sha-1\":\"engineering\",\"version\":\"master\" }");
+ assertEquals("cf", Marker.parse(markerString).getBackend());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java b/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
index 6ea338c..c71ca8b 100644
--- a/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
+++ b/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
@@ -21,7 +21,7 @@
@RunWith(Parameterized.class)
public class FailCompilationOnFutureVersionsTest extends TestBase {
- static final int UNSUPPORTED_CF_VERSION = InternalOptions.SUPPORTED_CF_MAJOR_VERSION + 1;
+ static final int UNSUPPORTED_CF_VERSION = InternalOptions.SUPPORTED_CF_VERSION.major() + 1;
static final int UNSUPPORTED_DEX_VERSION = InternalOptions.SUPPORTED_DEX_VERSION + 1;
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/MarkerMatcher.java b/src/test/java/com/android/tools/r8/MarkerMatcher.java
index ea6ea4a..d766770 100644
--- a/src/test/java/com/android/tools/r8/MarkerMatcher.java
+++ b/src/test/java/com/android/tools/r8/MarkerMatcher.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -108,6 +109,20 @@
};
}
+ public static Matcher<Marker> markerBackend(Backend backend) {
+ return new MarkerMatcher() {
+ @Override
+ protected boolean eval(Marker marker) {
+ return marker.getBackend().equals(backend.name().toLowerCase());
+ }
+
+ @Override
+ protected void explain(Description description) {
+ description.appendText(Marker.BACKEND + " ").appendText(backend.name().toLowerCase());
+ }
+ };
+ }
+
public static Matcher<Marker> markerIsDesugared() {
return new MarkerMatcher() {
@Override
diff --git a/src/test/java/com/android/tools/r8/MarkersTest.java b/src/test/java/com/android/tools/r8/MarkersTest.java
index 650cf39..1fdc6ae 100644
--- a/src/test/java/com/android/tools/r8/MarkersTest.java
+++ b/src/test/java/com/android/tools/r8/MarkersTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import static com.android.tools.r8.MarkerMatcher.assertMarkersMatch;
+import static com.android.tools.r8.MarkerMatcher.markerBackend;
import static com.android.tools.r8.MarkerMatcher.markerCompilationMode;
import static com.android.tools.r8.MarkerMatcher.markerDesugaredLibraryIdentifier;
import static com.android.tools.r8.MarkerMatcher.markerHasChecksums;
@@ -118,6 +119,7 @@
allOf(
markerTool(Tool.D8),
markerCompilationMode(compilationMode),
+ markerBackend(Backend.DEX),
markerIsDesugared(),
markerMinApi(apiLevel),
not(markerHasDesugaredLibraryIdentifier()));
@@ -144,6 +146,7 @@
allOf(
markerTool(Tool.D8),
markerCompilationMode(compilationMode),
+ markerBackend(Backend.CF),
markerIsDesugared(),
markerMinApi(apiLevel),
not(markerHasDesugaredLibraryIdentifier()));
@@ -171,6 +174,7 @@
allOf(
markerTool(Tool.R8),
markerCompilationMode(compilationMode),
+ markerBackend(Backend.DEX),
markerIsDesugared(),
markerMinApi(apiLevel),
not(markerHasDesugaredLibraryIdentifier()));
@@ -196,6 +200,7 @@
allOf(
markerTool(Tool.R8),
markerCompilationMode(compilationMode),
+ markerBackend(Backend.CF),
not(markerIsDesugared()),
not(markerHasMinApi()),
not(markerHasDesugaredLibraryIdentifier()));
@@ -242,6 +247,7 @@
allOf(
markerTool(Tool.R8),
markerCompilationMode(compilationMode),
+ markerBackend(Backend.CF),
not(markerIsDesugared()),
not(markerHasMinApi()),
not(markerHasDesugaredLibraryIdentifier())));
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 67721e4..4dc03b1 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1024,6 +1024,8 @@
.put("506-verify-aput", TestCondition.match(TestCondition.R8DEX_COMPILER))
// Test with invalid register usage: returns a register of either long or double.
.put("510-checker-try-catch", TestCondition.match(TestCondition.R8DEX_COMPILER))
+ // Test with backport method which is still present in DX input.
+ .put("530-checker-lse2", TestCondition.match(TestCondition.tools(DexTool.DX)))
// Test with invalid register usage: contains an int-to-byte on the result of aget-object.
.put("518-null-array-get", TestCondition.match(TestCondition.R8DEX_COMPILER))
// Test with invalid register usage: phi of int and float.
@@ -1032,11 +1034,21 @@
.put("552-checker-primitive-typeprop", TestCondition.match(TestCondition.R8DEX_COMPILER))
// Test with invalid register usage: invoke-static {v0,v0}, foo(IL)V
.put("557-checker-ref-equivalent", TestCondition.match(TestCondition.R8DEX_COMPILER))
+ // Test with smali code that calls a method that needs to be desugared.
+ // The smali code is only present in the non-legacy test distrubution, so this only fails
+ // when running the "default" runtime.
+ .put(
+ "567-checker-compare",
+ TestCondition.or(
+ TestCondition.match(TestCondition.runtimes(Runtime.ART_DEFAULT)),
+ TestCondition.match(TestCondition.tools(DexTool.DX))))
// This test is starting from invalid dex code. It splits up a double value and uses
// the first register of a double with the second register of another double.
.put("800-smali", TestCondition.match(TestCondition.R8DEX_COMPILER))
// Contains a loop in the class hierarchy.
.put("804-class-extends-itself", TestCondition.any())
+ // Test with backport method which is still present in DX input.
+ .put("912-classes", TestCondition.match(TestCondition.tools(DexTool.DX)))
// These tests have illegal class flag combinations, so we reject them.
.put("161-final-abstract-class", TestCondition.any())
.build();
@@ -2398,7 +2410,7 @@
CompilationMode compilationMode)
throws Throwable {
if (specification.expectedToFailWithX8) {
- expectException(CompilationError.class);
+ expectedException = true;
try {
executeCompilerUnderTest(
compilerUnderTest,
@@ -2406,7 +2418,8 @@
resultDir.getCanonicalPath(),
compilationMode,
new CompilationOptions(specification));
- } catch (CompilationFailedException e) {
+ } catch (CompilationFailedException | CompilationError e) {
+ expectException(CompilationError.class);
throw new CompilationError(e.getMessage(), e);
}
System.err.println("Should have failed R8/D8 compilation with a CompilationError.");
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index 5a90d52..23ec0c9 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -17,13 +17,13 @@
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.TestDescriptionWatcher;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
@@ -172,6 +172,10 @@
}
private boolean shouldCompileFail() {
+ if (input == Input.DX && getFailingCompileDxToDex().contains(mainClass)) {
+ assert output == Output.DEX;
+ return true;
+ }
if (output == Output.CF && getFailingCompileCf().contains(mainClass)) {
return true;
}
@@ -182,7 +186,7 @@
}
@Test
- public void outputIsIdentical() throws IOException, InterruptedException, ExecutionException {
+ public void outputIsIdentical() throws IOException {
if (shouldCompileFail()) {
// We expected an exception, but got none.
// Return to ensure that this test fails due to the missing exception.
@@ -261,6 +265,10 @@
protected abstract Set<String> getFailingCompileCfToDex();
+ protected Set<String> getFailingCompileDxToDex() {
+ return ImmutableSet.of();
+ }
+
// TODO(mathiasr): Add CompilerSet for CfToDex so we can fold this into getFailingRun().
protected abstract Set<String> getFailingRunCfToDex();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 5eaef9e..04ff03f 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -158,6 +158,15 @@
}
@Override
+ protected Set<String> getFailingCompileDxToDex() {
+ return new ImmutableSet.Builder<String>()
+ .add("regress_72361252.Test") // requires desugar
+ .add("regress_70703087.Test") // requires desugar
+ .add("regress_70737019.Test") // requires desugar
+ .build();
+ }
+
+ @Override
protected Set<String> getFailingCompileCf() {
return new ImmutableSet.Builder<String>()
.build();
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 4149659..37f8a22 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -126,6 +126,15 @@
dexItemFactory, horizontallyMergedClasses))));
}
+ public T addHorizontallyMergedClassesInspectorIf(
+ boolean condition, Consumer<HorizontallyMergedClassesInspector> inspector) {
+
+ if (condition) {
+ return addHorizontallyMergedClassesInspector(inspector);
+ }
+ return self();
+ }
+
public T addHorizontallyMergedLambdaClassesInspector(
Consumer<HorizontallyMergedLambdaClassesInspector> inspector) {
return addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/cf/CfVersionTest.java b/src/test/java/com/android/tools/r8/cf/CfVersionTest.java
new file mode 100644
index 0000000..48871e8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/CfVersionTest.java
@@ -0,0 +1,64 @@
+// 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.cf;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class CfVersionTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public CfVersionTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ @Test
+ public void test() throws Exception {
+ CfVersion v1_1 = CfVersion.V1_1;
+ assertEquals(Opcodes.V1_1, v1_1.raw());
+ assertEquals(45, v1_1.major());
+ assertEquals(3, v1_1.minor());
+
+ CfVersion v1_2 = CfVersion.V1_2;
+ assertEquals(Opcodes.V1_2, v1_2.raw());
+ assertEquals(46, v1_2.major());
+ assertEquals(0, v1_2.minor());
+
+ CfVersion v9 = CfVersion.V9;
+ assertEquals(Opcodes.V9, v9.raw());
+ assertEquals(53, v9.major());
+ assertEquals(0, v9.minor());
+
+ assertLessThan(v1_1, v1_2);
+ assertLessThan(v1_2, v9);
+ }
+
+ private static void assertLessThan(CfVersion less, CfVersion more) {
+ assertFalse(less.isEqual(more));
+ assertEquals(-1, less.compareTo(more));
+ assertEquals(1, more.compareTo(less));
+ assertTrue(less.isLessThan(more));
+ assertTrue(less.isLessThanOrEqual(more));
+ assertFalse(less.isGreaterThan(more));
+ assertFalse(less.isGreaterThanOrEqual(more));
+ assertFalse(more.isLessThan(less));
+ assertFalse(more.isLessThanOrEqual(less));
+ assertTrue(more.isGreaterThan(less));
+ assertTrue(more.isGreaterThanOrEqual(less));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
index c25dc40..da83514 100644
--- a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
+++ b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.cf;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
@@ -18,7 +17,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.objectweb.asm.Opcodes;
@RunWith(Parameterized.class)
public class GetClassLdcClassTest extends TestBase {
@@ -26,16 +24,16 @@
static final String EXPECTED = StringUtils.lines(Runner.class.getName());
private final TestParameters parameters;
- private final int version;
+ private final CfVersion version;
@Parameterized.Parameters(name = "{0}, cf:{1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- new Integer[] {Opcodes.V1_4, Opcodes.V1_5});
+ new CfVersion[] {CfVersion.V1_1, CfVersion.V1_4, CfVersion.V1_5});
}
- public GetClassLdcClassTest(TestParameters parameters, int version) {
+ public GetClassLdcClassTest(TestParameters parameters, CfVersion version) {
this.parameters = parameters;
this.version = version;
}
@@ -99,11 +97,11 @@
inspector -> {
if (parameters.isCfRuntime()) {
// We are assuming the runtimes we are testing are post CF SE 1.4 (version 48).
- int cfVersionForRuntime = getVersion(inspector, TestClass.class);
- assertNotEquals(Opcodes.V1_4, cfVersionForRuntime);
+ CfVersion cfVersionForRuntime = getVersion(inspector, TestClass.class);
+ assertTrue(CfVersion.V1_4.isLessThan(cfVersionForRuntime));
// Check that the downgraded class has been bumped to at least SE 1.5 (version 49).
- int cfVersionAfterUpgrade = getVersion(inspector, Runner.class);
- assertTrue(cfVersionAfterUpgrade >= Opcodes.V1_5);
+ CfVersion cfVersionAfterUpgrade = getVersion(inspector, Runner.class);
+ assertTrue(CfVersion.V1_4.isLessThan(cfVersionAfterUpgrade));
}
// Check that the method uses a const class instruction.
assertTrue(
@@ -115,11 +113,11 @@
});
}
- private static int getVersion(CodeInspector inspector, Class<?> clazz) {
+ private static CfVersion getVersion(CodeInspector inspector, Class<?> clazz) {
return inspector.clazz(clazz).getDexProgramClass().getInitialClassFileVersion();
}
- private static void checkVersion(CodeInspector inspector, Class<?> clazz, int version) {
+ private static void checkVersion(CodeInspector inspector, Class<?> clazz, CfVersion version) {
assertEquals(version, getVersion(inspector, clazz));
}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 279004e..070d6c8 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -72,13 +72,10 @@
@BeforeClass
public static void beforeAll() throws Exception {
- assertThrowsWithHorizontalClassMerging(
- () -> {
- if (data().stream().count() > 0) {
- r8R8Debug = compileR8(CompilationMode.DEBUG);
- r8R8Release = compileR8(CompilationMode.RELEASE);
- }
- });
+ if (data().stream().count() > 0) {
+ r8R8Debug = compileR8(CompilationMode.DEBUG);
+ r8R8Release = compileR8(CompilationMode.RELEASE);
+ }
}
@Parameters(name = "{0}")
@@ -124,7 +121,6 @@
@Test
public void testRetrace() throws IOException {
- expectThrowsWithHorizontalClassMerging();
ProcessResult processResult =
ToolHelper.runProcess(
new ProcessBuilder()
@@ -210,13 +206,11 @@
@Test
public void testSignatures() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testParseSignaturesInJar(r8R8Release.getFirst());
}
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Path helloJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
ProcessResult runResult = ToolHelper.runJava(helloJar, "hello.Hello");
assertEquals(0, runResult.exitCode);
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
index 1c7a809..722e4f6 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -73,7 +73,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
// Run hello.jar to ensure it exists and is valid.
Path hello = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
ProcessResult runHello = ToolHelper.runJava(hello, "hello.Hello");
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/MajorMinorStackTraceVerificationTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/MajorMinorStackTraceVerificationTest.java
new file mode 100644
index 0000000..44dfe61
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/MajorMinorStackTraceVerificationTest.java
@@ -0,0 +1,51 @@
+// 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.cf.stackmap;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.cf.CfVersion;
+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 MajorMinorStackTraceVerificationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MajorMinorStackTraceVerificationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class).setVersion(CfVersion.V1_1).stripFrames("main").transform())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World");
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ System.out.println("Hello World");
+ } else {
+ System.out.println("World, Hello");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
new file mode 100644
index 0000000..5ee01b3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
@@ -0,0 +1,73 @@
+// 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.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+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 ClassesWithNativeMethodsTest extends HorizontalClassMergingTestBase {
+ public ClassesWithNativeMethodsTest(
+ 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())
+ .addHorizontallyMergedClassesInspector(inspector -> inspector.assertNoClassesMerged())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(
+ allOf(
+ containsString("java.lang.UnsatisfiedLinkError:"),
+ containsString("com.android.tools.r8.classmerging.horizontal.b.a(")))
+ .inspectFailure(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(codeInspector.clazz(B.class), isPresent());
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("a");
+ }
+
+ @NeverInline
+ public String foo() {
+ return "foo";
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("b");
+ }
+
+ public native String foo();
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ System.out.println(new A().foo());
+ System.out.println(new B().foo());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java
new file mode 100644
index 0000000..b14763f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.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.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.A;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.B;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.Main;
+import org.junit.Test;
+
+public class CompanionClassMergingTest extends HorizontalClassMergingTestBase {
+ public CompanionClassMergingTest(
+ 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)
+ .addOptionsModification(options -> options.enableClassInlining = false)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector -> inspector.assertMergedInto(B.Companion.class, A.Companion.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("foo a 0", "foo b 1")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(codeInspector.clazz(B.class), isPresent());
+
+ assertThat(codeInspector.clazz(A.Companion.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.Companion.class),
+ notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public static final Companion companion = new Companion(null);
+ public static String foo;
+
+ static {
+ foo = "foo a " + Main.next();
+ }
+
+ public static String access$getFoo$cp() {
+ return foo;
+ }
+
+ public static class Companion {
+ public Companion() {}
+
+ public Companion(Object obj) {
+ this();
+ }
+
+ public String getFoo() {
+ return access$getFoo$cp();
+ }
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public static final Companion companion = new Companion(null);
+ public static String foo;
+
+ static {
+ foo = "foo b " + Main.next();
+ }
+
+ public static String access$getFoo$cp() {
+ return foo;
+ }
+
+ public static class Companion {
+ public Companion() {}
+
+ public Companion(Object obj) {
+ this();
+ }
+
+ public String getFoo() {
+ return access$getFoo$cp();
+ }
+ }
+ }
+
+ public static class Main {
+ static int COUNT = 0;
+
+ public static int next() {
+ return COUNT++;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(A.companion.getFoo());
+ System.out.println(B.companion.getFoo());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
new file mode 100644
index 0000000..2383a03
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
@@ -0,0 +1,80 @@
+// 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.notIf;
+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.NoHorizontalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ConstructorCantInlineTest extends HorizontalClassMergingTestBase {
+ public ConstructorCantInlineTest(
+ 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("c", "foo: foo")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), not(isPresent()));
+ assertThat(codeInspector.clazz(B.class), isPresent());
+ assertThat(codeInspector.clazz(C.class), isPresent());
+ assertThat(
+ codeInspector.clazz(D.class), notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ public static class A {
+ public String foo() {
+ return "foo";
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NeverClassInline
+ public static class B extends A {}
+
+ @NeverClassInline
+ public static class C {
+ C() {
+ System.out.println("c");
+ }
+ }
+
+ public static class D {
+ D() {
+ foo(new B());
+ }
+
+ @NeverInline
+ static void foo(A a) {
+ System.out.println("foo: " + a.foo());
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new C();
+ new D();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index dd1b6ca..109b0bc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -34,6 +34,9 @@
.addKeepMainRule(Main.class)
.addOptionsModification(
options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertMerged(A.class, B.class).assertMergedIntoDifferentType(B.class))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
index ffdae33..4cdca16 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
@@ -5,11 +5,13 @@
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.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.IsNot.not;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.CompanionClassMergingTest.A;
+import com.android.tools.r8.classmerging.horizontal.CompanionClassMergingTest.B;
import org.junit.Test;
public class EmptyClassTest extends HorizontalClassMergingTestBase {
@@ -26,36 +28,36 @@
options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccess()
+ .assertSuccessWithOutputLines("a", "b: foo")
.inspect(
codeInspector -> {
- if (enableHorizontalClassMerging) {
- assertThat(codeInspector.clazz(A.class), isPresent());
- 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());
- }
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
});
}
@NeverClassInline
- public static class A {}
+ public static class A {
+ public A() {
+ System.out.println("a");
+ }
+ }
@NeverClassInline
public static class B {
- // TODO(b/164924717): remove non overlapping constructor requirement
- public B(String s) {}
+ public B(String s) {
+ System.out.println("b: " + s);
+ }
}
public static class Main {
public static void main(String[] args) {
A a = new A();
- System.out.println(a);
- B b = new B("");
- System.out.println(b);
+ B b = new B("foo");
}
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
new file mode 100644
index 0000000..cfafb12
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
@@ -0,0 +1,90 @@
+// 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.notIf;
+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 com.android.tools.r8.classmerging.horizontal.SuperConstructorCallsVirtualMethodTest.A;
+import com.android.tools.r8.classmerging.horizontal.SuperConstructorCallsVirtualMethodTest.B;
+import com.android.tools.r8.classmerging.horizontal.SuperConstructorCallsVirtualMethodTest.Main;
+import com.android.tools.r8.classmerging.horizontal.SuperConstructorCallsVirtualMethodTest.Parent;
+import java.util.ArrayList;
+import org.junit.Test;
+
+public class InheritsFromLibraryClassTest extends HorizontalClassMergingTestBase {
+ public InheritsFromLibraryClassTest(
+ 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("a", "foo a", "b", "foo")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Parent.class), isPresent());
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ assertThat(codeInspector.clazz(C.class), isPresent());
+ });
+ }
+
+ public static class Parent {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class A extends Parent {
+ public A() {
+ System.out.println("a");
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("foo a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends Parent {
+ public B() {
+ System.out.println("b");
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends ArrayList {
+ public C() {}
+
+ public void fooB(B b) {
+ b.foo();
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A().foo();
+ new C().fooB(new B());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java
new file mode 100644
index 0000000..a5eb885
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java
@@ -0,0 +1,72 @@
+// 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.isFinal;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class MergeNonFinalAndFinalClassTest extends HorizontalClassMergingTestBase {
+ public MergeNonFinalAndFinalClassTest(
+ 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)
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector ->
+ assertThat(
+ inspector.clazz(A.class), notIf(isFinal(), enableHorizontalClassMerging)))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("a", "b", "b", "c");
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A();
+ new B();
+ new C();
+ }
+ }
+
+ @NeverClassInline
+ public static final class A {
+ public A() {
+ System.out.println("a");
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class B {
+ public B() {
+ System.out.println("b");
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends B {
+ public C() {
+ System.out.println("c");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedSuperMethodIsDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedSuperMethodIsDefaultMethodTest.java
new file mode 100644
index 0000000..2fa8996
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedSuperMethodIsDefaultMethodTest.java
@@ -0,0 +1,83 @@
+// 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.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class MergedSuperMethodIsDefaultMethodTest extends HorizontalClassMergingTestBase {
+ public MergedSuperMethodIsDefaultMethodTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(this.getClass())
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I.foo")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(I.class), isPresent());
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(codeInspector.clazz(B.class), isPresent());
+ assertThat(
+ codeInspector.clazz(C.class), notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ @NoVerticalClassMerging
+ public interface I {
+ @NeverInline
+ default void foo() {
+ System.out.println("I.foo");
+ }
+ }
+
+ public abstract static class A implements I {}
+
+ @NeverClassInline
+ public static class B extends A {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("B.foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends A {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ callA(args.length == 0 ? new C() : new B());
+ }
+
+ @NeverInline
+ private static void callA(A a) {
+ a.foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
index 91dc3b1..e357250 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -21,19 +21,16 @@
@Test
public void test() throws Exception {
- if (enableHorizontalClassMerging) {
- thrown.expect(Throwable.class);
- }
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
.addKeepMainRule(Main.class)
.addOptionsModification(
options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
- .addHorizontallyMergedLambdaClassesInspector(
+ .addHorizontallyMergedClassesInspector(
inspector -> {
if (enableHorizontalClassMerging) {
- inspector.assertMerged(C.class, D.class);
+ inspector.assertMerged(C.class, D.class).assertMergedIntoDifferentType(D.class);
}
})
.enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
index 3581801..3afcfb2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -22,9 +22,6 @@
@Test
public void test() throws Exception {
- if (enableHorizontalClassMerging) {
- thrown.expect(Throwable.class);
- }
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
new file mode 100644
index 0000000..9ecbf5a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
@@ -0,0 +1,71 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class VerticallyMergedClassDistinguishedByCheckCastTest
+ extends HorizontalClassMergingTestBase {
+
+ public VerticallyMergedClassDistinguishedByCheckCastTest(
+ 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)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("fail", "bar")
+ .inspect(codeInspector -> {});
+ }
+
+ @NeverClassInline
+ public static class Parent {
+ @NeverInline
+ public void bar() {
+ System.out.println("bar");
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends Parent {}
+
+ public static class Main {
+ @NeverInline
+ public static void checkObject(Object o) {
+ try {
+ Parent b = (Parent) o;
+ b.bar();
+ } catch (ClassCastException ex) {
+ System.out.println("fail");
+ }
+ }
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ checkObject(a);
+ checkObject(b);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
new file mode 100644
index 0000000..bba42bb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
@@ -0,0 +1,66 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class VerticallyMergedClassDistinguishedByInstanceOfTest
+ extends HorizontalClassMergingTestBase {
+
+ public VerticallyMergedClassDistinguishedByInstanceOfTest(
+ 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)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("false", "true")
+ .inspect(codeInspector -> {});
+ }
+
+ @NeverClassInline
+ public static class Parent {
+ @NeverInline
+ public void bar() {
+ System.out.println("bar");
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends Parent {}
+
+ public static class Main {
+ @NeverInline
+ public static void checkObject(Object o) {
+ System.out.println(o instanceof Parent);
+ }
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ checkObject(a);
+ checkObject(b);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
new file mode 100644
index 0000000..982fb65
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
@@ -0,0 +1,78 @@
+// 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.vertical;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Arrays;
+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 ForceInlineConstructorWithRetargetedLibMemberTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public ForceInlineConstructorWithRetargetedLibMemberTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ try {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ assertTrue(parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N));
+ } catch (CompilationFailedException e) {
+ // TODO(b/170677722): Fix compilation failure.
+ assertTrue(parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
+ assertTrue(e.getCause() instanceof InternalCompilerError);
+ assertThat(e.getCause().getMessage(), containsString("FORCE inlining on non-inlinable"));
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new B(args);
+ }
+ }
+
+ static class A {
+
+ A(String[] args) {
+ Arrays.stream(args);
+ }
+ }
+
+ @NeverClassInline
+ static class B extends A {
+
+ B(String[] args) {
+ super(args);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
index ecf49fc..0f28809 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.classmerging.vertical.NonReboundFieldAccessWithMergedTypeTest.GreetingBase;
import com.android.tools.r8.classmerging.vertical.testclasses.NonReboundFieldAccessOnMergedClassTestClasses;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,13 +30,12 @@
@Test
public void test() throws Exception {
- thrown.expect(Throwable.class);
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
.addKeepMainRule(Main.class)
.addVerticallyMergedClassesInspector(
- inspector -> inspector.assertMergedIntoSubtype(GreetingBase.class))
+ inspector -> inspector.assertMergedIntoSubtype(C.class))
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
index 5a3f831..6d8cef1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
@@ -30,7 +30,6 @@
@Test
public void test() throws Exception {
- thrown.expect(Throwable.class);
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 13d59dc..44bcc80 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -2108,14 +2108,6 @@
}
return false;
}
- if (isInLambdaClass(mirror, location)) {
- // Lambda classes must be skipped since they are only wrappers around lambda code.
- if (DEBUG_TESTS) {
- System.out.println("Skipping lambda class wrapper method");
- }
- wrapper.enqueueCommandFirst(stepCommand);
- return true;
- }
if (isSyntheticMethod(mirror, location)) {
if (DEBUG_TESTS) {
System.out.println("Skipping synthetic method");
@@ -2160,11 +2152,6 @@
return false;
}
- private static boolean isInLambdaClass(VmMirror mirror, Location location) {
- String classSig = mirror.getClassSignature(location.classID);
- return classSig.contains("$$Lambda$");
- }
-
private static boolean isLambdaMethod(VmMirror mirror, Location location) {
String methodName = mirror.getMethodName(location.classID, location.methodID);
return methodName.startsWith("lambda$");
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
index 11dff7b..18913cc 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
@@ -22,7 +22,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.RetraceMethodResult;
+import com.android.tools.r8.retrace.RetraceFrameResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -103,7 +103,7 @@
MINIFIED_LINE_POSITION,
20,
FILENAME_INLINE));
- RetraceMethodResult retraceResult =
+ RetraceFrameResult retraceResult =
throwingSubject
.streamInstructions()
.filter(InstructionSubject::isThrow)
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
index ae533a4..8c2ef82 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.RetraceMethodResult;
+import com.android.tools.r8.retrace.RetraceFrameResult;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -93,7 +93,7 @@
INLINED_DEX_PC,
23,
FILENAME_MAIN));
- RetraceMethodResult retraceResult =
+ RetraceFrameResult retraceResult =
mainSubject
.streamInstructions()
.filter(InstructionSubject::isThrow)
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index d4346cc..b4a4c48 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestBase;
@@ -19,6 +20,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -69,7 +71,7 @@
.minification(minify)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoInternalSyntheticNames);
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics);
}
@Test
@@ -79,7 +81,7 @@
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoInternalSyntheticNames)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
.inspect(this::checkExpectedSynthetics);
}
@@ -133,7 +135,7 @@
.writeToZip(out3)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoInternalSyntheticNames)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
.inspect(inspector -> assertEquals(syntheticsInParts, getSyntheticMethods(inspector)));
// Finally do a non-intermediate merge.
@@ -142,7 +144,7 @@
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoInternalSyntheticNames)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
.inspect(
inspector -> {
if (intermediate) {
@@ -179,16 +181,28 @@
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoInternalSyntheticNames)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
.inspect(this::checkExpectedSynthetics);
}
- private void checkNoInternalSyntheticNames(CodeInspector inspector) {
+ private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) {
inspector.forAllClasses(
clazz -> {
assertThat(
clazz.getFinalName(),
not(containsString(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR)));
+ if (!clazz.getOriginalName().equals(MiniAssert.class.getTypeName())) {
+ clazz.forAllMethods(
+ method ->
+ assertTrue(
+ "Unexpected static invoke to java.lang method:\n"
+ + method.getMethod().codeToString(),
+ method
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeStatic)
+ .noneMatch(
+ i -> i.getMethod().qualifiedName().startsWith("java.lang"))));
+ }
});
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
index 42582e6..cf0d8ef 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
@@ -4,15 +4,10 @@
package com.android.tools.r8.desugar.desugaredlibrary;
-import static junit.framework.TestCase.assertTrue;
-
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -45,17 +40,12 @@
@Test
public void testCustomCollectionD8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(
- shrinkDesugaredLibrary
- && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
- && !parameters.getDexRuntimeVersion().isDefault());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(CustomCollectionForwardingTest.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
- .inspect(this::assertForwardingMethods)
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
@@ -85,53 +75,6 @@
StringUtils.lines("false", "false", "false", "false", "false", "false"));
}
- private void assertForwardingMethods(CodeInspector inspector) {
- if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
- return;
- }
- ClassSubject cal = inspector.clazz(CustomArrayList.class);
- MethodSubject spliteratorCal = cal.method("j$.util.Spliterator", "spliterator");
- assertTrue(spliteratorCal.isPresent());
- assertTrue(
- spliteratorCal
- .streamInstructions()
- .anyMatch(i -> i.isInvokeStatic() && i.toString().contains("List$-CC")));
- MethodSubject streamCal = cal.method("j$.util.stream.Stream", "stream");
- assertTrue(streamCal.isPresent());
- assertTrue(
- streamCal
- .streamInstructions()
- .anyMatch(i -> i.isInvokeStatic() && i.toString().contains("Collection$-CC")));
-
- ClassSubject clhs = inspector.clazz(CustomLinkedHashSet.class);
- MethodSubject spliteratorClhs = clhs.method("j$.util.Spliterator", "spliterator");
- assertTrue(spliteratorClhs.isPresent());
- assertTrue(
- spliteratorClhs
- .streamInstructions()
- .anyMatch(i -> i.isInvokeStatic() && i.toString().contains("DesugarLinkedHashSet")));
- MethodSubject streamClhs = clhs.method("j$.util.stream.Stream", "stream");
- assertTrue(streamClhs.isPresent());
- assertTrue(
- streamClhs
- .streamInstructions()
- .anyMatch(i -> i.isInvokeStatic() && i.toString().contains("Collection$-CC")));
-
- ClassSubject cl = inspector.clazz(CustomList.class);
- MethodSubject spliteratorCl = cl.method("j$.util.Spliterator", "spliterator");
- assertTrue(spliteratorCl.isPresent());
- assertTrue(
- spliteratorCl
- .streamInstructions()
- .anyMatch(i -> i.isInvokeStatic() && i.toString().contains("List$-CC")));
- MethodSubject streamCl = cl.method("j$.util.stream.Stream", "stream");
- assertTrue(streamCl.isPresent());
- assertTrue(
- streamCl
- .streamInstructions()
- .anyMatch(i -> i.isInvokeStatic() && i.toString().contains("Collection$-CC")));
- }
-
static class Executor {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
index ce07cbf..ac48a3d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
@@ -56,8 +56,6 @@
@Test
public void testCustomCollectionSuperCallsR8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
R8TestRunResult r8TestRunResult =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index 2330854..3df7246 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -24,7 +24,6 @@
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -60,21 +59,18 @@
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.assertNoMessages()
- .inspect(
- inspector -> {
- this.assertCustomCollectionCallsCorrect(inspector, false);
- })
+ .inspect(this::assertCustomCollectionCallsCorrect)
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
- .run(parameters.getRuntime(), EXECUTOR)
+ .run(parameters.getRuntime(), Executor.class)
.assertSuccess();
- assertResultCorrect(d8TestRunResult.getStdOut(), d8TestRunResult.getStdErr());
+ assertResultCorrect(d8TestRunResult.getStdOut());
}
- private void assertResultCorrect(String stdOut, String stdErr) {
+ private void assertResultCorrect(String stdOut) {
if (requiresEmulatedInterfaceCoreLibDesugaring(parameters) && !shrinkDesugaredLibrary) {
// When shrinking the class names are not printed correctly anymore due to minification.
// Expected output is emulated interfaces expected output.
@@ -92,29 +88,20 @@
.addKeepClassAndMembersRules(Executor.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
- .inspect(
- inspector -> {
- this.assertCustomCollectionCallsCorrect(inspector, true);
- })
+ .inspect(this::assertCustomCollectionCallsCorrect)
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
- .run(parameters.getRuntime(), EXECUTOR)
+ .run(parameters.getRuntime(), Executor.class)
.assertSuccess();
- assertResultCorrect(r8TestRunResult.getStdOut(), r8TestRunResult.getStdErr());
+ assertResultCorrect(r8TestRunResult.getStdOut());
}
- private void assertCustomCollectionCallsCorrect(CodeInspector inspector, boolean r8) {
+ private void assertCustomCollectionCallsCorrect(CodeInspector inspector) {
MethodSubject direct = inspector.clazz(EXECUTOR).uniqueMethodWithName("directTypes");
- // TODO(b/134732760): Due to memberRebinding, R8 is not as precise as D8 regarding
- // desugaring of invokes. This will be fixed when creation of desugared method is moved
- // ahead of R8 compilation pipeline.
- if (!r8) {
- Assert.assertFalse(
- direct.streamInstructions().anyMatch(instr -> instr.toString().contains("$-EL")));
- } else if (requiresEmulatedInterfaceCoreLibDesugaring(parameters)) {
+ if (requiresEmulatedInterfaceCoreLibDesugaring(parameters)) {
assertTrue(
direct
.streamInstructions()
@@ -140,22 +127,17 @@
instr ->
instr.toString().contains("$-EL")
|| instr.toString().contains("Comparator$-CC")));
- inherited.streamInstructions().forEach(x -> assertInheritedDispatchCorrect(x, r8));
+ inherited.streamInstructions().forEach(this::assertInheritedDispatchCorrect);
}
- private void assertInheritedDispatchCorrect(InstructionSubject instructionSubject, boolean r8) {
+ private void assertInheritedDispatchCorrect(InstructionSubject instructionSubject) {
if (!instructionSubject.isConstString(JumboStringMode.ALLOW)) {
- for (String s : new String[] {"stream", "parallelStream", "spliterator", "sort"}) {
+ for (String s : new String[] {">stream", "spliterator", "sort"}) {
if (instructionSubject.toString().contains(s)) {
- if (!r8 || instructionSubject.isInvokeStatic()) {
assertTrue(instructionSubject.isInvokeStatic());
assertTrue(
instructionSubject.toString().contains("$-EL")
|| instructionSubject.toString().contains("Comparator$-CC"));
- } else {
- // Has been devirtualized.
- assertTrue(instructionSubject.isInvokeVirtual());
- }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
index d53e0e6..7c46fe3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import static com.android.tools.r8.MarkerMatcher.assertMarkersMatch;
+import static com.android.tools.r8.MarkerMatcher.markerBackend;
import static com.android.tools.r8.MarkerMatcher.markerCompilationMode;
import static com.android.tools.r8.MarkerMatcher.markerHasDesugaredLibraryIdentifier;
import static com.android.tools.r8.MarkerMatcher.markerIsDesugared;
@@ -138,6 +139,7 @@
allOf(
markerTool(Tool.D8),
markerCompilationMode(CompilationMode.DEBUG),
+ markerBackend(Backend.CF),
markerIsDesugared(),
markerMinApi(parameters.getApiLevel()),
not(markerHasDesugaredLibraryIdentifier()));
@@ -152,6 +154,8 @@
.writeToZip();
// D8 dex file output marker has the same marker as the D8 class file output.
+ // TODO(b/166617364): They should not be the same after backend is recorded and neither has
+ // library desugaring info.
assertMarkersMatch(ExtractMarker.extractMarkerFromJarFile(desugaredLibDex), markerMatcher);
// Build an app using library desugaring merging with library not using library desugaring.
@@ -164,7 +168,18 @@
.compile()
.writeToZip();
- assertMarkersMatch(ExtractMarker.extractMarkerFromDexFile(app), markerMatcher);
+ // When there is no class-file resources we are adding the marker for the last compilation.
+ assertMarkersMatch(
+ ExtractMarker.extractMarkerFromDexFile(app),
+ ImmutableList.of(
+ markerMatcher,
+ allOf(
+ markerTool(Tool.D8),
+ markerCompilationMode(CompilationMode.DEBUG),
+ markerBackend(Backend.DEX),
+ markerIsDesugared(),
+ markerMinApi(parameters.getApiLevel()),
+ not(markerHasDesugaredLibraryIdentifier()))));
} catch (CompilationFailedException e) {
assertTrue(someLibraryDesugaringRequired());
return;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDesugaredLibraryDexFileTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDesugaredLibraryDexFileTest.java
new file mode 100644
index 0000000..a6ea994
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDesugaredLibraryDexFileTest.java
@@ -0,0 +1,200 @@
+// 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.desugar.desugaredlibrary;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Assume;
+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 NoDesugaredLibraryDexFileTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public NoDesugaredLibraryDexFileTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testCustomCollectionD8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(NoDesugaredLibraryDexFileTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspect(this::assertNoForwardingStreamMethod)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("1", "0");
+ assertTrue(keepRuleConsumer.get().isEmpty());
+ }
+
+ @Test
+ public void testCustomCollectionR8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(NoDesugaredLibraryDexFileTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRules(Executor.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspect(this::assertNoForwardingStreamMethod)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("1", "0");
+ assertTrue(keepRuleConsumer.get().isEmpty());
+ }
+
+ private void assertNoForwardingStreamMethod(CodeInspector inspector) {
+ assertTrue(inspector.clazz(CustomArrayList.class).uniqueMethodWithName("stream").isAbsent());
+ assertTrue(inspector.clazz(CustomSortedSet.class).uniqueMethodWithName("stream").isAbsent());
+ }
+
+ static class Executor {
+
+ // No method here is using any emulated interface default method, so, there is no need for
+ // the desugared library dex file despite desugared library being enabled.
+ public static void main(String[] args) {
+ ArrayList<Object> cArrayList = new CustomArrayList<>();
+ SortedSet<Object> cSortedSet = new CustomSortedSet<>();
+ cArrayList.add(1);
+ cSortedSet.add(1);
+ System.out.println(cArrayList.size());
+ System.out.println(cSortedSet.size());
+ }
+ }
+
+ // Extends directly a core library class which implements other library interfaces.
+ private static class CustomArrayList<E> extends ArrayList<E> {}
+
+ // Implements directly a core library interface which implements other library interfaces.
+ static class CustomSortedSet<E> implements SortedSet<E> {
+
+ @Nullable
+ @Override
+ public Comparator<? super E> comparator() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public SortedSet<E> subSet(E fromElement, E toElement) {
+ return new CustomSortedSet<>();
+ }
+
+ @NotNull
+ @Override
+ public SortedSet<E> headSet(E toElement) {
+ return new CustomSortedSet<>();
+ }
+
+ @NotNull
+ @Override
+ public SortedSet<E> tailSet(E fromElement) {
+ return new CustomSortedSet<>();
+ }
+
+ @Override
+ public E first() {
+ return null;
+ }
+
+ @Override
+ public E last() {
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return false;
+ }
+
+ @NotNull
+ @Override
+ public Iterator<E> iterator() {
+ return Collections.emptyIterator();
+ }
+
+ @NotNull
+ @Override
+ public Object[] toArray() {
+ return new Object[0];
+ }
+
+ @NotNull
+ @Override
+ public <T> T[] toArray(@NotNull T[] a) {
+ return a;
+ }
+
+ @Override
+ public boolean add(Object o) {
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return false;
+ }
+
+ @Override
+ public boolean addAll(@NotNull Collection c) {
+ return false;
+ }
+
+ @Override
+ public void clear() {}
+
+ @Override
+ public boolean removeAll(@NotNull Collection c) {
+ return false;
+ }
+
+ @Override
+ public boolean retainAll(@NotNull Collection c) {
+ return false;
+ }
+
+ @Override
+ public boolean containsAll(@NotNull Collection c) {
+ return false;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoForwardingMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoForwardingMethodsTest.java
new file mode 100644
index 0000000..98ad7dc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoForwardingMethodsTest.java
@@ -0,0 +1,206 @@
+// 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.desugar.desugaredlibrary;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.SortedSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+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 NoForwardingMethodsTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public NoForwardingMethodsTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testCustomCollectionD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(NoForwardingMethodsTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspect(this::assertNoForwardingStreamMethod)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("str:1", "0");
+ }
+
+ @Test
+ public void testCustomCollectionR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(NoForwardingMethodsTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRules(Executor.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspect(this::assertNoForwardingStreamMethod)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("str:1", "0");
+ }
+
+ private void assertNoForwardingStreamMethod(CodeInspector inspector) {
+ assertTrue(inspector.clazz(CustomArrayList.class).uniqueMethodWithName("stream").isAbsent());
+ assertTrue(inspector.clazz(CustomSortedSet.class).uniqueMethodWithName("stream").isAbsent());
+ }
+
+ static class Executor {
+
+ // The main method is using stream, but since there are no overrides, the classes should not
+ // have any forwarding method.
+ public static void main(String[] args) {
+ ArrayList<Object> cArrayList = new CustomArrayList<>();
+ SortedSet<Object> cSortedSet = new CustomSortedSet<>();
+ cArrayList.add(1);
+ cSortedSet.add(1);
+ System.out.println(cArrayList.stream().map(i -> "str:" + i).toArray()[0]);
+ System.out.println(cSortedSet.stream().filter(Objects::isNull).count());
+ }
+ }
+
+ // Extends directly a core library class which implements other library interfaces.
+ private static class CustomArrayList<E> extends ArrayList<E> {}
+
+ // Implements directly a core library interface which implements other library interfaces.
+ static class CustomSortedSet<E> implements SortedSet<E> {
+
+ @Nullable
+ @Override
+ public Comparator<? super E> comparator() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public SortedSet<E> subSet(E fromElement, E toElement) {
+ return new CustomSortedSet<>();
+ }
+
+ @NotNull
+ @Override
+ public SortedSet<E> headSet(E toElement) {
+ return new CustomSortedSet<>();
+ }
+
+ @NotNull
+ @Override
+ public SortedSet<E> tailSet(E fromElement) {
+ return new CustomSortedSet<>();
+ }
+
+ @Override
+ public E first() {
+ return null;
+ }
+
+ @Override
+ public E last() {
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return false;
+ }
+
+ @NotNull
+ @Override
+ public Iterator<E> iterator() {
+ return Collections.emptyIterator();
+ }
+
+ @NotNull
+ @Override
+ public Object[] toArray() {
+ return new Object[0];
+ }
+
+ @NotNull
+ @Override
+ public <T> T[] toArray(@NotNull T[] a) {
+ return a;
+ }
+
+ @Override
+ public boolean add(Object o) {
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return false;
+ }
+
+ @Override
+ public boolean addAll(@NotNull Collection c) {
+ return false;
+ }
+
+ @Override
+ public void clear() {}
+
+ @Override
+ public boolean removeAll(@NotNull Collection c) {
+ return false;
+ }
+
+ @Override
+ public boolean retainAll(@NotNull Collection c) {
+ return false;
+ }
+
+ @Override
+ public boolean containsAll(@NotNull Collection c) {
+ return false;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 3268a35..9d9b5cb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -181,8 +181,6 @@
@Test
public void testStream() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(
- shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeFalse(
"getAllFilesWithSuffixInDirectory() seems to find different files on Windows",
ToolHelper.isWindows());
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index cbd5710..a04b0c1 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -97,7 +97,6 @@
@Test
public void testHello() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Assume.assumeTrue(!ToolHelper.isWindows());
Path prevGeneratedJar = null;
String prevRunResult = null;
@@ -131,7 +130,6 @@
@Test
public void testR8() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Assume.assumeTrue(!ToolHelper.isWindows());
Assume.assumeTrue(parameters.isCfRuntime());
Assume.assumeTrue(CfVm.JDK11 == parameters.getRuntime().asCf().getVm());
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index e1d892d..762916e 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -47,7 +47,6 @@
@Test
public void testR8CompiledWithR8() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
index 25a3434..0f313cf 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.List;
import java.util.Objects;
import org.junit.Test;
@@ -51,13 +50,10 @@
.inspectDiagnosticMessages(
m -> {
assertEnumIsUnboxed(MyEnum.class, MyEnum.class.getSimpleName(), m);
- // MyEnum19 is unboxed only if minAPI > 19 because Objects#requiredNonNull is then
- // present.
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K)) {
- assertEnumIsUnboxed(MyEnum19.class, MyEnum19.class.getSimpleName(), m);
- } else {
- assertEnumIsBoxed(MyEnum19.class, MyEnum19.class.getSimpleName(), m);
- }
+ // MyEnum19 is always unboxed. If minAPI > 19 the unboxer will identify
+ // Objects#requiredNonNull usage. For 19 and prior, the backport code should not
+ // prohibit the unboxing either.
+ assertEnumIsUnboxed(MyEnum19.class, MyEnum19.class.getSimpleName(), m);
})
.run(parameters.getRuntime(), MainNullTest.class)
.assertSuccess();
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/UnboundedFormalTypeGenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/UnboundedFormalTypeGenericSignatureTest.java
new file mode 100644
index 0000000..58812ee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/UnboundedFormalTypeGenericSignatureTest.java
@@ -0,0 +1,169 @@
+// 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.graph.genericsignature;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+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.TestRunResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.lang.reflect.Method;
+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 UnboundedFormalTypeGenericSignatureTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String SUPER_BINARY_NAME =
+ DescriptorUtils.getBinaryNameFromJavaType(Super.class.getTypeName());
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public UnboundedFormalTypeGenericSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(Main.class).removeInnerClasses().transform(),
+ transformer(Super.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(Super.class.getTypeName() + "<T>", "R", "T");
+ }
+
+ @Test
+ public void testUnboundParametersInClassRuntime() throws Exception {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature("L" + SUPER_BINARY_NAME + "<TR;>;")
+ .transform(),
+ transformer(Super.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isCfRuntime()) {
+ runResult.assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ } else {
+ runResult.assertSuccessWithOutputLines(Super.class.getTypeName() + "<R>", "R", "T");
+ }
+ }
+
+ @Test
+ public void testUnboundParametersInMethodRuntime() throws Exception {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature(
+ MethodPredicate.onName("testStatic"), "<R:Ljava/lang/Object;>()TS;")
+ .setGenericSignature(
+ MethodPredicate.onName("testVirtual"), "<R:Ljava/lang/Object;>()TQ;")
+ .transform(),
+ transformer(Super.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isCfRuntime()) {
+ runResult.assertSuccessWithOutputLines(Super.class.getTypeName() + "<T>", "null", "null");
+ } else {
+ runResult.assertSuccessWithOutputLines(Super.class.getTypeName() + "<T>", "S", "Q");
+ }
+ }
+
+ @Test
+ public void testUnboundParametersInClassR8() throws Exception {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature("L" + SUPER_BINARY_NAME + "<TR;>;")
+ .transform(),
+ transformer(Super.class).removeInnerClasses().transform())
+ .addKeepAllClassesRule()
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isCfRuntime()) {
+ runResult.assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ } else {
+ runResult.assertSuccessWithOutputLines(Super.class.getTypeName() + "<R>", "R", "T");
+ }
+ }
+
+ @Test
+ public void testUnboundParametersInMethodR8() throws Exception {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature(
+ MethodPredicate.onName("testStatic"), "<R:Ljava/lang/Object;>()TS;")
+ .setGenericSignature(
+ MethodPredicate.onName("testVirtual"), "<R:Ljava/lang/Object;>()TQ;")
+ .transform(),
+ transformer(Super.class).removeInnerClasses().transform())
+ .addKeepAllClassesRule()
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isCfRuntime()) {
+ runResult.assertSuccessWithOutputLines(Super.class.getTypeName() + "<T>", "null", "null");
+ } else {
+ runResult.assertSuccessWithOutputLines(Super.class.getTypeName() + "<T>", "S", "Q");
+ }
+ }
+
+ public static class Super<T> {}
+
+ public static class Main<T> extends Super<T> {
+
+ public static <R extends Super<R>> void main(String[] args) throws NoSuchMethodException {
+ System.out.println(Main.class.getGenericSuperclass());
+ testStatic();
+ new Main<>().testVirtual();
+ }
+
+ private static <R> R testStatic() {
+ try {
+ Method testStatic = Main.class.getDeclaredMethod("testStatic");
+ System.out.println(testStatic.getGenericReturnType());
+ return null;
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private T testVirtual() {
+ try {
+ Method testVirtual = Main.class.getDeclaredMethod("testVirtual");
+ System.out.println(testVirtual.getGenericReturnType());
+ return null;
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/UnknownClassInSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/UnknownClassInSignatureTest.java
new file mode 100644
index 0000000..4c2ac5e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/UnknownClassInSignatureTest.java
@@ -0,0 +1,83 @@
+// 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.graph.genericsignature;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.transformers.ClassFileTransformer.FieldPredicate;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+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 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 UnknownClassInSignatureTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String NEW_CLASS_SIGNATURE = "<T:LUnknownClass1;>LUnknownClass2<LUnknownClass3;>;";
+ private final String NEW_FIELD_SIGNATURE = "LUnknownClass4<LUnknownClass4;>;";
+ private final String NEW_METHOD_SIGNATURE = "()LUnkownClass5<LunknownPackage/UnknownClass6;>;";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public UnknownClassInSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature(NEW_CLASS_SIGNATURE)
+ .setGenericSignature(FieldPredicate.onName("field"), NEW_FIELD_SIGNATURE)
+ .setGenericSignature(MethodPredicate.onName("main"), NEW_METHOD_SIGNATURE)
+ .transform())
+ .addKeepAllClassesRule()
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.ENCLOSING_METHOD,
+ ProguardKeepAttributes.INNER_CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World!")
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(Main.class);
+ assertThat(clazz, isPresent());
+ assertEquals(NEW_CLASS_SIGNATURE, clazz.getFinalSignatureAttribute());
+ FieldSubject field = clazz.uniqueFieldWithFinalName("field");
+ assertThat(field, isPresent());
+ assertEquals(NEW_FIELD_SIGNATURE, field.getFinalSignatureAttribute());
+ MethodSubject method = clazz.uniqueMethodWithFinalName("main");
+ assertThat(method, isPresent());
+ assertEquals(NEW_METHOD_SIGNATURE, method.getFinalSignatureAttribute());
+ });
+ }
+
+ public static class Main {
+
+ private List<Main> field;
+
+ public static void main(String[] args) {
+ System.out.println("Hello World!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/fields/NonFinalFinalFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/fields/NonFinalFinalFieldTest.java
new file mode 100644
index 0000000..292f74e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/fields/NonFinalFinalFieldTest.java
@@ -0,0 +1,170 @@
+// 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.optimize.fields;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.transformers.MethodTransformer;
+import com.google.common.collect.ImmutableList;
+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 NonFinalFinalFieldTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NonFinalFinalFieldTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(TestClass.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("2", "2", "2");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("2", "2", "2");
+ }
+
+ private List<byte[]> getProgramClassFileData() throws Exception {
+ return ImmutableList.of(
+ transformer(A.class)
+ .setAccessFlags(A.class.getDeclaredField("f"), AccessFlags::setFinal)
+ .transform(),
+ transformer(B.class)
+ .setAccessFlags(B.class.getDeclaredField("f"), AccessFlags::setFinal)
+ .addMethodTransformer(createRemoveObjectInitMethodTransformer())
+ .transform(),
+ transformer(C.class)
+ .setAccessFlags(C.class.getDeclaredField("f"), AccessFlags::setFinal)
+ .addMethodTransformer(createRemoveObjectInitMethodTransformer())
+ .transform());
+ }
+
+ private MethodTransformer createRemoveObjectInitMethodTransformer() {
+ return new MethodTransformer() {
+
+ private boolean seenInit = false;
+
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ if (isDefaultConstructor()) {
+ if (name.equals("<init>")) {
+ seenInit = true;
+ return;
+ }
+ super.visitMethodInsn(
+ opcode, owner, name.equals("init") ? "<init>" : name, descriptor, isInterface);
+ } else {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ if (isDefaultConstructor()) {
+ if (seenInit) {
+ super.visitVarInsn(opcode, var);
+ }
+ } else {
+ super.visitVarInsn(opcode, var);
+ }
+ }
+
+ private boolean isDefaultConstructor() {
+ return getContext().getReference().getMethodName().equals("<init>")
+ && getContext().getReference().getFormalTypes().isEmpty();
+ }
+ };
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(new A().f);
+ System.out.println(new B().f);
+ System.out.println(new C().f);
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ /*final*/ int f;
+
+ A() {
+ this(1);
+ this.f = 2;
+ }
+
+ A(int f) {
+ this.f = f;
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ /*final*/ int f;
+
+ B() {
+ this.f = 1;
+ init(2); // Rewritten to B(2)
+ }
+
+ B(int f) {
+ this.f = f;
+ }
+
+ private void init(int f) {}
+ }
+
+ @NeverClassInline
+ static class C {
+
+ /*final*/ int f;
+
+ C() {
+ this.f = 1;
+ init(2, true); // Rewritten to B(2, true)
+ }
+
+ C(int f, boolean b) {
+ if (b) {
+ this.f = f;
+ }
+ }
+
+ private void init(int f, boolean b) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest.java
new file mode 100644
index 0000000..95fef57
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest.java
@@ -0,0 +1,64 @@
+// 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.optimize.inliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Arrays;
+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 InlineMethodWithRetargetedLibMemberTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public InlineMethodWithRetargetedLibMemberTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/171197204): Method should be inlined.
+ assertThat(
+ inspector.clazz(TestClass.class).uniqueMethodWithName("test"),
+ notIf(
+ isPresent(),
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)));
+ });
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ test(args);
+ }
+
+ static void test(String[] args) {
+ Arrays.stream(args);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index a684ea3..2012c6f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.AlwaysInline;
import com.android.tools.r8.AssumeMayHaveSideEffects;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -41,7 +42,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(SingleTargetAfterInliningTest.class)
.addKeepMainRule(TestClass.class)
@@ -52,6 +52,7 @@
})
.enableAlwaysInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableSideEffectAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -118,6 +119,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class C extends A {
@AssumeMayHaveSideEffects // To ensure that new C() cannot be removed.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 406e16d..1452fd4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ForceInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
@@ -33,6 +34,7 @@
static class Sub extends Base {}
+ @NoHorizontalClassMerging
static class EffectivelyFinal {}
static class Reflection implements Callable<Class<?>> {
@@ -208,7 +210,6 @@
@Test
public void testR8() throws Exception {
boolean isRelease = mode == CompilationMode.RELEASE;
- expectThrowsWithHorizontalClassMergingIf(isRelease);
boolean expectCallPresent = !isRelease;
int expectedGetClassCount = isRelease ? 0 : 5;
int expectedConstClassCount = isRelease ? (parameters.isCfRuntime() ? 8 : 6) : 1;
@@ -216,6 +217,7 @@
.setMode(mode)
.addInnerClasses(GetClassTest.class)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.addKeepMainRule(MAIN)
.noMinification()
.addOptionsModification(this::configure)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java
index 27b1a0c..86e30dc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java
@@ -36,7 +36,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public SwitchCaseRemovalTest(TestParameters parameters) {
@@ -59,7 +59,7 @@
})
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyOutput)
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index 179333f..2cbef3e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -36,7 +36,6 @@
@Test
public void testCompanionAndRegularObjects() throws Exception {
- expectThrowsWithHorizontalClassMerging();
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_staticizer.MainKt";
@@ -47,7 +46,6 @@
false,
app -> {
CodeInspector inspector = new CodeInspector(app);
- assertThat(inspector.clazz("class_staticizer.Regular$Companion"), isPresent());
assertThat(inspector.clazz("class_staticizer.Derived$Companion"), isPresent());
// The Util class is there, but its instance methods have been inlined.
@@ -67,7 +65,6 @@
app -> {
CodeInspector inspector = new CodeInspector(app);
assertThat(inspector.clazz("class_staticizer.Regular$Companion"), not(isPresent()));
- assertThat(inspector.clazz("class_staticizer.Derived$Companion"), not(isPresent()));
ClassSubject utilClass = inspector.clazz("class_staticizer.Util");
assertThat(utilClass, isPresent());
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index 84ee93d..446d982 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -35,7 +35,6 @@
@Test
public void testJStyleRunnable() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_jstyle_runnable.MainKt";
runTest("lambdas_jstyle_runnable", mainClassName, optionsModifier, null);
}
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
deleted file mode 100644
index 59cfbe8..0000000
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (c) 2018, 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.regress;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.function.Function;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class B76025099 extends TestBase {
-
- private Backend backend;
-
- @Parameterized.Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public B76025099(Backend backend) {
- this.backend = backend;
- }
-
- private static final String PRG =
- ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
-
- private AndroidApp runR8(AndroidApp app) throws Exception {
- R8Command command =
- ToolHelper.addProguardConfigurationConsumer(
- ToolHelper.prepareR8CommandBuilder(app, emptyConsumer(backend)),
- pgConfig -> {
- pgConfig.setPrintMapping(true);
- pgConfig.setPrintMappingFile(map);
- })
- .addLibraryFiles(runtimeJar(backend))
- .addProguardConfigurationFiles(pgConfig)
- .setDisableMinification(true)
- .setOutput(tempRoot.toPath(), outputMode(backend))
- .build();
- return ToolHelper.runR8(command);
- }
-
- private File tempRoot;
- private Path jarPath;
- private AndroidApp originalApp;
- private String mainName;
- private Path pgConfig;
- private Path map;
-
- @Before
- public void setUp() throws Exception {
- tempRoot = temp.getRoot();
- jarPath = Paths.get(PRG);
- originalApp = readJar(jarPath);
- mainName = "regress_76025099.Main";
- pgConfig = File.createTempFile("keep-rules", ".config", tempRoot).toPath();
- String config = keepMainProguardConfiguration(mainName);
- config += System.lineSeparator() + "-dontobfuscate";
- Files.write(pgConfig, config.getBytes());
- map = File.createTempFile("proguard", ".map", tempRoot).toPath();
- }
-
- @Test
- public void testProguardAndD8() throws Exception {
- Assume.assumeTrue(backend == Backend.DEX);
- if (!isRunProguard()) {
- return;
- }
-
- ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
- assertEquals(0, jvmOutput.exitCode);
-
- Path proguarded =
- File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, tempRoot).toPath();
- ProcessResult proguardResult = ToolHelper.runProguardRaw(jarPath, proguarded, pgConfig, map);
- assertEquals(0, proguardResult.exitCode);
-
- AndroidApp processedApp = ToolHelper.runD8(readJar(proguarded));
- verifyFieldAccess(processedApp, jvmOutput);
- }
-
- @Test
- public void testR8() throws Exception {
- ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
- assertEquals(0, jvmOutput.exitCode);
-
- AndroidApp processedApp = runR8(originalApp);
- verifyFieldAccess(processedApp, jvmOutput);
- }
-
- private static InstructionSubject findInstructionOrNull(
- Iterator<InstructionSubject> iterator, Function<InstructionSubject, Boolean> predicate) {
- while (iterator.hasNext()) {
- InstructionSubject instruction = iterator.next();
- if (predicate.apply(instruction)) {
- return instruction;
- }
- }
- return null;
- }
-
- private void verifyFieldAccess(AndroidApp processedApp, ProcessResult jvmOutput)
- throws Exception {
- CodeInspector inspector = new CodeInspector(processedApp);
- ClassSubject impl = inspector.clazz("regress_76025099.impl.Impl");
- assertThat(impl, isPresent());
- MethodSubject init = impl.init("java.lang.String");
- assertThat(init, isPresent());
- Iterator<InstructionSubject> iterator = init.iterateInstructions();
-
- assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isInvoke));
-
- InstructionSubject instruction =
- findInstructionOrNull(iterator, InstructionSubject::isInstancePut);
- assertNotNull(instruction);
- FieldAccessInstructionSubject fieldAccessInstruction =
- (FieldAccessInstructionSubject) instruction;
- assertEquals("name", fieldAccessInstruction.name());
- assertTrue(fieldAccessInstruction.holder().is(impl.getDexProgramClass().type.toString()));
-
- assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isReturnVoid));
-
- ProcessResult output;
- if (backend == Backend.DEX) {
- output = runOnArtRaw(processedApp, mainName);
- } else {
- assert backend == Backend.CF;
- output = runOnJavaRaw(processedApp, mainName, Collections.emptyList());
- }
- assertEquals(0, output.exitCode);
- assertEquals(jvmOutput.stdout, output.stdout);
- }
-
-}
diff --git a/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
index 0d2f1d5..541ca47 100644
--- a/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.transformers.MethodTransformer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
@@ -63,7 +64,7 @@
String oldLambdaName = "lambda$identity$0";
String newLambdaName = "lambda$identity$foo";
return transformer(Function.class)
- .renameMethod(oldLambdaName, newLambdaName)
+ .renameMethod(MethodPredicate.onName(oldLambdaName), newLambdaName)
.addMethodTransformer(
new MethodTransformer() {
@Override
diff --git a/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java b/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java
new file mode 100644
index 0000000..e179440
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2018, 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.regress.b76025099;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ProguardTestCompileResult;
+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.regress.b76025099.testclasses.Main;
+import com.android.tools.r8.regress.b76025099.testclasses.impl.Impl;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Iterator;
+import java.util.function.Function;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B76025099 extends TestBase {
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines(Main.class.getTypeName());
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "Backend: {0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public B76025099(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addTestClasspath()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testProguardAndD8() throws Exception {
+ assumeTrue(isRunProguard());
+
+ ProguardTestCompileResult proguardCompileResult =
+ testForProguard()
+ .addProgramFiles(ToolHelper.getClassFilesForTestPackage(Main.class.getPackage()))
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .compile();
+
+ if (parameters.isDexRuntime()) {
+ testForD8()
+ .addProgramFiles(proguardCompileResult.outputJar())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::verifyFieldAccess)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ proguardCompileResult
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(ToolHelper.getClassFilesForTestPackage(Main.class.getPackage()))
+ .addKeepMainRule(Main.class)
+ .enableNoVerticalClassMergingAnnotations()
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::verifyFieldAccess)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private static InstructionSubject findInstructionOrNull(
+ Iterator<InstructionSubject> iterator, Function<InstructionSubject, Boolean> predicate) {
+ while (iterator.hasNext()) {
+ InstructionSubject instruction = iterator.next();
+ if (predicate.apply(instruction)) {
+ return instruction;
+ }
+ }
+ return null;
+ }
+
+ private void verifyFieldAccess(CodeInspector inspector) {
+ ClassSubject impl = inspector.clazz(Impl.class);
+ assertThat(impl, isPresent());
+ MethodSubject init = impl.init("java.lang.String");
+ assertThat(init, isPresent());
+ Iterator<InstructionSubject> iterator = init.iterateInstructions();
+
+ assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isInvoke));
+
+ InstructionSubject instruction =
+ findInstructionOrNull(iterator, InstructionSubject::isInstancePut);
+ assertNotNull(instruction);
+ FieldAccessInstructionSubject fieldAccessInstruction =
+ (FieldAccessInstructionSubject) instruction;
+ assertEquals("name", fieldAccessInstruction.name());
+ assertTrue(fieldAccessInstruction.holder().is(impl.getDexProgramClass().type.toString()));
+
+ assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isReturnVoid));
+ }
+}
diff --git a/src/test/examples/regress_76025099/Logger.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Logger.java
similarity index 81%
rename from src/test/examples/regress_76025099/Logger.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Logger.java
index d6e9f95..b0178e0 100644
--- a/src/test/examples/regress_76025099/Logger.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Logger.java
@@ -1,7 +1,7 @@
// Copyright (c) 2018, 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 regress_76025099;
+package com.android.tools.r8.regress.b76025099.testclasses;
public interface Logger {
String getName();
diff --git a/src/test/examples/regress_76025099/Main.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Main.java
similarity index 74%
rename from src/test/examples/regress_76025099/Main.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Main.java
index 659bdbd..3d49201 100644
--- a/src/test/examples/regress_76025099/Main.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Main.java
@@ -1,9 +1,9 @@
// Copyright (c) 2018, 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 regress_76025099;
+package com.android.tools.r8.regress.b76025099.testclasses;
-import regress_76025099.impl.Factory;
+import com.android.tools.r8.regress.b76025099.testclasses.impl.Factory;
public class Main {
public static void main(String[] args) {
diff --git a/src/test/examples/regress_76025099/helper/AbstractBase.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractBase.java
similarity index 62%
rename from src/test/examples/regress_76025099/helper/AbstractBase.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractBase.java
index 046e12f..9d5694c 100644
--- a/src/test/examples/regress_76025099/helper/AbstractBase.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractBase.java
@@ -1,10 +1,12 @@
// Copyright (c) 2018, 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 regress_76025099.helper;
+package com.android.tools.r8.regress.b76025099.testclasses.helper;
-import regress_76025099.Logger;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.regress.b76025099.testclasses.Logger;
+@NoVerticalClassMerging
abstract class AbstractBase implements Logger {
protected String name;
diff --git a/src/test/examples/regress_76025099/helper/AbstractSub.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractSub.java
similarity index 63%
rename from src/test/examples/regress_76025099/helper/AbstractSub.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractSub.java
index 9240e4a..0dd1af7 100644
--- a/src/test/examples/regress_76025099/helper/AbstractSub.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractSub.java
@@ -1,7 +1,6 @@
// Copyright (c) 2018, 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 regress_76025099.helper;
+package com.android.tools.r8.regress.b76025099.testclasses.helper;
-public abstract class AbstractSub extends AbstractBase {
-}
+public abstract class AbstractSub extends AbstractBase {}
diff --git a/src/test/examples/regress_76025099/impl/Factory.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Factory.java
similarity index 82%
rename from src/test/examples/regress_76025099/impl/Factory.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Factory.java
index 0cc1cc9..fd98f42 100644
--- a/src/test/examples/regress_76025099/impl/Factory.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Factory.java
@@ -1,7 +1,7 @@
// Copyright (c) 2018, 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 regress_76025099.impl;
+package com.android.tools.r8.regress.b76025099.testclasses.impl;
public class Factory {
public static Impl getImpl(String name) {
diff --git a/src/test/examples/regress_76025099/impl/Impl.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Impl.java
similarity index 67%
rename from src/test/examples/regress_76025099/impl/Impl.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Impl.java
index 74eefcf..b24bde9 100644
--- a/src/test/examples/regress_76025099/impl/Impl.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Impl.java
@@ -1,12 +1,12 @@
// Copyright (c) 2018, 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 regress_76025099.impl;
+package com.android.tools.r8.regress.b76025099.testclasses.impl;
-import regress_76025099.helper.AbstractSub;
+import com.android.tools.r8.regress.b76025099.testclasses.helper.AbstractSub;
public class Impl extends AbstractSub {
Impl(String name) {
this.name = name;
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
index 0afee04..c1f2301 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -86,7 +86,6 @@
@Test
public void testR8() throws IOException, CompilationFailedException, ExecutionException {
- expectThrowsWithHorizontalClassMergingIf(parameters.isCfRuntime());
testForR8(parameters.getBackend())
.addInnerClasses(TargetInDefaultMethodTest.class)
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 1fa811a..0beee59 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -120,7 +120,7 @@
MethodSubject mainSubject,
LinePosition inlineStack) {
assertThat(mainSubject, isPresent());
- RetraceMethodResult retraceResult =
+ RetraceFrameResult retraceResult =
mainSubject
.streamInstructions()
.filter(InstructionSubject::isThrow)
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index 05b028a..3b0538d 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -225,7 +225,7 @@
MethodSubject mainSubject,
LinePosition inlineStack) {
assertThat(mainSubject, isPresent());
- RetraceMethodResult retraceResult =
+ RetraceFrameResult retraceResult =
mainSubject
.streamInstructions()
.filter(InstructionSubject::isThrow)
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index 85e6cbc..2074afa 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -7,6 +7,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper;
@@ -176,6 +177,12 @@
assertTrue(processResult.stdout.startsWith(WAITING_MESSAGE));
}
+ @Test
+ public void testHelpMessageWithQuiet() throws IOException {
+ ProcessResult processResult = runRetrace("", "", true, "--quiet");
+ assertFalse(processResult.stdout.startsWith(WAITING_MESSAGE));
+ }
+
private final String nonMappableStackTrace =
StringUtils.lines(
"com.android.r8.R8Exception: Problem when compiling program",
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index bffcd14..1b6735f 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -10,6 +10,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -43,6 +44,7 @@
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -107,16 +109,7 @@
@Test
public void testInvalidStackTraceLineWarnings() {
InvalidStackTrace invalidStackTraceTest = new InvalidStackTrace();
- TestDiagnosticMessagesImpl diagnosticsHandler = runRetraceTest(invalidStackTraceTest);
- if (!useRegExpParsing) {
- diagnosticsHandler.assertOnlyWarnings();
- diagnosticsHandler.assertWarningsCount(invalidStackTraceTest.expectedWarnings());
- assertThat(
- diagnosticsHandler.getWarnings().get(0).getDiagnosticMessage(),
- containsString(". . . 7 more"));
- } else {
- diagnosticsHandler.assertNoMessages();
- }
+ runRetraceTest(invalidStackTraceTest).assertNoMessages();
}
@Test
@@ -136,11 +129,15 @@
@Test
public void testAmbiguousStackTrace() {
+ // TODO(b/170797525): Remove when we have a fixed ordering.
+ assumeTrue(useRegExpParsing);
runRetraceTest(new AmbiguousStackTrace());
}
@Test
public void testAmbiguousMissingLineStackTrace() {
+ // TODO(b/170797525): Remove when we have a fixed ordering.
+ assumeTrue(useRegExpParsing);
runRetraceTest(new AmbiguousMissingLineStackTrace());
}
@@ -178,6 +175,7 @@
}
@Test
+ @Ignore("b/170293908")
public void testBootLoaderAndNamedModulesStackTrace() {
assumeFalse(useRegExpParsing);
runRetraceTest(new NamedModuleStackTrace());
@@ -185,6 +183,8 @@
@Test
public void testUnknownSourceStackTrace() {
+ // TODO(b/170797525): Remove when we have a fixed ordering.
+ assumeTrue(useRegExpParsing);
runRetraceTest(new UnknownSourceStackTrace());
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
index fb65c1d..3e5b4ca 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -57,6 +58,8 @@
}
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
+ // TODO(b/170293906): Remove assumption.
+ assumeTrue(useRegExpParsing);
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java
index a36feae..c86411e 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java
@@ -56,6 +56,6 @@
@Override
public int expectedWarnings() {
- return 1;
+ return 0;
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTrace.java
index 2c057d3..0e38636 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTrace.java
@@ -68,6 +68,6 @@
@Override
public int expectedWarnings() {
- return 1;
+ return 0;
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
index 816c073..4f65b48 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
@@ -3,12 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.assumenosideeffects;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.util.Collection;
import org.junit.Test;
@@ -19,25 +21,6 @@
public class AssumenosideeffectsPropagationTest extends TestBase {
private static final Class<?> MAIN = SubsUser.class;
- private static final String JVM_OUTPUT = StringUtils.lines(
- "[Sub1, info]: message00",
- "[Base1, debug]: message00",
- "[Sub1, verbose]: message00",
- "[Sub1, info]: message1",
- "[Base1, debug]: message2",
- "[Sub1, verbose]: message3",
- "[Base2, info]: message08",
- "[AnotherSub2, debug]: message08",
- "[AnotherSub2, verbose]: message08",
- "[Base2, info]: message4",
- "[AnotherSub2, debug]: message5",
- "[AnotherSub2, verbose]: message6",
- "The end"
- );
- private static final String OUTPUT_WITHOUT_MESSAGES = StringUtils.lines(
- "The end"
- );
-
enum TestConfig {
SPECIFIC_RULES,
NON_SPECIFIC_RULES_PARTIAL,
@@ -84,33 +67,40 @@
}
}
- public String expectedOutput() {
+ public String expectedOutput(boolean enableHorizontalClassMerging) {
switch (this) {
case SPECIFIC_RULES:
- return StringUtils.lines(
- // Itf#info has side effects due to the missing Base2.
- "[Sub1, info]: message00",
- "[Base2, info]: message08",
- // Base2#info also has side effects.
- "[Base2, info]: message4",
- "The end"
- );
+ return enableHorizontalClassMerging
+ ? StringUtils.lines(
+ "[Base2, info]: message08",
+ // Base2#info also has side effects.
+ "[Base2, info]: message4",
+ "The end")
+ : StringUtils.lines(
+ // Itf#info has side effects due to the missing Base2.
+ "[Sub1, info]: message00",
+ "[Base2, info]: message08",
+ // Base2#info also has side effects.
+ "[Base2, info]: message4",
+ "The end");
case NON_SPECIFIC_RULES_PARTIAL:
- return StringUtils.lines(
- // TODO(b/133208961): Introduce comparison/meet of assume rules.
- // Itf has side effects for all methods, since we don't compute the meet yet.
- "[Sub1, info]: message00",
- "[Base1, debug]: message00",
- "[Sub1, verbose]: message00",
- "[Base2, info]: message08",
- "[AnotherSub2, debug]: message08",
- "[AnotherSub2, verbose]: message08",
- // Base2#debug also has side effects.
- "[AnotherSub2, debug]: message5",
- "The end"
- );
+ return enableHorizontalClassMerging
+ ? StringUtils.lines(
+ "[AnotherSub2, debug]: message08", "[AnotherSub2, debug]: message5", "The end")
+ : StringUtils.lines(
+ // TODO(b/133208961): Introduce comparison/meet of assume rules.
+ // Itf has side effects for all methods, since we don't compute the meet yet.
+ "[Sub1, info]: message00",
+ "[Base1, debug]: message00",
+ "[Sub1, verbose]: message00",
+ "[Base2, info]: message08",
+ "[AnotherSub2, debug]: message08",
+ "[AnotherSub2, verbose]: message08",
+ // Base2#debug also has side effects.
+ "[AnotherSub2, debug]: message5",
+ "The end");
case NON_SPECIFIC_RULES_ALL:
- return OUTPUT_WITHOUT_MESSAGES;
+ return StringUtils.lines("The end");
default:
throw new Unreachable();
}
@@ -119,38 +109,60 @@
private final TestParameters parameters;
private final TestConfig config;
+ private final boolean enableHorizontalClassMerging;
@Parameterized.Parameters(name = "{0} {1}")
public static Collection<Object[]> data() {
- return buildParameters(getTestParameters().withAllRuntimes().build(), TestConfig.values());
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ TestConfig.values(),
+ BooleanUtils.values());
}
- public AssumenosideeffectsPropagationTest(TestParameters parameters, TestConfig config) {
+ public AssumenosideeffectsPropagationTest(
+ TestParameters parameters, TestConfig config, boolean enableHorizontalClassMerging) {
this.parameters = parameters;
this.config = config;
+ this.enableHorizontalClassMerging = enableHorizontalClassMerging;
}
@Test
public void testJVMOutput() throws Exception {
- assumeTrue(parameters.isCfRuntime() && config == TestConfig.SPECIFIC_RULES);
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(config == TestConfig.SPECIFIC_RULES);
+ assumeFalse(enableHorizontalClassMerging);
testForJvm()
.addTestClasspath()
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JVM_OUTPUT);
+ .assertSuccessWithOutputLines(
+ "[Sub1, info]: message00",
+ "[Base1, debug]: message00",
+ "[Sub1, verbose]: message00",
+ "[Sub1, info]: message1",
+ "[Base1, debug]: message2",
+ "[Sub1, verbose]: message3",
+ "[Base2, info]: message08",
+ "[AnotherSub2, debug]: message08",
+ "[AnotherSub2, verbose]: message08",
+ "[Base2, info]: message4",
+ "[AnotherSub2, debug]: message5",
+ "[AnotherSub2, verbose]: message6",
+ "The end");
}
@Test
public void testR8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(config != TestConfig.NON_SPECIFIC_RULES_ALL);
testForR8(parameters.getBackend())
.addInnerClasses(AssumenosideeffectsPropagationTest.class)
.addKeepMainRule(MAIN)
.addKeepRules(config.getKeepRules())
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
.enableInliningAnnotations()
.noMinification()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(config.expectedOutput());
+ .assertSuccessWithOutput(config.expectedOutput(enableHorizontalClassMerging));
}
interface Itf {
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
new file mode 100644
index 0000000..b441e6b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
@@ -0,0 +1,184 @@
+// 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.attributes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MemberSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@SuppressWarnings("ALL")
+@RunWith(Parameterized.class)
+public class KeepSignatureTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String[] EXPECTED = new String[] {"Hello", "World", "Hello", "World"};
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public KeepSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(transformer(KeptClass.class).removeInnerClasses().transform())
+ .addProgramClassFileData(transformer(NotKeptClass.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), KeptClass.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testKeptClassFieldAndMethodFull() throws Exception {
+ // TODO(b/170077516): The below should pass in false.
+ runTest(testForR8(parameters.getBackend()), true);
+ }
+
+ @Test
+ public void testKeptClassFieldAndMethodCompat() throws Exception {
+ runTest(testForR8Compat(parameters.getBackend()), true);
+ }
+
+ private void runTest(R8TestBuilder<?> testBuilder, boolean keptForNotKept) throws Exception {
+ testBuilder
+ .addProgramClassFileData(transformer(KeptClass.class).removeInnerClasses().transform())
+ .addProgramClassFileData(transformer(NotKeptClass.class).removeInnerClasses().transform())
+ .addKeepMainRule(KeptClass.class)
+ .addKeepRules(
+ StringUtils.lines(
+ "-keep class " + KeptClass.class.getTypeName() + " {",
+ " *** keptMethod(...);",
+ " *** keptField;",
+ "}"))
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .run(parameters.getRuntime(), KeptClass.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(inspector -> inspect(inspector, keptForNotKept));
+ }
+
+ private void inspect(CodeInspector inspector, boolean keepForNotKept) {
+ ClassSubject keptClass = inspector.clazz(KeptClass.class);
+ assertThat(keptClass, isPresent());
+ assertEquals(
+ "<T:Ljava/lang/Object;>Ljava/lang/Object;", keptClass.getFinalSignatureAttribute());
+ FieldSubject keptField = keptClass.uniqueFieldWithName("keptField");
+ assertThat(keptField, isPresent());
+ assertEquals("TT;", keptField.getFinalSignatureAttribute());
+ MethodSubject keptMethod = keptClass.uniqueMethodWithName("keptMethod");
+ assertThat(keptMethod, isPresent());
+ assertEquals("<R:Ljava/lang/Object;>(TT;)TR;", keptMethod.getFinalSignatureAttribute());
+
+ // For all remaining classes and members, we should only keep signatures if in compat mode.
+ checkMemberSignature(keptClass.uniqueFieldWithName("notKeptField"), keepForNotKept, "TT;");
+ checkMemberSignature(
+ keptClass.uniqueMethodWithName("notKeptMethod"),
+ keepForNotKept,
+ "<R:Ljava/lang/Object;>(TT;)TR;");
+
+ ClassSubject notKeptClass = inspector.clazz(NotKeptClass.class);
+ assertThat(notKeptClass, isPresent());
+ if (keepForNotKept) {
+ assertEquals(
+ "<P:Ljava/lang/Object;>Ljava/lang/Object;", notKeptClass.getFinalSignatureAttribute());
+ } else {
+ assertNull(notKeptClass.getFinalSignatureAttribute());
+ }
+ checkMemberSignature(
+ notKeptClass.uniqueFieldWithName("notKeptField"), keepForNotKept, "Ljava/util/List<TP;>;");
+ checkMemberSignature(
+ notKeptClass.uniqueMethodWithName("notKeptMethod"),
+ keepForNotKept,
+ "(TP;TP;)Ljava/util/List<TP;>;");
+ }
+
+ private void checkMemberSignature(
+ MemberSubject member, boolean keepForNotKept, String signature) {
+ assertThat(member, isPresent());
+ if (keepForNotKept) {
+ assertEquals(signature, member.getFinalSignatureAttribute());
+ } else {
+ assertNull(member.getFinalSignatureAttribute());
+ }
+ }
+
+ @NeverClassInline
+ public static class NotKeptClass<P> {
+
+ public List<P> notKeptField;
+
+ @NeverInline
+ public List<P> notKeptMethod(P p1, P p2) {
+ if (notKeptField != null) {
+ return notKeptField;
+ }
+ ArrayList<P> ps = new ArrayList<>();
+ ps.add(p1);
+ ps.add(p2);
+ notKeptField = ps;
+ return ps;
+ }
+ }
+
+ public static class KeptClass<T> {
+
+ private T keptField;
+ private T notKeptField;
+
+ public <R> R keptMethod(T t) {
+ if (keptField == null) {
+ keptField = t;
+ }
+ return (R) keptField;
+ }
+
+ @NeverInline
+ public <R> R notKeptMethod(T t) {
+ if (notKeptField == null) {
+ notKeptField = t;
+ }
+ return (R) notKeptField;
+ }
+
+ public static void main(String[] args) {
+ KeptClass<String> keptClass = new KeptClass<>();
+ System.out.println(keptClass.<String>keptMethod("Hello"));
+ System.out.println(keptClass.<String>notKeptMethod("World"));
+ NotKeptClass<String> stringNotKeptClass = new NotKeptClass<>();
+ List<String> strings = stringNotKeptClass.notKeptMethod("Hello", "World");
+ System.out.println(strings.get(0));
+ System.out.println(strings.get(1));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index 367bfbc..94bdbf1 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -37,15 +37,12 @@
return getTestParameters().withNoneRuntime().build();
}
- private final TestParameters parameters;
-
public WhyAreYouKeepingAllTest(TestParameters parameters) {
- this.parameters = parameters;
+ parameters.assertNoneRuntime();
}
@Test
public void test() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
testForR8(Backend.CF)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
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 269c6c2..764c1bb 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.ClassAccessFlags;
@@ -241,6 +242,10 @@
}
public ClassFileTransformer setVersion(int newVersion) {
+ return setVersion(CfVersion.fromRaw(newVersion));
+ }
+
+ public ClassFileTransformer setVersion(CfVersion newVersion) {
return addClassTransformer(
new ClassTransformer() {
@Override
@@ -251,7 +256,7 @@
String signature,
String superName,
String[] interfaces) {
- super.visit(newVersion, access, name, signature, superName, interfaces);
+ super.visit(newVersion.raw(), access, name, signature, superName, interfaces);
}
});
}
@@ -483,11 +488,24 @@
@FunctionalInterface
public interface MethodPredicate {
boolean test(int access, String name, String descriptor, String signature, String[] exceptions);
+
+ static MethodPredicate onName(String name) {
+ return (access, otherName, descriptor, signature, exceptions) -> name.equals(otherName);
+ }
}
@FunctionalInterface
public interface FieldPredicate {
boolean test(int access, String name, String descriptor, String signature, Object value);
+
+ static FieldPredicate onNameAndSignature(String name, String descriptor) {
+ return (access, otherName, otherDescriptor, signature, value) ->
+ name.equals(otherName) && descriptor.equals(otherDescriptor);
+ }
+
+ static FieldPredicate onName(String name) {
+ return (access, otherName, descriptor, signature, value) -> name.equals(otherName);
+ }
}
@FunctionalInterface
@@ -518,14 +536,29 @@
});
}
- public ClassFileTransformer renameMethod(String oldName, String newName) {
+ public ClassFileTransformer renameMethod(MethodPredicate predicate, String newName) {
return addClassTransformer(
new ClassTransformer() {
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
- return super.visitMethod(
- access, name.equals(oldName) ? newName : name, descriptor, signature, exceptions);
+ if (predicate.test(access, name, descriptor, signature, exceptions)) {
+ return super.visitMethod(access, newName, descriptor, signature, exceptions);
+ }
+ return super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+ });
+ }
+
+ public ClassFileTransformer setGenericSignature(MethodPredicate predicate, String newSignature) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ return predicate.test(access, name, descriptor, signature, exceptions)
+ ? super.visitMethod(access, name, descriptor, newSignature, exceptions)
+ : super.visitMethod(access, name, descriptor, signature, exceptions);
}
});
}
@@ -558,13 +591,13 @@
});
}
- public ClassFileTransformer renameField(FieldSignaturePredicate predicate, String newName) {
+ public ClassFileTransformer renameField(FieldPredicate predicate, String newName) {
return addClassTransformer(
new ClassTransformer() {
@Override
public FieldVisitor visitField(
int access, String name, String descriptor, String signature, Object value) {
- if (predicate.test(name, descriptor)) {
+ if (predicate.test(access, name, descriptor, signature, value)) {
return super.visitField(access, newName, descriptor, signature, value);
} else {
return super.visitField(access, name, descriptor, signature, value);
@@ -576,7 +609,21 @@
public ClassFileTransformer renameAndRemapField(String oldName, String newName) {
FieldSignaturePredicate matchPredicate = (name, signature) -> oldName.equals(name);
remapField(matchPredicate, newName);
- return renameField(matchPredicate, newName);
+ return renameField(FieldPredicate.onName(oldName), newName);
+ }
+
+ public ClassFileTransformer setGenericSignature(FieldPredicate predicate, String newSignature) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String descriptor, String signature, Object value) {
+ if (predicate.test(access, name, descriptor, signature, value)) {
+ return super.visitField(access, name, descriptor, newSignature, value);
+ }
+ return super.visitField(access, name, descriptor, signature, value);
+ }
+ });
}
/** Abstraction of the MethodVisitor.visitMethodInsn method with its sub visitor. */
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 6160dc6..ee4fe7b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -21,6 +21,11 @@
}
@Override
+ public boolean isFinal() {
+ throw new Unreachable("Cannot determine if an absent class is final");
+ }
+
+ @Override
public boolean isPresent() {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
new file mode 100644
index 0000000..14666f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.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.utils.codeinspector;
+
+public abstract class ClassOrMemberSubject extends Subject {
+
+ public abstract boolean isFinal();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 60203c1..54e3d63 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -27,7 +27,7 @@
import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.junit.rules.TemporaryFolder;
-public abstract class ClassSubject extends Subject {
+public abstract class ClassSubject extends ClassOrMemberSubject {
protected final ClassReference reference;
protected final CodeInspector codeInspector;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java
index 2fd8d37..ef44c64 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java
@@ -25,6 +25,7 @@
public abstract String getOriginalSignatureAttribute();
+ @Override
public abstract String getFinalSignatureAttribute();
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 3192f8b..bcedc29 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -63,6 +63,11 @@
}
@Override
+ public boolean isFinal() {
+ return dexClass.isFinal();
+ }
+
+ @Override
public boolean isPresent() {
return true;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index 1f14df7..a440523 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.utils.codeinspector;
import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.graph.DexItemFactory;
@@ -21,6 +23,29 @@
this.horizontallyMergedClasses = horizontallyMergedClasses;
}
+ public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
+ assertEquals(
+ horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from, dexItemFactory)),
+ toDexType(target, dexItemFactory));
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertNoClassesMerged() {
+ assertTrue(horizontallyMergedClasses.getSources().isEmpty());
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertClassNotMerged(Class<?> clazz) {
+ assertFalse(horizontallyMergedClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertClassNotMergedIntoDifferentType(Class<?> clazz) {
+ assertFalse(
+ horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
public HorizontallyMergedClassesInspector assertMerged(Class<?> clazz) {
assertTrue(
horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(toDexType(clazz, dexItemFactory)));
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 513de02..6f6bbf8 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.retrace.RetraceApi;
+import com.android.tools.r8.retrace.RetraceFrameResult;
import com.android.tools.r8.retrace.RetraceMethodResult;
public interface InstructionSubject {
@@ -137,11 +138,11 @@
return retracer.retrace(methodSubject.asFoundMethodSubject().asMethodReference());
}
- default RetraceMethodResult retraceLinePosition(RetraceApi retracer) {
- return retrace(retracer).narrowByLine(getLineNumber());
+ default RetraceFrameResult retraceLinePosition(RetraceApi retracer) {
+ return retrace(retracer).narrowByPosition(getLineNumber());
}
- default RetraceMethodResult retracePcPosition(RetraceApi retracer, MethodSubject methodSubject) {
- return retrace(retracer).narrowByLine(getOffset(methodSubject).offset);
+ default RetraceFrameResult retracePcPosition(RetraceApi retracer, MethodSubject methodSubject) {
+ return retrace(retracer).narrowByPosition(getOffset(methodSubject).offset);
}
}
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 017a637..98c8562 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
@@ -6,6 +6,7 @@
import static org.hamcrest.CoreMatchers.not;
+import com.android.tools.r8.Collectors;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.DexClass;
@@ -14,14 +15,11 @@
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
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.retrace.RetraceFrameResult;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.Visibility;
import com.google.common.collect.ImmutableList;
import java.util.List;
-import java.util.stream.Collectors;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@@ -275,24 +273,21 @@
};
}
- public static Matcher<MethodSubject> isFinal() {
- return new TypeSafeMatcher<MethodSubject>() {
+ public static Matcher<ClassOrMemberSubject> isFinal() {
+ return new TypeSafeMatcher<ClassOrMemberSubject>() {
@Override
- public boolean matchesSafely(final MethodSubject method) {
- return method.isPresent() && method.isFinal();
+ public boolean matchesSafely(ClassOrMemberSubject subject) {
+ return subject.isPresent() && subject.isFinal();
}
@Override
- public void describeTo(final Description description) {
+ public void describeTo(Description description) {
description.appendText("is final");
}
@Override
- public void describeMismatchSafely(final MethodSubject method, Description description) {
- description
- .appendText("method ")
- .appendValue(method.getOriginalName())
- .appendText(" was not");
+ public void describeMismatchSafely(ClassOrMemberSubject subject, Description description) {
+ description.appendText("subject ").appendValue(subject.toString()).appendText(" was not");
}
};
}
@@ -483,11 +478,12 @@
};
}
- public static Matcher<RetraceMethodResult> isInlineFrame() {
- return new TypeSafeMatcher<RetraceMethodResult>() {
+ public static Matcher<RetraceFrameResult> isInlineFrame() {
+ return new TypeSafeMatcher<RetraceFrameResult>() {
@Override
- protected boolean matchesSafely(RetraceMethodResult item) {
- return !item.isAmbiguous() && item.stream().count() > 1;
+ protected boolean matchesSafely(RetraceFrameResult item) {
+ return !item.isAmbiguous()
+ && item.stream().mapToLong(method -> method.getOuterFrames().size()).sum() > 0;
}
@Override
@@ -497,28 +493,29 @@
};
}
- public static Matcher<RetraceMethodResult> isInlineStack(LinePosition startPosition) {
- return new TypeSafeMatcher<RetraceMethodResult>() {
+ public static Matcher<RetraceFrameResult> isInlineStack(LinePosition startPosition) {
+ return new TypeSafeMatcher<RetraceFrameResult>() {
@Override
- protected boolean matchesSafely(RetraceMethodResult item) {
+ protected boolean matchesSafely(RetraceFrameResult item) {
+ RetraceFrameResult.Element single = item.stream().collect(Collectors.toSingle());
Box<LinePosition> currentPosition = new Box<>(startPosition);
Box<Boolean> returnValue = new Box<>();
- item.forEach(
- element -> {
+ single.visitFrames(
+ (method, __) -> {
boolean sameMethod;
LinePosition currentInline = currentPosition.get();
if (currentInline == null) {
returnValue.set(false);
return;
}
+ if (method.isUnknown()) {
+ returnValue.set(false);
+ return;
+ }
sameMethod =
- element.getMethod().isKnown()
- && element
- .getMethod()
- .asKnown()
- .equalsMethodReference(currentInline.methodReference);
+ method.asKnown().getMethodReference().equals(currentInline.methodReference);
boolean samePosition =
- element.getOriginalLineNumber(currentInline.minifiedPosition)
+ method.getOriginalPositionOrDefault(currentInline.minifiedPosition)
== currentInline.originalPosition;
if (!returnValue.isSet() || returnValue.get()) {
returnValue.set(sameMethod & samePosition);
@@ -535,28 +532,24 @@
};
}
- public static Matcher<RetraceMethodResult> isTopOfStackTrace(
+ public static Matcher<RetraceFrameResult> isTopOfStackTrace(
StackTrace stackTrace, List<Integer> minifiedPositions) {
- return new TypeSafeMatcher<RetraceMethodResult>() {
+ return new TypeSafeMatcher<RetraceFrameResult>() {
@Override
- protected boolean matchesSafely(RetraceMethodResult item) {
- List<Element> retraceElements = item.stream().collect(Collectors.toList());
- if (retraceElements.size() > stackTrace.size()
- || retraceElements.size() != minifiedPositions.size()) {
- return false;
- }
- for (int i = 0; i < retraceElements.size(); i++) {
- Element retraceElement = retraceElements.get(i);
- StackTraceLine stackTraceLine = stackTrace.get(i);
- RetracedMethod methodReference = retraceElement.getMethod();
- if (!stackTraceLine.methodName.equals(methodReference.getMethodName())
- || !stackTraceLine.className.equals(methodReference.getHolderClass().getTypeName())
- || stackTraceLine.lineNumber
- != retraceElement.getOriginalLineNumber(minifiedPositions.get(i))) {
- return false;
- }
- }
- return true;
+ protected boolean matchesSafely(RetraceFrameResult item) {
+ RetraceFrameResult.Element single = item.stream().collect(Collectors.toSingle());
+ Box<Boolean> matches = new Box<>(true);
+ single.visitFrames(
+ (method, index) -> {
+ StackTraceLine stackTraceLine = stackTrace.get(index);
+ if (!stackTraceLine.methodName.equals(method.getMethodName())
+ || !stackTraceLine.className.equals(method.getHolderClass().getTypeName())
+ || stackTraceLine.lineNumber
+ != method.getOriginalPositionOrDefault(minifiedPositions.get(index))) {
+ matches.set(false);
+ }
+ });
+ return matches.get();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
index 77172c3..847675f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
-public abstract class MemberSubject extends Subject {
+public abstract class MemberSubject extends ClassOrMemberSubject {
public abstract boolean isPublic();
@@ -18,8 +18,6 @@
public abstract boolean isStatic();
- public abstract boolean isFinal();
-
public abstract Signature getOriginalSignature();
public abstract Signature getFinalSignature();
@@ -48,6 +46,8 @@
return finalSignature == null ? null : finalSignature.name;
}
+ public abstract String getFinalSignatureAttribute();
+
public abstract AnnotationSubject annotation(String name);
public FieldSubject asFieldSubject() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 3c86361..c6a63e2 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -42,6 +42,7 @@
public abstract String getOriginalSignatureAttribute();
+ @Override
public abstract String getFinalSignatureAttribute();
public abstract DexEncodedMethod getMethod();
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 94ef0dc..00f4380 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -5,25 +5,24 @@
import apk_masseur
import apk_utils
+import as_utils
import golem
-import gradle
import jdk
import json
-import os
import optparse
+import os
import shutil
import signal
import subprocess
import sys
import time
-import utils
-import zipfile
-from xml.dom import minidom
-from datetime import datetime
-
-import as_utils
import update_prebuilds_in_android
-import download_all_benchmark_dependencies
+import zipfile
+from datetime import datetime
+from xml.dom import minidom
+
+import gradle
+import utils
SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full', 'pg']
WORKING_DIR = os.path.join(utils.BUILD, 'opensource_apps')
@@ -62,7 +61,9 @@
'releaseTarget': None,
'signed_apk_name': None,
'skip': False,
- 'has_lint_task': True
+ 'has_lint_task': True,
+ 'legacy_desugaring': False,
+ 'compatibility': None
}
self.__dict__ = dict(defaults.items() + fields.items())
@@ -114,7 +115,9 @@
'id': 'dev.dworks.apps.anexplorer.pro',
'flavor': 'googleMobilePro',
'signed_apk_name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
- 'min_sdk': 17
+ 'min_sdk': 17,
+ 'legacy_desugaring': True,
+ 'compatibility': '1.7',
})
]
}),
@@ -161,7 +164,9 @@
'apps': [
App({
'id': 'com.chanapps.four.activity',
- 'has_lint_task': False
+ 'has_lint_task': False,
+ 'min_sdk': 15,
+ 'compatibility': '1.7',
})
]
}),
@@ -171,7 +176,9 @@
'revision': '10091fa0ec37da12e66286559ad1b6098976b07b',
'apps': [
App({
- 'id': 'com.google.firebase.example.fireeats'
+ 'id': 'com.google.firebase.example.fireeats',
+ 'min_sdk': 16,
+ 'compatibility': '1.7',
})
]
}),
@@ -194,7 +201,9 @@
'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f',
'apps': [
App({
- 'id': 'com.example.instabug'
+ 'id': 'com.example.instabug',
+ 'min_sdk': 15,
+ 'compatibility': '1.7',
})
]
}),
@@ -218,7 +227,9 @@
'revision': '093da9ee0512e67192f62951c45a07a616fc3224',
'apps': [
App({
- 'id': 'fr.neamar.kiss'
+ 'id': 'fr.neamar.kiss',
+ 'min_sdk': 15,
+ 'compatibility': '1.7',
})
]
}),
@@ -374,8 +385,9 @@
App({
'id': 'eu.kanade.tachiyomi',
'flavor': 'dev',
- 'min_sdk': 16,
- 'has_lint_task': False
+ 'min_sdk': 21,
+ 'has_lint_task': False,
+ 'compatibility': '1.7',
})
]
}),
@@ -970,7 +982,10 @@
'--pg-conf', proguard_config_file,
'--lib', android_jar,
'--output', zip_dest,
- apk])
+ apk] +
+ (['--no-desugaring']
+ if app.legacy_desugaring or app.compatibility == '1.7'
+ else []))
for android_optional_jar in utils.get_android_optional_jars(compile_sdk):
cmd.append('--lib')