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