Deserialize annotations on class members

Make 'Callable' message of descriptors.proto extensible, extend it in
java_descriptors.proto with a JVM signature of the member. This is needed in
order for annotation deserializer to find out which member in the compiled
bytecode corresponds to which descriptor in the hierarchy.

Create a new module 'serialization.java' containing everything related to
Java-specific serialization of descriptors.

Add an extension point to DescriptorSerializer, allowing to perform
platform-specific serialization on a callable
This commit is contained in:
Alexander Udalov
2013-07-05 22:38:46 +04:00
parent d0635aac3e
commit 67ca7b78c4
26 changed files with 368 additions and 99 deletions
+1
View File
@@ -13,6 +13,7 @@
<orderEntry type="module" module-name="runtime" />
<orderEntry type="library" name="javax.inject" level="project" />
<orderEntry type="module" module-name="serialization" />
<orderEntry type="module" module-name="serialization.java" />
</component>
</module>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="protobuf-java" level="project" />
<orderEntry type="module" module-name="serialization" />
<orderEntry type="library" name="annotations" level="project" />
</component>
</module>
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.descriptors.serialization;
import "compiler/frontend/serialization/src/descriptors.proto";
option java_outer_classname = "JavaProtoBuf";
option optimize_for = LITE_RUNTIME;
extend Callable {
optional string java_signature = 100;
}
@@ -0,0 +1,29 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: compiler/frontend.java/serialization.java/src/java_descriptors.proto
package org.jetbrains.jet.descriptors.serialization;
public final class JavaProtoBuf {
private JavaProtoBuf() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
registry.add(org.jetbrains.jet.descriptors.serialization.JavaProtoBuf.javaSignature);
}
public static final int JAVA_SIGNATURE_FIELD_NUMBER = 100;
public static final
com.google.protobuf.GeneratedMessageLite.GeneratedExtension<
org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable,
java.lang.String> javaSignature = com.google.protobuf.GeneratedMessageLite
.newSingularGeneratedExtension(
org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable.getDefaultInstance(),
"",
null,
null,
100,
com.google.protobuf.WireFormat.FieldType.STRING);
static {
}
// @@protoc_insertion_point(outer_class_scope)
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.descriptors.serialization;
import com.google.protobuf.ExtensionRegistryLite;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.jetbrains.jet.descriptors.serialization.JavaProtoBuf.javaSignature;
public class JavaProtoBufUtil {
private JavaProtoBufUtil() {
}
@Nullable
public static String loadJavaSignature(@NotNull ProtoBuf.Callable callable) {
return callable.hasExtension(javaSignature) ? callable.getExtension(javaSignature) : null;
}
public static void saveJavaSignature(@NotNull ProtoBuf.Callable.Builder callable, @NotNull String signature) {
callable.setExtension(javaSignature, signature);
}
@NotNull
public static ClassData readJavaClassDataFrom(@NotNull byte[] data) {
ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
JavaProtoBuf.registerAllExtensions(registry);
return ClassData.read(data, registry);
}
}
@@ -19,10 +19,9 @@ package org.jetbrains.jet.lang.resolve.java.resolver;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.AnnotationVisitor;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import org.jetbrains.jet.descriptors.serialization.JavaProtoBufUtil;
import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationDeserializer;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
@@ -38,8 +37,7 @@ import org.jetbrains.jet.utils.ExceptionUtils;
import javax.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import static org.jetbrains.asm4.ClassReader.*;
import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN;
@@ -62,20 +60,8 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
@NotNull
@Override
public List<AnnotationDescriptor> loadClassAnnotations(
@NotNull ClassDescriptor descriptor,
@NotNull ProtoBuf.Class classProto
) {
FqName fqName = kotlinFqNameToJavaFqName(naiveKotlinFqName(descriptor));
PsiClass psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE /* TODO: ?! */);
if (psiClass == null) {
throw new IllegalStateException("Psi class is not found for class: " + descriptor);
}
VirtualFile virtualFile = getVirtualFile(psiClass, fqName, (ClassOrNamespaceDescriptor) descriptor.getContainingDeclaration());
if (virtualFile == null) {
throw new IllegalStateException("Virtual file is not found for class: " + descriptor) ;
}
public List<AnnotationDescriptor> loadClassAnnotations(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class classProto) {
VirtualFile virtualFile = findVirtualFileByDescriptor(descriptor);
try {
return loadClassAnnotationsFromFile(virtualFile);
}
@@ -84,6 +70,20 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
}
}
@NotNull
private VirtualFile findVirtualFileByDescriptor(@NotNull ClassDescriptor descriptor) {
FqName fqName = kotlinFqNameToJavaFqName(naiveKotlinFqName(descriptor));
PsiClass psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE /* TODO: ?! */);
if (psiClass == null) {
throw new IllegalStateException("Psi class is not found for class: " + descriptor);
}
VirtualFile virtualFile = getVirtualFile(psiClass, fqName, (ClassOrNamespaceDescriptor) descriptor.getContainingDeclaration());
if (virtualFile == null) {
throw new IllegalStateException("Virtual file is not found for class: " + descriptor) ;
}
return virtualFile;
}
@NotNull
private List<AnnotationDescriptor> loadClassAnnotationsFromFile(@NotNull VirtualFile virtualFile) throws IOException {
final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
@@ -91,12 +91,7 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
new ClassReader(virtualFile.getInputStream()).accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (ignoreAnnotation(desc)) return null;
FqName annotationFqName = convertJvmDescriptorToFqName(desc);
ClassDescriptor annotationClass = javaClassResolver.resolveClass(annotationFqName, ERROR_IF_FOUND_IN_KOTLIN);
assert annotationClass != null : "Annotation class is not found: " + desc;
return resolveAnnotation(annotationClass, result);
return resolveAnnotation(desc, result);
}
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
@@ -115,11 +110,13 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
return new FqName(fqName);
}
@NotNull
private static AnnotationVisitor resolveAnnotation(
@NotNull final ClassDescriptor annotationClass,
@NotNull final List<AnnotationDescriptor> result
) {
@Nullable
private AnnotationVisitor resolveAnnotation(@NotNull String desc, @NotNull final List<AnnotationDescriptor> result) {
if (ignoreAnnotation(desc)) return null;
FqName annotationFqName = convertJvmDescriptorToFqName(desc);
final ClassDescriptor annotationClass = javaClassResolver.resolveClass(annotationFqName, ERROR_IF_FOUND_IN_KOTLIN);
assert annotationClass != null : "Annotation class is not found: " + desc;
final AnnotationDescriptor annotation = new AnnotationDescriptor();
annotation.setAnnotationType(annotationClass.getDefaultType());
@@ -146,8 +143,56 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
@NotNull
@Override
public List<AnnotationDescriptor> loadCallableAnnotations(@NotNull ProtoBuf.Callable callableProto) {
throw new UnsupportedOperationException(); // TODO
public List<AnnotationDescriptor> loadCallableAnnotations(
@NotNull ClassOrNamespaceDescriptor container,
@NotNull ProtoBuf.Callable callableProto
) {
if (!(container instanceof ClassDescriptor)) return Collections.emptyList(); // TODO: NamespaceDescriptor
VirtualFile file = findVirtualFileByDescriptor((ClassDescriptor) container);
try {
// TODO: calculate this only once for each container
Map<String, List<AnnotationDescriptor>> memberAnnotations = loadMemberAnnotationsFromFile(file);
String signature = JavaProtoBufUtil.loadJavaSignature(callableProto);
if (signature == null) return Collections.emptyList();
List<AnnotationDescriptor> annotations = memberAnnotations.get(signature);
return annotations == null ? Collections.<AnnotationDescriptor>emptyList() : annotations;
}
catch (IOException e) {
throw ExceptionUtils.rethrow(e);
}
}
@NotNull
private Map<String, List<AnnotationDescriptor>> loadMemberAnnotationsFromFile(@NotNull VirtualFile file) throws IOException {
final Map<String, List<AnnotationDescriptor>> memberAnnotations = new HashMap<String, List<AnnotationDescriptor>>();
new ClassReader(file.getInputStream()).accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
final String methodSignature = name + desc;
final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
return new MethodVisitor(Opcodes.ASM4) {
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return resolveAnnotation(desc, result);
}
@Override
public void visitEnd() {
if (!result.isEmpty()) {
memberAnnotations.put(methodSignature, result);
}
}
};
}
}, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
return memberAnnotations;
}
@NotNull
@@ -155,10 +200,4 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
public List<AnnotationDescriptor> loadValueParameterAnnotations(@NotNull ProtoBuf.Callable.ValueParameter parameterProto) {
throw new UnsupportedOperationException(); // TODO
}
@NotNull
@Override
public List<AnnotationDescriptor> loadSetterAnnotations(@NotNull ProtoBuf.Callable callableProto) {
throw new UnsupportedOperationException(); // TODO
}
}
@@ -24,10 +24,7 @@ import org.jetbrains.asm4.AnnotationVisitor;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.jet.descriptors.serialization.ClassData;
import org.jetbrains.jet.descriptors.serialization.ClassId;
import org.jetbrains.jet.descriptors.serialization.DescriptorFinder;
import org.jetbrains.jet.descriptors.serialization.PackageData;
import org.jetbrains.jet.descriptors.serialization.*;
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedClassDescriptor;
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPackageMemberScope;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
@@ -141,7 +138,7 @@ public final class DeserializedDescriptorResolver {
private static ClassData readClassDataFromClassFile(@NotNull VirtualFile virtualFile) {
byte[] data = getKotlinInfoDataFromClassFile(virtualFile);
if (data == null) return null;
return ClassData.read(data);
return JavaProtoBufUtil.readJavaClassDataFrom(data);
}
@Nullable