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; package org.jetbrains.jet.lang.resolve.kotlin;
import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.Ref;
import kotlin.Function2; import kotlin.Function3;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader; import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader;
import org.jetbrains.jet.lang.resolve.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor; 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.jet.lang.resolve.name.Name;
import org.jetbrains.org.objectweb.asm.ClassReader; import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor; import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.FieldVisitor; import org.jetbrains.org.objectweb.asm.FieldVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor; 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.ClassReader.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.ASM5; import static org.jetbrains.org.objectweb.asm.Opcodes.ASM5;
public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass { public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
private final JvmClassName className; private final JvmClassName className;
private final KotlinClassHeader classHeader; 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.className = className;
this.classHeader = classHeader; 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 @NotNull
@@ -47,19 +84,27 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
@Nullable @Nullable
protected static <T extends FileBasedKotlinClass> T create( protected static <T extends FileBasedKotlinClass> T create(
@NotNull byte[] fileContents, @NotNull byte[] fileContents,
@NotNull Function2<JvmClassName, KotlinClassHeader, T> factory @NotNull Function3<JvmClassName, KotlinClassHeader, InnerClassesInfo, T> factory
) { ) {
final ReadKotlinClassHeaderAnnotationVisitor readHeaderVisitor = new ReadKotlinClassHeaderAnnotationVisitor(); 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) { new ClassReader(fileContents).accept(new ClassVisitor(ASM5) {
@Override @Override
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) { 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 @Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) { public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
return convertAnnotationVisitor(readHeaderVisitor, desc); return convertAnnotationVisitor(readHeaderVisitor, desc, innerClasses);
} }
@Override @Override
@@ -68,13 +113,14 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
} }
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES); }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
JvmClassName className = classNameRef.get(); String className = classNameRef.get();
if (className == null) return null; if (className == null) return null;
KotlinClassHeader header = readHeaderVisitor.createHeader(); KotlinClassHeader header = readHeaderVisitor.createHeader();
if (header == null) return null; if (header == null) return null;
return factory.invoke(className, header); ClassId id = resolveNameByInternalName(className, innerClasses);
return factory.invoke(JvmClassName.byClassId(id), header, innerClasses);
} }
@NotNull @NotNull
@@ -94,7 +140,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
new ClassReader(getFileContents()).accept(new ClassVisitor(ASM5) { new ClassReader(getFileContents()).accept(new ClassVisitor(ASM5) {
@Override @Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) { public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
return convertAnnotationVisitor(annotationVisitor, desc); return convertAnnotationVisitor(annotationVisitor, desc, innerClasses);
} }
@Override @Override
@@ -105,13 +151,17 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
} }
@Nullable @Nullable
private static org.jetbrains.org.objectweb.asm.AnnotationVisitor convertAnnotationVisitor(@NotNull AnnotationVisitor visitor, @NotNull String desc) { private static org.jetbrains.org.objectweb.asm.AnnotationVisitor convertAnnotationVisitor(
AnnotationArgumentVisitor v = visitor.visitAnnotation(classNameFromAsmDesc(desc)); @NotNull AnnotationVisitor visitor, @NotNull String desc, @NotNull InnerClassesInfo innerClasses
return v == null ? null : convertAnnotationVisitor(v); ) {
AnnotationArgumentVisitor v = visitor.visitAnnotation(JvmClassName.byClassId(resolveNameByDesc(desc, innerClasses)));
return v == null ? null : convertAnnotationVisitor(v, innerClasses);
} }
@NotNull @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) { return new org.jetbrains.org.objectweb.asm.AnnotationVisitor(ASM5) {
@Override @Override
public void visit(String name, @NotNull Object value) { public void visit(String name, @NotNull Object value) {
@@ -129,7 +179,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
@Override @Override
public void visitEnum(String name, @NotNull String desc, @NotNull String value) { 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 @Override
@@ -141,7 +191,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
@Override @Override
public void visitEnum(String name, @NotNull String desc, @NotNull String value) { 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 @Override
@@ -162,7 +212,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
return new FieldVisitor(ASM5) { return new FieldVisitor(ASM5) {
@Override @Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) { public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
return convertAnnotationVisitor(v, desc); return convertAnnotationVisitor(v, desc, innerClasses);
} }
@Override @Override
@@ -180,13 +230,13 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
return new MethodVisitor(ASM5) { return new MethodVisitor(ASM5) {
@Override @Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) { public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) {
return convertAnnotationVisitor(v, desc); return convertAnnotationVisitor(v, desc, innerClasses);
} }
@Override @Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(int parameter, @NotNull String desc, boolean visible) { public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(int parameter, @NotNull String desc, boolean visible) {
AnnotationArgumentVisitor av = v.visitParameterAnnotation(parameter, classNameFromAsmDesc(desc)); AnnotationArgumentVisitor av = v.visitParameterAnnotation(parameter, JvmClassName.byClassId(resolveNameByDesc(desc, innerClasses)));
return av == null ? null : convertAnnotationVisitor(av); return av == null ? null : convertAnnotationVisitor(av, innerClasses);
} }
@Override @Override
@@ -199,9 +249,31 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
} }
@NotNull @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; 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 @Override
@@ -19,7 +19,7 @@ package org.jetbrains.jet.lang.resolve.kotlin;
import com.intellij.ide.highlighter.JavaClassFileType; import com.intellij.ide.highlighter.JavaClassFileType;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import kotlin.Function2; import kotlin.Function3;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.java.JvmClassName;
@@ -33,8 +33,13 @@ public final class VirtualFileKotlinClass extends FileBasedKotlinClass {
private final VirtualFile file; private final VirtualFile file;
private VirtualFileKotlinClass(@NotNull VirtualFile file, @NotNull JvmClassName className, @NotNull KotlinClassHeader classHeader) { private VirtualFileKotlinClass(
super(className, classHeader); @NotNull VirtualFile file,
@NotNull JvmClassName className,
@NotNull KotlinClassHeader classHeader,
@NotNull InnerClassesInfo innerClasses
) {
super(className, classHeader, innerClasses);
this.file = file; this.file = file;
} }
@@ -42,12 +47,15 @@ public final class VirtualFileKotlinClass extends FileBasedKotlinClass {
/* package */ static VirtualFileKotlinClass create(@NotNull final VirtualFile file) { /* package */ static VirtualFileKotlinClass create(@NotNull final VirtualFile file) {
assert file.getFileType() == JavaClassFileType.INSTANCE : "Trying to read binary data from a non-class file " + file; assert file.getFileType() == JavaClassFileType.INSTANCE : "Trying to read binary data from a non-class file " + file;
try { try {
return create(file.contentsToByteArray(), new Function2<JvmClassName, KotlinClassHeader, VirtualFileKotlinClass>() { return create(file.contentsToByteArray(),
@Override new Function3<JvmClassName, KotlinClassHeader, InnerClassesInfo, VirtualFileKotlinClass>() {
public VirtualFileKotlinClass invoke(JvmClassName name, KotlinClassHeader header) { @Override
return new VirtualFileKotlinClass(file, name, header); public VirtualFileKotlinClass invoke(
} JvmClassName name, KotlinClassHeader header, InnerClassesInfo innerClasses
}); ) {
return new VirtualFileKotlinClass(file, name, header, innerClasses);
}
});
} }
catch (Throwable e) { catch (Throwable e) {
LOG.warn(renderFileReadingErrorMessage(file)); LOG.warn(renderFileReadingErrorMessage(file));
@@ -255,12 +255,11 @@ public final class FqNameUnsafe extends FqNameBase {
} }
@NotNull @NotNull
public static FqNameUnsafe fromSegments(@NotNull List<Name> names) { public static FqNameUnsafe fromSegments(@NotNull List<?> names) {
return new FqNameUnsafe(UtilsPackage.join(names, ".")); return new FqNameUnsafe(UtilsPackage.join(names, "."));
} }
@NotNull @NotNull
public static FqNameUnsafe topLevel(@NotNull Name shortName) { public static FqNameUnsafe topLevel(@NotNull Name shortName) {
return new FqNameUnsafe(shortName.asString(), FqName.ROOT.toUnsafe(), shortName); return new FqNameUnsafe(shortName.asString(), FqName.ROOT.toUnsafe(), shortName);
@@ -25,15 +25,16 @@ class LocalFileKotlinClass private(
private val file: File, private val file: File,
private val fileContents: ByteArray, private val fileContents: ByteArray,
className: JvmClassName, className: JvmClassName,
classHeader: KotlinClassHeader classHeader: KotlinClassHeader,
) : FileBasedKotlinClass(className, classHeader) { innerClasses: FileBasedKotlinClass.InnerClassesInfo
) : FileBasedKotlinClass(className, classHeader, innerClasses) {
class object { class object {
fun create(file: File): LocalFileKotlinClass? { fun create(file: File): LocalFileKotlinClass? {
val fileContents = file.readBytes() val fileContents = file.readBytes()
return FileBasedKotlinClass.create(fileContents) { return FileBasedKotlinClass.create(fileContents) {
className, classHeader -> className, classHeader, innerClasses ->
LocalFileKotlinClass(file, fileContents, className, classHeader) LocalFileKotlinClass(file, fileContents, className, classHeader, innerClasses)
} }
} }
} }