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:
@@ -73,10 +73,15 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
|
||||
generateStaticInitializer();
|
||||
|
||||
generateRemoveInIterator();
|
||||
|
||||
generateKotlinAnnotation();
|
||||
}
|
||||
|
||||
protected abstract void generateDeclaration();
|
||||
|
||||
protected void generateKotlinAnnotation() {
|
||||
}
|
||||
|
||||
protected void generateSyntheticParts() {
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.jetbrains.asm4.MethodVisitor;
|
||||
public abstract class ClassBuilder {
|
||||
private String thisName;
|
||||
|
||||
private final MemberMap members = new MemberMap();
|
||||
|
||||
public static class Concrete extends ClassBuilder {
|
||||
private final ClassVisitor v;
|
||||
|
||||
@@ -63,6 +65,11 @@ public abstract class ClassBuilder {
|
||||
return getVisitor().visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MemberMap getMemberMap() {
|
||||
return members;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AnnotationVisitor newAnnotation(@NotNull String desc, boolean visible) {
|
||||
return getVisitor().visitAnnotation(desc, visible);
|
||||
|
||||
@@ -104,7 +104,6 @@ public class FunctionCodegen extends GenerationStateAware {
|
||||
@NotNull MethodContext methodContext,
|
||||
@NotNull FunctionGenerationStrategy strategy
|
||||
) {
|
||||
|
||||
Method asmMethod = jvmSignature.getAsmMethod();
|
||||
|
||||
MethodVisitor mv = v.newMethod(origin,
|
||||
@@ -113,6 +112,7 @@ public class FunctionCodegen extends GenerationStateAware {
|
||||
asmMethod.getDescriptor(),
|
||||
jvmSignature.getGenericsSignature(),
|
||||
null);
|
||||
v.getMemberMap().recordMethodOfDescriptor(functionDescriptor, asmMethod);
|
||||
|
||||
AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(functionDescriptor);
|
||||
if (state.getClassBuilderMode() == ClassBuilderMode.SIGNATURES) return;
|
||||
|
||||
@@ -213,15 +213,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor);
|
||||
|
||||
if (isTopLevelOrInnerClass(descriptor)) {
|
||||
writeKotlinInfo();
|
||||
}
|
||||
|
||||
writeClassSignatureIfNeeded(signature);
|
||||
}
|
||||
|
||||
private void writeKotlinInfo() {
|
||||
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(typeMapper));
|
||||
@Override
|
||||
protected void generateKotlinAnnotation() {
|
||||
if (!isTopLevelOrInnerClass(descriptor)) return;
|
||||
|
||||
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(v.getMemberMap()));
|
||||
|
||||
ProtoBuf.Class classProto = serializer.classProto(descriptor).build();
|
||||
|
||||
|
||||
@@ -16,25 +16,23 @@
|
||||
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.asm4.Type;
|
||||
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.NameTable;
|
||||
import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
|
||||
import org.jetbrains.jet.descriptors.serialization.SerializerExtension;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||
|
||||
public class JavaSerializerExtension extends SerializerExtension {
|
||||
private final JetTypeMapper typeMapper;
|
||||
private final MemberMap memberMap;
|
||||
|
||||
public JavaSerializerExtension(@NotNull JetTypeMapper typeMapper) {
|
||||
this.typeMapper = typeMapper;
|
||||
public JavaSerializerExtension(@NotNull MemberMap memberMap) {
|
||||
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
|
||||
public void serializeCallable(
|
||||
@NotNull CallableMemberDescriptor callable,
|
||||
@@ -42,30 +40,35 @@ public class JavaSerializerExtension extends SerializerExtension {
|
||||
@NotNull NameTable nameTable
|
||||
) {
|
||||
if (callable instanceof FunctionDescriptor) {
|
||||
Method method = typeMapper.mapSignature((FunctionDescriptor) callable).getAsmMethod();
|
||||
JavaProtoBufUtil.saveMethodSignature(proto, method, nameTable);
|
||||
Method method = memberMap.getMethodOfDescriptor(callable);
|
||||
if (method != null) {
|
||||
JavaProtoBufUtil.saveMethodSignature(proto, method, nameTable);
|
||||
}
|
||||
}
|
||||
else if (callable instanceof PropertyDescriptor) {
|
||||
PropertyDescriptor property = (PropertyDescriptor) callable;
|
||||
Type type = typeMapper.mapType(property.getType());
|
||||
|
||||
PropertyGetterDescriptor getter = property.getGetter();
|
||||
PropertySetterDescriptor setter = property.getSetter();
|
||||
Method getterMethod = getter == null ? null : typeMapper.mapGetterSignature(property, OwnerKind.IMPLEMENTATION).getAsmMethod();
|
||||
Method setterMethod = setter == null ? null : typeMapper.mapSetterSignature(property, OwnerKind.IMPLEMENTATION).getAsmMethod();
|
||||
Method getterMethod = getter == null ? null : memberMap.getMethodOfDescriptor(getter);
|
||||
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 syntheticMethodName;
|
||||
if ((getter == null || getter.isDefault()) && (setter == null || setter.isDefault())) {
|
||||
fieldName = property.getName().asString();
|
||||
if (field != null) {
|
||||
fieldType = field.first;
|
||||
fieldName = field.second;
|
||||
syntheticMethodName = null;
|
||||
}
|
||||
else {
|
||||
fieldType = 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.util.PathUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.AnnotationVisitor;
|
||||
import org.jetbrains.asm4.MethodVisitor;
|
||||
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.Name;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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)) {
|
||||
AnnotationVisitor packageClassAnnotation =
|
||||
v.getClassBuilder().newAnnotation(JvmStdlibNames.JET_PACKAGE_CLASS.getDescriptor(), true);
|
||||
@@ -104,18 +106,21 @@ public class NamespaceCodegen extends MemberCodegen {
|
||||
packageClassAnnotation.visitEnd();
|
||||
}
|
||||
|
||||
writeKotlinInfoIfNeeded();
|
||||
List<MemberMap> namespaceMembers = new ArrayList<MemberMap>();
|
||||
|
||||
for (JetFile file : files) {
|
||||
VirtualFile vFile = file.getVirtualFile();
|
||||
try {
|
||||
generate(file);
|
||||
ClassBuilder builder = generate(file);
|
||||
if (builder != null) {
|
||||
namespaceMembers.add(builder.getMemberMap());
|
||||
}
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
throw 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);
|
||||
if (ApplicationManager.getApplication().isInternal()) {
|
||||
//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";
|
||||
}
|
||||
|
||||
private void writeKotlinInfoIfNeeded() {
|
||||
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(typeMapper));
|
||||
private void writeKotlinInfoIfNeeded(@NotNull MemberMap members) {
|
||||
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(members));
|
||||
ProtoBuf.Package.Builder packageProto = ProtoBuf.Package.newBuilder();
|
||||
boolean writeAnnotation = false;
|
||||
|
||||
@@ -171,13 +178,14 @@ public class NamespaceCodegen extends MemberCodegen {
|
||||
av.visitEnd();
|
||||
}
|
||||
|
||||
private void generate(JetFile file) {
|
||||
@Nullable
|
||||
private ClassBuilder generate(@NotNull JetFile file) {
|
||||
NamespaceDescriptor descriptor = state.getBindingContext().get(BindingContext.FILE_TO_NAMESPACE, file);
|
||||
assert descriptor != null : "No namespace found for file " + file + " declared package: " + file.getPackageName();
|
||||
int countOfDeclarationsInSrcClass = 0;
|
||||
boolean generateSrcClass = false;
|
||||
for (JetDeclaration declaration : file.getDeclarations()) {
|
||||
if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
|
||||
countOfDeclarationsInSrcClass++;
|
||||
generateSrcClass = true;
|
||||
}
|
||||
else if (declaration instanceof JetClassOrObject) {
|
||||
if (state.isGenerateDeclaredClasses()) {
|
||||
@@ -189,39 +197,39 @@ public class NamespaceCodegen extends MemberCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
if (countOfDeclarationsInSrcClass > 0) {
|
||||
String namespaceInternalName = JvmClassName.byFqNameWithoutInnerClasses(
|
||||
PackageClassUtils.getPackageClassFqName(name)).getInternalName();
|
||||
String className = getMultiFileNamespaceInternalName(namespaceInternalName, file);
|
||||
ClassBuilder builder = state.getFactory().forNamespacepart(className, file);
|
||||
if (!generateSrcClass) return null;
|
||||
|
||||
builder.defineClass(file, V1_6,
|
||||
ACC_PUBLIC | ACC_FINAL,
|
||||
className,
|
||||
null,
|
||||
//"jet/lang/Namespace",
|
||||
"java/lang/Object",
|
||||
new String[0]
|
||||
);
|
||||
builder.visitSource(file.getName(), null);
|
||||
String namespaceInternalName = JvmClassName.byFqNameWithoutInnerClasses(
|
||||
PackageClassUtils.getPackageClassFqName(name)).getInternalName();
|
||||
String className = getMultiFileNamespaceInternalName(namespaceInternalName, file);
|
||||
ClassBuilder builder = state.getFactory().forNamespacepart(className, file);
|
||||
|
||||
FieldOwnerContext nameSpaceContext =
|
||||
CodegenContext.STATIC.intoNamespace(descriptor);
|
||||
builder.defineClass(file, V1_6,
|
||||
ACC_PUBLIC | ACC_FINAL,
|
||||
className,
|
||||
null,
|
||||
//"jet/lang/Namespace",
|
||||
"java/lang/Object",
|
||||
new String[0]
|
||||
);
|
||||
builder.visitSource(file.getName(), null);
|
||||
|
||||
FieldOwnerContext nameSpacePart =
|
||||
CodegenContext.STATIC.intoNamespacePart(className, descriptor);
|
||||
FieldOwnerContext nameSpaceContext = CodegenContext.STATIC.intoNamespace(descriptor);
|
||||
|
||||
for (JetDeclaration declaration : file.getDeclarations()) {
|
||||
if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
|
||||
genFunctionOrProperty(nameSpaceContext, (JetTypeParameterListOwner) declaration, builder);
|
||||
genFunctionOrProperty(nameSpacePart, (JetTypeParameterListOwner) declaration, v.getClassBuilder());
|
||||
}
|
||||
FieldOwnerContext nameSpacePart = CodegenContext.STATIC.intoNamespacePart(className, descriptor);
|
||||
|
||||
for (JetDeclaration declaration : file.getDeclarations()) {
|
||||
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) {
|
||||
|
||||
@@ -137,12 +137,14 @@ public class PropertyCodegen extends GenerationStateAware {
|
||||
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
|
||||
// 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,
|
||||
ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC,
|
||||
JvmAbi.getSyntheticMethodNameForAnnotatedProperty(propertyDescriptor.getName()),
|
||||
methodName,
|
||||
JvmAbi.ANNOTATED_PROPERTY_METHOD_SIGNATURE,
|
||||
null,
|
||||
null);
|
||||
v.getMemberMap().recordSyntheticMethodNameOfProperty(propertyDescriptor, methodName);
|
||||
AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(propertyDescriptor);
|
||||
mv.visitCode();
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
@@ -188,6 +190,8 @@ public class PropertyCodegen extends GenerationStateAware {
|
||||
|
||||
String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
|
||||
|
||||
builder.getMemberMap().recordFieldOfProperty(propertyDescriptor, type, name);
|
||||
|
||||
return builder.newField(element, modifiers, name, type.getDescriptor(),
|
||||
typeMapper.mapFieldSignature(jetType), defaultValue);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user