From 4c9ec56bc807db3865c0e6f9a912a7a80350bea7 Mon Sep 17 00:00:00 2001 From: Michael Nedzelsky Date: Thu, 6 Aug 2015 09:21:06 +0300 Subject: [PATCH] GenerateProtoBufCompare: optimization and generate difference and hashCode methods --- .../protobuf/GenerateProtoBufCompare.kt | 343 +++++++++---- .../jps/incremental/ProtoCompareGenerated.kt | 479 ++++++++++++++++-- 2 files changed, 675 insertions(+), 147 deletions(-) diff --git a/generators/src/org/jetbrains/kotlin/generators/protobuf/GenerateProtoBufCompare.kt b/generators/src/org/jetbrains/kotlin/generators/protobuf/GenerateProtoBufCompare.kt index b669c471be1..b5b42506763 100644 --- a/generators/src/org/jetbrains/kotlin/generators/protobuf/GenerateProtoBufCompare.kt +++ b/generators/src/org/jetbrains/kotlin/generators/protobuf/GenerateProtoBufCompare.kt @@ -50,11 +50,19 @@ class GenerateProtoBufCompare { ) + private val RESULT_NAME = "result" + private val STRING_INDEXES_NANE = "StringIndexes" + private val FQ_NAME_INDEXES_NANE = "FqNameIndexes" + private val OLD_PREFIX = "old" + private val NEW_PREFIX = "new" + private val CHECK_EQAULS_NAME = "checkEquals" + private val HASH_CODE_NAME = "hashCode" + val extentionsMap = DebugJvmProtoBuf.getDescriptor().extensions.groupBy { it.containingType } - val doneMessages: MutableSet = hashSetOf() - val messages: MutableList = arrayListOf() - val repeatedFields: MutableList = arrayListOf() + val allMessages: MutableSet = linkedSetOf() + val messagesToProcess: Queue = linkedListOf() + val repeatedFields: MutableSet = linkedSetOf() fun generate(): String { val sb = StringBuilder() @@ -63,77 +71,115 @@ class GenerateProtoBufCompare { p.println("package org.jetbrains.kotlin.jps.incremental") p.println() + p.println("import org.jetbrains.kotlin.name.FqName") + p.println("import org.jetbrains.kotlin.serialization.Interner") p.println("import org.jetbrains.kotlin.serialization.ProtoBuf") p.println("import org.jetbrains.kotlin.serialization.deserialization.NameResolver") p.println("import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf") + p.println("import java.util.EnumSet") p.println() p.println("/** This file is generated by org.jetbrains.kotlin.generators.protobuf.GenerateProtoBufCompare. DO NOT MODIFY MANUALLY */") p.println() - p.println("open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, private val newNameResolver: NameResolver) {") + p.println("open class ProtoCompareGenerated(public val oldNameResolver: NameResolver, public val newNameResolver: NameResolver) {") p.pushIndent() - p.println("private val stringIdMap: MutableMap = hashMapOf()") - p.println("private val fqNameIdMap: MutableMap = hashMapOf()") + p.println("private val strings = Interner()") + p.println("public val $OLD_PREFIX$STRING_INDEXES_NANE: IntArray = oldNameResolver.stringTable.stringList.map { strings.intern(it) }.toIntArray()") + p.println("public val $NEW_PREFIX$STRING_INDEXES_NANE: IntArray = newNameResolver.stringTable.stringList.map { strings.intern(it) }.toIntArray()") + + p.println() + p.println("private val fqNames = Interner()") + p.println("public val $OLD_PREFIX$FQ_NAME_INDEXES_NANE: IntArray = oldNameResolver.qualifiedNameTable.qualifiedNameList.indices.map { fqNames.intern(oldNameResolver.getFqName(it)) }.toIntArray()") + p.println("public val $NEW_PREFIX$FQ_NAME_INDEXES_NANE: IntArray = newNameResolver.qualifiedNameTable.qualifiedNameList.indices.map { fqNames.intern(newNameResolver.getFqName(it)) }.toIntArray()") p.println() val fileDescriptor = DebugProtoBuf.getDescriptor() - messages.add(fileDescriptor.findMessageTypeByName("Package")) - messages.add(fileDescriptor.findMessageTypeByName("Class")) + addMessageToProcessIfNeeded(fileDescriptor.findMessageTypeByName("Package")) + addMessageToProcessIfNeeded(fileDescriptor.findMessageTypeByName("Class")) + val generateDifference = allMessages.toSet() - while (!messages.isEmpty()) { - val messageDescriptor = messages.remove(0) - doneMessages.add(messageDescriptor) + while (messagesToProcess.isNotEmpty()) { p.println() - generateForMessage(messageDescriptor, p) + + val message = messagesToProcess.poll() + generateForMessage(message, p) + + if (message in generateDifference) { + generateDiffForMessage(message, p) + } } - p.println() - repeatedFields.forEach { generateHelperMethodForRepeatedField(it, p) } - generatePredefined(p) - p.popIndent() p.println("}") + allMessages.forEach { generateHashCodeFun(it, p) } + return sb.toString() } - fun generatePredefined(p: Printer) { - p.println("fun checkStringIdEquals(old: Int, new: Int): Boolean {") - p.println(" stringIdMap.get(old)?.let { return it == new }") - p.println() - p.println(" val oldValue = oldNameResolver.stringTable.getString(old)") - p.println(" val newValue = newNameResolver.stringTable.getString(new)") - p.println() - p.println(" return if (oldValue == newValue) { stringIdMap[old] = new; true } else false") - p.println("}") + fun generateHashCodeFun(descriptor: Descriptors.Descriptor, p: Printer) { + val typeName = descriptor.typeName + + val fields = descriptor.fields.filter { !it.isSkip } + val extFields = extentionsMap[descriptor]?.filter { !it.isSkip } ?: emptyList() p.println() - p.println("fun checkNameIdEquals(old: Int, new: Int): Boolean = checkStringIdEquals(old, new)") + p.println("public fun $typeName.$HASH_CODE_NAME(stringIndexes: IntArray, fqNameIndexes: IntArray): Int {") + p.pushIndent() + p.println("var $HASH_CODE_NAME = 1") + fields.forEach { field -> generateHashCodeForField(field, p, false) } + extFields.forEach { field -> generateHashCodeForField(field, p, true) } p.println() - p.println("fun checkFqNameIdEquals(old: Int, new: Int): Boolean {") - p.println(" fqNameIdMap.get(old)?.let { return it == new }") - p.println() - p.println(" val oldValue = oldNameResolver.getFqName(old).asString()") - p.println(" val newValue = newNameResolver.getFqName(new).asString()") - p.println() - p.println(" return if (oldValue == newValue) { fqNameIdMap[old] = new; true } else false") + p.println("return $HASH_CODE_NAME") + + p.popIndent() p.println("}") } - fun generateForMessage(descriptor: Descriptors.Descriptor, p: Printer) { - val typeName = descriptor.typeName() + fun generateHashCodeForField(field: Descriptors.FieldDescriptor, p: Printer, isExtensionField: Boolean) { + val fieldName = field.name.javaName + val capFieldName = fieldName.capitalize() + val outerClassName = field.file.options.javaOuterClassname.removePrefix("Debug") + val fullFieldName = "$outerClassName.$fieldName" - p.println("open fun checkEquals(old: $typeName, new: $typeName): Boolean {") + val upperBound = if (isExtensionField) "getExtensionCount($fullFieldName)" else "${fieldName}Count" + val hasMethod = if (isExtensionField) "hasExtension($fullFieldName)" else "has$capFieldName()" + val fieldValue = if (isExtensionField) "getExtension($fullFieldName)" else fieldName + val repeatedFieldValue = if (isExtensionField) "getExtension($fullFieldName, i)" else "get$capFieldName(i)" + + p.println() + if (field.isRepeated) { + p.println("for(i in 0..$upperBound - 1) {") + p.println(" $HASH_CODE_NAME = 31 * $HASH_CODE_NAME + ${fieldToHashCode(field, repeatedFieldValue)}") + p.println("}") + + } + else if (field.isRequired) { + p.println("$HASH_CODE_NAME = 31 * $HASH_CODE_NAME + ${fieldToHashCode(field, fieldValue)}") + } + else if (field.isOptional) { + p.println("if ($hasMethod) {") + p.println(" $HASH_CODE_NAME = 31 * $HASH_CODE_NAME + ${fieldToHashCode(field, fieldValue)}") + p.println("}") + } + } + + fun generateForMessage(descriptor: Descriptors.Descriptor, p: Printer) { + val typeName = descriptor.typeName + + val fields = descriptor.fields.filter { !it.isSkip } + val extFields = extentionsMap[descriptor]?.filter { !it.isSkip } ?: emptyList() + + p.println("open fun $CHECK_EQAULS_NAME(old: $typeName, new: $typeName): Boolean {") p.pushIndent() - descriptor.fields.forEach { generateForField(it, p) } - - extentionsMap[descriptor]?.let { it.forEach { field -> generateForExtensionField(field, p) }} + fields.forEach { field -> FieldGeneratorImpl(field, p).generate() } + extFields.forEach { field -> ExtFieldGeneratorImpl(field, p).generate() } p.println("return true") @@ -141,127 +187,216 @@ class GenerateProtoBufCompare { p.println("}") } - fun generateForField(field: Descriptors.FieldDescriptor, p: Printer) { - val fieldName = field.name.toJavaName() - val capFieldName = fieldName.capitalize() + fun generateDiffForMessage(descriptor: Descriptors.Descriptor, p: Printer) { + val typeName = descriptor.typeName + val className = typeName.replace(".", "") - if (field.options.getExtension(DebugExtOptionsProtoBuf.skipInComparison)) return + val fields = descriptor.fields.filter { !it.isSkip } + val extFields = extentionsMap[descriptor]?.filter { !it.isSkip } ?: emptyList() + val allFields = fields + extFields - if (field.isRepeated) { - repeatedFields.add(field) - p.println("if (!${field.helperMethodName()}(old, new)) return false") - } - else if (field.isRequired) { - p.printlnIfWithComparison(field, fieldName) - } - else if (field.isOptional) { - p.println("if (old.has$capFieldName() != new.has$capFieldName()) return false") - p.println("if (old.has$capFieldName()) {") - p.printlnIfWithComparison(field, fieldName, withIndent = true) - p.println("}") - } + p.println("public enum class ${className}Kind {") + p.println(allFields.map { " " + it.enumName }.join(",\n ")) + p.println("}") p.println() + p.println("public fun difference(old: $typeName, new: $typeName): EnumSet<${className}Kind> {") + p.pushIndent() - addMessageTypeToProcessIfNeeded(field) + p.println("val $RESULT_NAME = EnumSet.noneOf(javaClass<${className}Kind>())") + p.println() + + fields.forEach { field -> FieldGeneratorForDiff(field, p).generate() } + extFields.forEach { field -> ExtFieldGeneratorForDiff(field, p).generate() } + + p.println("return $RESULT_NAME") + p.popIndent() + p.println("}") } fun generateHelperMethodForRepeatedField(field: Descriptors.FieldDescriptor, p: Printer) { - assert(field.isRepeated, "expected repeated field: ${field.name}") + assert(field.isRepeated) { "expected repeated field: ${field.name}" } - val typeName = field.containingType.typeName() - val fieldName = field.name.toJavaName() + val typeName = field.containingType.typeName + val fieldName = field.name.javaName val capFieldName = fieldName.capitalize() val methodName = field.helperMethodName() + p.println() p.println("open fun $methodName(old: $typeName, new: $typeName): Boolean {") p.pushIndent() p.println("if (old.${fieldName}Count != new.${fieldName}Count) return false") p.println() p.println("for(i in 0..old.${fieldName}Count - 1) {") - p.printlnIfWithComparison(field, "get$capFieldName(i)", withIndent = true) + p.printlnIfWithComparisonIndent(field, "get$capFieldName(i)") p.println("}") p.println() p.println("return true") p.popIndent() p.println("}") - p.println() } - fun generateForExtensionField(field: Descriptors.FieldDescriptor, p: Printer) { - val outerClassName = field.file.options.javaOuterClassname.removePrefix("Debug") - val fieldName = field.name.toJavaName() - if (field.options.getExtension(DebugExtOptionsProtoBuf.skipInComparison)) return + abstract inner class FieldGenerator(val field: Descriptors.FieldDescriptor, val p: Printer) { + val statement = field.getStatement() + abstract fun Descriptors.FieldDescriptor.getStatement(): String + + fun generate() { + if (field.isRepeated) { + printRepeatedField() + } + else if (field.isRequired) { + printRequiredField() + } + else if (field.isOptional) { + printOptionalField() + } + + p.println() + + addMessageTypeToProcessIfNeeded(field) + } + + abstract fun printRepeatedField() + + abstract fun printRequiredField() + + abstract fun printOptionalField() + } + + open inner class FieldGeneratorImpl(field: Descriptors.FieldDescriptor, p: Printer) : FieldGenerator(field, p) { + val fieldName = field.name.javaName + val capFieldName = fieldName.capitalize() + + override fun printRepeatedField() { + repeatedFields.add(field) + p.println("if (!${field.helperMethodName()}(old, new)) $statement") + } + + override fun printRequiredField() { + p.printlnIfWithComparison(field, fieldName, statement) + } + + override fun printOptionalField() { + p.println("if (old.has$capFieldName() != new.has$capFieldName()) $statement") + p.println("if (old.has$capFieldName()) {") + p.printlnIfWithComparisonIndent(field, fieldName, statement) + p.println("}") + } + + override fun Descriptors.FieldDescriptor.getStatement(): String = "return false" + } + + open inner class ExtFieldGeneratorImpl(field: Descriptors.FieldDescriptor, p: Printer) : FieldGenerator(field, p) { + val outerClassName = field.file.options.javaOuterClassname.removePrefix("Debug") + val fieldName = field.name.javaName val fullFieldName = "$outerClassName.$fieldName" - if (field.isRepeated) { - p.println("if (old.getExtensionCount($fullFieldName) != new.getExtensionCount($fullFieldName)) return false") + override fun printRepeatedField() { + p.println("if (old.getExtensionCount($fullFieldName) != new.getExtensionCount($fullFieldName)) $statement") p.println() p.println("for(i in 0..old.getExtensionCount($fullFieldName) - 1) {") - p.printlnIfWithComparison(field, "getExtension($fullFieldName, i)", withIndent = true) + p.printlnIfWithComparisonIndent(field, "getExtension($fullFieldName, i)", statement) p.println("}") - p.println() } - else if (field.isRequired) { - p.printlnIfWithComparison(field, "getExtension($fullFieldName)") + + override fun printRequiredField() { + p.printlnIfWithComparison(field, "getExtension($fullFieldName)", statement) } - else if (field.isOptional) { + + override fun printOptionalField() { p.println("if (old.hasExtension($fullFieldName) != new.hasExtension($fullFieldName)) return false") p.println("if (old.hasExtension($fullFieldName)) {") - p.printlnIfWithComparison(field, "getExtension($fullFieldName)", withIndent = true) + p.printlnIfWithComparisonIndent(field, "getExtension($fullFieldName)", statement) p.println("}") } - p.println() - - addMessageTypeToProcessIfNeeded(field) + override fun Descriptors.FieldDescriptor.getStatement(): String = "return false" } + inner class FieldGeneratorForDiff(field: Descriptors.FieldDescriptor, p: Printer) : FieldGeneratorImpl(field, p) { + override fun Descriptors.FieldDescriptor.getStatement(): String = statementForDiff + } + + inner class ExtFieldGeneratorForDiff(field: Descriptors.FieldDescriptor, p: Printer) : ExtFieldGeneratorImpl(field, p) { + override fun Descriptors.FieldDescriptor.getStatement(): String = statementForDiff + } + + private val Descriptors.FieldDescriptor.statementForDiff: String + get() = "$RESULT_NAME.add(${containingType.typeName.replace(".", "")}Kind.$enumName)" + + private val Descriptors.FieldDescriptor.isSkip: Boolean + get() = options.getExtension(DebugExtOptionsProtoBuf.skipInComparison) + private fun addMessageTypeToProcessIfNeeded(field: Descriptors.FieldDescriptor) { - if (field.javaType == Descriptors.FieldDescriptor.JavaType.MESSAGE && - !doneMessages.contains(field.messageType) && !messages.contains(field.messageType)) { - messages.add(field.messageType) + if (field.javaType == Descriptors.FieldDescriptor.JavaType.MESSAGE) { + addMessageToProcessIfNeeded(field.messageType) } } - private fun Printer.printlnIfWithComparison(field: Descriptors.FieldDescriptor, expr: String, withIndent: Boolean = false) { - val line = when { - field.options.getExtension(DebugExtOptionsProtoBuf.stringIdInTable) -> - "if (!checkStringIdEquals(old.$expr, new.$expr)) return false" - field.options.getExtension(DebugExtOptionsProtoBuf.nameIdInTable) -> - "if (!checkNameIdEquals(old.$expr, new.$expr)) return false" - field.options.getExtension(DebugExtOptionsProtoBuf.fqNameIdInTable) -> - "if (!checkFqNameIdEquals(old.$expr, new.$expr)) return false" - field.javaType in JAVA_TYPES_WITH_INLINED_EQUALS -> - "if (old.$expr != new.$expr) return false" - else -> - "if (!checkEquals(old.$expr, new.$expr)) return false" + private fun addMessageToProcessIfNeeded(descriptor: Descriptors.Descriptor) { + if (descriptor !in allMessages) { + allMessages.add(descriptor) + messagesToProcess.add(descriptor) } - if (withIndent) { - this.pushIndent() + } + + private fun Printer.printlnIfWithComparison(field: Descriptors.FieldDescriptor, expr: String, statement: String = "return false") { + val line = when { + field.options.getExtension(DebugExtOptionsProtoBuf.stringIdInTable), + field.options.getExtension(DebugExtOptionsProtoBuf.nameIdInTable) -> + "if ($OLD_PREFIX$STRING_INDEXES_NANE[old.$expr] != $NEW_PREFIX$STRING_INDEXES_NANE[new.$expr]) $statement" + field.options.getExtension(DebugExtOptionsProtoBuf.fqNameIdInTable) -> + "if ($OLD_PREFIX$FQ_NAME_INDEXES_NANE[old.$expr] != $NEW_PREFIX$FQ_NAME_INDEXES_NANE[new.$expr]) $statement" + field.javaType in JAVA_TYPES_WITH_INLINED_EQUALS -> + "if (old.$expr != new.$expr) $statement" + else -> + "if (!$CHECK_EQAULS_NAME(old.$expr, new.$expr)) $statement" } this.println(line) - if (withIndent) { - this.popIndent() - } } - private fun Descriptors.Descriptor.typeName(): String { - val outerClassName = this.file.options.javaOuterClassname.removePrefix("Debug") - val packageHeader = this.file.`package` - return outerClassName + this.fullName.removePrefix(packageHeader) + fun Printer.printlnIfWithComparisonIndent(field: Descriptors.FieldDescriptor, expr: String, statement: String = "return false") { + pushIndent() + printlnIfWithComparison(field, expr, statement) + popIndent() } + private fun fieldToHashCode(field: Descriptors.FieldDescriptor, expr: String): String = + when { + field.options.getExtension(DebugExtOptionsProtoBuf.stringIdInTable), + field.options.getExtension(DebugExtOptionsProtoBuf.nameIdInTable) -> + "stringIndexes[$expr]" + field.options.getExtension(DebugExtOptionsProtoBuf.fqNameIdInTable) -> + "fqNameIndexes[$expr]" + field.javaType == Descriptors.FieldDescriptor.JavaType.INT -> + "$expr" + field.javaType in JAVA_TYPES_WITH_INLINED_EQUALS -> + "$expr.$HASH_CODE_NAME()" + else -> + "$expr.$HASH_CODE_NAME(stringIndexes, fqNameIndexes)" + } + + private val Descriptors.Descriptor.typeName: String + get() { + val outerClassName = file.options.javaOuterClassname.removePrefix("Debug") + val packageHeader = file.`package` + return outerClassName + fullName.removePrefix(packageHeader) + } + + private val Descriptors.FieldDescriptor.enumName: String + get() = (name.javaName + (if (isRepeated) "List" else "")).replace("[A-Z]".toRegex()) { "_" + it.value }.toUpperCase() + private fun Descriptors.FieldDescriptor.helperMethodName(): String { val packageHeader = this.file.`package` val descriptor = this.containingType val className = descriptor.fullName.removePrefix(packageHeader).replace(".", "") - val capFieldName = this.name.toJavaName().capitalize() - return "checkEquals$className$capFieldName" + val capFieldName = this.name.javaName.capitalize() + return "$CHECK_EQAULS_NAME$className$capFieldName" } - private fun String.toJavaName() = this.split("_").map { it.capitalize() }.join("").decapitalize() + private val String.javaName: String + get() = this.split("_").map { it.capitalize() }.join("").decapitalize() } \ No newline at end of file diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/ProtoCompareGenerated.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/ProtoCompareGenerated.kt index d8c1a262027..594c1ec907a 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/ProtoCompareGenerated.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/ProtoCompareGenerated.kt @@ -16,15 +16,23 @@ package org.jetbrains.kotlin.jps.incremental +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.serialization.Interner import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.deserialization.NameResolver import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf +import java.util.EnumSet /** This file is generated by org.jetbrains.kotlin.generators.protobuf.GenerateProtoBufCompare. DO NOT MODIFY MANUALLY */ -open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, private val newNameResolver: NameResolver) { - private val stringIdMap: MutableMap = hashMapOf() - private val fqNameIdMap: MutableMap = hashMapOf() +open class ProtoCompareGenerated(public val oldNameResolver: NameResolver, public val newNameResolver: NameResolver) { + private val strings = Interner() + public val oldStringIndexes: IntArray = oldNameResolver.stringTable.stringList.map { strings.intern(it) }.toIntArray() + public val newStringIndexes: IntArray = newNameResolver.stringTable.stringList.map { strings.intern(it) }.toIntArray() + + private val fqNames = Interner() + public val oldFqNameIndexes: IntArray = oldNameResolver.qualifiedNameTable.qualifiedNameList.indices.map { fqNames.intern(oldNameResolver.getFqName(it)) }.toIntArray() + public val newFqNameIndexes: IntArray = newNameResolver.qualifiedNameTable.qualifiedNameList.indices.map { fqNames.intern(newNameResolver.getFqName(it)) }.toIntArray() open fun checkEquals(old: ProtoBuf.Package, new: ProtoBuf.Package): Boolean { @@ -32,6 +40,17 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv return true } + public enum class ProtoBufPackageKind { + MEMBER_LIST + } + + public fun difference(old: ProtoBuf.Package, new: ProtoBuf.Package): EnumSet { + val result = EnumSet.noneOf(javaClass()) + + if (!checkEqualsPackageMember(old, new)) result.add(ProtoBufPackageKind.MEMBER_LIST) + + return result + } open fun checkEquals(old: ProtoBuf.Class, new: ProtoBuf.Class): Boolean { if (old.hasFlags() != new.hasFlags()) return false @@ -39,11 +58,11 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.flags != new.flags) return false } - if (!checkFqNameIdEquals(old.fqName, new.fqName)) return false + if (oldFqNameIndexes[old.fqName] != newFqNameIndexes[new.fqName]) return false if (old.hasCompanionObjectName() != new.hasCompanionObjectName()) return false if (old.hasCompanionObjectName()) { - if (!checkNameIdEquals(old.companionObjectName, new.companionObjectName)) return false + if (oldStringIndexes[old.companionObjectName] != newStringIndexes[new.companionObjectName]) return false } if (!checkEqualsClassTypeParameter(old, new)) return false @@ -69,9 +88,62 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (!checkEquals(old.getExtension(JvmProtoBuf.classAnnotation, i), new.getExtension(JvmProtoBuf.classAnnotation, i))) return false } - return true } + public enum class ProtoBufClassKind { + FLAGS, + FQ_NAME, + COMPANION_OBJECT_NAME, + TYPE_PARAMETER_LIST, + SUPERTYPE_LIST, + NESTED_CLASS_NAME_LIST, + MEMBER_LIST, + ENUM_ENTRY_LIST, + PRIMARY_CONSTRUCTOR, + SECONDARY_CONSTRUCTOR_LIST, + CLASS_ANNOTATION_LIST + } + + public fun difference(old: ProtoBuf.Class, new: ProtoBuf.Class): EnumSet { + val result = EnumSet.noneOf(javaClass()) + + if (old.hasFlags() != new.hasFlags()) result.add(ProtoBufClassKind.FLAGS) + if (old.hasFlags()) { + if (old.flags != new.flags) result.add(ProtoBufClassKind.FLAGS) + } + + if (oldFqNameIndexes[old.fqName] != newFqNameIndexes[new.fqName]) result.add(ProtoBufClassKind.FQ_NAME) + + if (old.hasCompanionObjectName() != new.hasCompanionObjectName()) result.add(ProtoBufClassKind.COMPANION_OBJECT_NAME) + if (old.hasCompanionObjectName()) { + if (oldStringIndexes[old.companionObjectName] != newStringIndexes[new.companionObjectName]) result.add(ProtoBufClassKind.COMPANION_OBJECT_NAME) + } + + if (!checkEqualsClassTypeParameter(old, new)) result.add(ProtoBufClassKind.TYPE_PARAMETER_LIST) + + if (!checkEqualsClassSupertype(old, new)) result.add(ProtoBufClassKind.SUPERTYPE_LIST) + + if (!checkEqualsClassNestedClassName(old, new)) result.add(ProtoBufClassKind.NESTED_CLASS_NAME_LIST) + + if (!checkEqualsClassMember(old, new)) result.add(ProtoBufClassKind.MEMBER_LIST) + + if (!checkEqualsClassEnumEntry(old, new)) result.add(ProtoBufClassKind.ENUM_ENTRY_LIST) + + if (old.hasPrimaryConstructor() != new.hasPrimaryConstructor()) result.add(ProtoBufClassKind.PRIMARY_CONSTRUCTOR) + if (old.hasPrimaryConstructor()) { + if (!checkEquals(old.primaryConstructor, new.primaryConstructor)) result.add(ProtoBufClassKind.PRIMARY_CONSTRUCTOR) + } + + if (!checkEqualsClassSecondaryConstructor(old, new)) result.add(ProtoBufClassKind.SECONDARY_CONSTRUCTOR_LIST) + + if (old.getExtensionCount(JvmProtoBuf.classAnnotation) != new.getExtensionCount(JvmProtoBuf.classAnnotation)) result.add(ProtoBufClassKind.CLASS_ANNOTATION_LIST) + + for(i in 0..old.getExtensionCount(JvmProtoBuf.classAnnotation) - 1) { + if (!checkEquals(old.getExtension(JvmProtoBuf.classAnnotation, i), new.getExtension(JvmProtoBuf.classAnnotation, i))) result.add(ProtoBufClassKind.CLASS_ANNOTATION_LIST) + } + + return result + } open fun checkEquals(old: ProtoBuf.Callable, new: ProtoBuf.Callable): Boolean { if (old.hasFlags() != new.hasFlags()) return false @@ -96,7 +168,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (!checkEquals(old.receiverType, new.receiverType)) return false } - if (!checkNameIdEquals(old.name, new.name)) return false + if (oldStringIndexes[old.name] != newStringIndexes[new.name]) return false if (!checkEqualsCallableValueParameter(old, new)) return false @@ -114,7 +186,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.hasExtension(JvmProtoBuf.implClassName) != new.hasExtension(JvmProtoBuf.implClassName)) return false if (old.hasExtension(JvmProtoBuf.implClassName)) { - if (!checkNameIdEquals(old.getExtension(JvmProtoBuf.implClassName), new.getExtension(JvmProtoBuf.implClassName))) return false + if (oldStringIndexes[old.getExtension(JvmProtoBuf.implClassName)] != newStringIndexes[new.getExtension(JvmProtoBuf.implClassName)]) return false } return true @@ -123,7 +195,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv open fun checkEquals(old: ProtoBuf.TypeParameter, new: ProtoBuf.TypeParameter): Boolean { if (old.id != new.id) return false - if (!checkNameIdEquals(old.name, new.name)) return false + if (oldStringIndexes[old.name] != newStringIndexes[new.name]) return false if (old.hasReified() != new.hasReified()) return false if (old.hasReified()) { @@ -150,7 +222,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.hasFlexibleTypeCapabilitiesId() != new.hasFlexibleTypeCapabilitiesId()) return false if (old.hasFlexibleTypeCapabilitiesId()) { - if (!checkStringIdEquals(old.flexibleTypeCapabilitiesId, new.flexibleTypeCapabilitiesId)) return false + if (oldStringIndexes[old.flexibleTypeCapabilitiesId] != newStringIndexes[new.flexibleTypeCapabilitiesId]) return false } if (old.hasFlexibleUpperBound() != new.hasFlexibleUpperBound()) return false @@ -160,7 +232,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.hasConstructorClassName() != new.hasConstructorClassName()) return false if (old.hasConstructorClassName()) { - if (!checkFqNameIdEquals(old.constructorClassName, new.constructorClassName)) return false + if (oldFqNameIndexes[old.constructorClassName] != newFqNameIndexes[new.constructorClassName]) return false } if (old.hasConstructorTypeParameter() != new.hasConstructorTypeParameter()) return false @@ -174,7 +246,6 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (!checkEquals(old.getExtension(JvmProtoBuf.typeAnnotation, i), new.getExtension(JvmProtoBuf.typeAnnotation, i))) return false } - if (old.hasExtension(JvmProtoBuf.isRaw) != new.hasExtension(JvmProtoBuf.isRaw)) return false if (old.hasExtension(JvmProtoBuf.isRaw)) { if (old.getExtension(JvmProtoBuf.isRaw) != new.getExtension(JvmProtoBuf.isRaw)) return false @@ -193,7 +264,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv } open fun checkEquals(old: ProtoBuf.Annotation, new: ProtoBuf.Annotation): Boolean { - if (!checkFqNameIdEquals(old.id, new.id)) return false + if (oldFqNameIndexes[old.id] != newFqNameIndexes[new.id]) return false if (!checkEqualsAnnotationArgument(old, new)) return false @@ -206,7 +277,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.flags != new.flags) return false } - if (!checkNameIdEquals(old.name, new.name)) return false + if (oldStringIndexes[old.name] != newStringIndexes[new.name]) return false if (!checkEquals(old.type, new.type)) return false @@ -224,7 +295,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv } open fun checkEquals(old: JvmProtoBuf.JvmMethodSignature, new: JvmProtoBuf.JvmMethodSignature): Boolean { - if (!checkStringIdEquals(old.name, new.name)) return false + if (oldStringIndexes[old.name] != newStringIndexes[new.name]) return false if (!checkEquals(old.returnType, new.returnType)) return false @@ -272,7 +343,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv } open fun checkEquals(old: ProtoBuf.Annotation.Argument, new: ProtoBuf.Annotation.Argument): Boolean { - if (!checkNameIdEquals(old.nameId, new.nameId)) return false + if (oldStringIndexes[old.nameId] != newStringIndexes[new.nameId]) return false if (!checkEquals(old.value, new.value)) return false @@ -287,7 +358,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.hasClassFqName() != new.hasClassFqName()) return false if (old.hasClassFqName()) { - if (!checkFqNameIdEquals(old.classFqName, new.classFqName)) return false + if (oldFqNameIndexes[old.classFqName] != newFqNameIndexes[new.classFqName]) return false } if (old.hasArrayDimension() != new.hasArrayDimension()) return false @@ -299,7 +370,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv } open fun checkEquals(old: JvmProtoBuf.JvmFieldSignature, new: JvmProtoBuf.JvmFieldSignature): Boolean { - if (!checkStringIdEquals(old.name, new.name)) return false + if (oldStringIndexes[old.name] != newStringIndexes[new.name]) return false if (!checkEquals(old.type, new.type)) return false @@ -334,17 +405,17 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.hasStringValue() != new.hasStringValue()) return false if (old.hasStringValue()) { - if (!checkStringIdEquals(old.stringValue, new.stringValue)) return false + if (oldStringIndexes[old.stringValue] != newStringIndexes[new.stringValue]) return false } if (old.hasClassId() != new.hasClassId()) return false if (old.hasClassId()) { - if (!checkFqNameIdEquals(old.classId, new.classId)) return false + if (oldFqNameIndexes[old.classId] != newFqNameIndexes[new.classId]) return false } if (old.hasEnumValueId() != new.hasEnumValueId()) return false if (old.hasEnumValueId()) { - if (!checkNameIdEquals(old.enumValueId, new.enumValueId)) return false + if (oldStringIndexes[old.enumValueId] != newStringIndexes[new.enumValueId]) return false } if (old.hasAnnotation() != new.hasAnnotation()) return false @@ -391,7 +462,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.nestedClassNameCount != new.nestedClassNameCount) return false for(i in 0..old.nestedClassNameCount - 1) { - if (!checkNameIdEquals(old.getNestedClassName(i), new.getNestedClassName(i))) return false + if (oldStringIndexes[old.getNestedClassName(i)] != newStringIndexes[new.getNestedClassName(i)]) return false } return true @@ -411,7 +482,7 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv if (old.enumEntryCount != new.enumEntryCount) return false for(i in 0..old.enumEntryCount - 1) { - if (!checkNameIdEquals(old.getEnumEntry(i), new.getEnumEntry(i))) return false + if (oldStringIndexes[old.getEnumEntry(i)] != newStringIndexes[new.getEnumEntry(i)]) return false } return true @@ -496,24 +567,346 @@ open class ProtoCompareGenerated(private val oldNameResolver: NameResolver, priv return true } - - fun checkStringIdEquals(old: Int, new: Int): Boolean { - stringIdMap.get(old)?.let { return it == new } - - val oldValue = oldNameResolver.stringTable.getString(old) - val newValue = newNameResolver.stringTable.getString(new) - - return if (oldValue == newValue) { stringIdMap[old] = new; true } else false - } - - fun checkNameIdEquals(old: Int, new: Int): Boolean = checkStringIdEquals(old, new) - - fun checkFqNameIdEquals(old: Int, new: Int): Boolean { - fqNameIdMap.get(old)?.let { return it == new } - - val oldValue = oldNameResolver.getFqName(old).asString() - val newValue = newNameResolver.getFqName(new).asString() - - return if (oldValue == newValue) { fqNameIdMap[old] = new; true } else false - } +} + +public fun ProtoBuf.Package.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + for(i in 0..memberCount - 1) { + hashCode = 31 * hashCode + getMember(i).hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun ProtoBuf.Class.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasFlags()) { + hashCode = 31 * hashCode + flags + } + + hashCode = 31 * hashCode + fqNameIndexes[fqName] + + if (hasCompanionObjectName()) { + hashCode = 31 * hashCode + stringIndexes[companionObjectName] + } + + for(i in 0..typeParameterCount - 1) { + hashCode = 31 * hashCode + getTypeParameter(i).hashCode(stringIndexes, fqNameIndexes) + } + + for(i in 0..supertypeCount - 1) { + hashCode = 31 * hashCode + getSupertype(i).hashCode(stringIndexes, fqNameIndexes) + } + + for(i in 0..nestedClassNameCount - 1) { + hashCode = 31 * hashCode + stringIndexes[getNestedClassName(i)] + } + + for(i in 0..memberCount - 1) { + hashCode = 31 * hashCode + getMember(i).hashCode(stringIndexes, fqNameIndexes) + } + + for(i in 0..enumEntryCount - 1) { + hashCode = 31 * hashCode + stringIndexes[getEnumEntry(i)] + } + + if (hasPrimaryConstructor()) { + hashCode = 31 * hashCode + primaryConstructor.hashCode(stringIndexes, fqNameIndexes) + } + + for(i in 0..secondaryConstructorCount - 1) { + hashCode = 31 * hashCode + getSecondaryConstructor(i).hashCode(stringIndexes, fqNameIndexes) + } + + for(i in 0..getExtensionCount(JvmProtoBuf.classAnnotation) - 1) { + hashCode = 31 * hashCode + getExtension(JvmProtoBuf.classAnnotation, i).hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun ProtoBuf.Callable.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasFlags()) { + hashCode = 31 * hashCode + flags + } + + if (hasGetterFlags()) { + hashCode = 31 * hashCode + getterFlags + } + + if (hasSetterFlags()) { + hashCode = 31 * hashCode + setterFlags + } + + for(i in 0..typeParameterCount - 1) { + hashCode = 31 * hashCode + getTypeParameter(i).hashCode(stringIndexes, fqNameIndexes) + } + + if (hasReceiverType()) { + hashCode = 31 * hashCode + receiverType.hashCode(stringIndexes, fqNameIndexes) + } + + hashCode = 31 * hashCode + stringIndexes[name] + + for(i in 0..valueParameterCount - 1) { + hashCode = 31 * hashCode + getValueParameter(i).hashCode(stringIndexes, fqNameIndexes) + } + + hashCode = 31 * hashCode + returnType.hashCode(stringIndexes, fqNameIndexes) + + if (hasExtension(JvmProtoBuf.methodSignature)) { + hashCode = 31 * hashCode + getExtension(JvmProtoBuf.methodSignature).hashCode(stringIndexes, fqNameIndexes) + } + + if (hasExtension(JvmProtoBuf.propertySignature)) { + hashCode = 31 * hashCode + getExtension(JvmProtoBuf.propertySignature).hashCode(stringIndexes, fqNameIndexes) + } + + if (hasExtension(JvmProtoBuf.implClassName)) { + hashCode = 31 * hashCode + stringIndexes[getExtension(JvmProtoBuf.implClassName)] + } + + return hashCode +} + +public fun ProtoBuf.TypeParameter.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + hashCode = 31 * hashCode + id + + hashCode = 31 * hashCode + stringIndexes[name] + + if (hasReified()) { + hashCode = 31 * hashCode + reified.hashCode() + } + + if (hasVariance()) { + hashCode = 31 * hashCode + variance.hashCode() + } + + for(i in 0..upperBoundCount - 1) { + hashCode = 31 * hashCode + getUpperBound(i).hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun ProtoBuf.Type.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + for(i in 0..argumentCount - 1) { + hashCode = 31 * hashCode + getArgument(i).hashCode(stringIndexes, fqNameIndexes) + } + + if (hasNullable()) { + hashCode = 31 * hashCode + nullable.hashCode() + } + + if (hasFlexibleTypeCapabilitiesId()) { + hashCode = 31 * hashCode + stringIndexes[flexibleTypeCapabilitiesId] + } + + if (hasFlexibleUpperBound()) { + hashCode = 31 * hashCode + flexibleUpperBound.hashCode(stringIndexes, fqNameIndexes) + } + + if (hasConstructorClassName()) { + hashCode = 31 * hashCode + fqNameIndexes[constructorClassName] + } + + if (hasConstructorTypeParameter()) { + hashCode = 31 * hashCode + constructorTypeParameter + } + + for(i in 0..getExtensionCount(JvmProtoBuf.typeAnnotation) - 1) { + hashCode = 31 * hashCode + getExtension(JvmProtoBuf.typeAnnotation, i).hashCode(stringIndexes, fqNameIndexes) + } + + if (hasExtension(JvmProtoBuf.isRaw)) { + hashCode = 31 * hashCode + getExtension(JvmProtoBuf.isRaw).hashCode() + } + + return hashCode +} + +public fun ProtoBuf.Class.PrimaryConstructor.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasData()) { + hashCode = 31 * hashCode + data.hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun ProtoBuf.Annotation.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + hashCode = 31 * hashCode + fqNameIndexes[id] + + for(i in 0..argumentCount - 1) { + hashCode = 31 * hashCode + getArgument(i).hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun ProtoBuf.Callable.ValueParameter.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasFlags()) { + hashCode = 31 * hashCode + flags + } + + hashCode = 31 * hashCode + stringIndexes[name] + + hashCode = 31 * hashCode + type.hashCode(stringIndexes, fqNameIndexes) + + if (hasVarargElementType()) { + hashCode = 31 * hashCode + varargElementType.hashCode(stringIndexes, fqNameIndexes) + } + + if (hasExtension(JvmProtoBuf.index)) { + hashCode = 31 * hashCode + getExtension(JvmProtoBuf.index) + } + + return hashCode +} + +public fun JvmProtoBuf.JvmMethodSignature.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + hashCode = 31 * hashCode + stringIndexes[name] + + hashCode = 31 * hashCode + returnType.hashCode(stringIndexes, fqNameIndexes) + + for(i in 0..parameterTypeCount - 1) { + hashCode = 31 * hashCode + getParameterType(i).hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun JvmProtoBuf.JvmPropertySignature.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasField()) { + hashCode = 31 * hashCode + field.hashCode(stringIndexes, fqNameIndexes) + } + + if (hasSyntheticMethod()) { + hashCode = 31 * hashCode + syntheticMethod.hashCode(stringIndexes, fqNameIndexes) + } + + if (hasGetter()) { + hashCode = 31 * hashCode + getter.hashCode(stringIndexes, fqNameIndexes) + } + + if (hasSetter()) { + hashCode = 31 * hashCode + setter.hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun ProtoBuf.Type.Argument.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasProjection()) { + hashCode = 31 * hashCode + projection.hashCode() + } + + if (hasType()) { + hashCode = 31 * hashCode + type.hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode +} + +public fun ProtoBuf.Annotation.Argument.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + hashCode = 31 * hashCode + stringIndexes[nameId] + + hashCode = 31 * hashCode + value.hashCode(stringIndexes, fqNameIndexes) + + return hashCode +} + +public fun JvmProtoBuf.JvmType.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasPrimitiveType()) { + hashCode = 31 * hashCode + primitiveType.hashCode() + } + + if (hasClassFqName()) { + hashCode = 31 * hashCode + fqNameIndexes[classFqName] + } + + if (hasArrayDimension()) { + hashCode = 31 * hashCode + arrayDimension + } + + return hashCode +} + +public fun JvmProtoBuf.JvmFieldSignature.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + hashCode = 31 * hashCode + stringIndexes[name] + + hashCode = 31 * hashCode + type.hashCode(stringIndexes, fqNameIndexes) + + if (hasIsStaticInOuter()) { + hashCode = 31 * hashCode + isStaticInOuter.hashCode() + } + + return hashCode +} + +public fun ProtoBuf.Annotation.Argument.Value.hashCode(stringIndexes: IntArray, fqNameIndexes: IntArray): Int { + var hashCode = 1 + + if (hasType()) { + hashCode = 31 * hashCode + type.hashCode() + } + + if (hasIntValue()) { + hashCode = 31 * hashCode + intValue.hashCode() + } + + if (hasFloatValue()) { + hashCode = 31 * hashCode + floatValue.hashCode() + } + + if (hasDoubleValue()) { + hashCode = 31 * hashCode + doubleValue.hashCode() + } + + if (hasStringValue()) { + hashCode = 31 * hashCode + stringIndexes[stringValue] + } + + if (hasClassId()) { + hashCode = 31 * hashCode + fqNameIndexes[classId] + } + + if (hasEnumValueId()) { + hashCode = 31 * hashCode + stringIndexes[enumValueId] + } + + if (hasAnnotation()) { + hashCode = 31 * hashCode + annotation.hashCode(stringIndexes, fqNameIndexes) + } + + for(i in 0..arrayElementCount - 1) { + hashCode = 31 * hashCode + getArrayElement(i).hashCode(stringIndexes, fqNameIndexes) + } + + return hashCode }