Java serialization maps descriptors correctly

Instead of determining JVM method names, fields, types heuristically (mapType),
do it correctly: in the place where a member is generated, its descriptor and
signature are stored into MemberMap, which is queried later when we serialize
its container
This commit is contained in:
Alexander Udalov
2013-07-12 22:39:48 +04:00
parent 6035d89c33
commit 8dd27aa298
8 changed files with 179 additions and 61 deletions
@@ -73,10 +73,15 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
generateStaticInitializer(); generateStaticInitializer();
generateRemoveInIterator(); generateRemoveInIterator();
generateKotlinAnnotation();
} }
protected abstract void generateDeclaration(); protected abstract void generateDeclaration();
protected void generateKotlinAnnotation() {
}
protected void generateSyntheticParts() { protected void generateSyntheticParts() {
} }
@@ -27,6 +27,8 @@ import org.jetbrains.asm4.MethodVisitor;
public abstract class ClassBuilder { public abstract class ClassBuilder {
private String thisName; private String thisName;
private final MemberMap members = new MemberMap();
public static class Concrete extends ClassBuilder { public static class Concrete extends ClassBuilder {
private final ClassVisitor v; private final ClassVisitor v;
@@ -63,6 +65,11 @@ public abstract class ClassBuilder {
return getVisitor().visitMethod(access, name, desc, signature, exceptions); return getVisitor().visitMethod(access, name, desc, signature, exceptions);
} }
@NotNull
public MemberMap getMemberMap() {
return members;
}
@NotNull @NotNull
public AnnotationVisitor newAnnotation(@NotNull String desc, boolean visible) { public AnnotationVisitor newAnnotation(@NotNull String desc, boolean visible) {
return getVisitor().visitAnnotation(desc, visible); return getVisitor().visitAnnotation(desc, visible);
@@ -104,7 +104,6 @@ public class FunctionCodegen extends GenerationStateAware {
@NotNull MethodContext methodContext, @NotNull MethodContext methodContext,
@NotNull FunctionGenerationStrategy strategy @NotNull FunctionGenerationStrategy strategy
) { ) {
Method asmMethod = jvmSignature.getAsmMethod(); Method asmMethod = jvmSignature.getAsmMethod();
MethodVisitor mv = v.newMethod(origin, MethodVisitor mv = v.newMethod(origin,
@@ -113,6 +112,7 @@ public class FunctionCodegen extends GenerationStateAware {
asmMethod.getDescriptor(), asmMethod.getDescriptor(),
jvmSignature.getGenericsSignature(), jvmSignature.getGenericsSignature(),
null); null);
v.getMemberMap().recordMethodOfDescriptor(functionDescriptor, asmMethod);
AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(functionDescriptor); AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(functionDescriptor);
if (state.getClassBuilderMode() == ClassBuilderMode.SIGNATURES) return; if (state.getClassBuilderMode() == ClassBuilderMode.SIGNATURES) return;
@@ -213,15 +213,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor); AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor);
if (isTopLevelOrInnerClass(descriptor)) {
writeKotlinInfo();
}
writeClassSignatureIfNeeded(signature); writeClassSignatureIfNeeded(signature);
} }
private void writeKotlinInfo() { @Override
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(typeMapper)); protected void generateKotlinAnnotation() {
if (!isTopLevelOrInnerClass(descriptor)) return;
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(v.getMemberMap()));
ProtoBuf.Class classProto = serializer.classProto(descriptor).build(); ProtoBuf.Class classProto = serializer.classProto(descriptor).build();
@@ -16,25 +16,23 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.Type; import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.Method; import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.descriptors.serialization.JavaProtoBufUtil; import org.jetbrains.jet.descriptors.serialization.JavaProtoBufUtil;
import org.jetbrains.jet.descriptors.serialization.NameTable; import org.jetbrains.jet.descriptors.serialization.NameTable;
import org.jetbrains.jet.descriptors.serialization.ProtoBuf; import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
import org.jetbrains.jet.descriptors.serialization.SerializerExtension; import org.jetbrains.jet.descriptors.serialization.SerializerExtension;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
public class JavaSerializerExtension extends SerializerExtension { public class JavaSerializerExtension extends SerializerExtension {
private final JetTypeMapper typeMapper; private final MemberMap memberMap;
public JavaSerializerExtension(@NotNull JetTypeMapper typeMapper) { public JavaSerializerExtension(@NotNull MemberMap memberMap) {
this.typeMapper = typeMapper; this.memberMap = memberMap;
} }
// TODO: mapSignature should be done upon generation of the member instead, because we don't know enough at this point to map correctly
@Override @Override
public void serializeCallable( public void serializeCallable(
@NotNull CallableMemberDescriptor callable, @NotNull CallableMemberDescriptor callable,
@@ -42,30 +40,35 @@ public class JavaSerializerExtension extends SerializerExtension {
@NotNull NameTable nameTable @NotNull NameTable nameTable
) { ) {
if (callable instanceof FunctionDescriptor) { if (callable instanceof FunctionDescriptor) {
Method method = typeMapper.mapSignature((FunctionDescriptor) callable).getAsmMethod(); Method method = memberMap.getMethodOfDescriptor(callable);
JavaProtoBufUtil.saveMethodSignature(proto, method, nameTable); if (method != null) {
JavaProtoBufUtil.saveMethodSignature(proto, method, nameTable);
}
} }
else if (callable instanceof PropertyDescriptor) { else if (callable instanceof PropertyDescriptor) {
PropertyDescriptor property = (PropertyDescriptor) callable; PropertyDescriptor property = (PropertyDescriptor) callable;
Type type = typeMapper.mapType(property.getType());
PropertyGetterDescriptor getter = property.getGetter(); PropertyGetterDescriptor getter = property.getGetter();
PropertySetterDescriptor setter = property.getSetter(); PropertySetterDescriptor setter = property.getSetter();
Method getterMethod = getter == null ? null : typeMapper.mapGetterSignature(property, OwnerKind.IMPLEMENTATION).getAsmMethod(); Method getterMethod = getter == null ? null : memberMap.getMethodOfDescriptor(getter);
Method setterMethod = setter == null ? null : typeMapper.mapSetterSignature(property, OwnerKind.IMPLEMENTATION).getAsmMethod(); Method setterMethod = setter == null ? null : memberMap.getMethodOfDescriptor(setter);
// This is very wrong, see above todo Pair<Type, String> field = memberMap.getFieldOfProperty(property);
Type fieldType;
String fieldName; String fieldName;
String syntheticMethodName; String syntheticMethodName;
if ((getter == null || getter.isDefault()) && (setter == null || setter.isDefault())) { if (field != null) {
fieldName = property.getName().asString(); fieldType = field.first;
fieldName = field.second;
syntheticMethodName = null; syntheticMethodName = null;
} }
else { else {
fieldType = null;
fieldName = null; fieldName = null;
syntheticMethodName = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(property.getName()); syntheticMethodName = memberMap.getSyntheticMethodNameOfProperty(property);
} }
JavaProtoBufUtil.savePropertySignature(proto, type, fieldName, syntheticMethodName, getterMethod, setterMethod, nameTable); JavaProtoBufUtil.savePropertySignature(proto, fieldType, fieldName, syntheticMethodName, getterMethod, setterMethod, nameTable);
} }
} }
} }
@@ -0,0 +1,92 @@
/*
* 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.codegen;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public final class MemberMap {
private final Map<CallableMemberDescriptor, Method> methodForFunction = new HashMap<CallableMemberDescriptor, Method>();
private final Map<PropertyDescriptor, Pair<Type, String>> fieldForProperty = new HashMap<PropertyDescriptor, Pair<Type, String>>();
private final Map<PropertyDescriptor, String> syntheticMethodNameForProperty = new HashMap<PropertyDescriptor, String>();
@NotNull
public static MemberMap union(@NotNull Collection<MemberMap> maps) {
MemberMap result = new MemberMap();
for (MemberMap map : maps) {
for (Map.Entry<CallableMemberDescriptor, Method> entry : map.methodForFunction.entrySet()) {
result.recordMethodOfDescriptor(entry.getKey(), entry.getValue());
}
for (Map.Entry<PropertyDescriptor, Pair<Type, String>> entry : map.fieldForProperty.entrySet()) {
result.recordFieldOfProperty(entry.getKey(), entry.getValue().first, entry.getValue().second);
}
for (Map.Entry<PropertyDescriptor, String> entry : map.syntheticMethodNameForProperty.entrySet()) {
result.recordSyntheticMethodNameOfProperty(entry.getKey(), entry.getValue());
}
}
return result;
}
public void recordMethodOfDescriptor(@NotNull CallableMemberDescriptor descriptor, @NotNull Method method) {
Method old = methodForFunction.put(descriptor, method);
assert old == null : "Duplicate method for callable member: " + descriptor + "; " + old;
}
public void recordFieldOfProperty(@NotNull PropertyDescriptor descriptor, @NotNull Type type, @NotNull String fieldName) {
Pair<Type, String> old = fieldForProperty.put(descriptor, Pair.create(type, fieldName));
assert old == null : "Duplicate field for property: " + descriptor + "; " + old;
}
public void recordSyntheticMethodNameOfProperty(@NotNull PropertyDescriptor descriptor, @NotNull String name) {
String old = syntheticMethodNameForProperty.put(descriptor, name);
assert old == null : "Duplicate synthetic method for property: " + descriptor + "; " + old;
}
@Nullable
public Method getMethodOfDescriptor(@NotNull CallableMemberDescriptor descriptor) {
return methodForFunction.get(descriptor);
}
@Nullable
public Pair<Type, String> getFieldOfProperty(@NotNull PropertyDescriptor descriptor) {
return fieldForProperty.get(descriptor);
}
@Nullable
public String getSyntheticMethodNameOfProperty(@NotNull PropertyDescriptor descriptor) {
return syntheticMethodNameForProperty.get(descriptor);
}
@Override
public String toString() {
return "Functions: " + methodForFunction.size() +
", fields: " + fieldForProperty.size() +
", synthetic methods: " + syntheticMethodNameForProperty.size();
}
}
@@ -24,6 +24,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.util.PathUtil; import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.AnnotationVisitor; import org.jetbrains.asm4.AnnotationVisitor;
import org.jetbrains.asm4.MethodVisitor; import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Type; import org.jetbrains.asm4.Type;
@@ -47,6 +48,7 @@ import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.name.Name;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -96,7 +98,7 @@ public class NamespaceCodegen extends MemberCodegen {
}); });
} }
public void generate(CompilationErrorHandler errorHandler) { public void generate(@NotNull CompilationErrorHandler errorHandler) {
if (shouldGenerateNSClass(files)) { if (shouldGenerateNSClass(files)) {
AnnotationVisitor packageClassAnnotation = AnnotationVisitor packageClassAnnotation =
v.getClassBuilder().newAnnotation(JvmStdlibNames.JET_PACKAGE_CLASS.getDescriptor(), true); v.getClassBuilder().newAnnotation(JvmStdlibNames.JET_PACKAGE_CLASS.getDescriptor(), true);
@@ -104,18 +106,21 @@ public class NamespaceCodegen extends MemberCodegen {
packageClassAnnotation.visitEnd(); packageClassAnnotation.visitEnd();
} }
writeKotlinInfoIfNeeded(); List<MemberMap> namespaceMembers = new ArrayList<MemberMap>();
for (JetFile file : files) { for (JetFile file : files) {
VirtualFile vFile = file.getVirtualFile();
try { try {
generate(file); ClassBuilder builder = generate(file);
if (builder != null) {
namespaceMembers.add(builder.getMemberMap());
}
} }
catch (ProcessCanceledException e) { catch (ProcessCanceledException e) {
throw e; throw e;
} }
catch (Throwable e) { catch (Throwable e) {
if (errorHandler != null) errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl()); VirtualFile vFile = file.getVirtualFile();
errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
DiagnosticUtils.throwIfRunningOnServer(e); DiagnosticUtils.throwIfRunningOnServer(e);
if (ApplicationManager.getApplication().isInternal()) { if (ApplicationManager.getApplication().isInternal()) {
//noinspection CallToPrintStackTrace //noinspection CallToPrintStackTrace
@@ -124,11 +129,13 @@ public class NamespaceCodegen extends MemberCodegen {
} }
} }
writeKotlinInfoIfNeeded(MemberMap.union(namespaceMembers));
assert v.isActivated() == shouldGenerateNSClass(files) : "Different algorithms for generating namespace class and for heuristics"; assert v.isActivated() == shouldGenerateNSClass(files) : "Different algorithms for generating namespace class and for heuristics";
} }
private void writeKotlinInfoIfNeeded() { private void writeKotlinInfoIfNeeded(@NotNull MemberMap members) {
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(typeMapper)); DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(members));
ProtoBuf.Package.Builder packageProto = ProtoBuf.Package.newBuilder(); ProtoBuf.Package.Builder packageProto = ProtoBuf.Package.newBuilder();
boolean writeAnnotation = false; boolean writeAnnotation = false;
@@ -171,13 +178,14 @@ public class NamespaceCodegen extends MemberCodegen {
av.visitEnd(); av.visitEnd();
} }
private void generate(JetFile file) { @Nullable
private ClassBuilder generate(@NotNull JetFile file) {
NamespaceDescriptor descriptor = state.getBindingContext().get(BindingContext.FILE_TO_NAMESPACE, file); NamespaceDescriptor descriptor = state.getBindingContext().get(BindingContext.FILE_TO_NAMESPACE, file);
assert descriptor != null : "No namespace found for file " + file + " declared package: " + file.getPackageName(); assert descriptor != null : "No namespace found for file " + file + " declared package: " + file.getPackageName();
int countOfDeclarationsInSrcClass = 0; boolean generateSrcClass = false;
for (JetDeclaration declaration : file.getDeclarations()) { for (JetDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
countOfDeclarationsInSrcClass++; generateSrcClass = true;
} }
else if (declaration instanceof JetClassOrObject) { else if (declaration instanceof JetClassOrObject) {
if (state.isGenerateDeclaredClasses()) { if (state.isGenerateDeclaredClasses()) {
@@ -189,39 +197,39 @@ public class NamespaceCodegen extends MemberCodegen {
} }
} }
if (countOfDeclarationsInSrcClass > 0) { if (!generateSrcClass) return null;
String namespaceInternalName = JvmClassName.byFqNameWithoutInnerClasses(
PackageClassUtils.getPackageClassFqName(name)).getInternalName();
String className = getMultiFileNamespaceInternalName(namespaceInternalName, file);
ClassBuilder builder = state.getFactory().forNamespacepart(className, file);
builder.defineClass(file, V1_6, String namespaceInternalName = JvmClassName.byFqNameWithoutInnerClasses(
ACC_PUBLIC | ACC_FINAL, PackageClassUtils.getPackageClassFqName(name)).getInternalName();
className, String className = getMultiFileNamespaceInternalName(namespaceInternalName, file);
null, ClassBuilder builder = state.getFactory().forNamespacepart(className, file);
//"jet/lang/Namespace",
"java/lang/Object",
new String[0]
);
builder.visitSource(file.getName(), null);
FieldOwnerContext nameSpaceContext = builder.defineClass(file, V1_6,
CodegenContext.STATIC.intoNamespace(descriptor); ACC_PUBLIC | ACC_FINAL,
className,
null,
//"jet/lang/Namespace",
"java/lang/Object",
new String[0]
);
builder.visitSource(file.getName(), null);
FieldOwnerContext nameSpacePart = FieldOwnerContext nameSpaceContext = CodegenContext.STATIC.intoNamespace(descriptor);
CodegenContext.STATIC.intoNamespacePart(className, descriptor);
for (JetDeclaration declaration : file.getDeclarations()) { FieldOwnerContext nameSpacePart = CodegenContext.STATIC.intoNamespacePart(className, descriptor);
if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
genFunctionOrProperty(nameSpaceContext, (JetTypeParameterListOwner) declaration, builder); for (JetDeclaration declaration : file.getDeclarations()) {
genFunctionOrProperty(nameSpacePart, (JetTypeParameterListOwner) declaration, v.getClassBuilder()); if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
} genFunctionOrProperty(nameSpaceContext, (JetTypeParameterListOwner) declaration, builder);
genFunctionOrProperty(nameSpacePart, (JetTypeParameterListOwner) declaration, v.getClassBuilder());
} }
generateStaticInitializers(descriptor, builder, file, nameSpaceContext);
builder.done();
} }
generateStaticInitializers(descriptor, builder, file, nameSpaceContext);
builder.done();
return builder;
} }
public void generateClassOrObject(@NotNull NamespaceDescriptor descriptor, @NotNull JetClassOrObject classOrObject) { public void generateClassOrObject(@NotNull NamespaceDescriptor descriptor, @NotNull JetClassOrObject classOrObject) {
@@ -137,12 +137,14 @@ public class PropertyCodegen extends GenerationStateAware {
else if (!propertyDescriptor.getAnnotations().isEmpty()) { else if (!propertyDescriptor.getAnnotations().isEmpty()) {
// Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still // Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still
// accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally
String methodName = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(propertyDescriptor.getName());
MethodVisitor mv = v.newMethod(null, MethodVisitor mv = v.newMethod(null,
ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC,
JvmAbi.getSyntheticMethodNameForAnnotatedProperty(propertyDescriptor.getName()), methodName,
JvmAbi.ANNOTATED_PROPERTY_METHOD_SIGNATURE, JvmAbi.ANNOTATED_PROPERTY_METHOD_SIGNATURE,
null, null,
null); null);
v.getMemberMap().recordSyntheticMethodNameOfProperty(propertyDescriptor, methodName);
AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(propertyDescriptor); AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(propertyDescriptor);
mv.visitCode(); mv.visitCode();
mv.visitInsn(Opcodes.RETURN); mv.visitInsn(Opcodes.RETURN);
@@ -188,6 +190,8 @@ public class PropertyCodegen extends GenerationStateAware {
String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate); String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
builder.getMemberMap().recordFieldOfProperty(propertyDescriptor, type, name);
return builder.newField(element, modifiers, name, type.getDescriptor(), return builder.newField(element, modifiers, name, type.getDescriptor(),
typeMapper.mapFieldSignature(jetType), defaultValue); typeMapper.mapFieldSignature(jetType), defaultValue);
} }