JVM: Add D8 check that class files can be dexed with D8.
This change adds D8 in addition to Dx which is being deprecated. At some point after the official Dx deprecation, we should remove the Dx checker. Moving to D8 has the additional benefit that D8 can dex code using java 8 features without using a separate desugaring tool.
This commit is contained in:
committed by
Alexander Udalov
parent
222ceb7698
commit
73aa36ca59
@@ -173,6 +173,7 @@ extra["versions.jflex"] = "1.7.0"
|
||||
extra["versions.markdown"] = "0.1.25"
|
||||
extra["versions.trove4j"] = "1.0.20181211"
|
||||
extra["versions.completion-ranking-kotlin"] = "0.0.2"
|
||||
extra["versions.r8"] = "1.5.70"
|
||||
|
||||
// NOTE: please, also change KTOR_NAME in pathUtil.kt and all versions in corresponding jar names in daemon tests.
|
||||
extra["versions.ktor-network"] = "1.0.1"
|
||||
@@ -339,6 +340,7 @@ allprojects {
|
||||
maven("https://dl.bintray.com/kotlin/ktor")
|
||||
maven("https://kotlin.bintray.com/kotlin-dependencies")
|
||||
maven("https://jetbrains.bintray.com/intellij-third-party-dependencies")
|
||||
maven("https://dl.google.com/dl/android/maven2")
|
||||
bootstrapKotlinRepo?.let(::maven)
|
||||
internalKotlinRepo?.let(::maven)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// Exclamation marks are not valid in names in the dex file format.
|
||||
// Therefore, do not attemp to dex this file as it will fail.
|
||||
// See: https://source.android.com/devices/tech/dalvik/dex-format#simplename
|
||||
// IGNORE_DEXING
|
||||
|
||||
class `A!u00A0`() {
|
||||
val ok = "OK"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// Names with spaces are not valid according to the dex specification
|
||||
// before DEX version 040. Therefore, do not attempt to dex the resulting
|
||||
// class file. When D8 is updated to the most recent version, we can increase
|
||||
// the min android api level during dexing to allow these spaces.
|
||||
//
|
||||
// See: https://source.android.com/devices/tech/dalvik/dex-format#simplename
|
||||
// IGNORE_DEXING
|
||||
|
||||
fun `method with spaces`(): String {
|
||||
data class C(val s: String = "OK")
|
||||
return C().s
|
||||
|
||||
@@ -41,6 +41,7 @@ dependencies {
|
||||
testCompile(project(":kotlin-scripting-compiler-impl"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
testCompile(androidDxJar()) { isTransitive = false }
|
||||
testCompile(commonDep("com.android.tools:r8"))
|
||||
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
testCompile(intellijDep()) {
|
||||
includeJars(
|
||||
|
||||
@@ -534,8 +534,15 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
||||
);
|
||||
classFileFactory = generationState.getFactory();
|
||||
|
||||
if (verifyWithDex() && DxChecker.RUN_DX_CHECKER) {
|
||||
// Some names are not allowed in the dex file format and the VM will reject the program
|
||||
// if they are used. Therefore, a few tests cannot be dexed as they use such names that
|
||||
// are valid on the JVM but not on the Android Runtime.
|
||||
boolean ignoreDexing = myFiles.getPsiFiles().stream().anyMatch(
|
||||
it -> InTextDirectivesUtils.isDirectiveDefined(it.getText(), "IGNORE_DEXING")
|
||||
);
|
||||
if (verifyWithDex() && DxChecker.RUN_DX_CHECKER && !ignoreDexing) {
|
||||
DxChecker.check(classFileFactory);
|
||||
D8Checker.check(classFileFactory);
|
||||
}
|
||||
}
|
||||
catch (TestsCompiletimeError e) {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.android.tools.r8.*;
|
||||
import com.android.tools.r8.origin.PathOrigin;
|
||||
import com.android.tools.r8.utils.AndroidApiLevel;
|
||||
import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory;
|
||||
import kotlin.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class D8Checker {
|
||||
|
||||
private D8Checker() {
|
||||
}
|
||||
|
||||
public static void check(ClassFileFactory outputFiles) {
|
||||
runD8(builder -> {
|
||||
for (OutputFile file : ClassFileUtilsKt.getClassFiles(outputFiles)) {
|
||||
byte[] bytes = file.asByteArray();
|
||||
builder.addClassProgramData(bytes, new PathOrigin(Paths.get(file.getRelativePath())));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void checkFilesWithD8(Collection<Pair<byte[], String>> classFiles) {
|
||||
runD8(builder -> {
|
||||
classFiles.forEach(pair -> {
|
||||
builder.addClassProgramData(pair.getFirst(), new PathOrigin(Paths.get(pair.getSecond())));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void runD8(Consumer<D8Command.Builder> addInput) {
|
||||
ProgramConsumer ignoreOutputConsumer = new DexIndexedConsumer.ForwardingConsumer(null);
|
||||
D8Command.Builder builder = D8Command.builder()
|
||||
.setMinApiLevel(28)
|
||||
.setMode(CompilationMode.DEBUG)
|
||||
.setProgramConsumer(ignoreOutputConsumer);
|
||||
addInput.accept(builder);
|
||||
try {
|
||||
D8.run(builder.build(), Executors.newSingleThreadExecutor());
|
||||
}
|
||||
catch (CompilationFailedException e) {
|
||||
Assert.fail(generateExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateExceptionMessage(Throwable e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try (PrintWriter printWriter = new PrintWriter(writer)) {
|
||||
e.printStackTrace(printWriter);
|
||||
String stackTrace = writer.toString();
|
||||
return stackTrace;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,12 +34,16 @@ class TestStdlibWithDxTest {
|
||||
}
|
||||
|
||||
private fun doTest(file: File) {
|
||||
val files = mutableListOf<Pair<ByteArray, String>>();
|
||||
ZipInputStream(FileInputStream(file)).use { zip ->
|
||||
for (entry in generateSequence { zip.nextEntry }) {
|
||||
if (entry.name.endsWith(".class") && !entry.name.startsWith("META-INF/")) {
|
||||
DxChecker.checkFileWithDx(zip.readBytes(), entry.name)
|
||||
val bytes = zip.readBytes()
|
||||
DxChecker.checkFileWithDx(bytes, entry.name)
|
||||
files.add(Pair(bytes, entry.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
D8Checker.checkFilesWithD8(files)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user