diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0637ffe7..9bad0175 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,6 +27,7 @@ kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotl kotlin-gradleApi = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin-api", version.ref = "kotlin" } assertk = "com.willowtreeapps.assertk:assertk:0.26.1" +asm-util = "org.ow2.asm:asm-util:9.5" [plugins] diff --git a/poko-compiler-plugin/build.gradle.kts b/poko-compiler-plugin/build.gradle.kts index 61928bdb..f718f370 100644 --- a/poko-compiler-plugin/build.gradle.kts +++ b/poko-compiler-plugin/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { testImplementation(libs.kotlin.compileTesting) testImplementation(libs.junit) testImplementation(libs.assertk) + testImplementation(libs.asm.util) } tasks.withType().configureEach { diff --git a/poko-compiler-plugin/src/main/kotlin/dev/drewhamilton/poko/ir/hashCodeGeneration.kt b/poko-compiler-plugin/src/main/kotlin/dev/drewhamilton/poko/ir/hashCodeGeneration.kt index 73397137..60bfb6e3 100644 --- a/poko-compiler-plugin/src/main/kotlin/dev/drewhamilton/poko/ir/hashCodeGeneration.kt +++ b/poko-compiler-plugin/src/main/kotlin/dev/drewhamilton/poko/ir/hashCodeGeneration.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrNull +import org.jetbrains.kotlin.ir.types.isInt import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.render @@ -139,6 +140,11 @@ private fun IrBlockBodyBuilder.getHashCodeOf( value: IrExpression, messageCollector: MessageCollector, ): IrExpression { + // Fast path for integers which are already their own hashCode value. + if (property.type.isInt()) { + return value + } + val hasArrayContentBasedAnnotation = property.hasArrayContentBasedAnnotation() val classifier = property.type.classifierOrNull diff --git a/poko-compiler-plugin/src/test/kotlin/dev/drewhamilton/poko/PokoCompilerPluginTest.kt b/poko-compiler-plugin/src/test/kotlin/dev/drewhamilton/poko/PokoCompilerPluginTest.kt index 73b6e500..01eef74f 100644 --- a/poko-compiler-plugin/src/test/kotlin/dev/drewhamilton/poko/PokoCompilerPluginTest.kt +++ b/poko-compiler-plugin/src/test/kotlin/dev/drewhamilton/poko/PokoCompilerPluginTest.kt @@ -1,7 +1,9 @@ package dev.drewhamilton.poko +import assertk.all import assertk.assertThat import assertk.assertions.contains +import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.PluginOption @@ -122,6 +124,21 @@ class PokoCompilerPluginTest { } //endregion + //region Performance optimizations + @Test fun `int property does not emit hashCode method invocation`() { + testCompilation( + "api/Primitives", + ) { + val classFile = it.generatedFiles.single { it.name.endsWith(".class") } + val bytecode = bytecodeToText(classFile.readBytes()) + assertThat(bytecode).all { + contains("java/lang/Long.hashCode") + doesNotContain("java/lang/Integer.hashCode") + } + } + } + //endregion + private inline fun testCompilation( vararg sourceFileNames: String, pokoAnnotationName: String = Poko::class.java.name, diff --git a/poko-compiler-plugin/src/test/kotlin/dev/drewhamilton/poko/asm.kt b/poko-compiler-plugin/src/test/kotlin/dev/drewhamilton/poko/asm.kt new file mode 100644 index 00000000..2b1824e6 --- /dev/null +++ b/poko-compiler-plugin/src/test/kotlin/dev/drewhamilton/poko/asm.kt @@ -0,0 +1,16 @@ +package dev.drewhamilton.poko + +import java.io.PrintWriter +import java.io.StringWriter +import org.objectweb.asm.ClassReader +import org.objectweb.asm.util.Textifier +import org.objectweb.asm.util.TraceClassVisitor + +fun bytecodeToText(bytecode: ByteArray): String { + val textifier = Textifier() + ClassReader(bytecode).accept(TraceClassVisitor(null, textifier, null), 0) + + val writer = StringWriter() + textifier.print(PrintWriter(writer)) + return writer.toString().trim() +}