Merge commit '199b06b7dde9f75e04515100cff84ff669b5e542' into dev-release
diff --git a/.gitignore b/.gitignore
index d1bacd1..052f0a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,8 @@
third_party/kotlin/kotlin-compiler-1.4.20
third_party/kotlin/kotlin-compiler-1.5.0.tar.gz
third_party/kotlin/kotlin-compiler-1.5.0
+third_party/kotlin/kotlin-compiler-1.6.0.tar.gz
+third_party/kotlin/kotlin-compiler-1.6.0
third_party/kotlin/kotlin-compiler-dev.tar.gz
third_party/kotlin/kotlin-compiler-dev
third_party/kotlinx-coroutines-1.3.6.tar.gz
diff --git a/build.gradle b/build.gradle
index 007fe58..921a270 100644
--- a/build.gradle
+++ b/build.gradle
@@ -351,6 +351,7 @@
"kotlin/kotlin-compiler-1.3.72",
"kotlin/kotlin-compiler-1.4.20",
"kotlin/kotlin-compiler-1.5.0",
+ "kotlin/kotlin-compiler-1.6.0",
"kotlinx-coroutines-1.3.6",
"openjdk/openjdk-rt-1.8",
"openjdk/desugar_jdk_libs",
@@ -989,7 +990,7 @@
return baseD8CommandLine(allArgs)
}
-def r8LibCreateTask(name, pgConfs = [], r8Task, output, libs = []) {
+def r8LibCreateTask(name, pgConfs = [], r8Task, output, libs = [], classpath = []) {
return tasks.create("r8Lib${name}", Exec) {
inputs.files ([pgConfs, r8WithRelocatedDeps.outputs, r8Task.outputs])
outputs.file output
@@ -1001,7 +1002,8 @@
"--r8jar", r8Task.outputs.files[0],
"--output", output]
+ (pgConfs.collectMany { ["--pg-conf", it] })
- + (libs.collectMany { ["--lib", it] }))
+ + (libs.collectMany { ["--lib", it] })
+ + (classpath.collectMany { ["--classpath", it] }))
workingDir = projectDir
}
}
@@ -1100,6 +1102,7 @@
["src/main/keep.txt"],
r8NoManifestWithoutDeps,
r8LibExludeDepsPath,
+ [],
repackageDeps.outputs.files
).dependsOn(repackageDeps)
inputs.files ([r8NoManifestWithoutDeps.outputs, repackageDeps.outputs])
@@ -1136,6 +1139,7 @@
["src/main/keep_retrace.txt"],
R8LibNoDeps,
r8RetraceExludeDepsPath,
+ [],
repackageDeps.outputs.files
).dependsOn(R8LibNoDeps)
outputs.file r8RetraceExludeDepsPath
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index f4975b7..4623314 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -1095,6 +1095,35 @@
}
}
builders {
+ name: "linux-kotlin_old"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ recipe {
+ name: "rex"
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ properties_j: "builder_group:\"internal.client.r8\""
+ properties_j: "test_options:[\"--runtimes=dex-default:jdk11\",\"--kotlin-compiler-old\",\"--one_line_per_test\",\"--archive_failures\",\"--no-internal\",\"*kotlin*\"]"
+ }
+ priority: 26
+ execution_timeout_secs: 43200
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
+ key: "luci.use_realms"
+ value: 100
+ }
+ }
+ builders {
name: "linux-none"
swarming_host: "chrome-swarming.appspot.com"
swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index a48c9aa..6151ce6 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -106,6 +106,11 @@
short_name: "kotlin_dev"
}
builders {
+ name: "buildbucket/luci.r8.ci/linux-kotlin_old"
+ category: "R8"
+ short_name: "kotlin_old"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/linux-d8_jctf"
category: "jctf"
short_name: "d8_jctf"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index c6af3c9..e697c32 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -432,6 +432,18 @@
}
builders {
bucket: "ci"
+ name: "linux-kotlin_old"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
name: "linux-none"
repository: "https://r8.googlesource.com/r8"
}
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index e9b307f..bf973f3 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -512,6 +512,20 @@
}
}
job {
+ id: "linux-kotlin_old"
+ realm: "ci"
+ acl_sets: "ci"
+ triggering_policy {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 4
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-kotlin_old"
+ }
+}
+job {
id: "linux-none"
realm: "ci"
acl_sets: "ci"
@@ -676,6 +690,7 @@
triggers: "linux-jdk8"
triggers: "linux-jdk9"
triggers: "linux-kotlin_dev"
+ triggers: "linux-kotlin_old"
triggers: "linux-none"
triggers: "linux-r8cf_jctf"
triggers: "linux-run-on-app-dump"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 1659d63..525fcd4 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
name: "r8"
access: "group:all"
lucicfg {
- version: "1.29.1"
+ version: "1.30.4"
package_dir: ".."
config_dir: "generated"
entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 238f87c..3aa3fe2 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -336,6 +336,17 @@
}
)
+r8_builder(
+ "linux-kotlin_old",
+ dimensions = get_dimensions(),
+ execution_timeout = time.hour * 12,
+ expiration_timeout = time.hour * 35,
+ properties = {
+ "builder_group" : "internal.client.r8",
+ "test_options" : ["--runtimes=dex-default:jdk11", "--kotlin-compiler-old", "--one_line_per_test", "--archive_failures", "--no-internal", "*kotlin*"]
+ }
+)
+
def jctf():
for release in ["", "_release"]:
for tool in ["d8", "r8cf"]:
diff --git a/src/main/java/com/android/tools/r8/errors/InlinableStaticFinalFieldPreconditionDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InlinableStaticFinalFieldPreconditionDiagnostic.java
new file mode 100644
index 0000000..45b40d2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InlinableStaticFinalFieldPreconditionDiagnostic.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.shaking.ProguardIfRule;
+import com.android.tools.r8.utils.FieldReferenceUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Collection;
+import java.util.List;
+
+@Keep
+public class InlinableStaticFinalFieldPreconditionDiagnostic implements Diagnostic {
+
+ private final ProguardIfRule rule;
+ private final Collection<FieldReference> fields;
+
+ public InlinableStaticFinalFieldPreconditionDiagnostic(
+ ProguardIfRule rule, List<DexField> fields) {
+ this.rule = rule;
+ this.fields = ListUtils.map(fields, DexField::asFieldReference);
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return rule.getOrigin();
+ }
+
+ @Override
+ public Position getPosition() {
+ return rule.getPosition();
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return StringUtils.lines(
+ "Rule precondition matches static final fields javac has inlined.",
+ "Such rules are unsound as the shrinker cannot infer the inlining precisely.",
+ "Consider adding !static to the rule.",
+ "Matched fields are: ")
+ + StringUtils.joinLines(ListUtils.map(fields, FieldReferenceUtils::toSourceString));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 8c16e46..d49c43f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -572,9 +572,8 @@
return null;
}
if (debugInfoForWriting == null) {
- debugInfoForWriting = new DexDebugInfoForWriting(debugInfo);
+ debugInfoForWriting = DexDebugInfoForWriting.create(debugInfo);
}
-
return debugInfoForWriting;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index bb93147..e4994c0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.dex.DebugBytecodeWriter;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
-import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.utils.Int2StructuralItemArrayMap;
@@ -78,8 +77,20 @@
internalAcceptHashing(visitor);
}
- public abstract void writeOn(
- DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens);
+ public final void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
+ assert isWritableEvent();
+ internalWriteOn(writer, mapping, graphLens);
+ }
+
+ boolean isWritableEvent() {
+ return false;
+ }
+
+ void internalWriteOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
+ throw new Unreachable();
+ }
public abstract void accept(DexDebugEventVisitor visitor);
@@ -108,7 +119,12 @@
public final int delta;
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_ADVANCE_PC);
writer.putUleb128(delta);
@@ -158,7 +174,12 @@
}
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_SET_PROLOGUE_END);
}
@@ -203,7 +224,12 @@
}
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_SET_EPILOGUE_BEGIN);
}
@@ -249,7 +275,12 @@
}
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_ADVANCE_LINE);
writer.putSleb128(delta);
@@ -317,7 +348,12 @@
}
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(signature == null
? Constants.DBG_START_LOCAL
@@ -388,7 +424,12 @@
}
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_END_LOCAL);
writer.putUleb128(registerNum);
@@ -435,7 +476,12 @@
}
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_RESTART_LOCAL);
writer.putUleb128(registerNum);
@@ -489,9 +535,9 @@
}
@Override
- public void writeOn(
- DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
- throw new InternalCompilerError("Unused/unsupported SetFile event should never be written");
+ boolean isWritableEvent() {
+ // Even though this is a DEX specified event it is unsupported and should never be written.
+ return false;
}
@Override
@@ -547,12 +593,6 @@
}
@Override
- public void writeOn(
- DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
- // CallerPosition will not be written.
- }
-
- @Override
public void accept(DexDebugEventVisitor visitor) {
visitor.visit(this);
}
@@ -626,12 +666,6 @@
}
@Override
- public void writeOn(
- DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
- // Will not be written
- }
-
- @Override
public void accept(DexDebugEventVisitor visitor) {
visitor.visit(this);
}
@@ -663,12 +697,6 @@
}
@Override
- public void writeOn(
- DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
- // CallerPosition will not be written.
- }
-
- @Override
public void accept(DexDebugEventVisitor visitor) {
visitor.visit(this);
}
@@ -729,7 +757,12 @@
}
@Override
- public void writeOn(
+ boolean isWritableEvent() {
+ return true;
+ }
+
+ @Override
+ public void internalWriteOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(value);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
index 0777b85..ddb4efc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
@@ -4,20 +4,36 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
-import java.util.Arrays;
-
/**
* Wraps DexDebugInfo to make comparison and hashcode not consider
* the SetInlineFrames
*/
public class DexDebugInfoForWriting extends DexDebugInfo {
- public DexDebugInfoForWriting(DexDebugInfo dexDebugInfo) {
- super(dexDebugInfo.startLine, dexDebugInfo.parameters,
- Arrays.stream(dexDebugInfo.events)
- .filter(d -> !(d instanceof SetInlineFrame))
- .toArray(DexDebugEvent[]::new));
+ private DexDebugInfoForWriting(int startLine, DexString[] parameters, DexDebugEvent[] events) {
+ super(startLine, parameters, events);
}
+ public static DexDebugInfoForWriting create(DexDebugInfo debugInfo) {
+ assert debugInfo != null;
+ int nonWritableEvents = 0;
+ for (DexDebugEvent event : debugInfo.events) {
+ if (!event.isWritableEvent()) {
+ nonWritableEvents++;
+ }
+ }
+ DexDebugEvent[] writableEvents;
+ if (nonWritableEvents == 0) {
+ writableEvents = debugInfo.events;
+ } else {
+ writableEvents = new DexDebugEvent[debugInfo.events.length - nonWritableEvents];
+ int i = 0;
+ for (DexDebugEvent event : debugInfo.events) {
+ if (event.isWritableEvent()) {
+ writableEvents[i++] = event;
+ }
+ }
+ }
+ return new DexDebugInfoForWriting(debugInfo.startLine, debugInfo.parameters, writableEvents);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 44a5132..3ae4b1f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -43,6 +43,11 @@
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
private KotlinFieldLevelInfo kotlinMemberInfo = getNoKotlinInfo();
+ // Mark indicating if this field has been identified as potentially inlined by javac.
+ // This is to ensure consistent tracing in the second round of tree shaking. Remove this field
+ // once conditional rules are represented by rule-instances rather than reevaluating rule-schemas.
+ private boolean isInlinableByJavaC = false;
+
private static void specify(StructuralSpecification<DexEncodedField, ?> spec) {
spec.withItem(DexEncodedField::getReference)
.withItem(DexEncodedField::getAccessFlags)
@@ -358,6 +363,14 @@
return new Builder(true);
}
+ public void markAsInlinableByJavaC() {
+ isInlinableByJavaC = true;
+ }
+
+ public boolean isInlinableByJavaC() {
+ return isInlinableByJavaC;
+ }
+
public static class Builder {
private DexField field;
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 a8709ec..a68f038 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -47,7 +47,6 @@
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
@@ -55,12 +54,10 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.NestUtils;
-import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
import com.android.tools.r8.ir.optimize.info.MutableMethodOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
@@ -150,7 +147,6 @@
ComputedApiLevel.notSet(),
ComputedApiLevel.notSet(),
null,
- CallSiteOptimizationInfo.top(),
DefaultMethodOptimizationInfo.getInstance(),
false);
public static final Int2ReferenceMap<DebugLocalInfo> NO_PARAMETER_INFO =
@@ -164,7 +160,6 @@
// we need to maintain a set of states with (potentially different) contexts.
private CompilationState compilationState = CompilationState.NOT_PROCESSED;
private MethodOptimizationInfo optimizationInfo;
- private CallSiteOptimizationInfo callSiteOptimizationInfo;
private CfVersion classFileVersion;
/** The apiLevelForCode describes the api level needed for knowing all references in the code */
private ComputedApiLevel apiLevelForCode;
@@ -244,7 +239,6 @@
ComputedApiLevel apiLevelForDefinition,
ComputedApiLevel apiLevelForCode,
CfVersion classFileVersion,
- CallSiteOptimizationInfo callSiteOptimizationInfo,
MethodOptimizationInfo optimizationInfo,
boolean deprecated) {
super(method, annotations, d8R8Synthesized, apiLevelForDefinition);
@@ -255,7 +249,6 @@
this.code = code;
this.classFileVersion = classFileVersion;
this.apiLevelForCode = apiLevelForCode;
- this.callSiteOptimizationInfo = requireNonNull(callSiteOptimizationInfo);
this.optimizationInfo = requireNonNull(optimizationInfo);
assert accessFlags != null;
assert code == null || !shouldNotHaveCode();
@@ -1298,27 +1291,6 @@
optimizationInfo = info;
}
- public synchronized void abandonCallSiteOptimizationInfo() {
- checkIfObsolete();
- callSiteOptimizationInfo = CallSiteOptimizationInfo.abandoned();
- }
-
- public synchronized CallSiteOptimizationInfo getCallSiteOptimizationInfo() {
- checkIfObsolete();
- return callSiteOptimizationInfo;
- }
-
- public synchronized void joinCallSiteOptimizationInfo(
- CallSiteOptimizationInfo other, AppView<?> appView) {
- checkIfObsolete();
- callSiteOptimizationInfo = callSiteOptimizationInfo.join(other, appView, this);
- }
-
- public synchronized void setCallSiteOptimizationInfo(
- CallSiteOptimizationInfo callSiteOptimizationInfo) {
- this.callSiteOptimizationInfo = callSiteOptimizationInfo;
- }
-
public void copyMetadata(AppView<?> appView, DexEncodedMethod from) {
checkIfObsolete();
if (from.hasClassFileVersion()) {
@@ -1369,9 +1341,6 @@
private OptionalBool isLibraryMethodOverride = OptionalBool.UNKNOWN;
private ParameterAnnotationsList parameterAnnotations = ParameterAnnotationsList.empty();
private CompilationState compilationState = CompilationState.NOT_PROCESSED;
- // TODO(b/190154391): We should set this to top, but the old call site optimization requires
- // this to be bottom.
- private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.bottom();
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
private KotlinMethodLevelInfo kotlinInfo = getNoKotlinInfo();
private CfVersion classFileVersion = null;
@@ -1401,7 +1370,6 @@
code = from.getCode();
apiLevelForDefinition = from.getApiLevelForDefinition();
apiLevelForCode = from.getApiLevelForCode();
- callSiteOptimizationInfo = from.getCallSiteOptimizationInfo();
optimizationInfo =
from.getOptimizationInfo().isMutableOptimizationInfo()
? from.getOptimizationInfo().asMutableMethodOptimizationInfo().mutableCopy()
@@ -1440,31 +1408,10 @@
return this;
}
- public Builder fixupCallSiteOptimizationInfo(MethodOptimizationInfoFixer fixer) {
- if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()) {
- callSiteOptimizationInfo =
- fixer.fixupCallSiteOptimizationInfo(
- callSiteOptimizationInfo.asConcreteCallSiteOptimizationInfo());
- }
- return this;
- }
-
public Builder fixupOptimizationInfo(
AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
- return fixupCallSiteOptimizationInfo(fixer)
- .modifyOptimizationInfo(
- (newMethod, optimizationInfo) -> optimizationInfo.fixup(appView, fixer));
- }
-
- public Builder setSimpleInliningConstraint(
- DexProgramClass holder, SimpleInliningConstraint simpleInliningConstraint) {
- return addBuildConsumer(
- newMethod ->
- OptimizationFeedbackSimple.getInstance()
- .setSimpleInliningConstraint(
- // The method has not yet been installed so we cannot use
- // asProgramMethod(appView).
- new ProgramMethod(holder, newMethod), simpleInliningConstraint));
+ return modifyOptimizationInfo(
+ (newMethod, optimizationInfo) -> optimizationInfo.fixup(appView, fixer));
}
public Builder addBuildConsumer(Consumer<DexEncodedMethod> consumer) {
@@ -1669,7 +1616,6 @@
apiLevelForDefinition,
apiLevelForCode,
classFileVersion,
- callSiteOptimizationInfo,
optimizationInfo,
deprecated);
result.setKotlinMemberInfo(kotlinInfo);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index d72df8c..4879d46 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -883,6 +883,7 @@
boxedShortType,
boxedVoidType,
enumType,
+ javaLangSystemType,
npeType,
objectType,
stringBufferType,
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 900e79c..2bbb51b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -8,8 +8,6 @@
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
-import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
@@ -822,21 +820,4 @@
return checksumSupplier;
}
- public ComputedApiLevel getApiReferenceLevel(
- AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
- // The api level of a class is the max level of it's members, super class and interfaces.
- return getMembersApiReferenceLevel(
- apiLevelCompute.computeApiLevelForDefinition(
- allImmediateSupertypes(), apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
- }
-
- public ComputedApiLevel getMembersApiReferenceLevel(ComputedApiLevel memberLevel) {
- for (DexEncodedMember<?, ?> member : members()) {
- memberLevel = memberLevel.max(member.getApiLevel());
- if (memberLevel.isUnknownApiLevel()) {
- return memberLevel;
- }
- }
- return memberLevel;
- }
}
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 9236d1f..a6c1c5c 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -118,40 +118,64 @@
*/
public static class FieldLookupResult extends MemberLookupResult<DexField> {
- private final DexType castType;
+ private final DexType readCastType;
+ private final DexType writeCastType;
- private FieldLookupResult(DexField reference, DexField reboundReference, DexType castType) {
+ private FieldLookupResult(
+ DexField reference,
+ DexField reboundReference,
+ DexType readCastType,
+ DexType writeCastType) {
super(reference, reboundReference);
- this.castType = castType;
+ this.readCastType = readCastType;
+ this.writeCastType = writeCastType;
}
public static Builder builder(GraphLens lens) {
return new Builder(lens);
}
- public boolean hasCastType() {
- return castType != null;
+ public boolean hasReadCastType() {
+ return readCastType != null;
}
- public DexType getCastType() {
- return castType;
+ public DexType getReadCastType() {
+ return readCastType;
}
- public DexType getRewrittenCastType(Function<DexType, DexType> fn) {
- return hasCastType() ? fn.apply(castType) : null;
+ public DexType getRewrittenReadCastType(Function<DexType, DexType> fn) {
+ return hasReadCastType() ? fn.apply(readCastType) : null;
+ }
+
+ public boolean hasWriteCastType() {
+ return writeCastType != null;
+ }
+
+ public DexType getWriteCastType() {
+ return writeCastType;
+ }
+
+ public DexType getRewrittenWriteCastType(Function<DexType, DexType> fn) {
+ return hasWriteCastType() ? fn.apply(writeCastType) : null;
}
public static class Builder extends MemberLookupResult.Builder<DexField, Builder> {
- private DexType castType;
+ private DexType readCastType;
+ private DexType writeCastType;
private GraphLens lens;
private Builder(GraphLens lens) {
this.lens = lens;
}
- public Builder setCastType(DexType castType) {
- this.castType = castType;
+ public Builder setReadCastType(DexType readCastType) {
+ this.readCastType = readCastType;
+ return this;
+ }
+
+ public Builder setWriteCastType(DexType writeCastType) {
+ this.writeCastType = writeCastType;
return this;
}
@@ -162,7 +186,7 @@
public FieldLookupResult build() {
// TODO(b/168282032): All non-identity graph lenses should set the rebound reference.
- return new FieldLookupResult(reference, reboundReference, castType);
+ return new FieldLookupResult(reference, reboundReference, readCastType, writeCastType);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
index 62ce9d8..e5b3462 100644
--- a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
@@ -163,7 +163,9 @@
return FieldLookupResult.builder(this)
.setReboundReference(rewrittenReboundReference)
.setReference(rewrittenNonReboundReference)
- .setCastType(previous.getRewrittenCastType(this::internalDescribeLookupClassType))
+ .setReadCastType(previous.getRewrittenReadCastType(this::internalDescribeLookupClassType))
+ .setWriteCastType(
+ previous.getRewrittenWriteCastType(this::internalDescribeLookupClassType))
.build();
} else {
// TODO(b/168282032): We should always have the rebound reference, so this should become
@@ -171,7 +173,9 @@
DexField rewrittenReference = previous.getRewrittenReference(fieldMap);
return FieldLookupResult.builder(this)
.setReference(rewrittenReference)
- .setCastType(previous.getRewrittenCastType(this::internalDescribeLookupClassType))
+ .setReadCastType(previous.getRewrittenReadCastType(this::internalDescribeLookupClassType))
+ .setWriteCastType(
+ previous.getRewrittenWriteCastType(this::internalDescribeLookupClassType))
.build();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
index 1243e98..01b6d96 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.graph.analysis;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -16,8 +16,6 @@
import com.android.tools.r8.shaking.KeepInfo.Joiner;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
/**
* In Dalvik it is a verification error to read and use a field of type Missing[].
@@ -37,27 +35,11 @@
private final DexItemFactory dexItemFactory;
private final Enqueuer enqueuer;
- private final Set<DexType> knownToBePresentOnDalvik;
public GetArrayOfMissingTypeVerifyErrorWorkaround(
AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
this.dexItemFactory = appView.dexItemFactory();
this.enqueuer = enqueuer;
- this.knownToBePresentOnDalvik =
- ImmutableSet.<DexType>builder()
- .add(dexItemFactory.boxedBooleanType)
- .add(dexItemFactory.boxedByteType)
- .add(dexItemFactory.boxedCharType)
- .add(dexItemFactory.boxedDoubleType)
- .add(dexItemFactory.boxedFloatType)
- .add(dexItemFactory.boxedIntType)
- .add(dexItemFactory.boxedLongType)
- .add(dexItemFactory.boxedShortType)
- .add(dexItemFactory.classType)
- .add(dexItemFactory.objectType)
- .add(dexItemFactory.enumType)
- .add(dexItemFactory.stringType)
- .build();
}
public static void register(
@@ -77,7 +59,7 @@
@Override
public void traceInstanceFieldRead(
DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
- if (isUnsafeToUseFieldOnDalvik(field, context)) {
+ if (isUnsafeToUseFieldOnDalvik(field)) {
enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
}
}
@@ -85,12 +67,12 @@
@Override
public void traceStaticFieldRead(
DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
- if (isUnsafeToUseFieldOnDalvik(field, context)) {
+ if (isUnsafeToUseFieldOnDalvik(field)) {
enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
}
}
- private boolean isUnsafeToUseFieldOnDalvik(DexField field, ProgramMethod context) {
+ private boolean isUnsafeToUseFieldOnDalvik(DexField field) {
DexType fieldType = field.getType();
if (!fieldType.isArrayType()) {
return false;
@@ -99,13 +81,15 @@
if (!baseType.isClassType()) {
return false;
}
- if (knownToBePresentOnDalvik.contains(baseType)) {
- return false;
- }
- // TODO(b/206891715): Use the API database to determine if the given type is introduced in API
- // level L or later.
- DexClass baseClass = enqueuer.definitionFor(baseType, context);
- return baseClass != null && baseClass.isLibraryClass();
+ ComputedApiLevel baseTypeApiLevel =
+ enqueuer
+ .getApiLevelCompute()
+ .computeApiLevelForLibraryReference(baseType, ComputedApiLevel.unknown());
+ return !baseTypeApiLevel.isKnownApiLevel()
+ || baseTypeApiLevel
+ .asKnownApiLevel()
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 03a044d..937a1fc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -77,10 +77,11 @@
return FieldLookupResult.builder(this)
.setReference(lookup.getReference())
.setReboundReference(lookup.getReboundReference())
- .setCastType(
+ .setReadCastType(
lookup.getReference().getType() != previous.getReference().getType()
? lookupType(previous.getReference().getType())
: null)
+ .setWriteCastType(previous.getRewrittenWriteCastType(this::internalDescribeLookupClassType))
.build();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
index 1f953f4..6a083f0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import static com.android.tools.r8.utils.AndroidApiLevelUtils.getApiReferenceLevelForMerging;
+
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
@@ -37,6 +39,6 @@
@Override
public ComputedApiLevel getMergeKey(DexProgramClass clazz) {
assert enableApiCallerIdentification;
- return clazz.getApiReferenceLevel(appView, apiLevelCompute);
+ return getApiReferenceLevelForMerging(appView, apiLevelCompute, clazz);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
index b45feb0..991a263 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
@@ -10,10 +10,14 @@
DexField getField();
+ Position getPosition();
+
int getValueIndex();
Value value();
+ void setValue(Value value);
+
FieldInstruction asFieldInstruction();
boolean isInstancePut();
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 2a5d60e..95086dc 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
@@ -80,6 +80,11 @@
}
@Override
+ public void setValue(Value value) {
+ replaceValue(1, value);
+ }
+
+ @Override
public void buildDex(DexBuilder builder) {
com.android.tools.r8.code.Instruction instruction;
int valueRegister = builder.allocatedRegister(value(), getNumber());
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 8cbeb6d..339f16f 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
@@ -58,6 +58,11 @@
}
@Override
+ public void setValue(Value value) {
+ replaceValue(0, value);
+ }
+
+ @Override
public void buildDex(DexBuilder builder) {
com.android.tools.r8.code.Instruction instruction;
int src = builder.allocatedRegister(value(), getNumber());
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 40cb9e9..c3fd49f 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
@@ -753,7 +753,8 @@
// types, those could be still less precise at one single call site, where specific arguments
// will be passed during (double) inlining. Instead of adding assumptions and removing invalid
// ones, it's better not to insert assumptions for inlinee in the beginning.
- CallSiteOptimizationInfo callSiteOptimizationInfo = getMethod().getCallSiteOptimizationInfo();
+ CallSiteOptimizationInfo callSiteOptimizationInfo =
+ getMethod().getOptimizationInfo().getArgumentInfos();
if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && method == context) {
// TODO(b/190154391): Consider pruning all argument information from the optimization info
// after the second optimization pass. That way we save memory and can assert here that
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 269dc23..dafd559 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
@@ -68,6 +68,7 @@
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstMethodHandle;
import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.FieldPut;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
@@ -104,6 +105,7 @@
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
@@ -347,13 +349,14 @@
SingleNumberValue numberValue = parameter.getValue(appView);
// Try to find an existing constant instruction, otherwise generate a new one.
+ InstructionListIterator finalIterator = iterator;
Value value =
parameterMap
.computeIfAbsent(numberValue, ignore -> new IdentityHashMap<>())
.computeIfAbsent(
type,
ignore -> {
- iterator.previous();
+ finalIterator.previous();
Instruction instruction =
numberValue.createMaterializingInstruction(
appView,
@@ -363,8 +366,8 @@
assert !instruction.instructionTypeCanThrow();
instruction.setPosition(
options.debug ? invoke.getPosition() : Position.none());
- iterator.add(instruction);
- iterator.next();
+ finalIterator.add(instruction);
+ finalIterator.next();
return instruction.outValue();
});
@@ -448,15 +451,15 @@
new InstanceGet(newOutValue, instanceGet.object(), rewrittenField));
}
if (newOutValue != null) {
- if (lookup.hasCastType() && newOutValue.hasNonDebugUsers()) {
+ if (lookup.hasReadCastType() && newOutValue.hasNonDebugUsers()) {
TypeElement castType =
TypeElement.fromDexType(
- lookup.getCastType(), newOutValue.getType().nullability(), appView);
+ lookup.getReadCastType(), newOutValue.getType().nullability(), appView);
Value castOutValue = code.createValue(castType);
newOutValue.replaceUsers(castOutValue);
CheckCast checkCast =
SafeCheckCast.builder()
- .setCastType(lookup.getCastType())
+ .setCastType(lookup.getReadCastType())
.setObject(newOutValue)
.setOutValue(castOutValue)
.setPosition(instanceGet)
@@ -476,6 +479,8 @@
InstancePut instancePut = current.asInstancePut();
DexField field = instancePut.getField();
FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, instancePut, lookup);
+
DexField rewrittenField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
graphLens.lookupPutFieldForMethod(rewrittenField, method.getReference());
@@ -517,15 +522,15 @@
iterator.replaceCurrentInstruction(new StaticGet(newOutValue, rewrittenField));
}
if (newOutValue != null) {
- if (lookup.hasCastType() && newOutValue.hasNonDebugUsers()) {
+ if (lookup.hasReadCastType() && newOutValue.hasNonDebugUsers()) {
TypeElement castType =
TypeElement.fromDexType(
- lookup.getCastType(), newOutValue.getType().nullability(), appView);
+ lookup.getReadCastType(), newOutValue.getType().nullability(), appView);
Value castOutValue = code.createValue(castType);
newOutValue.replaceUsers(castOutValue);
CheckCast checkCast =
SafeCheckCast.builder()
- .setCastType(lookup.getCastType())
+ .setCastType(lookup.getReadCastType())
.setObject(newOutValue)
.setOutValue(castOutValue)
.setPosition(staticGet)
@@ -545,6 +550,8 @@
StaticPut staticPut = current.asStaticPut();
DexField field = staticPut.getField();
FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, staticPut, lookup);
+
DexField actualField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
graphLens.lookupPutFieldForMethod(actualField, method.getReference());
@@ -745,6 +752,30 @@
assert code.hasNoMergedClasses(appView);
}
+ private void insertCastForFieldAssignmentIfNeeded(
+ IRCode code,
+ ListIterator<BasicBlock> blocks,
+ InstructionListIterator iterator,
+ FieldPut fieldPut,
+ FieldLookupResult lookup) {
+ if (lookup.hasWriteCastType()) {
+ iterator.previous();
+ CheckCast checkCast =
+ SafeCheckCast.builder()
+ .setObject(fieldPut.value())
+ .setFreshOutValue(code, lookup.getWriteCastType().toTypeElement(appView))
+ .setCastType(lookup.getWriteCastType())
+ .setPosition(fieldPut.getPosition())
+ .build();
+ iterator.add(checkCast);
+ iterator =
+ iterator.splitCopyCatchHandlers(code, blocks, appView.options()).listIterator(code);
+ fieldPut.setValue(checkCast.outValue());
+ Instruction next = iterator.next();
+ assert next == fieldPut;
+ }
+ }
+
private DexField rewriteFieldReference(FieldLookupResult lookup, ProgramMethod context) {
if (lookup.hasReboundReference()) {
DexClass holder = appView.definitionFor(lookup.getReboundReference().getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
deleted file mode 100644
index d39f509..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ /dev/null
@@ -1,440 +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.ir.optimize;
-
-import static com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo.abandoned;
-import static com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo.top;
-import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeCustom;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.conversion.PostMethodProcessor;
-import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
-import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ForEachable;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
-import com.android.tools.r8.utils.LazyBox;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
-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.Consumer;
-
-public class CallSiteOptimizationInfoPropagator {
-
- // TODO(b/139246447): should we revisit new targets over and over again?
- // Maybe piggy-back on MethodProcessor's wave/batch processing?
- // For now, before revisiting methods with more precise argument info, we switch the mode.
- // Then, revisiting a target at a certain level will not improve call site information of
- // callees in lower levels.
- public enum Mode {
- // Set until the end of the 1st round of IR processing. CallSiteOptimizationInfo will be updated
- // in this mode only.
- COLLECT,
- // Set once the all methods are processed. IRBuilder will add other instructions that reflect
- // collected CallSiteOptimizationInfo.
- REVISIT;
-
- public boolean isRevisit() {
- return this == REVISIT;
- }
- }
-
- private final AppView<AppInfoWithLiveness> appView;
- private final InternalOptions options;
- private final CallSiteOptimizationOptions optimizationOptions;
- private Mode mode = Mode.COLLECT;
-
- private ProgramMethodSet revisitedMethodsForTesting = null;
-
- public CallSiteOptimizationInfoPropagator(AppView<AppInfoWithLiveness> appView) {
- assert appView.enableWholeProgramOptimizations();
- this.appView = appView;
- this.options = appView.options();
- this.optimizationOptions = appView.options().callSiteOptimizationOptions();
- if (Log.isLoggingEnabledFor(CallSiteOptimizationInfoPropagator.class)) {
- revisitedMethodsForTesting = ProgramMethodSet.create();
- }
- }
-
- public Mode getMode() {
- return mode;
- }
-
- public void logResults() {
- assert Log.ENABLED;
- if (revisitedMethodsForTesting != null) {
- Log.info(getClass(), "# of methods to revisit: %s", revisitedMethodsForTesting.size());
- for (ProgramMethod m : revisitedMethodsForTesting) {
- Log.info(
- getClass(),
- "%s: %s",
- m.toSourceString(),
- m.getDefinition().getCallSiteOptimizationInfo().toString());
- }
- }
- }
-
- public void collectCallSiteOptimizationInfo(IRCode code, Timing timing) {
- // TODO(b/139246447): we could collect call site optimization during REVISIT mode as well,
- // but that may require a separate copy of CallSiteOptimizationInfo.
- if (mode != Mode.COLLECT) {
- return;
- }
-
- ProgramMethod context = code.context();
- for (Instruction instruction : code.instructions()) {
- if (instruction.isInvokeMethod()) {
- collectCallSiteOptimizationInfoForInvokeMethod(
- instruction.asInvokeMethod(), context, timing);
- } else if (instruction.isInvokeCustom()) {
- collectCallSiteOptimizationInfoForInvokeCustom(instruction.asInvokeCustom());
- }
- }
- }
-
- private void collectCallSiteOptimizationInfoForInvokeMethod(
- InvokeMethod invoke, ProgramMethod context, Timing timing) {
- DexMethod invokedMethod = invoke.getInvokedMethod();
- SingleResolutionResult resolutionResult =
- appView
- .appInfo()
- .resolveMethod(invokedMethod, invoke.getInterfaceBit())
- .asSingleResolution();
- if (resolutionResult == null) {
- return;
- }
- // For virtual and interface calls, proceed on valid results only (since it's enforced).
- if (invoke.isInvokeMethodWithDynamicDispatch() && !resolutionResult.isVirtualTarget()) {
- return;
- }
- ProgramMethod resolutionTarget =
- resolutionResult.asSingleResolution().getResolutionPair().asProgramMethod();
- if (resolutionTarget == null || isMaybeClasspathOrLibraryMethodOverride(resolutionTarget)) {
- return;
- }
- propagateArgumentsToDispatchTargets(invoke, resolutionResult, context, timing);
- }
-
- private void collectCallSiteOptimizationInfoForInvokeCustom(InvokeCustom invoke) {
- // If the bootstrap method is program declared it will be called. The call is with runtime
- // provided arguments so ensure that the call-site info is TOP.
- DexMethodHandle bootstrapMethod = invoke.getCallSite().bootstrapMethod;
- SingleResolutionResult resolution =
- appView
- .appInfo()
- .resolveMethod(bootstrapMethod.asMethod(), bootstrapMethod.isInterface)
- .asSingleResolution();
- if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
- resolution.getResolvedMethod().joinCallSiteOptimizationInfo(top(), appView);
- }
- }
-
- private boolean isMaybeClasspathOrLibraryMethodOverride(ProgramMethod target) {
- // If the method overrides a library method, it is unsure how the method would be invoked by
- // that library.
- return target.getDefinition().isLibraryMethodOverride().isPossiblyTrue();
- }
-
- // Propagate information about the arguments to all possible dispatch targets of the invoke.
- private void propagateArgumentsToDispatchTargets(
- InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
- ProgramMethod context,
- Timing timing) {
- if (invoke.arguments().isEmpty()) {
- // Nothing to propagate.
- return;
- }
-
- if (invoke.arguments().size()
- != invoke.getInvokedMethod().getArity()
- + BooleanUtils.intValue(invoke.isInvokeMethodWithReceiver())) {
- // Verification error.
- assert false;
- return;
- }
-
- if (resolutionResult.getResolvedMethod().getCallSiteOptimizationInfo().isAbandoned()) {
- // We stopped tracking the arguments to all possible dispatch targets.
- assert verifyAllProgramDispatchTargetsHaveBeenAbandoned(invoke, context);
- return;
- }
-
- timing.begin("Lookup possible dispatch targets");
- ProgramMethodSet targets = invoke.lookupProgramDispatchTargets(appView, context);
- timing.end();
-
- assert invoke.isInvokeMethodWithDynamicDispatch()
- // For other invocation types, the size of targets should be at most one.
- || targets == null
- || targets.size() <= 1;
-
- if (targets == null || targets.isEmpty()) {
- return;
- }
-
- if (targets.size() > optimizationOptions.getMaxNumberOfDispatchTargetsBeforeAbandoning()) {
- // If the number of targets exceed the threshold, abandon call site optimization for all
- // targets.
- abandonCallSitePropagation(invoke, resolutionResult, targets, context);
- return;
- }
-
- timing.begin("Record arguments");
- // Lazily computed piece of information that needs to be propagated to all dispatch targets.
- LazyBox<CallSiteOptimizationInfo> callSiteOptimizationInfo =
- new LazyBox<>(() -> computeCallSiteOptimizationInfoFromArguments(invoke, context, timing));
- for (ProgramMethod target : targets) {
- CallSiteOptimizationInfo newCallSiteOptimizationInfo =
- propagateArgumentsToDispatchTarget(target, callSiteOptimizationInfo, timing);
-
- // If one of the targets is abandoned or ends up being abandoned, then abandon call site
- // optimization for all targets.
- if (newCallSiteOptimizationInfo.isAbandoned()) {
- abandonCallSitePropagation(invoke, resolutionResult, targets, context);
- break;
- }
- }
- timing.end();
- }
-
- private CallSiteOptimizationInfo propagateArgumentsToDispatchTarget(
- ProgramMethod target,
- LazyBox<CallSiteOptimizationInfo> lazyCallSiteOptimizationInfo,
- Timing timing) {
- CallSiteOptimizationInfo existingCallSiteOptimizationInfo =
- target.getDefinition().getCallSiteOptimizationInfo();
- if (existingCallSiteOptimizationInfo.isAbandoned()
- || existingCallSiteOptimizationInfo.isTop()) {
- return existingCallSiteOptimizationInfo;
- }
- if (!appView.appInfo().mayPropagateArgumentsTo(target)) {
- return top();
- }
- timing.begin("Join argument info");
- CallSiteOptimizationInfo callSiteOptimizationInfo =
- lazyCallSiteOptimizationInfo.computeIfAbsent();
- target
- .getDefinition()
- .joinCallSiteOptimizationInfo(
- callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, target)
- ? callSiteOptimizationInfo
- : top(),
- appView);
- timing.end();
- return target.getDefinition().getCallSiteOptimizationInfo();
- }
-
- private void abandonCallSitePropagation(
- InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
- ProgramMethodSet targets,
- ProgramMethod context) {
- if (invoke.isInvokeMethodWithDynamicDispatch()) {
- // When there is a dynamic dispatch, we may have used dynamic type information to reduce the
- // set of possible dispatch targets. However, it is an invariant that a method is marked as
- // abandoned if-and-only-if that method and all of its overrides have been marked as
- // abandoned. Therefore, we need to find all the overrides of the targeted method and mark
- // them as abandoned, which we accomplish by performing a lookup without any dynamic type
- // information.
- InvokeMethodWithReceiver invokeMethodWithReceiver = invoke.asInvokeMethodWithReceiver();
- if (invokeMethodWithReceiver.hasRefinedReceiverUpperBoundType(appView)
- || invokeMethodWithReceiver.hasRefinedReceiverLowerBoundType(appView)) {
- LookupResult lookupResult =
- resolutionResult.lookupVirtualDispatchTargets(context.getHolder(), appView.appInfo());
- // This should always succeed since we already looked up `targets` successfully.
- assert lookupResult.isLookupResultSuccess();
- abandonCallSitePropagation(
- consumer ->
- lookupResult.forEach(
- methodTarget -> {
- if (methodTarget.isProgramMethod()) {
- consumer.accept(methodTarget.asProgramMethod());
- } else {
- // This may happen if an interface method in the program is implemented
- // by a method in the classpath or library.
- assert invoke.isInvokeInterface();
- }
- },
- emptyConsumer()));
- return;
- }
- }
- abandonCallSitePropagation(targets::forEach);
- }
-
- private void abandonCallSitePropagation(ForEachable<ProgramMethod> methods) {
- if (InternalOptions.assertionsEnabled()) {
- synchronized (this) {
- methods.forEach(method -> method.getDefinition().abandonCallSiteOptimizationInfo());
- }
- } else {
- methods.forEach(method -> method.getDefinition().abandonCallSiteOptimizationInfo());
- }
- }
-
- public void abandonCallSitePropagationForLambdaImplementationMethods(
- ExecutorService executorService, Timing timing) throws ExecutionException {
- if (appView.options().isGeneratingClassFiles()) {
- timing.begin("Call site optimization: abandon lambda implementation methods");
- ForEachable<ProgramMethod> lambdaImplementationMethods =
- consumer ->
- appView
- .appInfo()
- .forEachMethod(
- method -> {
- if (appView
- .appInfo()
- .isMethodTargetedByInvokeDynamic(method.getReference())) {
- consumer.accept(method);
- }
- });
- ThreadUtils.processItems(
- lambdaImplementationMethods,
- this::abandonCallSitePropagationForMethodAndOverrides,
- executorService);
- timing.end();
- }
- }
-
- public void abandonCallSitePropagationForPinnedMethodsAndOverrides(
- ExecutorService executorService, Timing timing) throws ExecutionException {
- timing.begin("Call site optimization: abandon pinned methods");
- ThreadUtils.processItems(
- this::forEachPinnedNonPrivateVirtualMethod,
- this::abandonCallSitePropagationForMethodAndOverrides,
- executorService);
- timing.end();
- }
-
- private void forEachPinnedNonPrivateVirtualMethod(Consumer<ProgramMethod> consumer) {
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- for (ProgramMethod virtualProgramMethod : clazz.virtualProgramMethods()) {
- if (virtualProgramMethod.getDefinition().isNonPrivateVirtualMethod()
- && appView
- .getKeepInfo()
- .isPinned(virtualProgramMethod.getReference(), appView, options)) {
- consumer.accept(virtualProgramMethod);
- }
- }
- }
- }
-
- private void abandonCallSitePropagationForMethodAndOverrides(ProgramMethod method) {
- Set<ProgramMethod> abandonSet = Sets.newIdentityHashSet();
- if (method.getDefinition().isNonPrivateVirtualMethod()) {
- SingleResolutionResult resolutionResult =
- new SingleResolutionResult(
- method.getHolder(), method.getHolder(), method.getDefinition());
- resolutionResult
- .lookupVirtualDispatchTargets(method.getHolder(), appView.appInfo())
- .forEach(
- methodTarget -> {
- if (methodTarget.isProgramMethod()) {
- abandonSet.add(methodTarget.asProgramMethod());
- }
- },
- lambdaTarget -> {
- if (lambdaTarget.getImplementationMethod().isProgramMethod()) {
- abandonSet.add(lambdaTarget.getImplementationMethod().asProgramMethod());
- }
- });
- } else {
- abandonSet.add(method);
- }
- abandonCallSitePropagation(abandonSet::forEach);
- }
-
- private CallSiteOptimizationInfo computeCallSiteOptimizationInfoFromArguments(
- InvokeMethod invoke, ProgramMethod context, Timing timing) {
- timing.begin("Compute argument info");
- CallSiteOptimizationInfo callSiteOptimizationInfo =
- ConcreteCallSiteOptimizationInfo.fromArguments(
- appView, invoke.getInvokedMethod(), invoke.arguments(), context);
- if (callSiteOptimizationInfo.isTop()) {
- // If we are propagating unknown information to all call sites, then mark them as abandoned
- // such that we bail out before looking up the possible dispatch targets if we see any future
- // invokes to these methods.
- callSiteOptimizationInfo = abandoned();
- }
- timing.end();
- return callSiteOptimizationInfo;
- }
-
- public void enqueueMethodsForReprocessing(
- PostMethodProcessor.Builder postMethodProcessorBuilder) {
- postMethodProcessorBuilder
- .getMethodsToReprocessBuilder()
- .rewrittenWithLens(appView.graphLens())
- .merge(methodsToRevisit());
- }
-
- private LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToRevisit() {
- mode = Mode.REVISIT;
- GraphLens currentGraphLens = appView.graphLens();
- LongLivedProgramMethodSetBuilder<ProgramMethodSet> builder =
- LongLivedProgramMethodSetBuilder.createForIdentitySet(currentGraphLens);
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- clazz.forEachProgramMethodMatching(
- definition -> {
- assert !definition.isObsolete();
- if (definition.shouldNotHaveCode()
- || !definition.hasCode()
- || definition.getCode().isEmptyVoidMethod()) {
- return false;
- }
- // TODO(b/139246447): Assert no BOTTOM left.
- CallSiteOptimizationInfo callSiteOptimizationInfo =
- definition.getCallSiteOptimizationInfo();
- return callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, definition);
- },
- method -> {
- builder.add(method, currentGraphLens);
- appView.options().testing.callSiteOptimizationInfoInspector.accept(method);
- });
- }
- if (revisitedMethodsForTesting != null) {
- revisitedMethodsForTesting.addAll(builder.build(appView));
- }
- return builder;
- }
-
- private synchronized boolean verifyAllProgramDispatchTargetsHaveBeenAbandoned(
- InvokeMethod invoke, ProgramMethod context) {
- ProgramMethodSet targets = invoke.lookupProgramDispatchTargets(appView, context);
- if (targets != null) {
- for (ProgramMethod target : targets) {
- assert target.getDefinition().getCallSiteOptimizationInfo().isAbandoned()
- : "Expected method `"
- + target.toSourceString()
- + "` to be marked as abandoned (called from `"
- + invoke.toString()
- + "` in `"
- + context.toSourceString()
- + "`)";
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java
deleted file mode 100644
index a1dece6..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java
+++ /dev/null
@@ -1,25 +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.ir.optimize.info;
-
-/**
- * Used to represent that nothing is known about the argument values of a given method and all of
- * its overrides.
- */
-public class AbandonedCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
-
- private static final AbandonedCallSiteOptimizationInfo INSTANCE =
- new AbandonedCallSiteOptimizationInfo();
-
- private AbandonedCallSiteOptimizationInfo() {}
-
- static AbandonedCallSiteOptimizationInfo getInstance() {
- return INSTANCE;
- }
-
- @Override
- public boolean isAbandoned() {
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java
deleted file mode 100644
index 14d9dc0..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java
+++ /dev/null
@@ -1,22 +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.ir.optimize.info;
-
-// Nothing is known about arguments at call sites.
-public class BottomCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
-
- private static final BottomCallSiteOptimizationInfo INSTANCE =
- new BottomCallSiteOptimizationInfo();
-
- private BottomCallSiteOptimizationInfo() {}
-
- static BottomCallSiteOptimizationInfo getInstance() {
- return INSTANCE;
- }
-
- @Override
- public boolean isBottom() {
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
index ad274e9..3a9c9c0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
@@ -5,36 +5,18 @@
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.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
// A flat lattice structure:
// BOTTOM, TOP, and a lattice element that holds accumulated argument info.
public abstract class CallSiteOptimizationInfo {
- public static AbandonedCallSiteOptimizationInfo abandoned() {
- return AbandonedCallSiteOptimizationInfo.getInstance();
- }
-
- public static BottomCallSiteOptimizationInfo bottom() {
- return BottomCallSiteOptimizationInfo.getInstance();
- }
-
public static TopCallSiteOptimizationInfo top() {
return TopCallSiteOptimizationInfo.getInstance();
}
- public boolean isAbandoned() {
- return false;
- }
-
- public boolean isBottom() {
- return false;
- }
-
public boolean isTop() {
return false;
}
@@ -49,13 +31,10 @@
public CallSiteOptimizationInfo join(
CallSiteOptimizationInfo other, AppView<?> appView, DexEncodedMethod method) {
- if (isAbandoned() || other.isAbandoned()) {
- return abandoned();
- }
- if (isBottom() || other.isTop()) {
+ if (other.isTop()) {
return other;
}
- if (isTop() || other.isBottom()) {
+ if (isTop()) {
return this;
}
assert isConcreteCallSiteOptimizationInfo() && other.isConcreteCallSiteOptimizationInfo();
@@ -63,19 +42,10 @@
.join(other.asConcreteCallSiteOptimizationInfo(), appView, method);
}
- /**
- * {@link CallSiteOptimizationInfoPropagator} will reprocess the call target if its collected call
- * site optimization info has something useful that can trigger more optimizations. For example,
- * if a certain argument is guaranteed to be definitely not null for all call sites, null-check on
- * that argument can be simplified during the reprocessing of the method.
- */
public boolean hasUsefulOptimizationInfo(AppView<?> appView, DexEncodedMethod method) {
return false;
}
- public final boolean hasUsefulOptimizationInfo(AppView<?> appView, ProgramMethod method) {
- return hasUsefulOptimizationInfo(appView, method.getDefinition());
- }
// The index exactly matches with in values of invocation, i.e., even including receiver.
public TypeElement getDynamicUpperBoundType(int argIndex) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 24d3ad4..7bc6c1c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -59,6 +59,11 @@
}
@Override
+ public CallSiteOptimizationInfo getArgumentInfos() {
+ return CallSiteOptimizationInfo.top();
+ }
+
+ @Override
public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
return ClassInlinerMethodConstraint.alwaysFalse();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 08ef5d9..3de6a7a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -32,6 +32,8 @@
public abstract boolean classInitializerMayBePostponed();
+ public abstract CallSiteOptimizationInfo getArgumentInfos();
+
public abstract ClassInlinerMethodConstraint getClassInlinerMethodConstraint();
public abstract EnumUnboxerMethodClassification getEnumUnboxerMethodClassification();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 06a25cf..7bda54d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -35,6 +35,7 @@
public class MutableMethodOptimizationInfo extends MethodOptimizationInfo
implements MutableOptimizationInfo {
+ private CallSiteOptimizationInfo argumentInfos = CallSiteOptimizationInfo.top();
private Set<DexType> initializedClassesOnNormalExit =
DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
private int returnedArgument = DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT;
@@ -144,6 +145,7 @@
// Copy constructor used to create a mutable copy. Do not forget to copy from template when a new
// field is added.
private MutableMethodOptimizationInfo(MutableMethodOptimizationInfo template) {
+ argumentInfos = template.argumentInfos;
flags = template.flags;
initializedClassesOnNormalExit = template.initializedClassesOnNormalExit;
returnedArgument = template.returnedArgument;
@@ -162,7 +164,8 @@
public MutableMethodOptimizationInfo fixup(
AppView<AppInfoWithLiveness> appView, MethodOptimizationInfoFixer fixer) {
- return fixupBridgeInfo(fixer)
+ return fixupArgumentInfos(fixer)
+ .fixupBridgeInfo(fixer)
.fixupClassInlinerMethodConstraint(appView, fixer)
.fixupEnumUnboxerMethodClassification(fixer)
.fixupInstanceInitializerInfo(appView, fixer)
@@ -257,6 +260,23 @@
}
@Override
+ public CallSiteOptimizationInfo getArgumentInfos() {
+ return argumentInfos;
+ }
+
+ public MutableMethodOptimizationInfo fixupArgumentInfos(MethodOptimizationInfoFixer fixer) {
+ if (argumentInfos.isConcreteCallSiteOptimizationInfo()) {
+ argumentInfos =
+ fixer.fixupCallSiteOptimizationInfo(argumentInfos.asConcreteCallSiteOptimizationInfo());
+ }
+ return this;
+ }
+
+ void setArgumentInfos(CallSiteOptimizationInfo argumentInfos) {
+ this.argumentInfos = argumentInfos;
+ }
+
+ @Override
public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
return classInlinerConstraint;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 77615e2..40a6cdd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -154,6 +154,10 @@
// Ignored.
}
+ public void setArgumentInfos(ProgramMethod method, CallSiteOptimizationInfo argumentInfos) {
+ method.getDefinition().getMutableOptimizationInfo().setArgumentInfos(argumentInfos);
+ }
+
@Override
public void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {
method.getMutableOptimizationInfo().setBridgeInfo(bridgeInfo);
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index baa2951..9cd5455 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -31,7 +31,7 @@
if (options.isMinifying()) {
String renaming = getRenameSourceFileAttribute(options);
if (renaming != null) {
- return rewriteTo(renaming, isDefault(renaming, options));
+ return rewriteTo(renaming, isDefaultOrEmpty(renaming, options));
}
}
return null;
@@ -40,7 +40,7 @@
private static SourceFileProvider computeNonCompatProvider(InternalOptions options) {
String renaming = getRenameSourceFileAttribute(options);
if (renaming != null) {
- return rewriteTo(renaming, isDefault(renaming, options));
+ return rewriteTo(renaming, isDefaultOrEmpty(renaming, options));
}
if (options.isMinifying() || options.isOptimizing()) {
return rewriteToDefaultSourceFile(options.dexItemFactory());
@@ -52,8 +52,9 @@
return options.getProguardConfiguration().getRenameSourceFileAttribute();
}
- public static boolean isDefault(String sourceFile, InternalOptions options) {
- return options.dexItemFactory().defaultSourceFileAttributeString.equals(sourceFile);
+ public static boolean isDefaultOrEmpty(String sourceFile, InternalOptions options) {
+ return sourceFile.isEmpty()
+ || options.dexItemFactory().defaultSourceFileAttributeString.equals(sourceFile);
}
private static SourceFileProvider rewriteToDefaultSourceFile(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index e63eb5e..4f56738 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -43,7 +43,7 @@
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert !previous.hasCastType();
+ assert !previous.hasReadCastType();
assert !previous.hasReboundReference();
return FieldLookupResult.builder(this)
.setReference(previous.getReference())
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 0e6b99f..8d8962e 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -56,7 +56,7 @@
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert !previous.hasCastType();
+ assert !previous.hasReadCastType();
assert !previous.hasReboundReference();
return FieldLookupResult.builder(this)
.setReference(previous.getReference())
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 8861f6c..62bf247 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -96,7 +96,7 @@
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert !previous.hasCastType();
+ assert !previous.hasReadCastType();
assert !previous.hasReboundReference();
return FieldLookupResult.builder(this)
.setReference(previous.getReference())
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index efa1985..772a404 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -79,7 +79,7 @@
DexEncodedMethod::hasCode,
method -> {
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
&& !appView.appInfo().isNeverReprocessMethod(method)) {
methodsToReprocessBuilder.add(method, currentGraphLens);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 04afa1c..81df33e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.optimize.argumentpropagation;
+import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethodSignature;
@@ -227,9 +228,9 @@
}
ConcreteMonomorphicMethodState finalMethodState = widenedMethodState.asMonomorphic();
- method
- .getDefinition()
- .setCallSiteOptimizationInfo(
+ getSimpleFeedback()
+ .setArgumentInfos(
+ method,
ConcreteCallSiteOptimizationInfo.fromMethodState(appView, method, finalMethodState));
// Strengthen the return value of the method if the method is known to return one of the
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 51120e1..8e7380d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -412,8 +412,7 @@
// OK, this parameter can be removed.
continue;
}
- CallSiteOptimizationInfo optimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ CallSiteOptimizationInfo optimizationInfo = method.getOptimizationInfo().getArgumentInfos();
if (optimizationInfo.isConcreteCallSiteOptimizationInfo()) {
ConcreteCallSiteOptimizationInfo concreteOptimizationInfo =
optimizationInfo.asConcreteCallSiteOptimizationInfo();
@@ -620,7 +619,7 @@
private ArgumentInfoCollection computeParameterChangesForMethod(
ProgramMethod method, IntPredicate removableParameterIndices) {
ConcreteCallSiteOptimizationInfo optimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo().asConcreteCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
if (optimizationInfo == null) {
return ArgumentInfoCollection.empty();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 227cbf7..a5b539b 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -220,14 +220,6 @@
}
private void setMaxApiReferenceLevel(DexReference reference) {
- if (reference.isDexMember()) {
- maxApiReferenceLevel =
- maxApiReferenceLevel.max(
- apiLevelCompute.computeApiLevelForDefinition(
- reference.asDexMember(),
- appView.dexItemFactory(),
- apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
- }
maxApiReferenceLevel =
maxApiReferenceLevel.max(
apiLevelCompute.computeApiLevelForLibraryReference(
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 9f30d38..2121adc 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -517,6 +517,10 @@
return appView.appInfo();
}
+ public AndroidApiLevelCompute getApiLevelCompute() {
+ return apiLevelCompute;
+ }
+
public Mode getMode() {
return mode;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index d5c8c68..7292248 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -71,15 +71,17 @@
ifRules.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRuleEntry = it.next();
- ProguardIfRule ifRule = ifRuleEntry.getKey().get();
+ ProguardIfRule ifRuleKey = ifRuleEntry.getKey().get();
+ Set<ProguardIfRule> ifRulesInEquivalence = ifRuleEntry.getValue();
ProguardIfRuleEvaluationData ifRuleEvaluationData =
appView.options().testing.proguardIfRuleEvaluationData;
+ List<ProguardIfRule> toRemove = new ArrayList<>();
// Depending on which types that trigger the -if rule, the application of the subsequent
// -keep rule may vary (due to back references). So, we need to try all pairs of -if
// rule and live types.
for (DexProgramClass clazz :
- ifRule.relevantCandidatesForRule(
+ ifRuleKey.relevantCandidatesForRule(
appView, subtypingInfo, appView.appInfo().classes())) {
if (!isEffectivelyLive(clazz)) {
continue;
@@ -89,21 +91,21 @@
if (appView.options().testing.measureProguardIfRuleEvaluations) {
ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
}
- if (evaluateClassForIfRule(ifRule, clazz)) {
+ if (evaluateClassForIfRule(ifRuleKey, clazz)) {
// When matching an if rule against a type, the if-rule are filled with the current
// capture of wildcards. Propagate this down to member rules with same class part
// equivalence.
- ifRuleEntry
- .getValue()
- .removeIf(
- memberRule -> {
- registerClassCapture(memberRule, clazz, clazz);
- if (appView.options().testing.measureProguardIfRuleEvaluations) {
- ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
- }
- return evaluateIfRuleMembersAndMaterialize(memberRule, clazz, clazz)
- && canRemoveSubsequentKeepRule(memberRule);
- });
+ ifRulesInEquivalence.forEach(
+ ifRule -> {
+ registerClassCapture(ifRule, clazz, clazz);
+ if (appView.options().testing.measureProguardIfRuleEvaluations) {
+ ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
+ }
+ boolean matched = evaluateIfRuleMembersAndMaterialize(ifRule, clazz, clazz);
+ if (matched && canRemoveSubsequentKeepRule(ifRule)) {
+ toRemove.add(ifRule);
+ }
+ });
}
// Check if one of the types that have been merged into `clazz` satisfies the if-rule.
@@ -123,25 +125,26 @@
if (appView.options().testing.measureProguardIfRuleEvaluations) {
ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
}
- if (evaluateClassForIfRule(ifRule, sourceClass)) {
- ifRuleEntry
- .getValue()
- .removeIf(
- memberRule -> {
- registerClassCapture(memberRule, sourceClass, clazz);
- if (appView.options().testing.measureProguardIfRuleEvaluations) {
- ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
- }
- return evaluateIfRuleMembersAndMaterialize(
- memberRule, sourceClass, clazz)
- && canRemoveSubsequentKeepRule(memberRule);
- });
+ if (evaluateClassForIfRule(ifRuleKey, sourceClass)) {
+ ifRulesInEquivalence.forEach(
+ ifRule -> {
+ registerClassCapture(ifRule, sourceClass, clazz);
+ if (appView.options().testing.measureProguardIfRuleEvaluations) {
+ ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
+ }
+ if (evaluateIfRuleMembersAndMaterialize(ifRule, sourceClass, clazz)
+ && canRemoveSubsequentKeepRule(ifRule)) {
+ toRemove.add(ifRule);
+ }
+ });
}
}
}
}
- if (ifRuleEntry.getValue().isEmpty()) {
+ if (ifRulesInEquivalence.size() == toRemove.size()) {
it.remove();
+ } else if (!toRemove.isEmpty()) {
+ ifRulesInEquivalence.removeAll(toRemove);
}
}
ThreadUtils.awaitFutures(futures);
@@ -153,10 +156,7 @@
}
private boolean canRemoveSubsequentKeepRule(ProguardIfRule rule) {
- // We cannot remove an if-rule if there is a kept graph consumer, otherwise we would not record
- // all edges.
- return Iterables.isEmpty(rule.subsequentRule.getWildcards())
- && appView.options().keptGraphConsumer == null;
+ return Iterables.isEmpty(rule.subsequentRule.getWildcards());
}
/**
@@ -227,18 +227,27 @@
return true;
}
+ List<DexClassAndField> fieldsInlinedByJavaC = new ArrayList<>();
Set<DexDefinition> filteredMembers = Sets.newIdentityHashSet();
Iterables.addAll(
filteredMembers,
targetClass.fields(
- f ->
- // Fields referenced only by -keep may not be referenced, we therefore have to
- // filter on both live and referenced.
- (enqueuer.isFieldLive(f)
- || enqueuer.isFieldReferenced(f)
- || f.getOptimizationInfo().valueHasBeenPropagated())
- && appView.graphLens().getOriginalFieldSignature(f.getReference()).holder
- == sourceClass.type));
+ f -> {
+ // Fields that are javac inlined are unsound as predicates for conditional rules.
+ // Ignore any such field members and record it for possible reporting later.
+ if (isFieldInlinedByJavaC(f)) {
+ f.markAsInlinableByJavaC();
+ fieldsInlinedByJavaC.add(DexClassAndField.create(targetClass, f));
+ return false;
+ }
+ // Fields referenced only by -keep may not be referenced, we therefore have to
+ // filter on both live and referenced.
+ return (enqueuer.isFieldLive(f)
+ || enqueuer.isFieldReferenced(f)
+ || f.getOptimizationInfo().valueHasBeenPropagated())
+ && (appView.graphLens().getOriginalFieldSignature(f.getReference()).holder
+ == sourceClass.type);
+ }));
Iterables.addAll(
filteredMembers,
targetClass.methods(
@@ -249,6 +258,21 @@
&& appView.graphLens().getOriginalMethodSignature(m.getReference()).holder
== sourceClass.type));
+ // Check if the rule could hypothetically have matched a javac inlined field.
+ // If so mark the rule. Reporting happens only if the rule is otherwise unused.
+ if (!fieldsInlinedByJavaC.isEmpty()) {
+ for (ProguardMemberRule memberRule : memberKeepRules) {
+ if (!memberRule.getRuleType().includesFields()) {
+ continue;
+ }
+ for (DexClassAndField field : fieldsInlinedByJavaC) {
+ if (rootSetBuilder.sideEffectFreeIsRuleSatisfiedByField(memberRule, field)) {
+ rule.addInlinableFieldMatchingPrecondition(field.getReference());
+ }
+ }
+ }
+ }
+
// If the number of member rules to hold is more than live members, we can't make it.
if (filteredMembers.size() < memberKeepRules.size()) {
return false;
@@ -258,6 +282,7 @@
// -keep rule may vary (due to back references). So, we need to try literally all
// combinations of live members. But, we can at least limit the number of elements per
// combination as the size of member rules to satisfy.
+ // TODO(b/206086945): Consider ways of reducing the size of this set computation.
for (Set<DexDefinition> combination :
Sets.combinations(filteredMembers, memberKeepRules.size())) {
Collection<DexClassAndField> fieldsInCombination =
@@ -287,6 +312,27 @@
return false;
}
+ private boolean isFieldInlinedByJavaC(DexEncodedField field) {
+ if (enqueuer.getMode().isFinalTreeShaking()) {
+ // Ignore any field value in the final tree shaking pass so it remains consistent with the
+ // initial pass.
+ return field.isInlinableByJavaC();
+ }
+ if (!field.isStatic() || !field.isFinal()) {
+ return false;
+ }
+ if (!field.hasExplicitStaticValue()) {
+ return false;
+ }
+ if (field.getType().isPrimitiveType()) {
+ return true;
+ }
+ if (field.getType() != appView.dexItemFactory().stringType) {
+ return false;
+ }
+ return field.getStaticValue().isDexValueString();
+ }
+
private void materializeIfRule(ProguardIfRule rule, Set<DexReference> preconditions) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
ProguardIfRule materializedRule = rule.materialize(dexItemFactory, preconditions);
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
index 0c54589..140b86b 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
@@ -24,8 +24,11 @@
return bottom().joiner();
}
+ private final boolean allowFieldTypeStrengthening;
+
private KeepFieldInfo(Builder builder) {
super(builder);
+ this.allowFieldTypeStrengthening = builder.isFieldTypeStrengtheningAllowed();
}
// This builder is not private as there are known instances where it is safe to modify keep info
@@ -35,6 +38,14 @@
return new Builder(this);
}
+ public boolean isFieldTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
+ return internalIsFieldTypeStrengtheningAllowed();
+ }
+
+ boolean internalIsFieldTypeStrengtheningAllowed() {
+ return allowFieldTypeStrengthening;
+ }
+
public Joiner joiner() {
assert !isTop();
return new Joiner(this);
@@ -52,12 +63,32 @@
public static class Builder extends KeepInfo.Builder<Builder, KeepFieldInfo> {
+ private boolean allowFieldTypeStrengthening;
+
private Builder() {
super();
}
private Builder(KeepFieldInfo original) {
super(original);
+ allowFieldTypeStrengthening = original.internalIsFieldTypeStrengtheningAllowed();
+ }
+
+ public boolean isFieldTypeStrengtheningAllowed() {
+ return allowFieldTypeStrengthening;
+ }
+
+ public Builder setAllowFieldTypeStrengthening(boolean allowFieldTypeStrengthening) {
+ this.allowFieldTypeStrengthening = allowFieldTypeStrengthening;
+ return self();
+ }
+
+ public Builder allowFieldTypeStrengthening() {
+ return setAllowFieldTypeStrengthening(true);
+ }
+
+ public Builder disallowFieldTypeStrengthening() {
+ return setAllowFieldTypeStrengthening(false);
}
@Override
@@ -92,6 +123,11 @@
super(info.builder());
}
+ public Joiner disallowFieldTypeStrengthening() {
+ builder.disallowFieldTypeStrengthening();
+ return self();
+ }
+
@Override
public Joiner asFieldJoiner() {
return this;
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 12265aa..598f6a7 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -239,7 +239,7 @@
abstract boolean isEqualTo(K other);
- private K original;
+ protected K original;
private boolean allowAccessModification;
private boolean allowAnnotationRemoval;
private boolean allowMinification;
@@ -439,14 +439,14 @@
abstract J self();
- final Builder<B, K> builder;
+ final B builder;
// The set of rules that have contributed to this joiner. These are only needed for the
// interpretation of keep rules into keep info, and is therefore not stored in the keep info
// builder above.
final Set<ProguardKeepRuleBase> rules = Sets.newIdentityHashSet();
- Joiner(Builder<B, K> builder) {
+ Joiner(B builder) {
this.builder = builder;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoFieldTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoFieldTypeStrengtheningRule.java
new file mode 100644
index 0000000..80cf0db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoFieldTypeStrengtheningRule.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoFieldTypeStrengtheningRule extends ProguardConfigurationRule {
+
+ public static final String RULE_NAME = "nofieldtypestrengthening";
+
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<NoFieldTypeStrengtheningRule, Builder> {
+
+ private Builder() {
+ super();
+ }
+
+ @Override
+ public NoFieldTypeStrengtheningRule.Builder self() {
+ return this;
+ }
+
+ @Override
+ public NoFieldTypeStrengtheningRule build() {
+ return new NoFieldTypeStrengtheningRule(
+ origin,
+ getPosition(),
+ source,
+ buildClassAnnotations(),
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ buildInheritanceAnnotations(),
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+ }
+
+ private NoFieldTypeStrengtheningRule(
+ Origin origin,
+ Position position,
+ String source,
+ List<ProguardTypeMatcher> classAnnotations,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ List<ProguardTypeMatcher> inheritanceAnnotations,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotations,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotations,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return RULE_NAME;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index f86a251..b92040b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -480,6 +480,11 @@
configurationBuilder.addRule(rule);
return true;
}
+ if (acceptString(NoFieldTypeStrengtheningRule.RULE_NAME)) {
+ ProguardConfigurationRule rule = parseNoFieldTypeStrengtheningRule(optionStart);
+ configurationBuilder.addRule(rule);
+ return true;
+ }
if (acceptString(NoUnusedInterfaceRemovalRule.RULE_NAME)) {
ProguardConfigurationRule rule = parseNoUnusedInterfaceRemovalRule(optionStart);
configurationBuilder.addRule(rule);
@@ -779,6 +784,17 @@
return keepRuleBuilder.build();
}
+ private NoFieldTypeStrengtheningRule parseNoFieldTypeStrengtheningRule(Position start)
+ throws ProguardRuleParserException {
+ NoFieldTypeStrengtheningRule.Builder keepRuleBuilder =
+ NoFieldTypeStrengtheningRule.builder().setOrigin(origin).setStart(start);
+ parseClassSpec(keepRuleBuilder, false);
+ Position end = getPosition();
+ keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+ keepRuleBuilder.setEnd(end);
+ return keepRuleBuilder.build();
+ }
+
private NoUnusedInterfaceRemovalRule parseNoUnusedInterfaceRemovalRule(Position start)
throws ProguardRuleParserException {
NoUnusedInterfaceRemovalRule.Builder keepRuleBuilder =
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 8313325..f61b4ee 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.origin.Origin;
@@ -10,7 +11,9 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class ProguardIfRule extends ProguardKeepRuleBase {
@@ -34,6 +37,8 @@
private final Set<DexReference> preconditions;
final ProguardKeepRule subsequentRule;
+ private Map<DexField, DexField> inlinableFieldsInPrecondition = new ConcurrentHashMap<>();
+
public Set<DexReference> getPreconditions() {
return preconditions;
}
@@ -42,6 +47,16 @@
return subsequentRule;
}
+ public void addInlinableFieldMatchingPrecondition(DexField field) {
+ inlinableFieldsInPrecondition.put(field, field);
+ }
+
+ public Set<DexField> getAndClearInlinableFieldsMatchingPrecondition() {
+ Set<DexField> fields = inlinableFieldsInPrecondition.keySet();
+ inlinableFieldsInPrecondition = null;
+ return fields;
+ }
+
public static class Builder extends ProguardKeepRuleBase.Builder<ProguardIfRule, Builder> {
ProguardKeepRule subsequentRule = null;
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 03cff10..3644e3d 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.AssumeNoSideEffectsRuleForObjectMembersDiagnostic;
+import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -262,6 +263,8 @@
markMatchingOverriddenMethods(
appView.appInfo(), clazz, memberKeepRules, rule, null, true, ifRule);
markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
+ } else if (rule instanceof NoFieldTypeStrengtheningRule) {
+ markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
} else if (rule instanceof InlineRule
|| rule instanceof ConstantArgumentRule
|| rule instanceof UnusedArgumentRule
@@ -957,6 +960,10 @@
return false;
}
+ boolean sideEffectFreeIsRuleSatisfiedByField(ProguardMemberRule rule, DexClassAndField field) {
+ return rule.matches(field, appView, ignore -> {}, dexStringCache);
+ }
+
static AnnotationMatchResult containsAllAnnotations(
List<ProguardTypeMatcher> annotationMatchers, DexClass clazz) {
return containsAllAnnotations(
@@ -1220,6 +1227,13 @@
throw new Unreachable();
}
context.markAsUsed();
+ } else if (context instanceof NoFieldTypeStrengtheningRule) {
+ assert item.isProgramField();
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+ .asFieldJoiner()
+ .disallowFieldTypeStrengthening();
+ context.markAsUsed();
} else if (context instanceof NoUnusedInterfaceRemovalRule) {
noUnusedInterfaceRemoval.add(item.asClass().type);
context.markAsUsed();
@@ -1675,17 +1689,26 @@
public void checkAllRulesAreUsed(InternalOptions options) {
List<ProguardConfigurationRule> rules = options.getProguardConfiguration().getRules();
- if (rules != null) {
- for (ProguardConfigurationRule rule : rules) {
- if (!rule.isUsed()) {
- String message =
- "Proguard configuration rule does not match anything: `" + rule.toString() + "`";
- StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
- if (options.testing.reportUnusedProguardConfigurationRules) {
- options.reporter.info(diagnostic);
- }
+ if (rules == null) {
+ return;
+ }
+ for (ProguardConfigurationRule rule : rules) {
+ if (rule.isProguardIfRule()) {
+ ProguardIfRule ifRule = rule.asProguardIfRule();
+ Set<DexField> unorderedFields = ifRule.getAndClearInlinableFieldsMatchingPrecondition();
+ if (!unorderedFields.isEmpty()) {
+ List<DexField> fields = new ArrayList<>(unorderedFields);
+ fields.sort(DexField::compareTo);
+ options.reporter.warning(
+ new InlinableStaticFinalFieldPreconditionDiagnostic(ifRule, fields));
+ continue;
}
}
+ if (!rule.isUsed() && options.testing.reportUnusedProguardConfigurationRules) {
+ String message = "Proguard configuration rule does not match anything: `" + rule + "`";
+ StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
+ options.reporter.info(diagnostic);
+ }
}
}
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 9396f50..9822a54 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
+import static com.android.tools.r8.utils.AndroidApiLevelUtils.getApiReferenceLevelForMerging;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
@@ -531,8 +532,10 @@
// Only merge if api reference level of source class is equal to target class. The check is
// somewhat expensive.
if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
- ComputedApiLevel sourceApiLevel = sourceClass.getApiReferenceLevel(appView, apiLevelCompute);
- ComputedApiLevel targetApiLevel = targetClass.getApiReferenceLevel(appView, apiLevelCompute);
+ ComputedApiLevel sourceApiLevel =
+ getApiReferenceLevelForMerging(appView, apiLevelCompute, sourceClass);
+ ComputedApiLevel targetApiLevel =
+ getApiReferenceLevelForMerging(appView, apiLevelCompute, targetClass);
if (!sourceApiLevel.equals(targetApiLevel)) {
if (Log.ENABLED) {
AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 73cf09f..089ccce 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -6,6 +6,9 @@
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.ProgramMethod;
@@ -19,12 +22,38 @@
if (caller.getHolderType() == inlinee.getHolderType()) {
return true;
}
+ // For inlining we only measure if the code has invokes into the library.
return caller
.getDefinition()
- .getApiLevel()
+ .getApiLevelForCode()
.isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode());
}
+ public static ComputedApiLevel getApiReferenceLevelForMerging(
+ AppView<?> appView, AndroidApiLevelCompute apiLevelCompute, DexProgramClass clazz) {
+ // The api level of a class is the max level of it's members, super class and interfaces.
+ return getMembersApiReferenceLevelForMerging(
+ clazz,
+ apiLevelCompute.computeApiLevelForDefinition(
+ clazz.allImmediateSupertypes(), apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
+ }
+
+ private static ComputedApiLevel getMembersApiReferenceLevelForMerging(
+ DexProgramClass clazz, ComputedApiLevel memberLevel) {
+ // Based on b/138781768#comment57 there is almost no penalty for having an unknown reference
+ // as long as we are not invoking or accessing a field on it. Therefore we can disregard static
+ // types of fields and only consider method code api levels.
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.hasCode()) {
+ memberLevel = memberLevel.max(method.getApiLevelForCode());
+ }
+ if (memberLevel.isUnknownApiLevel()) {
+ return memberLevel;
+ }
+ }
+ return memberLevel;
+ }
+
public static boolean isApiSafeForMemberRebinding(
LibraryMethod method,
AndroidApiLevelCompute androidApiLevelCompute,
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 1cbbc21..de70549 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -735,6 +735,9 @@
public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
+ // TODO(b/207765416): Enable and remove this once fixed.
+ public boolean enablePcBasedMappingFile = false;
+
public CallSiteOptimizationOptions callSiteOptimizationOptions() {
return callSiteOptimizationOptions;
}
@@ -1863,7 +1866,9 @@
}
public boolean canUseDexPcAsDebugInformation() {
- return lineNumberOptimization == LineNumberOptimization.ON && hasMinApi(AndroidApiLevel.O);
+ return enablePcBasedMappingFile
+ && lineNumberOptimization == LineNumberOptimization.ON
+ && hasMinApi(AndroidApiLevel.O);
}
public boolean isInterfaceMethodDesugaringEnabled() {
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 94ceeb4..3cd4941 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -61,10 +61,11 @@
KOTLINC_1_3_72("kotlin-compiler-1.3.72"),
KOTLINC_1_4_20("kotlin-compiler-1.4.20"),
KOTLINC_1_5_0("kotlin-compiler-1.5.0"),
+ KOTLINC_1_6_0("kotlin-compiler-1.6.0"),
KOTLIN_DEV("kotlin-compiler-dev");
public static final KotlinCompilerVersion MIN_SUPPORTED_VERSION = KOTLINC_1_4_20;
- public static final KotlinCompilerVersion MAX_SUPPORTED_VERSION = KOTLINC_1_5_0;
+ public static final KotlinCompilerVersion MAX_SUPPORTED_VERSION = KOTLINC_1_6_0;
private final String folder;
@@ -79,6 +80,15 @@
public KotlinCompiler getCompiler() {
return new KotlinCompiler(this);
}
+
+ public static List<KotlinCompilerVersion> getSupported() {
+ return Arrays.stream(KotlinCompilerVersion.values())
+ .filter(
+ compiler ->
+ compiler.isGreaterThanOrEqualTo(MIN_SUPPORTED_VERSION)
+ && compiler.isLessThanOrEqualTo(MAX_SUPPORTED_VERSION))
+ .collect(Collectors.toList());
+ }
}
public static final class KotlinCompiler {
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
index 7996dc4..2ac3f87 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static org.junit.Assume.assumeTrue;
+
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
@@ -69,6 +71,8 @@
private Predicate<KotlinTargetVersion> targetVersionFilter = t -> false;
private boolean withDevCompiler =
System.getProperty("com.android.tools.r8.kotlincompilerdev") != null;
+ private boolean withOldCompilers =
+ System.getProperty("com.android.tools.r8.kotlincompilerold") != null;
private Builder() {}
@@ -101,6 +105,11 @@
return this;
}
+ public Builder withOldCompilersIfSet() {
+ assumeTrue(withOldCompilers);
+ return this;
+ }
+
public Builder withAllTargetVersions() {
withTargetVersionFilter(t -> t != KotlinTargetVersion.NONE);
return this;
@@ -126,10 +135,16 @@
List<KotlinCompilerVersion> compilerVersions;
if (withDevCompiler) {
compilerVersions = ImmutableList.of(KotlinCompilerVersion.KOTLIN_DEV);
- } else {
+ } else if (withOldCompilers) {
compilerVersions =
Arrays.stream(KotlinCompilerVersion.values())
- .filter(c -> c != KotlinCompilerVersion.KOTLIN_DEV && compilerFilter.test(c))
+ .filter(c -> c.isLessThan(KotlinCompilerVersion.MIN_SUPPORTED_VERSION))
+ .collect(Collectors.toList());
+
+ } else {
+ compilerVersions =
+ KotlinCompilerVersion.getSupported().stream()
+ .filter(c -> compilerFilter.test(c))
.collect(Collectors.toList());
}
for (KotlinCompilerVersion kotlinVersion : compilerVersions) {
diff --git a/src/test/java/com/android/tools/r8/NoFieldTypeStrengthening.java b/src/test/java/com/android/tools/r8/NoFieldTypeStrengthening.java
new file mode 100644
index 0000000..af7d5bd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoFieldTypeStrengthening.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD})
+public @interface NoFieldTypeStrengthening {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 6c8ac51..92270ae 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
@@ -27,6 +28,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import java.io.IOException;
+import java.lang.annotation.Annotation;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
@@ -429,6 +431,11 @@
.addInternalKeepRules("-neverclassinline @com.android.tools.r8.NeverClassInline class *");
}
+ T addInternalMatchAnnotationOnFieldRule(String name, Class<? extends Annotation> annotation) {
+ return addInternalKeepRules(
+ "-" + name + " class * { @" + annotation.getTypeName() + " <fields>; }");
+ }
+
T addInternalMatchInterfaceRule(String name, Class<?> matchInterface) {
return addInternalKeepRules("-" + name + " @" + matchInterface.getTypeName() + " class *");
}
@@ -489,6 +496,12 @@
return addOptionsModification(options -> options.testing.allowInliningOfSynthetics = false);
}
+ public T enableNoFieldTypeStrengtheningAnnotations() {
+ return addNoFieldTypeStrengtheningAnnotation()
+ .addInternalMatchAnnotationOnFieldRule(
+ NoFieldTypeStrengtheningRule.RULE_NAME, NoFieldTypeStrengthening.class);
+ }
+
public T enableNoUnusedInterfaceRemovalAnnotations() {
return addNoUnusedInterfaceRemovalAnnotations()
.addInternalMatchInterfaceRule(
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 04eb3c8..c53ec8c 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -88,15 +88,15 @@
public abstract TestDiagnosticMessages assertErrorsCount(int count);
public final TestDiagnosticMessages assertNoInfos() {
- return assertInfosCount(0);
+ return assertInfosMatch(Collections.emptyList());
}
public final TestDiagnosticMessages assertNoWarnings() {
- return assertWarningsCount(0);
+ return assertWarningsMatch(Collections.emptyList());
}
public final TestDiagnosticMessages assertNoErrors() {
- return assertErrorsCount(0);
+ return assertErrorsMatch(Collections.emptyList());
}
// Match exact.
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index acd96aa..7fc1ecc 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -119,6 +119,24 @@
return addKeepRules("-dontshrink");
}
+ public T addDontNote(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ addDontNote(clazz.getTypeName());
+ }
+ return self();
+ }
+
+ public T addDontNote(Collection<String> classes) {
+ for (String clazz : classes) {
+ addKeepRules("-dontnote " + clazz);
+ }
+ return self();
+ }
+
+ public T addDontNote(String... classes) {
+ return addDontNote(Arrays.asList(classes));
+ }
+
public T addDontWarn(Class<?>... classes) {
for (Class<?> clazz : classes) {
addDontWarn(clazz.getTypeName());
@@ -440,6 +458,10 @@
return addTestingAnnotation(NeverSingleCallerInline.class);
}
+ public final T addNoFieldTypeStrengtheningAnnotation() {
+ return addTestingAnnotation(NoFieldTypeStrengthening.class);
+ }
+
public final T addNoHorizontalClassMergingAnnotations() {
return addTestingAnnotation(NoHorizontalClassMerging.class);
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
index 7c4226c..c41c5e2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -44,14 +44,7 @@
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.addHorizontallyMergedClassesInspector(
- inspector -> {
- if (parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
- inspector.assertClassesMerged(A.class, B.class);
- } else {
- inspector.assertNoClassesMerged();
- }
- })
+ inspector -> inspector.assertClassesMerged(A.class, B.class))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::disableCheckAllApiReferencesAreNotUnknown)
.apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
index 149c93e..302cca5 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldTypeReferenceTest.java
@@ -53,9 +53,9 @@
if (Reference.methodFromMethod(readApiField).equals(method)
|| Reference.methodFromMethod(setApiField).equals(method)) {
if (parameters.isCfRuntime()) {
- assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+ assertEquals(AndroidApiLevel.B, apiLevel);
} else {
- assertEquals(AndroidApiLevel.L_MR1.max(parameters.getApiLevel()), apiLevel);
+ assertEquals(AndroidApiLevel.B.max(parameters.getApiLevel()), apiLevel);
}
}
}))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
index d3d9572..c4308ed 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMethodTypeReferenceTest.java
@@ -53,9 +53,9 @@
if (Reference.methodFromMethod(readApi).equals(method)
|| Reference.methodFromMethod(setApi).equals(method)) {
if (parameters.isCfRuntime()) {
- assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+ assertEquals(AndroidApiLevel.B, apiLevel);
} else {
- assertEquals(AndroidApiLevel.L_MR1.max(parameters.getApiLevel()), apiLevel);
+ assertEquals(AndroidApiLevel.B.max(parameters.getApiLevel()), apiLevel);
}
}
}))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
new file mode 100644
index 0000000..ef4737d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelTypeReferenceInvokeTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Method apiMethod = LibraryClass.class.getDeclaredMethod("apiMethod");
+ Method apiCaller = ApiHelper.class.getDeclaredMethod("apiCaller", LibraryClass.class);
+ Method apiCallerCaller = Main.class.getDeclaredMethod("typeReference", Object.class);
+ boolean libraryClassOnBoot =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.M);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, ApiHelper.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.M))
+ .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.M))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .addAndroidBuildVersion()
+ .compile()
+ .inspect(
+ verifyThat(parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.M))
+ .addRunClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(
+ libraryClassOnBoot, "LibraryClass::apiMethod", "Hello World")
+ .assertSuccessWithOutputLinesIf(!libraryClassOnBoot, "Hello World");
+ }
+
+ public static class LibraryClass {
+
+ public void apiMethod() {
+ System.out.println("LibraryClass::apiMethod");
+ }
+ }
+
+ public static class ApiHelper {
+
+ public static void apiCaller(LibraryClass libraryClass) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ libraryClass.apiMethod();
+ }
+ }
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static void typeReference(Object object) {
+ if (object instanceof LibraryClass) {
+ ApiHelper.apiCaller((LibraryClass) object);
+ }
+ }
+
+ public static void main(String[] args) {
+ typeReference(args.length == 0 ? new LibraryClass() : "Foo");
+ System.out.println("Hello World");
+ }
+ }
+}
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 5232896..59d408f 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command.NopCommand;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -390,6 +391,11 @@
return stepInto(DEFAULT_FILTER);
}
+ protected final JUnit3Wrapper.Command applyIf(
+ boolean condition, Supplier<JUnit3Wrapper.Command> action) {
+ return condition ? action.get() : new NopCommand();
+ }
+
protected final JUnit3Wrapper.Command stepInto(StepFilter stepFilter) {
return step(StepKind.INTO, stepFilter);
}
@@ -1763,6 +1769,14 @@
}
}
+ class NopCommand implements Command {
+
+ @Override
+ public void perform(JUnit3Wrapper testBase) {
+ // Intentionally empty - it is a nop.
+ }
+ }
+
// Break on exceptions thrown in className.methodName.
class BreakOnExceptionCommand implements Command {
private static final int ALL_EXCEPTIONS = 0;
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index d205f26..4d44f78 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
@@ -335,6 +336,10 @@
checkLocals(left_mangledLvName, right_mangledLvName),
// Enter "foo"
stepInto(),
+ // TODO(b/207743106): Remove when resolved.
+ applyIf(
+ kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_6_0),
+ this::stepInto),
checkMethod(DEBUGGEE_CLASS, "foo"),
checkLine(SOURCE_FILE, 34),
stepOut(),
@@ -384,6 +389,10 @@
checkNoLocal(inlinee2_lambda2_inlineScope),
// Enter the call to "foo"
stepInto(),
+ // TODO(b/207743106): Remove when resolved.
+ applyIf(
+ kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_6_0),
+ this::stepInto),
checkMethod(DEBUGGEE_CLASS, "foo"),
checkLine(SOURCE_FILE, 34),
run());
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index f93bfb4..ebba822 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -14,6 +14,8 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +55,7 @@
KotlinCompiler compiler = kotlinTestParameters.getCompiler();
testForR8(parameters.getBackend())
.addProgramFiles(compiler.getKotlinStdlibJar(), compiler.getKotlinAnnotationJar())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.addKeepAllAttributes()
.allowDiagnosticWarningMessages()
.noMinification()
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 37d2b6e..f194b99 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
@@ -70,6 +70,7 @@
.addKeepAttributeLineNumberTable()
.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
.run(parameters.getRuntime(), MAIN)
.assertFailureWithErrorThatMatches(containsString(EXPECTED))
.inspectOriginalStackTrace(
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 30505ab..14c8504 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -62,6 +62,7 @@
.addProgramClasses(MAIN)
.setMinApi(parameters.getApiLevel())
.internalEnableMappingOutput()
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
.run(parameters.getRuntime(), MAIN)
// For a debug build we always expect the output to have actual line information.
.inspectFailure(this::checkHasLineNumberInfo)
@@ -75,6 +76,7 @@
.addProgramClasses(MAIN)
.setMinApi(parameters.getApiLevel())
.internalEnableMappingOutput()
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
// TODO(b/191038746): Enable LineNumberOptimization for release builds for DEX PC Output.
.applyIf(
apiLevelSupportsPcOutput(),
@@ -113,6 +115,7 @@
.release()
.addProgramClasses(MAIN)
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
.run(parameters.getRuntime(), MAIN)
// If compiling without a map output actual debug info should also be retained. Otherwise
// there would not be any way to obtain the actual lines.
@@ -128,6 +131,7 @@
.addKeepMainRule(MAIN)
.addKeepAttributeLineNumberTable()
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
.run(parameters.getRuntime(), MAIN)
.inspectOriginalStackTrace(
(stackTrace, inspector) -> {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
index 64ed426..f83d077 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
@@ -99,6 +99,30 @@
}
@Test
+ public void testManuallySetEmptySourceFile() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSourceFile()
+ .addKeepAttributeLineNumberTable()
+ .addKeepRules("-renamesourcefileattribute")
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ assertThat(stackTrace, isSame(expectedStackTrace));
+ ClassSubject mainSubject = inspector.clazz(Main.class);
+ assertThat(mainSubject, isPresent());
+ assertThat(mainSubject.uniqueMethodWithName("inlinee"), not(isPresent()));
+ assertThat(
+ mainSubject.uniqueMethodWithName("shouldRemoveLineNumberForInline"),
+ notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ });
+ }
+
+ @Test
public void testNonDefaultSourceFile() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
index bc0d012..351b1e8 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
@@ -60,6 +60,7 @@
.addKeepAttributeSourceFile()
.addKeepAttributeLineNumberTable()
.enableInliningAnnotations()
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatThrows(NullPointerException.class)
.inspectStackTrace(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
index f2c3fad..d07e819 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
@@ -66,7 +66,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
if (method.getHolderType().toSourceString().endsWith("$C")) {
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
} else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
index cedf279..eb665f8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -61,7 +61,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
if (method.getHolderType().toSourceString().endsWith("$C")) {
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
} else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
index 0a68ca4..ee612f8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
@@ -57,7 +57,7 @@
assert method.getReference().name.toString().equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
index 8636291..fed41d3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
@@ -63,7 +63,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
index 7ed853e..042ca95 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -63,7 +63,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1);
assert abstractValue.isSingleStringValue()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
index ac071d2..4c1b255 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
@@ -55,7 +55,7 @@
assert method.getReference().name.toString().equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(0).isUnknown();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
index 10df35d..ba515db5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
@@ -60,7 +60,7 @@
assert methodName.equals("m") || methodName.equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
if (methodName.equals("m")) {
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
index 6eef61d..bbe45f0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -59,7 +59,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1);
if (method.getHolderType().toSourceString().endsWith("$A")) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
index 148d988..86a89ed 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
@@ -57,7 +57,7 @@
assert method.getReference().name.toString().equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isDefinitelyNotNull();
assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 6bb75b3..5b7fa84 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -60,7 +60,7 @@
assert methodName.equals("<init>") || methodName.equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType;
if (methodName.equals("test")) {
upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
index e77cbaa..4c72b52 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
@@ -63,7 +63,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isDefinitelyNotNull();
assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
index 045742d..0c4b99a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
@@ -65,7 +65,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isDefinitelyNotNull();
if (method.getHolderType().toSourceString().endsWith("$A")) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
index 2362fd6..0b46131 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
@@ -55,7 +55,7 @@
assert method.getReference().name.toString().equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(0);
assert upperBoundType.isDefinitelyNotNull();
assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
index 027d715..dae8e30 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -58,7 +58,7 @@
assert methodName.equals("<init>") || methodName.equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
// `arg` for `test` or the receiver of `Base#<init>`.
// TODO(b/139246447): should avoid visiting <init>, which is trivial, default init!
// For testing purpose, `Base` is not merged and kept. The system correctly caught that, when
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
index 2b666d2..1952e51 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
@@ -60,7 +60,7 @@
assert methodName.equals("m") || methodName.equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
if (methodName.equals("m")) {
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isDefinitelyNotNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index 56fdde5..e863304 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -60,7 +60,7 @@
assert methodName.equals("<init>") || methodName.equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType;
if (methodName.equals("m")) {
upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
index 8a88c46..dc8820b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -56,7 +56,7 @@
assert method.getReference().name.toString().equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
index 7624d2a..f989c9f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
@@ -63,7 +63,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isNullable();
assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
index 904e53c..0c38b8f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
@@ -62,7 +62,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
index c226571..d386c2f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -54,7 +54,7 @@
assert method.getReference().name.toString().equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
index 500f34a..e473e4a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
@@ -60,7 +60,7 @@
assert methodName.equals("m") || methodName.equals("test")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
if (methodName.equals("m")) {
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isNullable();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
index c294462..0ee8eca 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -59,7 +59,7 @@
assert method.getReference().name.toString().equals("m")
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
- method.getDefinition().getCallSiteOptimizationInfo();
+ method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().toSourceString().endsWith("$A");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfToNullCheckRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfToNullCheckRewritingTest.java
new file mode 100644
index 0000000..d51649b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfToNullCheckRewritingTest.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.instanceofremoval;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InstanceOfToNullCheckRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(inspector -> assertThat(inspector.clazz(I.class), isPresent()))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("true", "false");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ A nonNullA = System.currentTimeMillis() >= 0 ? new A() : null;
+ A nullA = System.currentTimeMillis() < 0 ? new A() : null;
+ System.out.println(nonNullA instanceof I);
+ System.out.println(nullA instanceof I);
+ }
+ }
+
+ @NoVerticalClassMerging
+ interface I {}
+
+ static class A implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index 5c45b49..0b10725 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jsr45;
-import static com.android.tools.r8.ToolHelper.getDefaultAndroidJar;
-
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
@@ -69,7 +67,7 @@
R8Command.builder()
.addProgramFiles(inputPath)
.addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
- .addLibraryFiles(getDefaultAndroidJar())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.setOutput(outputPath, OutputMode.DexIndexed)
.addProguardConfigurationFiles(keepRulesPath)
.build());
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
index 6169da8..5f6030f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -9,7 +9,6 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -43,10 +42,7 @@
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- getKotlinTestParameters()
- .withCompiler(KotlinCompilerVersion.KOTLINC_1_3_72)
- .withAllTargetVersions()
- .build(),
+ getKotlinTestParameters().withOldCompilersIfSet().withAllTargetVersions().build(),
BooleanUtils.values());
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index db73684..e14d7a6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -8,7 +8,9 @@
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
@@ -34,6 +36,7 @@
private void test(Collection<String> rules) throws Exception {
testForR8(parameters.getBackend())
.addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.addKeepRules(rules)
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 67c908e..b3af78a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_6_0;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -370,22 +371,31 @@
}
ClassSubject classSubject = checkClassIsKept(inspector, testedClass.getClassName());
+
+ // For kotlin 1.6 we completely remove the field and accessors. We are unable to
+ // remove the entire class because we are not reprocessing TestMain.main.
String propertyName = "property";
+ if (kotlinParameters.isNewerThanOrEqualTo(KOTLINC_1_6_0)) {
+ FieldSubject field = classSubject.field(JAVA_LANG_STRING, propertyName);
+ assertFalse(field.isPresent());
+ return;
+ }
+
FieldSubject fieldSubject =
checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
assertFalse(fieldSubject.getField().accessFlags.isStatic());
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
AccessorKind accessorKind =
- kotlinc.getCompilerVersion() == KOTLINC_1_5_0
+ kotlinc.getCompilerVersion().isGreaterThanOrEqualTo(KOTLINC_1_5_0)
? AccessorKind.FROM_INNER
: AccessorKind.FROM_LAMBDA;
MemberNaming.MethodSignature getterAccessor =
testedClass.getGetterAccessorForProperty(propertyName, accessorKind);
MemberNaming.MethodSignature setterAccessor =
testedClass.getSetterAccessorForProperty(propertyName, accessorKind);
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getterAccessor);
- checkMethodIsKept(classSubject, setterAccessor);
+ checkMethodIsKept(classSubject, getterAccessor);
+ checkMethodIsKept(classSubject, setterAccessor);
});
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
index de3e23d..f9381cb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
@@ -44,10 +44,7 @@
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- getKotlinTestParameters()
- .withCompiler(KotlinCompilerVersion.KOTLINC_1_3_72)
- .withAllTargetVersions()
- .build());
+ getKotlinTestParameters().withOldCompilersIfSet().withAllTargetVersions().build());
}
public MetadataFirstToLatestTest(
@@ -59,7 +56,7 @@
@Test
public void smokeTest() throws Exception {
runTest(
- KotlinCompilerVersion.KOTLINC_1_5_0,
+ KotlinCompilerVersion.MAX_SUPPORTED_VERSION,
libJars.getForConfiguration(kotlinc, targetVersion),
kotlinc.getKotlinStdlibJar());
}
@@ -93,7 +90,7 @@
assertThrows(
AssertionError.class,
() -> {
- runTest(KotlinCompilerVersion.KOTLINC_1_3_72, libJar, stdLibJar);
+ runTest(kotlinParameters.getCompiler().getCompilerVersion(), libJar, stdLibJar);
});
assertThat(
assertionError.getMessage(),
@@ -120,7 +117,7 @@
.assertAllWarningMessagesMatch(
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.writeToZip();
- runTest(KotlinCompilerVersion.KOTLINC_1_5_0, libJar, stdLibJar);
+ runTest(KotlinCompilerVersion.MAX_SUPPORTED_VERSION, libJar, stdLibJar);
}
private void runTest(KotlinCompilerVersion kotlinCompilerVersion, Path libJar, Path stdLibJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index 11e391d..a29d2ec 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -142,7 +142,9 @@
KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
assertThat(age, isPresent());
assertThat(age, not(isExtensionProperty()));
- assertEquals(kotlinc.is(KOTLINC_1_5_0) ? "b:I" : "a:I", age.fieldSignature().asString());
+ assertEquals(
+ kotlinParameters.isNewerThanOrEqualTo(KOTLINC_1_5_0) ? "b:I" : "a:I",
+ age.fieldSignature().asString());
assertEquals("getAge()I", age.getterSignature().asString());
assertEquals("setAge(I)V", age.setterSignature().asString());
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index 833ba26..b7b222f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -162,7 +162,7 @@
.compileRaw();
assertNotEquals(0, kotlinTestCompileResult.exitCode);
- if (kotlinc.is(KotlinCompilerVersion.KOTLINC_1_5_0)) {
+ if (kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_5_0)) {
assertThat(
kotlinTestCompileResult.stderr,
containsString(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
index 84139d9..1cd5dea 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -8,7 +8,6 @@
import static org.junit.Assert.fail;
import static org.objectweb.asm.Opcodes.ASM7;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
@@ -43,7 +42,7 @@
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters()
- .withCompiler(KotlinCompilerVersion.KOTLINC_1_3_72)
+ .withOldCompilersIfSet()
.withTargetVersion(KotlinTargetVersion.JAVA_8)
.build());
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index 1ad87d5..1cc87e4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import java.io.File;
@@ -59,6 +60,7 @@
.addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
.addProgramFiles(kotlinc.getKotlinStdlibJar())
.addProgramFiles(kotlinc.getKotlinReflectJar())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.run(parameters.getRuntime(), PKG + ".SimpleReflectKt")
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
@@ -90,6 +92,7 @@
.addProgramFiles(kotlinc.getKotlinStdlibJar())
.addProgramFiles(kotlinc.getKotlinReflectJar())
.addProgramFiles(kotlinc.getKotlinAnnotationJar())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
index 5da69a2..98ead74 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
@@ -91,6 +91,7 @@
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
.addProgramClasses(LIBRARY_CLASSES)
+ .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
.addKeepMainRule(LibraryMain.class)
.setMinApi(parameters.getApiLevel())
.compile();
@@ -107,8 +108,8 @@
.addProgramClasses(PROGRAM_CLASSES)
.addApplyMapping(libraryResult.getProguardMap())
.addLibraryClasses(LIBRARY_CLASSES)
+ .addDefaultRuntimeLibrary(parameters)
.addTestingAnnotationsAsLibraryClasses()
- .addLibraryFiles(runtimeJar(parameters.getBackend()))
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
diff --git a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
index cc7900e..3dc67f0 100644
--- a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
@@ -38,6 +38,7 @@
.addKeepMainRule(InlineInto.class)
.addKeepRules("-keepparameternames")
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
.run(parameters.getRuntime(), InlineInto.class)
.assertSuccessWithOutputLines("42foo")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
index c8a47ca..4f028fb 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
@@ -52,6 +52,7 @@
.addMemberValuePropagationAnnotations()
.apply(this::configureRepackaging)
.enableMemberValuePropagationAnnotations(enableMemberValuePropagationAnnotations)
+ .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTest.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTest.java
new file mode 100644
index 0000000..1eb5db5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTest.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.retrace;
+
+public class StackTraceWithPcAndJumboStringTest {
+
+ public static void foo() {
+ if ((System.nanoTime() > 0 ? "SomeString" : "AndSomeOtherString").length() == 10) {
+ throw new RuntimeException("MoreStrings" + System.nanoTime() + "That Other String");
+ }
+ }
+
+ public static void bar() {
+ foo();
+ }
+
+ public static void main(String[] args) {
+ bar();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTestRunner.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTestRunner.java
new file mode 100644
index 0000000..652de07
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndJumboStringTestRunner.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.CoreMatchers.not;
+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.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StackTraceWithPcAndJumboStringTestRunner extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StackTraceWithPcAndJumboStringTestRunner(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private Class<?> getTestClass() {
+ return StackTraceWithPcAndJumboStringTest.class;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getTestClass())
+ .run(parameters.getRuntime(), getTestClass())
+ .assertFailureWithErrorThatThrows(RuntimeException.class)
+ .inspectStackTrace(stacktrace -> assertThat(stacktrace, isSame(getExpectedStackTrace())));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(getTestClass())
+ .noTreeShaking()
+ .addKeepAttributeLineNumberTable()
+ .addKeepMainRule(getTestClass())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ o -> {
+ o.enablePcBasedMappingFile = true;
+ o.testing.forceJumboStringProcessing = true;
+ })
+ .run(parameters.getRuntime(), getTestClass())
+ .assertFailureWithErrorThatThrows(RuntimeException.class)
+ .inspectStackTrace(
+ stacktrace -> {
+ if (isApiLevelWithPcSupport()) {
+ // TODO(b/207765416): Remove this when PC support works with jumbo string rewriting.
+ assertThat(stacktrace, not(isSame(getExpectedStackTrace())));
+ } else {
+ assertThat(stacktrace, isSame(getExpectedStackTrace()));
+ }
+ });
+ }
+
+ private boolean isApiLevelWithPcSupport() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport());
+ }
+
+ private StackTrace getExpectedStackTrace() {
+ String className = getTestClass().getName();
+ String sourceFile = getTestClass().getSimpleName() + ".java";
+ return StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName(className)
+ .setFileName(sourceFile)
+ .setMethodName("foo")
+ .setLineNumber(10)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName(className)
+ .setFileName(sourceFile)
+ .setMethodName("bar")
+ .setLineNumber(15)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName(className)
+ .setFileName(sourceFile)
+ .setMethodName("main")
+ .setLineNumber(19)
+ .build())
+ .build();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index 5e17250..d36489a 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -12,13 +12,12 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
@@ -40,6 +39,7 @@
public class TreeShakingSpecificTest extends TestBase {
private Backend backend;
+ private AndroidApiLevel minApi = AndroidApiLevel.LATEST;
@Parameters(name = "Backend: {1}")
public static List<Object[]> data() {
@@ -63,18 +63,6 @@
return Files.readAllBytes(Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex"));
}
- private void finishBuild(R8Command.Builder builder, Path out, String test) throws IOException {
- Path input;
- if (backend == Backend.DEX) {
- builder.setOutput(out, OutputMode.DexIndexed);
- input = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
- } else {
- builder.setOutput(out, OutputMode.ClassFile);
- input = Paths.get(EXAMPLES_BUILD_DIR, test + ".jar");
- }
- ToolHelper.getAppBuilder(builder).addProgramFiles(input);
- }
-
@Test
public void testIgnoreWarnings() throws Exception {
// Generate R8 processed version without library option.
@@ -86,6 +74,7 @@
builder -> builder.addProgramDexFileData(getProgramDexFileData(test)))
.addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt"))
.addIgnoreWarnings()
+ .setMinApi(minApi)
.compile();
}
@@ -101,6 +90,7 @@
.addLibraryFiles()
.addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt"))
.allowDiagnosticErrorMessages()
+ .setMinApi(minApi)
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics
@@ -131,6 +121,8 @@
builder -> builder.addProgramDexFileData(getProgramDexFileData(test)))
.addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt"))
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
+ .setMinApi(minApi)
.compile()
.inspectProguardMap(
proguardMap -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
index 82bcb21..6413bb8 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
@@ -119,6 +119,7 @@
.addKeepRules(keepRules)
.enableSideEffectAnnotations()
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.enablePcBasedMappingFile = true)
.run(parameters.getRuntime(), CLASS)
.inspector()
.clazz(CLASS)
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnStaticFinalFieldTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnStaticFinalFieldTest.java
new file mode 100644
index 0000000..e368a4f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnStaticFinalFieldTest.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.ifrule;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class IfOnStaticFinalFieldTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public IfOnStaticFinalFieldTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("A", "B");
+ }
+
+ @Test
+ public void testRuleMatchingOnlyStaticFinalFieldsYieldWarning() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(
+ "-if class " + StaticFinalField.class.getTypeName() + " { int f; }",
+ "-keep class " + A.class.getTypeName())
+ .addKeepRules(
+ "-if class " + StaticNonFinalField.class.getTypeName() + " { int f; }",
+ "-keep class " + B.class.getTypeName())
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertOnlyWarnings()
+ .assertWarningsMatch(
+ allOf(
+ diagnosticType(InlinableStaticFinalFieldPreconditionDiagnostic.class),
+ diagnosticMessage(containsString("StaticFinalField.f")))))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("ClassNotFoundException: A", "B");
+ }
+
+ @Test
+ public void testRuleMatchingMoreHasWarning() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-if class * { int f; } -keep class " + B.class.getTypeName())
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertWarningsMatch(
+ allOf(
+ diagnosticType(InlinableStaticFinalFieldPreconditionDiagnostic.class),
+ diagnosticMessage(containsString("StaticFinalField.f"))))
+ .assertOnlyWarnings())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("ClassNotFoundException: A", "B");
+ }
+
+ static class StaticFinalField {
+ static final int f = 1;
+ }
+
+ static class StaticNonFinalField {
+ static int f = 2;
+ }
+
+ public static class A {
+
+ @Override
+ public String toString() {
+ return "A";
+ }
+ }
+
+ public static class B {
+
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+
+ static class TestClass {
+
+ // Simulate construction of an object (View) from a field value (R-value).
+ static List<Object> getObjects(int... keys) {
+ List<Object> objects = new ArrayList<>();
+ for (int key : keys) {
+ String name;
+ if (key == 1) {
+ name = "A";
+ } else if (key == 2) {
+ name = "B";
+ } else {
+ continue;
+ }
+ Class<TestClass> clazz = TestClass.class;
+ String prefix = clazz.getName().substring(0, clazz.getName().indexOf('$') + 1);
+ try {
+ objects.add(clazz.forName(prefix + name).getConstructor().newInstance());
+ } catch (Exception e) {
+ objects.add(e.getClass().getSimpleName() + ": " + name);
+ }
+ }
+ return objects;
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (System.nanoTime() < 0) {
+ // Force evaluation of the conditional rule for the final field type.
+ System.out.println(StaticFinalField.class.getTypeName());
+ System.out.println(new StaticFinalField());
+ }
+ // Two field references, one of which is inlined by javac.
+ for (Object object : getObjects(StaticFinalField.f, StaticNonFinalField.f)) {
+ System.out.println(object.toString());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
index 6fdcdf9..70f8903 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
@@ -81,7 +81,7 @@
throws IOException, CompilationFailedException, ExecutionException {
runTest(
8,
- 12,
+ 18,
"-if class **$R* { int keepA; }",
"-keep class " + A.class.getTypeName() + " { void a(); }",
"-if class **$R* { int keepB; }",
@@ -115,7 +115,7 @@
throws IOException, CompilationFailedException, ExecutionException {
runTest(
8,
- 12,
+ 18,
"-if class **$R*,**$X { int keepA; }",
"-keep class " + A.class.getTypeName() + " { void a(); }",
"-if class **$R*,**$X { int keepB; }",
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningSource.java b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningSource.java
new file mode 100644
index 0000000..f67d414
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningSource.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.ifrule;
+
+public class StaticFinalFieldInliningSource {
+
+ public static final Object unusedField = "Unused Field";
+
+ public static final Object nullObject = null;
+ public static final Object constObject = "const";
+ public static final String nullString = null;
+ public static final String constString = "const";
+ public static final int nonConstInt = System.nanoTime() < 0 ? 42 : 21;
+ public static final int constInt = 42;
+ public static final double constDouble = 42.5;
+
+ public static Object getNullObject() {
+ return nullObject;
+ }
+
+ public static Object getConstObject() {
+ return constObject;
+ }
+
+ public static String getNullString() {
+ return nullString;
+ }
+
+ public static String getConstString() {
+ return constString;
+ }
+
+ public static int getNonConstInt() {
+ return nonConstInt;
+ }
+
+ public static int getConstInt() {
+ return constInt;
+ }
+
+ public static double getConstDouble() {
+ return constDouble;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(getNullObject());
+ System.out.println(getConstObject());
+ System.out.println(getNullString());
+ System.out.println(getConstString());
+ System.out.println(getNonConstInt());
+ System.out.println(getConstInt());
+ System.out.println(getConstDouble());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningTest.java
new file mode 100644
index 0000000..7a0d692
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/StaticFinalFieldInliningTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.ifrule;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.JavaCompilerTool;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StaticFinalFieldInliningTest extends TestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines("null", "const", "null", "const", "21", "42", "42.5");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntimes().build();
+ }
+
+ public StaticFinalFieldInliningTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJavaCompilers() throws Exception {
+ Path output =
+ JavaCompilerTool.create(parameters.getRuntime().asCf(), temp)
+ .addSourceFiles(
+ ToolHelper.getSourceFileForTestClass(StaticFinalFieldInliningSource.class))
+ .compile();
+
+ testForJvm()
+ .addProgramFiles(output)
+ .run(parameters.getRuntime(), StaticFinalFieldInliningSource.class)
+ .assertSuccessWithOutput(EXPECTED);
+
+ CodeInspector inspector = new CodeInspector(output);
+ assertHasStaticGet(true, "getNullObject", inspector);
+ assertHasStaticGet(true, "getConstObject", inspector);
+ assertHasStaticGet(true, "getNullString", inspector);
+ assertHasStaticGet(false, "getConstString", inspector);
+ assertHasStaticGet(true, "getNonConstInt", inspector);
+ assertHasStaticGet(false, "getConstInt", inspector);
+ assertHasStaticGet(false, "getConstDouble", inspector);
+ }
+
+ private static void assertHasStaticGet(
+ boolean expected, String methodName, CodeInspector inspector) {
+ MethodSubject method =
+ inspector.clazz(StaticFinalFieldInliningSource.class).uniqueMethodWithName(methodName);
+ assertEquals(
+ method.getMethod().codeToString(),
+ expected,
+ method.streamInstructions().anyMatch(InstructionSubject::isStaticGet));
+ }
+
+ private static String makeIfRule(String fieldName) {
+ return "-if class * { *** " + fieldName + "; } -keep class <1> { *** unusedField; }";
+ }
+
+ @Test
+ public void testNoWarningsOnNonInlinedFields() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(StaticFinalFieldInliningSource.class)
+ .addKeepMainRule(StaticFinalFieldInliningSource.class)
+ .addKeepRules(makeIfRule("nullObject"))
+ .addKeepRules(makeIfRule("constObject"))
+ .addKeepRules(makeIfRule("nullString"))
+ .addKeepRules(makeIfRule("nonConstInt"))
+ .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoWarnings);
+ }
+
+ @Test
+ public void testWarningsOnMultipleMatches() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(StaticFinalFieldInliningSource.class)
+ .addKeepMainRule(StaticFinalFieldInliningSource.class)
+ // This rule matches both constObject (non-inlined) and constString/Int/Double (inlined).
+ .addKeepRules(makeIfRule("const*"))
+ .allowDiagnosticWarningMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(InlinableStaticFinalFieldPreconditionDiagnostic.class),
+ diagnosticMessage(containsString("constString")),
+ diagnosticMessage(containsString("constInt")),
+ diagnosticMessage(containsString("constDouble")),
+ not(diagnosticMessage(containsString("constObject")))));
+ });
+ }
+
+ @Test
+ public void testWarningsOnInlinedFields() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(StaticFinalFieldInliningSource.class)
+ .addKeepMainRule(StaticFinalFieldInliningSource.class)
+ .addKeepRules(makeIfRule("constString"))
+ .addKeepRules(makeIfRule("constInt"))
+ .addKeepRules(makeIfRule("constDouble"))
+ .allowDiagnosticWarningMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ diagnosticMessage(containsString("constString")),
+ diagnosticMessage(containsString("constInt")),
+ diagnosticMessage(containsString("constDouble"))));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassDirectTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassDirectTest.java
new file mode 100644
index 0000000..a7c5297
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassDirectTest.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.keep;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ExtendsMissingClassDirectTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ run(
+ testForProguard(ProguardVersion.V7_0_0)
+ .addDontWarn(MissingClass.class)
+ .addDontWarn(ExtendsMissingClassDirectTest.class));
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
+ .addProgramClasses(Main.class, A.class)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class ** extends " + MissingClass.class.getTypeName())
+ .compile()
+ .inspect(
+ inspector ->
+ assertEquals(
+ ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+ }
+
+ static class MissingClass {}
+
+ static class A extends MissingClass {}
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassTransitiveTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassTransitiveTest.java
new file mode 100644
index 0000000..2f7bcd4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ExtendsMissingClassTransitiveTest.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.keep;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ExtendsMissingClassTransitiveTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ run(
+ testForProguard(ProguardVersion.V7_0_0)
+ .addDontWarn(MissingClass.class)
+ .addDontWarn(ExtendsMissingClassTransitiveTest.class));
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
+ .addProgramClasses(Main.class, A.class, B.class)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class ** extends " + MissingClass.class.getTypeName())
+ .compile()
+ .inspect(
+ inspector ->
+ assertEquals(
+ ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+ }
+
+ static class MissingClass {}
+
+ static class A extends MissingClass {}
+
+ static class B extends A {}
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassDirectTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassDirectTest.java
new file mode 100644
index 0000000..a4724aa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassDirectTest.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.keep;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ImplementsMissingClassDirectTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ run(
+ testForProguard(ProguardVersion.V7_0_0)
+ .addDontWarn(MissingInterface.class)
+ .addDontWarn(ImplementsMissingClassDirectTest.class));
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
+ .addProgramClasses(Main.class, A.class)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class ** implements " + MissingInterface.class.getTypeName())
+ .compile()
+ .inspect(
+ inspector ->
+ assertEquals(
+ ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+ }
+
+ interface MissingInterface {}
+
+ static class A implements MissingInterface {}
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassTransitiveTest.java b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassTransitiveTest.java
new file mode 100644
index 0000000..a087d90
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/ImplementsMissingClassTransitiveTest.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.keep;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ImplementsMissingClassTransitiveTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ run(testForR8(Backend.CF).allowUnusedProguardConfigurationRules());
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ run(
+ testForProguard(ProguardVersion.V7_0_0)
+ .addDontWarn(MissingInterface.class)
+ .addDontWarn(ImplementsMissingClassTransitiveTest.class));
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
+ .addProgramClasses(Main.class, A.class, B.class)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class ** implements " + MissingInterface.class.getTypeName())
+ .compile()
+ .inspect(
+ inspector ->
+ assertEquals(
+ ImmutableList.of(inspector.clazz(Main.class)), inspector.allClasses()));
+ }
+
+ interface MissingInterface {}
+
+ static class A implements MissingInterface {}
+
+ static class B extends A {}
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index 2851643..e7bb456 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -138,46 +138,51 @@
}
private void noInterfaceKept(CodeInspector inspector) {
- // Indirectly assert that method is inlined into x, y and z.
- assertEquals(1, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
- assertEquals(1, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
- assertEquals(1, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
+ // Indirectly assert that method is inlined into x, y and z and that redundant field loads
+ // remove invokes.
+ assertEquals(0, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
+ assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
+ assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
}
@Test
public void testNoInterfaceKept() throws Exception {
- runTest(ImmutableList.of(),
+ runTest(
+ ImmutableList.of(),
this::noInterfaceKept,
- "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nEXCEPTION\n");
+ "TestClass 1\nTestClass 1\nTestClass 1\nEXCEPTION\n");
}
private void baseInterfaceKept(CodeInspector inspector) {
// Indirectly assert that method is not inlined into x.
assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
- // Indirectly assert that method is inlined into y and z.
- assertEquals(1, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
- assertEquals(1, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
- assertEquals(1, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
+ // Indirectly assert that method is inlined into y and z and that redundant field loads
+ // remove invokes.
+ assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
+ assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
+ assertEquals(0, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
}
@Test
public void testBaseInterfaceKept() throws Exception {
- runTest(ImmutableList.of(
- "-keep interface " + BaseInterface.class.getCanonicalName() + " {",
- " <methods>;",
- "}"),
+ runTest(
+ ImmutableList.of(
+ "-keep interface " + BaseInterface.class.getCanonicalName() + " {",
+ " <methods>;",
+ "}"),
this::baseInterfaceKept,
- "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n" +
- "TestClass 2\nTestClass 2\nTestClass 2\nProxy\nEXCEPTION\n");
+ "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n"
+ + "TestClass 2\nTestClass 2\nTestClass 2\nEXCEPTION\n");
}
private void subInterfaceKept(CodeInspector inspector) {
// Indirectly assert that method is not inlined into x or y.
assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
assertEquals(3, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
- // Indirectly assert that method is inlined into z.
- assertEquals(1, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
- assertEquals(1, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
+ // Indirectly assert that method is inlined into x, y and z and that redundant field loads
+ // remove invokes.
+ assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
+ assertEquals(0, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java b/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
index aba5532..b28721d 100644
--- a/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
+++ b/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
@@ -51,7 +51,6 @@
UsedDuringLaunch.class, NotUsedDuringLaunch.class))
.assertNoOtherClassesMerged())
.enableInliningAnnotations()
- .addOptionsModification(o -> o.apiModelingOptions().disableApiCallerIdentification())
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/workaround/MethodReturnWithMissingBaseTypeTest.java b/src/test/java/com/android/tools/r8/workaround/MethodReturnWithMissingBaseTypeTest.java
new file mode 100644
index 0000000..87a601b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/workaround/MethodReturnWithMissingBaseTypeTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.workaround;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MethodReturnWithMissingBaseTypeTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .applyIf(
+ parameters.isDexRuntime(),
+ testBuilder -> testBuilder.addLibraryFiles(ToolHelper.getMostRecentAndroidJar()))
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Utils.class)
+ .addKeepRules(
+ "-keep class " + Main.class.getTypeName() + " { void notUsedDuringLaunch(); }")
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertIsCompleteMergeGroup(UsedDuringLaunch.class, NotUsedDuringLaunch.class)
+ .assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new UsedDuringLaunch().usedDuringLaunch();
+ }
+
+ // @Keep
+ static void notUsedDuringLaunch() {
+ Consumer<?> emptyConsumer = Utils.getEmptyConsumer();
+ new UsedDuringLaunch().onlyUsedOnHighApiLevels(emptyConsumer);
+ new NotUsedDuringLaunch().useOfConsumerArray();
+ }
+ }
+
+ @NeverClassInline
+ static class UsedDuringLaunch {
+
+ @NeverInline
+ void usedDuringLaunch() {
+ System.out.println("Hello world!");
+ }
+
+ @NeverInline
+ void onlyUsedOnHighApiLevels(Consumer<?> c) {
+ System.out.println(c);
+ }
+ }
+
+ @NeverClassInline
+ static class NotUsedDuringLaunch {
+
+ @NeverInline
+ void useOfConsumerArray() {
+ Utils.accept(Utils.getEmptyConsumers());
+ }
+ }
+
+ // @Keep
+ static class Utils {
+
+ // @Keep
+ static void accept(Consumer<?>[] array) {
+ System.out.println(array.length);
+ }
+
+ // @Keep
+ public static Consumer<?> getEmptyConsumer() {
+ return ignore -> {};
+ }
+
+ // @Keep
+ public static Consumer<?>[] getEmptyConsumers() {
+ return new Consumer<?>[] {getEmptyConsumer()};
+ }
+ }
+}
diff --git a/third_party/kotlin/kotlin-compiler-1.6.0.tar.gz.sha1 b/third_party/kotlin/kotlin-compiler-1.6.0.tar.gz.sha1
new file mode 100644
index 0000000..0a1ac8f
--- /dev/null
+++ b/third_party/kotlin/kotlin-compiler-1.6.0.tar.gz.sha1
@@ -0,0 +1 @@
+bd6cc2fbbc946a62268f76157aa4643b021b9b00
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index dbb4a18..98c9489 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -55,7 +55,7 @@
'--r8-flags', '--r8_flags',
help='Additional option(s) for the compiler.')
parser.add_argument(
- '-override',
+ '--override',
help='Do not override any extracted dump in temp-dir',
default=False,
action='store_true')
diff --git a/tools/create_r8lib.py b/tools/create_r8lib.py
index 329ae8e..c8fcac3 100755
--- a/tools/create_r8lib.py
+++ b/tools/create_r8lib.py
@@ -38,6 +38,10 @@
'--lib',
action='append',
help='Additional libraries (JDK 1.8 rt.jar already included)')
+ parser.add_argument(
+ '--classpath',
+ action='append',
+ help='Dependencies to add to classpath')
return parser.parse_args()
def get_r8_version(r8jar):
@@ -86,6 +90,9 @@
if args.lib:
for lib in args.lib:
cmd.extend(['--lib', lib])
+ if args.classpath:
+ for cp in args.classpath:
+ cmd.extend(['--classpath', cp])
print(' '.join(cmd))
subprocess.check_call(cmd)
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index f71a314..6c25a4d 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -945,6 +945,9 @@
result.add_argument('--shrinker',
help='The shrinkers to use (by default, all are run)',
action='append')
+ result.add_argument('--temp',
+ help='A directory to use for temporaries and outputs.',
+ default=None)
result.add_argument('--version',
default='main',
help='The version of R8 to use (e.g., 1.4.51)')
@@ -1087,6 +1090,8 @@
return 0
with utils.TempDir() as temp_dir:
+ if options.temp:
+ temp_dir = options.temp
if options.hash:
# Download r8-<hash>.jar from
# https://storage.googleapis.com/r8-releases/raw/.
diff --git a/tools/test.py b/tools/test.py
index 7235b6e..f5ce159 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -196,6 +196,9 @@
help='Specify to download a kotlin dev compiler and run '
'tests with that',
default=False, action='store_true')
+ result.add_option('--kotlin-compiler-old',
+ help='Specify to run tests on older kotlin compilers',
+ default=False, action='store_true')
return result.parse_args()
def archive_failures(options):
@@ -283,6 +286,8 @@
gradle_args.append('-Pprint_full_stacktraces')
if options.print_obfuscated_stacktraces:
gradle_args.append('-Pprint_obfuscated_stacktraces')
+ if options.kotlin_compiler_old:
+ gradle_args.append('-Dcom.android.tools.r8.kotlincompilerold=1')
if options.kotlin_compiler_dev:
gradle_args.append('-Dcom.android.tools.r8.kotlincompilerdev=1')
download_kotlin_dev.download_newest()