diff --git a/compiler/frontend.java/frontend.java.iml b/compiler/frontend.java/frontend.java.iml index 5cd8c2e9746..de19dcc7a1b 100644 --- a/compiler/frontend.java/frontend.java.iml +++ b/compiler/frontend.java/frontend.java.iml @@ -14,6 +14,7 @@ + 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 939b5484818..91c70499799 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 @@ -18,6 +18,19 @@ package org.jetbrains.jet.lang.resolve.kotlin; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.asm4.ClassReader; +import org.jetbrains.asm4.ClassVisitor; +import org.jetbrains.asm4.FieldVisitor; +import org.jetbrains.asm4.MethodVisitor; +import org.jetbrains.jet.lang.resolve.java.JvmClassName; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.utils.ExceptionUtils; + +import java.io.IOException; + +import static org.jetbrains.asm4.ClassReader.*; +import static org.jetbrains.asm4.Opcodes.ASM4; public class VirtualFileKotlinClass implements KotlinJvmBinaryClass { private final VirtualFile file; @@ -32,6 +45,104 @@ public class VirtualFileKotlinClass implements KotlinJvmBinaryClass { return file; } + @Override + public void loadClassAnnotations(@NotNull final AnnotationVisitor annotationVisitor) { + try { + new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) { + @Override + public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return convertAnnotationVisitor(annotationVisitor, desc); + } + + @Override + public void visitEnd() { + annotationVisitor.visitEnd(); + } + }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES); + } + catch (IOException e) { + throw ExceptionUtils.rethrow(e); + } + } + + @Nullable + private static org.jetbrains.asm4.AnnotationVisitor convertAnnotationVisitor( + @NotNull AnnotationVisitor visitor, + @NotNull String desc + ) { + final AnnotationArgumentVisitor v = visitor.visitAnnotation(classNameFromAsmDesc(desc)); + if (v == null) return null; + + return new org.jetbrains.asm4.AnnotationVisitor(ASM4) { + @Override + public void visit(String name, Object value) { + v.visit(Name.identifier(name), value); + } + + @Override + public void visitEnum(String name, String desc, String value) { + v.visitEnum(Name.identifier(name), classNameFromAsmDesc(desc), Name.identifier(value)); + } + + @Override + public void visitEnd() { + v.visitEnd(); + } + }; + } + + @Override + public void loadMemberAnnotations(@NotNull final MemberVisitor memberVisitor) { + try { + new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) { + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + final AnnotationVisitor v = memberVisitor.visitField(Name.guess(name), desc); + if (v == null) return null; + + return new FieldVisitor(ASM4) { + @Override + public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return convertAnnotationVisitor(v, desc); + } + + @Override + public void visitEnd() { + v.visitEnd(); + } + }; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + final AnnotationVisitor v = memberVisitor.visitMethod(Name.guess(name), desc); + if (v == null) return null; + + return new MethodVisitor(ASM4) { + @Override + public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return convertAnnotationVisitor(v, desc); + } + + @Override + public void visitEnd() { + super.visitEnd(); + } + }; + } + }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES); + } + catch (IOException e) { + throw ExceptionUtils.rethrow(e); + } + } + + @NotNull + private static JvmClassName classNameFromAsmDesc(@NotNull String desc) { + assert desc.startsWith("L") && desc.endsWith(";") : "Not a JVM descriptor: " + desc; + return JvmClassName.byInternalName(desc.substring(1, desc.length() - 1)); + } + @Override public int hashCode() { return file.hashCode(); diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/AnnotationDescriptorDeserializer.java b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/AnnotationDescriptorDeserializer.java index 8616b795382..8ba6bb3dd1c 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/AnnotationDescriptorDeserializer.java +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/AnnotationDescriptorDeserializer.java @@ -19,7 +19,6 @@ package org.jetbrains.jet.lang.resolve.kotlin; import com.intellij.openapi.diagnostic.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.asm4.*; import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf; import org.jetbrains.jet.descriptors.serialization.NameResolver; import org.jetbrains.jet.descriptors.serialization.ProtoBuf; @@ -32,6 +31,7 @@ import org.jetbrains.jet.lang.resolve.constants.EnumValue; import org.jetbrains.jet.lang.resolve.constants.ErrorValue; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames; +import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils; import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationArgumentResolver; @@ -47,7 +47,6 @@ import javax.inject.Inject; import java.io.IOException; import java.util.*; -import static org.jetbrains.asm4.ClassReader.*; import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.IGNORE_KOTLIN_SOURCES; import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.kotlinFqNameToJavaFqName; import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.naiveKotlinFqName; @@ -123,56 +122,54 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer private List loadClassAnnotationsFromClass(@NotNull KotlinJvmBinaryClass kotlinClass) throws IOException { final List result = new ArrayList(); - new ClassReader(kotlinClass.getFile().contentsToByteArray()).accept(new ClassVisitor(Opcodes.ASM4) { + kotlinClass.loadClassAnnotations(new KotlinJvmBinaryClass.AnnotationVisitor() { + @Nullable @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return resolveAnnotation(desc, result); + public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) { + return resolveAnnotation(className, result); } - }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES); + + @Override + public void visitEnd() { + } + }); return result; } - private static boolean ignoreAnnotation(@NotNull String desc) { + private static boolean ignoreAnnotation(@NotNull JvmClassName className) { // TODO: JvmAbi.JETBRAINS_NOT_NULL_ANNOTATION ? - return desc.equals(JvmAnnotationNames.KOTLIN_CLASS.getDescriptor()) - || desc.equals(JvmAnnotationNames.KOTLIN_PACKAGE.getDescriptor()) - || desc.startsWith("Ljet/runtime/typeinfo/"); - } - - @NotNull - private static FqName convertJvmDescriptorToFqName(@NotNull String desc) { - assert desc.startsWith("L") && desc.endsWith(";") : "Not a JVM descriptor: " + desc; - String fqName = desc.substring(1, desc.length() - 1).replace('$', '.').replace('/', '.'); - return new FqName(fqName); + return className.equals(JvmAnnotationNames.KOTLIN_CLASS) + || className.equals(JvmAnnotationNames.KOTLIN_PACKAGE) + || className.getInternalName().startsWith("jet/runtime/typeinfo/"); } @Nullable - private AnnotationVisitor resolveAnnotation(@NotNull String desc, @NotNull final List result) { - if (ignoreAnnotation(desc)) return null; + private KotlinJvmBinaryClass.AnnotationArgumentVisitor resolveAnnotation( + @NotNull JvmClassName className, + @NotNull final List result + ) { + if (ignoreAnnotation(className)) return null; - FqName annotationFqName = convertJvmDescriptorToFqName(desc); - final ClassDescriptor annotationClass = resolveAnnotationClass(annotationFqName); + final ClassDescriptor annotationClass = resolveAnnotationClass(className); final AnnotationDescriptor annotation = new AnnotationDescriptor(); annotation.setAnnotationType(annotationClass.getDefaultType()); - return new AnnotationVisitor(Opcodes.ASM4) { - // TODO: arrays, annotations, java.lang.Class + return new KotlinJvmBinaryClass.AnnotationArgumentVisitor() { @Override - public void visit(String name, Object value) { + public void visit(@NotNull Name name, @Nullable Object value) { CompileTimeConstant argument = JavaAnnotationArgumentResolver.resolveCompileTimeConstantValue(value, null); setArgumentValueByName(name, argument != null ? argument : ErrorValue.create("Unsupported annotation argument: " + name)); } @Override - public void visitEnum(String name, String desc, String value) { - FqName fqName = convertJvmDescriptorToFqName(desc); - setArgumentValueByName(name, enumEntryValue(fqName, Name.identifier(value))); + public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) { + setArgumentValueByName(name, enumEntryValue(enumClassName, enumEntryName)); } @NotNull - private CompileTimeConstant enumEntryValue(@NotNull FqName enumFqName, @NotNull Name name) { - ClassDescriptor enumClass = javaClassResolver.resolveClass(enumFqName, IGNORE_KOTLIN_SOURCES); + private CompileTimeConstant enumEntryValue(@NotNull JvmClassName enumClassName, @NotNull Name name) { + ClassDescriptor enumClass = javaClassResolver.resolveClass(enumClassName.getFqName(), IGNORE_KOTLIN_SOURCES); if (enumClass != null && enumClass.getKind() == ClassKind.ENUM_CLASS) { ClassDescriptor classObject = enumClass.getClassObjectDescriptor(); if (classObject != null) { @@ -185,7 +182,7 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer } } } - return ErrorValue.create("Unresolved enum entry: " + enumFqName + "." + name); + return ErrorValue.create("Unresolved enum entry: " + enumClassName.getFqName() + "." + name); } @Override @@ -193,9 +190,8 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer result.add(annotation); } - private void setArgumentValueByName(@NotNull String name, @NotNull CompileTimeConstant argumentValue) { - ValueParameterDescriptor parameter = - DescriptorResolverUtils.getAnnotationParameterByName(Name.identifier(name), annotationClass); + private void setArgumentValueByName(@NotNull Name name, @NotNull CompileTimeConstant argumentValue) { + ValueParameterDescriptor parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass); if (parameter != null) { annotation.setValueArgument(parameter, argumentValue); } @@ -204,8 +200,8 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer } @NotNull - private ClassDescriptor resolveAnnotationClass(@NotNull FqName fqName) { - ClassDescriptor annotationClass = javaClassResolver.resolveClass(fqName, IGNORE_KOTLIN_SOURCES); + private ClassDescriptor resolveAnnotationClass(@NotNull JvmClassName className) { + ClassDescriptor annotationClass = javaClassResolver.resolveClass(className.getFqName(), IGNORE_KOTLIN_SOURCES); return annotationClass != null ? annotationClass : ErrorUtils.getErrorClass(); } @@ -301,11 +297,11 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer JavaProtoBuf.JavaFieldSignature field = propertySignature.getField(); String type = new SignatureDeserializer(nameResolver).typeDescriptor(field.getType()); Name name = nameResolver.getName(field.getName()); - return MemberSignature.fromFieldNameAndDesc(name.asString(), type); + return MemberSignature.fromFieldNameAndDesc(name, type); } else if (propertySignature.hasSyntheticMethodName()) { Name name = nameResolver.getName(propertySignature.getSyntheticMethodName()); - return MemberSignature.fromMethodNameAndDesc(name.asString(), JvmAbi.ANNOTATED_PROPERTY_METHOD_SIGNATURE); + return MemberSignature.fromMethodNameAndDesc(name, JvmAbi.ANNOTATED_PROPERTY_METHOD_SIGNATURE); } } break; @@ -319,47 +315,39 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer final Map> memberAnnotations = new HashMap>(); - new ClassReader(kotlinClass.getFile().contentsToByteArray()).accept(new ClassVisitor(Opcodes.ASM4) { + kotlinClass.loadMemberAnnotations(new KotlinJvmBinaryClass.MemberVisitor() { + @Nullable @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - final MemberSignature methodSignature = MemberSignature.fromMethodNameAndDesc(name, desc); - final List result = new ArrayList(); + public KotlinJvmBinaryClass.AnnotationVisitor visitMethod(@NotNull Name name, @NotNull String desc) { + return annotationVisitor(MemberSignature.fromMethodNameAndDesc(name, desc)); + } - return new MethodVisitor(Opcodes.ASM4) { + @Nullable + @Override + public KotlinJvmBinaryClass.AnnotationVisitor visitField(@NotNull Name name, @NotNull String desc) { + return annotationVisitor(MemberSignature.fromFieldNameAndDesc(name, desc)); + } + + @NotNull + private KotlinJvmBinaryClass.AnnotationVisitor annotationVisitor(@NotNull final MemberSignature signature) { + return new KotlinJvmBinaryClass.AnnotationVisitor() { + private final List result = new ArrayList(); + + @Nullable @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return resolveAnnotation(desc, result); + public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) { + return resolveAnnotation(className, result); } @Override public void visitEnd() { if (!result.isEmpty()) { - memberAnnotations.put(methodSignature, result); + memberAnnotations.put(signature, result); } } }; } - - @Override - public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { - final MemberSignature fieldSignature = MemberSignature.fromFieldNameAndDesc(name, desc); - final List result = new ArrayList(); - - return new FieldVisitor(Opcodes.ASM4) { - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return resolveAnnotation(desc, result); - } - - @Override - public void visitEnd() { - if (!result.isEmpty()) { - memberAnnotations.put(fieldSignature, result); - } - } - }; - } - }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES); + }); return memberAnnotations; } @@ -374,13 +362,13 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer } @NotNull - public static MemberSignature fromMethodNameAndDesc(@NotNull String name, @NotNull String desc) { - return new MemberSignature(name + desc); + public static MemberSignature fromMethodNameAndDesc(@NotNull Name name, @NotNull String desc) { + return new MemberSignature(name.asString() + desc); } @NotNull - public static MemberSignature fromFieldNameAndDesc(@NotNull String name, @NotNull String desc) { - return new MemberSignature(name + "#" + desc); + public static MemberSignature fromFieldNameAndDesc(@NotNull Name name, @NotNull String desc) { + return new MemberSignature(name.asString() + "#" + desc); } @Override @@ -411,7 +399,7 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer @NotNull public MemberSignature methodSignature(@NotNull JavaProtoBuf.JavaMethodSignature signature) { - String name = nameResolver.getName(signature.getName()).asString(); + Name name = nameResolver.getName(signature.getName()); StringBuilder sb = new StringBuilder(); sb.append('('); diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/KotlinJvmBinaryClass.java b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/KotlinJvmBinaryClass.java index b4b70d89477..a4ba321d8eb 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/KotlinJvmBinaryClass.java +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/kotlin/KotlinJvmBinaryClass.java @@ -18,8 +18,41 @@ package org.jetbrains.jet.lang.resolve.kotlin; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.resolve.java.JvmClassName; +import org.jetbrains.jet.lang.resolve.name.Name; public interface KotlinJvmBinaryClass { @NotNull VirtualFile getFile(); + + void loadClassAnnotations(@NotNull AnnotationVisitor visitor); + + void loadMemberAnnotations(@NotNull MemberVisitor visitor); + + interface MemberVisitor { + // TODO: abstract signatures for methods and fields instead of ASM 'desc' strings? + + @Nullable + AnnotationVisitor visitMethod(@NotNull Name name, @NotNull String desc); + + @Nullable + AnnotationVisitor visitField(@NotNull Name name, @NotNull String desc); + } + + interface AnnotationVisitor { + @Nullable + AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className); + + void visitEnd(); + } + + interface AnnotationArgumentVisitor { + // TODO: arrays, annotations, java.lang.Class + void visit(@NotNull Name name, @Nullable Object value); + + void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName); + + void visitEnd(); + } }