[java] optimizes file content reading for .class stub building

This commit is contained in:
Roman Shevchenko
2016-07-08 21:17:59 +02:00
parent fabddbf186
commit 2d8d8144c3
3 changed files with 89 additions and 54 deletions
@@ -37,6 +37,7 @@ import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
@@ -612,7 +613,8 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
PsiJavaFileStub stub = stubBuilder.fun(packageName);
try {
StubBuildingVisitor<VirtualFile> visitor = new StubBuildingVisitor<VirtualFile>(file, STRATEGY, stub, 0, className);
FileContentPair source = new FileContentPair(file, bytes);
StubBuildingVisitor<FileContentPair> visitor = new StubBuildingVisitor<FileContentPair>(source, STRATEGY, stub, 0, className);
reader.accept(visitor, EMPTY_ATTRIBUTES, ClassReader.SKIP_FRAMES);
PsiClassStub<?> result = visitor.getResult();
if (result == null) return null;
@@ -633,23 +635,38 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
return p > 0 ? internalName.substring(0, p).replace('/', '.') : "";
}
private static final InnerClassSourceStrategy<VirtualFile> STRATEGY = new InnerClassSourceStrategy<VirtualFile>() {
@Nullable
@Override
public VirtualFile findInnerClass(String innerName, VirtualFile outerClass) {
String baseName = outerClass.getNameWithoutExtension();
VirtualFile dir = outerClass.getParent();
assert dir != null : outerClass;
return dir.findChild(baseName + '$' + innerName + ".class");
private static class FileContentPair extends Pair<VirtualFile, byte[]> {
public FileContentPair(VirtualFile file, byte[] content) {
super(file, content);
}
@Override
public void accept(VirtualFile innerClass, StubBuildingVisitor<VirtualFile> visitor) {
try {
byte[] bytes = innerClass.contentsToByteArray(false);
new ClassReader(bytes).accept(visitor, EMPTY_ATTRIBUTES, ClassReader.SKIP_FRAMES);
public String toString() {
return first.toString();
}
}
private static final InnerClassSourceStrategy<FileContentPair> STRATEGY = new InnerClassSourceStrategy<FileContentPair>() {
@Nullable
@Override
public FileContentPair findInnerClass(String innerName, FileContentPair outerClass) {
String baseName = outerClass.first.getNameWithoutExtension();
VirtualFile dir = outerClass.first.getParent();
assert dir != null : outerClass;
VirtualFile innerClass = dir.findChild(baseName + '$' + innerName + ".class");
if (innerClass != null) {
try {
byte[] bytes = innerClass.contentsToByteArray(false);
return new FileContentPair(innerClass, bytes);
}
catch (IOException ignored) { }
}
catch (IOException ignored) { }
return null;
}
@Override
public void accept(FileContentPair innerClass, StubBuildingVisitor<FileContentPair> visitor) {
new ClassReader(innerClass.second).accept(visitor, EMPTY_ATTRIBUTES, ClassReader.SKIP_FRAMES);
}
};
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-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.
@@ -22,7 +22,7 @@ import org.jetbrains.annotations.Nullable;
*/
public interface InnerClassSourceStrategy<T> {
@Nullable
T findInnerClass(String name, T outerClass);
T findInnerClass(String innerName, T outerClass);
void accept(T innerClass, StubBuildingVisitor<T> visitor);
}
}
@@ -39,6 +39,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
@@ -787,47 +788,64 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
}
private static Function<String, String> createMapping(Object classSource) {
if (classSource instanceof VirtualFile) {
final Map<String, Pair<String, String>> mapping = ContainerUtil.newHashMap();
Function<String, String> mapping = null;
try {
byte[] bytes = ((VirtualFile)classSource).contentsToByteArray(false);
new ClassReader(bytes).accept(new ClassVisitor(ASM_API) {
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (outerName != null && innerName != null) {
mapping.put(name, pair(outerName, innerName));
}
}
}, ClsFileImpl.EMPTY_ATTRIBUTES, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
}
catch (Exception ignored) { }
if (!mapping.isEmpty()) {
return new Function<String, String>() {
@Override
public String fun(String internalName) {
String className = internalName;
if (className.indexOf('$') >= 0) {
Pair<String, String> p = mapping.get(className);
if (p == null) {
return GUESSING_MAPPER.fun(className);
}
className = p.first;
if (p.second != null) {
className = fun(p.first) + '.' + p.second;
mapping.put(className, pair(className, (String)null));
}
}
return className.replace('/', '.');
}
};
if (classSource instanceof Pair) {
Object second = ((Pair)classSource).second;
if (second instanceof byte[]) {
mapping = createMapping((byte[])second);
}
}
else if (classSource instanceof VirtualFile) {
try {
byte[] bytes = ((VirtualFile)classSource).contentsToByteArray(false);
mapping = createMapping(bytes);
}
catch (IOException ignored) { }
}
return GUESSING_MAPPER;
return mapping != null ? mapping : GUESSING_MAPPER;
}
private static Function<String, String> createMapping(byte[] classBytes) {
final Map<String, Pair<String, String>> mapping = ContainerUtil.newHashMap();
try {
new ClassReader(classBytes).accept(new ClassVisitor(ASM_API) {
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (outerName != null && innerName != null) {
mapping.put(name, pair(outerName, innerName));
}
}
}, ClsFileImpl.EMPTY_ATTRIBUTES, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
}
catch (Exception ignored) { }
if (!mapping.isEmpty()) {
return new Function<String, String>() {
@Override
public String fun(String internalName) {
String className = internalName;
if (className.indexOf('$') >= 0) {
Pair<String, String> p = mapping.get(className);
if (p == null) {
return GUESSING_MAPPER.fun(className);
}
className = p.first;
if (p.second != null) {
className = fun(p.first) + '.' + p.second;
mapping.put(className, pair(className, (String)null));
}
}
return className.replace('/', '.');
}
};
}
return null;
}
public static final Function<String, String> GUESSING_MAPPER = new Function<String, String>() {