Report error on generating calls to binary incompatible classes

This commit is contained in:
Alexander Udalov
2016-01-13 22:11:23 +03:00
parent 4bd1d064bb
commit e17cd12c3c
15 changed files with 315 additions and 49 deletions
@@ -16,12 +16,12 @@
package org.jetbrains.kotlin.cli;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Charsets;
import kotlin.Pair;
import kotlin.io.FilesKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.cli.common.CLICompiler;
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.cli.common.KotlinVersion;
import org.jetbrains.kotlin.cli.js.K2JSCompiler;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.Tmpdir;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
@@ -58,7 +59,7 @@ public class CliBaseTest {
try {
System.setErr(new PrintStream(bytes));
ExitCode exitCode = CLICompiler.doMainNoExit(compiler, ArrayUtil.toStringArray(args));
return Pair.create(bytes.toString("utf-8"), exitCode);
return new Pair<String, ExitCode>(bytes.toString("utf-8"), exitCode);
}
catch (Exception e) {
throw ExceptionUtilsKt.rethrow(e);
@@ -70,9 +71,19 @@ public class CliBaseTest {
@NotNull
public static String getNormalizedCompilerOutput(@NotNull String pureOutput, @NotNull ExitCode exitCode, @NotNull String testDataDir) {
return getNormalizedCompilerOutput(pureOutput, exitCode, testDataDir, JvmMetadataVersion.INSTANCE);
}
@NotNull
public static String getNormalizedCompilerOutput(
@NotNull String pureOutput,
@NotNull ExitCode exitCode,
@NotNull String testDataDir,
@NotNull BinaryVersion version
) {
String normalizedOutputWithoutExitCode = pureOutput
.replace(new File(testDataDir).getAbsolutePath(), "$TESTDATA_DIR$")
.replace("expected version is " + JvmMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$")
.replace("expected version is " + version, "expected version is $ABI_VERSION$")
.replace("\\", "/")
.replace(KotlinVersion.VERSION, "$VERSION$");
@@ -95,7 +106,7 @@ public class CliBaseTest {
Pair<String, ExitCode> outputAndExitCode =
executeCompilerGrabOutput(compiler, readArgs(testDataDir + "/" + testName.getMethodName() + ".args", testDataDir,
tmpdir.getTmpDir().getPath()));
String actual = getNormalizedCompilerOutput(outputAndExitCode.first, outputAndExitCode.second, testDataDir);
String actual = getNormalizedCompilerOutput(outputAndExitCode.getFirst(), outputAndExitCode.getSecond(), testDataDir);
KotlinTestUtils.assertEqualsToFile(new File(testDataDir + "/" + testName.getMethodName() + ".out"), actual);
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.org.objectweb.asm.*
import java.io.File
class WrongBytecodeVersionTest : UsefulTestCase() {
private val incompatibleVersion = JvmBytecodeBinaryVersion(42, 0, 0).toArray()
private fun doTest(relativeDirectory: String) {
val directory = KotlinTestUtils.getTestDataPathBase() + relativeDirectory
val librarySource = File(directory, "A.kt")
val usageSource = File(directory, "B.kt")
val tmpdir = KotlinTestUtils.tmpDir(javaClass.simpleName)
LoadDescriptorUtil.compileKotlinToDirAndGetAnalysisResult(
listOf(librarySource), tmpdir, testRootDisposable, ConfigurationKind.ALL, false
)
for (classFile in File(tmpdir, "library").listFiles { file -> file.extension == JavaClassFileType.INSTANCE.defaultExtension }) {
changeVersionInBytecode(classFile)
}
val (output, exitCode) = CliBaseTest.executeCompilerGrabOutput(K2JVMCompiler(), listOf(
usageSource.path,
"-classpath", tmpdir.path,
"-d", tmpdir.path
))
assertEquals("Compilation error expected", ExitCode.COMPILATION_ERROR, exitCode)
val normalized = CliBaseTest.getNormalizedCompilerOutput(output, exitCode, tmpdir.path, JvmBytecodeBinaryVersion.INSTANCE)
KotlinTestUtils.assertEqualsToFile(File(directory, "output.txt"), normalized)
}
private fun changeVersionInBytecode(file: File) {
val writer = ClassWriter(0)
ClassReader(file.inputStream()).accept(object : ClassVisitor(Opcodes.ASM5, writer) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
val superVisitor = super.visitAnnotation(desc, visible)!!
if (desc == AsmUtil.asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.METADATA)) {
return object : AnnotationVisitor(Opcodes.ASM5, superVisitor) {
override fun visit(name: String?, value: Any) {
val updatedValue: Any =
if (name == JvmAnnotationNames.BYTECODE_VERSION_FIELD_NAME) incompatibleVersion
else value
super.visit(name, updatedValue)
}
}
}
return superVisitor
}
}, 0)
file.writeBytes(writer.toByteArray())
}
fun testSimple() {
doTest("/bytecodeVersion/simple")
}
}
@@ -18,9 +18,9 @@ package org.jetbrains.kotlin.jvm.compiler;
import com.google.common.collect.Iterables;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.cli.CliBaseTest;
@@ -75,6 +75,11 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
);
}
@NotNull
private String normalizeOutput(@NotNull Pair<String, ExitCode> output) {
return CliBaseTest.getNormalizedCompilerOutput(output.getFirst(), output.getSecond(), getTestDataDirectory().getPath());
}
private void doTestWithTxt(@NotNull File... extraClassPath) throws Exception {
PackageViewDescriptor packageView = analyzeFileToPackageView(extraClassPath);
@@ -154,25 +159,23 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
File libSrc = new File(getTestDataDirectory(), "library/test/lib.kt");
Pair<String, ExitCode> pair1 = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
Pair<String, ExitCode> outputLib = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
libSrc.getPath(),
"-classpath", tmpdir.getPath(),
"-d", tmpdir.getPath()
));
String outputLib = CliBaseTest.getNormalizedCompilerOutput(pair1.first, pair1.second, getTestDataDirectory().getPath());
File mainSrc = new File(getTestDataDirectory(), "main.kt");
Pair<String, ExitCode> pair2 = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
Pair<String, ExitCode> outputMain = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
mainSrc.getPath(),
"-classpath", tmpdir.getPath(),
"-d", tmpdir.getPath()
));
String outputMain = CliBaseTest.getNormalizedCompilerOutput(pair2.first, pair2.second, getTestDataDirectory().getPath());
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), outputLib + "\n" + outputMain);
KotlinTestUtils.assertEqualsToFile(
new File(getTestDataDirectory(), "output.txt"), normalizeOutput(outputLib) + "\n" + normalizeOutput(outputMain)
);
}
public void testDuplicateObjectInBinaryAndSources() throws Exception {
@@ -237,14 +240,13 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
File source = new File(getTestDataDirectory(), "source.kt");
Pair<String, ExitCode> pair = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
Pair<String, ExitCode> output = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
source.getPath(),
"-classpath", tmpdir.getPath(),
"-d", tmpdir.getPath()
));
String output = CliBaseTest.getNormalizedCompilerOutput(pair.first, pair.second, getTestDataDirectory().getPath());
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), output);
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
}
public void testIncompleteHierarchyInKotlin() throws Exception {
@@ -254,14 +256,13 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
File source = new File(getTestDataDirectory(), "source.kt");
Pair<String, ExitCode> pair = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
Pair<String, ExitCode> output = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
source.getPath(),
"-classpath", library.getPath(),
"-d", tmpdir.getPath()
));
String output = CliBaseTest.getNormalizedCompilerOutput(pair.first, pair.second, getTestDataDirectory().getPath());
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), output);
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
}
/*test source mapping generation when source info is absent*/