Introduce new string table optimized for JVM class files

This format of the string table allows to reduce the size of the Kotlin
metadata in JVM class files by reusing constants already present in the
constant pool. Currently the string table produced by JvmStringTable is not
fully optimized in serialization (in particular, the 'substring' operation
which will be used to extract type names out of generic signature, is not used
at all), but the format and its complete support in the deserialization
(JvmNameResolver) allows future improvement without changing the binary version
This commit is contained in:
Alexander Udalov
2015-09-21 20:35:27 +03:00
parent 542bfab96f
commit 1036506b25
19 changed files with 4719 additions and 67 deletions
@@ -46,6 +46,8 @@ import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
import org.jetbrains.kotlin.resolve.jvm.JvmPackage;
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
import org.jetbrains.kotlin.codegen.serialization.JvmStringTable;
import org.jetbrains.kotlin.serialization.jvm.BitEncoding;
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor;
import org.jetbrains.kotlin.types.JetType;
@@ -842,7 +844,7 @@ public class AsmUtil {
av.visitEnd();
}
public static void writeAnnotationData(@NotNull AnnotationVisitor av, @NotNull byte[] bytes) {
public static void writeAnnotationData(@NotNull AnnotationVisitor av, @NotNull DescriptorSerializer serializer, @NotNull byte[] bytes) {
JvmCodegenUtil.writeAbiVersion(av);
AnnotationVisitor data = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(bytes)) {
@@ -850,7 +852,9 @@ public class AsmUtil {
}
data.visitEnd();
AnnotationVisitor strings = av.visitArray(JvmAnnotationNames.STRINGS_FIELD_NAME);
// TODO: write the actual string table
for (String string : ((JvmStringTable) serializer.getStringTable()).getStrings()) {
strings.visit(null, string);
}
strings.visitEnd();
}
@@ -230,7 +230,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
ProtoBuf.Callable callableProto = serializer.callableProto(funDescriptor).build();
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CALLABLE), true);
writeAnnotationData(av, serializer.serialize(callableProto));
writeAnnotationData(av, serializer, serializer.serialize(callableProto));
av.visitEnd();
}
@@ -253,7 +253,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
ProtoBuf.Class classProto = serializer.classProto(descriptor).build();
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CLASS), true);
writeAnnotationData(av, serializer.serialize(classProto));
writeAnnotationData(av, serializer, serializer.serialize(classProto));
if (kind != null) {
av.visitEnum(
JvmAnnotationNames.KIND_FIELD_NAME,
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.serialization.JvmStringTable;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
@@ -39,7 +40,7 @@ import static org.jetbrains.kotlin.codegen.JvmSerializationBindings.*;
public class JvmSerializerExtension extends SerializerExtension {
private final JvmSerializationBindings bindings;
private final JetTypeMapper typeMapper;
private final StringTableImpl stringTable = new StringTableImpl(this);
private final StringTable stringTable = new JvmStringTable(this);
private final AnnotationSerializer annotationSerializer = new AnnotationSerializer(stringTable);
public JvmSerializerExtension(@NotNull JvmSerializationBindings bindings, @NotNull JetTypeMapper typeMapper) {
@@ -95,7 +95,7 @@ public class MultifileClassPartCodegen(
if (packageProto.memberCount == 0) return
val av = v.newAnnotation(AsmUtil.asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_MULTIFILE_CLASS_PART), true)
AsmUtil.writeAnnotationData(av, serializer.serialize(packageProto))
AsmUtil.writeAnnotationData(av, serializer, serializer.serialize(packageProto))
av.visit(JvmAnnotationNames.MULTIFILE_CLASS_NAME_FIELD_NAME, multifileClassFqName.shortName().asString())
av.visitEnd()
}
@@ -290,7 +290,7 @@ public class PackageCodegen {
ProtoBuf.Package packageProto = serializer.packageProtoWithoutDescriptors().build();
AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true);
AsmUtil.writeAnnotationData(av, serializer.serialize(packageProto));
AsmUtil.writeAnnotationData(av, serializer, serializer.serialize(packageProto));
av.visitEnd();
}
@@ -128,7 +128,7 @@ public class PackagePartCodegen extends MemberCodegen<JetFile> {
if (packageProto.getMemberCount() == 0) return;
AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_FILE_FACADE), true);
writeAnnotationData(av, serializer.serialize(packageProto));
writeAnnotationData(av, serializer, serializer.serialize(packageProto));
av.visitEnd();
}
}
@@ -0,0 +1,132 @@
/*
* Copyright 2010-2015 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.kotlin.codegen.serialization
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.serialization.StringTable
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record
import org.jetbrains.kotlin.types.ErrorUtils
import java.io.OutputStream
import java.util.*
class JvmStringTable(private val extension: SerializerExtension) : StringTable {
public val strings = ArrayList<String>()
private val records = ArrayList<Record>()
private val map = HashMap<String, Int>()
private val localNames = HashSet<Int>()
override fun getStringIndex(string: String): Int =
map.getOrPut(string) {
strings.size().apply {
strings.add(string)
records.add(Record.newBuilder().apply {
// TODO: optimize, don't always store the operation
setOperation(Record.Operation.NONE)
}.build())
}
}
override fun getFqNameIndex(descriptor: ClassDescriptor): Int {
if (ErrorUtils.isError(descriptor)) {
throw IllegalStateException("Cannot get FQ name of error class: " + descriptor)
}
val string: String
val storeAsLiteral: Boolean
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
map[string]?.let { recordedIndex ->
if (isLocal == (recordedIndex in localNames)) {
return recordedIndex
}
}
val index = strings.size()
if (isLocal) {
localNames.add(index)
}
val record = Record.newBuilder()
if (storeAsLiteral) {
strings.add(string)
// TODO: optimize, don't always store the operation
record.setOperation(Record.Operation.NONE)
}
else {
val predefinedIndex = JvmNameResolver.getPredefinedStringIndex(string)
if (predefinedIndex != null) {
record.setPredefinedIndex(predefinedIndex)
record.setOperation(Record.Operation.NONE)
// TODO: move all records with predefined names to the end and do not write associated strings for them (since they are ignored)
strings.add("")
}
else {
record.setOperation(Record.Operation.DESC_TO_CLASS_ID)
strings.add("L$string;")
}
}
records.add(record.build())
map[string] = index
return index
}
private fun ClassDescriptor.localClassName(): String {
val container = containingDeclaration
return when (container) {
is ClassDescriptor -> container.localClassName() + "." + name.asString()
else -> extension.getLocalClassName(this)
}
}
override fun serializeTo(output: OutputStream) {
with(JvmProtoBuf.StringTableTypes.newBuilder()) {
addAllRecord(records)
addAllLocalName(localNames)
build().writeDelimitedTo(output)
}
}
}
@@ -12,6 +12,6 @@ interface A {
}
}
// 1 foo
// 1 foo\(
// 1 getProp
// 1 defaultFun\$
// 1 defaultFun\$
@@ -13042,7 +13042,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
boolean hasFlags();
@@ -13059,7 +13061,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
int getFlags();
@@ -14599,7 +14603,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public boolean hasFlags() {
@@ -14618,7 +14624,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public int getFlags() {
@@ -15352,7 +15360,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public boolean hasFlags() {
@@ -15371,7 +15381,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public int getFlags() {
@@ -15390,7 +15402,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public Builder setFlags(int value) {
@@ -15412,7 +15426,9 @@ public final class DebugProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public Builder clearFlags() {
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,161 @@
/*
* Copyright 2010-2015 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.kotlin.serialization.jvm
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.load.kotlin.JvmNameResolver
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record.Operation.DESC_TO_CLASS_ID
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record.Operation.INTERNAL_TO_CLASS_ID
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record.Operation.NONE
import java.util.*
import kotlin.test.assertEquals
class JvmNameResolverTest : UsefulTestCase() {
private class Context {
val types = JvmProtoBuf.StringTableTypes.newBuilder()
val strings = ArrayList<String>()
fun string(
string: String?,
range: Int? = null,
predefinedIndex: Int? = null,
operation: Record.Operation? = null,
substringIndex: List<Int>? = null,
replaceChar: List<Char>? = null
) {
types.addRecord(Record.newBuilder().apply {
range?.let { setRange(it) }
predefinedIndex?.let { setPredefinedIndex(it) }
operation?.let { setOperation(it) }
substringIndex?.let { addAllSubstringIndex(it) }
replaceChar?.let { addAllReplaceChar(it.map(Char::toInt)) }
}.build())
string?.let { strings.add(it) }
}
}
private fun create(init: Context.() -> Unit): JvmNameResolver {
return Context().run {
init()
JvmNameResolver(types.build(), strings.toTypedArray())
}
}
private fun str(string: String?, predefinedIndex: Int? = null, operation: Record.Operation? = null): String {
return create { string(string, null, predefinedIndex, operation, null, null) }.getString(0)
}
fun testSimpleString() {
assertEquals("abc", str("abc"))
}
fun testSimpleClassId() {
assertEquals(
ClassId.topLevel(FqName("foo.bar.Baz")),
create { string("Lfoo/bar/Baz;", operation = DESC_TO_CLASS_ID) }.getClassId(0)
)
}
fun testBasicOperations() {
assertEquals("java/util/Map.Entry", str("Ljava/util/Map\$Entry;", operation = DESC_TO_CLASS_ID))
assertEquals("java/util/Map.Entry", str("java/util/Map\$Entry", operation = INTERNAL_TO_CLASS_ID))
}
fun testPredefined() {
for ((index, predefined) in JvmNameResolver.PREDEFINED_STRINGS.withIndex()) {
assertEquals(predefined, str("ignored", predefinedIndex = index), "Predefined string failed: $predefined (index $index)")
}
}
fun testNotExistingPredefinedString() {
assertEquals("not-ignored", str("not-ignored", predefinedIndex = 123456789))
}
fun testOperationOnBadString() {
assertEquals("X", str("X", operation = DESC_TO_CLASS_ID))
assertEquals("", str("", operation = DESC_TO_CLASS_ID))
}
fun testSubstring() {
val n = create {
string("kotlin", substringIndex = listOf(0, 6))
string("kotlin", substringIndex = listOf(1, 4))
string("kotlin", substringIndex = listOf(6, 6))
// Invalid operations
string("kotlin", substringIndex = listOf(7, 5))
string("kotlin", substringIndex = listOf(0, -2))
string("kotlin", substringIndex = listOf(3, 1))
}
assertEquals("kotlin", n.getString(0))
assertEquals("otl", n.getString(1))
assertEquals("", n.getString(2))
// All invalid operations should be ignored
(3..5).forEach { assertEquals("kotlin", n.getString(it)) }
}
fun testSubstringHappensAfterOperation() {
assertEquals("tl", create {
string("kotlin", substringIndex = listOf(1, 5), operation = DESC_TO_CLASS_ID)
}.getString(0))
}
fun testReplaceAll() {
val n = create {
string("kotlin", replaceChar = listOf('k', 'm'))
string("java", replaceChar = listOf('a', 'o', 'a', 'b', 'c', 'd')) // All chars after the first two are ignored
// Invalid operations
string("kotlin", replaceChar = listOf())
string("kotlin", replaceChar = listOf('k'))
}
assertEquals("motlin", n.getString(0))
assertEquals("jovo", n.getString(1))
// All invalid operations should be ignored
(2..3).forEach { assertEquals("kotlin", n.getString(it)) }
}
fun testRange() {
val n = create {
string("a\$b\$c", operation = INTERNAL_TO_CLASS_ID, range = 2)
string("d\$e\$f", operation = NONE, range = 2)
string("abc")
string("def")
}
assertEquals("d.e.f", n.getString(1))
assertEquals("def", n.getString(3))
}
fun testRangeWithDifferentOperations() {
val n = create {
string("a\$b\$c", operation = INTERNAL_TO_CLASS_ID, range = 2)
string("d\$e\$f", operation = NONE, substringIndex = listOf(2, 5))
}
assertEquals("a.b.c", n.getString(0))
assertEquals("d.e.f", n.getString(1))
}
}
@@ -22,6 +22,45 @@ import "core/deserialization/src/descriptors.proto";
option java_outer_classname = "JvmProtoBuf";
option optimize_for = LITE_RUNTIME;
message StringTableTypes {
message Record {
// The number of times this record should be repeated; this is used to collapse identical subsequent records in the list
optional int32 range = 1 [default = 1];
// Index of the predefined constant. If this field is present, the associated string is ignored
optional int32 predefined_index = 2;
enum Operation {
NONE = 0;
// replaceAll('$', '.')
// java/util/Map$Entry -> java/util/Map.Entry;
INTERNAL_TO_CLASS_ID = 1;
// substring(1, length - 1) and then replaceAll('$', '.')
// Ljava/util/Map$Entry; -> java/util/Map.Entry
DESC_TO_CLASS_ID = 2;
}
// Perform a described operation on the string
optional Operation operation = 3 [default = NONE];
// If this field is present, the "substring" operation must be performed with the first element of this list as the start index,
// and the second element as the end index.
// If an operation is not NONE, it's applied _after_ this substring operation
repeated int32 substring_index = 4 [packed = true];
// If this field is present, the "replaceAll" operation must be performed with the first element of this list as the code point
// of the character to replace, and the second element as the code point of the replacement character
repeated int32 replace_char = 5 [packed = true];
}
repeated Record record = 1;
// Indices of strings which are names of local classes or anonymous objects
repeated int32 local_name = 5 [packed = true];
}
message JvmMethodSignature {
required int32 name = 1 [(string_id_in_table) = true];
@@ -0,0 +1,136 @@
/*
* Copyright 2010-2015 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.kotlin.load.kotlin
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record.Operation.DESC_TO_CLASS_ID
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record.Operation.INTERNAL_TO_CLASS_ID
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record.Operation.NONE
import java.util.*
class JvmNameResolver(
private val types: JvmProtoBuf.StringTableTypes,
private val strings: Array<String>
) : NameResolver {
private val localNameIndices = types.localNameList.run { if (isEmpty()) emptySet() else toSet() }
// Here we expand the 'range' field of the Record message for simplicity to a list of records
private val records: List<Record> = ArrayList<Record>().apply {
val records = types.recordList
this.ensureCapacity(records.size())
for (record in records) {
repeat(record.range) {
this.add(record)
}
}
this.trimToSize()
}
override fun getString(index: Int): String {
val record = records[index]
var string =
if (record.hasPredefinedIndex() && record.predefinedIndex in PREDEFINED_STRINGS.indices)
PREDEFINED_STRINGS[record.predefinedIndex]
else strings[index]
if (record.substringIndexCount >= 2) {
val (begin, end) = record.substringIndexList
if (0 <= begin && begin <= end && end <= string.length()) {
string = string.substring(begin, end)
}
}
if (record.replaceCharCount >= 2) {
val (from, to) = record.replaceCharList
string = string.replace(from.toChar(), to.toChar())
}
when (record.operation ?: NONE) {
NONE -> {
// Do nothing
}
INTERNAL_TO_CLASS_ID -> {
string = string.replace('$', '.')
}
DESC_TO_CLASS_ID -> {
if (string.length() >= 2) {
string = string.substring(1, string.length() - 1)
}
string = string.replace('$', '.')
}
}
return string
}
override fun getName(index: Int) = Name.guess(getString(index))
override fun getClassId(index: Int): ClassId {
val string = getString(index)
val lastSlash = string.lastIndexOf('/')
val packageName =
if (lastSlash < 0) FqName.ROOT
else FqName(string.substring(0, lastSlash).replace('/', '.'))
val className = FqName(string.substring(lastSlash + 1))
return ClassId(packageName, className, index in localNameIndices)
}
companion object {
val PREDEFINED_STRINGS = listOf(
"kotlin/Any",
"kotlin/Nothing",
"kotlin/Unit",
"kotlin/Throwable",
"kotlin/Number",
"kotlin/Byte", "kotlin/Double", "kotlin/Float", "kotlin/Int",
"kotlin/Long", "kotlin/Short", "kotlin/Boolean", "kotlin/Char",
"kotlin/CharSequence",
"kotlin/String",
"kotlin/Comparable",
"kotlin/Enum",
"kotlin/Array",
"kotlin/ByteArray", "kotlin/DoubleArray", "kotlin/FloatArray", "kotlin/IntArray",
"kotlin/LongArray", "kotlin/ShortArray", "kotlin/BooleanArray", "kotlin/CharArray",
"kotlin/Cloneable",
"kotlin/Annotation",
"kotlin/Iterable", "kotlin/MutableIterable",
"kotlin/Collection", "kotlin/MutableCollection",
"kotlin/List", "kotlin/MutableList",
"kotlin/Set", "kotlin/MutableSet",
"kotlin/Map", "kotlin/MutableMap",
"kotlin/Map.Entry", "kotlin/MutableMap.MutableEntry",
"kotlin/Iterator", "kotlin/MutableIterator",
"kotlin/ListIterator", "kotlin/MutableListIterator"
)
private val PREDEFINED_STRINGS_MAP = PREDEFINED_STRINGS.withIndex().toMap({ it.value }, { it.index })
fun getPredefinedStringIndex(string: String): Int? = PREDEFINED_STRINGS_MAP[string]
}
}
@@ -17,10 +17,10 @@
package org.jetbrains.kotlin.serialization.jvm
import com.google.protobuf.ExtensionRegistryLite
import org.jetbrains.kotlin.load.kotlin.JvmNameResolver
import org.jetbrains.kotlin.serialization.ClassData
import org.jetbrains.kotlin.serialization.PackageData
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
import java.io.ByteArrayInputStream
public object JvmProtoBufUtil {
@@ -37,7 +37,7 @@ public object JvmProtoBufUtil {
@JvmStatic
public fun readClassDataFrom(bytes: ByteArray, strings: Array<String>): ClassData {
val input = ByteArrayInputStream(bytes)
val nameResolver = NameResolverImpl.read(input)
val nameResolver = JvmNameResolver(JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, EXTENSION_REGISTRY), strings)
val classProto = ProtoBuf.Class.parseFrom(input, EXTENSION_REGISTRY)
return ClassData(nameResolver, classProto)
}
@@ -49,7 +49,7 @@ public object JvmProtoBufUtil {
@JvmStatic
public fun readPackageDataFrom(bytes: ByteArray, strings: Array<String>): PackageData {
val input = ByteArrayInputStream(bytes)
val nameResolver = NameResolverImpl.read(input)
val nameResolver = JvmNameResolver(JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, EXTENSION_REGISTRY), strings)
val packageProto = ProtoBuf.Package.parseFrom(input, EXTENSION_REGISTRY)
return PackageData(nameResolver, packageProto)
}
@@ -10043,7 +10043,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
boolean hasFlags();
@@ -10060,7 +10062,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
int getFlags();
@@ -11277,7 +11281,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public boolean hasFlags() {
@@ -11296,7 +11302,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public int getFlags() {
@@ -11907,7 +11915,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public boolean hasFlags() {
@@ -11926,7 +11936,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public int getFlags() {
@@ -11945,7 +11957,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public Builder setFlags(int value) {
@@ -11967,7 +11981,9 @@ public final class ProtoBuf {
*hasGetter
*hasSetter
*hasConstant
*isConst
*lateinit
*isOperator
* </pre>
*/
public Builder clearFlags() {
@@ -17,11 +17,12 @@
package kotlin.reflect.jvm
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.load.kotlin.JvmNameResolver
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import kotlin.jvm.internal.KotlinCallable
import kotlin.reflect.KFunction
@@ -37,7 +38,10 @@ import kotlin.reflect.jvm.internal.getOrCreateModule
public fun <R> Function<R>.reflect(): KFunction<R>? {
val callable = javaClass.getAnnotation(KotlinCallable::class.java) ?: return null
val input = BitEncoding.decodeBytes(callable.data).inputStream()
val nameResolver = NameResolverImpl.read(input)
val nameResolver = JvmNameResolver(
JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY),
callable.strings
)
val proto = ProtoBuf.Callable.parseFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY)
val moduleData = javaClass.getOrCreateModule()
val context = DeserializationContext(moduleData.deserialization, nameResolver, moduleData.module,
@@ -16,28 +16,28 @@
package org.jetbrains.kotlin.jps.build.classFilesComparison
import java.io.File
import org.junit.Assert.*
import org.jetbrains.org.objectweb.asm.util.TraceClassVisitor
import java.io.PrintWriter
import org.jetbrains.org.objectweb.asm.ClassReader
import java.io.StringWriter
import org.jetbrains.kotlin.utils.Printer
import com.google.common.io.Files
import com.google.common.hash.Hashing
import com.intellij.openapi.util.io.FileUtil
import com.google.common.collect.Sets
import java.util.HashSet
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
import org.jetbrains.kotlin.serialization.jvm.DebugJvmProtoBuf
import com.google.common.hash.Hashing
import com.google.common.io.Files
import com.google.protobuf.ExtensionRegistry
import java.io.ByteArrayInputStream
import org.jetbrains.kotlin.serialization.DebugProtoBuf
import java.util.Arrays
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.jps.incremental.LocalFileKotlinClass
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatiblePackageFacadeKind
import org.jetbrains.kotlin.serialization.DebugProtoBuf
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
import org.jetbrains.kotlin.serialization.jvm.DebugJvmProtoBuf
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.util.TraceClassVisitor
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import java.io.ByteArrayInputStream
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.util.*
// Set this to true if you want to dump all bytecode (test will fail in this case)
val DUMP_ALL = System.getProperty("comparison.dump.all") == "true"
@@ -139,8 +139,7 @@ fun classFileToString(classFile: File): String {
ByteArrayInputStream(BitEncoding.decodeBytes(annotationDataEncoded)).use {
input ->
out.write("\n------ simpleNames proto -----\n${DebugProtoBuf.StringTable.parseDelimitedFrom(input)}")
out.write("\n------ qualifiedNames proto -----\n${DebugProtoBuf.QualifiedNameTable.parseDelimitedFrom(input)}")
out.write("\n------ string table types proto -----\n${DebugJvmProtoBuf.StringTableTypes.parseDelimitedFrom(input)}")
when {
classHeader!!.isCompatiblePackageFacadeKind() ->