Read InnerClasses attribute value in FileBasedKotlinClass
Resolve names mentioned in class declarations based on this information
This commit is contained in:
+93
-21
@@ -17,28 +17,65 @@
|
||||
package org.jetbrains.jet.lang.resolve.kotlin;
|
||||
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import kotlin.Function2;
|
||||
import kotlin.Function3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.descriptors.serialization.ClassId;
|
||||
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.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader;
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.FieldVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.org.objectweb.asm.ClassReader.*;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.ASM5;
|
||||
|
||||
public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
private final JvmClassName className;
|
||||
private final KotlinClassHeader classHeader;
|
||||
private final InnerClassesInfo innerClasses;
|
||||
|
||||
protected FileBasedKotlinClass(@NotNull JvmClassName className, @NotNull KotlinClassHeader classHeader) {
|
||||
protected FileBasedKotlinClass(
|
||||
@NotNull JvmClassName className,
|
||||
@NotNull KotlinClassHeader classHeader,
|
||||
@NotNull InnerClassesInfo innerClasses
|
||||
) {
|
||||
this.className = className;
|
||||
this.classHeader = classHeader;
|
||||
this.innerClasses = innerClasses;
|
||||
}
|
||||
|
||||
private static class OuterAndInnerName {
|
||||
public final String outerInternalName;
|
||||
public final String innerSimpleName;
|
||||
|
||||
private OuterAndInnerName(@NotNull String outerInternalName, @NotNull String innerSimpleName) {
|
||||
this.outerInternalName = outerInternalName;
|
||||
this.innerSimpleName = innerSimpleName;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class InnerClassesInfo {
|
||||
private Map<String, OuterAndInnerName> map = null;
|
||||
|
||||
public void add(@NotNull String name, @NotNull String outerName, @NotNull String innerName) {
|
||||
if (map == null) {
|
||||
map = new HashMap<String, OuterAndInnerName>();
|
||||
}
|
||||
map.put(name, new OuterAndInnerName(outerName, innerName));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public OuterAndInnerName get(@NotNull String name) {
|
||||
return map == null ? null : map.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -47,19 +84,27 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
@Nullable
|
||||
protected static <T extends FileBasedKotlinClass> T create(
|
||||
@NotNull byte[] fileContents,
|
||||
@NotNull Function2<JvmClassName, KotlinClassHeader, T> factory
|
||||
@NotNull Function3<JvmClassName, KotlinClassHeader, InnerClassesInfo, T> factory
|
||||
) {
|
||||
final ReadKotlinClassHeaderAnnotationVisitor readHeaderVisitor = new ReadKotlinClassHeaderAnnotationVisitor();
|
||||
final Ref<JvmClassName> classNameRef = Ref.create();
|
||||
final Ref<String> classNameRef = Ref.create();
|
||||
final InnerClassesInfo innerClasses = new InnerClassesInfo();
|
||||
new ClassReader(fileContents).accept(new ClassVisitor(ASM5) {
|
||||
@Override
|
||||
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
|
||||
classNameRef.set(JvmClassName.byInternalName(name));
|
||||
classNameRef.set(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(@NotNull String name, String outerName, String innerName, int access) {
|
||||
if (outerName != null && innerName != null) {
|
||||
innerClasses.add(name, outerName, innerName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
|
||||
return convertAnnotationVisitor(readHeaderVisitor, desc);
|
||||
return convertAnnotationVisitor(readHeaderVisitor, desc, innerClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,13 +113,14 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
}
|
||||
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
|
||||
|
||||
JvmClassName className = classNameRef.get();
|
||||
String className = classNameRef.get();
|
||||
if (className == null) return null;
|
||||
|
||||
KotlinClassHeader header = readHeaderVisitor.createHeader();
|
||||
if (header == null) return null;
|
||||
|
||||
return factory.invoke(className, header);
|
||||
ClassId id = resolveNameByInternalName(className, innerClasses);
|
||||
return factory.invoke(JvmClassName.byClassId(id), header, innerClasses);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -94,7 +140,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
new ClassReader(getFileContents()).accept(new ClassVisitor(ASM5) {
|
||||
@Override
|
||||
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
|
||||
return convertAnnotationVisitor(annotationVisitor, desc);
|
||||
return convertAnnotationVisitor(annotationVisitor, desc, innerClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,13 +151,17 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static org.jetbrains.org.objectweb.asm.AnnotationVisitor convertAnnotationVisitor(@NotNull AnnotationVisitor visitor, @NotNull String desc) {
|
||||
AnnotationArgumentVisitor v = visitor.visitAnnotation(classNameFromAsmDesc(desc));
|
||||
return v == null ? null : convertAnnotationVisitor(v);
|
||||
private static org.jetbrains.org.objectweb.asm.AnnotationVisitor convertAnnotationVisitor(
|
||||
@NotNull AnnotationVisitor visitor, @NotNull String desc, @NotNull InnerClassesInfo innerClasses
|
||||
) {
|
||||
AnnotationArgumentVisitor v = visitor.visitAnnotation(JvmClassName.byClassId(resolveNameByDesc(desc, innerClasses)));
|
||||
return v == null ? null : convertAnnotationVisitor(v, innerClasses);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static org.jetbrains.org.objectweb.asm.AnnotationVisitor convertAnnotationVisitor(@NotNull final AnnotationArgumentVisitor v) {
|
||||
private static org.jetbrains.org.objectweb.asm.AnnotationVisitor convertAnnotationVisitor(
|
||||
@NotNull final AnnotationArgumentVisitor v, @NotNull final InnerClassesInfo innerClasses
|
||||
) {
|
||||
return new org.jetbrains.org.objectweb.asm.AnnotationVisitor(ASM5) {
|
||||
@Override
|
||||
public void visit(String name, @NotNull Object value) {
|
||||
@@ -129,7 +179,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, @NotNull String desc, @NotNull String value) {
|
||||
arv.visitEnum(classNameFromAsmDesc(desc), Name.identifier(value));
|
||||
arv.visitEnum(JvmClassName.byClassId(resolveNameByDesc(desc, innerClasses)), Name.identifier(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,7 +191,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, @NotNull String desc, @NotNull String value) {
|
||||
v.visitEnum(Name.identifier(name), classNameFromAsmDesc(desc), Name.identifier(value));
|
||||
v.visitEnum(Name.identifier(name), JvmClassName.byClassId(resolveNameByDesc(desc, innerClasses)), Name.identifier(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,7 +212,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
return new FieldVisitor(ASM5) {
|
||||
@Override
|
||||
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
|
||||
return convertAnnotationVisitor(v, desc);
|
||||
return convertAnnotationVisitor(v, desc, innerClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,13 +230,13 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
return new MethodVisitor(ASM5) {
|
||||
@Override
|
||||
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
|
||||
return convertAnnotationVisitor(v, desc);
|
||||
return convertAnnotationVisitor(v, desc, innerClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(int parameter, @NotNull String desc, boolean visible) {
|
||||
AnnotationArgumentVisitor av = v.visitParameterAnnotation(parameter, classNameFromAsmDesc(desc));
|
||||
return av == null ? null : convertAnnotationVisitor(av);
|
||||
AnnotationArgumentVisitor av = v.visitParameterAnnotation(parameter, JvmClassName.byClassId(resolveNameByDesc(desc, innerClasses)));
|
||||
return av == null ? null : convertAnnotationVisitor(av, innerClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,9 +249,31 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JvmClassName classNameFromAsmDesc(@NotNull String desc) {
|
||||
private static ClassId resolveNameByDesc(@NotNull String desc, @NotNull InnerClassesInfo innerClasses) {
|
||||
assert desc.startsWith("L") && desc.endsWith(";") : "Not a JVM descriptor: " + desc;
|
||||
return JvmClassName.byInternalName(desc.substring(1, desc.length() - 1));
|
||||
String name = desc.substring(1, desc.length() - 1);
|
||||
return resolveNameByInternalName(name, innerClasses);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ClassId resolveNameByInternalName(@NotNull String name, @NotNull InnerClassesInfo innerClasses) {
|
||||
List<String> classes = new ArrayList<String>(1);
|
||||
|
||||
while (true) {
|
||||
OuterAndInnerName outer = innerClasses.get(name);
|
||||
if (outer == null) break;
|
||||
classes.add(outer.innerSimpleName);
|
||||
name = outer.outerInternalName;
|
||||
}
|
||||
|
||||
FqName outermostClassFqName = new FqName(name.replace('/', '.'));
|
||||
classes.add(outermostClassFqName.shortName().asString());
|
||||
|
||||
Collections.reverse(classes);
|
||||
|
||||
FqName packageFqName = outermostClassFqName.parent();
|
||||
FqNameUnsafe relativeClassName = FqNameUnsafe.fromSegments(classes);
|
||||
return new ClassId(packageFqName, relativeClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+17
-9
@@ -19,7 +19,7 @@ package org.jetbrains.jet.lang.resolve.kotlin;
|
||||
import com.intellij.ide.highlighter.JavaClassFileType;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import kotlin.Function2;
|
||||
import kotlin.Function3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||
@@ -33,8 +33,13 @@ public final class VirtualFileKotlinClass extends FileBasedKotlinClass {
|
||||
|
||||
private final VirtualFile file;
|
||||
|
||||
private VirtualFileKotlinClass(@NotNull VirtualFile file, @NotNull JvmClassName className, @NotNull KotlinClassHeader classHeader) {
|
||||
super(className, classHeader);
|
||||
private VirtualFileKotlinClass(
|
||||
@NotNull VirtualFile file,
|
||||
@NotNull JvmClassName className,
|
||||
@NotNull KotlinClassHeader classHeader,
|
||||
@NotNull InnerClassesInfo innerClasses
|
||||
) {
|
||||
super(className, classHeader, innerClasses);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@@ -42,12 +47,15 @@ public final class VirtualFileKotlinClass extends FileBasedKotlinClass {
|
||||
/* package */ static VirtualFileKotlinClass create(@NotNull final VirtualFile file) {
|
||||
assert file.getFileType() == JavaClassFileType.INSTANCE : "Trying to read binary data from a non-class file " + file;
|
||||
try {
|
||||
return create(file.contentsToByteArray(), new Function2<JvmClassName, KotlinClassHeader, VirtualFileKotlinClass>() {
|
||||
@Override
|
||||
public VirtualFileKotlinClass invoke(JvmClassName name, KotlinClassHeader header) {
|
||||
return new VirtualFileKotlinClass(file, name, header);
|
||||
}
|
||||
});
|
||||
return create(file.contentsToByteArray(),
|
||||
new Function3<JvmClassName, KotlinClassHeader, InnerClassesInfo, VirtualFileKotlinClass>() {
|
||||
@Override
|
||||
public VirtualFileKotlinClass invoke(
|
||||
JvmClassName name, KotlinClassHeader header, InnerClassesInfo innerClasses
|
||||
) {
|
||||
return new VirtualFileKotlinClass(file, name, header, innerClasses);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.warn(renderFileReadingErrorMessage(file));
|
||||
|
||||
Reference in New Issue
Block a user