Add KotlinInfo annotation, serialize classes to bytecode

This commit is contained in:
Alexander Udalov
2013-06-26 23:07:33 +04:00
parent fed14f4fbf
commit 1acc1564d9
8 changed files with 222 additions and 33 deletions
+2
View File
@@ -12,6 +12,8 @@
<orderEntry type="module" module-name="runtime" />
<orderEntry type="library" scope="PROVIDED" name="intellij-core" level="project" />
<orderEntry type="library" name="javax.inject" level="project" />
<orderEntry type="module" module-name="serialization" />
<orderEntry type="library" name="protobuf-java" level="project" />
</component>
</module>
@@ -41,6 +41,7 @@ import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.codegen.state.JetTypeMapperMode;
import org.jetbrains.jet.descriptors.serialization.*;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
import org.jetbrains.jet.lang.psi.*;
@@ -60,17 +61,20 @@ import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.utils.ExceptionUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import static org.jetbrains.asm4.Opcodes.*;
import static org.jetbrains.jet.codegen.AsmUtil.*;
import static org.jetbrains.jet.codegen.CodegenUtil.*;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
import static org.jetbrains.jet.descriptors.serialization.ClassSerializationUtil.constantSerializer;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.callableDescriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isKindOf;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
@@ -209,9 +213,31 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor);
writeKotlinInfoIfNeeded();
writeClassSignatureIfNeeded(signature);
}
private void writeKotlinInfoIfNeeded() {
if (!(descriptor.getContainingDeclaration() instanceof ClassOrNamespaceDescriptor)) return;
final AnnotationVisitor av = v.getVisitor().visitAnnotation("Ljet/KotlinInfo;", true);
DescriptorSerializer serializer = new DescriptorSerializer(DescriptorNamer.DEFAULT);
ClassSerializationUtil.serializeClass(descriptor, constantSerializer(serializer), new ClassSerializationUtil.Sink() {
@Override
public void writeClass(@NotNull ClassDescriptor classDescriptor, @NotNull ProtoBuf.Class classProto) {
av.visit("data", classProto.toByteArray());
}
});
ByteArrayOutputStream nameTable = new ByteArrayOutputStream();
NameSerializationUtil.serializeNameTable(nameTable, serializer.getNameTable());
av.visit("nameTable", nameTable.toByteArray());
av.visitEnd();
}
private void writeEnclosingMethod() {
//JVMS7: A class must have an EnclosingMethod attribute if and only if it is a local class or an anonymous class.
DeclarationDescriptor parentDescriptor = descriptor.getContainingDeclaration();
@@ -17,25 +17,39 @@
package org.jetbrains.jet.descriptors.serialization;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.utils.ExceptionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class NameSerializationUtil {
@NotNull
public static NameResolver deserializeNameResolver(@NotNull InputStream in) throws IOException {
ProtoBuf.SimpleNameTable simpleNames = ProtoBuf.SimpleNameTable.parseDelimitedFrom(in);
ProtoBuf.QualifiedNameTable qualifiedNames = ProtoBuf.QualifiedNameTable.parseDelimitedFrom(in);
return new NameResolver(simpleNames, qualifiedNames);
private NameSerializationUtil() {
}
public static void serializeNameTable(@NotNull OutputStream out, @NotNull NameTable nameTable) throws IOException {
ProtoBuf.SimpleNameTable simpleNamesProto = toSimpleNameTable(nameTable);
simpleNamesProto.writeDelimitedTo(out);
@NotNull
public static NameResolver deserializeNameResolver(@NotNull InputStream in) {
try {
ProtoBuf.SimpleNameTable simpleNames = ProtoBuf.SimpleNameTable.parseDelimitedFrom(in);
ProtoBuf.QualifiedNameTable qualifiedNames = ProtoBuf.QualifiedNameTable.parseDelimitedFrom(in);
return new NameResolver(simpleNames, qualifiedNames);
}
catch (IOException e) {
throw ExceptionUtils.rethrow(e);
}
}
ProtoBuf.QualifiedNameTable qualifiedNameTable = toQualifiedNameTable(nameTable);
qualifiedNameTable.writeDelimitedTo(out);
public static void serializeNameTable(@NotNull OutputStream out, @NotNull NameTable nameTable) {
try {
ProtoBuf.SimpleNameTable simpleNamesProto = toSimpleNameTable(nameTable);
simpleNamesProto.writeDelimitedTo(out);
ProtoBuf.QualifiedNameTable qualifiedNameTable = toQualifiedNameTable(nameTable);
qualifiedNameTable.writeDelimitedTo(out);
}
catch (IOException e) {
throw ExceptionUtils.rethrow(e);
}
}
@NotNull
@@ -84,10 +84,13 @@ public class NameTable {
NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
builder.setParentQualifiedName(getFqNameIndex(namespaceDescriptor));
}
else {
else if (containingDeclaration instanceof ClassDescriptor) {
ClassDescriptor outerClass = (ClassDescriptor) containingDeclaration;
builder.setParentQualifiedName(getFqNameIndex(outerClass));
}
else {
throw new IllegalStateException("FQ names are only stored for top-level or inner classes: " + classDescriptor);
}
return qualifiedNames.intern(builder);
}
@@ -43,19 +43,27 @@ import org.jetbrains.jet.config.CompilerConfiguration;
import org.jetbrains.jet.lang.ModuleConfiguration;
import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorImpl;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.diagnostics.Severity;
import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.ImportPath;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
import org.jetbrains.jet.lang.resolve.java.JavaBridgeConfiguration;
import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.lazy.LazyResolveTestUtil;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.plugin.JetLanguage;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
@@ -614,4 +622,17 @@ public class JetTestUtils {
descriptor.setModuleConfiguration(ModuleConfiguration.EMPTY);
return descriptor;
}
@NotNull
public static NamespaceDescriptorImpl createTestNamespace(@NotNull String testPackageName) {
ModuleDescriptorImpl module = new ModuleDescriptorImpl(Name.special("<test module>"), JavaBridgeConfiguration.ALL_JAVA_IMPORTS,
JavaToKotlinClassMap.getInstance());
NamespaceDescriptorImpl rootNamespace =
new NamespaceDescriptorImpl(module, Collections.<AnnotationDescriptor>emptyList(), JetPsiUtil.ROOT_NAMESPACE_NAME);
module.setRootNamespace(rootNamespace);
NamespaceDescriptorImpl test =
new NamespaceDescriptorImpl(rootNamespace, Collections.<AnnotationDescriptor>emptyList(), Name.identifier(testPackageName));
test.initialize(new WritableScopeImpl(JetScope.EMPTY, test, RedeclarationHandler.DO_NOTHING, "members of test namespace"));
return test;
}
}
@@ -21,31 +21,28 @@ import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.ConfigurationKind;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.TestJdkKind;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationDeserializer;
import org.jetbrains.jet.jvm.compiler.ExpectedLoadErrorsUtil;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorImpl;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.JavaBridgeConfiguration;
import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.lazy.KotlinTestWithEnvironment;
import org.jetbrains.jet.lang.resolve.lazy.storage.LockBasedStorageManager;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import org.jetbrains.jet.renderer.DescriptorRendererBuilder;
import org.jetbrains.jet.test.util.NamespaceComparator;
import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.jetbrains.jet.descriptors.serialization.ClassSerializationUtil.constantSerializer;
@@ -95,7 +92,8 @@ public class BuiltinsDeserializationTest extends KotlinTestWithEnvironment {
List<ProtoBuf.Callable> callableProtos = serializeCallables(serializer, allDescriptors);
final NamespaceDescriptorImpl actualNamespace = createTestNamespace(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.shortName().asString());
final NamespaceDescriptorImpl actualNamespace = JetTestUtils
.createTestNamespace(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.shortName().asString());
final NameResolver nameResolver = NameSerializationUtil.createNameResolver(serializer.getNameTable());
@@ -215,16 +213,4 @@ public class BuiltinsDeserializationTest extends KotlinTestWithEnvironment {
private static ClassId getClassId(ClassDescriptor classDescriptor) {
return ClassSerializationUtil.getClassId(classDescriptor, NAMER);
}
private static NamespaceDescriptorImpl createTestNamespace(String testPackageName) {
ModuleDescriptorImpl module = new ModuleDescriptorImpl(Name.special("<test module>"), JavaBridgeConfiguration.ALL_JAVA_IMPORTS,
JavaToKotlinClassMap.getInstance());
NamespaceDescriptorImpl rootNamespace =
new NamespaceDescriptorImpl(module, Collections.<AnnotationDescriptor>emptyList(), JetPsiUtil.ROOT_NAMESPACE_NAME);
module.setRootNamespace(rootNamespace);
NamespaceDescriptorImpl test =
new NamespaceDescriptorImpl(rootNamespace, Collections.<AnnotationDescriptor>emptyList(), Name.identifier(testPackageName));
test.initialize(new WritableScopeImpl(JetScope.EMPTY, test, RedeclarationHandler.DO_NOTHING, "members of test namespace"));
return test;
}
}
@@ -0,0 +1,110 @@
/*
* 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.InvalidProtocolBufferException;
import jet.KotlinInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.ConfigurationKind;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.codegen.CodegenTestCase;
import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationDeserializer;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorImpl;
import org.jetbrains.jet.lang.resolve.lazy.storage.LockBasedStorageManager;
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 java.io.ByteArrayInputStream;
public class WriteSerializedInfoTest extends CodegenTestCase {
public static final FqName NAMESPACE_NAME = new FqName("test");
public static final FqNameUnsafe CLASS_NAME = new FqNameUnsafe("A");
@Override
protected void setUp() throws Exception {
super.setUp();
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL);
}
public void testKotlinInfo() throws Exception {
loadText("package " + NAMESPACE_NAME + "\n" +
"\n" +
"class " + CLASS_NAME + " {\n" +
" fun foo() {}\n" +
" fun bar() = 42\n" +
"}\n");
Class aClass = generateClass(NAMESPACE_NAME + "." + CLASS_NAME);
assertTrue(aClass.isAnnotationPresent(KotlinInfo.class));
KotlinInfo kotlinInfo = (KotlinInfo) aClass.getAnnotation(KotlinInfo.class);
AbstractClassResolver classResolver = new KotlinInfoBasedClassResolver(kotlinInfo);
ClassDescriptor descriptor = classResolver.findClass(new ClassId(NAMESPACE_NAME, CLASS_NAME));
assertNotNull(descriptor);
assertEquals(CLASS_NAME.asString(), descriptor.getName().asString());
}
private static class KotlinInfoBasedClassResolver extends AbstractClassResolver {
private final NameResolver nameResolver;
private final ProtoBuf.Class classProto;
private final NamespaceDescriptorImpl namespace;
public KotlinInfoBasedClassResolver(@NotNull KotlinInfo kotlinInfo) throws InvalidProtocolBufferException {
super(new LockBasedStorageManager(), AnnotationDeserializer.UNSUPPORTED);
this.nameResolver = NameSerializationUtil.deserializeNameResolver(new ByteArrayInputStream(kotlinInfo.nameTable()));
this.classProto = ProtoBuf.Class.parseFrom(kotlinInfo.data());
this.namespace = JetTestUtils.createTestNamespace(NAMESPACE_NAME.asString());
}
@Nullable
@Override
protected ClassData getClassData(@NotNull ClassId classId) {
assert classId.getPackageFqName().equals(NAMESPACE_NAME) &&
classId.getRelativeClassName().equals(CLASS_NAME) : "Unsupported classId: " + classId;
return new ClassData(nameResolver, classProto);
}
@NotNull
@Override
protected DeclarationDescriptor getPackage(@NotNull FqName fqName) {
assert fqName.equals(NAMESPACE_NAME) : "Unsupported namespace: " + fqName;
return namespace;
}
@NotNull
@Override
protected ClassId getClassId(@NotNull ClassDescriptor classDescriptor) {
return ClassSerializationUtil.getClassId(classDescriptor, DescriptorNamer.DEFAULT);
}
@NotNull
@Override
protected Name getClassObjectName(@NotNull ClassDescriptor outerClass) {
return Name.special("<class object>");
}
@Override
protected void classDescriptorCreated(@NotNull ClassDescriptor classDescriptor) {
}
}
}
+27
View File
@@ -0,0 +1,27 @@
/*
* 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 jet;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinInfo {
byte[] data();
byte[] nameTable();
}