Refactor KotlinJvmBinaryClass, VirtualFileKotlinClass, etc.
VirtualFileKotlinClass now reads its header and name on creation. This is not lazy enough and may be slower in some circumstances, but has the following advantage: if anything is wrong in the header, a VirtualFileKotlinClass instance will not be created at all, making it nearly impossible for the client code to operate on invalid data causing all kinds of exceptions
This commit is contained in:
+7
-7
@@ -17,29 +17,29 @@
|
||||
package org.jetbrains.jet.lang.resolve.kotlin;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.SLRUCache;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.storage.LockBasedStorageManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class KotlinBinaryClassCache {
|
||||
|
||||
// This cache must be small: we only query the same file a few times in a row (from different places)
|
||||
// Since it is on application level we should be careful about this cache. Consider profiling multiple projects indexing simultaneously.
|
||||
private final SLRUCache<VirtualFile, KotlinJvmBinaryClass> cache = new SLRUCache<VirtualFile, KotlinJvmBinaryClass>(2, 2) {
|
||||
private final SLRUCache<VirtualFile, Ref<VirtualFileKotlinClass>> cache = new SLRUCache<VirtualFile, Ref<VirtualFileKotlinClass>>(2, 2) {
|
||||
@NotNull
|
||||
@Override
|
||||
public KotlinJvmBinaryClass createValue(VirtualFile virtualFile) {
|
||||
// Operations under this lock are not supposed to involve other locks
|
||||
return new VirtualFileKotlinClass(new LockBasedStorageManager(), virtualFile);
|
||||
public Ref<VirtualFileKotlinClass> createValue(VirtualFile virtualFile) {
|
||||
return Ref.create(VirtualFileKotlinClass.create(virtualFile));
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
public static KotlinJvmBinaryClass getKotlinBinaryClass(@NotNull VirtualFile file) {
|
||||
KotlinBinaryClassCache service = ServiceManager.getService(KotlinBinaryClassCache.class);
|
||||
synchronized (service.cache) {
|
||||
return service.cache.get(file);
|
||||
return service.cache.get(file).get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+52
-53
@@ -19,7 +19,6 @@ package org.jetbrains.jet.lang.resolve.kotlin;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import kotlin.Function0;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
@@ -30,9 +29,6 @@ import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader;
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
import org.jetbrains.jet.storage.NotNullLazyValue;
|
||||
import org.jetbrains.jet.storage.NullableLazyValue;
|
||||
import org.jetbrains.jet.storage.StorageManager;
|
||||
import org.jetbrains.jet.utils.UtilsPackage;
|
||||
|
||||
import static org.jetbrains.asm4.ClassReader.*;
|
||||
@@ -42,27 +38,49 @@ public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
|
||||
private final static Logger LOG = Logger.getInstance(VirtualFileKotlinClass.class);
|
||||
|
||||
private final VirtualFile file;
|
||||
private final NotNullLazyValue<JvmClassName> className;
|
||||
private final NullableLazyValue<KotlinClassHeader> classHeader;
|
||||
private final JvmClassName className;
|
||||
private final KotlinClassHeader classHeader;
|
||||
|
||||
public VirtualFileKotlinClass(@NotNull StorageManager storageManager, @NotNull VirtualFile file) {
|
||||
private VirtualFileKotlinClass(@NotNull VirtualFile file, @NotNull JvmClassName className, @NotNull KotlinClassHeader classHeader) {
|
||||
this.file = file;
|
||||
this.className = storageManager.createLazyValue(
|
||||
new Function0<JvmClassName>() {
|
||||
@Override
|
||||
public JvmClassName invoke() {
|
||||
return computeClassName();
|
||||
}
|
||||
this.className = className;
|
||||
this.classHeader = classHeader;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/* package */ static VirtualFileKotlinClass create(@NotNull VirtualFile file) {
|
||||
try {
|
||||
final ReadKotlinClassHeaderAnnotationVisitor readHeaderVisitor = new ReadKotlinClassHeaderAnnotationVisitor();
|
||||
final Ref<JvmClassName> classNameRef = Ref.create();
|
||||
new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
classNameRef.set(JvmClassName.byInternalName(name));
|
||||
}
|
||||
);
|
||||
this.classHeader = storageManager.createNullableLazyValue(
|
||||
new Function0<KotlinClassHeader>() {
|
||||
@Override
|
||||
public KotlinClassHeader invoke() {
|
||||
return ReadKotlinClassHeaderAnnotationVisitor.read(VirtualFileKotlinClass.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
return convertAnnotationVisitor(readHeaderVisitor, desc);
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
readHeaderVisitor.visitEnd();
|
||||
}
|
||||
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
|
||||
|
||||
JvmClassName className = classNameRef.get();
|
||||
if (className == null) return null;
|
||||
|
||||
KotlinClassHeader header = readHeaderVisitor.createHeader();
|
||||
if (header == null) return null;
|
||||
|
||||
return new VirtualFileKotlinClass(file, className, header);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.warn(renderFileReadingErrorMessage(file), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -71,32 +89,15 @@ public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JvmClassName computeClassName() {
|
||||
final Ref<JvmClassName> classNameRef = Ref.create();
|
||||
try {
|
||||
new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
classNameRef.set(JvmClassName.byInternalName(name));
|
||||
}
|
||||
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logFileReadingError(e);
|
||||
throw UtilsPackage.rethrow(e);
|
||||
}
|
||||
return classNameRef.get();
|
||||
@Override
|
||||
public JvmClassName getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JvmClassName getClassName() {
|
||||
return className.invoke();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KotlinClassHeader getClassHeader() {
|
||||
return classHeader.invoke();
|
||||
return classHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,7 +116,7 @@ public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
|
||||
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logFileReadingError(e);
|
||||
LOG.error(renderFileReadingErrorMessage(file), e);
|
||||
throw UtilsPackage.rethrow(e);
|
||||
}
|
||||
}
|
||||
@@ -200,7 +201,7 @@ public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
|
||||
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logFileReadingError(e);
|
||||
LOG.error(renderFileReadingErrorMessage(file), e);
|
||||
throw UtilsPackage.rethrow(e);
|
||||
}
|
||||
}
|
||||
@@ -211,6 +212,13 @@ public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
|
||||
return JvmClassName.byInternalName(desc.substring(1, desc.length() - 1));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String renderFileReadingErrorMessage(@NotNull VirtualFile file) {
|
||||
return "Could not read file: " + file.getPath() + "\n"
|
||||
+ "Size in bytes: " + file.getLength() + "\n"
|
||||
+ "File type: " + file.getFileType().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return file.hashCode();
|
||||
@@ -225,13 +233,4 @@ public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": " + file.toString();
|
||||
}
|
||||
|
||||
private void logFileReadingError(@NotNull Throwable e) {
|
||||
LOG.error(
|
||||
"Could not read file: " + file.getPath() + "\n"
|
||||
+ "Size in bytes: " + file.getLength() + "\n"
|
||||
+ "File type: " + file.getFileType().getName(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user