Report error on generating calls to binary incompatible classes
This commit is contained in:
+3
-1
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen.signature
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||
import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
|
||||
@@ -26,7 +27,8 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.KotlinToJvmSignatureMapper
|
||||
|
||||
class KotlinToJvmSignatureMapperImpl : KotlinToJvmSignatureMapper {
|
||||
// We use empty BindingContext, because it is only used by JetTypeMapper for purposes irrelevant to the needs of this class
|
||||
private val typeMapper: JetTypeMapper = JetTypeMapper(BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES, NoResolveFileClassesProvider, null, JvmAbi.DEFAULT_MODULE_NAME)
|
||||
private val typeMapper = JetTypeMapper(BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES, NoResolveFileClassesProvider, null,
|
||||
IncompatibleClassTracker.DoNothing, JvmAbi.DEFAULT_MODULE_NAME)
|
||||
|
||||
override fun mapToJvmMethodSignature(function: FunctionDescriptor) = typeMapper.mapSignature(function)
|
||||
}
|
||||
|
||||
+2
-1
@@ -51,7 +51,8 @@ class BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||
) : SignatureCollectingClassBuilderFactory(builderFactory) {
|
||||
|
||||
// Avoid errors when some classes are not loaded for some reason
|
||||
private val typeMapper = JetTypeMapper(bindingContext, ClassBuilderMode.LIGHT_CLASSES, fileClassesProvider, incrementalCache, moduleName)
|
||||
private val typeMapper = JetTypeMapper(bindingContext, ClassBuilderMode.LIGHT_CLASSES, fileClassesProvider, incrementalCache,
|
||||
IncompatibleClassTracker.DoNothing, moduleName)
|
||||
private val reportDiagnosticsTasks = ArrayList<() -> Unit>()
|
||||
|
||||
fun reportDiagnostics() {
|
||||
|
||||
@@ -95,7 +95,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
incrementalCompilationComponents.getIncrementalCache(targetId)
|
||||
else null
|
||||
|
||||
private val extraJvmDiagnosticsTrace: BindingTrace = DelegatingBindingTrace(bindingContext, false, "For extra diagnostics in ${this.javaClass}")
|
||||
val extraJvmDiagnosticsTrace: BindingTrace = DelegatingBindingTrace(bindingContext, false, "For extra diagnostics in ${this.javaClass}")
|
||||
private val interceptedBuilderFactory: ClassBuilderFactory
|
||||
private var used = false
|
||||
|
||||
@@ -109,7 +109,10 @@ class GenerationState @JvmOverloads constructor(
|
||||
val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode
|
||||
val bindingTrace: BindingTrace = DelegatingBindingTrace(bindingContext, "trace in GenerationState")
|
||||
val bindingContext: BindingContext = bindingTrace.bindingContext
|
||||
val typeMapper: JetTypeMapper = JetTypeMapper(this.bindingContext, classBuilderMode, fileClassesProvider, getIncrementalCacheForThisTarget(), this.moduleName)
|
||||
val typeMapper: JetTypeMapper = JetTypeMapper(
|
||||
this.bindingContext, classBuilderMode, fileClassesProvider, getIncrementalCacheForThisTarget(),
|
||||
IncompatibleClassTrackerImpl(extraJvmDiagnosticsTrace), this.moduleName
|
||||
)
|
||||
val intrinsics: IntrinsicMethods = IntrinsicMethods()
|
||||
val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this)
|
||||
val inlineCycleReporter: InlineCycleReporter = InlineCycleReporter(diagnostics)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.codegen.state
|
||||
|
||||
import org.jetbrains.kotlin.load.java.components.IncompatibleVersionErrorData
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.util.slicedMap.Slices
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||
|
||||
interface IncompatibleClassTracker {
|
||||
fun record(binaryClass: KotlinJvmBinaryClass)
|
||||
|
||||
object DoNothing : IncompatibleClassTracker {
|
||||
override fun record(binaryClass: KotlinJvmBinaryClass) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IncompatibleClassTrackerImpl(val trace: BindingTrace) : IncompatibleClassTracker {
|
||||
private val classes = linkedSetOf<String>()
|
||||
|
||||
override fun record(binaryClass: KotlinJvmBinaryClass) {
|
||||
if (classes.add(binaryClass.location)) {
|
||||
val errorData = IncompatibleVersionErrorData(binaryClass.classHeader.bytecodeVersion, binaryClass.location, binaryClass.classId)
|
||||
trace.record(BYTECODE_VERSION_ERRORS, binaryClass.location, errorData)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val BYTECODE_VERSION_ERRORS: WritableSlice<String, IncompatibleVersionErrorData> = Slices.createCollectiveSlice()
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.codegen.state;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.Pair;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.BuiltinsPackageFragment;
|
||||
@@ -38,10 +38,15 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature;
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion;
|
||||
import org.jetbrains.kotlin.load.java.SpecialBuiltinMembers;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment;
|
||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageScope;
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryPackageSourceElement;
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement;
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider;
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache;
|
||||
import org.jetbrains.kotlin.name.*;
|
||||
@@ -66,6 +71,7 @@ import org.jetbrains.kotlin.resolve.scopes.AbstractScopeAdapter;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializedType;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -90,6 +96,7 @@ public class JetTypeMapper {
|
||||
private final ClassBuilderMode classBuilderMode;
|
||||
private final JvmFileClassesProvider fileClassesProvider;
|
||||
private final IncrementalCache incrementalCache;
|
||||
private final IncompatibleClassTracker incompatibleClassTracker;
|
||||
private final String moduleName;
|
||||
|
||||
public JetTypeMapper(
|
||||
@@ -97,12 +104,14 @@ public class JetTypeMapper {
|
||||
@NotNull ClassBuilderMode classBuilderMode,
|
||||
@NotNull JvmFileClassesProvider fileClassesProvider,
|
||||
@Nullable IncrementalCache incrementalCache,
|
||||
@NotNull IncompatibleClassTracker incompatibleClassTracker,
|
||||
@NotNull String moduleName
|
||||
) {
|
||||
this.bindingContext = bindingContext;
|
||||
this.classBuilderMode = classBuilderMode;
|
||||
this.fileClassesProvider = fileClassesProvider;
|
||||
this.incrementalCache = incrementalCache;
|
||||
this.incompatibleClassTracker = incompatibleClassTracker;
|
||||
this.moduleName = moduleName;
|
||||
}
|
||||
|
||||
@@ -978,12 +987,17 @@ public class JetTypeMapper {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JvmMethodSignature mapSignature(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind,
|
||||
List<ValueParameterDescriptor> valueParameters) {
|
||||
public JvmMethodSignature mapSignature(
|
||||
@NotNull FunctionDescriptor f,
|
||||
@NotNull OwnerKind kind,
|
||||
@NotNull List<ValueParameterDescriptor> valueParameters
|
||||
) {
|
||||
if (f instanceof FunctionImportedFromObject) {
|
||||
return mapSignature(((FunctionImportedFromObject) f).getCallableFromObject());
|
||||
}
|
||||
|
||||
checkOwnerCompatibility(f);
|
||||
|
||||
BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD);
|
||||
|
||||
if (f instanceof ConstructorDescriptor) {
|
||||
@@ -1036,6 +1050,33 @@ public class JetTypeMapper {
|
||||
return signature;
|
||||
}
|
||||
|
||||
private void checkOwnerCompatibility(@NotNull FunctionDescriptor descriptor) {
|
||||
if (!(descriptor instanceof DeserializedCallableMemberDescriptor)) return;
|
||||
|
||||
KotlinJvmBinaryClass ownerClass = null;
|
||||
|
||||
DeclarationDescriptor container = descriptor.getContainingDeclaration();
|
||||
if (container instanceof DeserializedClassDescriptor) {
|
||||
SourceElement source = ((DeserializedClassDescriptor) container).getSource();
|
||||
if (source instanceof KotlinJvmBinarySourceElement) {
|
||||
ownerClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass();
|
||||
}
|
||||
}
|
||||
else if (container instanceof LazyJavaPackageFragment) {
|
||||
SourceElement source = ((LazyJavaPackageFragment) container).getSource();
|
||||
if (source instanceof KotlinJvmBinaryPackageSourceElement) {
|
||||
ownerClass = ((KotlinJvmBinaryPackageSourceElement) source).getRepresentativeBinaryClass();
|
||||
}
|
||||
}
|
||||
|
||||
if (ownerClass != null) {
|
||||
JvmBytecodeBinaryVersion version = ownerClass.getClassHeader().getBytecodeVersion();
|
||||
if (!version.isCompatible()) {
|
||||
incompatibleClassTracker.record(ownerClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeCustomParameter(
|
||||
@NotNull FunctionDescriptor f,
|
||||
@NotNull ValueParameterDescriptor parameter,
|
||||
|
||||
+44
-14
@@ -26,13 +26,15 @@ import kotlin.jvm.functions.Function0;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.codegen.state.IncompatibleClassTrackerImpl;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.*;
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
|
||||
import org.jetbrains.kotlin.load.java.JavaBindingContext;
|
||||
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion;
|
||||
import org.jetbrains.kotlin.load.java.components.IncompatibleVersionErrorData;
|
||||
import org.jetbrains.kotlin.load.java.components.TraceBasedErrorReporter;
|
||||
import org.jetbrains.kotlin.load.java.components.TraceBasedErrorReporter.MetadataVersionErrorData;
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
|
||||
@@ -41,6 +43,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
|
||||
import org.jetbrains.kotlin.utils.StringsKt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -146,12 +149,12 @@ public final class AnalyzerWithCompilerReport {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<MetadataVersionErrorData> getAbiVersionErrors() {
|
||||
private List<IncompatibleVersionErrorData> getAbiVersionErrors() {
|
||||
assert analysisResult != null;
|
||||
BindingContext bindingContext = analysisResult.getBindingContext();
|
||||
|
||||
Collection<String> errorClasses = bindingContext.getKeys(TraceBasedErrorReporter.METADATA_VERSION_ERRORS);
|
||||
List<MetadataVersionErrorData> result = new ArrayList<MetadataVersionErrorData>(errorClasses.size());
|
||||
List<IncompatibleVersionErrorData> result = new ArrayList<IncompatibleVersionErrorData>(errorClasses.size());
|
||||
for (String kotlinClass : errorClasses) {
|
||||
result.add(bindingContext.get(TraceBasedErrorReporter.METADATA_VERSION_ERRORS, kotlinClass));
|
||||
}
|
||||
@@ -159,16 +162,43 @@ public final class AnalyzerWithCompilerReport {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void reportMetadataVersionErrors(@NotNull List<MetadataVersionErrorData> errors) {
|
||||
for (MetadataVersionErrorData data : errors) {
|
||||
String path = toSystemDependentName(data.getFilePath());
|
||||
messageCollector.report(
|
||||
CompilerMessageSeverity.ERROR,
|
||||
"Class '" + JvmClassName.byClassId(data.getClassId()) + "' was compiled with an incompatible version of Kotlin. " +
|
||||
"The binary version of its metadata is " + data.getActualVersion() +
|
||||
", expected version is " + JvmMetadataVersion.INSTANCE,
|
||||
CompilerMessageLocation.create(path, -1, -1, null)
|
||||
);
|
||||
private void reportMetadataVersionErrors(@NotNull List<IncompatibleVersionErrorData> errors) {
|
||||
for (IncompatibleVersionErrorData data : errors) {
|
||||
reportIncompatibleBinaryVersion(messageCollector, data, JvmMetadataVersion.INSTANCE, "metadata", CompilerMessageSeverity.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportIncompatibleBinaryVersion(
|
||||
@NotNull MessageCollector messageCollector,
|
||||
@NotNull IncompatibleVersionErrorData data,
|
||||
@NotNull BinaryVersion expectedVersion,
|
||||
@NotNull String versionSortText,
|
||||
@NotNull CompilerMessageSeverity severity
|
||||
) {
|
||||
messageCollector.report(
|
||||
severity,
|
||||
"Class '" + JvmClassName.byClassId(data.getClassId()) + "' was compiled with an incompatible version of Kotlin. " +
|
||||
"The binary version of its " + versionSortText + " is " + data.getActualVersion() + ", " +
|
||||
"expected version is " + expectedVersion,
|
||||
CompilerMessageLocation.create(toSystemDependentName(data.getFilePath()), -1, -1, null)
|
||||
);
|
||||
}
|
||||
|
||||
public static void reportBytecodeVersionErrors(@NotNull BindingContext bindingContext, @NotNull MessageCollector messageCollector) {
|
||||
// TODO: a -X command line option
|
||||
CompilerMessageSeverity severity =
|
||||
"true".equals(System.getProperty("kotlin.jvm.disable.bytecode.version.error"))
|
||||
? CompilerMessageSeverity.WARNING
|
||||
: CompilerMessageSeverity.ERROR;
|
||||
|
||||
Collection<String> locations = bindingContext.getKeys(IncompatibleClassTrackerImpl.BYTECODE_VERSION_ERRORS);
|
||||
if (locations.isEmpty()) return;
|
||||
|
||||
for (String location : locations) {
|
||||
IncompatibleVersionErrorData data =
|
||||
bindingContext.get(IncompatibleClassTrackerImpl.BYTECODE_VERSION_ERRORS, location);
|
||||
assert data != null : "Value is missing for key in binding context: " + location;
|
||||
reportIncompatibleBinaryVersion(messageCollector, data, JvmBytecodeBinaryVersion.INSTANCE, "bytecode", severity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +296,7 @@ public final class AnalyzerWithCompilerReport {
|
||||
public void analyzeAndReport(@NotNull Collection<KtFile> files, @NotNull Function0<AnalysisResult> analyzer) {
|
||||
analysisResult = analyzer.invoke();
|
||||
reportSyntaxErrors(files);
|
||||
List<MetadataVersionErrorData> abiVersionErrors = getAbiVersionErrors();
|
||||
List<IncompatibleVersionErrorData> abiVersionErrors = getAbiVersionErrors();
|
||||
reportDiagnostics(analysisResult.getBindingContext().getDiagnostics(), messageCollector, !abiVersionErrors.isEmpty());
|
||||
if (hasErrors()) {
|
||||
reportMetadataVersionErrors(abiVersionErrors);
|
||||
|
||||
+9
-2
@@ -363,8 +363,15 @@ object KotlinToJVMBytecodeCompiler {
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(
|
||||
FilteredJvmDiagnostics(
|
||||
generationState.collectedExtraJvmDiagnostics,
|
||||
result.bindingContext.diagnostics),
|
||||
environment.messageCollector())
|
||||
result.bindingContext.diagnostics
|
||||
),
|
||||
environment.messageCollector()
|
||||
)
|
||||
|
||||
AnalyzerWithCompilerReport.reportBytecodeVersionErrors(
|
||||
generationState.extraJvmDiagnosticsTrace.bindingContext, environment.messageCollector()
|
||||
);
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
return generationState
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.load.java.components
|
||||
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
|
||||
|
||||
data class IncompatibleVersionErrorData(
|
||||
val actualVersion: BinaryVersion,
|
||||
val filePath: String,
|
||||
val classId: ClassId
|
||||
)
|
||||
+2
-8
@@ -33,21 +33,15 @@ class TraceBasedErrorReporter(private val trace: BindingTrace) : ErrorReporter {
|
||||
private val LOG = Logger.getInstance(TraceBasedErrorReporter::class.java)
|
||||
|
||||
@JvmField
|
||||
val METADATA_VERSION_ERRORS: WritableSlice<String, MetadataVersionErrorData> = Slices.createCollectiveSlice()
|
||||
val METADATA_VERSION_ERRORS: WritableSlice<String, IncompatibleVersionErrorData> = Slices.createCollectiveSlice()
|
||||
|
||||
// TODO: MutableList is a workaround for KT-5792 Covariant types in Kotlin translated to wildcard types in Java
|
||||
@JvmField
|
||||
val INCOMPLETE_HIERARCHY: WritableSlice<ClassDescriptor, MutableList<String>> = Slices.createCollectiveSlice()
|
||||
}
|
||||
|
||||
data class MetadataVersionErrorData(
|
||||
val actualVersion: BinaryVersion,
|
||||
val filePath: String,
|
||||
val classId: ClassId
|
||||
)
|
||||
|
||||
override fun reportIncompatibleMetadataVersion(classId: ClassId, filePath: String, actualVersion: BinaryVersion) {
|
||||
trace.record(METADATA_VERSION_ERRORS, filePath, MetadataVersionErrorData(actualVersion, filePath, classId))
|
||||
trace.record(METADATA_VERSION_ERRORS, filePath, IncompatibleVersionErrorData(actualVersion, filePath, classId))
|
||||
}
|
||||
|
||||
override fun reportIncompleteHierarchy(descriptor: ClassDescriptor, unresolvedSuperClasses: List<String>) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package library
|
||||
|
||||
class A {
|
||||
fun member() {}
|
||||
}
|
||||
|
||||
fun topLevel() {}
|
||||
@@ -0,0 +1,6 @@
|
||||
import library.*
|
||||
|
||||
fun test() {
|
||||
A().member()
|
||||
topLevel()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
$TESTDATA_DIR$/library/A.class: error: class 'library/A' was compiled with an incompatible version of Kotlin. The binary version of its bytecode is 42.0.0, expected version is $ABI_VERSION$
|
||||
$TESTDATA_DIR$/library/AKt.class: error: class 'library/AKt' was compiled with an incompatible version of Kotlin. The binary version of its bytecode is 42.0.0, expected version is $ABI_VERSION$
|
||||
COMPILATION_ERROR
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
+15
-14
@@ -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*/
|
||||
|
||||
Reference in New Issue
Block a user