Merge commit '72ba207f11c889ef80da7f23f8848c57d780cc66' into dev-release
diff --git a/build.gradle b/build.gradle
index d9be6a4..041431e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,6 +11,12 @@
import tasks.DownloadDependency
import tasks.GetJarsFromConfiguration
import utils.Utils
+import org.gradle.api.tasks.testing.logging.TestExceptionFormat
+import org.gradle.api.tasks.testing.logging.TestLogEvent
+
+import java.nio.charset.StandardCharsets
+import java.nio.file.Files
+import java.nio.file.StandardOpenOption
buildscript {
repositories {
@@ -1848,43 +1854,47 @@
outputs.dir r8LibTestPath
}
+def shouldRetrace() {
+ return project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')
+}
+
+def retrace(Throwable exception) {
+ def out = new StringBuffer()
+ def err = new StringBuffer()
+ def command = "python tools/retrace.py --quiet"
+ def header = "RETRACED STACKTRACE";
+ if (System.getenv('BUILDBOT_BUILDERNAME') != null
+ && !System.getenv('BUILDBOT_BUILDERNAME').endsWith("_release")) {
+ header += ": (${command} --commit_hash ${System.getenv('BUILDBOT_REVISION')})";
+ }
+ out.append("\n--------------------------------------\n")
+ out.append("${header}\n")
+ out.append("--------------------------------------\n")
+ Process process = command.execute()
+ def processIn = new PrintStream(process.getOut())
+ process.consumeProcessOutput(out, err)
+ exception.printStackTrace(processIn)
+ processIn.flush()
+ processIn.close()
+ def errorDuringRetracing = process.waitFor() != 0
+ if (errorDuringRetracing) {
+ out.append("ERROR DURING RETRACING\n")
+ out.append(err.toString())
+ }
+ if (project.hasProperty('print_obfuscated_stacktraces') || errorDuringRetracing) {
+ out.append("\n\n--------------------------------------\n")
+ out.append("OBFUSCATED STACKTRACE\n")
+ out.append("--------------------------------------\n")
+ }
+ return out.toString()
+}
+
def printStackTrace(TestResult result) {
filterStackTraces(result)
- if (project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')) {
- def out = new StringBuffer()
- def err = new StringBuffer()
- def command = "python tools/retrace.py --quiet"
- def header = "RETRACED STACKTRACE";
- if (System.getenv('BUILDBOT_BUILDERNAME') != null
- && !System.getenv('BUILDBOT_BUILDERNAME').endsWith("_release")) {
- header += ": (${command} --commit_hash ${System.getenv('BUILDBOT_REVISION')})";
- }
- out.append("\n--------------------------------------\n")
- out.append("${header}\n")
- out.append("--------------------------------------\n")
- Process process = command.execute()
- def processIn = new PrintStream(process.getOut())
- process.consumeProcessOutput(out, err)
- result.exception.printStackTrace(processIn)
- processIn.flush()
- processIn.close()
- def errorDuringRetracing = process.waitFor() != 0
- if (errorDuringRetracing) {
- out.append("ERROR DURING RETRACING\n")
- out.append(err.toString())
- }
- if (project.hasProperty('print_obfuscated_stacktraces') || errorDuringRetracing) {
- out.append("\n\n--------------------------------------\n")
- out.append("OBFUSCATED STACKTRACE\n")
- out.append("--------------------------------------\n")
- } else {
- result.exceptions.clear()
- }
- def exception = new Exception(out.toString())
+ if (shouldRetrace()) {
+ def exception = new Exception(retrace(result.exception))
exception.setStackTrace([] as StackTraceElement[])
result.exceptions.add(0, exception)
- } else {
- result.exception.printStackTrace()
}
}
@@ -1894,6 +1904,7 @@
}
}
+// It would be nice to do this in a non-destructive way...
def filterStackTrace(Throwable exception) {
if (!project.hasProperty('print_full_stacktraces')) {
def elements = []
@@ -1911,10 +1922,136 @@
}
}
+def printAllStackTracesToFile(List<Throwable> exceptions, File out) {
+ new PrintStream(new FileOutputStream(out)).withCloseable {printer ->
+ exceptions.forEach { it.printStackTrace(printer) }
+ }
+}
+
+def ensureDir(File dir) {
+ dir.mkdirs()
+ return dir
+}
+
+def getTestReportEntryDir(reportDir, testDesc) {
+ return ensureDir(reportDir.toPath()
+ .resolve(testDesc.getClassName())
+ .resolve(testDesc.getName())
+ .toFile())
+}
+
+def getTestResultEntryOutputFile(reportDir, testDesc, fileName) {
+ def dir = getTestReportEntryDir(reportDir, testDesc).toPath()
+ return dir.resolve(fileName).toFile()
+}
+
+def getGitBranchName() {
+ def out = new StringBuilder()
+ def err = new StringBuilder()
+ def proc = "git rev-parse --abbrev-ref HEAD".execute()
+ proc.waitForProcessOutput(out, err)
+ return out
+}
+
+def setUpNewTestReporting(Test task) {
+ // Hide all test events from the console, they are written to the report.
+ task.testLogging { events = [] }
+
+ def testReportOutput = project.hasProperty('test_report_output')
+ ? file(project.hasProperty('test_report_output'))
+ : file("${buildDir}/testreport")
+ def index = testReportOutput.toPath().resolve("index.html").toFile()
+
+ task.beforeSuite { desc ->
+ if (!desc.parent) {
+ // Start by printing a link to the test report for easy access.
+ println "Test report being written to:"
+ println "file://${index}"
+
+ def branch = getGitBranchName()
+ def title = "${desc} @ ${branch}"
+
+ // TODO(b/186607794): Support resuming testing based on the existing report.
+ delete testReportOutput
+ testReportOutput.mkdirs()
+ index << "<html><head><title>${title}</title></head>"
+ index << "<body><h1>${title}</h1>"
+ index << "<p>Run on: ${new Date()}</p>"
+ index << "<p>Git branch: ${branch}</p>"
+ index << "<p><a href=\"file://${testReportOutput}\">Test directories</a></p>"
+ index << "<h2>Failing tests (reload to refresh)</h2><ul>"
+ }
+ }
+
+ task.afterSuite { desc, result ->
+ if (!desc.parent) {
+ // Update the final test results in the index.
+ index << "</ul>"
+ index << "<h2>Tests finished: ${result.resultType.name()}</h2><ul>"
+ index << "<li>Number of tests: ${result.testCount}"
+ index << "<li>Failing tests: ${result.failedTestCount}"
+ index << "<li>Successful tests: ${result.successfulTestCount}"
+ index << "<li>Skipped tests: ${result.skippedTestCount}"
+ index << "</ul></body></html>"
+ }
+ }
+
+ // Events to stdout/err are appended to the files in the test directories.
+ task.onOutput { desc, event ->
+ getTestResultEntryOutputFile(testReportOutput, desc, event.getDestination().name()) <<
+ event.getMessage()
+ }
+
+ task.afterTest { desc, result ->
+ if (result.getTestCount() != 1) {
+ throw new IllegalStateException("Unexpected test with more than one result: ${desc}")
+ }
+ def testReportPath = getTestReportEntryDir(testReportOutput, desc)
+ // Emit the result type status in a file of the same name: SUCCESS, FAILURE or SKIPPED.
+ getTestResultEntryOutputFile(testReportOutput, desc, result.getResultType().name()) <<
+ result.getResultType().name()
+ // Emit the test time.
+ getTestResultEntryOutputFile(testReportOutput, desc, "time") <<
+ "${result.getEndTime() - result.getStartTime()}"
+ // For failed tests, update the index and emit stack trace information.
+ if (result.resultType == TestResult.ResultType.FAILURE) {
+ def title = "${desc.className}.${desc.name}"
+ index << "<li><a href=\"file://${testReportPath}\">${title}</a></li>"
+ if (!result.exceptions.isEmpty()) {
+ printAllStackTracesToFile(
+ result.exceptions,
+ getTestResultEntryOutputFile(
+ testReportOutput,
+ desc,
+ "exceptions-raw.txt"))
+ filterStackTraces(result)
+ printAllStackTracesToFile(
+ result.exceptions,
+ getTestResultEntryOutputFile(
+ testReportOutput,
+ desc,
+ "exceptions-filtered.txt"))
+ if (shouldRetrace()) {
+ def out = getTestResultEntryOutputFile(
+ testReportOutput,
+ desc,
+ "exceptions-retraced.txt")
+ result.exceptions.forEach { out << retrace(it) }
+ }
+ }
+ }
+ }
+}
+
def testTimes = [:]
def numberOfTestTimesToPrint = 40
-test {
+test { task ->
+ def newTestingReport = project.hasProperty('testing-report')
+ if (newTestingReport) {
+ setUpNewTestReporting(task)
+ }
+
if (project.hasProperty('generate_golden_files_to')) {
systemProperty 'generate_golden_files_to', project.property('generate_golden_files_to')
assert project.hasProperty('HEAD_sha1')
@@ -1927,13 +2064,11 @@
systemProperty 'test_git_HEAD_sha1', project.property('HEAD_sha1')
}
- dependsOn buildLibraryDesugarConversions
- dependsOn getJarsFromSupportLibs
- // R8.jar is required for running bootstrap tests.
- dependsOn r8
- testLogging.exceptionFormat = 'full'
- if (project.hasProperty('print_test_stdout')) {
- testLogging.showStandardStreams = true
+ if (!newTestingReport) {
+ testLogging.exceptionFormat = 'full'
+ if (project.hasProperty('print_test_stdout')) {
+ testLogging.showStandardStreams = true
+ }
}
if (project.hasProperty('dex_vm') && project.property('dex_vm') != 'default') {
println "NOTE: Running with non default vm: " + project.property('dex_vm')
@@ -1958,39 +2093,42 @@
systemProperty 'desugar_jdk_libs', project.property('desugar_jdk_libs')
}
- if (project.hasProperty('print_times') || project.hasProperty('one_line_per_test')) {
- afterTest { desc, result ->
- def executionTime = (result.endTime - result.startTime) / 1000
- testTimes["${desc.name} [${desc.className}]"] = executionTime
- }
- afterSuite { desc, result ->
- // parent is null if all tests are done.
- if (desc.parent == null) {
- def sortedTimes = testTimes.sort({e1, e2 -> e2.value <=> e1.value})
- sortedTimes.eachWithIndex{key, value, i ->
- if (i < numberOfTestTimesToPrint) println "$key: $value"}
- }
- }
- }
-
- if (project.hasProperty('one_line_per_test')) {
- beforeTest { desc ->
- println "Start executing test ${desc.name} [${desc.className}]"
+ if (!newTestingReport) {
+ if (project.hasProperty('print_times') || project.hasProperty('one_line_per_test')) {
+ afterTest { desc, result ->
+ def executionTime = (result.endTime - result.startTime) / 1000
+ testTimes["${desc.name} [${desc.className}]"] = executionTime
+ }
+ afterSuite { desc, result ->
+ // parent is null if all tests are done.
+ if (desc.parent == null) {
+ def sortedTimes = testTimes.sort({ e1, e2 -> e2.value <=> e1.value })
+ sortedTimes.eachWithIndex { key, value, i ->
+ if (i < numberOfTestTimesToPrint) println "$key: $value"
+ }
+ }
+ }
}
- afterTest { desc, result ->
- if (result.resultType == TestResult.ResultType.FAILURE) {
- printStackTrace(result)
+ if (project.hasProperty('one_line_per_test')) {
+ beforeTest { desc ->
+ println "Start executing test ${desc.name} [${desc.className}]"
}
- if (project.hasProperty('update_test_timestamp')) {
- file(project.getProperty('update_test_timestamp')).text = new Date().getTime()
+
+ afterTest { desc, result ->
+ if (result.resultType == TestResult.ResultType.FAILURE) {
+ printStackTrace(result)
+ }
+ if (project.hasProperty('update_test_timestamp')) {
+ file(project.getProperty('update_test_timestamp')).text = new Date().getTime()
+ }
+ println "Done executing test ${desc.name} [${desc.className}] with result: ${result.resultType}"
}
- println "Done executing test ${desc.name} [${desc.className}] with result: ${result.resultType}"
- }
- } else {
- afterTest { desc, result ->
- if (result.resultType == TestResult.ResultType.FAILURE) {
- printStackTrace(result)
+ } else {
+ afterTest { desc, result ->
+ if (result.resultType == TestResult.ResultType.FAILURE) {
+ printStackTrace(result)
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index b1c7df4..81a8f82 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -324,7 +324,7 @@
appView,
marker == null ? null : ImmutableList.copyOf(markers),
appView.graphLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
namingLens,
proguardMapSupplier)
.write(executor);
@@ -394,7 +394,7 @@
appView,
null,
GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
desugaringLens,
null,
convertedCfFiles)
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 4d1c0dc..d5f8787 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -106,7 +106,7 @@
appView,
markers,
GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
NamingLens.getIdentityLens(),
null);
writer.write(executor);
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 7896d57..d71cabc 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -113,7 +113,7 @@
appView,
markers,
GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
NamingLens.getIdentityLens(),
null,
consumer)
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index af10925..ec9955d 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -333,7 +333,7 @@
if (isShrinking()) {
l8CfConsumer = new InMemoryJarContent();
R8Command.Builder r8Builder =
- new CompatProguardCommandBuilder(true, getReporter())
+ R8Command.builder(getReporter())
.addProgramResourceProvider((ProgramResourceProvider) l8CfConsumer)
.setSynthesizedClassesPrefix(
libraryConfiguration.getSynthesizedLibraryClassesPackagePrefix())
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 6a21762..7406a28 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -193,7 +193,7 @@
AppView.createForD8(AppInfo.createInitialAppInfo(app)),
null,
GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
NamingLens.getIdentityLens(),
null);
writer.write(executor);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 1db8182..eee2f72 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -641,12 +641,10 @@
}
// At least one method needs a jumbo string in which case we construct a thread local mapping
// for all code objects and write the processed results into that map.
+ // TODO(b/181636450): Reconsider the code mapping setup now that synthetics are never duplicated
+ // in outputs.
Map<DexEncodedMethod, DexCode> codeMapping = new IdentityHashMap<>();
for (DexProgramClass clazz : classes) {
- // TODO(b/181636450): Reconsider the code mapping setup now that synthetics are never
- // duplicated in outputs.
- boolean isSharedSynthetic =
- appView.getSyntheticItems().getSynthesizingContextTypes(clazz.getType()).size() > 1;
clazz.forEachMethod(
method -> {
DexCode code =
@@ -655,12 +653,10 @@
application.dexItemFactory,
options.testing.forceJumboStringProcessing);
codeMapping.put(method, code);
- if (!isSharedSynthetic) {
- // If the class is not a shared class the mapping now has ownership of the methods
- // code object. This ensures freeing of code resources once the map entry is cleared
- // and also ensures that we don't end up using the incorrect code pointer again later!
- method.removeCode();
- }
+ // The mapping now has ownership of the methods code object. This ensures freeing of
+ // code resources once the map entry is cleared and also ensures that we don't end up
+ // using the incorrect code pointer again later!
+ method.removeCode();
});
}
return MethodToCodeObjectMapping.fromMapBacking(codeMapping);
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 9b7c9da..42bd66b 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -653,8 +653,7 @@
}
}
- private void writeEncodedMethods(
- Iterable<DexEncodedMethod> unsortedMethods, boolean isSharedSynthetic) {
+ private void writeEncodedMethods(Iterable<DexEncodedMethod> unsortedMethods) {
List<DexEncodedMethod> methods = IterableUtils.toNewArrayList(unsortedMethods);
methods.sort(
(a, b) ->
@@ -675,7 +674,7 @@
dest.putUleb128(mixedSectionOffsets.getOffsetFor(code));
// Writing the methods starts to take up memory so we are going to flush the
// code objects since they are no longer necessary after this.
- codeMapping.clearCode(method, isSharedSynthetic);
+ codeMapping.clearCode(method);
}
}
}
@@ -689,10 +688,8 @@
dest.putUleb128(clazz.getMethodCollection().numberOfVirtualMethods());
writeEncodedFields(clazz.staticFields());
writeEncodedFields(clazz.instanceFields());
- boolean isSharedSynthetic =
- appInfo.getSyntheticItems().getSynthesizingContextTypes(clazz.getType()).size() > 1;
- writeEncodedMethods(clazz.directMethods(), isSharedSynthetic);
- writeEncodedMethods(clazz.virtualMethods(), isSharedSynthetic);
+ writeEncodedMethods(clazz.directMethods());
+ writeEncodedMethods(clazz.virtualMethods());
}
private void addStaticFieldValues(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
index 273bf3b..1de1144 100644
--- a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
+++ b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
@@ -106,7 +106,7 @@
}
default InitClassLens getInitClassLens() {
- return InitClassLens.getDefault();
+ return InitClassLens.getThrowingInstance();
}
default DexString getRenamedName(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
index feeb64b..5541398 100644
--- a/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
+++ b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
@@ -13,7 +13,7 @@
public abstract DexCode getCode(DexEncodedMethod method);
- public abstract void clearCode(DexEncodedMethod method, boolean isSharedSynthetic);
+ public abstract void clearCode(DexEncodedMethod method);
public abstract boolean verifyCodeObjects(Collection<DexCode> codes);
@@ -37,11 +37,8 @@
}
@Override
- public void clearCode(DexEncodedMethod method, boolean isSharedSynthetic) {
- // When using methods directly any shared class needs to maintain its methods as read-only.
- if (!isSharedSynthetic) {
- method.removeCode();
- }
+ public void clearCode(DexEncodedMethod method) {
+ method.removeCode();
}
@Override
@@ -64,7 +61,7 @@
}
@Override
- public void clearCode(DexEncodedMethod method, boolean isSharedSynthetic) {
+ public void clearCode(DexEncodedMethod method) {
// We can safely clear the thread local pointer to even shared methods.
codes.put(method, null);
}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index fa17d7c..072fab7 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -329,11 +329,10 @@
Collection<DexProgramClass> synthetics = new ArrayList<>();
// Assign dedicated virtual files for all program classes.
for (DexProgramClass clazz : appView.appInfo().classes()) {
- Collection<DexType> contexts =
- appView.getSyntheticItems().getSynthesizingContextTypes(clazz.getType());
// TODO(b/181636450): Simplify this making use of the assumption that synthetics are never
// duplicated.
- if (!combineSyntheticClassesWithPrimaryClass || contexts.isEmpty()) {
+ if (!combineSyntheticClassesWithPrimaryClass
+ || !appView.getSyntheticItems().isSyntheticClass(clazz)) {
VirtualFile file =
new VirtualFile(
virtualFiles.size(),
@@ -353,13 +352,14 @@
}
}
for (DexProgramClass synthetic : synthetics) {
- for (DexType context :
- appView.getSyntheticItems().getSynthesizingContextTypes(synthetic.getType())) {
- DexProgramClass inputType = appView.definitionForProgramType(context);
- VirtualFile file = files.get(inputType);
- file.addClass(synthetic);
- file.commitTransaction();
- }
+ Collection<DexType> synthesizingContexts =
+ appView.getSyntheticItems().getSynthesizingContextTypes(synthetic.getType());
+ assert synthesizingContexts.size() == 1;
+ DexProgramClass inputType =
+ appView.definitionForProgramType(synthesizingContexts.iterator().next());
+ VirtualFile file = files.get(inputType);
+ file.addClass(synthetic);
+ file.commitTransaction();
}
return virtualFiles;
}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
index 34f2f2c..224b2a4 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
@@ -59,7 +59,8 @@
methodReference, other.asMissingClass().getClassReference());
}
if (other.isMissingField()) {
- MethodReferenceUtils.compare(methodReference, other.asMissingField().getFieldReference());
+ return MethodReferenceUtils.compare(
+ methodReference, other.asMissingField().getFieldReference());
}
return MethodReferenceUtils.compare(
methodReference, other.asMissingMethod().getMethodReference());
@@ -86,16 +87,21 @@
public static void writeDiagnosticMessage(
StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo) {
- builder.append("Missing class ");
MissingDefinitionInfoUtils.accept(
missingDefinitionInfo,
- missingClassInfo -> builder.append(missingClassInfo.getClassReference().getTypeName()),
+ missingClassInfo ->
+ builder
+ .append("Missing class ")
+ .append(missingClassInfo.getClassReference().getTypeName()),
missingFieldInfo ->
- builder.append(
- FieldReferenceUtils.toSourceString(missingFieldInfo.getFieldReference())),
+ builder
+ .append("Missing field ")
+ .append(FieldReferenceUtils.toSourceString(missingFieldInfo.getFieldReference())),
missingMethodInfo ->
- builder.append(
- MethodReferenceUtils.toSourceString(missingMethodInfo.getMethodReference())));
+ builder
+ .append("Missing method ")
+ .append(
+ MethodReferenceUtils.toSourceString(missingMethodInfo.getMethodReference())));
writeReferencedFromSuffix(builder, missingDefinitionInfo);
}
@@ -119,14 +125,15 @@
missingDefinitionMethodContext.getMethodReference(),
getMethodReferenceComparator()));
}
- assert classContext.isSet() || fieldContext.isSet() || methodContext.isSet();
+ // TODO(b/186506586): Reenable assert once trace references also provide contextual information.
+ // assert classContext.isSet() || fieldContext.isSet() || methodContext.isSet();
if (fieldContext.isSet()) {
writeReferencedFromSuffix(
builder, missingDefinitionInfo, FieldReferenceUtils.toSourceString(fieldContext.get()));
} else if (methodContext.isSet()) {
writeReferencedFromSuffix(
builder, missingDefinitionInfo, MethodReferenceUtils.toSourceString(methodContext.get()));
- } else {
+ } else if (classContext.isSet()) {
writeReferencedFromSuffix(builder, missingDefinitionInfo, classContext.get().getTypeName());
}
}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingFieldInfoImpl.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingFieldInfoImpl.java
new file mode 100644
index 0000000..216080d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingFieldInfoImpl.java
@@ -0,0 +1,47 @@
+// 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.diagnostic.internal;
+
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
+import com.android.tools.r8.diagnostic.MissingFieldInfo;
+import com.android.tools.r8.references.FieldReference;
+import java.util.Collection;
+
+public class MissingFieldInfoImpl extends MissingDefinitionInfoBase implements MissingFieldInfo {
+
+ private final FieldReference fieldReference;
+
+ private MissingFieldInfoImpl(
+ FieldReference fieldReference, Collection<MissingDefinitionContext> referencedFromContexts) {
+ super(referencedFromContexts);
+ this.fieldReference = fieldReference;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public FieldReference getFieldReference() {
+ return fieldReference;
+ }
+
+ public static class Builder extends MissingDefinitionInfoBase.Builder {
+
+ private FieldReference fieldReference;
+
+ private Builder() {}
+
+ public Builder setField(FieldReference fieldReference) {
+ this.fieldReference = fieldReference;
+ return this;
+ }
+
+ public MissingDefinitionInfo build() {
+ return new MissingFieldInfoImpl(fieldReference, referencedFromContextsBuilder.build());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingMethodInfoImpl.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingMethodInfoImpl.java
new file mode 100644
index 0000000..5be14dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingMethodInfoImpl.java
@@ -0,0 +1,48 @@
+// 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.diagnostic.internal;
+
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
+import com.android.tools.r8.diagnostic.MissingMethodInfo;
+import com.android.tools.r8.references.MethodReference;
+import java.util.Collection;
+
+public class MissingMethodInfoImpl extends MissingDefinitionInfoBase implements MissingMethodInfo {
+
+ private final MethodReference methodReference;
+
+ private MissingMethodInfoImpl(
+ MethodReference methodReference,
+ Collection<MissingDefinitionContext> referencedFromContexts) {
+ super(referencedFromContexts);
+ this.methodReference = methodReference;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public MethodReference getMethodReference() {
+ return methodReference;
+ }
+
+ public static class Builder extends MissingDefinitionInfoBase.Builder {
+
+ private MethodReference methodReference;
+
+ private Builder() {}
+
+ public Builder setMethod(MethodReference methodReference) {
+ this.methodReference = methodReference;
+ return this;
+ }
+
+ public MissingDefinitionInfo build() {
+ return new MissingMethodInfoImpl(methodReference, referencedFromContextsBuilder.build());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 1cd36de..b3c5308 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -116,7 +116,7 @@
this.dontWarnConfiguration = DontWarnConfiguration.create(options().getProguardConfiguration());
this.wholeProgramOptimizations = wholeProgramOptimizations;
this.graphLens = GraphLens.getIdentityLens();
- this.initClassLens = InitClassLens.getDefault();
+ this.initClassLens = InitClassLens.getThrowingInstance();
this.rewritePrefix = mapper;
if (enableWholeProgramOptimizations() && options().callSiteOptimizationOptions().isEnabled()) {
@@ -128,12 +128,7 @@
this.libraryMethodSideEffectModelCollection = new LibraryMethodSideEffectModelCollection(this);
this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
-
- if (enableWholeProgramOptimizations() && options().protoShrinking().isProtoShrinkingEnabled()) {
- this.protoShrinker = new ProtoShrinker(withLiveness());
- } else {
- this.protoShrinker = null;
- }
+ this.protoShrinker = ProtoShrinker.create(withLiveness());
}
public boolean verifyMainThread() {
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index be553ac6..19f0fdf 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -58,6 +58,11 @@
super(originalFlags, modifiedFlags);
}
+ public static ClassAccessFlags createPublicFinalSynthetic() {
+ return new ClassAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
+ }
+
public static ClassAccessFlags fromSharedAccessFlags(int access) {
assert (access & SHARED_FLAGS) == access;
assert SHARED_FLAGS == DEX_FLAGS;
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 65bc4fb..7095940 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -5,7 +5,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
@@ -38,7 +38,7 @@
private FieldTypeSignature genericSignature;
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
- private KotlinFieldLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
+ private KotlinFieldLevelInfo kotlinMemberInfo = getNoKotlinInfo();
private static void specify(StructuralSpecification<DexEncodedField, ?> spec) {
spec.withItem(DexEncodedField::getReference)
@@ -135,17 +135,22 @@
}
@Override
- public KotlinFieldLevelInfo getKotlinMemberInfo() {
+ public KotlinFieldLevelInfo getKotlinInfo() {
return kotlinMemberInfo;
}
@Override
+ public void clearKotlinInfo() {
+ kotlinMemberInfo = getNoKotlinInfo();
+ }
+
+ @Override
public FieldAccessFlags getAccessFlags() {
return accessFlags;
}
public void setKotlinMemberInfo(KotlinFieldLevelInfo kotlinMemberInfo) {
- assert this.kotlinMemberInfo == NO_KOTLIN_INFO;
+ assert this.kotlinMemberInfo == getNoKotlinInfo();
this.kotlinMemberInfo = kotlinMemberInfo;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index 0074842..f596146 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -24,7 +24,9 @@
this.d8R8Synthesized = d8R8Synthesized;
}
- public abstract KotlinMemberLevelInfo getKotlinMemberInfo();
+ public abstract KotlinMemberLevelInfo getKotlinInfo();
+
+ public abstract void clearKotlinInfo();
public DexType getHolderType() {
return getReference().getHolderType();
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 d387d77..60c0ec6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -10,7 +10,7 @@
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS;
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
import com.android.tools.r8.cf.CfVersion;
@@ -160,7 +160,7 @@
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.bottom();
private CfVersion classFileVersion;
- private KotlinMethodLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
+ private KotlinMethodLevelInfo kotlinMemberInfo = getNoKotlinInfo();
/** Generic signature information if the attribute is present in the input */
private MethodTypeSignature genericSignature;
@@ -626,10 +626,15 @@
}
@Override
- public KotlinMethodLevelInfo getKotlinMemberInfo() {
+ public KotlinMethodLevelInfo getKotlinInfo() {
return kotlinMemberInfo;
}
+ @Override
+ public void clearKotlinInfo() {
+ kotlinMemberInfo = getNoKotlinInfo();
+ }
+
public void setKotlinMemberInfo(KotlinMethodLevelInfo kotlinMemberInfo) {
// Structure-changing optimizations, such as (vertical|horizontal) merger or inliner, that
// may need to redefine what this method is. Simply, the method merged/inlined by optimization
@@ -640,10 +645,14 @@
// an inlinee, is extension property. Being merged here means:
// * That inlinee is not an extension property anymore. We can ignore metadata from it.
// * This method is still an extension function, just with a bigger body.
- assert this.kotlinMemberInfo == NO_KOTLIN_INFO;
+ assert this.kotlinMemberInfo == getNoKotlinInfo();
this.kotlinMemberInfo = kotlinMemberInfo;
}
+ public void clearKotlinMemberInfo() {
+ kotlinMemberInfo = getNoKotlinInfo();
+ }
+
public boolean isKotlinFunction() {
return kotlinMemberInfo.isFunction();
}
@@ -1252,16 +1261,13 @@
DexMethod libraryMethod,
List<Pair<DexType, DexMethod>> extraDispatchCases,
AppView<?> appView) {
- MethodAccessFlags accessFlags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false);
CfCode code =
new EmulateInterfaceSyntheticCfCodeProvider(
interfaceType, companionMethod, libraryMethod, extraDispatchCases, appView)
.generateCfCode();
return new DexEncodedMethod(
newMethod,
- accessFlags,
+ MethodAccessFlags.createPublicStaticSynthetic(),
MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 92659f8..c4affd3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -68,6 +70,10 @@
assert kind == Kind.CF : "Invalid kind " + kind + " for library-path class " + type;
}
+ public static Builder builder(DexItemFactory dexItemFactory) {
+ return new Builder(dexItemFactory);
+ }
+
public static DexLibraryClass asLibraryClassOrNull(DexClass clazz) {
return clazz != null ? clazz.asLibraryClass() : null;
}
@@ -152,4 +158,77 @@
? appView.options().libraryInterfacesMayHaveStaticInitialization
: !appView.dexItemFactory().libraryClassesWithoutStaticInitialization.contains(type);
}
+
+ public static class Builder {
+
+ // Required.
+ private DexType type;
+ private ClassAccessFlags accessFlags;
+
+ // Optional.
+ private Origin origin = Origin.unknown();
+ private DexType superType;
+ private DexTypeList interfaces = DexTypeList.empty();
+ private DexString sourceFile = null;
+ private NestHostClassAttribute nestHost = null;
+ private List<NestMemberClassAttribute> nestMembers = Collections.emptyList();
+ private EnclosingMethodAttribute enclosingMember = null;
+ private List<InnerClassAttribute> innerClasses = Collections.emptyList();
+ private ClassSignature classSignature = ClassSignature.noSignature();
+ private DexAnnotationSet annotations = DexAnnotationSet.empty();
+ private DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
+ private DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
+ private DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
+ private DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+ private boolean skipNameValidationForTesting;
+
+ private Builder(DexItemFactory dexItemFactory) {
+ this.superType = dexItemFactory.objectType;
+ this.skipNameValidationForTesting = dexItemFactory.getSkipNameValidationForTesting();
+ }
+
+ public Builder setAccessFlags(ClassAccessFlags accessFlags) {
+ this.accessFlags = accessFlags;
+ return this;
+ }
+
+ public Builder setDirectMethods(Collection<DexEncodedMethod> directMethods) {
+ this.directMethods = directMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
+ return this;
+ }
+
+ public Builder setType(DexType type) {
+ this.type = type;
+ return this;
+ }
+
+ public DexLibraryClass build() {
+ assert validate();
+ return new DexLibraryClass(
+ type,
+ ProgramResource.Kind.CF,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ classSignature,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting);
+ }
+
+ private boolean validate() {
+ assert type != null;
+ assert accessFlags != null;
+ return true;
+ }
+ }
}
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 31a24ff..1a2bc2b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.ProgramResource;
@@ -49,7 +49,7 @@
private final ProgramResource.Kind originKind;
private CfVersion initialClassFileVersion = null;
private boolean deprecated = false;
- private KotlinClassLevelInfo kotlinInfo = NO_KOTLIN_INFO;
+ private KotlinClassLevelInfo kotlinInfo = getNoKotlinInfo();
private final ChecksumSupplier checksumSupplier;
@@ -415,7 +415,7 @@
public void setKotlinInfo(KotlinClassLevelInfo kotlinInfo) {
assert kotlinInfo != null;
- assert this.kotlinInfo == NO_KOTLIN_INFO || kotlinInfo == NO_KOTLIN_INFO;
+ assert this.kotlinInfo == getNoKotlinInfo() || kotlinInfo == getNoKotlinInfo();
this.kotlinInfo = kotlinInfo;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
index 2c17201..dfa8a95 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
@@ -26,10 +26,6 @@
private final Set<String> introducedClassTypeVariables = new HashSet<>();
private final Set<String> introducedMethodTypeVariables = new HashSet<>();
- // Wildcards can only be called be used in certain positions:
- // https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
- private boolean canUseWildcardInArguments = true;
-
private GenericSignaturePartialTypeArgumentApplier(
Map<String, DexType> substitutions, DexType objectType) {
this.substitutions = substitutions;
@@ -37,10 +33,9 @@
}
public static GenericSignaturePartialTypeArgumentApplier build(
- AppView<?> appView, ClassSignature classSignature, Map<String, DexType> substitutions) {
+ DexType objectType, ClassSignature classSignature, Map<String, DexType> substitutions) {
GenericSignaturePartialTypeArgumentApplier applier =
- new GenericSignaturePartialTypeArgumentApplier(
- substitutions, appView.dexItemFactory().objectType);
+ new GenericSignaturePartialTypeArgumentApplier(substitutions, objectType);
classSignature.formalTypeParameters.forEach(
parameter -> applier.introducedClassTypeVariables.add(parameter.name));
return applier;
@@ -97,11 +92,7 @@
if (interfaceSignatures.isEmpty()) {
return interfaceSignatures;
}
- canUseWildcardInArguments = false;
- List<ClassTypeSignature> map =
- ListUtils.mapOrElse(interfaceSignatures, this::visitSuperInterface);
- canUseWildcardInArguments = true;
- return map;
+ return ListUtils.mapOrElse(interfaceSignatures, this::visitSuperInterface);
}
@Override
@@ -109,7 +100,9 @@
if (typeArguments.isEmpty()) {
return typeArguments;
}
- return ListUtils.mapOrElse(typeArguments, this::visitFieldTypeSignature);
+ // Wildcards can only be called be used in certain positions:
+ // https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
+ return ListUtils.mapOrElse(typeArguments, arg -> visitFieldTypeSignature(arg, true));
}
@Override
@@ -172,14 +165,16 @@
@Override
public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
- canUseWildcardInArguments = false;
- ClassTypeSignature visit = classTypeSignature.visit(this);
- canUseWildcardInArguments = true;
- return visit;
+ return classTypeSignature.visit(this);
}
@Override
public FieldTypeSignature visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ return visitFieldTypeSignature(fieldSignature, false);
+ }
+
+ private FieldTypeSignature visitFieldTypeSignature(
+ FieldTypeSignature fieldSignature, boolean canUseWildcardInArguments) {
if (fieldSignature.isStar()) {
return fieldSignature;
} else if (fieldSignature.isClassTypeSignature()) {
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVariableRemover.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVariableRemover.java
index f33c9c3..23500e9 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVariableRemover.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVariableRemover.java
@@ -37,7 +37,7 @@
}
GenericSignaturePartialTypeArgumentApplier genericSignatureTypeArgumentApplier =
GenericSignaturePartialTypeArgumentApplier.build(
- appView, clazz.getClassSignature(), substitutions);
+ appView.dexItemFactory().objectType, clazz.getClassSignature(), substitutions);
clazz.setClassSignature(
genericSignatureTypeArgumentApplier.visitClassSignature(clazz.getClassSignature()));
clazz
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 88c0bc7..88d0852 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -346,6 +346,26 @@
return lookupMethod(method, null, null).getReference();
}
+ public final MethodLookupResult lookupInvokeDirect(DexMethod method, ProgramMethod context) {
+ return lookupMethod(method, context.getReference(), Type.DIRECT);
+ }
+
+ public final MethodLookupResult lookupInvokeInterface(DexMethod method, ProgramMethod context) {
+ return lookupMethod(method, context.getReference(), Type.INTERFACE);
+ }
+
+ public final MethodLookupResult lookupInvokeStatic(DexMethod method, ProgramMethod context) {
+ return lookupMethod(method, context.getReference(), Type.STATIC);
+ }
+
+ public final MethodLookupResult lookupInvokeSuper(DexMethod method, ProgramMethod context) {
+ return lookupMethod(method, context.getReference(), Type.SUPER);
+ }
+
+ public final MethodLookupResult lookupInvokeVirtual(DexMethod method, ProgramMethod context) {
+ return lookupMethod(method, context.getReference(), Type.VIRTUAL);
+ }
+
/** Lookup a rebound or non-rebound method reference using the current graph lens. */
public abstract MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type);
diff --git a/src/main/java/com/android/tools/r8/graph/InitClassLens.java b/src/main/java/com/android/tools/r8/graph/InitClassLens.java
index e3e7fd2..2b74f9f 100644
--- a/src/main/java/com/android/tools/r8/graph/InitClassLens.java
+++ b/src/main/java/com/android/tools/r8/graph/InitClassLens.java
@@ -13,8 +13,8 @@
return new Builder();
}
- public static DefaultInitClassLens getDefault() {
- return DefaultInitClassLens.getInstance();
+ public static ThrowingInitClassLens getThrowingInstance() {
+ return ThrowingInitClassLens.getInstance();
}
public abstract DexField getInitClassField(DexType clazz);
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramField.java b/src/main/java/com/android/tools/r8/graph/ProgramField.java
index 42d2453..3e2a276 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramField.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramField.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
public class ProgramField extends DexClassAndField
implements ProgramMember<DexEncodedField, DexField> {
@@ -57,4 +58,14 @@
assert holder.isProgramClass();
return holder.asProgramClass();
}
+
+ @Override
+ public KotlinMemberLevelInfo getKotlinInfo() {
+ return getDefinition().getKotlinInfo();
+ }
+
+ @Override
+ public void clearKotlinInfo() {
+ getDefinition().clearKotlinInfo();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMember.java b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
index fc55509..1234378 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMember.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
+
public interface ProgramMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
extends ProgramDefinition {
@@ -18,4 +20,8 @@
DexProgramClass getHolder();
DexType getHolderType();
+
+ KotlinMemberLevelInfo getKotlinInfo();
+
+ void clearKotlinInfo();
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index a8817dc..617f620 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
@@ -94,6 +95,16 @@
return holder.asProgramClass();
}
+ @Override
+ public KotlinMemberLevelInfo getKotlinInfo() {
+ return getDefinition().getKotlinInfo();
+ }
+
+ @Override
+ public void clearKotlinInfo() {
+ getDefinition().clearKotlinMemberInfo();
+ }
+
public MethodPosition getPosition() {
return getDefinition().getPosition();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInitClassLens.java b/src/main/java/com/android/tools/r8/graph/ThrowingInitClassLens.java
similarity index 71%
rename from src/main/java/com/android/tools/r8/graph/DefaultInitClassLens.java
rename to src/main/java/com/android/tools/r8/graph/ThrowingInitClassLens.java
index ec2f4c6..730a873 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInitClassLens.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowingInitClassLens.java
@@ -6,13 +6,13 @@
import com.android.tools.r8.errors.Unreachable;
-public class DefaultInitClassLens extends InitClassLens {
+public class ThrowingInitClassLens extends InitClassLens {
- private static final DefaultInitClassLens INSTANCE = new DefaultInitClassLens();
+ private static final ThrowingInitClassLens INSTANCE = new ThrowingInitClassLens();
- private DefaultInitClassLens() {}
+ private ThrowingInitClassLens() {}
- public static DefaultInitClassLens getInstance() {
+ public static ThrowingInitClassLens getInstance() {
return INSTANCE;
}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 8f64090..565511a 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -51,7 +51,9 @@
registerInvokeStatic(method);
}
- public abstract void registerNewInstance(DexType type);
+ public void registerNewInstance(DexType type) {
+ registerTypeReference(type);
+ }
public abstract void registerStaticFieldRead(DexField field);
@@ -67,7 +69,9 @@
public abstract void registerTypeReference(DexType type);
- public abstract void registerInstanceOf(DexType type);
+ public void registerInstanceOf(DexType type) {
+ registerTypeReference(type);
+ }
public void registerConstClass(
DexType type, ListIterator<? extends CfOrDexInstruction> iterator) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
index 7783f20..5e0a18e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
@@ -26,7 +26,7 @@
private boolean verifyNoUnexpectedKotlinMemberInfo(DexProgramClass clazz) {
assert Streams.stream(clazz.members())
- .allMatch(member -> member.getKotlinMemberInfo().isNoKotlinInformation());
+ .allMatch(member -> member.getKotlinInfo().isNoKotlinInformation());
return true;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
index 6a84212..753ac5d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy.ClassKind;
import com.android.tools.r8.synthesis.SyntheticItems;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
public class SyntheticItemsPolicy extends MultiClassSameReferencePolicy<ClassKind> {
@@ -34,19 +33,9 @@
return ClassKind.NOT_SYNTHETIC;
}
- // Do not allow merging synthetics that are not lambdas.
- if (!syntheticItems.isNonLegacySynthetic(clazz)
- || syntheticItems.getNonLegacySyntheticKind(clazz) != SyntheticKind.LAMBDA) {
- return ineligibleForClassMerging();
- }
-
- // Allow merging Java lambdas with Java lambdas.
- if (appView.options().horizontalClassMergerOptions().isJavaLambdaMergingEnabled()) {
- return ClassKind.SYNTHETIC;
- }
-
- // Java lambda merging is disabled.
- return ineligibleForClassMerging();
+ return syntheticItems.isEligibleForClassMerging(clazz)
+ ? ClassKind.SYNTHETIC
+ : ineligibleForClassMerging();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index bf81f7e..be5a107 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.ir.analysis.proto;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
@@ -25,13 +27,17 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.DefaultTreePrunerConfiguration;
import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.Mode;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.TreePrunerConfiguration;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -71,8 +77,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final ProtoReferences references;
- private final Set<DexType> classesWithRemovedExtensionFields = Sets.newIdentityHashSet();
- private final Set<DexField> removedExtensionFields = Sets.newIdentityHashSet();
+ private final Map<DexType, Map<DexField, Mode>> removedExtensionFields = new IdentityHashMap<>();
GeneratedExtensionRegistryShrinker(
AppView<AppInfoWithLiveness> appView, ProtoReferences references) {
@@ -92,14 +97,16 @@
* of these methods and replace the reads of these fields by null.
*/
public TreePrunerConfiguration run(Enqueuer.Mode mode) {
- forEachDeadProtoExtensionField(this::recordDeadProtoExtensionField);
+ forEachDeadProtoExtensionField(field -> recordDeadProtoExtensionField(field, mode));
appView.appInfo().getFieldAccessInfoCollection().removeIf((field, info) -> wasRemoved(field));
return createTreePrunerConfiguration(mode);
}
- private void recordDeadProtoExtensionField(DexField field) {
- classesWithRemovedExtensionFields.add(field.holder);
- removedExtensionFields.add(field);
+ private void recordDeadProtoExtensionField(DexField field, Enqueuer.Mode mode) {
+ assert mode.isInitialTreeShaking() || mode.isFinalTreeShaking();
+ removedExtensionFields
+ .computeIfAbsent(field.getHolderType(), ignore -> new IdentityHashMap<>())
+ .put(field, mode);
}
private TreePrunerConfiguration createTreePrunerConfiguration(Enqueuer.Mode mode) {
@@ -124,7 +131,7 @@
*/
public void rewriteCode(DexEncodedMethod method, IRCode code) {
if (method.isClassInitializer()
- && classesWithRemovedExtensionFields.contains(method.getHolderType())
+ && removedExtensionFields.containsKey(method.getHolderType())
&& code.metadata().mayHaveStaticPut()) {
rewriteClassInitializer(code);
}
@@ -133,7 +140,7 @@
private void rewriteClassInitializer(IRCode code) {
List<StaticPut> toBeRemoved = new ArrayList<>();
for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) {
- if (removedExtensionFields.contains(staticPut.getField())) {
+ if (wasRemoved(staticPut.getField())) {
toBeRemoved.add(staticPut);
}
}
@@ -147,7 +154,9 @@
}
public boolean wasRemoved(DexField field) {
- return removedExtensionFields.contains(field);
+ return removedExtensionFields
+ .getOrDefault(field.getHolderType(), Collections.emptyMap())
+ .containsKey(field);
}
public void postOptimizeGeneratedExtensionRegistry(
@@ -155,7 +164,7 @@
throws ExecutionException {
timing.begin("[Proto] Post optimize generated extension registry");
SortedProgramMethodSet wave =
- SortedProgramMethodSet.create(this::forEachFindLiteExtensionByNumberMethod);
+ SortedProgramMethodSet.create(this::forEachMethodThatRequiresPostOptimization);
OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView);
methodProcessor.forEachWaveWithExtension(
(method, methodProcessingContext) ->
@@ -168,6 +177,34 @@
timing.end();
}
+ private void forEachMethodThatRequiresPostOptimization(Consumer<ProgramMethod> consumer) {
+ forEachClassInitializerWithRemovedExtensionFields(consumer, Enqueuer.Mode.FINAL_TREE_SHAKING);
+ forEachFindLiteExtensionByNumberMethod(consumer);
+ }
+
+ private void forEachClassInitializerWithRemovedExtensionFields(
+ Consumer<ProgramMethod> consumer, Enqueuer.Mode modeOfInterest) {
+ Set<DexType> classesWithRemovedExtensionFieldsInModeOfInterest = Sets.newIdentityHashSet();
+ removedExtensionFields
+ .values()
+ .forEach(
+ removedExtensionFieldsForHolder ->
+ removedExtensionFieldsForHolder.forEach(
+ (field, mode) -> {
+ if (mode == modeOfInterest) {
+ classesWithRemovedExtensionFieldsInModeOfInterest.add(
+ field.getHolderType());
+ }
+ }));
+ classesWithRemovedExtensionFieldsInModeOfInterest.forEach(
+ type -> {
+ DexProgramClass clazz = asProgramClassOrNull(appView.appInfo().definitionFor(type));
+ if (clazz != null && clazz.hasClassInitializer()) {
+ consumer.accept(clazz.getProgramClassInitializer());
+ }
+ });
+ }
+
private void forEachFindLiteExtensionByNumberMethod(Consumer<ProgramMethod> consumer) {
appView
.appInfo()
@@ -183,9 +220,10 @@
});
}
- public void handleFailedOrUnknownFieldResolution(DexField fieldReference, ProgramMethod context) {
- if (references.isFindLiteExtensionByNumberMethod(context)) {
- removedExtensionFields.add(fieldReference);
+ public void handleFailedOrUnknownFieldResolution(
+ DexField fieldReference, ProgramMethod context, Enqueuer.Mode mode) {
+ if (mode.isTreeShaking() && references.isFindLiteExtensionByNumberMethod(context)) {
+ recordDeadProtoExtensionField(fieldReference, mode);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index 2a6be81..17758fa 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -59,14 +59,22 @@
* not trace any static-get instructions in every implementation of dynamicMethod() that accesses
* an 'INSTANCE' or a 'DEFAULT_INSTANCE' field.
*
+ * <p>The motivation for this is that the proto shrinker will optimize the proto schemas in each
+ * dynamicMethod() after the second round of tree shaking. This is done by {@link
+ * GeneratedMessageLiteShrinker#postOptimizeDynamicMethods}. If we traced all static field reads
+ * as the {@link DefaultEnqueuerUseRegistry} we would end up retaining the types that are
+ * references from the non-optimized proto schemas, but which do not end up being referenced from
+ * the final optimized proto schemas.
+ *
* <p>The static-get instructions that remain after the proto schema has been optimized will be
* traced manually by {@link ProtoEnqueuerExtension#tracePendingInstructionsInDynamicMethods}.
*/
@Override
public void registerStaticFieldRead(DexField field) {
if (references.isDynamicMethod(getContextMethod())
+ && field.getHolderType() != getContextHolder().getType()
&& isStaticFieldReadForProtoSchemaDefinition(field)) {
- enqueuer.addDeadProtoTypeCandidate(field.holder);
+ enqueuer.addDeadProtoTypeCandidate(field.getHolderType());
return;
}
super.registerStaticFieldRead(field);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
index a0f75d7..c83f118 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldTypeFactory;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -26,9 +27,8 @@
private Set<DexType> deadProtoTypes = Sets.newIdentityHashSet();
- public ProtoShrinker(AppView<AppInfoWithLiveness> appView) {
+ public ProtoShrinker(AppView<AppInfoWithLiveness> appView, ProtoReferences references) {
ProtoFieldTypeFactory factory = new ProtoFieldTypeFactory();
- ProtoReferences references = new ProtoReferences(appView.dexItemFactory());
this.decoder = new RawMessageInfoDecoder(factory, references);
this.factory = factory;
this.generatedExtensionRegistryShrinker =
@@ -54,6 +54,26 @@
this.references = references;
}
+ public static ProtoShrinker create(AppView<AppInfoWithLiveness> appView) {
+ if (!appView.enableWholeProgramOptimizations()
+ || !appView.options().protoShrinking().isProtoShrinkingEnabled()) {
+ return null;
+ }
+
+ ProtoReferences references = new ProtoReferences(appView.dexItemFactory());
+ if (appView.definitionFor(references.generatedMessageLiteType) == null) {
+ appView
+ .reporter()
+ .warning(
+ new StringDiagnostic(
+ "Ignoring -shrinkunusedprotofields since the protobuf-lite runtime is missing"));
+ appView.options().protoShrinking().disable();
+ return null;
+ }
+
+ return new ProtoShrinker(appView, references);
+ }
+
public Set<DexType> getDeadProtoTypes() {
return deadProtoTypes;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 6fe8c6f..d076960 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -83,6 +83,26 @@
return dexOpcodeRange;
}
+ public boolean isDirect() {
+ return this == DIRECT;
+ }
+
+ public boolean isInterface() {
+ return this == INTERFACE;
+ }
+
+ public boolean isStatic() {
+ return this == STATIC;
+ }
+
+ public boolean isSuper() {
+ return this == SUPER;
+ }
+
+ public boolean isVirtual() {
+ return this == VIRTUAL;
+ }
+
public MethodHandleType toMethodHandle(DexMethod targetMethod) {
switch (this) {
case STATIC:
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index e8f4756..e8dc257 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar.itf;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.emulateInterfaceLibraryMethod;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -214,7 +216,7 @@
emulationMethods.add(
DexEncodedMethod.toEmulateDispatchLibraryMethod(
method.getHolderType(),
- rewriter.emulateInterfaceLibraryMethod(method),
+ emulateInterfaceLibraryMethod(method, rewriter.factory),
companionMethod,
libraryMethod,
extraDispatchCases,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 180c2aa..ea1d102 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -744,7 +744,7 @@
assert !defaultMethod.getAccessFlags().isAbstract();
instructions.replaceCurrentInstruction(
new InvokeStatic(
- emulateInterfaceLibraryMethod(defaultMethod),
+ emulateInterfaceLibraryMethod(defaultMethod, factory),
invokeMethod.outValue(),
invokeMethod.arguments()));
}
@@ -773,7 +773,8 @@
return false;
}
- DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
+ public static DexMethod emulateInterfaceLibraryMethod(
+ DexClassAndMethod method, DexItemFactory factory) {
return factory.createMethod(
getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
factory.prependTypeToProto(method.getHolderType(), method.getProto()),
@@ -786,7 +787,7 @@
+ ";";
}
- static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
+ public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
assert type.isClassType();
String descriptor = type.descriptor.toString();
String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index a48292a..436f996 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -908,11 +908,7 @@
.verifySyntheticLambdaProperty(
context.getHolder(),
lambdaClass ->
- appView.appInfo().hasPinnedInstanceInitializer(lambdaClass.getType())
- || appView
- .options()
- .horizontalClassMergerOptions()
- .isJavaLambdaMergingEnabled(),
+ appView.appInfo().hasPinnedInstanceInitializer(lambdaClass.getType()),
nonLambdaClass -> true)
: "Unexpected observable side effects from lambda `" + context.toSourceString() + "`";
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index ca49a7d..33f4ba5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -167,10 +167,14 @@
}
if (histogramOfLengthOfPartialResult != null) {
Log.info(getClass(), "------ histogram of StringBuilder partial result lengths ------");
- histogramOfLengthOfPartialResult.forEach((length, count) -> {
- Log.info(getClass(),
- "%s: %s (%s)", length, StringUtils.times("*", Math.min(count, 53)), count);
- });
+ histogramOfLengthOfPartialResult.forEach(
+ (length, count) ->
+ Log.info(
+ getClass(),
+ "%s: %s (%s)",
+ length,
+ StringUtils.times("*", Math.min(count, 53)),
+ count));
}
}
@@ -712,20 +716,7 @@
builder, optimizationConfiguration)) {
return null;
}
- String result = StringUtils.join("", contents);
- int estimate = estimateSizeReduction(contents);
- return estimate > result.length() ? result : null;
- }
-
- private int estimateSizeReduction(List<String> contents) {
- int result = 8; // builder initialization
- for (String content : contents) {
- result += 4; // builder append()
- // If a certain string is only used as part of the resulting string, it will be gone.
- result += (int) (content.length() * 0.5); // Magic number of that chance: 50%.
- }
- result += 4; // builder toString()
- return result;
+ return StringUtils.join("", contents);
}
void removeTrivialBuilders() {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
index 953766c..883be66 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
@@ -10,14 +10,13 @@
import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
-import com.android.tools.r8.utils.Box;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import kotlinx.metadata.KmAnnotation;
+import java.util.function.Consumer;
import kotlinx.metadata.KmAnnotationArgument;
import kotlinx.metadata.KmAnnotationArgument.AnnotationValue;
import kotlinx.metadata.KmAnnotationArgument.ArrayValue;
@@ -29,7 +28,8 @@
private static final Map<String, KotlinAnnotationArgumentInfo> EMPTY_ARGUMENTS =
ImmutableMap.of();
- abstract KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens);
+ abstract boolean rewrite(
+ Consumer<KmAnnotationArgument<?>> consumer, AppView<?> appView, NamingLens namingLens);
private static KotlinAnnotationArgumentInfo createArgument(
KmAnnotationArgument<?> arg, DexItemFactory factory) {
@@ -75,9 +75,13 @@
}
@Override
- KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
- return new KClassValue(
- value.toRenamedBinaryNameOrDefault(appView, namingLens, ClassClassifiers.anyName));
+ boolean rewrite(
+ Consumer<KmAnnotationArgument<?>> consumer, AppView<?> appView, NamingLens namingLens) {
+ return value.toRenamedBinaryNameOrDefault(
+ rewrittenValue -> consumer.accept(new KClassValue(rewrittenValue)),
+ appView,
+ namingLens,
+ ClassClassifiers.anyName);
}
}
@@ -103,10 +107,14 @@
}
@Override
- KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
- return new EnumValue(
- enumClassName.toRenamedBinaryNameOrDefault(appView, namingLens, ClassClassifiers.anyName),
- enumEntryName);
+ boolean rewrite(
+ Consumer<KmAnnotationArgument<?>> consumer, AppView<?> appView, NamingLens namingLens) {
+ return enumClassName.toRenamedBinaryNameOrDefault(
+ rewrittenEnumClassName ->
+ consumer.accept(new EnumValue(rewrittenEnumClassName, enumEntryName)),
+ appView,
+ namingLens,
+ ClassClassifiers.anyName);
}
}
@@ -130,13 +138,16 @@
}
@Override
- KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
- Box<KmAnnotation> rewrittenAnnotation = new Box<>();
- value.rewrite(rewrittenAnnotation::set, appView, namingLens);
- if (rewrittenAnnotation.isSet()) {
- return new AnnotationValue(rewrittenAnnotation.get());
- }
- return null;
+ boolean rewrite(
+ Consumer<KmAnnotationArgument<?>> consumer, AppView<?> appView, NamingLens namingLens) {
+ return value.rewrite(
+ rewrittenAnnotation -> {
+ if (rewrittenAnnotation != null) {
+ consumer.accept(new AnnotationValue(rewrittenAnnotation));
+ }
+ },
+ appView,
+ namingLens);
}
}
@@ -170,16 +181,23 @@
}
@Override
- KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(
+ Consumer<KmAnnotationArgument<?>> consumer, AppView<?> appView, NamingLens namingLens) {
List<KmAnnotationArgument<?>> rewrittenArguments = new ArrayList<>();
+ boolean rewritten = false;
for (KotlinAnnotationArgumentInfo kotlinAnnotationArgumentInfo : value) {
- KmAnnotationArgument<?> rewrittenArg =
- kotlinAnnotationArgumentInfo.rewrite(appView, namingLens);
- if (rewrittenArg != null) {
- rewrittenArguments.add(rewrittenArg);
- }
+ rewritten |=
+ kotlinAnnotationArgumentInfo.rewrite(
+ rewrittenArg -> {
+ if (rewrittenArg != null) {
+ rewrittenArguments.add(rewrittenArg);
+ }
+ },
+ appView,
+ namingLens);
}
- return new ArrayValue(rewrittenArguments);
+ consumer.accept(new ArrayValue(rewrittenArguments));
+ return rewritten;
}
}
@@ -201,8 +219,10 @@
}
@Override
- KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
- return argument;
+ boolean rewrite(
+ Consumer<KmAnnotationArgument<?>> consumer, AppView<?> appView, NamingLens namingLens) {
+ consumer.accept(argument);
+ return false;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
index 45adb46..fd036d8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
+import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
import java.util.LinkedHashMap;
@@ -48,26 +49,38 @@
return builder.build();
}
- public void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmAnnotationVisitorProvider visitorProvider,
AppView<?> appView,
NamingLens namingLens) {
- String renamedDescriptor =
- annotationType.toRenamedDescriptorOrDefault(appView, namingLens, null);
- if (renamedDescriptor == null) {
- // The type has been pruned
- return;
- }
- String classifier = DescriptorUtils.descriptorToKotlinClassifier(renamedDescriptor);
- Map<String, KmAnnotationArgument<?>> rewrittenArguments = new LinkedHashMap<>();
- arguments.forEach(
- (key, arg) -> {
- KmAnnotationArgument<?> rewrittenArg = arg.rewrite(appView, namingLens);
- if (rewrittenArg != null) {
- rewrittenArguments.put(key, rewrittenArg);
- }
- });
- visitorProvider.get(new KmAnnotation(classifier, rewrittenArguments));
+ BooleanBox rewritten = new BooleanBox(false);
+ rewritten.or(
+ annotationType.toRenamedDescriptorOrDefault(
+ renamedDescriptor -> {
+ if (renamedDescriptor == null) {
+ // The type has been pruned
+ rewritten.set(true);
+ return;
+ }
+ String classifier = DescriptorUtils.descriptorToKotlinClassifier(renamedDescriptor);
+ Map<String, KmAnnotationArgument<?>> rewrittenArguments = new LinkedHashMap<>();
+ arguments.forEach(
+ (key, arg) ->
+ rewritten.or(
+ arg.rewrite(
+ rewrittenArg -> {
+ if (rewrittenArg != null) {
+ rewrittenArguments.put(key, rewrittenArg);
+ }
+ },
+ appView,
+ namingLens)));
+ visitorProvider.get(new KmAnnotation(classifier, rewrittenArguments));
+ },
+ appView,
+ namingLens,
+ null));
+ return rewritten.get();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index f10902f..e440049 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
import java.util.HashMap;
@@ -84,13 +85,18 @@
}
public static KotlinClassInfo create(
- KmClass kmClass,
+ KotlinClassMetadata.Class metadata,
String packageName,
int[] metadataVersion,
DexClass hostClass,
- DexItemFactory factory,
- Reporter reporter,
+ AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode) {
+ DexItemFactory factory = appView.dexItemFactory();
+ Reporter reporter = appView.reporter();
+ KmClass kmClass = metadata.toKmClass();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(
+ metadata, appView.options());
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : hostClass.fields()) {
fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
@@ -100,9 +106,12 @@
methodMap.put(toJvmMethodSignature(method.getReference()).asString(), method);
}
ImmutableList.Builder<KotlinConstructorInfo> notBackedConstructors = ImmutableList.builder();
+ int constructorIndex = 0;
for (KmConstructor kmConstructor : kmClass.getConstructors()) {
+ boolean readConstructorSignature =
+ extensionInformation.hasJvmMethodSignatureExtensionForConstructor(constructorIndex++);
KotlinConstructorInfo constructorInfo =
- KotlinConstructorInfo.create(kmConstructor, factory, reporter);
+ KotlinConstructorInfo.create(kmConstructor, factory, reporter, readConstructorSignature);
JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmConstructor);
if (signature != null) {
DexEncodedMethod method = methodMap.get(signature.asString());
@@ -116,7 +125,7 @@
}
KotlinDeclarationContainerInfo container =
KotlinDeclarationContainerInfo.create(
- kmClass, methodMap, fieldMap, factory, reporter, keepByteCode);
+ kmClass, methodMap, fieldMap, factory, reporter, keepByteCode, extensionInformation);
setCompanionObject(kmClass, hostClass, reporter);
return new KotlinClassInfo(
kmClass.getFlags(),
@@ -185,7 +194,7 @@
}
for (DexEncodedField field : hostClass.fields()) {
if (field.getReference().name.toString().equals(companionObjectName)) {
- field.setKotlinMemberInfo(new KotlinCompanionInfo());
+ field.setKotlinMemberInfo(new KotlinCompanionInfo(companionObjectName));
return;
}
}
@@ -204,7 +213,8 @@
}
@Override
- public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ public Pair<KotlinClassHeader, Boolean> rewrite(
+ DexClass clazz, AppView<?> appView, NamingLens namingLens) {
KmClass kmClass = new KmClass();
// TODO(b/154348683): Set flags.
kmClass.setFlags(flags);
@@ -214,88 +224,110 @@
// If the original descriptor equals the rewritten descriptor, we pick the original name
// to preserve potential errors in the original name. As an example, the kotlin stdlib has
// name: .kotlin/collections/CollectionsKt___CollectionsKt$groupingBy$1, which seems incorrect.
+ boolean rewritten = !originalDescriptor.equals(rewrittenDescriptor);
kmClass.setName(
- originalDescriptor.equals(rewrittenDescriptor)
+ !rewritten
? this.name
: DescriptorUtils.getBinaryNameFromDescriptor(rewrittenDescriptor.toString()));
// Find a companion object.
for (DexEncodedField field : clazz.fields()) {
- if (field.getKotlinMemberInfo().isCompanion()) {
- field
- .getKotlinMemberInfo()
- .asCompanion()
- .rewrite(kmClass, field.getReference(), namingLens);
+ if (field.getKotlinInfo().isCompanion()) {
+ rewritten |=
+ field.getKotlinInfo().asCompanion().rewrite(kmClass, field.getReference(), namingLens);
}
}
// Take all not backed constructors because we will never find them in definitions.
for (KotlinConstructorInfo constructorInfo : constructorsWithNoBacking) {
- constructorInfo.rewrite(kmClass, null, appView, namingLens);
+ rewritten |= constructorInfo.rewrite(kmClass, null, appView, namingLens);
}
// Find all constructors.
for (DexEncodedMethod method : clazz.methods()) {
- if (method.getKotlinMemberInfo().isConstructor()) {
- KotlinConstructorInfo constructorInfo = method.getKotlinMemberInfo().asConstructor();
- constructorInfo.rewrite(kmClass, method, appView, namingLens);
+ if (method.getKotlinInfo().isConstructor()) {
+ KotlinConstructorInfo constructorInfo = method.getKotlinInfo().asConstructor();
+ rewritten |= constructorInfo.rewrite(kmClass, method, appView, namingLens);
}
}
// Rewrite functions, type-aliases and type-parameters.
- declarationContainerInfo.rewrite(
- kmClass::visitFunction,
- kmClass::visitProperty,
- kmClass::visitTypeAlias,
- clazz,
- appView,
- namingLens);
+ rewritten |=
+ declarationContainerInfo.rewrite(
+ kmClass::visitFunction,
+ kmClass::visitProperty,
+ kmClass::visitTypeAlias,
+ clazz,
+ appView,
+ namingLens);
// Rewrite type parameters.
for (KotlinTypeParameterInfo typeParameter : typeParameters) {
- typeParameter.rewrite(kmClass::visitTypeParameter, appView, namingLens);
+ rewritten |= typeParameter.rewrite(kmClass::visitTypeParameter, appView, namingLens);
}
// Rewrite super types.
for (KotlinTypeInfo superType : superTypes) {
// Ensure the rewritten super type is not this type.
if (clazz.getType() != superType.rewriteType(appView.graphLens())) {
- superType.rewrite(kmClass::visitSupertype, appView, namingLens);
+ rewritten |= superType.rewrite(kmClass::visitSupertype, appView, namingLens);
+ } else {
+ rewritten = true;
}
}
// Rewrite nested classes.
for (KotlinTypeReference nestedClass : nestedClasses) {
- String nestedDescriptor = nestedClass.toRenamedBinaryNameOrDefault(appView, namingLens, null);
- if (nestedDescriptor != null) {
- // If the class is a nested class, it should be on the form Foo.Bar$Baz, where Baz is the
- // name we should record.
- int innerClassIndex = nestedDescriptor.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
- kmClass.visitNestedClass(nestedDescriptor.substring(innerClassIndex + 1));
- }
+ rewritten |=
+ nestedClass.toRenamedBinaryNameOrDefault(
+ nestedDescriptor -> {
+ if (nestedDescriptor != null) {
+ // If the class is a nested class, it should be on the form Foo.Bar$Baz, where Baz
+ // is the
+ // name we should record.
+ int innerClassIndex =
+ nestedDescriptor.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
+ kmClass.visitNestedClass(nestedDescriptor.substring(innerClassIndex + 1));
+ }
+ },
+ appView,
+ namingLens,
+ null);
}
// Rewrite sealed sub classes.
for (KotlinTypeReference sealedSubClass : sealedSubClasses) {
- String sealedDescriptor =
- sealedSubClass.toRenamedBinaryNameOrDefault(appView, namingLens, null);
- if (sealedDescriptor != null) {
- kmClass.visitSealedSubclass(
- sealedDescriptor.replace(
- DescriptorUtils.INNER_CLASS_SEPARATOR, DescriptorUtils.JAVA_PACKAGE_SEPARATOR));
- }
+ rewritten |=
+ sealedSubClass.toRenamedBinaryNameOrDefault(
+ sealedDescriptor -> {
+ if (sealedDescriptor != null) {
+ kmClass.visitSealedSubclass(
+ sealedDescriptor.replace(
+ DescriptorUtils.INNER_CLASS_SEPARATOR,
+ DescriptorUtils.JAVA_PACKAGE_SEPARATOR));
+ }
+ },
+ appView,
+ namingLens,
+ null);
}
// TODO(b/154347404): Understand enum entries.
kmClass.getEnumEntries().addAll(enumEntries);
- versionRequirements.rewrite(kmClass::visitVersionRequirement);
+ rewritten |= versionRequirements.rewrite(kmClass::visitVersionRequirement);
JvmClassExtensionVisitor extensionVisitor =
(JvmClassExtensionVisitor) kmClass.visitExtensions(JvmClassExtensionVisitor.TYPE);
extensionVisitor.visitModuleName(moduleName);
if (anonymousObjectOrigin != null) {
- String renamedAnon =
- anonymousObjectOrigin.toRenamedBinaryNameOrDefault(appView, namingLens, null);
- if (renamedAnon != null) {
- extensionVisitor.visitAnonymousObjectOriginName(renamedAnon);
- }
+ rewritten |=
+ anonymousObjectOrigin.toRenamedBinaryNameOrDefault(
+ renamedAnon -> {
+ if (renamedAnon != null) {
+ extensionVisitor.visitAnonymousObjectOriginName(renamedAnon);
+ }
+ },
+ appView,
+ namingLens,
+ null);
}
- localDelegatedProperties.rewrite(
- extensionVisitor::visitLocalDelegatedProperty, appView, namingLens);
+ rewritten |=
+ localDelegatedProperties.rewrite(
+ extensionVisitor::visitLocalDelegatedProperty, appView, namingLens);
extensionVisitor.visitEnd();
KotlinClassMetadata.Class.Writer writer = new KotlinClassMetadata.Class.Writer();
kmClass.accept(writer);
- return writer.write().getHeader();
+ return Pair.create(writer.write().getHeader(), rewritten);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
index 1d968d2..34c60de 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
+import com.android.tools.r8.utils.Pair;
import kotlinx.metadata.jvm.KotlinClassHeader;
public interface KotlinClassLevelInfo extends EnqueuerMetadataTraceable {
@@ -56,7 +57,8 @@
return null;
}
- KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens);
+ Pair<KotlinClassHeader, Boolean> rewrite(
+ DexClass clazz, AppView<?> appView, NamingLens namingLens);
String getPackageName();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 6fc09f5..bfabd6c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -3,8 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.INVALID_KOTLIN_INFO;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getInvalidKotlinInfo;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.getFlavour;
import com.android.tools.r8.graph.AppView;
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.Flavour;
-import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -27,49 +26,49 @@
import kotlinx.metadata.InconsistentKotlinMetadataException;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassFacade;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
public final class KotlinClassMetadataReader {
public static KotlinClassLevelInfo getKotlinInfo(
- Kotlin kotlin,
- DexClass clazz,
- DexItemFactory factory,
- Reporter reporter,
- Consumer<DexEncodedMethod> keepByteCode) {
- DexAnnotation meta = clazz.annotations().getFirstMatching(factory.kotlinMetadataType);
- if (meta != null) {
- return getKotlinInfo(kotlin, clazz, factory, reporter, keepByteCode, meta);
- }
- return NO_KOTLIN_INFO;
+ DexClass clazz, AppView<?> appView, Consumer<DexEncodedMethod> keepByteCode) {
+ DexAnnotation meta =
+ clazz.annotations().getFirstMatching(appView.dexItemFactory().kotlinMetadataType);
+ return meta != null ? getKotlinInfo(clazz, appView, keepByteCode, meta) : getNoKotlinInfo();
}
public static KotlinClassLevelInfo getKotlinInfo(
- Kotlin kotlin,
DexClass clazz,
- DexItemFactory factory,
- Reporter reporter,
+ AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode,
DexAnnotation annotation) {
try {
+ Kotlin kotlin = appView.dexItemFactory().kotlin;
KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, annotation.annotation);
- return createKotlinInfo(kotlin, clazz, kMetadata, factory, reporter, keepByteCode);
+ return createKotlinInfo(kotlin, clazz, kMetadata, appView, keepByteCode);
} catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
- reporter.info(
- new StringDiagnostic(
- "Class "
- + clazz.type.toSourceString()
- + " has malformed kotlin.Metadata: "
- + e.getMessage()));
- return INVALID_KOTLIN_INFO;
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Class "
+ + clazz.type.toSourceString()
+ + " has malformed kotlin.Metadata: "
+ + e.getMessage()));
+ return getInvalidKotlinInfo();
} catch (Throwable e) {
- reporter.info(
- new StringDiagnostic(
- "Unexpected error while reading "
- + clazz.type.toSourceString()
- + "'s kotlin.Metadata: "
- + e.getMessage()));
- return INVALID_KOTLIN_INFO;
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Unexpected error while reading "
+ + clazz.type.toSourceString()
+ + "'s kotlin.Metadata: "
+ + e.getMessage()));
+ return getNoKotlinInfo();
}
}
@@ -132,46 +131,34 @@
Kotlin kotlin,
DexClass clazz,
KotlinClassMetadata kMetadata,
- DexItemFactory factory,
- Reporter reporter,
+ AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode) {
String packageName = kMetadata.getHeader().getPackageName();
int[] metadataVersion = kMetadata.getHeader().getMetadataVersion();
if (kMetadata instanceof KotlinClassMetadata.Class) {
return KotlinClassInfo.create(
- ((KotlinClassMetadata.Class) kMetadata).toKmClass(),
+ (KotlinClassMetadata.Class) kMetadata,
packageName,
metadataVersion,
clazz,
- factory,
- reporter,
+ appView,
keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
// e.g., B.kt becomes class `BKt`
return KotlinFileFacadeInfo.create(
- (KotlinClassMetadata.FileFacade) kMetadata,
- packageName,
- metadataVersion,
- clazz,
- factory,
- reporter,
- keepByteCode);
+ (FileFacade) kMetadata, packageName, metadataVersion, clazz, appView, keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
// multi-file class with the same @JvmName.
return KotlinMultiFileClassFacadeInfo.create(
- (KotlinClassMetadata.MultiFileClassFacade) kMetadata,
- packageName,
- metadataVersion,
- factory);
+ (MultiFileClassFacade) kMetadata, packageName, metadataVersion, appView.dexItemFactory());
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
// A single file, which is part of multi-file class.
return KotlinMultiFileClassPartInfo.create(
- (KotlinClassMetadata.MultiFileClassPart) kMetadata,
+ (MultiFileClassPart) kMetadata,
packageName,
metadataVersion,
clazz,
- factory,
- reporter,
+ appView,
keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
return KotlinSyntheticClassInfo.create(
@@ -180,8 +167,7 @@
metadataVersion,
clazz,
kotlin,
- factory,
- reporter);
+ appView);
} else {
throw new MetadataError("unsupported 'k' value: " + kMetadata.getHeader().getKind());
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
index 54b8054..5f5b696 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
@@ -48,7 +48,7 @@
}
}
- abstract void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens);
+ abstract boolean rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens);
public DexType rewriteType(GraphLens graphLens) {
return null;
@@ -65,16 +65,20 @@
}
@Override
- void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
- String descriptor =
- type.toRenamedDescriptorOrDefault(appView, namingLens, ClassClassifiers.anyDescriptor);
- // For local or anonymous classes, the classifier is prefixed with '.' and inner classes are
- // separated with '$'.
- if (isLocalOrAnonymous) {
- visitor.visitClass("." + DescriptorUtils.getBinaryNameFromDescriptor(descriptor));
- } else {
- visitor.visitClass(DescriptorUtils.descriptorToKotlinClassifier(descriptor));
- }
+ boolean rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
+ return type.toRenamedDescriptorOrDefault(
+ descriptor -> {
+ // For local or anonymous classes, the classifier is prefixed with '.' and inner classes
+ // are separated with '$'.
+ if (isLocalOrAnonymous) {
+ visitor.visitClass("." + DescriptorUtils.getBinaryNameFromDescriptor(descriptor));
+ } else {
+ visitor.visitClass(DescriptorUtils.descriptorToKotlinClassifier(descriptor));
+ }
+ },
+ appView,
+ namingLens,
+ ClassClassifiers.anyDescriptor);
}
@Override
@@ -97,8 +101,9 @@
}
@Override
- void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
visitor.visitTypeParameter(typeId);
+ return false;
}
@Override
@@ -116,8 +121,9 @@
}
@Override
- void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
visitor.visitTypeAlias(typeAlias);
+ return false;
}
@Override
@@ -134,8 +140,9 @@
}
@Override
- void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
visitor.visitClass(classifier);
+ return false;
}
@Override
@@ -152,8 +159,9 @@
}
@Override
- void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
visitor.visitTypeAlias(classifier);
+ return false;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
index 1e19f1a..f90ed3a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
@@ -13,6 +13,12 @@
// Structure around a kotlin companion object that can be assigned to a field.
public class KotlinCompanionInfo implements KotlinFieldLevelInfo {
+ private final String companionObjectFieldName;
+
+ public KotlinCompanionInfo(String companionObjectFieldName) {
+ this.companionObjectFieldName = companionObjectFieldName;
+ }
+
@Override
public boolean isCompanion() {
return true;
@@ -23,10 +29,11 @@
return this;
}
- public void rewrite(KmClassVisitor visitor, DexField field, NamingLens lens) {
+ boolean rewrite(KmClassVisitor visitor, DexField field, NamingLens lens) {
DexString dexString = lens.lookupName(field);
String finalName = dexString.toString();
visitor.visitCompanionObject(finalName);
+ return !finalName.equals(companionObjectFieldName);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
index 2e4ee98..dd82d58 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -41,28 +41,42 @@
}
public static KotlinConstructorInfo create(
- KmConstructor kmConstructor, DexItemFactory factory, Reporter reporter) {
+ KmConstructor kmConstructor,
+ DexItemFactory factory,
+ Reporter reporter,
+ boolean readConstructorSignature) {
return new KotlinConstructorInfo(
kmConstructor.getFlags(),
KotlinValueParameterInfo.create(kmConstructor.getValueParameters(), factory, reporter),
KotlinVersionRequirementInfo.create(kmConstructor.getVersionRequirements()),
- KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmConstructor), factory));
+ readConstructorSignature
+ ? KotlinJvmMethodSignatureInfo.create(
+ JvmExtensionsKt.getSignature(kmConstructor), factory)
+ : null);
}
- public void rewrite(
+ boolean rewrite(
KmClass kmClass, DexEncodedMethod method, AppView<?> appView, NamingLens namingLens) {
// Note that JvmExtensionsKt.setSignature does not have an overload for KmConstructorVisitor,
// thus we rely on creating the KmConstructor manually.
// TODO(b/154348683): Check for special flags to pass in.
KmConstructor kmConstructor = new KmConstructor(flags);
+ boolean rewritten = false;
if (signature != null) {
- JvmExtensionsKt.setSignature(kmConstructor, signature.rewrite(method, appView, namingLens));
+ rewritten =
+ signature.rewrite(
+ rewrittenSignature -> JvmExtensionsKt.setSignature(kmConstructor, rewrittenSignature),
+ method,
+ appView,
+ namingLens);
}
for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
- valueParameterInfo.rewrite(kmConstructor::visitValueParameter, appView, namingLens);
+ rewritten |=
+ valueParameterInfo.rewrite(kmConstructor::visitValueParameter, appView, namingLens);
}
- versionRequirements.rewrite(kmConstructor::visitVersionRequirement);
+ rewritten |= versionRequirements.rewrite(kmConstructor::visitVersionRequirement);
kmClass.getConstructors().add(kmConstructor);
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
index fd0d60d..2afb66e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
@@ -49,17 +49,19 @@
forEachApply(effects, effect -> effect::trace, definitionSupplier);
}
- public void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmContractVisitorProvider visitorProvider,
AppView<?> appView,
NamingLens namingLens) {
if (this == NO_EFFECT) {
- return;
+ return false;
}
+ boolean rewritten = false;
KmContractVisitor kmContractVisitor = visitorProvider.get();
for (KotlinEffectInfo effect : effects) {
- effect.rewrite(kmContractVisitor::visitEffect, appView, namingLens);
+ rewritten |= effect.rewrite(kmContractVisitor::visitEffect, appView, namingLens);
}
kmContractVisitor.visitEnd();
+ return rewritten;
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index 7447bbd..eb654b5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -55,8 +55,10 @@
Map<String, DexEncodedField> fieldSignatureMap,
DexItemFactory factory,
Reporter reporter,
- Consumer<DexEncodedMethod> keepByteCode) {
+ Consumer<DexEncodedMethod> keepByteCode,
+ KotlinJvmSignatureExtensionInformation extensionInformation) {
ImmutableList.Builder<KotlinFunctionInfo> notBackedFunctions = ImmutableList.builder();
+ int functionCounter = 0;
for (KmFunction kmFunction : container.getFunctions()) {
JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmFunction);
if (signature == null) {
@@ -64,7 +66,11 @@
continue;
}
KotlinFunctionInfo kotlinFunctionInfo =
- KotlinFunctionInfo.create(kmFunction, factory, reporter);
+ KotlinFunctionInfo.create(
+ kmFunction,
+ factory,
+ reporter,
+ extensionInformation.hasJvmMethodSignatureExtensionForFunction(functionCounter++));
DexEncodedMethod method = methodSignatureMap.get(signature.asString());
if (method == null) {
notBackedFunctions.add(kotlinFunctionInfo);
@@ -149,7 +155,7 @@
return builder.build();
}
- public void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmFunctionVisitorProvider functionProvider,
KmVisitorProviders.KmPropertyVisitorProvider propertyProvider,
KmVisitorProviders.KmTypeAliasVisitorProvider typeAliasProvider,
@@ -157,28 +163,26 @@
AppView<?> appView,
NamingLens namingLens) {
// Type aliases only have a representation here, so we can generate them directly.
+ boolean rewritten = false;
for (KotlinTypeAliasInfo typeAlias : typeAliases) {
- typeAlias.rewrite(typeAliasProvider, appView, namingLens);
+ rewritten |= typeAlias.rewrite(typeAliasProvider, appView, namingLens);
}
// For properties, we need to combine potentially a field, setter and getter.
Map<KotlinPropertyInfo, KotlinPropertyGroup> properties = new IdentityHashMap<>();
for (DexEncodedField field : clazz.fields()) {
- if (field.getKotlinMemberInfo().isFieldProperty()) {
+ if (field.getKotlinInfo().isProperty()) {
properties
.computeIfAbsent(
- field.getKotlinMemberInfo().asFieldProperty(), ignored -> new KotlinPropertyGroup())
+ field.getKotlinInfo().asProperty(), ignored -> new KotlinPropertyGroup())
.setBackingField(field);
}
}
for (DexEncodedMethod method : clazz.methods()) {
- if (method.getKotlinMemberInfo().isFunction()) {
- method
- .getKotlinMemberInfo()
- .asFunction()
- .rewrite(functionProvider, method, appView, namingLens);
+ if (method.getKotlinInfo().isFunction()) {
+ method.getKotlinInfo().asFunction().rewrite(functionProvider, method, appView, namingLens);
continue;
}
- KotlinPropertyInfo kotlinPropertyInfo = method.getKotlinMemberInfo().asProperty();
+ KotlinPropertyInfo kotlinPropertyInfo = method.getKotlinInfo().asProperty();
if (kotlinPropertyInfo == null) {
continue;
}
@@ -193,21 +197,24 @@
}
for (KotlinPropertyInfo kotlinPropertyInfo : properties.keySet()) {
KotlinPropertyGroup kotlinPropertyGroup = properties.get(kotlinPropertyInfo);
- kotlinPropertyInfo.rewrite(
- propertyProvider,
- kotlinPropertyGroup.backingField,
- kotlinPropertyGroup.getter,
- kotlinPropertyGroup.setter,
- appView,
- namingLens);
+ rewritten |=
+ kotlinPropertyInfo.rewrite(
+ propertyProvider,
+ kotlinPropertyGroup.backingField,
+ kotlinPropertyGroup.getter,
+ kotlinPropertyGroup.setter,
+ appView,
+ namingLens);
}
// Add all not backed functions and properties.
for (KotlinFunctionInfo notBackedFunction : functionsWithNoBacking) {
- notBackedFunction.rewrite(functionProvider, null, appView, namingLens);
+ rewritten |= notBackedFunction.rewrite(functionProvider, null, appView, namingLens);
}
for (KotlinPropertyInfo notBackedProperty : propertiesWithNoBacking) {
- notBackedProperty.rewrite(propertyProvider, null, null, null, appView, namingLens);
+ rewritten |=
+ notBackedProperty.rewrite(propertyProvider, null, null, null, appView, namingLens);
}
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
index d6664d1..bc7cda0 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
@@ -85,25 +85,27 @@
forEachApply(orArguments, arg -> arg::trace, definitionSupplier);
}
- public void rewrite(
+ boolean rewrite(
KmEffectExpressionVisitorProvider provider, AppView<?> appView, NamingLens namingLens) {
if (this == NO_EXPRESSION) {
- return;
+ return false;
}
KmEffectExpressionVisitor visitor = provider.get();
visitor.visit(flags, parameterIndex);
if (constantValue != null) {
visitor.visitConstantValue(constantValue.getValue());
}
+ boolean rewritten = false;
if (isInstanceType != null) {
- isInstanceType.rewrite(visitor::visitIsInstanceType, appView, namingLens);
+ rewritten |= isInstanceType.rewrite(visitor::visitIsInstanceType, appView, namingLens);
}
for (KotlinEffectExpressionInfo andArgument : andArguments) {
- andArgument.rewrite(visitor::visitAndArgument, appView, namingLens);
+ rewritten |= andArgument.rewrite(visitor::visitAndArgument, appView, namingLens);
}
for (KotlinEffectExpressionInfo orArgument : orArguments) {
- orArgument.rewrite(visitor::visitAndArgument, appView, namingLens);
+ rewritten |= orArgument.rewrite(visitor::visitAndArgument, appView, namingLens);
}
visitor.visitEnd();
+ return rewritten;
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
index 3b1c748..e581526 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
@@ -51,12 +51,18 @@
conclusion.trace(definitionSupplier);
}
- void rewrite(KmEffectVisitorProvider visitorProvider, AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(
+ KmEffectVisitorProvider visitorProvider, AppView<?> appView, NamingLens namingLens) {
KmEffectVisitor kmEffectVisitor = visitorProvider.get(type, invocationKind);
- conclusion.rewrite(kmEffectVisitor::visitConclusionOfConditionalEffect, appView, namingLens);
+ boolean rewritten =
+ conclusion.rewrite(
+ kmEffectVisitor::visitConclusionOfConditionalEffect, appView, namingLens);
for (KotlinEffectExpressionInfo constructorArgument : constructorArguments) {
- constructorArgument.rewrite(kmEffectVisitor::visitConstructorArgument, appView, namingLens);
+ rewritten |=
+ constructorArgument.rewrite(
+ kmEffectVisitor::visitConstructorArgument, appView, namingLens);
}
kmEffectVisitor.visitEnd();
+ return rewritten;
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFieldLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFieldLevelInfo.java
index acf2d64..26a35ba 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFieldLevelInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFieldLevelInfo.java
@@ -6,19 +6,4 @@
public interface KotlinFieldLevelInfo extends KotlinMemberLevelInfo {
- default boolean isCompanion() {
- return false;
- }
-
- default KotlinCompanionInfo asCompanion() {
- return null;
- }
-
- default boolean isFieldProperty() {
- return false;
- }
-
- default KotlinPropertyInfo asFieldProperty() {
- return null;
- }
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index b012758..7981195 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -8,9 +8,8 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.Pair;
import java.util.function.Consumer;
import kotlinx.metadata.KmPackage;
import kotlinx.metadata.jvm.KotlinClassHeader;
@@ -36,12 +35,14 @@
String packageName,
int[] metadataVersion,
DexClass clazz,
- DexItemFactory factory,
- Reporter reporter,
+ AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode) {
+ KmPackage kmPackage = kmFileFacade.toKmPackage();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(
+ kmFileFacade, appView.options());
return new KotlinFileFacadeInfo(
- KotlinPackageInfo.create(
- kmFileFacade.toKmPackage(), clazz, factory, reporter, keepByteCode),
+ KotlinPackageInfo.create(kmPackage, clazz, appView, keepByteCode, extensionInformation),
packageName,
metadataVersion);
}
@@ -57,12 +58,13 @@
}
@Override
- public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
- KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
+ public Pair<KotlinClassHeader, Boolean> rewrite(
+ DexClass clazz, AppView<?> appView, NamingLens namingLens) {
KmPackage kmPackage = new KmPackage();
- packageInfo.rewrite(kmPackage, clazz, appView, namingLens);
+ boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView, namingLens);
+ KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
kmPackage.accept(writer);
- return writer.write().getHeader();
+ return Pair.create(writer.write().getHeader(), rewritten);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
index 91413c7..f2a6eb4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
@@ -62,15 +62,16 @@
flexibleTypeUpperBound.getTypeFlexibilityId());
}
- public void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmFlexibleUpperBoundVisitorProvider visitorProvider,
AppView<?> appView,
NamingLens namingLens) {
if (this == NO_FLEXIBLE_UPPER_BOUND) {
// Nothing to do.
- return;
+ return false;
}
- super.rewrite(flags -> visitorProvider.get(flags, typeFlexibilityId), appView, namingLens);
+ return super.rewrite(
+ flags -> visitorProvider.get(flags, typeFlexibilityId), appView, namingLens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index 008177e..e6a249c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -73,7 +73,10 @@
}
static KotlinFunctionInfo create(
- KmFunction kmFunction, DexItemFactory factory, Reporter reporter) {
+ KmFunction kmFunction,
+ DexItemFactory factory,
+ Reporter reporter,
+ boolean readMethodSignature) {
boolean isCrossInline = false;
List<KotlinValueParameterInfo> valueParameters =
KotlinValueParameterInfo.create(kmFunction.getValueParameters(), factory, reporter);
@@ -90,7 +93,9 @@
KotlinTypeInfo.create(kmFunction.getReceiverParameterType(), factory, reporter),
valueParameters,
KotlinTypeParameterInfo.create(kmFunction.getTypeParameters(), factory, reporter),
- KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory),
+ readMethodSignature
+ ? KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory)
+ : null,
getlambdaClassOrigin(kmFunction, factory),
KotlinVersionRequirementInfo.create(kmFunction.getVersionRequirements()),
KotlinContractInfo.create(kmFunction.getContract(), factory, reporter),
@@ -106,46 +111,60 @@
return null;
}
- public void rewrite(
+ public String getName() {
+ return name;
+ }
+
+ boolean rewrite(
KmVisitorProviders.KmFunctionVisitorProvider visitorProvider,
DexEncodedMethod method,
AppView<?> appView,
NamingLens namingLens) {
// TODO(b/154348683): Check method for flags to pass in.
+ boolean rewritten = false;
String finalName = this.name;
if (method != null) {
String methodName = method.getReference().name.toString();
String rewrittenName = namingLens.lookupName(method.getReference()).toString();
if (!methodName.equals(rewrittenName)) {
+ rewritten = true;
finalName = rewrittenName;
}
}
KmFunctionVisitor kmFunction = visitorProvider.get(flags, finalName);
// TODO(b/154348149): ReturnType could have been merged to a subtype.
- returnType.rewrite(kmFunction::visitReturnType, appView, namingLens);
+ rewritten |= returnType.rewrite(kmFunction::visitReturnType, appView, namingLens);
for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
- valueParameterInfo.rewrite(kmFunction::visitValueParameter, appView, namingLens);
+ rewritten |= valueParameterInfo.rewrite(kmFunction::visitValueParameter, appView, namingLens);
}
for (KotlinTypeParameterInfo typeParameterInfo : typeParameters) {
- typeParameterInfo.rewrite(kmFunction::visitTypeParameter, appView, namingLens);
+ rewritten |= typeParameterInfo.rewrite(kmFunction::visitTypeParameter, appView, namingLens);
}
if (receiverParameterType != null) {
- receiverParameterType.rewrite(kmFunction::visitReceiverParameterType, appView, namingLens);
+ rewritten |=
+ receiverParameterType.rewrite(
+ kmFunction::visitReceiverParameterType, appView, namingLens);
}
- versionRequirements.rewrite(kmFunction::visitVersionRequirement);
+ rewritten |= versionRequirements.rewrite(kmFunction::visitVersionRequirement);
JvmFunctionExtensionVisitor extensionVisitor =
(JvmFunctionExtensionVisitor) kmFunction.visitExtensions(JvmFunctionExtensionVisitor.TYPE);
if (signature != null && extensionVisitor != null) {
- extensionVisitor.visit(signature.rewrite(method, appView, namingLens));
+ rewritten |= signature.rewrite(extensionVisitor::visit, method, appView, namingLens);
}
if (lambdaClassOrigin != null && extensionVisitor != null) {
- String lambdaClassOriginName =
- lambdaClassOrigin.toRenamedBinaryNameOrDefault(appView, namingLens, null);
- if (lambdaClassOriginName != null) {
- extensionVisitor.visitLambdaClassOriginName(lambdaClassOriginName);
- }
+ rewritten |=
+ lambdaClassOrigin.toRenamedBinaryNameOrDefault(
+ lambdaClassOriginName -> {
+ if (lambdaClassOriginName != null) {
+ extensionVisitor.visitLambdaClassOriginName(lambdaClassOriginName);
+ }
+ },
+ appView,
+ namingLens,
+ null);
}
- contract.rewrite(kmFunction::visitContract, appView, namingLens);
+ rewritten |= contract.rewrite(kmFunction::visitContract, appView, namingLens);
+ return rewritten;
}
@Override
@@ -162,10 +181,6 @@
return receiverParameterType != null;
}
- public KotlinJvmMethodSignatureInfo getSignature() {
- return signature;
- }
-
@Override
public void trace(DexDefinitionSupplier definitionSupplier) {
forEachApply(valueParameters, param -> param::trace, definitionSupplier);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
index 2606807..340275a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
+import com.android.tools.r8.utils.Box;
+import java.util.function.Consumer;
import kotlinx.metadata.jvm.JvmFieldSignature;
/**
@@ -36,19 +38,27 @@
KotlinTypeReference.fromDescriptor(fieldSignature.getDesc(), factory));
}
- public JvmFieldSignature rewrite(
- DexEncodedField field, AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(
+ Consumer<JvmFieldSignature> consumer,
+ DexEncodedField field,
+ AppView<?> appView,
+ NamingLens namingLens) {
+ boolean rewritten = false;
String finalName = name;
if (field != null) {
String fieldName = field.getReference().name.toString();
String rewrittenName = namingLens.lookupName(field.getReference()).toString();
if (!fieldName.equals(rewrittenName)) {
+ rewritten = true;
finalName = rewrittenName;
}
}
String defValue = appView.dexItemFactory().objectType.toDescriptorString();
- return new JvmFieldSignature(
- finalName, type.toRenamedDescriptorOrDefault(appView, namingLens, defValue));
+ Box<String> renamedDescriptor = new Box<>();
+ rewritten |=
+ type.toRenamedDescriptorOrDefault(renamedDescriptor::set, appView, namingLens, defValue);
+ consumer.accept(new JvmFieldSignature(finalName, renamedDescriptor.get()));
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
index b0314be..5cfa5ac 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import java.util.function.Consumer;
import kotlinx.metadata.jvm.JvmMethodSignature;
/**
@@ -50,50 +51,59 @@
if (methodSignature == null) {
return null;
}
- String kotlinDescriptor = methodSignature.getDesc();
- if (!KotlinMetadataUtils.isValidMethodDescriptor(kotlinDescriptor)) {
+ String name = methodSignature.getName();
+ String descriptor = methodSignature.getDesc();
+ if (!KotlinMetadataUtils.isValidMethodDescriptor(descriptor)) {
// If the method descriptor is invalid, keep it as invalid.
- return new KotlinJvmMethodSignatureInfo(methodSignature.getName(), kotlinDescriptor);
+ return new KotlinJvmMethodSignatureInfo(methodSignature.getName(), descriptor);
}
- String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(kotlinDescriptor);
+ String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(descriptor);
KotlinTypeReference returnType =
KotlinTypeReference.fromDescriptor(returnTypeDescriptor, factory);
- String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(kotlinDescriptor);
+ String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(descriptor);
if (descriptors.length == 0) {
- return new KotlinJvmMethodSignatureInfo(
- methodSignature.getName(), returnType, EMPTY_PARAMETERS_LIST);
+ return new KotlinJvmMethodSignatureInfo(name, returnType, EMPTY_PARAMETERS_LIST);
}
ImmutableList.Builder<KotlinTypeReference> parameters = ImmutableList.builder();
- for (String descriptor : descriptors) {
- parameters.add(KotlinTypeReference.fromDescriptor(descriptor, factory));
+ for (String paramDescriptor : descriptors) {
+ parameters.add(KotlinTypeReference.fromDescriptor(paramDescriptor, factory));
}
- return new KotlinJvmMethodSignatureInfo(
- methodSignature.getName(), returnType, parameters.build());
+ return new KotlinJvmMethodSignatureInfo(name, returnType, parameters.build());
}
- public JvmMethodSignature rewrite(
- DexEncodedMethod method, AppView<?> appView, NamingLens namingLens) {
+ boolean rewrite(
+ Consumer<JvmMethodSignature> consumer,
+ DexEncodedMethod method,
+ AppView<?> appView,
+ NamingLens namingLens) {
if (invalidDescriptor != null) {
- return new JvmMethodSignature(name, invalidDescriptor);
+ consumer.accept(new JvmMethodSignature(name, invalidDescriptor));
+ return false;
}
assert returnType != null;
String finalName = name;
+ boolean rewritten = false;
if (method != null) {
String methodName = method.getReference().name.toString();
String rewrittenName = namingLens.lookupName(method.getReference()).toString();
if (!methodName.equals(rewrittenName)) {
finalName = rewrittenName;
+ rewritten = true;
}
}
StringBuilder descBuilder = new StringBuilder();
descBuilder.append("(");
String defValue = appView.dexItemFactory().objectType.toDescriptorString();
for (KotlinTypeReference parameter : parameters) {
- descBuilder.append(parameter.toRenamedDescriptorOrDefault(appView, namingLens, defValue));
+ rewritten |=
+ parameter.toRenamedDescriptorOrDefault(
+ descBuilder::append, appView, namingLens, defValue);
}
descBuilder.append(")");
- descBuilder.append(returnType.toRenamedDescriptorOrDefault(appView, namingLens, defValue));
- return new JvmMethodSignature(finalName, descBuilder.toString());
+ rewritten |=
+ returnType.toRenamedDescriptorOrDefault(descBuilder::append, appView, namingLens, defValue);
+ consumer.accept(new JvmMethodSignature(finalName, descBuilder.toString()));
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java
new file mode 100644
index 0000000..98ebaed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java
@@ -0,0 +1,175 @@
+// 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.kotlin;
+
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ReflectionHelper;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import kotlin.Pair;
+import kotlinx.metadata.internal.metadata.ProtoBuf;
+import kotlinx.metadata.internal.metadata.jvm.JvmProtoBuf;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
+import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
+
+// Due to kotlin-metadata-jvm library synthesizing jvm method signatures from type-information
+// we do an extra check to figure out if we should model the signature. If we model it, we will
+// add name and descriptor information to the string pool for the proto message, so it is
+// better to avoid it.
+// https://github.com/Kotlin/kotlinx.reflect.lite/blob/46d47f118f9846166b6b8f8212bdbc822fe2f634/src/main/java/org/jetbrains/kotlin/serialization/jvm/JvmProtoBufUtil.kt
+
+public class KotlinJvmSignatureExtensionInformation {
+
+ private final Set<Integer> noExtensionIndicesForFunctions;
+ private final Set<Integer> noExtensionIndicesForConstructors;
+
+ private static final KotlinJvmSignatureExtensionInformation EMPTY = builder().build();
+
+ private KotlinJvmSignatureExtensionInformation(
+ Set<Integer> noExtensionIndicesForFunctions, Set<Integer> noExtensionIndicesForConstructors) {
+ this.noExtensionIndicesForFunctions = noExtensionIndicesForFunctions;
+ this.noExtensionIndicesForConstructors = noExtensionIndicesForConstructors;
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ FileFacade fileFacadeMetadata, InternalOptions options) {
+ return readPackageDataFromMessage(fileFacadeMetadata, options);
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ MultiFileClassPart classPart, InternalOptions options) {
+ return readPackageDataFromMessage(classPart, options);
+ }
+
+ private static KotlinJvmSignatureExtensionInformation readPackageDataFromMessage(
+ Object object, InternalOptions options) {
+ try {
+ Pair<?, ProtoBuf.Package> kotlinPairData =
+ ReflectionHelper.performReflection(
+ object,
+ ReflectionHelper.builder()
+ .readField("packageData$delegate")
+ .setSetAccessible(true)
+ .done()
+ .readMethod("getValue")
+ .setSetAccessible(true)
+ .done()
+ .build());
+ return builder().visit(kotlinPairData.getSecond()).build();
+ } catch (Exception e) {
+ options.warningReadingKotlinMetadataReflective();
+ return empty();
+ }
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ SyntheticClass syntheticClass, InternalOptions options) {
+ try {
+ Pair<?, ProtoBuf.Function> kotlinPairData =
+ ReflectionHelper.performReflection(
+ syntheticClass,
+ ReflectionHelper.builder()
+ .readField("functionData$delegate")
+ .setSetAccessible(true)
+ .done()
+ .readMethod("getValue")
+ .setSetAccessible(true)
+ .done()
+ .build());
+ if (kotlinPairData == null) {
+ return empty();
+ }
+ return builder().visit(kotlinPairData.getSecond(), 0).build();
+ } catch (Exception e) {
+ options.warningReadingKotlinMetadataReflective();
+ return empty();
+ }
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ KotlinClassMetadata.Class kMetadata, InternalOptions options) {
+ try {
+ Pair<?, ProtoBuf.Class> kotlinPairData =
+ ReflectionHelper.performReflection(
+ kMetadata,
+ ReflectionHelper.builder()
+ .readField("classData$delegate")
+ .setSetAccessible(true)
+ .done()
+ .readMethod("getValue")
+ .setSetAccessible(true)
+ .done()
+ .build());
+ return builder().visit(kotlinPairData.getSecond()).build();
+ } catch (Exception e) {
+ options.warningReadingKotlinMetadataReflective();
+ return empty();
+ }
+ }
+
+ public boolean hasJvmMethodSignatureExtensionForFunction(int index) {
+ return !noExtensionIndicesForFunctions.contains(index);
+ }
+
+ public boolean hasJvmMethodSignatureExtensionForConstructor(int index) {
+ return !noExtensionIndicesForConstructors.contains(index);
+ }
+
+ public static KotlinJvmSignatureExtensionInformationBuilder builder() {
+ return new KotlinJvmSignatureExtensionInformationBuilder();
+ }
+
+ public static KotlinJvmSignatureExtensionInformation empty() {
+ return EMPTY;
+ }
+
+ private static class KotlinJvmSignatureExtensionInformationBuilder {
+
+ private final Set<Integer> noExtensionIndicesForFunctions = new HashSet<>();
+ private final Set<Integer> noExtensionIndicesForConstructors = new HashSet<>();
+
+ private KotlinJvmSignatureExtensionInformation build() {
+ return new KotlinJvmSignatureExtensionInformation(
+ noExtensionIndicesForFunctions, noExtensionIndicesForConstructors);
+ }
+
+ private KotlinJvmSignatureExtensionInformationBuilder visit(ProtoBuf.Class clazz) {
+ visitFunctions(clazz.getFunctionList());
+ visitConstructors(clazz.getConstructorList());
+ return this;
+ }
+
+ private KotlinJvmSignatureExtensionInformationBuilder visit(ProtoBuf.Package pkg) {
+ visitFunctions(pkg.getFunctionList());
+ return this;
+ }
+
+ private void visitFunctions(List<ProtoBuf.Function> functions) {
+ ListUtils.forEachWithIndex(functions, this::visit);
+ }
+
+ public KotlinJvmSignatureExtensionInformationBuilder visit(
+ ProtoBuf.Function function, int index) {
+ if (!function.hasExtension(JvmProtoBuf.methodSignature)) {
+ noExtensionIndicesForFunctions.add(index);
+ }
+ return this;
+ }
+
+ private void visitConstructors(List<ProtoBuf.Constructor> constructors) {
+ ListUtils.forEachWithIndex(
+ constructors,
+ (constructor, index) -> {
+ if (!constructor.hasExtension(JvmProtoBuf.constructorSignature)) {
+ noExtensionIndicesForConstructors.add(index);
+ }
+ });
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 01a7c2a..7a4fc78 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -30,13 +30,21 @@
}
static KotlinLambdaInfo create(
- DexClass clazz, KmLambda lambda, DexItemFactory factory, Reporter reporter) {
+ DexClass clazz,
+ KmLambda lambda,
+ DexItemFactory factory,
+ Reporter reporter,
+ KotlinJvmSignatureExtensionInformation extensionInformation) {
if (lambda == null) {
assert false;
return null;
}
KotlinFunctionInfo kotlinFunctionInfo =
- KotlinFunctionInfo.create(lambda.function, factory, reporter);
+ KotlinFunctionInfo.create(
+ lambda.function,
+ factory,
+ reporter,
+ extensionInformation.hasJvmMethodSignatureExtensionForFunction(0));
JvmMethodSignature signature = JvmExtensionsKt.getSignature(lambda.function);
if (signature != null) {
for (DexEncodedMethod method : clazz.methods()) {
@@ -60,7 +68,7 @@
}
DexEncodedMethod backing = null;
for (DexEncodedMethod method : clazz.methods()) {
- if (method.getKotlinMemberInfo() == function) {
+ if (method.getKotlinInfo() == function) {
backing = method;
break;
}
@@ -69,12 +77,10 @@
appView
.options()
.reporter
- .info(
- KotlinMetadataDiagnostic.lambdaBackingNotFound(clazz.type, function.getSignature()));
+ .info(KotlinMetadataDiagnostic.lambdaBackingNotFound(clazz.type, function.getName()));
return false;
}
- function.rewrite(visitorProvider.get()::visitFunction, backing, appView, namingLens);
- return true;
+ return function.rewrite(visitorProvider.get()::visitFunction, backing, appView, namingLens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
index fca17cf..9548bde 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
@@ -52,10 +52,12 @@
forEachApply(propertyInfos, prop -> prop::trace, definitionSupplier);
}
- public void rewrite(
+ boolean rewrite(
KmPropertyVisitorProvider visitorProvider, AppView<?> appView, NamingLens namingLens) {
+ boolean rewritten = false;
for (KotlinPropertyInfo propertyInfo : propertyInfos) {
- propertyInfo.rewrite(visitorProvider, null, null, null, appView, namingLens);
+ rewritten |= propertyInfo.rewrite(visitorProvider, null, null, null, appView, namingLens);
}
+ return rewritten;
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java
index f7312ad..41f23c1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java
@@ -11,4 +11,36 @@
default boolean isNoKotlinInformation() {
return false;
}
+
+ default boolean isCompanion() {
+ return false;
+ }
+
+ default KotlinCompanionInfo asCompanion() {
+ return null;
+ }
+
+ default boolean isConstructor() {
+ return false;
+ }
+
+ default KotlinConstructorInfo asConstructor() {
+ return null;
+ }
+
+ default boolean isFunction() {
+ return false;
+ }
+
+ default KotlinFunctionInfo asFunction() {
+ return null;
+ }
+
+ default boolean isProperty() {
+ return false;
+ }
+
+ default KotlinPropertyInfo asProperty() {
+ return null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
index 3415e13..6c4c963 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
@@ -74,13 +74,12 @@
+ StringUtils.stacktraceAsString(t));
}
- static KotlinMetadataDiagnostic lambdaBackingNotFound(
- DexType type, KotlinJvmMethodSignatureInfo signatureInfo) {
+ static KotlinMetadataDiagnostic lambdaBackingNotFound(DexType type, String functionName) {
return new KotlinMetadataDiagnostic(
Origin.unknown(),
Position.UNKNOWN,
"The lambda function "
- + signatureInfo.toString()
+ + functionName
+ " could no longer be found in "
+ type.toSourceString()
+ " . The method is most likely pruned and would require a specific keep rule to keep"
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 4eef6cb..6422c69 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.kotlin.KotlinClassMetadataReader.hasKotlinClassMetadataAnnotation;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -67,24 +67,19 @@
&& clazz.hasClassInitializer()) {
feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
}
- clazz.setKotlinInfo(NO_KOTLIN_INFO);
+ clazz.setKotlinInfo(getNoKotlinInfo());
clazz.removeAnnotations(
annotation -> annotation.getAnnotationType() == kotlinMetadataType);
} else {
clazz.setKotlinInfo(
KotlinClassMetadataReader.getKotlinInfo(
- appView.dexItemFactory().kotlin,
- clazz,
- appView.dexItemFactory(),
- appView.options().reporter,
- method -> keepByteCodeFunctions.add(method.getReference())));
+ clazz, appView, method -> keepByteCodeFunctions.add(method.getReference())));
if (clazz.getEnclosingMethodAttribute() != null
&& clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
localOrAnonymousClasses.add(clazz);
}
}
});
- appView.setCfByteCodePassThrough(keepByteCodeFunctions);
for (DexProgramClass localOrAnonymousClass : localOrAnonymousClasses) {
EnclosingMethodAttribute enclosingAttribute =
localOrAnonymousClass.getEnclosingMethodAttribute();
@@ -97,12 +92,13 @@
DexEncodedMethod method = holder.lookupMethod(enclosingAttribute.getEnclosingMethod());
// If we cannot lookup the method, the conservative choice is keep the byte code.
if (method == null
- || (method.getKotlinMemberInfo().isFunction()
- && method.getKotlinMemberInfo().asFunction().hasCrossInlineParameter())) {
+ || (method.getKotlinInfo().isFunction()
+ && method.getKotlinInfo().asFunction().hasCrossInlineParameter())) {
localOrAnonymousClass.forEachProgramMethod(
m -> keepByteCodeFunctions.add(m.getReference()));
}
}
+ appView.setCfByteCodePassThrough(keepByteCodeFunctions);
} else {
assert verifyKotlinMetadataModeledForAllClasses(enqueuer, keepMetadata);
}
@@ -112,10 +108,7 @@
clazz.getKotlinInfo().trace(definitionsForContext(clazz));
clazz.forEachProgramMember(
member ->
- member
- .getDefinition()
- .getKotlinMemberInfo()
- .trace(definitionsForContext(member)));
+ member.getDefinition().getKotlinInfo().trace(definitionsForContext(member)));
});
}
@@ -127,7 +120,7 @@
assert !hasKotlinClassMetadataAnnotation(clazz, definitionsForContext(clazz))
|| !keepMetadata
|| !enqueuer.isPinned(clazz.type)
- || clazz.getKotlinInfo() != NO_KOTLIN_INFO;
+ || clazz.getKotlinInfo() != getNoKotlinInfo();
});
return true;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 4a5904e..d22a2b4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -3,8 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.INVALID_KOTLIN_INFO;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getInvalidKotlinInfo;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ConsumerUtils;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
@@ -101,15 +101,15 @@
appView.appInfo().classes(),
clazz -> {
KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
- DexAnnotation oldMeta = clazz.annotations().getFirstMatching(rewrittenMetadataType);
- if (kotlinInfo == INVALID_KOTLIN_INFO) {
+ if (kotlinInfo == getInvalidKotlinInfo()) {
// Maintain invalid kotlin info for classes.
return;
}
+ DexAnnotation oldMeta = clazz.annotations().getFirstMatching(rewrittenMetadataType);
// TODO(b/181103083): Consider removing if rewrittenMetadataType
// != factory.kotlinMetadataType
if (oldMeta == null
- || kotlinInfo == NO_KOTLIN_INFO
+ || kotlinInfo == getNoKotlinInfo()
|| (appView.appInfo().hasLiveness()
&& !appView.withLiveness().appInfo().isPinned(clazz.type))) {
// Remove @Metadata in DexAnnotation when there is no kotlin info and the type is not
@@ -131,8 +131,6 @@
if (lens.isIdentityLens()) {
return;
}
- final Kotlin kotlin = factory.kotlin;
- final Reporter reporter = appView.options().reporter;
final WriteMetadataFieldInfo writeMetadataFieldInfo = WriteMetadataFieldInfo.rewriteAll();
ThreadUtils.processItems(
appView.appInfo().classes(),
@@ -141,10 +139,10 @@
if (metadata == null) {
return;
}
- final KotlinClassLevelInfo kotlinInfo =
+ KotlinClassLevelInfo kotlinInfo =
KotlinClassMetadataReader.getKotlinInfo(
- kotlin, clazz, factory, reporter, ConsumerUtils.emptyConsumer(), metadata);
- if (kotlinInfo == NO_KOTLIN_INFO) {
+ clazz, appView, ConsumerUtils.emptyConsumer(), metadata);
+ if (kotlinInfo == getNoKotlinInfo()) {
return;
}
writeKotlinInfoToAnnotation(clazz, kotlinInfo, metadata, writeMetadataFieldInfo);
@@ -158,10 +156,15 @@
DexAnnotation oldMeta,
WriteMetadataFieldInfo writeMetadataFieldInfo) {
try {
- KotlinClassHeader kotlinClassHeader = kotlinInfo.rewrite(clazz, appView, lens);
+ Pair<KotlinClassHeader, Boolean> kotlinClassHeader = kotlinInfo.rewrite(clazz, appView, lens);
+ // TODO(b/185756596): Remove when special handling is no longer needed.
+ if (!kotlinClassHeader.getSecond() && !appView.enableWholeProgramOptimizations()) {
+ // No rewrite occurred and the data is the same as before.
+ return;
+ }
DexAnnotation newMeta =
createKotlinMetadataAnnotation(
- kotlinClassHeader,
+ kotlinClassHeader.getFirst(),
kotlinInfo.getPackageName(),
getMaxVersion(METADATA_VERSION_1_4, kotlinInfo.getMetadataVersion()),
writeMetadataFieldInfo);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index fad37c6..7eb45f6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.shaking.ProguardKeepRule;
import com.android.tools.r8.shaking.ProguardKeepRuleType;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.Pair;
import kotlinx.metadata.KmExtensionType;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmPropertyExtensionVisitor;
@@ -30,8 +31,8 @@
public class KotlinMetadataUtils {
- public static final NoKotlinInfo NO_KOTLIN_INFO = new NoKotlinInfo("NO_KOTLIN_INFO");
- public static final NoKotlinInfo INVALID_KOTLIN_INFO = new NoKotlinInfo("INVALID_KOTLIN_INFO");
+ private static final NoKotlinInfo NO_KOTLIN_INFO = new NoKotlinInfo("NO_KOTLIN_INFO");
+ private static final NoKotlinInfo INVALID_KOTLIN_INFO = new NoKotlinInfo("INVALID_KOTLIN_INFO");
private static class NoKotlinInfo
implements KotlinClassLevelInfo, KotlinFieldLevelInfo, KotlinMethodLevelInfo {
@@ -48,7 +49,8 @@
}
@Override
- public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ public Pair<KotlinClassHeader, Boolean> rewrite(
+ DexClass clazz, AppView<?> appView, NamingLens namingLens) {
throw new Unreachable("Should never be called");
}
@@ -73,6 +75,14 @@
}
}
+ public static NoKotlinInfo getNoKotlinInfo() {
+ return NO_KOTLIN_INFO;
+ }
+
+ public static NoKotlinInfo getInvalidKotlinInfo() {
+ return INVALID_KOTLIN_INFO;
+ }
+
static JvmFieldSignature toJvmFieldSignature(DexField field) {
return new JvmFieldSignature(field.name.toString(), field.type.toDescriptorString());
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMethodLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMethodLevelInfo.java
index a43e677..c7ecdf8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMethodLevelInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMethodLevelInfo.java
@@ -6,27 +6,4 @@
public interface KotlinMethodLevelInfo extends KotlinMemberLevelInfo {
- default boolean isConstructor() {
- return false;
- }
-
- default KotlinConstructorInfo asConstructor() {
- return null;
- }
-
- default boolean isFunction() {
- return false;
- }
-
- default KotlinFunctionInfo asFunction() {
- return null;
- }
-
- default boolean isProperty() {
- return false;
- }
-
- default KotlinPropertyInfo asProperty() {
- return null;
- }
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
index ec31d79..7088c7f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
@@ -55,17 +56,25 @@
}
@Override
- public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ public Pair<KotlinClassHeader, Boolean> rewrite(
+ DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ List<String> partClassNameStrings = new ArrayList<>(partClassNames.size());
+ boolean rewritten = false;
+ for (KotlinTypeReference partClassName : partClassNames) {
+ rewritten |=
+ partClassName.toRenamedBinaryNameOrDefault(
+ binaryName -> {
+ if (binaryName != null) {
+ partClassNameStrings.add(binaryName);
+ }
+ },
+ appView,
+ namingLens,
+ null);
+ }
KotlinClassMetadata.MultiFileClassFacade.Writer writer =
new KotlinClassMetadata.MultiFileClassFacade.Writer();
- List<String> partClassNameStrings = new ArrayList<>(partClassNames.size());
- for (KotlinTypeReference partClassName : partClassNames) {
- String binaryName = partClassName.toRenamedBinaryNameOrDefault(appView, namingLens, null);
- if (binaryName != null) {
- partClassNameStrings.add(binaryName);
- }
- }
- return writer.write(partClassNameStrings).getHeader();
+ return Pair.create(writer.write(partClassNameStrings).getHeader(), rewritten);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index 6b99d15..24cb385 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -8,9 +8,8 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.Pair;
import java.util.function.Consumer;
import kotlinx.metadata.KmPackage;
import kotlinx.metadata.jvm.KotlinClassHeader;
@@ -42,12 +41,15 @@
String packageName,
int[] metadataVersion,
DexClass clazz,
- DexItemFactory factory,
- Reporter reporter,
+ AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode) {
+ KmPackage kmPackage = classPart.toKmPackage();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(
+ classPart, appView.options());
return new KotlinMultiFileClassPartInfo(
classPart.getFacadeClassName(),
- KotlinPackageInfo.create(classPart.toKmPackage(), clazz, factory, reporter, keepByteCode),
+ KotlinPackageInfo.create(kmPackage, clazz, appView, keepByteCode, extensionInformation),
packageName,
metadataVersion);
}
@@ -63,13 +65,14 @@
}
@Override
- public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ public Pair<KotlinClassHeader, Boolean> rewrite(
+ DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ KmPackage kmPackage = new KmPackage();
+ boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView, namingLens);
KotlinClassMetadata.MultiFileClassPart.Writer writer =
new KotlinClassMetadata.MultiFileClassPart.Writer();
- KmPackage kmPackage = new KmPackage();
- packageInfo.rewrite(kmPackage, clazz, appView, namingLens);
kmPackage.accept(writer);
- return writer.write(facadeClassName).getHeader();
+ return Pair.create(writer.write(facadeClassName).getHeader(), rewritten);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index 41ff17d..d28e7c3 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -12,10 +12,8 @@
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
-import com.android.tools.r8.utils.Reporter;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
@@ -42,9 +40,9 @@
public static KotlinPackageInfo create(
KmPackage kmPackage,
DexClass clazz,
- DexItemFactory factory,
- Reporter reporter,
- Consumer<DexEncodedMethod> keepByteCode) {
+ AppView<?> appView,
+ Consumer<DexEncodedMethod> keepByteCode,
+ KotlinJvmSignatureExtensionInformation extensionInformation) {
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : clazz.fields()) {
fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
@@ -56,26 +54,36 @@
return new KotlinPackageInfo(
JvmExtensionsKt.getModuleName(kmPackage),
KotlinDeclarationContainerInfo.create(
- kmPackage, methodMap, fieldMap, factory, reporter, keepByteCode),
+ kmPackage,
+ methodMap,
+ fieldMap,
+ appView.dexItemFactory(),
+ appView.reporter(),
+ keepByteCode,
+ extensionInformation),
KotlinLocalDelegatedPropertyInfo.create(
- JvmExtensionsKt.getLocalDelegatedProperties(kmPackage), factory, reporter));
+ JvmExtensionsKt.getLocalDelegatedProperties(kmPackage),
+ appView.dexItemFactory(),
+ appView.reporter()));
}
- public void rewrite(
- KmPackage kmPackage, DexClass clazz, AppView<?> appView, NamingLens namingLens) {
- containerInfo.rewrite(
- kmPackage::visitFunction,
- kmPackage::visitProperty,
- kmPackage::visitTypeAlias,
- clazz,
- appView,
- namingLens);
+ boolean rewrite(KmPackage kmPackage, DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ boolean rewritten =
+ containerInfo.rewrite(
+ kmPackage::visitFunction,
+ kmPackage::visitProperty,
+ kmPackage::visitTypeAlias,
+ clazz,
+ appView,
+ namingLens);
JvmPackageExtensionVisitor extensionVisitor =
(JvmPackageExtensionVisitor) kmPackage.visitExtensions(JvmPackageExtensionVisitor.TYPE);
- localDelegatedProperties.rewrite(
- extensionVisitor::visitLocalDelegatedProperty, appView, namingLens);
+ rewritten |=
+ localDelegatedProperties.rewrite(
+ extensionVisitor::visitLocalDelegatedProperty, appView, namingLens);
extensionVisitor.visitModuleName(moduleName);
extensionVisitor.visitEnd();
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
index 6fceb06..9d0597d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -12,11 +12,14 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.Reporter;
import java.util.List;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmPropertyVisitor;
import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmMethodSignature;
import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
// Holds information about KmProperty
@@ -109,16 +112,6 @@
}
@Override
- public boolean isFieldProperty() {
- return true;
- }
-
- @Override
- public KotlinPropertyInfo asFieldProperty() {
- return this;
- }
-
- @Override
public boolean isProperty() {
return true;
}
@@ -140,7 +133,7 @@
return setterSignature;
}
- void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmPropertyVisitorProvider visitorProvider,
DexEncodedField field,
DexEncodedMethod getter,
@@ -150,32 +143,52 @@
// TODO(b/154348683): Flags again.
KmPropertyVisitor kmProperty = visitorProvider.get(flags, name, getterFlags, setterFlags);
// TODO(b/154348149): ReturnType could have been merged to a subtype.
+ boolean rewritten = false;
if (returnType != null) {
- returnType.rewrite(kmProperty::visitReturnType, appView, namingLens);
+ rewritten = returnType.rewrite(kmProperty::visitReturnType, appView, namingLens);
}
if (receiverParameterType != null) {
- receiverParameterType.rewrite(kmProperty::visitReceiverParameterType, appView, namingLens);
+ rewritten |=
+ receiverParameterType.rewrite(
+ kmProperty::visitReceiverParameterType, appView, namingLens);
}
if (setterParameter != null) {
- setterParameter.rewrite(kmProperty::visitSetterParameter, appView, namingLens);
+ rewritten |= setterParameter.rewrite(kmProperty::visitSetterParameter, appView, namingLens);
}
for (KotlinTypeParameterInfo typeParameter : typeParameters) {
- typeParameter.rewrite(kmProperty::visitTypeParameter, appView, namingLens);
+ rewritten |= typeParameter.rewrite(kmProperty::visitTypeParameter, appView, namingLens);
}
- versionRequirements.rewrite(kmProperty::visitVersionRequirement);
+ rewritten |= versionRequirements.rewrite(kmProperty::visitVersionRequirement);
JvmPropertyExtensionVisitor extensionVisitor =
(JvmPropertyExtensionVisitor) kmProperty.visitExtensions(JvmPropertyExtensionVisitor.TYPE);
if (extensionVisitor != null) {
+ Box<JvmFieldSignature> rewrittenFieldSignature = new Box<>();
+ if (fieldSignature != null) {
+ rewritten |=
+ fieldSignature.rewrite(rewrittenFieldSignature::set, field, appView, namingLens);
+ }
+ Box<JvmMethodSignature> rewrittenGetterSignature = new Box<>();
+ if (getterSignature != null) {
+ rewritten |=
+ getterSignature.rewrite(rewrittenGetterSignature::set, getter, appView, namingLens);
+ }
+ Box<JvmMethodSignature> rewrittenSetterSignature = new Box<>();
+ if (setterSignature != null) {
+ rewritten |=
+ setterSignature.rewrite(rewrittenSetterSignature::set, setter, appView, namingLens);
+ }
extensionVisitor.visit(
jvmFlags,
- fieldSignature == null ? null : fieldSignature.rewrite(field, appView, namingLens),
- getterSignature == null ? null : getterSignature.rewrite(getter, appView, namingLens),
- setterSignature == null ? null : setterSignature.rewrite(setter, appView, namingLens));
+ rewrittenFieldSignature.get(),
+ rewrittenGetterSignature.get(),
+ rewrittenSetterSignature.get());
if (syntheticMethodForAnnotations != null) {
- extensionVisitor.visitSyntheticMethodForAnnotations(
- syntheticMethodForAnnotations.rewrite(null, appView, namingLens));
+ rewritten |=
+ syntheticMethodForAnnotations.rewrite(
+ extensionVisitor::visitSyntheticMethodForAnnotations, null, appView, namingLens);
}
}
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
index 36bf5cf..08e95b3 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -7,9 +7,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.Pair;
import kotlinx.metadata.KmLambda;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -45,15 +44,17 @@
int[] metadataVersion,
DexClass clazz,
Kotlin kotlin,
- DexItemFactory factory,
- Reporter reporter) {
- KmLambda lambda = null;
- if (syntheticClass.isLambda()) {
- lambda = syntheticClass.toKmLambda();
- assert lambda != null;
- }
+ AppView<?> appView) {
+ KmLambda lambda = syntheticClass.toKmLambda();
+ assert lambda == null || syntheticClass.isLambda();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(
+ syntheticClass, appView.options());
return new KotlinSyntheticClassInfo(
- lambda != null ? KotlinLambdaInfo.create(clazz, lambda, factory, reporter) : null,
+ lambda != null
+ ? KotlinLambdaInfo.create(
+ clazz, lambda, appView.dexItemFactory(), appView.reporter(), extensionInformation)
+ : null,
getFlavour(syntheticClass, clazz, kotlin),
packageName,
metadataVersion);
@@ -82,15 +83,16 @@
}
@Override
- public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
+ public Pair<KotlinClassHeader, Boolean> rewrite(
+ DexClass clazz, AppView<?> appView, NamingLens namingLens) {
Writer writer = new Writer();
+ boolean rewritten = false;
if (lambda != null) {
KmLambda kmLambda = new KmLambda();
- if (lambda.rewrite(() -> kmLambda, clazz, appView, namingLens)) {
- kmLambda.accept(writer);
- }
+ rewritten = lambda.rewrite(() -> kmLambda, clazz, appView, namingLens);
+ kmLambda.accept(writer);
}
- return writer.write().getHeader();
+ return Pair.create(writer.write().getHeader(), rewritten);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
index 3ecbc2c..c4e307b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -58,20 +58,23 @@
KotlinVersionRequirementInfo.create(alias.getVersionRequirements()));
}
- void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmTypeAliasVisitorProvider visitorProvider,
AppView<?> appView,
NamingLens namingLens) {
KmTypeAliasVisitor kmTypeAliasVisitor = visitorProvider.get(flags, name);
- underlyingType.rewrite(kmTypeAliasVisitor::visitUnderlyingType, appView, namingLens);
- expandedType.rewrite(kmTypeAliasVisitor::visitExpandedType, appView, namingLens);
+ boolean rewritten =
+ underlyingType.rewrite(kmTypeAliasVisitor::visitUnderlyingType, appView, namingLens);
+ rewritten |= expandedType.rewrite(kmTypeAliasVisitor::visitExpandedType, appView, namingLens);
for (KotlinTypeParameterInfo typeParameter : typeParameters) {
- typeParameter.rewrite(kmTypeAliasVisitor::visitTypeParameter, appView, namingLens);
+ rewritten |=
+ typeParameter.rewrite(kmTypeAliasVisitor::visitTypeParameter, appView, namingLens);
}
for (KotlinAnnotationInfo annotation : annotations) {
- annotation.rewrite(kmTypeAliasVisitor::visitAnnotation, appView, namingLens);
+ rewritten |= annotation.rewrite(kmTypeAliasVisitor::visitAnnotation, appView, namingLens);
}
- versionRequirements.rewrite(kmTypeAliasVisitor::visitVersionRequirement);
+ rewritten |= versionRequirements.rewrite(kmTypeAliasVisitor::visitVersionRequirement);
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
index 7f068f6..9503404 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -79,34 +79,42 @@
return arguments.build();
}
- public void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmTypeVisitorProvider visitorProvider,
AppView<?> appView,
NamingLens namingLens) {
// TODO(b/154348683): Check for correct flags
KmTypeVisitor kmTypeVisitor = visitorProvider.get(flags);
- classifier.rewrite(kmTypeVisitor, appView, namingLens);
+ boolean rewritten = classifier.rewrite(kmTypeVisitor, appView, namingLens);
if (abbreviatedType != null) {
- abbreviatedType.rewrite(kmTypeVisitor::visitAbbreviatedType, appView, namingLens);
+ rewritten |=
+ abbreviatedType.rewrite(kmTypeVisitor::visitAbbreviatedType, appView, namingLens);
}
if (outerType != null) {
- outerType.rewrite(kmTypeVisitor::visitOuterType, appView, namingLens);
+ rewritten |= outerType.rewrite(kmTypeVisitor::visitOuterType, appView, namingLens);
}
for (KotlinTypeProjectionInfo argument : arguments) {
- argument.rewrite(
- kmTypeVisitor::visitArgument, kmTypeVisitor::visitStarProjection, appView, namingLens);
+ rewritten |=
+ argument.rewrite(
+ kmTypeVisitor::visitArgument,
+ kmTypeVisitor::visitStarProjection,
+ appView,
+ namingLens);
}
- flexibleTypeUpperBound.rewrite(kmTypeVisitor::visitFlexibleTypeUpperBound, appView, namingLens);
+ rewritten |=
+ flexibleTypeUpperBound.rewrite(
+ kmTypeVisitor::visitFlexibleTypeUpperBound, appView, namingLens);
if (annotations.isEmpty()) {
- return;
+ return rewritten;
}
JvmTypeExtensionVisitor extensionVisitor =
(JvmTypeExtensionVisitor) kmTypeVisitor.visitExtensions(JvmTypeExtensionVisitor.TYPE);
if (extensionVisitor != null) {
for (KotlinAnnotationInfo annotation : annotations) {
- annotation.rewrite(extensionVisitor::visitAnnotation, appView, namingLens);
+ rewritten |= annotation.rewrite(extensionVisitor::visitAnnotation, appView, namingLens);
}
}
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
index d8457ed..21aeaa1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
@@ -84,23 +84,28 @@
return builder.build();
}
- void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmTypeParameterVisitorProvider visitorProvider,
AppView<?> appView,
NamingLens namingLens) {
KmTypeParameterVisitor kmTypeParameterVisitor = visitorProvider.get(flags, name, id, variance);
+ boolean rewritten = false;
for (KotlinTypeInfo originalUpperBound : originalUpperBounds) {
- originalUpperBound.rewrite(kmTypeParameterVisitor::visitUpperBound, appView, namingLens);
+ rewritten |=
+ originalUpperBound.rewrite(kmTypeParameterVisitor::visitUpperBound, appView, namingLens);
}
if (annotations.isEmpty()) {
- return;
+ return rewritten;
}
JvmTypeParameterExtensionVisitor extensionVisitor =
(JvmTypeParameterExtensionVisitor)
kmTypeParameterVisitor.visitExtensions(JvmTypeParameterExtensionVisitor.TYPE);
- for (KotlinAnnotationInfo annotation : annotations) {
- annotation.rewrite(extensionVisitor::visitAnnotation, appView, namingLens);
+ if (extensionVisitor != null) {
+ for (KotlinAnnotationInfo annotation : annotations) {
+ rewritten |= annotation.rewrite(extensionVisitor::visitAnnotation, appView, namingLens);
+ }
}
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
index 1f2ce01..3f83e53 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -35,7 +35,7 @@
return variance == null && typeInfo == null;
}
- public void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmTypeProjectionVisitorProvider visitorProvider,
KmVisitorProviders.KmTypeStarProjectionVisitorProvider starProjectionProvider,
AppView<?> appView,
@@ -45,6 +45,7 @@
} else {
typeInfo.rewrite(flags -> visitorProvider.get(flags, variance), appView, namingLens);
}
+ return false;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
index 2e1ddf9..4d0d990 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.function.Consumer;
/**
* To account for invalid type references in kotlin metadata, the class KotlinTypeReference will
@@ -60,34 +61,48 @@
return new KotlinTypeReference(unknownValue);
}
- String toRenamedDescriptorOrDefault(
- AppView<?> appView, NamingLens namingLens, String defaultValue) {
+ boolean toRenamedDescriptorOrDefault(
+ Consumer<String> rewrittenConsumer,
+ AppView<?> appView,
+ NamingLens namingLens,
+ String defaultValue) {
if (unknown != null) {
- return unknown;
+ rewrittenConsumer.accept(unknown);
+ return false;
}
assert known != null;
DexType rewrittenType = toRewrittenTypeOrNull(appView, known);
if (rewrittenType == null) {
- return defaultValue;
+ rewrittenConsumer.accept(defaultValue);
+ return true;
}
- return namingLens.lookupDescriptor(rewrittenType).toString();
+ String renamedString = namingLens.lookupDescriptor(rewrittenType).toString();
+ rewrittenConsumer.accept(renamedString);
+ return !known.toDescriptorString().equals(renamedString);
}
- String toRenamedBinaryNameOrDefault(
- AppView<?> appView, NamingLens namingLens, String defaultValue) {
+ boolean toRenamedBinaryNameOrDefault(
+ Consumer<String> rewrittenConsumer,
+ AppView<?> appView,
+ NamingLens namingLens,
+ String defaultValue) {
if (unknown != null) {
// Unknown values are always on the input form, so we can just return it.
- return unknown;
+ rewrittenConsumer.accept(unknown);
+ return false;
}
- String descriptor = toRenamedDescriptorOrDefault(appView, namingLens, defaultValue);
- if (descriptor == null) {
- return null;
- }
- if (descriptor.equals(defaultValue)) {
- // We assume that the default value passed in is already a binary name.
- return descriptor;
- }
- return DescriptorUtils.getBinaryNameFromDescriptor(descriptor);
+ return toRenamedDescriptorOrDefault(
+ descriptor -> {
+ // We assume that the default value passed in is already a binary name.
+ if (descriptor == null || descriptor.equals(defaultValue)) {
+ rewrittenConsumer.accept(descriptor);
+ } else {
+ rewrittenConsumer.accept(DescriptorUtils.getBinaryNameFromDescriptor(descriptor));
+ }
+ },
+ appView,
+ namingLens,
+ defaultValue);
}
private static DexType toRewrittenTypeOrNull(AppView<?> appView, DexType type) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index db54d35..293c35a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -66,16 +66,18 @@
return builder.build();
}
- void rewrite(
+ boolean rewrite(
KmVisitorProviders.KmValueParameterVisitorProvider visitorProvider,
AppView<?> appView,
NamingLens namingLens) {
KmValueParameterVisitor kmValueParameterVisitor = visitorProvider.get(flags, name);
- type.rewrite(kmValueParameterVisitor::visitType, appView, namingLens);
+ boolean rewritten = type.rewrite(kmValueParameterVisitor::visitType, appView, namingLens);
if (varargElementType != null) {
- varargElementType.rewrite(
- kmValueParameterVisitor::visitVarargElementType, appView, namingLens);
+ rewritten |=
+ varargElementType.rewrite(
+ kmValueParameterVisitor::visitVarargElementType, appView, namingLens);
}
+ return rewritten;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
index 9530f4c..3ab63f9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
@@ -34,13 +34,14 @@
return new KotlinVersionRequirementInfo(builder.build());
}
- public void rewrite(KmVisitorProviders.KmVersionRequirementVisitorProvider visitorProvider) {
+ boolean rewrite(KmVisitorProviders.KmVersionRequirementVisitorProvider visitorProvider) {
if (this == NO_VERSION_REQUIREMENTS) {
- return;
+ return false;
}
for (KotlinVersionRequirementPoint versionRequirement : versionRequirements) {
versionRequirement.rewrite(visitorProvider.get());
}
+ return false;
}
private static class KotlinVersionRequirementPoint {
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index faf8616..63d0098 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.Keep;
import com.android.tools.r8.Version;
import com.android.tools.r8.retrace.RetraceCommand.Builder;
-import com.android.tools.r8.retrace.internal.PlainStackTraceLineParser;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
@@ -248,9 +247,7 @@
timing.begin("Report result");
StringRetrace stringRetrace =
new StringRetrace(
- options.getRegularExpression() == null
- ? new PlainStackTraceLineParser()
- : new StackTraceRegularExpressionParser(options.getRegularExpression()),
+ new StackTraceRegularExpressionParser(options.getRegularExpression()),
StackTraceElementProxyRetracer.createDefault(retracer),
diagnosticsHandler,
options.isVerbose());
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index 66819e7..76d6b77 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -25,10 +25,14 @@
List<String> stackTrace,
Consumer<List<String>> retracedStackTraceConsumer,
boolean isVerbose) {
- options =
- new RetraceOptions(regularExpression, diagnosticsHandler, proguardMapProducer, isVerbose);
this.stackTrace = stackTrace;
this.retracedStackTraceConsumer = retracedStackTraceConsumer;
+ this.options =
+ RetraceOptions.builder(diagnosticsHandler)
+ .setRegularExpression(regularExpression)
+ .setProguardMapProducer(proguardMapProducer)
+ .setVerbose(isVerbose)
+ .build();
assert this.stackTrace != null;
assert this.retracedStackTraceConsumer != null;
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java b/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
index 4784f09..47d8c74 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
@@ -21,7 +21,7 @@
private final DiagnosticsHandler diagnosticsHandler;
private final ProguardMapProducer proguardMapProducer;
- RetraceOptions(
+ private RetraceOptions(
String regularExpression,
DiagnosticsHandler diagnosticsHandler,
ProguardMapProducer proguardMapProducer,
@@ -112,6 +112,9 @@
if (this.proguardMapProducer == null) {
throw new RuntimeException("ProguardMapSupplier not specified");
}
+ if (this.regularExpression == null) {
+ throw new RuntimeException("Regular expression not specified");
+ }
return new RetraceOptions(
regularExpression, diagnosticsHandler, proguardMapProducer, isVerbose);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceLineParser.java b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceLineParser.java
deleted file mode 100644
index ddaf675..0000000
--- a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceLineParser.java
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.retrace.internal;
-
-import static com.android.tools.r8.retrace.internal.RetraceUtils.firstCharFromIndex;
-import static com.android.tools.r8.retrace.internal.RetraceUtils.firstNonWhiteSpaceCharacterFromIndex;
-
-import com.android.tools.r8.retrace.StackTraceLineParser;
-import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
-import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
-import com.android.tools.r8.utils.DescriptorUtils;
-
-public final class PlainStackTraceLineParser
- implements StackTraceLineParser<String, StackTraceElementStringProxy> {
-
- public PlainStackTraceLineParser() {}
-
- @Override
- public StackTraceElementStringProxy parse(String stackTraceLine) {
- return parseLine(stackTraceLine);
- }
-
- /**
- * Captures a stack trace line of the following formats:
- *
- * <ul>
- * <li>com.android.r8.R8Exception
- * <li>com.android.r8.R8Exception: Problem when compiling program
- * <li>Caused by: com.android.r8.R8InnerException: You have to write the program first
- * <li>com.android.r8.R8InnerException: You have to write the program first
- * </ul>
- *
- * <p>This will also contains false positives, such as
- *
- * <pre>
- * W( 8207) VFY: unable to resolve static method 11: Lprivateinterfacemethods/I$-CC;....
- * </pre>
- *
- * <p>The only invalid chars for type-identifiers for a java type-name is ';', '[' and '/', so we
- * cannot really disregard the above line.
- *
- * <p>Caused by and Suppressed seems to not change based on locale, so we use these as markers.
- */
- private static class ExceptionLine {
-
- private static final String CAUSED_BY = "Caused by: ";
- private static final String SUPPRESSED = "Suppressed: ";
-
- private static StackTraceElementStringProxy tryParse(String line) {
- if (line.isEmpty()) {
- return null;
- }
- int firstNonWhiteSpaceChar = firstNonWhiteSpaceCharacterFromIndex(line, 0);
- String description = "";
- if (line.startsWith(CAUSED_BY, firstNonWhiteSpaceChar)) {
- description = CAUSED_BY;
- } else if (line.startsWith(SUPPRESSED, firstNonWhiteSpaceChar)) {
- description = SUPPRESSED;
- }
- int exceptionStartIndex = firstNonWhiteSpaceChar + description.length();
- int messageStartIndex = firstCharFromIndex(line, exceptionStartIndex, ':');
- String className = line.substring(exceptionStartIndex, messageStartIndex);
- if (!DescriptorUtils.isValidJavaType(className)) {
- return null;
- }
- return StackTraceElementStringProxy.builder(line)
- .registerClassName(exceptionStartIndex, messageStartIndex, ClassNameType.TYPENAME)
- .build();
- }
- }
-
- /**
- * Captures a stack trace line on the following form
- *
- * <ul>
- * <li>at dalvik.system.NativeStart.main(NativeStart.java:99)
- * <li>at dalvik.system.NativeStart.main(:99)
- * <li>at dalvik.system.NativeStart.main(Foo.java:)
- * <li>at dalvik.system.NativeStart.main(Native Method)
- * <li>at classloader/named_module@version/foo.bar.baz(:20)
- * <li>at classloader//foo.bar.baz(:20)
- * </ul>
- *
- * <p>Empirical evidence suggests that the "at" string is never localized.
- */
- private static class AtLine {
-
- private static StackTraceElementStringProxy tryParse(String line) {
- // Check that the line is indented with some amount of white space.
- if (line.length() == 0 || !Character.isWhitespace(line.charAt(0))) {
- return null;
- }
- // Find the first non-white space character and check that we have the sequence 'a', 't', ' '.
- int firstNonWhiteSpace = firstNonWhiteSpaceCharacterFromIndex(line, 0);
- if (firstNonWhiteSpace + 2 >= line.length()
- || line.charAt(firstNonWhiteSpace) != 'a'
- || line.charAt(firstNonWhiteSpace + 1) != 't'
- || line.charAt(firstNonWhiteSpace + 2) != ' ') {
- return null;
- }
- int classClassLoaderOrModuleStartIndex =
- firstNonWhiteSpaceCharacterFromIndex(line, firstNonWhiteSpace + 2);
- if (classClassLoaderOrModuleStartIndex >= line.length()
- || classClassLoaderOrModuleStartIndex != firstNonWhiteSpace + 3) {
- return null;
- }
- int parensStart = firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '(');
- if (parensStart >= line.length()) {
- return null;
- }
- int parensEnd = line.lastIndexOf(')');
- if (parensEnd <= parensStart) {
- return null;
- }
- if (firstNonWhiteSpaceCharacterFromIndex(line, parensEnd) == line.length()) {
- return null;
- }
- int methodSeparator = line.lastIndexOf('.', parensStart);
- if (methodSeparator <= classClassLoaderOrModuleStartIndex) {
- return null;
- }
- int classStartIndex = classClassLoaderOrModuleStartIndex;
- int classLoaderOrModuleEndIndex =
- firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '/');
- if (classLoaderOrModuleEndIndex < methodSeparator) {
- int moduleEndIndex = firstCharFromIndex(line, classLoaderOrModuleEndIndex + 1, '/');
- if (moduleEndIndex < methodSeparator) {
- // The stack trace contains both a class loader and module
- classStartIndex = moduleEndIndex + 1;
- } else {
- classStartIndex = classLoaderOrModuleEndIndex + 1;
- }
- }
- StackTraceElementStringProxyBuilder builder =
- StackTraceElementStringProxy.builder(line)
- .registerClassName(classStartIndex, methodSeparator, ClassNameType.TYPENAME)
- .registerMethodName(methodSeparator + 1, parensStart);
- // Check if we have a filename and position.
- int separatorIndex = line.lastIndexOf(':', parensEnd);
- if (separatorIndex > -1 && separatorIndex < parensEnd) {
- builder.registerSourceFile(parensStart + 1, separatorIndex);
- builder.registerLineNumber(separatorIndex + 1, parensEnd);
- } else {
- builder.registerSourceFile(parensStart + 1, parensEnd);
- }
- return builder.build();
- }
- }
-
- static class CircularReferenceLine {
-
- private static final String CIRCULAR_REFERENCE = "[CIRCULAR REFERENCE:";
-
- static StackTraceElementStringProxy tryParse(String line) {
- // Check that the line is indented with some amount of white space.
- if (line.length() == 0 || !Character.isWhitespace(line.charAt(0))) {
- return null;
- }
- // Find the first non-white space character and check that we have the sequence
- // '[CIRCULAR REFERENCE:Exception]'.
- int firstNonWhiteSpace = firstNonWhiteSpaceCharacterFromIndex(line, 0);
- if (!line.startsWith(CIRCULAR_REFERENCE, firstNonWhiteSpace)) {
- return null;
- }
- int exceptionStartIndex = firstNonWhiteSpace + CIRCULAR_REFERENCE.length();
- int lastBracketPosition = firstCharFromIndex(line, exceptionStartIndex, ']');
- if (lastBracketPosition == line.length()) {
- return null;
- }
- int onlyWhitespaceFromLastBracket =
- firstNonWhiteSpaceCharacterFromIndex(line, lastBracketPosition + 1);
- if (onlyWhitespaceFromLastBracket != line.length()) {
- return null;
- }
- return StackTraceElementStringProxy.builder(line)
- .registerClassName(exceptionStartIndex, lastBracketPosition, ClassNameType.TYPENAME)
- .build();
- }
- }
-
- private StackTraceElementStringProxy parseLine(String line) {
- // Most lines are 'at lines' so attempt to parse it first.
- StackTraceElementStringProxy parsedLine = AtLine.tryParse(line);
- if (parsedLine != null) {
- return parsedLine;
- }
- parsedLine = ExceptionLine.tryParse(line);
- if (parsedLine != null) {
- return parsedLine;
- }
- parsedLine = CircularReferenceLine.tryParse(line);
- if (parsedLine != null) {
- return parsedLine;
- }
- return StackTraceElementStringProxy.builder(line).build();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index a561761..f17d49b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -18,6 +18,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
+import com.android.tools.r8.kotlin.KotlinPropertyInfo;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
@@ -191,33 +193,55 @@
stripAttributes(clazz);
clazz.setAnnotations(
clazz.annotations().rewrite(annotation -> rewriteAnnotation(clazz, annotation)));
- clazz.forEachMethod(method -> processMethod(method, clazz));
- clazz.forEachField(field -> processField(field, clazz));
+ // Kotlin properties are split over fields and methods. Check if any is pinned before pruning
+ // the information.
+ Set<KotlinPropertyInfo> pinnedKotlinProperties = Sets.newIdentityHashSet();
+ clazz.forEachMethod(method -> processMethod(method, clazz, pinnedKotlinProperties));
+ clazz.forEachField(field -> processField(field, clazz, pinnedKotlinProperties));
+ clazz.forEachProgramMember(
+ member -> {
+ KotlinMemberLevelInfo kotlinInfo = member.getKotlinInfo();
+ if (kotlinInfo.isProperty()
+ && !pinnedKotlinProperties.contains(kotlinInfo.asProperty())) {
+ member.clearKotlinInfo();
+ }
+ });
}
}
- private void processMethod(DexEncodedMethod method, DexProgramClass clazz) {
+ private void processMethod(
+ DexEncodedMethod method,
+ DexProgramClass clazz,
+ Set<KotlinPropertyInfo> pinnedKotlinProperties) {
method.setAnnotations(
method.annotations().rewrite(annotation -> rewriteAnnotation(method, annotation)));
method.parameterAnnotationsList =
method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations);
- if (appView
- .getKeepInfo()
- .getMethodInfo(method, clazz)
- .isAllowSignatureAttributeRemovalAllowed(options)) {
+ KeepMethodInfo methodInfo = appView.getKeepInfo().getMethodInfo(method, clazz);
+ if (methodInfo.isAllowSignatureAttributeRemovalAllowed(options)) {
method.clearGenericSignature();
}
+ if (!methodInfo.isPinned() && method.getKotlinInfo().isFunction()) {
+ method.clearKotlinMemberInfo();
+ }
+ if (methodInfo.isPinned() && method.getKotlinInfo().isProperty()) {
+ pinnedKotlinProperties.add(method.getKotlinInfo().asProperty());
+ }
}
- private void processField(DexEncodedField field, DexProgramClass clazz) {
+ private void processField(
+ DexEncodedField field,
+ DexProgramClass clazz,
+ Set<KotlinPropertyInfo> pinnedKotlinProperties) {
field.setAnnotations(
field.annotations().rewrite(annotation -> rewriteAnnotation(field, annotation)));
- if (appView
- .getKeepInfo()
- .getFieldInfo(field, clazz)
- .isAllowSignatureAttributeRemovalAllowed(options)) {
+ KeepFieldInfo fieldInfo = appView.getKeepInfo().getFieldInfo(field, clazz);
+ if (fieldInfo.isAllowSignatureAttributeRemovalAllowed(options)) {
field.clearGenericSignature();
}
+ if (fieldInfo.isPinned() && field.getKotlinInfo().isProperty()) {
+ pinnedKotlinProperties.add(field.getKotlinInfo().asProperty());
+ }
}
private DexAnnotation rewriteAnnotation(DexDefinition holder, DexAnnotation original) {
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 7c2162c..d377362 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -6,6 +6,8 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.emulateInterfaceLibraryMethod;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType;
import static com.android.tools.r8.ir.optimize.enums.UnboxedEnumMemberRelocator.ENUM_UNBOXING_UTILITY_CLASS_SUFFIX;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
@@ -24,6 +26,7 @@
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.ClassDefinition;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.ClasspathOrLibraryDefinition;
@@ -57,13 +60,16 @@
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupLambdaTarget;
import com.android.tools.r8.graph.LookupTarget;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramDerivedContext;
import com.android.tools.r8.graph.ProgramField;
@@ -116,6 +122,7 @@
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.SetUtils;
@@ -178,6 +185,10 @@
GENERATE_MAIN_DEX_LIST,
WHY_ARE_YOU_KEEPING;
+ public boolean isTreeShaking() {
+ return isInitialTreeShaking() || isFinalTreeShaking();
+ }
+
public boolean isInitialTreeShaking() {
return this == INITIAL_TREE_SHAKING;
}
@@ -223,6 +234,7 @@
// Don't hold a direct pointer to app info (use appView).
private AppInfoWithClassHierarchy appInfo;
private final AppView<AppInfoWithClassHierarchy> appView;
+ private final DexItemFactory dexItemFactory;
private final ExecutorService executorService;
private SubtypingInfo subtypingInfo;
private final InternalOptions options;
@@ -404,6 +416,7 @@
InternalOptions options = appView.options();
this.appInfo = appView.appInfo();
this.appView = appView.withClassHierarchy();
+ this.dexItemFactory = appView.dexItemFactory();
this.executorService = executorService;
this.subtypingInfo = subtypingInfo;
this.forceProguardCompatibility = options.forceProguardCompatibility;
@@ -1495,7 +1508,8 @@
// Record field reference for generated extension registry shrinking.
appView.withGeneratedExtensionRegistryShrinker(
- shrinker -> shrinker.handleFailedOrUnknownFieldResolution(fieldReference, currentMethod));
+ shrinker ->
+ shrinker.handleFailedOrUnknownFieldResolution(fieldReference, currentMethod, mode));
return;
}
@@ -3255,6 +3269,9 @@
assert false;
}
}
+ if (mode.isInitialTreeShaking()) {
+ libraryClasses.addAll(synthesizeDesugaredLibraryClasses());
+ }
// Add just referenced non-program types. We can't replace the program classes at this point as
// they are needed in tree pruning.
@@ -3391,6 +3408,56 @@
return true;
}
+ private List<DexLibraryClass> synthesizeDesugaredLibraryClasses() {
+ List<DexLibraryClass> synthesizedClasses = new ArrayList<>();
+ Map<DexType, DexType> emulateLibraryInterface =
+ options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ emulateLibraryInterface
+ .keySet()
+ .forEach(
+ interfaceType -> {
+ DexClass interfaceClass = appView.definitionFor(interfaceType);
+ if (interfaceClass == null) {
+ appView
+ .reporter()
+ .error(
+ new StringDiagnostic(
+ "The interface "
+ + interfaceType.getTypeName()
+ + " is missing, but is required for Java 8+ API desugaring."));
+ return;
+ }
+
+ DexType emulateInterfaceType =
+ getEmulateLibraryInterfaceClassType(interfaceType, dexItemFactory);
+ assert appView.definitionFor(emulateInterfaceType) == null;
+
+ List<DexEncodedMethod> emulateInterfaceClassMethods =
+ ListUtils.newArrayList(
+ builder ->
+ interfaceClass.forEachClassMethodMatching(
+ DexEncodedMethod::isDefaultMethod,
+ method ->
+ builder.accept(
+ new DexEncodedMethod(
+ emulateInterfaceLibraryMethod(method, dexItemFactory),
+ MethodAccessFlags.createPublicStaticSynthetic(),
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ null,
+ true))));
+
+ synthesizedClasses.add(
+ DexLibraryClass.builder(dexItemFactory)
+ .setAccessFlags(ClassAccessFlags.createPublicFinalSynthetic())
+ .setDirectMethods(emulateInterfaceClassMethods)
+ .setType(emulateInterfaceType)
+ .build());
+ });
+ return synthesizedClasses;
+ }
+
private void synthesizeLibraryConversionWrappers(SyntheticAdditions additions) {
if (desugaredLibraryWrapperAnalysis == null) {
return;
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index aa87461..3e8ad60 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.DESCRIPTOR_VIVIFIED_PREFIX;
import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.getRetargetPackageAndClassPrefixDescriptor;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.utils.collections.IdentityHashSetFromMap.newProgramDerivedContextSet;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
@@ -282,8 +281,6 @@
AppView<?> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
InternalOptions options = appView.options();
- DexString emulatedLibraryClassNameSuffix =
- dexItemFactory.createString(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
DexString retargetPackageAndClassPrefixDescriptor =
dexItemFactory.createString(
getRetargetPackageAndClassPrefixDescriptor(options.desugaredLibraryConfiguration));
@@ -291,8 +288,7 @@
return type -> {
DexString descriptor = type.getDescriptor();
return descriptor.startsWith(retargetPackageAndClassPrefixDescriptor)
- || descriptor.startsWith(vivifiedClassNamePrefix)
- || descriptor.endsWith(emulatedLibraryClassNameSuffix);
+ || descriptor.startsWith(vivifiedClassNamePrefix);
};
}
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 e78f53b..72a0a0b 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -488,16 +488,17 @@
break;
}
// In compat mode traverse all direct methods in the hierarchy.
- if (currentClass == clazz || options.forceProguardCompatibility) {
- currentClass
- .directMethods()
- .forEach(
- method -> {
- DexDefinition precondition =
- testAndGetPrecondition(method, preconditionSupplier);
- markMethod(method, memberKeepRules, methodsMarked, rule, precondition, ifRule);
- });
- }
+ currentClass
+ .getMethodCollection()
+ .forEachDirectMethodMatching(
+ method ->
+ currentClass == clazz
+ || (method.isStatic() && !method.isPrivate() && !method.isInitializer())
+ || options.forceProguardCompatibility,
+ method -> {
+ DexDefinition precondition = testAndGetPrecondition(method, preconditionSupplier);
+ markMethod(method, memberKeepRules, methodsMarked, rule, precondition, ifRule);
+ });
currentClass
.virtualMethods()
.forEach(
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
index a201d6a..f8c56fa 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
@@ -3,15 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import static java.util.Collections.emptyList;
+
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.IterableUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
@@ -26,10 +31,9 @@
static class Builder {
private final CommittedSyntheticsCollection parent;
- private ImmutableMap.Builder<DexType, SyntheticProgramClassReference> newNonLegacyClasses =
- null;
- private ImmutableMap.Builder<DexType, SyntheticMethodReference> newNonLegacyMethods = null;
- private ImmutableMap.Builder<DexType, LegacySyntheticReference> newLegacyClasses = null;
+ private Map<DexType, List<SyntheticProgramClassReference>> newNonLegacyClasses = null;
+ private Map<DexType, List<SyntheticMethodReference>> newNonLegacyMethods = null;
+ private Map<DexType, List<LegacySyntheticReference>> newLegacyClasses = null;
private ImmutableSet.Builder<DexType> newSyntheticInputs = null;
public Builder(CommittedSyntheticsCollection parent) {
@@ -49,9 +53,11 @@
public Builder addNonLegacyClass(SyntheticProgramClassReference reference) {
if (newNonLegacyClasses == null) {
- newNonLegacyClasses = ImmutableMap.builder();
+ newNonLegacyClasses = new IdentityHashMap<>();
}
- newNonLegacyClasses.put(reference.getHolder(), reference);
+ newNonLegacyClasses
+ .computeIfAbsent(reference.getHolder(), ignore -> new ArrayList<>())
+ .add(reference);
return this;
}
@@ -61,30 +67,57 @@
public Builder addNonLegacyMethod(SyntheticMethodReference reference) {
if (newNonLegacyMethods == null) {
- newNonLegacyMethods = ImmutableMap.builder();
+ newNonLegacyMethods = new IdentityHashMap<>();
}
- newNonLegacyMethods.put(reference.getHolder(), reference);
+ newNonLegacyMethods
+ .computeIfAbsent(reference.getHolder(), ignore -> new ArrayList<>())
+ .add(reference);
return this;
}
public Builder addLegacyClasses(Map<DexType, LegacySyntheticDefinition> classes) {
if (newLegacyClasses == null) {
- newLegacyClasses = ImmutableMap.builder();
+ newLegacyClasses = new IdentityHashMap<>();
}
- classes.forEach((type, item) -> newLegacyClasses.put(type, item.toReference()));
+ classes.forEach(
+ (type, item) ->
+ newLegacyClasses
+ .computeIfAbsent(type, ignore -> new ArrayList<>())
+ .add(item.toReference()));
return this;
}
- public Builder addLegacyClass(LegacySyntheticReference item) {
+ public Builder addLegacyClass(LegacySyntheticReference reference) {
if (newLegacyClasses == null) {
- newLegacyClasses = ImmutableMap.builder();
+ newLegacyClasses = new IdentityHashMap<>();
}
- newLegacyClasses.put(item.getHolder(), item);
+ newLegacyClasses
+ .computeIfAbsent(reference.getHolder(), ignore -> new ArrayList<>())
+ .add(reference);
return this;
}
- Builder addToSyntheticInputs() {
- newSyntheticInputs = ImmutableSet.builder();
+ public Builder addSyntheticInput(DexType syntheticInput) {
+ if (newSyntheticInputs == null) {
+ newSyntheticInputs = ImmutableSet.builder();
+ }
+ newSyntheticInputs.add(syntheticInput);
+ return this;
+ }
+
+ Builder collectSyntheticInputs() {
+ if (newSyntheticInputs == null) {
+ newSyntheticInputs = ImmutableSet.builder();
+ }
+ if (newNonLegacyClasses != null) {
+ newSyntheticInputs.addAll(newNonLegacyClasses.keySet());
+ }
+ if (newNonLegacyMethods != null) {
+ newSyntheticInputs.addAll(newNonLegacyMethods.keySet());
+ }
+ if (newLegacyClasses != null) {
+ newSyntheticInputs.addAll(newLegacyClasses.keySet());
+ }
return this;
}
@@ -92,31 +125,30 @@
if (newNonLegacyClasses == null && newNonLegacyMethods == null && newLegacyClasses == null) {
return parent;
}
- ImmutableMap<DexType, SyntheticProgramClassReference> allNonLegacyClasses =
- newNonLegacyClasses == null
- ? parent.nonLegacyClasses
- : newNonLegacyClasses.putAll(parent.nonLegacyClasses).build();
- ImmutableMap<DexType, SyntheticMethodReference> allNonLegacyMethods =
- newNonLegacyMethods == null
- ? parent.nonLegacyMethods
- : newNonLegacyMethods.putAll(parent.nonLegacyMethods).build();
- ImmutableMap<DexType, LegacySyntheticReference> allLegacyClasses =
- newLegacyClasses == null
- ? parent.legacyTypes
- : newLegacyClasses.putAll(parent.legacyTypes).build();
+ ImmutableMap<DexType, List<SyntheticProgramClassReference>> allNonLegacyClasses =
+ merge(newNonLegacyClasses, parent.nonLegacyClasses);
+ ImmutableMap<DexType, List<SyntheticMethodReference>> allNonLegacyMethods =
+ merge(newNonLegacyMethods, parent.nonLegacyMethods);
+ ImmutableMap<DexType, List<LegacySyntheticReference>> allLegacyClasses =
+ merge(newLegacyClasses, parent.legacyTypes);
ImmutableSet<DexType> allSyntheticInputs =
- newSyntheticInputs == null
- ? parent.syntheticInputs
- : newSyntheticInputs
- .addAll(allNonLegacyClasses.keySet())
- .addAll(allNonLegacyMethods.keySet())
- .addAll(allLegacyClasses.keySet())
- .build();
+ newSyntheticInputs == null ? parent.syntheticInputs : newSyntheticInputs.build();
return new CommittedSyntheticsCollection(
allLegacyClasses, allNonLegacyMethods, allNonLegacyClasses, allSyntheticInputs);
}
}
+ private static <T> ImmutableMap<DexType, List<T>> merge(
+ Map<DexType, List<T>> newSynthetics, ImmutableMap<DexType, List<T>> oldSynthetics) {
+ if (newSynthetics == null) {
+ return oldSynthetics;
+ }
+ oldSynthetics.forEach(
+ (type, elements) ->
+ newSynthetics.computeIfAbsent(type, ignore -> new ArrayList<>()).addAll(elements));
+ return ImmutableMap.copyOf(newSynthetics);
+ }
+
private static final CommittedSyntheticsCollection EMPTY =
new CommittedSyntheticsCollection(
ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of());
@@ -126,31 +158,42 @@
*
* <p>TODO(b/158159959): Remove legacy support.
*/
- private final ImmutableMap<DexType, LegacySyntheticReference> legacyTypes;
+ private final ImmutableMap<DexType, List<LegacySyntheticReference>> legacyTypes;
/** Mapping from synthetic type to its synthetic method item description. */
- private final ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods;
+ private final ImmutableMap<DexType, List<SyntheticMethodReference>> nonLegacyMethods;
/** Mapping from synthetic type to its synthetic class item description. */
- private final ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses;
+ private final ImmutableMap<DexType, List<SyntheticProgramClassReference>> nonLegacyClasses;
/** Set of synthetic types that were present in the input. */
- private final ImmutableSet<DexType> syntheticInputs;
+ public final ImmutableSet<DexType> syntheticInputs;
public CommittedSyntheticsCollection(
- ImmutableMap<DexType, LegacySyntheticReference> legacyTypes,
- ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods,
- ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses,
+ ImmutableMap<DexType, List<LegacySyntheticReference>> legacyTypes,
+ ImmutableMap<DexType, List<SyntheticMethodReference>> nonLegacyMethods,
+ ImmutableMap<DexType, List<SyntheticProgramClassReference>> nonLegacyClasses,
ImmutableSet<DexType> syntheticInputs) {
this.legacyTypes = legacyTypes;
this.nonLegacyMethods = nonLegacyMethods;
this.nonLegacyClasses = nonLegacyClasses;
this.syntheticInputs = syntheticInputs;
- assert legacyTypes.size() + nonLegacyMethods.size() + nonLegacyClasses.size()
- == Sets.union(
- Sets.union(nonLegacyMethods.keySet(), nonLegacyClasses.keySet()),
- legacyTypes.keySet())
- .size();
+ assert verifySyntheticInputsSubsetOfSynthetics();
+ }
+
+ private boolean verifySyntheticInputsSubsetOfSynthetics() {
+ Set<DexType> synthetics =
+ ImmutableSet.<DexType>builder()
+ .addAll(legacyTypes.keySet())
+ .addAll(nonLegacyMethods.keySet())
+ .addAll(nonLegacyClasses.keySet())
+ .build();
+ syntheticInputs.forEach(
+ syntheticInput -> {
+ assert synthetics.contains(syntheticInput)
+ : "Expected " + syntheticInput.toSourceString() + " to be a synthetic";
+ });
+ return true;
}
public static CommittedSyntheticsCollection empty() {
@@ -161,10 +204,6 @@
return new Builder(this);
}
- Builder builderForSyntheticInputs() {
- return new Builder(this).addToSyntheticInputs();
- }
-
boolean isEmpty() {
boolean empty =
legacyTypes.isEmpty() && nonLegacyMethods.isEmpty() && nonLegacyClasses.isEmpty();
@@ -188,24 +227,26 @@
return syntheticInputs.contains(type);
}
- public ImmutableMap<DexType, LegacySyntheticReference> getLegacyTypes() {
+ public ImmutableMap<DexType, List<LegacySyntheticReference>> getLegacyTypes() {
return legacyTypes;
}
- public ImmutableMap<DexType, SyntheticMethodReference> getNonLegacyMethods() {
+ public List<LegacySyntheticReference> getLegacyTypes(DexType type) {
+ return legacyTypes.getOrDefault(type, emptyList());
+ }
+
+ public ImmutableMap<DexType, List<SyntheticMethodReference>> getNonLegacyMethods() {
return nonLegacyMethods;
}
- public ImmutableMap<DexType, SyntheticProgramClassReference> getNonLegacyClasses() {
+ public ImmutableMap<DexType, List<SyntheticProgramClassReference>> getNonLegacyClasses() {
return nonLegacyClasses;
}
- public SyntheticReference<?, ?, ?> getNonLegacyItem(DexType type) {
- SyntheticMethodReference reference = nonLegacyMethods.get(type);
- if (reference != null) {
- return reference;
- }
- return nonLegacyClasses.get(type);
+ public Iterable<SyntheticReference<?, ?, ?>> getNonLegacyItems(DexType type) {
+ return Iterables.concat(
+ nonLegacyClasses.getOrDefault(type, emptyList()),
+ nonLegacyMethods.getOrDefault(type, emptyList()));
}
public void forEachSyntheticInput(Consumer<DexType> fn) {
@@ -213,8 +254,8 @@
}
public void forEachNonLegacyItem(Consumer<SyntheticReference<?, ?, ?>> fn) {
- nonLegacyMethods.forEach((t, r) -> fn.accept(r));
- nonLegacyClasses.forEach((t, r) -> fn.accept(r));
+ nonLegacyMethods.values().forEach(r -> r.forEach(fn));
+ nonLegacyClasses.values().forEach(r -> r.forEach(fn));
}
CommittedSyntheticsCollection pruneItems(PrunedItems prunedItems) {
@@ -223,30 +264,37 @@
return this;
}
Builder builder = CommittedSyntheticsCollection.empty().builder();
- BooleanBox changed = new BooleanBox(false);
- legacyTypes.forEach(
- (type, item) -> {
- if (removed.contains(type)) {
- changed.set();
- } else {
- builder.addLegacyClass(item);
- }
- });
- for (SyntheticMethodReference reference : nonLegacyMethods.values()) {
+ boolean changed = false;
+ for (LegacySyntheticReference reference : IterableUtils.flatten(legacyTypes.values())) {
if (removed.contains(reference.getHolder())) {
- changed.set();
+ changed = true;
+ } else {
+ builder.addLegacyClass(reference);
+ }
+ }
+ for (SyntheticMethodReference reference : IterableUtils.flatten(nonLegacyMethods.values())) {
+ if (removed.contains(reference.getHolder())) {
+ changed = true;
} else {
builder.addNonLegacyMethod(reference);
}
}
- for (SyntheticProgramClassReference reference : nonLegacyClasses.values()) {
+ for (SyntheticProgramClassReference reference :
+ IterableUtils.flatten(nonLegacyClasses.values())) {
if (removed.contains(reference.getHolder())) {
- changed.set();
+ changed = true;
} else {
builder.addNonLegacyClass(reference);
}
}
- return changed.isTrue() ? builder.build() : this;
+ for (DexType syntheticInput : syntheticInputs) {
+ if (removed.contains(syntheticInput)) {
+ changed = true;
+ } else {
+ builder.addSyntheticInput(syntheticInput);
+ }
+ }
+ return changed ? builder.build() : this;
}
CommittedSyntheticsCollection rewriteWithLens(NonIdentityGraphLens lens) {
@@ -265,22 +313,25 @@
return rewrittenItems.build();
}
- private static <R extends Rewritable<R>> ImmutableMap<DexType, R> rewriteItems(
- Map<DexType, R> items, NonIdentityGraphLens lens) {
- ImmutableMap.Builder<DexType, R> rewrittenItems = ImmutableMap.builder();
- for (R reference : items.values()) {
+ private <R extends Rewritable<R>> ImmutableMap<DexType, List<R>> rewriteItems(
+ Map<DexType, List<R>> items, NonIdentityGraphLens lens) {
+ Map<DexType, List<R>> rewrittenItems = new IdentityHashMap<>();
+ for (R reference : IterableUtils.flatten(items.values())) {
R rewritten = reference.rewrite(lens);
if (rewritten != null) {
- rewrittenItems.put(rewritten.getHolder(), rewritten);
+ rewrittenItems
+ .computeIfAbsent(rewritten.getHolder(), ignore -> new ArrayList<>())
+ .add(rewritten);
}
}
- return rewrittenItems.build();
+ return ImmutableMap.copyOf(rewrittenItems);
}
boolean verifyTypesAreInApp(DexApplication application) {
assert verifyTypesAreInApp(application, legacyTypes.keySet());
assert verifyTypesAreInApp(application, nonLegacyMethods.keySet());
assert verifyTypesAreInApp(application, nonLegacyClasses.keySet());
+ assert verifyTypesAreInApp(application, syntheticInputs);
return true;
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 5fa5498..46bb432 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -27,12 +27,12 @@
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.structural.RepresentativeMap;
-import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -92,6 +92,15 @@
BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
+ boolean isEmpty() {
+ if (typeMap.isEmpty()) {
+ assert fieldMap.isEmpty();
+ assert methodMap.isEmpty();
+ return true;
+ }
+ return false;
+ }
+
void move(DexType from, DexType to) {
DexType old = typeMap.put(from, to);
assert old == null || old == to;
@@ -196,19 +205,24 @@
public static void finalizeWithLiveness(AppView<AppInfoWithLiveness> appView) {
Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
- appView.setAppInfo(appView.appInfo().rebuildWithLiveness(result.commit));
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo));
- appView.rewriteWithLens(result.lens);
+ if (result.lens != null) {
+ appView.rewriteWithLensAndApplication(result.lens, result.commit.getApplication().asDirect());
+ } else {
+ assert result.commit.getApplication() == appView.appInfo().app();
+ }
+ appView.setAppInfo(appView.appInfo().rebuildWithLiveness(result.commit));
appView.pruneItems(result.prunedItems);
}
Result computeFinalSynthetics(AppView<?> appView) {
assert verifyNoNestedSynthetics();
+ assert verifyOneSyntheticPerSyntheticClass();
DexApplication application;
Builder lensBuilder = new Builder();
- ImmutableMap.Builder<DexType, SyntheticMethodReference> finalMethodsBuilder =
+ ImmutableMap.Builder<DexType, List<SyntheticMethodReference>> finalMethodsBuilder =
ImmutableMap.builder();
- ImmutableMap.Builder<DexType, SyntheticProgramClassReference> finalClassesBuilder =
+ ImmutableMap.Builder<DexType, List<SyntheticProgramClassReference>> finalClassesBuilder =
ImmutableMap.builder();
Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet();
{
@@ -217,16 +231,19 @@
buildLensAndProgram(
appView,
computeEquivalences(
- appView, committed.getNonLegacyMethods().values(), generators, lensBuilder),
+ appView, committed.getNonLegacyMethods(), generators, lensBuilder),
computeEquivalences(
- appView, committed.getNonLegacyClasses().values(), generators, lensBuilder),
+ appView, committed.getNonLegacyClasses(), generators, lensBuilder),
lensBuilder,
- (clazz, reference) -> finalClassesBuilder.put(clazz.getType(), reference),
- (clazz, reference) -> finalMethodsBuilder.put(clazz.getType(), reference),
+ (clazz, reference) ->
+ finalClassesBuilder.put(clazz.getType(), ImmutableList.of(reference)),
+ (clazz, reference) ->
+ finalMethodsBuilder.put(clazz.getType(), ImmutableList.of(reference)),
derivedMainDexTypes);
}
- ImmutableMap<DexType, SyntheticMethodReference> finalMethods = finalMethodsBuilder.build();
- ImmutableMap<DexType, SyntheticProgramClassReference> finalClasses =
+ ImmutableMap<DexType, List<SyntheticMethodReference>> finalMethods =
+ finalMethodsBuilder.build();
+ ImmutableMap<DexType, List<SyntheticProgramClassReference>> finalClasses =
finalClassesBuilder.build();
Set<DexType> prunedSynthetics = Sets.newIdentityHashSet();
@@ -266,7 +283,7 @@
private <R extends SyntheticReference<R, D, ?>, D extends SyntheticDefinition<R, D, ?>>
Map<DexType, EquivalenceGroup<D>> computeEquivalences(
AppView<?> appView,
- ImmutableCollection<R> references,
+ ImmutableMap<DexType, List<R>> references,
Map<String, NumberGenerator> generators,
Builder lensBuilder) {
boolean intermediate = appView.options().intermediate;
@@ -306,6 +323,32 @@
return true;
}
+ private boolean verifyOneSyntheticPerSyntheticClass() {
+ Set<DexType> seen = Sets.newIdentityHashSet();
+ committed
+ .getLegacyTypes()
+ .forEach(
+ (type, references) -> {
+ assert seen.add(type);
+ assert references.size() == 1;
+ });
+ committed
+ .getNonLegacyClasses()
+ .forEach(
+ (type, references) -> {
+ assert seen.add(type);
+ assert references.size() == 1;
+ });
+ committed
+ .getNonLegacyMethods()
+ .forEach(
+ (type, references) -> {
+ assert seen.add(type);
+ assert references.size() == 1;
+ });
+ return true;
+ }
+
private static DexApplication buildLensAndProgram(
AppView<?> appView,
Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
@@ -316,8 +359,7 @@
Set<DexType> derivedMainDexSynthetics) {
DexApplication application = appView.appInfo().app();
MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
- List<DexProgramClass> newProgramClasses = new ArrayList<>();
- Set<DexType> pruned = Sets.newIdentityHashSet();
+ Set<DexProgramClass> pruned = Sets.newIdentityHashSet();
TreeFixerBase treeFixer =
new TreeFixerBase(appView) {
@@ -353,7 +395,7 @@
assert representativeClass.getMethodCollection().size() == 1;
for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
if (member != representative) {
- pruned.add(member.getHolder().getType());
+ pruned.add(member.getHolder());
deduplicatedClasses.add(member.getHolder());
}
if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
@@ -371,9 +413,8 @@
representative.getKind(), representative.getHolder(), context, appView);
for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
DexProgramClass memberClass = member.getHolder();
- DexType memberType = memberClass.getType();
if (member != representative) {
- pruned.add(memberType);
+ pruned.add(memberClass);
deduplicatedClasses.add(memberClass);
}
if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
@@ -382,21 +423,31 @@
}
});
- for (DexProgramClass clazz : application.classes()) {
- if (!pruned.contains(clazz.type)) {
- newProgramClasses.add(clazz);
+ // Only create a new application if anything changed.
+ if (lensBuilder.isEmpty()) {
+ assert deduplicatedClasses.isEmpty();
+ assert pruned.isEmpty();
+ } else {
+ if (!pruned.isEmpty()) {
+ List<DexProgramClass> newProgramClasses = new ArrayList<>();
+ for (DexProgramClass clazz : application.classes()) {
+ if (!pruned.contains(clazz)) {
+ newProgramClasses.add(clazz);
+ }
+ }
+ assert newProgramClasses.size() < application.classes().size();
+ application = application.builder().replaceProgramClasses(newProgramClasses).build();
}
+
+ // Assert that the non-representatives have been removed from the app.
+ assert verifyNonRepresentativesRemovedFromApplication(application, syntheticClassGroups);
+ assert verifyNonRepresentativesRemovedFromApplication(application, syntheticMethodGroups);
+
+ DexApplication.Builder<?> builder = application.builder();
+ treeFixer.fixupClasses(deduplicatedClasses);
+ builder.replaceProgramClasses(treeFixer.fixupClasses(application.classes()));
+ application = builder.build();
}
- application = application.builder().replaceProgramClasses(newProgramClasses).build();
-
- // Assert that the non-representatives have been removed from the app.
- assert verifyNonRepresentativesRemovedFromApplication(application, syntheticClassGroups);
- assert verifyNonRepresentativesRemovedFromApplication(application, syntheticMethodGroups);
-
- DexApplication.Builder<?> builder = application.builder();
- treeFixer.fixupClasses(deduplicatedClasses);
- builder.replaceProgramClasses(treeFixer.fixupClasses(application.classes()));
- application = builder.build();
// Add the synthesized from after repackaging which changed class definitions.
final DexApplication appForLookup = application;
@@ -653,9 +704,10 @@
}
private <R extends SyntheticReference<R, D, ?>, D extends SyntheticDefinition<R, D, ?>>
- Map<DexType, D> lookupDefinitions(AppView<?> appView, Collection<R> references) {
+ Map<DexType, D> lookupDefinitions(
+ AppView<?> appView, ImmutableMap<DexType, List<R>> references) {
Map<DexType, D> definitions = new IdentityHashMap<>(references.size());
- for (R reference : references) {
+ for (R reference : IterableUtils.flatten(references.values())) {
D definition = reference.lookupDefinition(appView::definitionFor);
if (definition == null) {
// We expect pruned definitions to have been removed.
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 6a539f0..befe594 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -23,8 +23,10 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -36,7 +38,6 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
public class SyntheticItems implements SyntheticDefinitionsProvider {
@@ -122,8 +123,7 @@
// If the compilation is in intermediate mode the synthetics should just be passed through.
return;
}
- CommittedSyntheticsCollection.Builder builder =
- synthetics.committed.builderForSyntheticInputs();
+ CommittedSyntheticsCollection.Builder builder = synthetics.committed.builder();
// TODO(b/158159959): Consider identifying synthetics in the input reader to speed this up.
for (DexProgramClass clazz : appView.appInfo().classes()) {
SyntheticMarker marker = SyntheticMarker.stripMarkerFromClass(clazz, appView);
@@ -138,7 +138,7 @@
new SyntheticProgramClassDefinition(marker.getKind(), marker.getContext(), clazz));
}
}
- CommittedSyntheticsCollection committed = builder.build();
+ CommittedSyntheticsCollection committed = builder.collectSyntheticInputs().build();
if (committed.isEmpty()) {
return;
}
@@ -184,15 +184,6 @@
return baseDefinitionFor.apply(type);
}
- public boolean verifyNonLegacySyntheticsAreCommitted() {
- assert pending.nonLegacyDefinitions.isEmpty()
- : "Uncommitted synthetics: "
- + pending.nonLegacyDefinitions.keySet().stream()
- .map(DexType::getName)
- .collect(Collectors.joining(", "));
- return true;
- }
-
public boolean hasPendingSyntheticClasses() {
return !pending.isEmpty();
}
@@ -233,20 +224,27 @@
return isCommittedSynthetic(type) || isPendingSynthetic(type);
}
- public SyntheticKind getNonLegacySyntheticKind(DexProgramClass clazz) {
- assert isNonLegacySynthetic(clazz);
- SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(clazz.getType());
- if (reference == null) {
- SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
- if (definition != null) {
- reference = definition.toReference();
- }
+ public boolean isEligibleForClassMerging(DexProgramClass clazz) {
+ assert isSyntheticClass(clazz);
+ return isSyntheticLambda(clazz);
+ }
+
+ // TODO(b/186211926): Allow merging of legacy synthetics.
+ private boolean isSyntheticLambda(DexProgramClass clazz) {
+ if (!isNonLegacySynthetic(clazz)) {
+ return false;
}
- if (reference != null) {
- return reference.getKind();
+ Iterable<SyntheticReference<?, ?, ?>> references = committed.getNonLegacyItems(clazz.getType());
+ if (!Iterables.isEmpty(references)) {
+ assert Iterables.size(references) == 1;
+ return references.iterator().next().getKind() == SyntheticKind.LAMBDA;
+ }
+ SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
+ if (definition != null) {
+ return definition.getKind() == SyntheticKind.LAMBDA;
}
assert false;
- return null;
+ return false;
}
public boolean isSubjectToKeepRules(DexProgramClass clazz) {
@@ -280,36 +278,32 @@
return true;
}
- private List<SynthesizingContext> getSynthesizingContexts(DexType type) {
- SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(type);
- if (reference != null) {
- return Collections.singletonList(reference.getContext());
+ private void forEachSynthesizingContext(DexType type, Consumer<SynthesizingContext> consumer) {
+ for (SyntheticReference<?, ?, ?> reference : committed.getNonLegacyItems(type)) {
+ consumer.accept(reference.getContext());
}
SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(type);
if (definition != null) {
- return Collections.singletonList(definition.getContext());
+ consumer.accept(definition.getContext());
}
- return Collections.emptyList();
+ }
+
+ private List<SynthesizingContext> getSynthesizingContexts(DexType type) {
+ return ListUtils.newImmutableList(builder -> forEachSynthesizingContext(type, builder));
}
public Collection<DexType> getSynthesizingContextTypes(DexType type) {
- SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(type);
- if (reference != null) {
- return Collections.singletonList(reference.getContext().getSynthesizingContextType());
- }
- SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(type);
- if (definition != null) {
- return Collections.singletonList(definition.getContext().getSynthesizingContextType());
- }
- LegacySyntheticReference legacyReference = committed.getLegacyTypes().get(type);
- if (legacyReference != null) {
- return legacyReference.getContexts();
+ ImmutableList.Builder<DexType> builder = ImmutableList.builder();
+ forEachSynthesizingContext(
+ type, synthesizingContext -> builder.add(synthesizingContext.getSynthesizingContextType()));
+ for (LegacySyntheticReference legacyReference : committed.getLegacyTypes(type)) {
+ builder.addAll(legacyReference.getContexts());
}
LegacySyntheticDefinition legacyDefinition = pending.legacyClasses.get(type);
if (legacyDefinition != null) {
- return legacyDefinition.getContexts();
+ builder.addAll(legacyDefinition.getContexts());
}
- return Collections.emptyList();
+ return builder.build();
}
// TODO(b/180091213): Implement this and remove client provided the oracle.
@@ -330,14 +324,12 @@
DexProgramClass clazz,
Predicate<DexProgramClass> ifIsLambda,
Predicate<DexProgramClass> ifNotLambda) {
- SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(clazz.getType());
- if (reference == null) {
- SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
- if (definition != null) {
- reference = definition.toReference();
- }
+ Iterable<SyntheticReference<?, ?, ?>> references = committed.getNonLegacyItems(clazz.getType());
+ SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
+ if (definition != null) {
+ references = Iterables.concat(references, IterableUtils.singleton(definition.toReference()));
}
- if (reference != null && reference.getKind() == SyntheticKind.LAMBDA) {
+ if (Iterables.any(references, reference -> reference.getKind() == SyntheticKind.LAMBDA)) {
assert ifIsLambda.test(clazz);
} else {
assert ifNotLambda.test(clazz);
@@ -355,6 +347,7 @@
context, appView.appInfoForDesugaring().getClassToFeatureSplitMap());
}
+ /** Used to find the synthesizing context for a new synthetic that is about to be created. */
private SynthesizingContext getSynthesizingContext(
ProgramDefinition context, ClassToFeatureSplitMap featureSplits) {
DexType contextType = context.getContextType();
@@ -362,9 +355,15 @@
if (existingDefinition != null) {
return existingDefinition.getContext();
}
- SyntheticReference<?, ?, ?> existingReference = committed.getNonLegacyItem(contextType);
- if (existingReference != null) {
- return existingReference.getContext();
+ Iterable<SyntheticReference<?, ?, ?>> existingReferences =
+ committed.getNonLegacyItems(contextType);
+ if (!Iterables.isEmpty(existingReferences)) {
+ // Use a deterministic synthesizing context from the set of contexts.
+ return IterableUtils.min(
+ existingReferences,
+ (existingReference, other) ->
+ existingReference.getReference().compareTo(other.getReference()))
+ .getContext();
}
// This context is not nested in an existing synthetic context so create a new "leaf" context.
FeatureSplit featureSplit = featureSplits.getFeatureSplit(context, this);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
index 9c19e81..b6bb218 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -54,7 +54,7 @@
@Override
SyntheticMethodReference internalRewrite(
SynthesizingContext rewrittenContext, NonIdentityGraphLens lens) {
- DexMethod rewritten = lens.lookupMethod(method);
+ DexMethod rewritten = lens.getRenamedMethodSignature(method);
// If the reference has been non-trivially rewritten the compiler has changed it and it can no
// longer be considered a synthetic. The context may or may not have changed.
if (method != rewritten && !lens.isSimpleRenaming(method, rewritten)) {
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Formatter.java b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
index b572f26..fd188c0 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
@@ -76,20 +76,14 @@
protected abstract void printTypeFooter();
void format(TraceReferencesResult result) {
- print(
- result.types,
- result.keepPackageNames,
- result.fields,
- result.methods,
- result.missingDefinition);
+ print(result.types, result.keepPackageNames, result.fields, result.methods);
}
private void print(
Set<TracedClass> types,
Set<PackageReference> keepPackageNames,
Map<ClassReference, Set<TracedField>> fields,
- Map<ClassReference, Set<TracedMethod>> methods,
- Set<Object> missingDefinition) {
+ Map<ClassReference, Set<TracedMethod>> methods) {
List<TracedClass> sortedTypes = new ArrayList<>(types);
sortedTypes.sort(Comparator.comparing(tracedClass -> tracedClass.getReference().getTypeName()));
for (TracedClass type : sortedTypes) {
@@ -97,7 +91,7 @@
methods.getOrDefault(type.getReference(), Collections.emptySet());
Set<TracedField> fieldsForClass =
fields.getOrDefault(type.getReference(), Collections.emptySet());
- if (missingDefinition.contains(type.getReference())) {
+ if (type.isMissingDefinition()) {
continue;
}
printTypeHeader(type);
diff --git a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java b/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
deleted file mode 100644
index 1d4e46a..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.tracereferences;
-
-import com.android.tools.r8.Diagnostic;
-import com.android.tools.r8.Keep;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.position.Position;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.FieldReference;
-import com.android.tools.r8.references.MethodReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-@Keep
-public class MissingDefinitionsDiagnostic implements Diagnostic {
-
- private final Set<ClassReference> missingClasses;
- private final Set<FieldReference> missingFields;
- private final Set<MethodReference> missingMethods;
-
- MissingDefinitionsDiagnostic(
- Set<ClassReference> missingClasses,
- Set<FieldReference> missingFields,
- Set<MethodReference> missingMethods) {
- this.missingClasses = missingClasses;
- this.missingFields = missingFields;
- this.missingMethods = missingMethods;
- }
-
- @Override
- public Origin getOrigin() {
- return Origin.unknown();
- }
-
- @Override
- public Position getPosition() {
- return Position.UNKNOWN;
- }
-
- private <T> void appendSorted(StringBuilder builder, Set<T> missing) {
- missing.stream()
- .map(Object::toString)
- .sorted()
- .forEach(item -> builder.append(" ").append(item).append(System.lineSeparator()));
- }
-
- public Set<ClassReference> getMissingClasses() {
- return missingClasses;
- }
-
- public Set<FieldReference> getMissingFields() {
- return missingFields;
- }
-
- public Set<MethodReference> getMissingMethods() {
- return missingMethods;
- }
-
- @Override
- public String getDiagnosticMessage() {
- StringBuilder builder = new StringBuilder("Tracereferences found ");
- List<String> components = new ArrayList<>();
- if (missingClasses.size() > 0) {
- components.add("" + missingClasses.size() + " classe(s)");
- }
- if (missingFields.size() > 0) {
- components.add("" + missingFields.size() + " field(s)");
- }
- if (missingMethods.size() > 0) {
- components.add("" + missingMethods.size() + " method(s)");
- }
- assert components.size() > 0;
- for (int i = 0; i < components.size(); i++) {
- if (i != 0) {
- builder.append(i < components.size() - 1 ? ", " : " and ");
- }
- builder.append(components.get(i));
- }
- builder.append(" without definition");
- builder.append(System.lineSeparator());
- builder.append(System.lineSeparator());
- if (missingClasses.size() > 0) {
- builder.append("Classe(s) without definition:" + System.lineSeparator());
- appendSorted(builder, missingClasses);
- }
- if (missingFields.size() > 0) {
- builder.append("Field(s) without definition:" + System.lineSeparator());
- appendSorted(builder, missingFields);
- }
- if (missingMethods.size() > 0) {
- builder.append("Method(s) without definition:" + System.lineSeparator());
- appendSorted(builder, missingMethods);
- }
- return builder.toString();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
index b11a901..1c98765 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
@@ -329,7 +329,7 @@
return this;
}
- Builder setConsumer(TraceReferencesConsumer consumer) {
+ public Builder setConsumer(TraceReferencesConsumer consumer) {
this.consumer = consumer;
return this;
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
index e40f3d7..e0ce86a 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
@@ -16,25 +16,22 @@
import java.util.Map;
import java.util.Set;
-class TraceReferencesResult {
+public class TraceReferencesResult {
final Set<TracedClass> types;
final Map<ClassReference, Set<TracedField>> fields;
final Map<ClassReference, Set<TracedMethod>> methods;
final Set<PackageReference> keepPackageNames;
- final Set<Object> missingDefinition;
TraceReferencesResult(
Set<TracedClass> types,
Map<ClassReference, Set<TracedField>> fields,
Map<ClassReference, Set<TracedMethod>> methods,
- Set<PackageReference> keepPackageNames,
- Set<Object> missingDefinition) {
+ Set<PackageReference> keepPackageNames) {
this.types = types;
this.fields = fields;
this.methods = methods;
this.keepPackageNames = keepPackageNames;
- this.missingDefinition = missingDefinition;
}
static Builder builder() {
@@ -45,33 +42,23 @@
private final Set<TracedClass> types = new HashSet<>();
private final Map<ClassReference, Set<TracedField>> fields = new HashMap<>();
private final Map<ClassReference, Set<TracedMethod>> methods = new HashMap<>();
- private final Set<Object> missingDefinition = new HashSet<>();
private final Set<PackageReference> keepPackageNames = new HashSet<>();
@Override
public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
types.add(tracedClass);
- if (tracedClass.isMissingDefinition()) {
- this.missingDefinition.add(tracedClass.getReference());
- }
}
@Override
public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
FieldReference field = tracedField.getReference();
fields.computeIfAbsent(field.getHolderClass(), k -> new HashSet<>()).add(tracedField);
- if (tracedField.isMissingDefinition()) {
- this.missingDefinition.add(field);
- }
}
@Override
public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
MethodReference method = tracedMethod.getReference();
methods.computeIfAbsent(method.getHolderClass(), k -> new HashSet<>()).add(tracedMethod);
- if (tracedMethod.isMissingDefinition()) {
- this.missingDefinition.add(method);
- }
}
@Override
@@ -83,13 +70,7 @@
public void finished(DiagnosticsHandler handler) {}
TraceReferencesResult build() {
- missingDefinition.forEach(
- missingDefinition -> {
- assert missingDefinition instanceof ClassReference
- || missingDefinition instanceof FieldReference
- || missingDefinition instanceof MethodReference;
- });
- return new TraceReferencesResult(types, fields, methods, keepPackageNames, missingDefinition);
+ return new TraceReferencesResult(types, fields, methods, keepPackageNames);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 16a6bfc..1dc8da6 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -3,15 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.diagnostic.internal.MissingClassInfoImpl;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionsDiagnosticImpl;
+import com.android.tools.r8.diagnostic.internal.MissingFieldInfoImpl;
+import com.android.tools.r8.diagnostic.internal.MissingMethodInfoImpl;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -20,13 +23,16 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.FieldLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -45,8 +51,8 @@
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
+import java.util.function.Predicate;
class Tracer {
@@ -160,13 +166,21 @@
static class TracedClassImpl extends TracedReferenceBase<ClassReference, ClassAccessFlags>
implements TracedClass {
+ private TracedClassImpl(DexType type) {
+ this(type, null);
+ }
+
private TracedClassImpl(DexType reference, DexClass definition) {
super(
- Reference.classFromDescriptor(reference.toDescriptorString()),
+ reference.asClassReference(),
definition != null ? new ClassAccessFlagsImpl(definition.getAccessFlags()) : null,
definition == null);
}
+ private TracedClassImpl(DexClass clazz) {
+ this(clazz.getType(), clazz);
+ }
+
@Override
public String getKindName() {
return "type";
@@ -180,16 +194,21 @@
static class TracedFieldImpl extends TracedReferenceBase<FieldReference, FieldAccessFlags>
implements TracedField {
+ private TracedFieldImpl(DexField field) {
+ this(field, null);
+ }
+
private TracedFieldImpl(DexField reference, DexEncodedField definition) {
super(
- Reference.field(
- Reference.classFromDescriptor(reference.holder.toDescriptorString()),
- reference.name.toString(),
- Reference.typeFromDescriptor(reference.type.toDescriptorString())),
+ reference.asFieldReference(),
definition != null ? new FieldAccessFlagsImpl(definition.getAccessFlags()) : null,
definition == null);
}
+ private TracedFieldImpl(DexClassAndField field) {
+ this(field.getReference(), field.getDefinition());
+ }
+
@Override
public String getKindName() {
return "field";
@@ -203,6 +222,10 @@
static class TracedMethodImpl extends TracedReferenceBase<MethodReference, MethodAccessFlags>
implements TracedMethod {
+ private TracedMethodImpl(DexMethod reference) {
+ this(reference, null);
+ }
+
private TracedMethodImpl(DexMethod reference, DexEncodedMethod definition) {
super(
reference.asMethodReference(),
@@ -210,6 +233,10 @@
definition == null);
}
+ private TracedMethodImpl(DexClassAndMethod method) {
+ this(method.getReference(), method.getDefinition());
+ }
+
@Override
public String getKindName() {
return "method";
@@ -221,59 +248,86 @@
}
}
- private final Set<String> descriptors;
- private final DiagnosticsHandler diagnostics;
- private final DirectMappedDexApplication application;
private final AppInfoWithClassHierarchy appInfo;
+ private final DiagnosticsHandler diagnostics;
+ private final GraphLens graphLens;
+ private final InitClassLens initClassLens;
+ private final Predicate<DexType> targetPredicate;
- Tracer(Set<String> descriptors, AndroidApp inputApp, DiagnosticsHandler diagnostics)
+ Tracer(Set<String> targetDescriptors, AndroidApp inputApp, DiagnosticsHandler diagnostics)
throws IOException {
- this.descriptors = descriptors;
- this.diagnostics = diagnostics;
- InternalOptions options = new InternalOptions();
- application = new ApplicationReader(inputApp, options, Timing.empty()).read().toDirect();
- appInfo =
+ this(
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
- application,
+ new ApplicationReader(inputApp, new InternalOptions(), Timing.empty())
+ .read()
+ .toDirect(),
ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
- MainDexInfo.none());
+ MainDexInfo.none()),
+ diagnostics,
+ GraphLens.getIdentityLens(),
+ InitClassLens.getThrowingInstance(),
+ type -> targetDescriptors.contains(type.toDescriptorString()));
+ }
+
+ private Tracer(
+ AppInfoWithClassHierarchy appInfo,
+ DiagnosticsHandler diagnostics,
+ GraphLens graphLens,
+ InitClassLens initClassLens,
+ Predicate<DexType> targetPredicate) {
+ this.appInfo = appInfo;
+ this.diagnostics = diagnostics;
+ this.graphLens = graphLens;
+ this.initClassLens = initClassLens;
+ this.targetPredicate = targetPredicate;
}
void run(TraceReferencesConsumer consumer) {
- UseCollector useCollector = new UseCollector(appInfo.dexItemFactory(), consumer, diagnostics);
- for (DexProgramClass clazz : application.classes()) {
- useCollector.setContext(clazz);
+ UseCollector useCollector = new UseCollector(appInfo, consumer, diagnostics, targetPredicate);
+ for (DexProgramClass clazz : appInfo.classes()) {
useCollector.registerSuperType(clazz, clazz.superType);
- for (DexType implementsType : clazz.interfaces.values) {
+ for (DexType implementsType : clazz.getInterfaces()) {
useCollector.registerSuperType(clazz, implementsType);
}
- clazz.forEachProgramMethod(useCollector::registerMethod);
clazz.forEachField(useCollector::registerField);
+ clazz.forEachProgramMethod(
+ method -> {
+ useCollector.registerMethod(method);
+ useCollector.traceCode(method, graphLens, initClassLens);
+ });
}
consumer.finished(diagnostics);
useCollector.reportMissingDefinitions();
}
- class UseCollector extends UseRegistry {
+ // The graph lens is intentionally only made accessible to the MethodUseCollector, since the
+ // graph lens should only be applied to the code.
+ static class UseCollector {
- private DexItemFactory factory;
+ private final AppInfoWithClassHierarchy appInfo;
+ private final DexItemFactory factory;
private final TraceReferencesConsumer consumer;
- private DexProgramClass context;
private final DiagnosticsHandler diagnostics;
+ private final Predicate<DexType> targetPredicate;
+
private final Set<ClassReference> missingClasses = new HashSet<>();
private final Set<FieldReference> missingFields = new HashSet<>();
private final Set<MethodReference> missingMethods = new HashSet<>();
UseCollector(
- DexItemFactory factory, TraceReferencesConsumer consumer, DiagnosticsHandler diagnostics) {
- super(factory);
- this.factory = factory;
+ AppInfoWithClassHierarchy appInfo,
+ TraceReferencesConsumer consumer,
+ DiagnosticsHandler diagnostics,
+ Predicate<DexType> targetPredicate) {
+ this.appInfo = appInfo;
+ this.factory = appInfo.dexItemFactory();
this.consumer = consumer;
this.diagnostics = diagnostics;
+ this.targetPredicate = targetPredicate;
}
private boolean isTargetType(DexType type) {
- return descriptors.contains(type.toDescriptorString());
+ return targetPredicate.test(type);
}
private void addType(DexType type) {
@@ -284,71 +338,57 @@
if (type.isPrimitiveType() || type.isVoidType()) {
return;
}
+ assert type.isClassType();
+ addClassType(type);
+ }
+
+ private void addTypes(DexTypeList types) {
+ types.forEach(this::addType);
+ }
+
+ private void addClassType(DexType type) {
+ assert type.isClassType();
DexClass clazz = appInfo.definitionFor(type);
- TracedClassImpl tracedClass = new TracedClassImpl(type, clazz);
- checkMissingDefinition(tracedClass);
- if (isTargetType(type) || tracedClass.isMissingDefinition()) {
+ if (clazz != null) {
+ addClass(clazz);
+ } else {
+ TracedClassImpl tracedClass = new TracedClassImpl(type);
+ collectMissingClass(tracedClass);
consumer.acceptType(tracedClass, diagnostics);
- if (!tracedClass.isMissingDefinition()
- && clazz.accessFlags.isVisibilityDependingOnPackage()) {
+ }
+ }
+
+ private void addClass(DexClass clazz) {
+ if (isTargetType(clazz.getType())) {
+ TracedClassImpl tracedClass = new TracedClassImpl(clazz);
+ consumer.acceptType(tracedClass, diagnostics);
+ if (clazz.getAccessFlags().isVisibilityDependingOnPackage()) {
consumer.acceptPackage(
- Reference.packageFromString(clazz.type.getPackageName()), diagnostics);
+ Reference.packageFromString(clazz.getType().getPackageName()), diagnostics);
}
}
}
- private void addField(DexField field) {
- addType(field.type);
- DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
- if (baseField != null && baseField.getHolderType() != field.holder) {
- field = baseField.getReference();
- }
- addType(field.holder);
- TracedFieldImpl tracedField = new TracedFieldImpl(field, baseField);
- checkMissingDefinition(tracedField);
- if (isTargetType(field.holder) || tracedField.isMissingDefinition()) {
- consumer.acceptField(tracedField, diagnostics);
- if (!tracedField.isMissingDefinition()
- && baseField.accessFlags.isVisibilityDependingOnPackage()) {
- consumer.acceptPackage(
- Reference.packageFromString(baseField.getHolderType().getPackageName()), diagnostics);
- }
- }
- }
+ private void addSuperMethodFromTarget(DexClassAndMethod method) {
+ assert !method.isProgramMethod();
+ assert isTargetType(method.getHolderType());
- private void addMethod(DexMethod method) {
- addType(method.holder);
- for (DexType parameterType : method.proto.parameters.values) {
- addType(parameterType);
- }
- addType(method.proto.returnType);
- DexClass holder = appInfo.definitionForHolder(method);
- DexEncodedMethod definition = method.lookupOnClass(holder);
- TracedMethodImpl tracedMethod = new TracedMethodImpl(method, definition);
- if (isTargetType(method.holder) || tracedMethod.isMissingDefinition()) {
+ // There should be no need to register the types referenced from the method signature:
+ // - The return type and the parameter types are registered when visiting the source method
+ // that overrides this target method,
+ // - The holder type is registered from visiting the extends/implements clause of the sub
+ // class.
+
+ TracedMethodImpl tracedMethod = new TracedMethodImpl(method);
+ if (isTargetType(method.getHolderType())) {
consumer.acceptMethod(tracedMethod, diagnostics);
- checkMissingDefinition(tracedMethod);
- if (!tracedMethod.isMissingDefinition()
- && definition.accessFlags.isVisibilityDependingOnPackage()) {
+ if (method.getAccessFlags().isVisibilityDependingOnPackage()) {
consumer.acceptPackage(
- Reference.packageFromString(definition.getHolderType().getPackageName()),
- diagnostics);
+ Reference.packageFromString(method.getHolderType().getPackageName()), diagnostics);
}
}
}
- private void checkMissingDefinition(TracedClassImpl tracedClass) {
- collectMissing(tracedClass, missingClasses);
- }
-
- private void checkMissingDefinition(TracedFieldImpl tracedField) {
- collectMissing(tracedField, missingFields);
- }
-
- private void checkMissingDefinition(TracedMethodImpl tracedMethod) {
- collectMissing(tracedMethod, missingMethods);
- }
-
private <R, T extends TracedReferenceBase<R, ?>> void collectMissing(
T tracedReference, Set<R> missingCollection) {
if (tracedReference.isMissingDefinition()) {
@@ -356,163 +396,255 @@
}
}
+ private void collectMissingClass(TracedClassImpl tracedClass) {
+ assert tracedClass.isMissingDefinition();
+ collectMissing(tracedClass, missingClasses);
+ }
+
+ private void collectMissingField(TracedFieldImpl tracedField) {
+ assert tracedField.isMissingDefinition();
+ collectMissing(tracedField, missingFields);
+ }
+
+ private void collectMissingMethod(TracedMethodImpl tracedMethod) {
+ assert tracedMethod.isMissingDefinition();
+ collectMissing(tracedMethod, missingMethods);
+ }
+
private void reportMissingDefinitions() {
if (missingClasses.size() > 0 || missingFields.size() > 0 || missingMethods.size() > 0) {
- diagnostics.error(
- new MissingDefinitionsDiagnostic(missingClasses, missingFields, missingMethods));
+ MissingDefinitionsDiagnosticImpl.Builder diagnosticBuilder =
+ MissingDefinitionsDiagnosticImpl.builder();
+ missingClasses.forEach(
+ classReference ->
+ diagnosticBuilder.addMissingDefinitionInfo(
+ MissingClassInfoImpl.builder().setClass(classReference).build()));
+ missingFields.forEach(
+ fieldReference ->
+ diagnosticBuilder.addMissingDefinitionInfo(
+ MissingFieldInfoImpl.builder().setField(fieldReference).build()));
+ missingMethods.forEach(
+ methodReference ->
+ diagnosticBuilder.addMissingDefinitionInfo(
+ MissingMethodInfoImpl.builder().setMethod(methodReference).build()));
+ diagnostics.error(diagnosticBuilder.build());
}
}
- public void setContext(DexProgramClass context) {
- this.context = context;
- }
-
- @Override
- public void registerInitClass(DexType clazz) {
- addType(clazz);
- }
-
- @Override
- public void registerInvokeVirtual(DexMethod method) {
- if (method.holder.isArrayType()) {
- addType(method.holder);
- return;
- }
- ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
- DexEncodedMethod target =
- resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
- if (target != null && target.getReference() != method) {
- addType(method.holder);
- addMethod(target.getReference());
- } else {
- addMethod(method);
- }
- }
-
- @Override
- public void registerInvokeDirect(DexMethod method) {
- addMethod(method);
- }
-
- @Override
- public void registerInvokeStatic(DexMethod method) {
- DexEncodedMethod target = appInfo.unsafeResolveMethodDueToDexFormat(method).getSingleTarget();
- if (target != null && target.getReference() != method) {
- addType(method.holder);
- addMethod(target.getReference());
- } else {
- addMethod(method);
- }
- }
-
- @Override
- public void registerInvokeInterface(DexMethod method) {
- registerInvokeVirtual(method);
- }
-
- @Override
- public void registerInvokeSuper(DexMethod method) {
- DexClassAndMethod superTarget = appInfo.lookupSuperTarget(method, context);
- if (superTarget != null) {
- addMethod(superTarget.getReference());
- } else {
- addMethod(method);
- }
- }
-
- @Override
- public void registerInstanceFieldWrite(DexField field) {
- addField(field);
- }
-
- @Override
- public void registerInstanceFieldRead(DexField field) {
- addField(field);
- }
-
- @Override
- public void registerNewInstance(DexType type) {
- addType(type);
- }
-
- @Override
- public void registerStaticFieldRead(DexField field) {
- addField(field);
- }
-
- @Override
- public void registerStaticFieldWrite(DexField field) {
- addField(field);
- }
-
- @Override
- public void registerTypeReference(DexType type) {
- addType(type);
- }
-
- @Override
- public void registerInstanceOf(DexType type) {
- addType(type);
- }
-
private void registerField(DexEncodedField field) {
- registerTypeReference(field.getReference().type);
+ addType(field.getType());
}
private void registerMethod(ProgramMethod method) {
- DexClassAndMethod superTarget =
- appInfo
- .resolveMethodOn(method.getHolder(), method.getReference())
- .lookupInvokeSpecialTarget(context, appInfo);
- if (superTarget != null) {
- addMethod(superTarget.getReference());
- }
- for (DexType type : method.getDefinition().getParameters()) {
- registerTypeReference(type);
- }
+ addTypes(method.getParameters());
+ addType(method.getReturnType());
for (DexAnnotation annotation : method.getDefinition().annotations().annotations) {
- if (annotation.annotation.type == appInfo.dexItemFactory().annotationThrows) {
+ if (annotation.getAnnotationType() == appInfo.dexItemFactory().annotationThrows) {
DexValueArray dexValues = annotation.annotation.elements[0].value.asDexValueArray();
for (DexValue dexValType : dexValues.getValues()) {
- registerTypeReference(dexValType.asDexValueType().value);
+ addType(dexValType.asDexValueType().value);
}
}
}
- registerTypeReference(method.getDefinition().returnType());
- method.registerCodeReferences(this);
+
+ DexClassAndMethod superTarget =
+ appInfo
+ .resolveMethodOn(method.getHolder(), method.getReference())
+ .lookupInvokeSpecialTarget(method.getHolder(), appInfo);
+ if (superTarget != null
+ && !superTarget.isProgramMethod()
+ && isTargetType(superTarget.getHolderType())) {
+ addSuperMethodFromTarget(superTarget);
+ }
+ }
+
+ private void traceCode(ProgramMethod method, GraphLens graphLens, InitClassLens initClassLens) {
+ method.registerCodeReferences(new MethodUseCollector(method, graphLens, initClassLens));
}
private void registerSuperType(DexProgramClass clazz, DexType superType) {
- registerTypeReference(superType);
+ addType(superType);
// If clazz overrides any methods in superType, we should keep those as well.
clazz.forEachMethod(
method -> {
- ResolutionResult resolutionResult =
- appInfo.resolveMethodOn(
- superType, method.getReference(), superType != clazz.superType);
- DexEncodedMethod dexEncodedMethod = resolutionResult.getSingleTarget();
- if (dexEncodedMethod != null) {
- addMethod(dexEncodedMethod.getReference());
+ DexClassAndMethod resolvedMethod =
+ appInfo
+ .resolveMethodOn(superType, method.getReference(), superType != clazz.superType)
+ .getResolutionPair();
+ if (resolvedMethod != null
+ && !resolvedMethod.isProgramMethod()
+ && isTargetType(resolvedMethod.getHolderType())) {
+ addSuperMethodFromTarget(resolvedMethod);
}
});
}
- @Override
- public void registerCallSite(DexCallSite callSite) {
- super.registerCallSite(callSite);
+ class MethodUseCollector extends UseRegistry {
- // For Lambda's, in order to find the correct use, we need to register the method for the
- // functional interface.
- List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
- if (directInterfaces != null) {
- for (DexType directInterface : directInterfaces) {
- DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(directInterface));
- if (clazz != null) {
- clazz.forEachProgramVirtualMethodMatching(
- definition -> definition.getReference().name.equals(callSite.methodName),
- this::registerMethod);
- }
+ private final ProgramMethod context;
+ private final GraphLens graphLens;
+ private final InitClassLens initClassLens;
+
+ public MethodUseCollector(
+ ProgramMethod context, GraphLens graphLens, InitClassLens initClassLens) {
+ super(appInfo.dexItemFactory());
+ this.context = context;
+ this.graphLens = graphLens;
+ this.initClassLens = initClassLens;
+ }
+
+ // Method references.
+
+ @Override
+ public void registerInvokeDirect(DexMethod method) {
+ MethodLookupResult lookupResult = graphLens.lookupInvokeDirect(method, context);
+ assert lookupResult.getType().isDirect();
+ DexMethod rewrittenMethod = lookupResult.getReference();
+ DexClass holder = appInfo.definitionFor(rewrittenMethod.getHolderType());
+ handleRewrittenMethodReference(
+ rewrittenMethod, rewrittenMethod.lookupMemberOnClass(holder));
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod method) {
+ MethodLookupResult lookupResult = graphLens.lookupInvokeInterface(method, context);
+ assert lookupResult.getType().isInterface();
+ handleInvokeWithDynamicDispatch(lookupResult);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ MethodLookupResult lookupResult = graphLens.lookupInvokeStatic(method, context);
+ assert lookupResult.getType().isStatic();
+ DexMethod rewrittenMethod = lookupResult.getReference();
+ DexClassAndMethod resolvedMethod =
+ appInfo.unsafeResolveMethodDueToDexFormat(rewrittenMethod).getResolutionPair();
+ handleRewrittenMethodReference(rewrittenMethod, resolvedMethod);
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ MethodLookupResult lookupResult = graphLens.lookupInvokeSuper(method, context);
+ assert lookupResult.getType().isSuper();
+ DexMethod rewrittenMethod = lookupResult.getReference();
+ DexClassAndMethod superTarget = appInfo.lookupSuperTarget(rewrittenMethod, context);
+ handleRewrittenMethodReference(rewrittenMethod, superTarget);
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod method) {
+ MethodLookupResult lookupResult = graphLens.lookupInvokeVirtual(method, context);
+ assert lookupResult.getType().isVirtual();
+ handleInvokeWithDynamicDispatch(lookupResult);
+ }
+
+ private void handleInvokeWithDynamicDispatch(MethodLookupResult lookupResult) {
+ DexMethod method = lookupResult.getReference();
+ if (method.getHolderType().isArrayType()) {
+ assert lookupResult.getType().isVirtual();
+ addType(method.getHolderType());
+ return;
}
+ assert lookupResult.getType().isInterface() || lookupResult.getType().isVirtual();
+ ResolutionResult resolutionResult =
+ lookupResult.getType().isInterface()
+ ? appInfo.resolveMethodOnInterface(method)
+ : appInfo.resolveMethodOnClass(method);
+ DexClassAndMethod resolvedMethod =
+ resolutionResult.isVirtualTarget() ? resolutionResult.getResolutionPair() : null;
+ handleRewrittenMethodReference(method, resolvedMethod);
+ }
+
+ private void handleRewrittenMethodReference(
+ DexMethod method, DexClassAndMethod resolvedMethod) {
+ assert resolvedMethod == null || resolvedMethod.getReference().match(method);
+ addType(method.getHolderType());
+ addTypes(method.getParameters());
+ addType(method.getReturnType());
+ if (resolvedMethod != null) {
+ if (isTargetType(resolvedMethod.getHolderType())) {
+ if (resolvedMethod.getHolderType() != method.getHolderType()) {
+ addType(resolvedMethod.getHolderType());
+ }
+ TracedMethodImpl tracedMethod = new TracedMethodImpl(resolvedMethod);
+ consumer.acceptMethod(tracedMethod, diagnostics);
+ if (resolvedMethod.getAccessFlags().isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(
+ Reference.packageFromString(resolvedMethod.getHolderType().getPackageName()),
+ diagnostics);
+ }
+ }
+ } else {
+ TracedMethodImpl tracedMethod = new TracedMethodImpl(method);
+ collectMissingMethod(tracedMethod);
+ consumer.acceptMethod(tracedMethod, diagnostics);
+ }
+ }
+
+ // Field references.
+
+ @Override
+ public void registerInitClass(DexType clazz) {
+ DexType rewrittenClass = graphLens.lookupType(clazz);
+ DexField clinitField = initClassLens.getInitClassField(rewrittenClass);
+ handleRewrittenFieldReference(clinitField);
+ }
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ handleFieldAccess(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ handleFieldAccess(field);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ handleFieldAccess(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ handleFieldAccess(field);
+ }
+
+ private void handleFieldAccess(DexField field) {
+ FieldLookupResult lookupResult = graphLens.lookupFieldResult(field);
+ handleRewrittenFieldReference(lookupResult.getReference());
+ }
+
+ private void handleRewrittenFieldReference(DexField field) {
+ addType(field.getHolderType());
+ addType(field.getType());
+
+ DexClassAndField resolvedField = appInfo.resolveField(field).getResolutionPair();
+ if (resolvedField != null) {
+ if (isTargetType(resolvedField.getHolderType())) {
+ if (resolvedField.getHolderType() != field.getHolderType()) {
+ addClass(resolvedField.getHolder());
+ }
+ TracedFieldImpl tracedField = new TracedFieldImpl(resolvedField);
+ consumer.acceptField(tracedField, diagnostics);
+ if (resolvedField.getAccessFlags().isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(
+ Reference.packageFromString(resolvedField.getHolderType().getPackageName()),
+ diagnostics);
+ }
+ }
+ } else {
+ TracedFieldImpl tracedField = new TracedFieldImpl(field);
+ collectMissingField(tracedField);
+ consumer.acceptField(tracedField, diagnostics);
+ }
+ }
+
+ // Type references.
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ addType(graphLens.lookupType(type));
}
}
}
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 2cb1e9e..d868ed1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -81,6 +81,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
@@ -815,6 +816,8 @@
/** A set of dexitems we have reported missing to dedupe warnings. */
private final Set<DexItem> reportedMissingForDesugaring = Sets.newConcurrentHashSet();
+ private final AtomicBoolean reportedErrorReadingKotlinMetadataReflectively =
+ new AtomicBoolean(false);
private final Set<DexItem> invalidLibraryClasses = Sets.newConcurrentHashSet();
public RuntimeException errorMissingNestHost(DexClass clazz) {
@@ -903,6 +906,16 @@
}
}
+ public void warningReadingKotlinMetadataReflective() {
+ if (reportedErrorReadingKotlinMetadataReflectively.compareAndSet(false, true)) {
+ reporter.warning(
+ new StringDiagnostic(
+ "Could not read the kotlin metadata message reflectively which indicates the"
+ + " compiler running in the context of a Security Manager. Not being able to"
+ + " read the kotlin metadata will have a negative effect oncode size"));
+ }
+ }
+
public void warningInvalidLibrarySuperclassForDesugar(
Origin origin,
DexType libraryType,
@@ -1176,7 +1189,6 @@
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null;
public boolean enableConstructorMerging = true;
- public boolean enableJavaLambdaMerging = true;
public int maxGroupSize = 30;
@@ -1192,10 +1204,6 @@
this.enable = enable;
}
- public void enableJavaLambdaMerging() {
- enableJavaLambdaMerging = true;
- }
-
public int getMaxGroupSize() {
return maxGroupSize;
}
@@ -1211,10 +1219,6 @@
public boolean isEnabled() {
return enable;
}
-
- public boolean isJavaLambdaMergingEnabled() {
- return enableJavaLambdaMerging;
- }
}
public static class ProtoShrinkingOptions {
@@ -1228,6 +1232,14 @@
// See b/174530756 for more details.
public boolean enableProtoEnumSwitchMapShrinking = true;
+ public void disable() {
+ enableGeneratedExtensionRegistryShrinking = false;
+ enableGeneratedMessageLiteShrinking = false;
+ enableGeneratedMessageLiteBuilderShrinking = false;
+ traverseOneOfAndRepeatedProtoFields = false;
+ enableEnumLiteProtoShrinking = false;
+ }
+
public boolean enableRemoveProtoEnumSwitchMap() {
return isProtoShrinkingEnabled() && enableProtoEnumSwitchMapShrinking;
}
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index edf9879..1a61b44 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -8,6 +8,7 @@
import com.google.common.collect.Iterators;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiFunction;
@@ -74,6 +75,16 @@
return iterable.iterator().hasNext();
}
+ public static <T> T min(Iterable<T> iterable, Comparator<T> comparator) {
+ T min = null;
+ for (T element : iterable) {
+ if (min == null || comparator.compare(element, min) < 0) {
+ min = element;
+ }
+ }
+ return min;
+ }
+
public static <T> int size(Iterable<T> iterable) {
int result = 0;
for (T element : iterable) {
@@ -104,7 +115,7 @@
return Iterables.concat(singleton(t), iterable);
}
- public static <T> T flatten(T init, BiFunction<T, T, T> combine, Iterable<? extends T> iterable) {
+ public static <T> T reduce(T init, BiFunction<T, T, T> combine, Iterable<? extends T> iterable) {
T v = init;
for (T t : iterable) {
v = combine.apply(v, t);
@@ -113,7 +124,7 @@
}
public static int sumInt(Iterable<Integer> iterable) {
- return flatten(0, Integer::sum, iterable);
+ return reduce(0, Integer::sum, iterable);
}
public static <F> int sumInt(Iterable<F> iterable, Function<? super F, Integer> fn) {
@@ -121,6 +132,10 @@
return sumInt(integers);
}
+ public static <T> Iterable<T> flatten(Iterable<? extends Iterable<T>> iterable) {
+ return flatMap(iterable, Function.identity());
+ }
+
public static <T, U> Iterable<U> flatMap(
Iterable<T> iterable, Function<? super T, Iterable<U>> map) {
return Iterables.concat(Iterables.transform(iterable, map::apply));
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 62a45dd..e7c8e46 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -140,6 +141,12 @@
return list;
}
+ public static <T> ImmutableList<T> newImmutableList(ForEachable<T> forEachable) {
+ ImmutableList.Builder<T> builder = ImmutableList.builder();
+ forEachable.forEach(builder::add);
+ return builder.build();
+ }
+
public static <T> Optional<T> removeFirstMatch(List<T> list, Predicate<T> element) {
int index = firstIndexMatching(list, element);
if (index >= 0) {
diff --git a/src/main/java/com/android/tools/r8/utils/Pair.java b/src/main/java/com/android/tools/r8/utils/Pair.java
index c2f3489..24007cc 100644
--- a/src/main/java/com/android/tools/r8/utils/Pair.java
+++ b/src/main/java/com/android/tools/r8/utils/Pair.java
@@ -55,4 +55,8 @@
public String toString() {
return "Pair{" + first + ", " + second + '}';
}
+
+ public static <T, S> Pair<T, S> create(T t, S s) {
+ return new Pair<>(t, s);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java b/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java
new file mode 100644
index 0000000..5e1cdbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java
@@ -0,0 +1,171 @@
+// 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.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class ReflectionHelper {
+
+ @SuppressWarnings("unchecked")
+ public static <T> T performReflection(Object object, ReflectiveOperation<?> operation)
+ throws Exception {
+ return (T) operation.compute(object);
+ }
+
+ public static ReflectiveOperationSequenceBuilder builder() {
+ return new ReflectiveOperationSequenceBuilder();
+ }
+
+ public enum DeclaredType {
+ FIELD,
+ METHOD
+ }
+
+ public abstract static class ReflectiveOperation<Member> {
+
+ final Class<?> classForDeclaration;
+ final String declaredMember;
+ final Consumer<Member> modifier;
+ final ReflectiveOperation<?> nextOperation;
+
+ private ReflectiveOperation(
+ Class<?> classForDeclaration,
+ String declaredMember,
+ ReflectiveOperation<?> nextOperation,
+ Consumer<Member> modifier) {
+ this.classForDeclaration = classForDeclaration;
+ this.declaredMember = declaredMember;
+ this.nextOperation = nextOperation;
+ this.modifier = modifier;
+ }
+
+ public abstract Object compute(Object object) throws Exception;
+ }
+
+ public static class ReflectiveMethodOperation extends ReflectiveOperation<Method> {
+
+ private ReflectiveMethodOperation(
+ Class<?> classForDeclaration,
+ String declaredMember,
+ ReflectiveOperation<?> nextOperation,
+ Consumer<Method> modifier) {
+ super(classForDeclaration, declaredMember, nextOperation, modifier);
+ }
+
+ @Override
+ public Object compute(Object object) throws Exception {
+ Class<?> clazz = classForDeclaration == null ? object.getClass() : classForDeclaration;
+ Method declaredMethod = clazz.getDeclaredMethod(declaredMember);
+ modifier.accept(declaredMethod);
+ // The reflection helper do not support arguments at this point.
+ Object returnValue = declaredMethod.invoke(object);
+ return nextOperation != null ? nextOperation.compute(returnValue) : returnValue;
+ }
+ }
+
+ public static class ReflectiveFieldOperation extends ReflectiveOperation<Field> {
+
+ private ReflectiveFieldOperation(
+ Class<?> classForDeclaration,
+ String declaredMember,
+ ReflectiveOperation<?> nextOperation,
+ Consumer<Field> modifier) {
+ super(classForDeclaration, declaredMember, nextOperation, modifier);
+ }
+
+ @Override
+ public Object compute(Object object) throws Exception {
+ Class<?> clazz = classForDeclaration == null ? object.getClass() : classForDeclaration;
+ Field declaredField = clazz.getDeclaredField(declaredMember);
+ modifier.accept(declaredField);
+ Object fieldValue = declaredField.get(object);
+ return nextOperation != null ? nextOperation.compute(fieldValue) : fieldValue;
+ }
+ }
+
+ public static class ReflectiveOperationSequenceBuilder {
+
+ List<ReflectiveOperationBuilder> reflectiveOperationBuilderList = new ArrayList<>();
+
+ public ReflectiveOperationBuilder readMethod(String declaredMember) {
+ return add(declaredMember, DeclaredType.METHOD);
+ }
+
+ public ReflectiveOperationBuilder readField(String declaredMember) {
+ return add(declaredMember, DeclaredType.FIELD);
+ }
+
+ private ReflectiveOperationBuilder add(String declaredMember, DeclaredType declaredType) {
+ ReflectiveOperationBuilder reflectiveOperationBuilder =
+ new ReflectiveOperationBuilder(declaredMember, declaredType, this);
+ reflectiveOperationBuilderList.add(reflectiveOperationBuilder);
+ return reflectiveOperationBuilder;
+ }
+
+ public ReflectiveOperation<?> build() {
+ assert !reflectiveOperationBuilderList.isEmpty();
+ ReflectiveOperation<?> lastOperation = null;
+ for (int i = reflectiveOperationBuilderList.size() - 1; i >= 0; i--) {
+ lastOperation = reflectiveOperationBuilderList.get(i).build(lastOperation);
+ }
+ return lastOperation;
+ }
+ }
+
+ public static class ReflectiveOperationBuilder {
+
+ private final String declaredMember;
+ private final DeclaredType declaredType;
+ private boolean setAccessible = false;
+ private final ReflectiveOperationSequenceBuilder sequenceBuilder;
+
+ private ReflectiveOperationBuilder(
+ String declaredMember,
+ DeclaredType declaredType,
+ ReflectiveOperationSequenceBuilder sequenceBuilder) {
+ this.declaredMember = declaredMember;
+ this.declaredType = declaredType;
+ this.sequenceBuilder = sequenceBuilder;
+ }
+
+ public ReflectiveOperationBuilder setSetAccessible(boolean setAccessible) {
+ this.setAccessible = setAccessible;
+ return this;
+ }
+
+ public ReflectiveOperationSequenceBuilder done() {
+ return sequenceBuilder;
+ }
+
+ private ReflectiveOperation<?> build(ReflectiveOperation<?> nextOperation) {
+ if (declaredType == DeclaredType.FIELD) {
+ return new ReflectiveFieldOperation(
+ null,
+ declaredMember,
+ nextOperation,
+ field -> {
+ if (setAccessible) {
+ field.setAccessible(true);
+ }
+ });
+ } else {
+ assert declaredType == DeclaredType.METHOD;
+ return new ReflectiveMethodOperation(
+ null,
+ declaredMember,
+ nextOperation,
+ method -> {
+ if (setAccessible) {
+ method.setAccessible(true);
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index 4e476e3..65103ac 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -37,6 +37,13 @@
|| diagnosticsClassName.equals(diagnostic.getClass().getTypeName())) {
return to;
}
+ // Some diagnostics are exposed as interfaces, and implemented by internal implementations.
+ for (Class<?> clazz : diagnostic.getClass().getInterfaces()) {
+ if (diagnosticsClassName.equals(clazz.getSimpleName())
+ || diagnosticsClassName.equals(clazz.getTypeName())) {
+ return to;
+ }
+ }
return level;
}
}
diff --git a/src/main/keep.txt b/src/main/keep.txt
index f3e6dd4..e03ef61 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -29,3 +29,20 @@
# TODO(b/176783536): Avoid need to use -dontwarn.
-include dontwarn.txt
+
+# TODO(b/185756596): Remove when no longer needed
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$FileFacade {
+ com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$Class {
+ com.android.tools.r8.jetbrains.kotlin.Lazy classData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$SyntheticClass {
+ com.android.tools.r8.jetbrains.kotlin.Lazy functionData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$MultiFileClassPart {
+ com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlin.SafePublicationLazyImpl {
+ java.lang.Object getValue();
+}
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index e36ff2d..7e8e572 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -4,7 +4,6 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import java.util.Set;
@@ -29,7 +28,7 @@
@Override
public Set<String> getMainDexClasses() {
- throw new Unimplemented();
+ return state.getMainDexClasses();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 73d1d3a..bec8829 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -22,6 +22,8 @@
import java.util.function.Consumer;
// Helper to check that a particular error occurred.
+// TODO(b/186505526): Prefer TestDiagnosticMessages.
+@Deprecated
public class DiagnosticsChecker implements DiagnosticsHandler {
public List<Diagnostic> errors = new ArrayList<>();
@@ -60,26 +62,10 @@
diagnostics.stream().anyMatch(d -> d.getDiagnosticMessage().contains(snippet)));
}
- private static void checkNotContains(String snippet, List<Diagnostic> diagnostics) {
- List<String> messages = ListUtils.map(diagnostics, Diagnostic::getDiagnosticMessage);
- System.out.println("Expecting no match for '" + snippet + "'");
- System.out.println("Diagnostics messages:\n" + messages);
- assertTrue(
- "Expected to *not* find snippet '"
- + snippet
- + "' in error messages:\n"
- + String.join("\n", messages),
- diagnostics.stream().noneMatch(d -> d.getDiagnosticMessage().contains(snippet)));
- }
-
public static void checkContains(Collection<String> snippets, List<Diagnostic> diagnostics) {
snippets.forEach(snippet -> checkContains(snippet, diagnostics));
}
- public static void checkNotContains(Collection<String> snippets, List<Diagnostic> diagnostics) {
- snippets.forEach(snippet -> checkNotContains(snippet, diagnostics));
- }
-
public void checkErrorsContains(String snippet) {
checkContains(snippet, errors);
}
@@ -109,19 +95,6 @@
}
}
- public static void checkErrorDiagnostics(
- Consumer<DiagnosticsChecker> checker, FailingRunner runner)
- throws CompilationFailedException {
- DiagnosticsChecker handler = new DiagnosticsChecker();
- try {
- runner.run(handler);
- fail("Failure expected");
- } catch (CompilationFailedException e) {
- checker.accept(handler);
- throw e;
- }
- }
-
public static void checkDiagnostics(Consumer<DiagnosticsChecker> checker, FailingRunner runner)
throws CompilationFailedException {
DiagnosticsChecker handler = new DiagnosticsChecker();
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 8bb9f30..eb44743 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -47,6 +48,10 @@
parameters.assertNoneRuntime();
}
+ protected final Matcher<Diagnostic> cfL8NotSupportedDiagnostic =
+ diagnosticMessage(
+ containsString("L8 does not support shrinking when generating class files"));
+
@Test(expected = CompilationFailedException.class)
public void emptyBuilder() throws Throwable {
verifyEmptyCommand(L8Command.builder().build());
@@ -192,8 +197,7 @@
"--classfile");
fail("Expected failure");
} catch (CompilationFailedException e) {
- diagnostics.assertErrorsMatch(
- diagnosticMessage(containsString("not support shrinking when generating class files")));
+ diagnostics.assertErrorsMatch(cfL8NotSupportedDiagnostic);
}
}
@@ -300,8 +304,7 @@
addProguardConfigurationString(diagnostics, ClassFileConsumer.emptyConsumer());
fail("Expected failure");
} catch (CompilationFailedException e) {
- diagnostics.assertErrorsMatch(
- diagnosticMessage(containsString("not support shrinking when generating class files")));
+ diagnostics.assertErrorsMatch(cfL8NotSupportedDiagnostic);
}
}
@@ -345,8 +348,7 @@
addProguardConfigurationFile(diagnostics, ClassFileConsumer.emptyConsumer());
fail("Expected failure");
} catch (CompilationFailedException e) {
- diagnostics.assertErrorsMatch(
- diagnosticMessage(containsString("not support shrinking when generating class files")));
+ diagnostics.assertErrorsMatch(cfL8NotSupportedDiagnostic);
}
}
diff --git a/src/test/java/com/android/tools/r8/MarkersTest.java b/src/test/java/com/android/tools/r8/MarkersTest.java
index 2ab8a16..f1baee5 100644
--- a/src/test/java/com/android/tools/r8/MarkersTest.java
+++ b/src/test/java/com/android/tools/r8/MarkersTest.java
@@ -91,14 +91,11 @@
markerTool(Tool.R8),
markerCompilationMode(compilationMode),
markerMinApi(apiLevel),
- markerR8Mode("compatibility"));
+ markerR8Mode("full"));
Matcher<Marker> d8Matcher =
allOf(markerTool(Tool.D8), markerCompilationMode(compilationMode), markerMinApi(apiLevel));
- if (shrinkDesugaredLibrary) {
- assertMarkersMatch(markers, ImmutableList.of(l8Matcher, r8Matcher));
- } else {
- assertMarkersMatch(markers, ImmutableList.of(l8Matcher, d8Matcher));
- }
+ assertMarkersMatch(
+ markers, ImmutableList.of(l8Matcher, shrinkDesugaredLibrary ? r8Matcher : d8Matcher));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 19b414a..d85d8d9 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -309,6 +309,11 @@
return self();
}
+ public T allowUnnecessaryDontWarnWildcards() {
+ return addOptionsModification(
+ options -> options.testing.allowUnnecessaryDontWarnWildcards = true);
+ }
+
public T allowUnusedDontWarnKotlinReflectJvmInternal() {
addOptionsModification(
options ->
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e638abf..0577d2f 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -4,9 +4,11 @@
package com.android.tools.r8;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.TestBuilder.getTestingAnnotations;
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import static com.google.common.collect.Lists.cartesianProduct;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -119,6 +121,7 @@
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
+import org.hamcrest.Matcher;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
@@ -1810,4 +1813,9 @@
extractor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return extractor.getClassFileVersion();
}
+
+ // Default messages
+ protected final Matcher<Diagnostic> cfD8NotSupportedDiagnostic =
+ diagnosticMessage(
+ containsString("Compiling to Java class files with D8 is not officially supported"));
}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 97b1a5c..f7395d1 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -379,6 +379,11 @@
return self();
}
+ public CR assertWarningThatMatches(Matcher<Diagnostic> matcher) {
+ getDiagnosticMessages().assertWarningThatMatches(matcher);
+ return self();
+ }
+
public CR assertAllWarningMessagesMatch(Matcher<String> matcher) {
getDiagnosticMessages().assertHasWarnings().assertAllWarningsMatch(diagnosticMessage(matcher));
return self();
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index b52db8d..713578f 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -57,11 +57,7 @@
@Override
public void warning(Diagnostic warning) {
- // When testing D8 with class file output this warning is always emitted. Discard this, as
- // for tests this is not relevant.
- if (!warning.equals("Compiling to Java class files with D8 is not officially supported")) {
- warnings.add(warning);
- }
+ warnings.add(warning);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index c3949d9..5a9dba5 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -2113,7 +2113,7 @@
Executors.newSingleThreadExecutor(),
appView,
GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
NamingLens.getIdentityLens(),
options,
null);
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index 330535e..9954a41 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -52,7 +52,7 @@
appView,
GraphLens.getIdentityLens(),
NamingLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
new LensCodeRewriterUtils(appView),
Collections.emptyList(),
Collections.emptyList(),
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
deleted file mode 100644
index 1584f41..0000000
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright (c) 2017, 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.dex;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.code.ConstString;
-import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexCode.Try;
-import com.android.tools.r8.graph.DexCode.TryHandler;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.InitClassLens;
-import com.android.tools.r8.graph.LazyLoadedDexApplication;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Sets;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class SharedClassWritingTest {
-
- private final static String PREFIX = "A";
- private final static int NUMBER_OF_FILES = 500;
-
- DexItemFactory dexItemFactory = new DexItemFactory();
-
- private DexString[] strings;
-
- @Before
- public void generateStringArray() {
- strings = new DexString[Constants.MAX_NON_JUMBO_INDEX + 100];
- for (int i = 0; i < strings.length; i++) {
- // Format i as string with common prefix and leading 0's so that they are in the array
- // in lexicographic order.
- String string = PREFIX + StringUtils.zeroPrefix(i, 8);
- strings[i] = dexItemFactory.createString(string);
- }
- }
-
- private DexEncodedMethod makeMethod(DexType holder, int stringCount, int startOffset) {
- assert stringCount + startOffset < strings.length;
- Instruction[] instructions = new Instruction[stringCount + 1];
- for (int i = 0; i < stringCount; i++) {
- instructions[i] = new ConstString(0, strings[startOffset + i]);
- }
- instructions[stringCount] = new ReturnVoid();
- DexCode code = new DexCode(1, 0, 0, instructions, new Try[0], new TryHandler[0], null);
- return new DexEncodedMethod(
- dexItemFactory.createMethod(
- holder, dexItemFactory.createProto(dexItemFactory.voidType), "theMethod"),
- MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false),
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- code);
- }
-
- private DexProgramClass makeClass(
- InternalOptions options,
- String name,
- int stringCount,
- int startOffset,
- Collection<DexProgramClass> synthesizedFrom) {
- String desc = DescriptorUtils.javaTypeToDescriptor(name);
- DexType type = dexItemFactory.createType(desc);
- DexProgramClass programClass =
- new DexProgramClass(
- type,
- null,
- new SynthesizedOrigin("test", getClass()),
- ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC),
- dexItemFactory.objectType,
- DexTypeList.empty(),
- null,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedMethod.EMPTY_ARRAY,
- new DexEncodedMethod[] {makeMethod(type, stringCount, startOffset)},
- false,
- DexProgramClass::invalidChecksumRequest);
- return programClass;
- }
-
- // TODO(b/181636450): Reconsider this test as it no longer reflects the compiler synthetics.
- @Test
- public void manyFilesWithSharedSynthesizedClass() throws ExecutionException, IOException {
- InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
-
- // Create classes that all reference enough strings to overflow the index, but are all
- // at different offsets in the strings array. This ensures we trigger multiple rounds of
- // rewrites.
- List<DexProgramClass> classes = new ArrayList<>();
- for (int i = 0; i < NUMBER_OF_FILES; i++) {
- classes.add(
- makeClass(
- options,
- "Class" + i,
- Constants.MAX_NON_JUMBO_INDEX - 1,
- i % 100,
- Collections.emptyList()));
- }
-
- // Create a shared class that references strings above the maximum.
- DexProgramClass sharedSynthesizedClass =
- makeClass(options, "SharedSynthesized", 100, Constants.MAX_NON_JUMBO_INDEX - 1, classes);
-
- LazyLoadedDexApplication.Builder builder =
- DirectMappedDexApplication.builder(options, Timing.empty());
- builder.addSynthesizedClass(sharedSynthesizedClass);
- classes.forEach(builder::addProgramClass);
- DexApplication application = builder.build();
- AppView<AppInfo> appView = AppView.createForD8(AppInfo.createInitialAppInfo(application));
- classes.forEach(
- c -> appView.getSyntheticItems().addLegacySyntheticClass(sharedSynthesizedClass, c));
-
- CollectInfoConsumer consumer = new CollectInfoConsumer();
- options.programConsumer = consumer;
- ApplicationWriter writer =
- new ApplicationWriter(
- appView,
- null,
- GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
- NamingLens.getIdentityLens(),
- null);
- ExecutorService executorService = ThreadUtils.getExecutorService(options);
- writer.write(executorService);
- List<Set<String>> generatedDescriptors = consumer.getDescriptors();
- // Check all files present.
- Assert.assertEquals(NUMBER_OF_FILES, generatedDescriptors.size());
- // And each file contains two classes of which one is the shared one.
- for (Set<String> classDescriptors : generatedDescriptors) {
- Assert.assertEquals(2, classDescriptors.size());
- Assert
- .assertTrue(classDescriptors.contains(sharedSynthesizedClass.type.toDescriptorString()));
- }
- }
-
- private static class CollectInfoConsumer implements DexFilePerClassFileConsumer {
-
- private final List<Set<String>> descriptors = new ArrayList<>();
-
- private final Deque<ByteBuffer> freeBuffers = new ArrayDeque<>();
- private final Set<ByteBuffer> activeBuffers = Sets.newIdentityHashSet();
-
- @Override
- public ByteBuffer acquireByteBuffer(int capacity) {
- synchronized (freeBuffers) {
- ByteBuffer buffer = freeBuffers.pollFirst();
- // Ensure the buffer has sufficient capacity, eg, skip buffers that are too small.
- if (buffer != null && buffer.capacity() < capacity) {
- List<ByteBuffer> small = new ArrayList<>(freeBuffers.size());
- do {
- small.add(buffer);
- buffer = freeBuffers.pollFirst();
- } while (buffer != null && buffer.capacity() < capacity);
- freeBuffers.addAll(small);
- }
- if (buffer == null) {
- buffer = ByteBuffer.allocate(capacity);
- }
- assert !activeBuffers.contains(buffer);
- activeBuffers.add(buffer);
- return buffer;
- }
- }
-
- @Override
- public void releaseByteBuffer(ByteBuffer buffer) {
- synchronized (freeBuffers) {
- assert activeBuffers.contains(buffer);
- activeBuffers.remove(buffer);
- buffer.position(0);
- freeBuffers.offerFirst(buffer);
- }
- }
-
- @Override
- public void accept(
- String primaryClassDescriptor,
- ByteDataView data,
- Set<String> descriptors,
- DiagnosticsHandler handler) {
- addDescriptors(descriptors);
- }
-
- synchronized void addDescriptors(Set<String> descriptors) {
- this.descriptors.add(descriptors);
- }
-
- public List<Set<String>> getDescriptors() {
- return descriptors;
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {}
- }
-}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/AbsentDiagnosticSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/AbsentDiagnosticSubject.java
deleted file mode 100644
index f94c8f4..0000000
--- a/src/test/java/com/android/tools/r8/diagnosticinspector/AbsentDiagnosticSubject.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.diagnosticinspector;
-
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.errors.Unreachable;
-
-public class AbsentDiagnosticSubject implements DiagnosticSubject {
- @Override
- public FoundMissingDefinitionsDiagnosticSubject assertIsMissingDefinitionsDiagnostic() {
- fail("Expected MissingDefinitionsDiagnostic, but was absent");
- throw new Unreachable();
- }
-}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/DiagnosticSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/DiagnosticSubject.java
index f7123eb..c560843 100644
--- a/src/test/java/com/android/tools/r8/diagnosticinspector/DiagnosticSubject.java
+++ b/src/test/java/com/android/tools/r8/diagnosticinspector/DiagnosticSubject.java
@@ -7,4 +7,6 @@
public interface DiagnosticSubject {
FoundMissingDefinitionsDiagnosticSubject assertIsMissingDefinitionsDiagnostic();
+
+ FoundStringDiagnosticSubject assertIsStringDiagnostic();
}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundDiagnosticSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundDiagnosticSubject.java
index 427bef0..42dff98 100644
--- a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundDiagnosticSubject.java
+++ b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundDiagnosticSubject.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
+import com.android.tools.r8.utils.StringDiagnostic;
public class FoundDiagnosticSubject<D extends Diagnostic> implements DiagnosticSubject {
@@ -27,4 +28,10 @@
assertThat(diagnostic, diagnosticType(MissingDefinitionsDiagnostic.class));
return new FoundMissingDefinitionsDiagnosticSubject((MissingDefinitionsDiagnostic) diagnostic);
}
+
+ @Override
+ public FoundStringDiagnosticSubject assertIsStringDiagnostic() {
+ assertThat(diagnostic, diagnosticType(StringDiagnostic.class));
+ return new FoundStringDiagnosticSubject((StringDiagnostic) diagnostic);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java
index 84e3f4b..28c8fbd 100644
--- a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java
+++ b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java
@@ -13,7 +13,11 @@
import com.android.tools.r8.diagnostic.MissingDefinitionContext;
import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
+import com.android.tools.r8.diagnostic.MissingFieldInfo;
+import com.android.tools.r8.diagnostic.MissingMethodInfo;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import java.util.Arrays;
import java.util.HashMap;
@@ -24,15 +28,26 @@
extends FoundDiagnosticSubject<MissingDefinitionsDiagnostic> {
private final Map<ClassReference, MissingClassInfo> missingClasses = new HashMap<>();
+ private final Map<FieldReference, MissingFieldInfo> missingFields = new HashMap<>();
+ private final Map<MethodReference, MissingMethodInfo> missingMethods = new HashMap<>();
public FoundMissingDefinitionsDiagnosticSubject(MissingDefinitionsDiagnostic diagnostic) {
super(diagnostic);
diagnostic.getMissingDefinitions().stream()
- .filter(MissingDefinitionInfo::isMissingClass)
- .map(MissingDefinitionInfo::asMissingClass)
.forEach(
- missingClassInfo ->
- missingClasses.put(missingClassInfo.getClassReference(), missingClassInfo));
+ missingDefinitionInfo -> {
+ if (missingDefinitionInfo.isMissingClass()) {
+ MissingClassInfo missingClassInfo = missingDefinitionInfo.asMissingClass();
+ missingClasses.put(missingClassInfo.getClassReference(), missingClassInfo);
+ } else if (missingDefinitionInfo.isMissingField()) {
+ MissingFieldInfo missingFieldInfo = missingDefinitionInfo.asMissingField();
+ missingFields.put(missingFieldInfo.getFieldReference(), missingFieldInfo);
+ } else {
+ assert missingDefinitionInfo.isMissingMethod();
+ MissingMethodInfo missingMethodInfo = missingDefinitionInfo.asMissingMethod();
+ missingMethods.put(missingMethodInfo.getMethodReference(), missingMethodInfo);
+ }
+ });
}
public FoundMissingDefinitionsDiagnosticSubject assertHasMessage(String expectedMessage) {
@@ -62,8 +77,65 @@
missingClassInfoSubject -> missingClassInfoSubject.assertExactContexts(expectedContexts));
}
+ public FoundMissingDefinitionsDiagnosticSubject assertIsAllMissingClasses(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ assertIsMissingClass(clazz);
+ }
+ return assertNumberOfMissingClasses(classes.length);
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertIsMissingField(
+ FieldReference fieldReference) {
+ assertTrue(missingFields.containsKey(fieldReference));
+ return this;
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertIsAllMissingFields(
+ FieldReference... fieldReferences) {
+ for (FieldReference fieldReference : fieldReferences) {
+ assertIsMissingField(fieldReference);
+ }
+ return assertNumberOfMissingFields(fieldReferences.length);
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertIsMissingMethod(
+ MethodReference methodReference) {
+ assertTrue(missingMethods.containsKey(methodReference));
+ return this;
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertIsAllMissingMethods(
+ MethodReference... methodReferences) {
+ for (MethodReference methodReference : methodReferences) {
+ assertIsMissingMethod(methodReference);
+ }
+ return assertNumberOfMissingMethods(methodReferences.length);
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertNoMissingClasses() {
+ return assertNumberOfMissingClasses(0);
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertNoMissingFields() {
+ return assertNumberOfMissingFields(0);
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertNoMissingMethods() {
+ return assertNumberOfMissingMethods(0);
+ }
+
public FoundMissingDefinitionsDiagnosticSubject assertNumberOfMissingClasses(int expected) {
- assertEquals(expected, getDiagnostic().getMissingDefinitions().size());
+ assertEquals(expected, missingClasses.size());
+ return this;
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertNumberOfMissingFields(int expected) {
+ assertEquals(expected, missingFields.size());
+ return this;
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertNumberOfMissingMethods(int expected) {
+ assertEquals(expected, missingMethods.size());
return this;
}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundStringDiagnosticSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundStringDiagnosticSubject.java
new file mode 100644
index 0000000..fb90421
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundStringDiagnosticSubject.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.diagnosticinspector;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.StringDiagnostic;
+
+public class FoundStringDiagnosticSubject extends FoundDiagnosticSubject<StringDiagnostic> {
+
+ public FoundStringDiagnosticSubject(StringDiagnostic diagnostic) {
+ super(diagnostic);
+ }
+
+ public FoundStringDiagnosticSubject assertHasMessage(String expectedMessage) {
+ assertEquals(expectedMessage, getDiagnostic().getDiagnosticMessage());
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
new file mode 100644
index 0000000..f3cee7b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
@@ -0,0 +1,82 @@
+// 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.graph.genericsignature;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignaturePartialTypeArgumentApplier;
+import com.android.tools.r8.origin.Origin;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GenericSignaturePartialTypeArgumentApplierTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final DexItemFactory itemFactory = new DexItemFactory();
+ private final DexType objectType = itemFactory.objectType;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public GenericSignaturePartialTypeArgumentApplierTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testVariablesInOuterPosition() {
+ runTest(
+ ImmutableMap.of("T", objectType, "R", objectType),
+ "(TT;)TR;",
+ "(Ljava/lang/Object;)Ljava/lang/Object;")
+ .assertNoMessages();
+ }
+
+ @Test
+ public void testVariablesInInnerPosition() {
+ runTest(
+ ImmutableMap.of("T", objectType, "R", objectType),
+ "(LList<TT;>;)LList<TR;>;",
+ "(LList<*>;)LList<*>;")
+ .assertNoMessages();
+ }
+
+ private TestDiagnosticMessages runTest(
+ Map<String, DexType> substitutions,
+ String initialSignature,
+ String expectedRewrittenSignature) {
+ GenericSignaturePartialTypeArgumentApplier argumentApplier =
+ GenericSignaturePartialTypeArgumentApplier.build(
+ objectType, ClassSignature.noSignature(), substitutions);
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ MethodTypeSignature methodTypeSignature =
+ argumentApplier.visitMethodSignature(
+ GenericSignature.parseMethodSignature(
+ "foo", initialSignature, Origin.unknown(), itemFactory, diagnosticsHandler));
+ diagnosticsHandler.assertNoMessages();
+ String rewrittenSignature =
+ argumentApplier.visitMethodSignature(methodTypeSignature).toString();
+ assertEquals(expectedRewrittenSignature, rewrittenSignature);
+ GenericSignature.parseMethodSignature(
+ "foo", rewrittenSignature, Origin.unknown(), itemFactory, diagnosticsHandler);
+ return diagnosticsHandler;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterTest.java
index 608b8d3..ebca08d 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterTest.java
@@ -55,20 +55,9 @@
public void checkSignatures(CodeInspector inspector) {
checkSignature(
inspector.clazz(Bar.class.getTypeName() + "$1"),
- "L"
- + binaryName(Foo.class)
- + "<"
- + descriptor(Object.class)
- + descriptor(Main.class)
- + ">;");
+ "L" + binaryName(Foo.class) + "<*" + descriptor(Main.class) + ">;");
checkSignature(
- inspector.clazz(Bar.class.getTypeName() + "$2"),
- "L"
- + binaryName(Foo.class)
- + "<"
- + descriptor(Object.class)
- + descriptor(Object.class)
- + ">;");
+ inspector.clazz(Bar.class.getTypeName() + "$2"), "L" + binaryName(Foo.class) + "<**>;");
}
private void checkSignature(ClassSubject classSubject, String expectedSignature) {
diff --git a/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java b/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
index cebfab3..82570e9 100644
--- a/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
+++ b/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
@@ -41,8 +41,9 @@
.addDontWarn("dalvik.system.VMStack")
.addDontWarn("zzz.com.facebook.litho.R$id")
.addDontWarn("com.google.android.libraries.elements.R$id")
- .allowUnusedProguardConfigurationRules()
.allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules()
+ .allowUnnecessaryDontWarnWildcards()
.setMinApi(AndroidApiLevel.N)
.compileWithExpectedDiagnostics(TestDiagnosticMessages::assertOnlyInfos);
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
index edef7bf..36c9ba5 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
@@ -8,7 +8,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
@@ -20,7 +19,6 @@
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
-import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -49,10 +47,6 @@
options.testing.forceIRForCfToCfDesugar = true;
}
- private final Matcher<Diagnostic> cfNotSupportedDiagnostic =
- diagnosticMessage(
- containsString("Compiling to Java class files with D8 is not officially supported"));
-
private void assertInvalidTypeMessage(TestDiagnosticMessages diagnostics) {
assertInvalidInfoMessages(diagnostics, "Attempt to define local of type");
}
@@ -64,7 +58,7 @@
private void assertInvalidInfoMessages(TestDiagnosticMessages diagnostics, String message) {
if (parameters.isCfRuntime()) {
diagnostics.assertNoErrors();
- diagnostics.assertWarningsMatch(cfNotSupportedDiagnostic);
+ diagnostics.assertWarningsMatch(cfD8NotSupportedDiagnostic);
} else {
diagnostics.assertOnlyInfos();
}
@@ -79,7 +73,7 @@
private void assertNoMessages(TestDiagnosticMessages diagnostics) {
if (parameters.isCfRuntime()) {
diagnostics.assertOnlyWarnings();
- diagnostics.assertWarningsMatch(cfNotSupportedDiagnostic);
+ diagnostics.assertWarningsMatch(cfD8NotSupportedDiagnostic);
} else {
diagnostics.assertNoMessages();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index 70ae682..11ad201 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -92,7 +92,12 @@
"org.junit.runners.model.Statement",
"org.junit.runners.model.TestTimedOutException")
.compile()
- .inspect(inspector -> assertEqualMetadata(new CodeInspector(BASE_LIBRARY), inspector))
+ .inspect(
+ inspector ->
+ assertEqualMetadata(
+ new CodeInspector(BASE_LIBRARY),
+ inspector,
+ (addedStrings, addedNonInitStrings) -> {}))
.writeToZip();
Path testJar = compileTestSources(baseJar);
runTestsInJar(testJar, baseJar);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index e9324e9..996dc79 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertNotNull;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import com.android.tools.r8.KotlinTestBase;
@@ -15,9 +17,19 @@
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
import junit.framework.TestCase;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -31,7 +43,6 @@
static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
static final String PKG_PREFIX = DescriptorUtils.getBinaryNameFromJavaType(PKG);
- static final String KT_ANY = "Lkotlin/Any;";
static final String KT_ARRAY = "Lkotlin/Array;";
static final String KT_CHAR_SEQUENCE = "Lkotlin/CharSequence;";
static final String KT_STRING = "Lkotlin/String;";
@@ -44,8 +55,15 @@
static final String KT_COMPARABLE = "Lkotlin/Comparable;";
public void assertEqualMetadata(
- CodeInspector originalInspector, CodeInspector rewrittenInspector) {
- for (FoundClassSubject clazzSubject : originalInspector.allClasses()) {
+ CodeInspector originalInspector,
+ CodeInspector rewrittenInspector,
+ BiConsumer<Integer, Integer> addedStringsInspector) {
+ IntBox addedStrings = new IntBox();
+ IntBox addedNonInitStrings = new IntBox();
+ for (FoundClassSubject clazzSubject :
+ originalInspector.allClasses().stream()
+ .sorted(Comparator.comparing(FoundClassSubject::getFinalName))
+ .collect(Collectors.toList())) {
ClassSubject r8Clazz = rewrittenInspector.clazz(clazzSubject.getOriginalName());
assertThat(r8Clazz, isPresent());
KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
@@ -58,14 +76,47 @@
KotlinClassHeader originalHeader = originalMetadata.getHeader();
KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
- // TODO(b/154199572): Should we check for meta-data version?
- TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
- // We cannot assert equality of the data since it may be ordered differently. Instead we use
- // the KotlinMetadataWriter.
+
+ // We cannot assert equality of the data since it may be ordered differently. However, we
+ // will check for the changes to the string pool and then validate the same parsing
+ // by using the KotlinMetadataWriter.
+ Map<String, List<String>> descriptorToNames = new HashMap<>();
+ clazzSubject.forAllMethods(
+ method ->
+ descriptorToNames
+ .computeIfAbsent(
+ method.getFinalSignature().toDescriptor(), ignoreArgument(ArrayList::new))
+ .add(method.getFinalName()));
+ HashSet<String> originalStrings = new HashSet<>(Arrays.asList(originalHeader.getData2()));
+ HashSet<String> rewrittenStrings = new HashSet<>(Arrays.asList(rewrittenHeader.getData2()));
+ rewrittenStrings.forEach(
+ rewrittenString -> {
+ if (originalStrings.contains(rewrittenString)) {
+ return;
+ }
+ addedStrings.increment();
+ // The init is not needed by if we cannot lookup the descriptor in the table, we have
+ // to emit it and that adds <init>.
+ if (rewrittenString.equals("<init>")) {
+ return;
+ }
+ // We have decided to keep invalid signatures, but they will end up in the string pool
+ // when we emit them. The likely cause of them not being there in the first place seems
+ // to be that they are not correctly written in the type table.
+ if (rewrittenString.equals("L;") || rewrittenString.equals("(L;)V")) {
+ return;
+ }
+ System.out.println(clazzSubject.toString() + ": " + rewrittenString);
+ addedNonInitStrings.increment();
+ });
+ System.out.flush();
+ assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+
String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
- TestCase.assertEquals(expected, actual);
+ assertEquals(expected, actual);
}
+ addedStringsInspector.accept(addedStrings.get(), addedNonInitStrings.get());
}
public static void verifyExpectedWarningsFromKotlinReflectAndStdLib(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index 3752a9e..ffe8b3a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -60,7 +60,6 @@
.assertSuccessWithOutput(EXPECTED_MAIN);
}
-
@Test
public void testMetadataForLib() throws Exception {
Path outputJar =
@@ -77,7 +76,8 @@
inspector ->
assertEqualMetadata(
new CodeInspector(jars.getForConfiguration(kotlinc, targetVersion)),
- inspector))
+ inspector,
+ (addedStrings, addedNonInitStrings) -> {}))
.writeToZip();
testForJvm()
.addRunClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinReflectJar(kotlinc))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index 137d6f5..80578a3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -7,10 +7,17 @@
import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.Collection;
import org.junit.Test;
@@ -23,8 +30,11 @@
@Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withCfRuntimes().build(),
- getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+ getTestParameters().withCfRuntimes().withNoneRuntime().build(),
+ getKotlinTestParameters()
+ .withAllCompilers()
+ .withTargetVersion(KotlinTargetVersion.JAVA_8)
+ .build());
}
private final TestParameters parameters;
@@ -35,8 +45,33 @@
this.parameters = parameters;
}
+ public int getExpectedAddedCount() {
+ if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
+ return 597;
+ } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
+ return 685;
+ } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
+ return 694;
+ } else {
+ throw new Unreachable("Should not compile in this configuration");
+ }
+ }
+
+ public int getExpectedNonInitAddedCount() {
+ if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
+ return 327;
+ } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
+ return 413;
+ } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
+ return 417;
+ } else {
+ throw new Unreachable("Should not compile in this configuration");
+ }
+ }
+
@Test
public void testKotlinStdLib() throws Exception {
+ assumeFalse(parameters.isNoneRuntime());
testForR8(parameters.getBackend())
.addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
.setMinApi(parameters.getApiLevel())
@@ -48,6 +83,33 @@
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspect(
inspector ->
- assertEqualMetadata(new CodeInspector(getKotlinStdlibJar(kotlinc)), inspector));
+ assertEqualMetadata(
+ new CodeInspector(getKotlinStdlibJar(kotlinc)),
+ inspector,
+ (addedStrings, addedNonInitStrings) -> {
+ assertEquals(getExpectedAddedCount(), addedStrings.intValue());
+ assertEquals(getExpectedNonInitAddedCount(), addedNonInitStrings.intValue());
+ }));
+ }
+
+ @Test
+ public void testKotlinStdLibD8() throws Exception {
+ assumeTrue(parameters.isNoneRuntime());
+ testForD8(Backend.DEX)
+ .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+ .setMinApi(AndroidApiLevel.B)
+ // Enable record desugaring support to force a non-identity naming lens
+ .addOptionsModification(
+ options -> options.testing.enableExperimentalRecordDesugaring = true)
+ .compile()
+ .inspect(
+ inspector ->
+ assertEqualMetadata(
+ new CodeInspector(getKotlinStdlibJar(kotlinc)),
+ inspector,
+ (addedStrings, addedNonInitStrings) -> {
+ assertEquals(0, addedStrings.intValue());
+ assertEquals(0, addedNonInitStrings.intValue());
+ }));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 2de9622..e59e318 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -26,13 +26,17 @@
@RunWith(Parameterized.class)
public class MetadataRewritePrunedObjectsTest extends KotlinMetadataTestBase {
- private final String EXPECTED = StringUtils.lines("42");
+ private final String EXPECTED = StringUtils.lines("42", "0", "Goodbye World");
private static final String PKG_LIB = PKG + ".pruned_lib";
private static final String PKG_APP = PKG + ".pruned_app";
private static final KotlinCompileMemoizer libJars =
getCompileMemoizer(
- getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"))
+ .configure(
+ kotlinCompilerTool -> {
+ kotlinCompilerTool.addClasspathFiles(ToolHelper.getClassPathForTests());
+ });
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}, {1}")
@@ -70,8 +74,12 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
- .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
- .addKeepRules("-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); }")
+ .enableInliningAnnotations()
+ .addClasspathFiles(
+ ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinAnnotationJar(kotlinc))
+ .addKeepRules(
+ "-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); *** keptProperty; }")
+ .addKeepClassAndMembersRules(PKG_LIB + ".SubUser")
.addKeepRuntimeVisibleAnnotations()
.noMinification()
.compile()
@@ -98,6 +106,13 @@
KmClassSubject kmClass = sub.getKmClass();
assertThat(kmClass, isPresent());
assertEquals(0, kmClass.getSuperTypes().size());
+ // Ensure that we do not prune the constructors.
+ assertEquals(1, kmClass.getConstructors().size());
+ // Assert that we have removed the metadata for a function that is removed.
assertThat(kmClass.kmFunctionWithUniqueName("notKept"), not(isPresent()));
+ assertThat(kmClass.kmFunctionWithUniqueName("keptWithoutPinning"), not(isPresent()));
+ // Check that we have not pruned the property information for a kept field.
+ assertThat(kmClass.kmPropertyWithUniqueName("keptProperty"), isPresent());
+ assertThat(kmClass.kmPropertyWithUniqueName("notExposedProperty"), not(isPresent()));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_app/main.kt
index fb35a60..1ba0b77 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_app/main.kt
@@ -5,7 +5,10 @@
package com.android.tools.r8.kotlin.metadata.pruned_app
import com.android.tools.r8.kotlin.metadata.pruned_lib.Sub
+import com.android.tools.r8.kotlin.metadata.pruned_lib.SubUser
fun main() {
- println(Sub().kept())
+ val sub = Sub()
+ println(sub.kept())
+ SubUser().use(sub)
}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
index 3ceaa4d..a7672fd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
@@ -4,17 +4,42 @@
package com.android.tools.r8.kotlin.metadata.pruned_lib
-// The Base class will be removed during
+import com.android.tools.r8.NeverInline
+
+// The Base class will be removed during compilation
open class Base
class Sub : Base() {
+ var notExposedProperty : Int = 42
+ var keptProperty : String = "Hello World";
+
fun notKept() : Boolean {
return true
}
+ @NeverInline
+ fun keptWithoutPinning() : Int {
+ if (System.currentTimeMillis() == 0L) {
+ return 41;
+ }
+ return 42;
+ }
+
fun kept() : Int {
- return 42
+ if (System.currentTimeMillis() > 0) {
+ notExposedProperty = 0
+ keptProperty = "Goodbye World"
+ }
+ return keptWithoutPinning()
}
}
+class SubUser {
+
+ @NeverInline
+ fun use(s : Sub) {
+ println(s.notExposedProperty)
+ println(s.keptProperty)
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index f37cc64..36ebfd8 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -878,7 +878,7 @@
AppView.createForD8(AppInfo.createInitialAppInfo(application)),
null,
GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
NamingLens.getIdentityLens(),
null);
ExecutorService executor = ThreadUtils.getExecutorService(options);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexSourceAndClassRetentionTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexSourceAndClassRetentionTest.java
new file mode 100644
index 0000000..74cc91d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexSourceAndClassRetentionTest.java
@@ -0,0 +1,122 @@
+// 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.maindexlist;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableSet;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MainDexSourceAndClassRetentionTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ public MainDexSourceAndClassRetentionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testMainDex() throws Exception {
+ List<ClassReference> mainDexList =
+ testForMainDexListGenerator(temp)
+ .addInnerClasses(getClass())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.B))
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}")
+ .run()
+ .getMainDexList();
+ // TODO(b/186090713): {Foo, BAR} and {Source,Class}RetentionAnnotation should not be included.
+ assertEquals(
+ ImmutableSet.of(
+ Reference.classFromClass(Foo.class),
+ Reference.classFromClass(Bar.class),
+ Reference.classFromClass(Main.class),
+ Reference.classFromClass(ClassRetentionAnnotation.class),
+ Reference.classFromClass(SourceRetentionAnnotation.class)),
+ new HashSet<>(mainDexList));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ Set<String> mainDexClasses =
+ testForD8(temp)
+ .addInnerClasses(getClass())
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .setMinApi(parameters.getApiLevel())
+ .collectMainDexClasses()
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}")
+ .compile()
+ .getMainDexClasses();
+ // TODO(b/186090713): {Foo, BAR} and {Source,Class}RetentionAnnotation should not be included.
+ assertEquals(
+ ImmutableSet.of(
+ typeName(Foo.class),
+ typeName(Bar.class),
+ typeName(Main.class),
+ typeName(ClassRetentionAnnotation.class),
+ typeName(SourceRetentionAnnotation.class)),
+ mainDexClasses);
+ }
+
+ public enum Foo {
+ TEST;
+ }
+
+ public enum Bar {
+ TEST;
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @Target(ElementType.TYPE)
+ public @interface SourceRetentionAnnotation {
+
+ Foo value();
+ }
+
+ @Retention(RetentionPolicy.CLASS)
+ @Target(ElementType.TYPE)
+ public @interface ClassRetentionAnnotation {
+
+ Bar value();
+ }
+
+ @SourceRetentionAnnotation(Foo.TEST)
+ @ClassRetentionAnnotation(Bar.TEST)
+ public static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 0cc94d7..3b0b052 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -301,19 +301,14 @@
}
public StackTrace retraceAllowExperimentalMapping(String map) {
- return retrace(map, null, true);
+ return retrace(map, true);
}
public StackTrace retrace(String map) {
- return retrace(map, null, true);
+ return retrace(map, true);
}
- public StackTrace retrace(String map, String regularExpression) {
- return retrace(map, regularExpression, true);
- }
-
- public StackTrace retrace(
- String map, String regularExpression, boolean allowExperimentalMapping) {
+ public StackTrace retrace(String map, boolean allowExperimentalMapping) {
class Box {
List<String> result;
}
@@ -325,7 +320,6 @@
stackTraceLines.stream()
.map(line -> line.originalLine)
.collect(Collectors.toList()))
- .setRegularExpression(regularExpression)
.setRetracedStackTraceConsumer(retraced -> box.result = retraced)
.build(),
allowExperimentalMapping);
diff --git a/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java b/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java
index 745d166..7d3d07b 100644
--- a/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.fail;
import com.android.tools.r8.ClassFileConsumer;
@@ -82,9 +81,7 @@
private void checkDiagnostics(TestDiagnosticMessages diagnostics, boolean isD8) {
if (classFileConsumer && isD8) {
- diagnostics.assertWarningsMatch(
- diagnosticMessage(
- equalTo("Compiling to Java class files with D8 is not officially supported")));
+ diagnostics.assertWarningsMatch(cfD8NotSupportedDiagnostic);
} else {
diagnostics.assertOnlyErrors();
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index d7a13c8..220bea7 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -4,12 +4,10 @@
package com.android.tools.r8.retrace;
-import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
@@ -61,7 +59,6 @@
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -70,19 +67,16 @@
@RunWith(Parameterized.class)
public class RetraceTests extends TestBase {
- @Parameters(name = "{0}, use regular expression: {1}, external: {2}")
+ @Parameters(name = "{0}, external: {1}")
public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), BooleanUtils.values(), BooleanUtils.values());
+ return buildParameters(getTestParameters().withCfRuntimes().build(), BooleanUtils.values());
}
private final TestParameters testParameters;
- private final boolean useRegExpParsing;
private final boolean external;
- public RetraceTests(TestParameters parameters, boolean useRegExpParsing, boolean external) {
+ public RetraceTests(TestParameters parameters, boolean external) {
this.testParameters = parameters;
- this.useRegExpParsing = useRegExpParsing;
this.external = external;
}
@@ -154,8 +148,7 @@
List<ActualBotStackTraceBase> stackTraces =
ImmutableList.of(new ActualIdentityStackTrace(), new ActualRetraceBotStackTrace());
for (ActualBotStackTraceBase stackTrace : stackTraces) {
- runRetraceTest(stackTrace)
- .assertWarningsCount(useRegExpParsing ? 0 : stackTrace.expectedWarnings());
+ runRetraceTest(stackTrace).assertNoWarnings();
}
}
@@ -193,7 +186,7 @@
public void testCircularReferenceStackTrace() throws Exception {
// Proguard retrace (and therefore the default regular expression) will not retrace circular
// reference exceptions.
- assumeFalse(useRegExpParsing);
+ assumeTrue("b/178599214", false);
runRetraceTest(new CircularReferenceStackTrace());
}
@@ -203,9 +196,8 @@
}
@Test
- @Ignore("b/170293908")
public void testBootLoaderAndNamedModulesStackTrace() throws Exception {
- assumeFalse(useRegExpParsing);
+ assumeTrue("b/170293908", false);
runRetraceTest(new NamedModuleStackTrace());
}
@@ -287,7 +279,6 @@
private TestDiagnosticMessagesImpl runRetraceTest(
StackTraceForTest stackTraceForTest, boolean allowExperimentalMapping) throws Exception {
if (external) {
- assumeTrue(useRegExpParsing);
assumeTrue(testParameters.isCfRuntime());
// The external dependency is built on top of R8Lib. If test.py is run with
// no r8lib, do not try and run the external R8 Retrace since it has not been built.
@@ -327,7 +318,6 @@
RetraceCommand.builder(diagnosticsHandler)
.setProguardMapProducer(ProguardMapProducer.fromString(stackTraceForTest.mapping()))
.setStackTrace(stackTraceForTest.obfuscatedStackTrace())
- .setRegularExpression(useRegExpParsing ? DEFAULT_REGULAR_EXPRESSION : null)
.setRetracedStackTraceConsumer(
retraced ->
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
index bcca0af..8f8e049 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
@@ -4,9 +4,8 @@
package com.android.tools.r8.retrace;
-import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
import static junit.framework.TestCase.assertEquals;
-import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -16,7 +15,6 @@
import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
import com.android.tools.r8.retrace.stacktraces.VerboseUnknownStackTrace;
-import com.android.tools.r8.utils.BooleanUtils;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,16 +24,12 @@
@RunWith(Parameterized.class)
public class RetraceVerboseTests extends TestBase {
- @Parameters(name = "{0}, use regular expression: {1}")
+ @Parameters(name = "{0}")
public static Collection<Object[]> data() {
- return buildParameters(getTestParameters().withNoneRuntime().build(), BooleanUtils.values());
+ return buildParameters(getTestParameters().withNoneRuntime().build());
}
- private final boolean useRegExpParsing;
-
- public RetraceVerboseTests(TestParameters parameters, boolean useRegExpParsing) {
- this.useRegExpParsing = useRegExpParsing;
- }
+ public RetraceVerboseTests(TestParameters parameters) {}
@Test
public void testFoundMethod() {
@@ -54,23 +48,20 @@
@Test
public void testAmbiguousMissingLineVerbose() {
- // TODO(b/169346455): Enable when separated parser.
- assumeFalse(useRegExpParsing);
+ assumeTrue("b/169346455", false);
runRetraceTest(new AmbiguousWithSignatureVerboseStackTrace());
}
- private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
+ private void runRetraceTest(StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
.setProguardMapProducer(ProguardMapProducer.fromString(stackTraceForTest.mapping()))
.setStackTrace(stackTraceForTest.obfuscatedStackTrace())
- .setRegularExpression(useRegExpParsing ? DEFAULT_REGULAR_EXPRESSION : null)
.setVerbose(true)
.setRetracedStackTraceConsumer(
retraced -> assertEquals(stackTraceForTest.retracedStackTrace(), retraced))
.build();
Retrace.run(retraceCommand);
- return diagnosticsHandler;
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java b/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
index ff4296d..9e2b2df 100644
--- a/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.retrace;
-import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
@@ -13,10 +12,9 @@
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.retrace.StackTrace;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.util.List;
import java.util.function.BiConsumer;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,18 +25,15 @@
public class SourceFileTest extends TestBase {
private final TestParameters parameters;
- private final boolean useRegularExpression;
private static final String FILE_NAME = "foobarbaz.java";
- @Parameters(name = "{0}, useRegularExpression: {1}")
- public static List<Object[]> data() {
- return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public SourceFileTest(TestParameters parameters, boolean useRegularExpression) {
+ public SourceFileTest(TestParameters parameters) {
this.parameters = parameters;
- this.useRegularExpression = useRegularExpression;
}
@Test
@@ -106,9 +101,7 @@
: r8FullTestBuilder.run(parameters.getRuntime(), Main.class);
runResult.assertFailureWithErrorThatMatches(containsString("Hello World!"));
StackTrace originalStackTrace = runResult.getOriginalStackTrace();
- StackTrace retracedStackTrace =
- originalStackTrace.retrace(
- runResult.proguardMap(), useRegularExpression ? DEFAULT_REGULAR_EXPRESSION : null);
+ StackTrace retracedStackTrace = originalStackTrace.retrace(runResult.proguardMap());
runResult.inspectFailure(inspector -> consumer.accept(retracedStackTrace, inspector));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/keep/IndirectKeepStaticMethodTest.java b/src/test/java/com/android/tools/r8/shaking/keep/IndirectKeepStaticMethodTest.java
new file mode 100644
index 0000000..3e20dcd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keep/IndirectKeepStaticMethodTest.java
@@ -0,0 +1,59 @@
+// 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 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.Parameters;
+
+@RunWith(Parameterized.class)
+public class IndirectKeepStaticMethodTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public IndirectKeepStaticMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, B.class)
+ .addKeepRules("-keep class " + B.class.getTypeName() + " {", " static void m();", "}")
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Main.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ B.m();
+ }
+ }
+
+ @NoVerticalClassMerging
+ static class A {
+
+ static void m() {
+ System.out.println("A");
+ }
+ }
+
+ static class B extends A {}
+}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index 5138617..02deedd 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -21,7 +21,9 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FieldReferenceUtils;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ImmutableList;
@@ -469,9 +471,11 @@
assertEquals(1, diagnosticsChecker.errors.size());
assertEquals(0, diagnosticsChecker.warnings.size());
assertEquals(0, diagnosticsChecker.infos.size());
- diagnosticsChecker.checkErrorsContains(Reference.classFromClass(Target.class).toString());
- diagnosticsChecker.checkErrorsContains(Reference.fieldFromField(field).toString());
- diagnosticsChecker.checkErrorsContains(Reference.methodFromMethod(method).toString());
+ diagnosticsChecker.checkErrorsContains(Reference.classFromClass(Target.class).getTypeName());
+ diagnosticsChecker.checkErrorsContains(
+ FieldReferenceUtils.toSourceString(Reference.fieldFromField(field)));
+ diagnosticsChecker.checkErrorsContains(
+ MethodReferenceUtils.toSourceString(Reference.methodFromMethod(method)));
}
@Test
@@ -570,8 +574,15 @@
assertThat(
baosErr.toString(Charsets.UTF_8.name()),
containsString(
- "Warning: Tracereferences found 1 classe(s), 1 field(s) and 1 method(s) without"
- + " definition"));
+ StringUtils.lines(
+ "Warning: Missing class " + Target.class.getTypeName(),
+ "Missing field "
+ + FieldReferenceUtils.toSourceString(
+ FieldReferenceUtils.fieldFromField(Target.class, "field")),
+ "Missing method "
+ + MethodReferenceUtils.toSourceString(
+ MethodReferenceUtils.methodFromMethod(
+ Target.class, "method", int.class)))));
assertEquals(0, baosOut.size());
}
@@ -613,8 +624,15 @@
assertThat(
baosOut.toString(Charsets.UTF_8.name()),
containsString(
- "Info: Tracereferences found 1 classe(s), 1 field(s) and 1 method(s) without"
- + " definition"));
+ StringUtils.lines(
+ "Info: Missing class " + Target.class.getTypeName(),
+ "Missing field "
+ + FieldReferenceUtils.toSourceString(
+ FieldReferenceUtils.fieldFromField(Target.class, "field")),
+ "Missing method "
+ + MethodReferenceUtils.toSourceString(
+ MethodReferenceUtils.methodFromMethod(
+ Target.class, "method", int.class)))));
}
private void checkTargetPartlyMissing(DiagnosticsChecker diagnosticsChecker) {
@@ -629,8 +647,10 @@
assertEquals(1, diagnosticsChecker.errors.size());
assertEquals(0, diagnosticsChecker.warnings.size());
assertEquals(0, diagnosticsChecker.infos.size());
- diagnosticsChecker.checkErrorsContains(Reference.fieldFromField(field).toString());
- diagnosticsChecker.checkErrorsContains(Reference.methodFromMethod(method).toString());
+ diagnosticsChecker.checkErrorsContains(
+ FieldReferenceUtils.toSourceString(Reference.fieldFromField(field)));
+ diagnosticsChecker.checkErrorsContains(
+ MethodReferenceUtils.toSourceString(Reference.methodFromMethod(method)));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
index 4f456f6..292271d 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
@@ -3,14 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DiagnosticsChecker;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
@@ -20,10 +18,7 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -66,68 +61,44 @@
ToolHelper.getClassFileForTestClass(Source.class))
.build();
- String prefix = " Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
- List<String> snippets =
- ImmutableList.of(
- "Tracereferences found 3 classe(s), 2 field(s) and 4 method(s) without definition",
- StringUtils.lines(
- "Classe(s) without definition:",
- prefix + "Target1;",
- prefix + "Target2;",
- prefix + "Target3;"),
- StringUtils.lines(
- "Field(s) without definition:",
- prefix + "Target;missingField1:I",
- prefix + "Target;missingField2:I"),
- StringUtils.lines(
- "Method(s) without definition:",
- prefix + "Target1;<init>()V",
- prefix + "Target2;<init>()V",
- prefix + "Target3;<init>()V",
- prefix + "Target;missingMethod()V"));
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
try {
- DiagnosticsChecker.checkErrorDiagnostics(
- checker -> {
- DiagnosticsChecker.checkContains(snippets, checker.errors);
- try {
- assertEquals(1, checker.errors.size());
- assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
- MissingDefinitionsDiagnostic diagnostic =
- (MissingDefinitionsDiagnostic) checker.errors.get(0);
- assertEquals(
- diagnostic.getMissingClasses(),
- ImmutableSet.of(
- Reference.classFromClass(Target1.class),
- Reference.classFromClass(Target2.class),
- Reference.classFromClass(Target3.class)));
- assertEquals(
- diagnostic.getMissingFields(),
- ImmutableSet.of(
- Reference.fieldFromField(Target.class.getField("missingField1")),
- Reference.fieldFromField(Target.class.getField("missingField2"))));
- assertEquals(
- diagnostic.getMissingMethods(),
- ImmutableSet.of(
- Reference.methodFromMethod(Target1.class.getDeclaredConstructor()),
- Reference.methodFromMethod(Target2.class.getDeclaredConstructor()),
- Reference.methodFromMethod(Target3.class.getDeclaredConstructor()),
- Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
- } catch (ReflectiveOperationException e) {
- fail("Unexpected exception");
- }
- },
- handler ->
- TraceReferences.run(
- TraceReferencesCommand.builder(handler)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addSourceFiles(sourceJar)
- .addTargetFiles(targetJar)
- .setConsumer(TraceReferencesConsumer.emptyConsumer())
- .build()));
+ TraceReferences.run(
+ TraceReferencesCommand.builder(testDiagnosticMessages)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(TraceReferencesConsumer.emptyConsumer())
+ .build());
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
+
+ testDiagnosticMessages.inspectErrors(
+ diagnostic ->
+ diagnostic
+ .assertIsMissingDefinitionsDiagnostic()
+ .assertHasMessage(
+ StringUtils.joinLines(
+ "Missing class " + Target1.class.getTypeName(),
+ "Missing method void " + Target1.class.getTypeName() + ".<init>()",
+ "Missing class " + Target2.class.getTypeName(),
+ "Missing method void " + Target2.class.getTypeName() + ".<init>()",
+ "Missing class " + Target3.class.getTypeName(),
+ "Missing method void " + Target3.class.getTypeName() + ".<init>()",
+ "Missing field int " + Target.class.getTypeName() + ".missingField1",
+ "Missing field int " + Target.class.getTypeName() + ".missingField2",
+ "Missing method void " + Target.class.getTypeName() + ".missingMethod()"))
+ .assertIsAllMissingClasses(Target1.class, Target2.class, Target3.class)
+ .assertIsAllMissingFields(
+ Reference.fieldFromField(Target.class.getField("missingField1")),
+ Reference.fieldFromField(Target.class.getField("missingField2")))
+ .assertIsAllMissingMethods(
+ Reference.methodFromMethod(Target1.class.getDeclaredConstructor()),
+ Reference.methodFromMethod(Target2.class.getDeclaredConstructor()),
+ Reference.methodFromMethod(Target3.class.getDeclaredConstructor()),
+ Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
}
@Test
@@ -161,52 +132,35 @@
ToolHelper.getClassFileForTestClass(Source.class))
.build();
- String prefix = " Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
- List<String> snippets =
- ImmutableList.of(
- "Tracereferences found 2 field(s) and 1 method(s) without definition",
- StringUtils.lines(
- "Field(s) without definition:",
- prefix + "Target;missingField1:I",
- prefix + "Target;missingField2:I"),
- StringUtils.lines("Method(s) without definition:", prefix + "Target;missingMethod()V"));
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
try {
- DiagnosticsChecker.checkErrorDiagnostics(
- checker -> {
- DiagnosticsChecker.checkContains(snippets, checker.errors);
- DiagnosticsChecker.checkNotContains(
- ImmutableList.of("Classe(s) without definition:"), checker.errors);
- try {
- assertEquals(1, checker.errors.size());
- assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
- MissingDefinitionsDiagnostic diagnostic =
- (MissingDefinitionsDiagnostic) checker.errors.get(0);
- assertEquals(diagnostic.getMissingClasses(), ImmutableSet.of());
- assertEquals(
- diagnostic.getMissingFields(),
- ImmutableSet.of(
- Reference.fieldFromField(Target.class.getField("missingField1")),
- Reference.fieldFromField(Target.class.getField("missingField2"))));
- assertEquals(
- diagnostic.getMissingMethods(),
- ImmutableSet.of(
- Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
- } catch (ReflectiveOperationException e) {
- fail("Unexpected exception");
- }
- },
- handler ->
- TraceReferences.run(
- TraceReferencesCommand.builder(handler)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addSourceFiles(sourceJar)
- .addTargetFiles(targetJar)
- .setConsumer(TraceReferencesConsumer.emptyConsumer())
- .build()));
+ TraceReferences.run(
+ TraceReferencesCommand.builder(testDiagnosticMessages)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(TraceReferencesConsumer.emptyConsumer())
+ .build());
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
+
+ testDiagnosticMessages.inspectErrors(
+ diagnostic ->
+ diagnostic
+ .assertIsMissingDefinitionsDiagnostic()
+ .assertHasMessage(
+ StringUtils.joinLines(
+ "Missing field int " + Target.class.getTypeName() + ".missingField1",
+ "Missing field int " + Target.class.getTypeName() + ".missingField2",
+ "Missing method void " + Target.class.getTypeName() + ".missingMethod()"))
+ .assertNoMissingClasses()
+ .assertIsAllMissingFields(
+ Reference.fieldFromField(Target.class.getField("missingField1")),
+ Reference.fieldFromField(Target.class.getField("missingField2")))
+ .assertIsAllMissingMethods(
+ Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
}
@Test
@@ -234,46 +188,35 @@
ToolHelper.getClassFileForTestClass(Source.class))
.build();
- String prefix = " Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
- List<String> snippets =
- ImmutableList.of(
- "Tracereferences found 1 method(s) without definition",
- StringUtils.lines("Method(s) without definition:", prefix + "Target;missingMethod()V"));
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
try {
- DiagnosticsChecker.checkErrorDiagnostics(
- checker -> {
- DiagnosticsChecker.checkContains(snippets, checker.errors);
- try {
- assertEquals(1, checker.errors.size());
- assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
- MissingDefinitionsDiagnostic diagnostic =
- (MissingDefinitionsDiagnostic) checker.errors.get(0);
- assertEquals(diagnostic.getMissingClasses(), ImmutableSet.of());
- assertEquals(diagnostic.getMissingFields(), ImmutableSet.of());
- assertEquals(
- diagnostic.getMissingMethods(),
- ImmutableSet.of(
- Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
- } catch (ReflectiveOperationException e) {
- fail("Unexpected exception");
- }
- },
- handler ->
- TraceReferences.run(
- TraceReferencesCommand.builder(handler)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addSourceFiles(sourceJar)
- .addTargetFiles(targetJar)
- .setConsumer(TraceReferencesConsumer.emptyConsumer())
- .build()));
+ TraceReferences.run(
+ TraceReferencesCommand.builder(testDiagnosticMessages)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(TraceReferencesConsumer.emptyConsumer())
+ .build());
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
+
+ testDiagnosticMessages.inspectErrors(
+ diagnostic ->
+ diagnostic
+ .assertIsMissingDefinitionsDiagnostic()
+ .assertHasMessage(
+ "Missing method void " + Target.class.getTypeName() + ".missingMethod()")
+ .assertNoMissingClasses()
+ .assertNoMissingFields()
+ .assertIsAllMissingMethods(
+ Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
}
static class FailingConsumer implements TraceReferencesConsumer {
private final String where;
+ private boolean reported;
FailingConsumer(String where) {
this.where = where;
@@ -281,29 +224,33 @@
@Override
public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
- if (where.equals("acceptType")) {
+ if (!reported && where.equals("acceptType")) {
handler.error(new StringDiagnostic("Error in " + where));
+ reported = true;
}
}
@Override
public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
- if (where.equals("acceptField")) {
+ if (!reported && where.equals("acceptField")) {
handler.error(new StringDiagnostic("Error in " + where));
+ reported = true;
}
}
@Override
public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
- if (where.equals("acceptMethod")) {
+ if (!reported && where.equals("acceptMethod")) {
handler.error(new StringDiagnostic("Error in " + where));
+ reported = true;
}
}
@Override
public void finished(DiagnosticsHandler handler) {
- if (where.equals("finished")) {
+ if (!reported && where.equals("finished")) {
handler.error(new StringDiagnostic("Error in " + where));
+ reported = true;
}
}
}
@@ -328,21 +275,23 @@
.build();
for (String where : new String[] {"acceptType", "acceptField", "acceptMethod", "finished"}) {
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
try {
- DiagnosticsChecker.checkErrorsContains(
- "Error in " + where,
- handler ->
- TraceReferences.run(
- TraceReferencesCommand.builder(handler)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addSourceFiles(sourceJar)
- .addTargetFiles(targetJar)
- .setConsumer(new FailingConsumer(where))
- .build()));
+ TraceReferences.run(
+ TraceReferencesCommand.builder(testDiagnosticMessages)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(new FailingConsumer(where))
+ .build());
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
+
+ testDiagnosticMessages.inspectErrors(
+ diagnostic ->
+ diagnostic.assertIsStringDiagnostic().assertHasMessage("Error in " + where));
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 26e85e5..eb269c1 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -118,7 +118,7 @@
AppView.createForD8(AppInfo.createInitialAppInfo(dexApp)),
null,
GraphLens.getIdentityLens(),
- InitClassLens.getDefault(),
+ InitClassLens.getThrowingInstance(),
NamingLens.getIdentityLens(),
null);
writer.write(executor);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
index a58d5ee..d87e74e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
@@ -21,6 +21,11 @@
}
@Override
+ public List<KmConstructorSubject> getConstructors() {
+ return null;
+ }
+
+ @Override
public boolean isPresent() {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
index 51d7152..cc9bfd0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
@@ -37,6 +37,13 @@
}
@Override
+ public List<KmConstructorSubject> getConstructors() {
+ return kmClass.getConstructors().stream()
+ .map(constructor -> new FoundKmConstructorSubject(codeInspector, constructor))
+ .collect(Collectors.toList());
+ }
+
+ @Override
public CodeInspector codeInspector() {
return codeInspector;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmConstructorSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmConstructorSubject.java
new file mode 100644
index 0000000..19a783f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmConstructorSubject.java
@@ -0,0 +1,53 @@
+// 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.utils.codeinspector;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import kotlinx.metadata.KmConstructor;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+
+public class FoundKmConstructorSubject extends KmConstructorSubject {
+
+ private final CodeInspector codeInspector;
+ private final KmConstructor kmConstructor;
+
+ FoundKmConstructorSubject(CodeInspector codeInspector, KmConstructor kmConstructor) {
+ this.codeInspector = codeInspector;
+ this.kmConstructor = kmConstructor;
+ }
+
+ @Override
+ public JvmMethodSignature signature() {
+ JvmExtensionsKt.getSignature(this.kmConstructor);
+ return null;
+ }
+
+ @Override
+ public List<KmValueParameterSubject> valueParameters() {
+ return kmConstructor.getValueParameters().stream()
+ .map(kmValueParameter -> new KmValueParameterSubject(codeInspector, kmValueParameter))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ // TODO(b/151194869): need to know the corresponding DexEncodedMethod.
+ return false;
+ }
+
+ @Override
+ public boolean isSynthetic() {
+ // TODO(b/151194785): This should return `true` conditionally if we start synthesizing @Metadata
+ // from scratch.
+ return false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
index 753f02a..9d776d3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
@@ -12,6 +12,8 @@
public abstract DexClass getDexClass();
+ public abstract List<KmConstructorSubject> getConstructors();
+
public abstract List<String> getSuperTypeDescriptors();
public abstract List<ClassSubject> getSuperTypes();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmConstructorSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmConstructorSubject.java
new file mode 100644
index 0000000..7a2e468
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmConstructorSubject.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.codeinspector;
+
+import java.util.List;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+
+public abstract class KmConstructorSubject extends Subject {
+
+ public abstract JvmMethodSignature signature();
+
+ public abstract List<KmValueParameterSubject> valueParameters();
+}
diff --git a/tools/test.py b/tools/test.py
index 6f3fe83..685cbf2 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -170,6 +170,14 @@
result.add_option('--print-times', '--print_times',
help='Print the execution time of the slowest tests..',
default=False, action='store_true')
+ result.add_option(
+ '--testing-report',
+ help='Use the custom testing report output format',
+ default=False, action='store_true')
+ result.add_option(
+ '--stacktrace',
+ help='Pass --stacktrace to the gradle run',
+ default=False, action='store_true')
return result.parse_args()
def archive_failures():
@@ -214,7 +222,11 @@
shutil.copyfile(library_jar, desugar_jdk_libs)
print('Desugared library for test in ' + desugar_jdk_libs)
- gradle_args = ['--stacktrace']
+ gradle_args = []
+
+ if options.stacktrace or utils.is_bot():
+ gradle_args.append('--stacktrace')
+
if utils.is_bot():
# Bots don't like dangling processes.
gradle_args.append('--no-daemon')
@@ -301,7 +313,8 @@
gradle_args.append('-Pdesugar_jdk_json_dir=' + desugar_jdk_json_dir)
if desugar_jdk_libs:
gradle_args.append('-Pdesugar_jdk_libs=' + desugar_jdk_libs)
-
+ if options.testing_report:
+ gradle_args.append('-Ptesting-report')
# Build an R8 with dependencies for bootstrapping tests before adding test sources.
gradle_args.append('r8WithDeps')