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 index f1535f68d2f..8772d9474a8 100644 --- 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 @@ -27,6 +27,7 @@ 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 final int INVALID_VERSION = -1; public static boolean isAbiVersionCompatible(int abiVersion) { return abiVersion == JvmAbi.VERSION; diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 2824b4423d5..9934e6e965f 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -215,6 +215,8 @@ + + diff --git a/idea/src/org/jetbrains/jet/plugin/versions/KotlinAbiVersionIndex.java b/idea/src/org/jetbrains/jet/plugin/versions/KotlinAbiVersionIndex.java new file mode 100644 index 00000000000..e44dd71793e --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/versions/KotlinAbiVersionIndex.java @@ -0,0 +1,130 @@ +/* + * 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.plugin.versions; + +import com.google.common.collect.Maps; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.indexing.*; +import com.intellij.util.io.ExternalIntegerKeyDescriptor; +import com.intellij.util.io.KeyDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.asm4.AnnotationVisitor; +import org.jetbrains.asm4.ClassReader; +import org.jetbrains.asm4.ClassVisitor; +import org.jetbrains.asm4.Opcodes; +import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; + +import java.util.Map; + +/** + * Important! This is not a stub-based index. And it has its own version + */ +public class KotlinAbiVersionIndex extends ScalarIndexExtension { + + public static final KotlinAbiVersionIndex INSTANCE = new KotlinAbiVersionIndex(); + + private static final int VERSION = 0; + + private static final ID NAME = ID.create(KotlinAbiVersionIndex.class.getCanonicalName()); + private static final ExternalIntegerKeyDescriptor KEY_DESCRIPTOR = new ExternalIntegerKeyDescriptor(); + + private static final FileBasedIndex.InputFilter INPUT_FILTER = new FileBasedIndex.InputFilter() { + @Override + public boolean acceptInput(VirtualFile file) { + return file.getFileType() == StdFileTypes.CLASS; + } + }; + private static final DataIndexer INDEXER = new DataIndexer() { + @NotNull + @Override + public Map map(FileContent inputData) { + final Map result = Maps.newHashMap(); + final Ref annotationPresent = new Ref(false); + + ClassReader classReader = new ClassReader(inputData.getContent()); + classReader.accept(new ClassVisitor(Opcodes.ASM4) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (!JvmStdlibNames.JET_CLASS.getDescriptor().equals(desc) && + !JvmStdlibNames.JET_PACKAGE_CLASS.getDescriptor().equals(desc)) { + return null; + } + annotationPresent.set(true); + return new AnnotationVisitor(Opcodes.ASM4) { + @Override + public void visit(String name, Object value) { + if (JvmStdlibNames.ABI_VERSION_NAME.equals(name)) { + if (value instanceof Integer) { + Integer abiVersion = (Integer) value; + result.put(abiVersion, null); + } + else { + // Version is set to something weird + result.put(AbiVersionUtil.INVALID_VERSION, null); + } + } + } + }; + } + }, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + + if (annotationPresent.get() && result.isEmpty()) { + // No version at all: the class is too old + result.put(AbiVersionUtil.INVALID_VERSION, null); + } + + return result; + } + }; + + private KotlinAbiVersionIndex() {} + + @NotNull + @Override + public ID getName() { + return NAME; + } + + @NotNull + @Override + public DataIndexer getIndexer() { + return INDEXER; + } + + @Override + public KeyDescriptor getKeyDescriptor() { + return KEY_DESCRIPTOR; + } + + @Override + public FileBasedIndex.InputFilter getInputFilter() { + return INPUT_FILTER; + } + + @Override + public boolean dependsOnFileContent() { + return true; + } + + @Override + public int getVersion() { + return VERSION; + } +}