Read InnerClasses attribute value in FileBasedKotlinClass

Resolve names mentioned in class declarations based on this information
This commit is contained in:
Alexander Udalov
2014-09-15 10:55:31 +04:00
parent e3b01f073f
commit 1f8b2cef52
4 changed files with 116 additions and 36 deletions
@@ -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
@@ -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));