From 0dc24bcd92d4cca8ec74670cd6ad441484c835e5 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Mon, 21 Jan 2013 20:01:23 -0800 Subject: [PATCH] KT-3268 Check binary format version in the compiler --- .../messages/AnalyzerWithCompilerReport.java | 18 ++++++++ .../jet/lang/resolve/java/AbiVersionUtil.java | 46 +++++++++++++++++++ .../resolve/java/kt/JetClassAnnotation.java | 2 +- .../java/kt/JetPackageClassAnnotation.java | 2 +- .../java/kt/PsiAnnotationWithAbiVersion.java | 31 +++++++++++++ .../java/resolver/JavaClassResolver.java | 2 + .../java/resolver/JavaNamespaceResolver.java | 4 +- compiler/testData/cli/wrongAbiVersion.kt | 5 ++ compiler/testData/cli/wrongAbiVersion.out | 4 ++ .../ClassWithWrongAbiVersion.java | 4 ++ .../wrong/WrongPackage.java | 6 +++ .../org/jetbrains/jet/cli/jvm/CliTest.java | 9 ++++ 12 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AbiVersionUtil.java create mode 100644 compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/PsiAnnotationWithAbiVersion.java create mode 100644 compiler/testData/cli/wrongAbiVersion.kt create mode 100644 compiler/testData/cli/wrongAbiVersion.out create mode 100644 compiler/testData/cli/wrongAbiVersionLib/ClassWithWrongAbiVersion.java create mode 100644 compiler/testData/cli/wrongAbiVersionLib/wrong/WrongPackage.java diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java index b495a2769fb..97330744992 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.cli.common.messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiModifierListOwner; @@ -36,6 +37,8 @@ import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; import java.util.Collection; import java.util.List; @@ -138,6 +141,20 @@ public final class AnalyzerWithCompilerReport { } } + private void reportAbiVersionErrors() { + assert analyzeExhaust != null; + BindingContext bindingContext = analyzeExhaust.getBindingContext(); + + Collection psiClasses = bindingContext.getKeys(AbiVersionUtil.ABI_VERSION_ERRORS); + for (PsiClass psiClass : psiClasses) { + Integer abiVersion = bindingContext.get(AbiVersionUtil.ABI_VERSION_ERRORS, psiClass); + messageCollectorWrapper.report(CompilerMessageSeverity.ERROR, + "Class '" + psiClass.getQualifiedName() + "' was compiled with an incompatible version of Kotlin. " + + "Its ABI version is " + abiVersion + ", expected ABI version is " + JvmAbi.VERSION, + MessageUtil.psiElementToMessageLocation(psiClass)); + } + } + public static boolean reportDiagnostics(@NotNull BindingContext bindingContext, @NotNull MessageCollector messageCollector) { boolean hasErrors = false; for (Diagnostic diagnostic : sortedDiagnostics(bindingContext.getDiagnostics())) { @@ -219,6 +236,7 @@ public final class AnalyzerWithCompilerReport { reportDiagnostics(analyzeExhaust.getBindingContext(), messageCollectorWrapper); reportIncompleteHierarchies(); reportAlternativeSignatureErrors(); + reportAbiVersionErrors(); } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AbiVersionUtil.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AbiVersionUtil.java new file mode 100644 index 00000000000..f1535f68d2f --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AbiVersionUtil.java @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2013 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.jet.lang.resolve.java; + +import com.intellij.psi.PsiClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.java.kt.PsiAnnotationWithAbiVersion; +import org.jetbrains.jet.util.slicedmap.BasicWritableSlice; +import org.jetbrains.jet.util.slicedmap.Slices; +import org.jetbrains.jet.util.slicedmap.WritableSlice; + +public class AbiVersionUtil { + public static final WritableSlice ABI_VERSION_ERRORS = + new BasicWritableSlice(Slices.ONLY_REWRITE_TO_EQUAL, true); + + public static boolean isAbiVersionCompatible(int abiVersion) { + return abiVersion == JvmAbi.VERSION; + } + + public static void checkAbiVersion( + @NotNull PsiClass psiClass, + @NotNull PsiAnnotationWithAbiVersion versionAnnotation, + @NotNull BindingTrace trace) { + if (!versionAnnotation.isDefined()) return; + + int abiVersion = versionAnnotation.getAbiVersion(); + if (isAbiVersionCompatible(abiVersion)) return; + + trace.record(ABI_VERSION_ERRORS, psiClass, abiVersion); + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetClassAnnotation.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetClassAnnotation.java index b63d8ba2570..1dfca21e3f0 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetClassAnnotation.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetClassAnnotation.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationResolver; -public class JetClassAnnotation extends PsiAnnotationWithFlags { +public class JetClassAnnotation extends PsiAnnotationWithAbiVersion { private static final JetClassAnnotation NULL_ANNOTATION = new JetClassAnnotation(null); static { NULL_ANNOTATION.checkInitialized(); diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetPackageClassAnnotation.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetPackageClassAnnotation.java index 48845354bb8..519d82ba4de 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetPackageClassAnnotation.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetPackageClassAnnotation.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationResolver; -public class JetPackageClassAnnotation extends PsiAnnotationWithFlags { +public class JetPackageClassAnnotation extends PsiAnnotationWithAbiVersion { private static final JetPackageClassAnnotation NULL_ANNOTATION = new JetPackageClassAnnotation(null); static { NULL_ANNOTATION.checkInitialized(); diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/PsiAnnotationWithAbiVersion.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/PsiAnnotationWithAbiVersion.java new file mode 100644 index 00000000000..e0d41130963 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/PsiAnnotationWithAbiVersion.java @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2013 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.jet.lang.resolve.java.kt; + +import com.intellij.psi.PsiAnnotation; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; + +public abstract class PsiAnnotationWithAbiVersion extends PsiAnnotationWithFlags { + protected PsiAnnotationWithAbiVersion(@Nullable PsiAnnotation psiAnnotation) { + super(psiAnnotation); + } + + public int getAbiVersion() { + return getIntAttribute(JvmStdlibNames.ABI_VERSION_NAME, -1); + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java index 29d45c10b13..3f06b1a74bf 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java @@ -217,6 +217,8 @@ public final class JavaClassResolver { @NotNull ClassOrNamespaceDescriptor containingDeclaration ) { JetClassAnnotation jetClassAnnotation = JetClassAnnotation.get(psiClass); + AbiVersionUtil.checkAbiVersion(psiClass, jetClassAnnotation, trace); + ClassKind kind = getClassKind(psiClass, jetClassAnnotation); ClassPsiDeclarationProvider classData = semanticServices.getPsiDeclarationProviderFactory().createBinaryClassData(psiClass); ClassDescriptorFromJvmBytecode classDescriptor = new ClassDescriptorFromJvmBytecode(containingDeclaration, kind, diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java index 22cc0a44b74..1a5bd82d3db 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java @@ -31,12 +31,12 @@ import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.*; import org.jetbrains.jet.lang.resolve.java.descriptor.JavaNamespaceDescriptor; +import org.jetbrains.jet.lang.resolve.java.kt.JetPackageClassAnnotation; import org.jetbrains.jet.lang.resolve.java.scope.JavaBaseScope; import org.jetbrains.jet.lang.resolve.java.scope.JavaClassStaticMembersScope; import org.jetbrains.jet.lang.resolve.java.scope.JavaPackageScopeWithoutMembers; import org.jetbrains.jet.lang.resolve.java.scope.JavaScopeForKotlinNamespace; import org.jetbrains.jet.lang.resolve.name.FqName; -import org.jetbrains.jet.lang.resolve.name.Name; import javax.inject.Inject; import java.util.Collections; @@ -153,6 +153,8 @@ public final class JavaNamespaceResolver { javaSemanticServices.getPsiDeclarationProviderFactory().createDeclarationProviderForNamespaceWithoutMembers(psiPackage), fqName, javaSemanticServices); } + + AbiVersionUtil.checkAbiVersion(psiClass, JetPackageClassAnnotation.get(psiClass), trace); return new JavaScopeForKotlinNamespace( namespaceDescriptor, javaSemanticServices.getPsiDeclarationProviderFactory().createDeclarationForKotlinNamespace(psiPackage, psiClass), diff --git a/compiler/testData/cli/wrongAbiVersion.kt b/compiler/testData/cli/wrongAbiVersion.kt new file mode 100644 index 00000000000..3ba290b1244 --- /dev/null +++ b/compiler/testData/cli/wrongAbiVersion.kt @@ -0,0 +1,5 @@ +import wrong.* + +fun foo(x: ClassWithWrongAbiVersion) { + bar() +} \ No newline at end of file diff --git a/compiler/testData/cli/wrongAbiVersion.out b/compiler/testData/cli/wrongAbiVersion.out new file mode 100644 index 00000000000..82cacf2a786 --- /dev/null +++ b/compiler/testData/cli/wrongAbiVersion.out @@ -0,0 +1,4 @@ +WARNING: $TESTDATA_DIR$/wrongAbiVersion.kt: (3, 9) Parameter 'x' is never used +ERROR: $TESTDATA_DIR$/wrongAbiVersionLib/wrong/WrongPackage.java: (3, 1) Class 'wrong.WrongPackage' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is 0 +ERROR: $TESTDATA_DIR$/wrongAbiVersionLib/ClassWithWrongAbiVersion.java: (3, 1) Class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is 0 +COMPILATION_ERROR diff --git a/compiler/testData/cli/wrongAbiVersionLib/ClassWithWrongAbiVersion.java b/compiler/testData/cli/wrongAbiVersionLib/ClassWithWrongAbiVersion.java new file mode 100644 index 00000000000..cf36db25305 --- /dev/null +++ b/compiler/testData/cli/wrongAbiVersionLib/ClassWithWrongAbiVersion.java @@ -0,0 +1,4 @@ +import jet.runtime.typeinfo.JetClass; + +@JetClass("Ljava/lang/Object;", 16, -1) +class ClassWithWrongAbiVersion {} \ No newline at end of file diff --git a/compiler/testData/cli/wrongAbiVersionLib/wrong/WrongPackage.java b/compiler/testData/cli/wrongAbiVersionLib/wrong/WrongPackage.java new file mode 100644 index 00000000000..d5656b88e39 --- /dev/null +++ b/compiler/testData/cli/wrongAbiVersionLib/wrong/WrongPackage.java @@ -0,0 +1,6 @@ +package wrong; + +@jet.runtime.typeinfo.JetPackageClass(abiVersion = -1) +public class WrongPackage { + public static void bar() {} +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/cli/jvm/CliTest.java b/compiler/tests/org/jetbrains/jet/cli/jvm/CliTest.java index c8fde0b8c89..3da26707c1e 100644 --- a/compiler/tests/org/jetbrains/jet/cli/jvm/CliTest.java +++ b/compiler/tests/org/jetbrains/jet/cli/jvm/CliTest.java @@ -133,6 +133,15 @@ public class CliTest { executeCompilerCompareOutput(args); } + @Test + public void wrongAbiVersion() throws Exception { + String[] args = { + "-src", "compiler/testData/cli/wrongAbiVersion.kt", + "-classpath", "compiler/testData/cli/wrongAbiVersionLib", + "-output", tmpdir.getTmpDir().getPath()}; + executeCompilerCompareOutput(args); + } + @Test public void help() throws Exception { executeCompilerCompareOutput(new String[] {"-help"});