Report incompatible metadata version error correctly
Similarly to pre-release classes, load metadata for the class anyway and allow the resolution to select it as the result and prohibit its usage in the end with the special diagnostic reported in MissingDependencyClassChecker
This commit is contained in:
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier;
|
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier;
|
||||||
import org.jetbrains.kotlin.resolve.checkers.PlatformImplDeclarationChecker;
|
import org.jetbrains.kotlin.resolve.checkers.PlatformImplDeclarationChecker;
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData;
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -83,6 +84,7 @@ public interface Errors {
|
|||||||
|
|
||||||
DiagnosticFactory1<PsiElement, FqName> MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<PsiElement, FqName> MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory1<PsiElement, FqName> PRE_RELEASE_CLASS = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<PsiElement, FqName> PRE_RELEASE_CLASS = DiagnosticFactory1.create(ERROR);
|
||||||
|
DiagnosticFactory2<PsiElement, FqName, IncompatibleVersionErrorData<?>> INCOMPATIBLE_CLASS = DiagnosticFactory2.create(ERROR);
|
||||||
|
|
||||||
//Elements with "INVISIBLE_REFERENCE" error are marked as unresolved, unlike elements with "INVISIBLE_MEMBER" error
|
//Elements with "INVISIBLE_REFERENCE" error are marked as unresolved, unlike elements with "INVISIBLE_MEMBER" error
|
||||||
//"INVISIBLE_REFERENCE" is used for invisible classes references and references in import
|
//"INVISIBLE_REFERENCE" is used for invisible classes references and references in import
|
||||||
|
|||||||
+13
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.diagnostics.rendering;
|
package org.jetbrains.kotlin.diagnostics.rendering;
|
||||||
|
|
||||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -31,6 +32,7 @@ import org.jetbrains.kotlin.psi.KtExpression;
|
|||||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
|
||||||
import org.jetbrains.kotlin.psi.KtTypeConstraint;
|
import org.jetbrains.kotlin.psi.KtTypeConstraint;
|
||||||
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
|
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData;
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
import org.jetbrains.kotlin.util.MappedExtensionProvider;
|
import org.jetbrains.kotlin.util.MappedExtensionProvider;
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||||
@@ -321,6 +323,17 @@ public class DefaultErrorMessages {
|
|||||||
|
|
||||||
MAP.put(MISSING_DEPENDENCY_CLASS, "Cannot access class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
|
MAP.put(MISSING_DEPENDENCY_CLASS, "Cannot access class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
|
||||||
MAP.put(PRE_RELEASE_CLASS, "Class ''{0}'' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler", TO_STRING);
|
MAP.put(PRE_RELEASE_CLASS, "Class ''{0}'' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler", TO_STRING);
|
||||||
|
MAP.put(INCOMPATIBLE_CLASS,
|
||||||
|
"Class ''{0}'' was compiled with an incompatible version of Kotlin. {1}",
|
||||||
|
TO_STRING, new DiagnosticParameterRenderer<IncompatibleVersionErrorData<?>>() {
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String render(@NotNull IncompatibleVersionErrorData<?> incompatibility, @NotNull RenderingContext renderingContext) {
|
||||||
|
return "The binary version of its metadata is " + incompatibility.getActualVersion() +
|
||||||
|
", expected version is " + incompatibility.getExpectedVersion() + ".\n" +
|
||||||
|
"The class is loaded from " + FileUtil.toSystemIndependentName(incompatibility.getFilePath());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
MAP.put(LOCAL_OBJECT_NOT_ALLOWED, "Named object ''{0}'' is a singleton and cannot be local. Try to use anonymous object instead", NAME);
|
MAP.put(LOCAL_OBJECT_NOT_ALLOWED, "Named object ''{0}'' is a singleton and cannot be local. Try to use anonymous object instead", NAME);
|
||||||
MAP.put(LOCAL_INTERFACE_NOT_ALLOWED, "''{0}'' is an interface so it cannot be local. Try to use anonymous object or abstract class instead", NAME);
|
MAP.put(LOCAL_INTERFACE_NOT_ALLOWED, "''{0}'' is an interface so it cannot be local. Try to use anonymous object or abstract class instead", NAME);
|
||||||
|
|||||||
+10
-5
@@ -23,8 +23,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors.MISSING_DEPENDENCY_CLASS
|
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors.PRE_RELEASE_CLASS
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||||
@@ -57,9 +56,15 @@ object MissingDependencyClassChecker : CallChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun incompatibilityDiagnosticFor(source: SourceElement?, reportOn: PsiElement): Diagnostic? {
|
private fun incompatibilityDiagnosticFor(source: SourceElement?, reportOn: PsiElement): Diagnostic? {
|
||||||
if (source is DeserializedContainerSource && source.isPreReleaseInvisible) {
|
if (source is DeserializedContainerSource) {
|
||||||
// TODO: if at least one PRE_RELEASE_CLASS is reported, display a hint to disable the diagnostic
|
val incompatibility = source.incompatibility
|
||||||
return PRE_RELEASE_CLASS.on(reportOn, source.presentableFqName)
|
if (incompatibility != null) {
|
||||||
|
return INCOMPATIBLE_CLASS.on(reportOn, source.presentableFqName, incompatibility)
|
||||||
|
}
|
||||||
|
if (source.isPreReleaseInvisible) {
|
||||||
|
// TODO: if at least one PRE_RELEASE_CLASS is reported, display a hint to disable the diagnostic
|
||||||
|
return PRE_RELEASE_CLASS.on(reportOn, source.presentableFqName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|||||||
+4
-3
@@ -1,11 +1,12 @@
|
|||||||
|
compiler/testData/cli/jvm/wrongAbiVersion.kt:3:12: error: class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 0.30.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TESTDATA_DIR$/wrongAbiVersionLib/bin/ClassWithWrongAbiVersion.class
|
||||||
|
fun foo(x: ClassWithWrongAbiVersion) {
|
||||||
|
^
|
||||||
compiler/testData/cli/jvm/wrongAbiVersion.kt:4:5: error: unresolved reference: bar
|
compiler/testData/cli/jvm/wrongAbiVersion.kt:4:5: error: unresolved reference: bar
|
||||||
(note: this may be caused by the fact that some classes compiled with an incompatible version of Kotlin were found in the classpath. Such classes cannot be loaded properly by this version of Kotlin compiler. See below for more information)
|
|
||||||
bar()
|
bar()
|
||||||
^
|
^
|
||||||
compiler/testData/cli/jvm/wrongAbiVersion.kt:6:7: error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
|
compiler/testData/cli/jvm/wrongAbiVersion.kt:6:7: error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
|
||||||
public fun String.replaceIndent(newIndent: String = ...): String defined in kotlin.text
|
public fun String.replaceIndent(newIndent: String = ...): String defined in kotlin.text
|
||||||
(note: this may be caused by the fact that some classes compiled with an incompatible version of Kotlin were found in the classpath. Such classes cannot be loaded properly by this version of Kotlin compiler. See below for more information)
|
|
||||||
1.replaceIndent(2, 3)
|
1.replaceIndent(2, 3)
|
||||||
^
|
^
|
||||||
compiler/testData/cli/jvm/wrongAbiVersionLib/bin/ClassWithWrongAbiVersion.class: error: class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 0.30.0, expected version is $ABI_VERSION$
|
|
||||||
COMPILATION_ERROR
|
COMPILATION_ERROR
|
||||||
+4
-1
@@ -1,2 +1,5 @@
|
|||||||
compiler/testData/cli/jvm/wrongAbiVersionLib/bin/wrong/ClassWithInnerLambda.class: error: class 'wrong/ClassWithInnerLambda' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 0.30.0, expected version is $ABI_VERSION$
|
compiler/testData/cli/jvm/wrongAbiVersionNoErrors.kt:3:14: error: class 'wrong.ClassWithInnerLambda' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 0.30.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TESTDATA_DIR$/wrongAbiVersionLib/bin/wrong/ClassWithInnerLambda.class
|
||||||
|
import wrong.ClassWithInnerLambda
|
||||||
|
^
|
||||||
COMPILATION_ERROR
|
COMPILATION_ERROR
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
class Nested
|
||||||
|
|
||||||
|
fun method() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
var bar = 42
|
||||||
|
typealias TA = String
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:5:16: error: class 'a.A' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class
|
||||||
|
fun baz(param: A, nested: A.Nested) {
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:5:27: error: class 'a.A' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class
|
||||||
|
fun baz(param: A, nested: A.Nested) {
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:5:29: error: class 'a.A.Nested' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A$Nested.class
|
||||||
|
fun baz(param: A, nested: A.Nested) {
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:6:23: error: class 'a.A' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class
|
||||||
|
val constructor = A()
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:7:18: error: class 'a.A' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class
|
||||||
|
val nested = A.Nested()
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:7:20: error: class 'a.A.Nested' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A$Nested.class
|
||||||
|
val nested = A.Nested()
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:8:22: error: class 'a.A' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class
|
||||||
|
val methodCall = param.method()
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:9:30: error: class 'a.A' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class
|
||||||
|
val supertype = object : A() {}
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:11:13: error: class 'a.AKt' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/AKt.class
|
||||||
|
val x = foo()
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:12:13: error: class 'a.AKt' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/AKt.class
|
||||||
|
val y = bar
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:13:5: error: class 'a.AKt' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/AKt.class
|
||||||
|
bar = 239
|
||||||
|
^
|
||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:14:12: error: class 'a.AKt' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$.
|
||||||
|
The class is loaded from $TMP_DIR$/library-after.jar!/a/AKt.class
|
||||||
|
val z: TA = ""
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package usage
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun baz(param: A, nested: A.Nested) {
|
||||||
|
val constructor = A()
|
||||||
|
val nested = A.Nested()
|
||||||
|
val methodCall = param.method()
|
||||||
|
val supertype = object : A() {}
|
||||||
|
|
||||||
|
val x = foo()
|
||||||
|
val y = bar
|
||||||
|
bar = 239
|
||||||
|
val z: TA = ""
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
class Nested
|
||||||
|
|
||||||
|
fun method() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
var bar = 42
|
||||||
|
typealias TA = String
|
||||||
compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipVersionCheck/output.txt
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
@file:Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER")
|
||||||
|
package usage
|
||||||
|
|
||||||
|
import a.*
|
||||||
|
|
||||||
|
fun baz(param: A, nested: A.Nested) {
|
||||||
|
val constructor = A()
|
||||||
|
val nested2 = A.Nested()
|
||||||
|
val methodCall = param.method()
|
||||||
|
val supertype = object : A() {}
|
||||||
|
|
||||||
|
val x = foo()
|
||||||
|
val y = bar
|
||||||
|
bar = 239
|
||||||
|
val z: TA = ""
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli;
|
package org.jetbrains.kotlin.cli;
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
import com.intellij.util.ArrayUtil;
|
import com.intellij.util.ArrayUtil;
|
||||||
import kotlin.Pair;
|
import kotlin.Pair;
|
||||||
@@ -72,8 +73,10 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
|||||||
@NotNull String testDataDir,
|
@NotNull String testDataDir,
|
||||||
@NotNull BinaryVersion version
|
@NotNull BinaryVersion version
|
||||||
) {
|
) {
|
||||||
|
String testDataAbsoluteDir = new File(testDataDir).getAbsolutePath();
|
||||||
String normalizedOutputWithoutExitCode = pureOutput
|
String normalizedOutputWithoutExitCode = pureOutput
|
||||||
.replace(new File(testDataDir).getAbsolutePath(), "$TESTDATA_DIR$")
|
.replace(testDataAbsoluteDir, "$TESTDATA_DIR$")
|
||||||
|
.replace(FileUtil.toSystemIndependentName(testDataAbsoluteDir), "$TESTDATA_DIR$")
|
||||||
.replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getAbsolutePath(), "$PROJECT_DIR$")
|
.replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getAbsolutePath(), "$PROJECT_DIR$")
|
||||||
.replace("expected version is " + version, "expected version is $ABI_VERSION$")
|
.replace("expected version is " + version, "expected version is $ABI_VERSION$")
|
||||||
.replace("\\", "/")
|
.replace("\\", "/")
|
||||||
|
|||||||
+54
-4
@@ -22,9 +22,11 @@ import com.intellij.openapi.util.io.FileUtil;
|
|||||||
import kotlin.Pair;
|
import kotlin.Pair;
|
||||||
import kotlin.collections.SetsKt;
|
import kotlin.collections.SetsKt;
|
||||||
import kotlin.io.FilesKt;
|
import kotlin.io.FilesKt;
|
||||||
|
import kotlin.jvm.functions.Function2;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||||
import org.jetbrains.kotlin.cli.AbstractCliTest;
|
import org.jetbrains.kotlin.cli.AbstractCliTest;
|
||||||
|
import org.jetbrains.kotlin.cli.WrongBytecodeVersionTest;
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport;
|
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport;
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer;
|
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer;
|
||||||
@@ -37,6 +39,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration;
|
|||||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion;
|
import org.jetbrains.kotlin.config.KotlinCompilerVersion;
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
|
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
|
||||||
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
|
||||||
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver;
|
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver;
|
||||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
|
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
@@ -92,7 +95,7 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
private String normalizeOutput(@NotNull Pair<String, ExitCode> output) {
|
private String normalizeOutput(@NotNull Pair<String, ExitCode> output) {
|
||||||
return AbstractCliTest.getNormalizedCompilerOutput(
|
return AbstractCliTest.getNormalizedCompilerOutput(
|
||||||
output.getFirst(), output.getSecond(), getTestDataDirectory().getPath(), JvmMetadataVersion.INSTANCE
|
output.getFirst(), output.getSecond(), getTestDataDirectory().getPath(), JvmMetadataVersion.INSTANCE
|
||||||
);
|
).replace(FileUtil.toSystemIndependentName(tmpdir.getAbsolutePath()), "$TMP_DIR$");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doTestWithTxt(@NotNull File... extraClassPath) throws Exception {
|
private void doTestWithTxt(@NotNull File... extraClassPath) throws Exception {
|
||||||
@@ -134,6 +137,20 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static File copyJarFileWithoutEntry(@NotNull File jarPath, @NotNull String... entriesToDelete) {
|
private static File copyJarFileWithoutEntry(@NotNull File jarPath, @NotNull String... entriesToDelete) {
|
||||||
|
return transformJar(jarPath, new Function2<String, byte[], byte[]>() {
|
||||||
|
@Override
|
||||||
|
public byte[] invoke(String s, byte[] bytes) {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}, entriesToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static File transformJar(
|
||||||
|
@NotNull File jarPath,
|
||||||
|
@NotNull Function2<String, byte[], byte[]> transformEntry,
|
||||||
|
@NotNull String... entriesToDelete
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
File outputFile = new File(jarPath.getParentFile(), FileUtil.getNameWithoutExtension(jarPath) + "-after.jar");
|
File outputFile = new File(jarPath.getParentFile(), FileUtil.getNameWithoutExtension(jarPath) + "-after.jar");
|
||||||
Set<String> toDelete = SetsKt.setOf(entriesToDelete);
|
Set<String> toDelete = SetsKt.setOf(entriesToDelete);
|
||||||
@@ -144,11 +161,16 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
try {
|
try {
|
||||||
for (Enumeration<JarEntry> enumeration = jar.entries(); enumeration.hasMoreElements(); ) {
|
for (Enumeration<JarEntry> enumeration = jar.entries(); enumeration.hasMoreElements(); ) {
|
||||||
JarEntry jarEntry = enumeration.nextElement();
|
JarEntry jarEntry = enumeration.nextElement();
|
||||||
if (toDelete.contains(jarEntry.getName())) {
|
String name = jarEntry.getName();
|
||||||
|
if (toDelete.contains(name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
output.putNextEntry(jarEntry);
|
byte[] bytes = FileUtil.loadBytes(jar.getInputStream(jarEntry));
|
||||||
output.write(FileUtil.loadBytes(jar.getInputStream(jarEntry)));
|
byte[] newBytes = name.endsWith(".class") ? transformEntry.invoke(name, bytes) : bytes;
|
||||||
|
JarEntry newEntry = new JarEntry(name);
|
||||||
|
newEntry.setSize(newBytes.length);
|
||||||
|
output.putNextEntry(newEntry);
|
||||||
|
output.write(newBytes);
|
||||||
output.closeEntry();
|
output.closeEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -228,6 +250,23 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doTestKotlinLibraryWithWrongMetadataVersion(@NotNull String libraryName, @NotNull String... additionalOptions) throws Exception {
|
||||||
|
final int[] version = new JvmMetadataVersion(42, 0, 0).toArray();
|
||||||
|
File library = transformJar(compileLibrary(libraryName), new Function2<String, byte[], byte[]>() {
|
||||||
|
@Override
|
||||||
|
public byte[] invoke(String name, byte[] bytes) {
|
||||||
|
return WrongBytecodeVersionTest.Companion.transformMetadataInClassFile(bytes, new Function2<String, Object, Object>() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(String name, Object value) {
|
||||||
|
return JvmAnnotationNames.METADATA_VERSION_FIELD_NAME.equals(name) ? version : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Pair<String, ExitCode> output = compileKotlin("source.kt", tmpdir, Arrays.asList(additionalOptions), library);
|
||||||
|
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void doTestPreReleaseKotlinLibrary(@NotNull String libraryName, @NotNull String... additionalOptions) throws Exception {
|
private void doTestPreReleaseKotlinLibrary(@NotNull String libraryName, @NotNull String... additionalOptions) throws Exception {
|
||||||
// Compiles the library with the "pre-release" flag, then compiles a usage of this library in the release mode
|
// Compiles the library with the "pre-release" flag, then compiles a usage of this library in the release mode
|
||||||
@@ -377,6 +416,17 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
public void testWrongMetadataVersion() throws Exception {
|
||||||
|
doTestKotlinLibraryWithWrongMetadataVersion("library");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// TODO: refactor and uncomment
|
||||||
|
public void testWrongMetadataVersionSkipVersionCheck() throws Exception {
|
||||||
|
doTestKotlinLibraryWithWrongMetadataVersion("library", "-Xskip-metadata-version-check");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/*test source mapping generation when source info is absent*/
|
/*test source mapping generation when source info is absent*/
|
||||||
public void testInlineFunWithoutDebugInfo() throws Exception {
|
public void testInlineFunWithoutDebugInfo() throws Exception {
|
||||||
compileKotlin("sourceInline.kt", tmpdir);
|
compileKotlin("sourceInline.kt", tmpdir);
|
||||||
|
|||||||
+13
-18
@@ -99,25 +99,20 @@ class LazyJavaPackageScope(
|
|||||||
object SyntheticClass : KotlinClassLookupResult()
|
object SyntheticClass : KotlinClassLookupResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveKotlinBinaryClass(kotlinClass: KotlinJvmBinaryClass?): KotlinClassLookupResult {
|
private fun resolveKotlinBinaryClass(kotlinClass: KotlinJvmBinaryClass?): KotlinClassLookupResult =
|
||||||
if (kotlinClass == null) return KotlinClassLookupResult.NotFound
|
when {
|
||||||
|
kotlinClass == null -> {
|
||||||
val header = kotlinClass.classHeader
|
KotlinClassLookupResult.NotFound
|
||||||
return when {
|
}
|
||||||
!header.metadataVersion.isCompatible() -> {
|
kotlinClass.classHeader.kind == KotlinClassHeader.Kind.CLASS -> {
|
||||||
c.components.errorReporter.reportIncompatibleMetadataVersion(kotlinClass.classId, kotlinClass.location, header.metadataVersion)
|
val descriptor = c.components.deserializedDescriptorResolver.resolveClass(kotlinClass)
|
||||||
KotlinClassLookupResult.NotFound
|
if (descriptor != null) KotlinClassLookupResult.Found(descriptor) else KotlinClassLookupResult.NotFound
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// This is a package or interface DefaultImpls or something like that
|
||||||
|
KotlinClassLookupResult.SyntheticClass
|
||||||
|
}
|
||||||
}
|
}
|
||||||
header.kind == KotlinClassHeader.Kind.CLASS -> {
|
|
||||||
val descriptor = c.components.deserializedDescriptorResolver.resolveClass(kotlinClass)
|
|
||||||
if (descriptor != null) KotlinClassLookupResult.Found(descriptor) else KotlinClassLookupResult.NotFound
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// This is a package or interface DefaultImpls or something like that
|
|
||||||
KotlinClassLookupResult.SyntheticClass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// javaClass here is only for sake of optimizations
|
// javaClass here is only for sake of optimizations
|
||||||
private class FindClassRequest(val name: Name, val javaClass: JavaClass?) {
|
private class FindClassRequest(val name: Name, val javaClass: JavaClass?) {
|
||||||
|
|||||||
+15
-9
@@ -25,8 +25,10 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
|||||||
import org.jetbrains.kotlin.serialization.ClassDataWithSource
|
import org.jetbrains.kotlin.serialization.ClassDataWithSource
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
|
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
|
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
|
||||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||||
import org.jetbrains.kotlin.utils.sure
|
import org.jetbrains.kotlin.utils.sure
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -50,7 +52,11 @@ class DeserializedDescriptorResolver(private val errorReporter: ErrorReporter) {
|
|||||||
val classData = parseProto(kotlinClass) {
|
val classData = parseProto(kotlinClass) {
|
||||||
JvmProtoBufUtil.readClassDataFrom(data, strings)
|
JvmProtoBufUtil.readClassDataFrom(data, strings)
|
||||||
}
|
}
|
||||||
val sourceElement = KotlinJvmBinarySourceElement(kotlinClass, !IS_PRE_RELEASE && kotlinClass.classHeader.isPreRelease)
|
val sourceElement = KotlinJvmBinarySourceElement(
|
||||||
|
kotlinClass,
|
||||||
|
kotlinClass.incompatibility,
|
||||||
|
!IS_PRE_RELEASE && kotlinClass.classHeader.isPreRelease
|
||||||
|
)
|
||||||
return ClassDataWithSource(classData, sourceElement)
|
return ClassDataWithSource(classData, sourceElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +68,7 @@ class DeserializedDescriptorResolver(private val errorReporter: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
val source = JvmPackagePartSource(
|
val source = JvmPackagePartSource(
|
||||||
kotlinClass,
|
kotlinClass,
|
||||||
|
kotlinClass.incompatibility,
|
||||||
isPreReleaseInvisible = !IS_PRE_RELEASE && kotlinClass.classHeader.isPreRelease
|
isPreReleaseInvisible = !IS_PRE_RELEASE && kotlinClass.classHeader.isPreRelease
|
||||||
)
|
)
|
||||||
return DeserializedPackageMemberScope(descriptor, packageProto, nameResolver, source, components) {
|
return DeserializedPackageMemberScope(descriptor, packageProto, nameResolver, source, components) {
|
||||||
@@ -70,16 +77,15 @@ class DeserializedDescriptorResolver(private val errorReporter: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readData(kotlinClass: KotlinJvmBinaryClass, expectedKinds: Set<KotlinClassHeader.Kind>): Array<String>? {
|
private val KotlinJvmBinaryClass.incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>?
|
||||||
val header = kotlinClass.classHeader
|
get() {
|
||||||
if (!header.metadataVersion.isCompatible()) {
|
if (classHeader.metadataVersion.isCompatible()) return null
|
||||||
errorReporter.reportIncompatibleMetadataVersion(kotlinClass.classId, kotlinClass.location, header.metadataVersion)
|
return IncompatibleVersionErrorData(classHeader.metadataVersion, JvmMetadataVersion.INSTANCE, location, classId)
|
||||||
}
|
|
||||||
else if (expectedKinds.contains(header.kind)) {
|
|
||||||
return header.data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
internal fun readData(kotlinClass: KotlinJvmBinaryClass, expectedKinds: Set<KotlinClassHeader.Kind>): Array<String>? {
|
||||||
|
val header = kotlinClass.classHeader
|
||||||
|
return (header.data ?: header.incompatibleData)?.check { header.kind in expectedKinds }
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <T> parseProto(klass: KotlinJvmBinaryClass, block: () -> T): T {
|
private inline fun <T> parseProto(klass: KotlinJvmBinaryClass, block: () -> T): T {
|
||||||
|
|||||||
+8
-1
@@ -21,18 +21,25 @@ import org.jetbrains.kotlin.name.ClassId
|
|||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
||||||
|
|
||||||
class JvmPackagePartSource(
|
class JvmPackagePartSource(
|
||||||
val className: JvmClassName,
|
val className: JvmClassName,
|
||||||
val facadeClassName: JvmClassName?,
|
val facadeClassName: JvmClassName?,
|
||||||
|
override val incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null,
|
||||||
override val isPreReleaseInvisible: Boolean = false
|
override val isPreReleaseInvisible: Boolean = false
|
||||||
) : DeserializedContainerSource {
|
) : DeserializedContainerSource {
|
||||||
constructor(kotlinClass: KotlinJvmBinaryClass, isPreReleaseInvisible: Boolean = false) : this(
|
constructor(
|
||||||
|
kotlinClass: KotlinJvmBinaryClass,
|
||||||
|
incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null,
|
||||||
|
isPreReleaseInvisible: Boolean = false
|
||||||
|
) : this(
|
||||||
JvmClassName.byClassId(kotlinClass.classId),
|
JvmClassName.byClassId(kotlinClass.classId),
|
||||||
kotlinClass.classHeader.multifileClassName?.let {
|
kotlinClass.classHeader.multifileClassName?.let {
|
||||||
if (it.isNotEmpty()) JvmClassName.byInternalName(it) else null
|
if (it.isNotEmpty()) JvmClassName.byInternalName(it) else null
|
||||||
},
|
},
|
||||||
|
incompatibility,
|
||||||
isPreReleaseInvisible
|
isPreReleaseInvisible
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
@@ -18,10 +18,12 @@ package org.jetbrains.kotlin.load.kotlin
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.SourceFile
|
import org.jetbrains.kotlin.descriptors.SourceFile
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
||||||
|
|
||||||
class KotlinJvmBinarySourceElement(
|
class KotlinJvmBinarySourceElement(
|
||||||
val binaryClass: KotlinJvmBinaryClass,
|
val binaryClass: KotlinJvmBinaryClass,
|
||||||
|
override val incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null,
|
||||||
override val isPreReleaseInvisible: Boolean = false
|
override val isPreReleaseInvisible: Boolean = false
|
||||||
) : DeserializedContainerSource {
|
) : DeserializedContainerSource {
|
||||||
override val presentableFqName: FqName
|
override val presentableFqName: FqName
|
||||||
|
|||||||
+1
@@ -27,6 +27,7 @@ class KotlinClassHeader(
|
|||||||
val metadataVersion: JvmMetadataVersion,
|
val metadataVersion: JvmMetadataVersion,
|
||||||
val bytecodeVersion: JvmBytecodeBinaryVersion,
|
val bytecodeVersion: JvmBytecodeBinaryVersion,
|
||||||
val data: Array<String>?,
|
val data: Array<String>?,
|
||||||
|
val incompatibleData: Array<String>?,
|
||||||
val strings: Array<String>?,
|
val strings: Array<String>?,
|
||||||
val extraString: String?,
|
val extraString: String?,
|
||||||
val extraInt: Int
|
val extraInt: Int
|
||||||
|
|||||||
+6
@@ -54,6 +54,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
|||||||
private int extraInt = 0;
|
private int extraInt = 0;
|
||||||
private String[] data = null;
|
private String[] data = null;
|
||||||
private String[] strings = null;
|
private String[] strings = null;
|
||||||
|
private String[] incompatibleData = null;
|
||||||
private KotlinClassHeader.Kind headerKind = null;
|
private KotlinClassHeader.Kind headerKind = null;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -62,6 +63,10 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!metadataVersion.isCompatible()) {
|
||||||
|
incompatibleData = data;
|
||||||
|
}
|
||||||
|
|
||||||
if (metadataVersion == null || !metadataVersion.isCompatible()) {
|
if (metadataVersion == null || !metadataVersion.isCompatible()) {
|
||||||
data = null;
|
data = null;
|
||||||
}
|
}
|
||||||
@@ -76,6 +81,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
|||||||
metadataVersion != null ? metadataVersion : JvmMetadataVersion.INVALID_VERSION,
|
metadataVersion != null ? metadataVersion : JvmMetadataVersion.INVALID_VERSION,
|
||||||
bytecodeVersion != null ? bytecodeVersion : JvmBytecodeBinaryVersion.INVALID_VERSION,
|
bytecodeVersion != null ? bytecodeVersion : JvmBytecodeBinaryVersion.INVALID_VERSION,
|
||||||
data,
|
data,
|
||||||
|
incompatibleData,
|
||||||
strings,
|
strings,
|
||||||
extraString,
|
extraString,
|
||||||
extraInt
|
extraInt
|
||||||
|
|||||||
+4
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||||
import org.jetbrains.kotlin.serialization.Flags
|
import org.jetbrains.kotlin.serialization.Flags
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||||
import org.jetbrains.kotlin.types.SimpleType
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
@@ -43,6 +44,9 @@ interface DeserializedMemberDescriptor : MemberDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DeserializedContainerSource : SourceElement {
|
interface DeserializedContainerSource : SourceElement {
|
||||||
|
// Non-null if this container is loaded from a class with an incompatible binary version
|
||||||
|
val incompatibility: IncompatibleVersionErrorData<*>?
|
||||||
|
|
||||||
// True iff this is container is "invisible" because it's loaded from a pre-release class and this compiler is a release
|
// True iff this is container is "invisible" because it's loaded from a pre-release class and this compiler is a release
|
||||||
val isPreReleaseInvisible: Boolean
|
val isPreReleaseInvisible: Boolean
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user