From 1f8b2cef523eaf31ddee7f0aed6b20d9522f5507 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 15 Sep 2014 10:55:31 +0400 Subject: [PATCH] Read InnerClasses attribute value in FileBasedKotlinClass Resolve names mentioned in class declarations based on this information --- .../resolve/kotlin/FileBasedKotlinClass.java | 114 ++++++++++++++---- .../kotlin/VirtualFileKotlinClass.java | 26 ++-- .../jet/lang/resolve/name/FqNameUnsafe.java | 3 +- .../jps/incremental/LocalFileKotlinClass.kt | 9 +- 4 files changed, 116 insertions(+), 36 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/FileBasedKotlinClass.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/FileBasedKotlinClass.java index 574825ab3a0..bd01525249d 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/FileBasedKotlinClass.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/FileBasedKotlinClass.java @@ -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 map = null; + + public void add(@NotNull String name, @NotNull String outerName, @NotNull String innerName) { + if (map == null) { + map = new HashMap(); + } + 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 create( @NotNull byte[] fileContents, - @NotNull Function2 factory + @NotNull Function3 factory ) { final ReadKotlinClassHeaderAnnotationVisitor readHeaderVisitor = new ReadKotlinClassHeaderAnnotationVisitor(); - final Ref classNameRef = Ref.create(); + final Ref 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 classes = new ArrayList(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 diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/VirtualFileKotlinClass.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/VirtualFileKotlinClass.java index a4f4ed0c460..e9386d25d3e 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/VirtualFileKotlinClass.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/VirtualFileKotlinClass.java @@ -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() { - @Override - public VirtualFileKotlinClass invoke(JvmClassName name, KotlinClassHeader header) { - return new VirtualFileKotlinClass(file, name, header); - } - }); + return create(file.contentsToByteArray(), + new Function3() { + @Override + public VirtualFileKotlinClass invoke( + JvmClassName name, KotlinClassHeader header, InnerClassesInfo innerClasses + ) { + return new VirtualFileKotlinClass(file, name, header, innerClasses); + } + }); } catch (Throwable e) { LOG.warn(renderFileReadingErrorMessage(file)); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/name/FqNameUnsafe.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/name/FqNameUnsafe.java index 39fa87f2ae3..11c2b8e4221 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/name/FqNameUnsafe.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/name/FqNameUnsafe.java @@ -255,12 +255,11 @@ public final class FqNameUnsafe extends FqNameBase { } @NotNull - public static FqNameUnsafe fromSegments(@NotNull List names) { + public static FqNameUnsafe fromSegments(@NotNull List names) { return new FqNameUnsafe(UtilsPackage.join(names, ".")); } - @NotNull public static FqNameUnsafe topLevel(@NotNull Name shortName) { return new FqNameUnsafe(shortName.asString(), FqName.ROOT.toUnsafe(), shortName); diff --git a/jps-plugin/src/org/jetbrains/jet/jps/incremental/LocalFileKotlinClass.kt b/jps-plugin/src/org/jetbrains/jet/jps/incremental/LocalFileKotlinClass.kt index dd55ccff47a..a414e46deb4 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/incremental/LocalFileKotlinClass.kt +++ b/jps-plugin/src/org/jetbrains/jet/jps/incremental/LocalFileKotlinClass.kt @@ -25,15 +25,16 @@ class LocalFileKotlinClass private( private val file: File, private val fileContents: ByteArray, className: JvmClassName, - classHeader: KotlinClassHeader -) : FileBasedKotlinClass(className, classHeader) { + classHeader: KotlinClassHeader, + innerClasses: FileBasedKotlinClass.InnerClassesInfo +) : FileBasedKotlinClass(className, classHeader, innerClasses) { class object { fun create(file: File): LocalFileKotlinClass? { val fileContents = file.readBytes() return FileBasedKotlinClass.create(fileContents) { - className, classHeader -> - LocalFileKotlinClass(file, fileContents, className, classHeader) + className, classHeader, innerClasses -> + LocalFileKotlinClass(file, fileContents, className, classHeader, innerClasses) } } }