Simplify local class name serialization, don't go through extension
Also fix it for the case of a class in a non-default package
This commit is contained in:
@@ -25,7 +25,10 @@ import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeCapabilities;
|
||||
import org.jetbrains.kotlin.serialization.*;
|
||||
import org.jetbrains.kotlin.serialization.AnnotationSerializer;
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf;
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtension;
|
||||
import org.jetbrains.kotlin.serialization.StringTable;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor;
|
||||
@@ -34,18 +37,17 @@ import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.shortNameByAsmType;
|
||||
import static org.jetbrains.kotlin.codegen.JvmSerializationBindings.*;
|
||||
|
||||
public class JvmSerializerExtension extends SerializerExtension {
|
||||
private final JvmSerializationBindings bindings;
|
||||
private final JetTypeMapper typeMapper;
|
||||
private final StringTable stringTable = new JvmStringTable(this);
|
||||
private final AnnotationSerializer annotationSerializer = new AnnotationSerializer(stringTable);
|
||||
private final StringTable stringTable;
|
||||
private final AnnotationSerializer annotationSerializer;
|
||||
|
||||
public JvmSerializerExtension(@NotNull JvmSerializationBindings bindings, @NotNull JetTypeMapper typeMapper) {
|
||||
this.bindings = bindings;
|
||||
this.typeMapper = typeMapper;
|
||||
this.stringTable = new JvmStringTable(typeMapper);
|
||||
this.annotationSerializer = new AnnotationSerializer(stringTable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -91,12 +93,6 @@ public class JvmSerializerExtension extends SerializerExtension {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLocalClassName(@NotNull ClassDescriptor descriptor) {
|
||||
return shortNameByAsmType(typeMapper.mapClass(descriptor));
|
||||
}
|
||||
|
||||
private void saveSignature(@NotNull CallableMemberDescriptor callable, @NotNull ProtoBuf.Callable.Builder proto) {
|
||||
SignatureSerializer signatureSerializer = new SignatureSerializer();
|
||||
if (callable instanceof FunctionDescriptor) {
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.serialization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassOrPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmNameResolver
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtension
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.StringTable
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record
|
||||
@@ -30,7 +29,7 @@ import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
||||
class JvmStringTable(private val extension: SerializerExtension) : StringTable {
|
||||
class JvmStringTable(private val typeMapper: JetTypeMapper) : StringTable {
|
||||
public val strings = ArrayList<String>()
|
||||
private val records = ArrayList<Record>()
|
||||
private val map = HashMap<String, Int>()
|
||||
@@ -52,43 +51,35 @@ class JvmStringTable(private val extension: SerializerExtension) : StringTable {
|
||||
throw IllegalStateException("Cannot get FQ name of error class: " + descriptor)
|
||||
}
|
||||
|
||||
val string: String
|
||||
val storeAsLiteral: Boolean
|
||||
// We use the following format to encode ClassId: "pkg/Outer.Inner".
|
||||
// It represents a unique name, but such names don't usually appear in the constant pool, so we're writing "Lpkg/Outer$Inner;"
|
||||
// instead and an instruction to drop the first and the last character in this string and replace all '$' with '.'.
|
||||
// This works most of the time, except in two rare cases:
|
||||
// - the name of the class or any of its outer classes contains dollars. In this case we're just storing the described
|
||||
// string literally: "pkg/Outer.Inner$with$dollars"
|
||||
// - the class is local or nested in local. In this case we're also storing the literal string, and also storing the fact that
|
||||
// this name represents a local class in a separate list
|
||||
|
||||
val parent = sequence(descriptor, DeclarationDescriptor::getContainingDeclaration).first { it !is ClassDescriptor }
|
||||
if (parent is PackageFragmentDescriptor) {
|
||||
val classId = descriptor.classId
|
||||
val packageName = classId.packageFqName
|
||||
val className = classId.relativeClassName.asString()
|
||||
string =
|
||||
if (packageName.isRoot) className
|
||||
else packageName.asString().replace('.', '/') + "/" + className
|
||||
|
||||
// If any of the class names contains '$', we can't simply replace all '$' with '.' upon deserialization.
|
||||
// This case is rather rare, so we're storing a literal string
|
||||
storeAsLiteral = '$' in string && classId.relativeClassName.pathSegments().any { '$' in it.asString() }
|
||||
}
|
||||
else {
|
||||
storeAsLiteral = true
|
||||
string = descriptor.localClassName()
|
||||
}
|
||||
|
||||
val isLocal = descriptor.containingDeclaration !is ClassOrPackageFragmentDescriptor
|
||||
val classId = descriptor.classId
|
||||
val string = classId.asString()
|
||||
|
||||
map[string]?.let { recordedIndex ->
|
||||
if (isLocal == (recordedIndex in localNames)) {
|
||||
// If we already recorded such string, we only return its index if it's local and our name is local
|
||||
// OR it's not local and our name is not local as well
|
||||
if (classId.isLocal == (recordedIndex in localNames)) {
|
||||
return recordedIndex
|
||||
}
|
||||
}
|
||||
|
||||
val index = strings.size()
|
||||
if (isLocal) {
|
||||
if (classId.isLocal) {
|
||||
localNames.add(index)
|
||||
}
|
||||
|
||||
val record = Record.newBuilder()
|
||||
|
||||
if (storeAsLiteral) {
|
||||
// If the class is local or any of its outer class names contains '$', store a literal string
|
||||
if (classId.isLocal || '$' in string) {
|
||||
strings.add(string)
|
||||
// TODO: optimize, don't always store the operation
|
||||
record.setOperation(Record.Operation.NONE)
|
||||
@@ -103,7 +94,7 @@ class JvmStringTable(private val extension: SerializerExtension) : StringTable {
|
||||
}
|
||||
else {
|
||||
record.setOperation(Record.Operation.DESC_TO_CLASS_ID)
|
||||
strings.add("L$string;")
|
||||
strings.add("L${string.replace('.', '$')};")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +105,18 @@ class JvmStringTable(private val extension: SerializerExtension) : StringTable {
|
||||
return index
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.localClassName(): String {
|
||||
val container = containingDeclaration
|
||||
return when (container) {
|
||||
is ClassDescriptor -> container.localClassName() + "." + name.asString()
|
||||
else -> extension.getLocalClassName(this)
|
||||
private val ClassDescriptor.classId: ClassId
|
||||
get() {
|
||||
val container = containingDeclaration
|
||||
return when (container) {
|
||||
is ClassDescriptor -> container.classId.createNestedClassId(name)
|
||||
is PackageFragmentDescriptor -> ClassId(container.fqName, name)
|
||||
else -> {
|
||||
val fqName = FqName(typeMapper.mapClass(this).internalName.replace('/', '.'))
|
||||
ClassId(fqName.parent(), FqName.topLevel(fqName.shortName()), /* isLocal = */ true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun serializeTo(output: OutputStream) {
|
||||
with(JvmProtoBuf.StringTableTypes.newBuilder()) {
|
||||
|
||||
Reference in New Issue
Block a user