Giter VIP home page Giter VIP logo

poko's Introduction

poko's People

Contributors

3flex avatar drewhamilton avatar jakewharton avatar lupuuss avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

poko's Issues

Compilation with no constructor properties

Currently, an empty primary constructor successfully compiles. However, it later causes R8 to error (in Android projects).

Figure out if this is producing invalid bytecode or if something else is wrong. More importantly, define the correct behavior for this use case. Favor disallowing it, like data classes do.

Also consider what to do for non-empty primary constructors with no properties (only plain parameters).

Not working with Jetpack Compose

When a @Poko class is in a module with Compose enabled, compilation fails when trying to generate the hashCode function. The issue only exists with hashCode – so with a manual hashCode override, the plugin skips generating it, but still generates equals and toString just fine. Thus, this TODO is suspicious.

It's also been suggested that this issue may be related but I'm not sure of that.

I'd start by finding the stacktrace in the Kotlin compiler source and going from there.

See https://github.com/drewhamilton/ComposePlayground/tree/poko to reproduce.

java.lang.IllegalStateException: Symbol for public kotlin/String.hashCode|3409210261493131192[0] is unbound
	at org.jetbrains.kotlin.ir.symbols.impl.IrBindablePublicSymbolBase.getOwner(IrPublicSymbolBase.kt:45)
	at org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionPublicSymbolImpl.getOwner(IrPublicSymbolBase.kt:67)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.visitCall(LiveLiteralTransformer.kt:609)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitCall(IrElementTransformerVoid.kt:199)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitCall(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl.accept(IrCallImpl.kt:47)
	at org.jetbrains.kotlin.ir.expressions.IrExpression.transform(IrExpression.kt:33)
	at org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl.transformChildren(IrReturnImpl.kt:41)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitExpression(IrElementTransformerVoid.kt:131)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitReturn(IrElementTransformerVoid.kt:292)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitReturn(IrElementTransformerVoid.kt:293)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitReturn(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl.accept(IrReturnImpl.kt:34)
	at org.jetbrains.kotlin.ir.expressions.IrExpression.transform(IrExpression.kt:33)
	at org.jetbrains.kotlin.ir.expressions.IrExpression.transform(IrExpression.kt:26)
	at org.jetbrains.kotlin.ir.expressions.IrBlockBody.transformChildren(IrBody.kt:62)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitBody(IrElementTransformerVoid.kt:108)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitBlockBody(IrElementTransformerVoid.kt:117)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.access$visitBlockBody$s1031542550(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitBlockBody$1.invoke(LiveLiteralTransformer.kt:795)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitBlockBody$1.invoke(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor.siblings(DurableKeyVisitor.kt:117)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.siblings(LiveLiteralTransformer.kt:193)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.visitBlockBody(LiveLiteralTransformer.kt:794)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitBlockBody(IrElementTransformerVoid.kt:118)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitBlockBody(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.expressions.IrBlockBody.accept(IrBody.kt:54)
	at org.jetbrains.kotlin.ir.expressions.IrBody.transform(IrBody.kt:27)
	at org.jetbrains.kotlin.ir.declarations.IrFunction.transformChildren(IrFunction.kt:71)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitDeclaration(IrElementTransformerVoid.kt:57)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitFunction(IrElementTransformerVoid.kt:69)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:72)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.access$visitSimpleFunction$s1031542550(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitSimpleFunction$1.invoke(LiveLiteralTransformer.kt:654)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitSimpleFunction$1.invoke(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor.enter(DurableKeyVisitor.kt:96)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.enter(LiveLiteralTransformer.kt:191)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.visitSimpleFunction(LiveLiteralTransformer.kt:654)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:73)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.declarations.IrSimpleFunction.accept(IrSimpleFunction.kt:28)
	at org.jetbrains.kotlin.ir.IrElement$DefaultImpls.transform(IrElement.kt:32)
	at org.jetbrains.kotlin.ir.IrElementBase.transform(IrElementBase.kt:19)
	at org.jetbrains.kotlin.ir.util.TransformKt.transformInPlace(transform.kt:35)
	at org.jetbrains.kotlin.ir.declarations.IrClass.transformChildren(IrClass.kt:66)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitDeclaration(IrElementTransformerVoid.kt:57)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitClass(IrElementTransformerVoid.kt:66)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.access$visitClass$s1031542550(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitClass$1.invoke(LiveLiteralTransformer.kt:450)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitClass$1.invoke(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor.siblings(DurableKeyVisitor.kt:117)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor$siblings$1.invoke(DurableKeyVisitor.kt:131)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor.enter(DurableKeyVisitor.kt:96)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor.siblings(DurableKeyVisitor.kt:131)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.siblings(LiveLiteralTransformer.kt:192)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.visitClass(LiveLiteralTransformer.kt:449)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitClass(IrElementTransformerVoid.kt:67)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitClass(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.declarations.IrClass.accept(IrClass.kt:55)
	at org.jetbrains.kotlin.ir.IrElement$DefaultImpls.transform(IrElement.kt:32)
	at org.jetbrains.kotlin.ir.IrElementBase.transform(IrElementBase.kt:19)
	at org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl.transformChildren(IrFileImpl.kt:71)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitPackageFragment(IrElementTransformerVoid.kt:41)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitFile(IrElementTransformerVoid.kt:47)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.access$visitFile$s1031542550(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitFile$1.invoke(LiveLiteralTransformer.kt:496)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer$visitFile$1.invoke(LiveLiteralTransformer.kt:158)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor.siblings(DurableKeyVisitor.kt:117)
	at androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor.root(DurableKeyVisitor.kt:152)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.visitFile(LiveLiteralTransformer.kt:463)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitFile(IrElementTransformerVoid.kt:48)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitFile(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl.accept(IrFileImpl.kt:63)
	at org.jetbrains.kotlin.ir.declarations.IrFile.transform(IrFile.kt:48)
	at org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl.transformChildren(IrModuleFragmentImpl.kt:45)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoidKt.transformChildrenVoid(IrElementTransformerVoid.kt:330)
	at androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer.lower(LiveLiteralTransformer.kt:169)
	at androidx.compose.compiler.plugins.kotlin.ComposeIrGenerationExtension.generate(ComposeIrGenerationExtension.kt:68)
	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory$generateModule$1.invoke(JvmIrCodegenFactory.kt:93)
	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory$generateModule$1.invoke(JvmIrCodegenFactory.kt:89)
	at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment(Psi2IrTranslator.kt:91)
	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.generateModule(JvmIrCodegenFactory.kt:106)
	at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.compileCorrectFiles(KotlinCodegenFacade.java:35)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.generate(KotlinToJVMBytecodeCompiler.kt:595)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:211)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:154)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:169)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:52)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:88)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:98)
	at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:386)
	at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:110)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:286)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:111)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:74)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:607)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:96)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1659)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)

Make Poko annotations use source retention

Poko could reduce its footprint by shipping the annotations with source retention rather than binary retention, and adding the poko-annotations dependency as compileOnly.

I tried using source retention in #16, but immediately reverted it without comment. Evidently it broke something, but I don't remember what, so I'd like to try again (and keep notes if it breaks again).

I also tried using compileOnly when I first introduced the @Poko annotation, but reverted to implementation in #48 because consuming libraries' test compilation units were broken. I suspect this might be fixed if source retention is used at the same time, otherwise maybe there is a more robust way to add a compileOnly dependency to all compilation units.

One possible downside is that @Poko etc. will be highlighted in red in the IDEs of consumers of libraries which use Poko, which will look kind of weird.

Incompatible with Kotlin 2.0.0-Beta2

Consuming build fails on compileKotlin task with error e: java.lang.IncompatibleClassChangeError: class dev.drewhamilton.poko.ir.PokoOrigin cannot inherit from final class org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl

Caused by this change: JetBrains/kotlin@a3b55cf

I didn't see a simple backwards-compatible fix but didn't spend much time on it.

Migrate compiler tests to "normal" unit tests

This is required for #142 as we cannot rely on the embedded Kotlin compiler to the same degree as when targeting the JVM backend, but it also becomes hard or impossible to execute the compiled target's code.

However, Kotlin has a mechanism which is built-in to do this for us: regular tests!

Regular tests written in commonTest will run on all supported targets of the host OS. Since Poko doesn't vary its IR based on backend, we really only need one JVM target, one JS target, and one native target to run each test. This will always be doable on every host OS since linux works on linux, macos on macos, and mingw on windows. JS and JVM work everywhere.

This obviously only works for tests that have successful processing of the annotation. Failing tests can remain executed by the embedded Kotlin compiler, or migrate to a functional test if we want to test it across backends.

I also think this should come with removal of running these tests across multiple JDKs. Since the IR is not varied across different JVM targets, there's little reason to suspect the runtime behavior would be affected by different JVM versions as the reliance of platform APIs is near zero. Technically it is exactly zero as the APIs are all Kotlin APIs which should be guaranteeing behavioral compatibility across JVM versions.

Now maybe in the future when targeting 17 or higher we start using invokedynamic bytecodes on the JVM leveraging Java's ObjectsMethods class which is what records use, but until that day varying the JDK is mostly redundant.

Implement FIR extension

I thought #118 would require this but it didn't. We still want it because it will (eventually) make any Poko warnings or errors show up in the IDE. Also should let us remove some ObsoleteDescriptorBasedAPI usages.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • actions/setup-java v4
  • gradle/actions v3
  • actions/upload-artifact v4
  • actions/checkout v4
  • actions/setup-java v4
  • gradle/actions v3
  • actions/checkout v4
  • actions/setup-java v4
  • actions/download-artifact v4
  • gradle/actions v3
.github/workflows/gradle-wrapper.yml
  • actions/checkout v4
  • gradle/actions v3
.github/workflows/release.yml
  • actions/checkout v4
  • gradle/actions v3
  • actions/setup-java v4
  • gradle/actions v3
gradle
sample/buildSrc/src/main/kotlin/dev/drewhamilton/poko/sample/build/java.kt
gradle.properties
settings.gradle.kts
  • org.gradle.toolchains.foojay-resolver-convention 0.8.0
build.gradle.kts
build-support/settings.gradle.kts
build-support/build.gradle.kts
gradle/libs.versions.toml
  • androidx.compose.compiler:compiler 1.5.13
  • androidx.compose.runtime:runtime 1.6.7
  • com.google.auto.service:auto-service-annotations 1.1.1
  • dev.zacsweers.autoservice:auto-service-ksp 1.1.0
  • junit:junit 4.13.2
  • com.github.tschuchortdev:kotlin-compile-testing 1.5.1
  • dev.zacsweers.kctfork:core 0.4.1
  • com.willowtreeapps.assertk:assertk 0.28.1
  • org.ow2.asm:asm-util 9.7
  • com.google.testparameterinjector:test-parameter-injector 1.16
  • com.github.gmazzo.buildconfig:plugin 5.3.5
  • com.vanniktech:gradle-maven-publish-plugin 0.25.3
  • org.jetbrains.dokka:dokka-gradle-plugin 1.9.20
  • com.android.library 8.4.0
  • org.jetbrains.kotlin.jvm 1.9.23
  • org.jetbrains.kotlin.android 1.9.23
  • org.jetbrains.kotlin.multiplatform 1.9.23
  • org.jetbrains.kotlinx.binary-compatibility-validator 0.14.0
  • com.google.devtools.ksp 1.9.23-1.0.20
poko-annotations/build.gradle.kts
poko-compiler-plugin/gradle.properties
poko-compiler-plugin/build.gradle.kts
poko-gradle-plugin/build.gradle.kts
poko-tests/build.gradle.kts
poko-tests/performance/build.gradle.kts
sample/gradle.properties
sample/properties.gradle
sample/settings.gradle.kts
sample/build.gradle.kts
sample/buildSrc/settings.gradle.kts
sample/buildSrc/build.gradle.kts
sample/compose/build.gradle.kts
sample/jvm/build.gradle.kts
sample/mpp/build.gradle.kts
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.7
sample/gradle/wrapper/gradle-wrapper.properties
  • gradle 8.7

  • Check this box to trigger a request for Renovate to run again on this repository

Compiler error: NoSuchMethod Kotlin 1.7.0-RC

I get the following error when running my gradle build after updating to:

Kotlin: 1.7.0-RC
KSP: 1.7.0-RC-1.0.5
Poko: 0.10.0

(I know it's pre-release, but figured I'd raise awareness that there appears to be an API change coming)

e: java.lang.NoSuchMethodError: 'boolean org.jetbrains.kotlin.ir.declarations.IrClass.isInline()'
	at dev.drewhamilton.poko.ir.PokoMembersTransformer.isPokoClass(PokoMembersTransformer.kt:104)
	at dev.drewhamilton.poko.ir.PokoMembersTransformer.visitFunctionNew(PokoMembersTransformer.kt:77)
	at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitFunction(IrElementTransformerVoidWithContext.kt:83)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitConstructor(IrElementTransformerVoid.kt:75)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitConstructor(IrElementTransformerVoid.kt:76)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitConstructor(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.declarations.IrConstructor.accept(IrConstructor.kt:21)

The code in question is essentially,

public sealed interface Animal {
    public val name: String 
}

@Poko
public class Dog(override val name: String) : Animal

Support multiplatform

I never really knew what data class did on other platforms, but it sounds like they generate the exact same functions that they do on the JVM. Since Poko is implemented in IR, it should theoretically "just work" for other platform targets.

Haven't done much with Kotlin multiplatform so help and/or PRs are welcome.

Steps to support multiplatform:

  • Update annotation module structure to multiplatform
  • Update Gradle plugin to apply to all platforms
  • Add compiler tests for additional platforms
  • Create sample project(s) for additional platform(s)
  • Update CI to build and test all platforms
  • Determine best use of implementation vs. compileOnly
  • Update CI to publish all platforms

Publish lint rules and/or IDE hints

Ideally not Android-specific. Make the IDE tell developers what things are disallowed and/or discouraged, like no primary constructor and array properties. Mimic existing data class hints.

Kotlin 1.8.20 support

0.12.0 fails with the below:

e: java.lang.NoSuchMethodError: 'org.jetbrains.kotlin.ir.declarations.IrVariable org.jetbrains.kotlin.ir.builders.ExpressionHelpersKt.irTemporary$default(org.jetbrains.kotlin.ir.builders.IrStatementsBuilder, org.jetbrains.kotlin.ir.expressions.IrExpression, java.lang.String, org.jetbrains.kotlin.ir.types.IrType, boolean, int, java.lang.Object)'
	at dev.drewhamilton.poko.ir.PokoMembersTransformer.generateEqualsMethodBody(PokoMembersTransformer.kt:175)
	at dev.drewhamilton.poko.ir.PokoMembersTransformer.visitFunctionNew(PokoMembersTransformer.kt:84)
	at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitFunction(IrElementTransformerVoidWithContext.kt:83)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:72)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:73)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:24)
	at org.jetbrains.kotlin.ir.declarations.IrSimpleFunction.accept(IrSimpleFunction.kt:36)

Support IR compiler

Currently this plugin does nothing if the useIR compiler option is turned on in the consuming project. Notably, this makes this plugin incompatible with Jetpack Compose.

Support custom annotations

Consumers should be able to specify their own annotation class to use instead of dev.drewhamilton.extracare.DataApi if they want. Redacted allows this.

I initially left this out because I envisioned the @DataApi annotation eventually needing parameters, like generateBuilder = true etc. But I think it will make more sense to add features like that either as separate annotations or a separate plugin, because annotations with parameters get clunky if they have to be used throughout a whole code base. Thus, it should be safe to allow consumers to provide any annotation they choose.

Cache computed `hashCode` and `toString` for deeply immutable types

Idea from @ZacSweers. Similar to AutoValue's @Memoized annotation.

If a Poko class is deeply immutable—i.e. all of its members are vals and are also deeply immutable—then there is no need to repeatedly re-calculate hashCode and toString results after their first call.

I like the feature but have a few concerns before moving forward:

  • Kotlin data classes don't have this feature. Do I want to add features that data classes don't have?
  • How do I want to approach adding multiple opt-in features: Does each new opt-in feature get a new annotation? Does each new annotation get a corresponding customization Gradle property? It's easy to imagine this getting out of hand. (Also applies to #50.)
    -- Possible alternative to adding new annotations would be a "mode" for Poko to operate in as a whole—but then how does it know which classes are deeply immutable?
  • Is it possible to have mutually exclusive opt-in features? How can I protect against that?

Support for all multiplatform targets

Are there any major blockers for enabling Android Native and Wasm-WASI targets? The lack of support for these targets is currently preventing me from adopting Poko.

Automatically update Compose & AGP version whenever new ones are released

Whenever a new version of Jetpack Compose and/or the AGP version it works with is released, there's a chance the compiler gets more strict about some IR bytecode that Poko is using an outdated API for. This has happened twice so far. We now have tests for this, but we'll need to bump the Compose and/or AGP versions manually whenever they are released. It would be nice to have a pipeline/bot do this and open a PR automatically.

Support CharArrays in toString

Redacted leaves CharArrays out of toString completely and I'm not sure why. Maybe because it would end up printing a bunch of numbers?

Right now Careful does the same because it copied from Redacted.

Support arrays in generated equals function

toString and hashCode already support this. data classes do not, and have an IDE warning about it.

It should be fairly trivial to add support, I'm just not sure if it's a good idea to. It may be unexpected behavior?

Release K2 compatible version

K2 support was added in #223

Is there any chance a new release could be issued please now the Kotlin 2.0 release cycle has begun with beta 1?

Currently I get this in a Kotlin/JVM project using 2.0.0-Beta1:

e: There are some plugins incompatible with language version 2.0:
  dev.drewhamilton.poko.PokoCompilerPluginRegistrar
Please use language version 1.9 or below

Bad bytecode is generated for equals with float members

Where data classes generate

INVOKESTATIC java/lang/Float.compare (FF)I
IFNE L1

Extra Care is generating

IF_ICMPNE L1

Which leads to this stacktrace at consumer compile time:

The root cause java.lang.AssertionError was thrown at: org.jetbrains.kotlin.codegen.optimization.MethodVerifier.transform(MethodVerifier.kt:28)
at org.jetbrains.kotlin.codegen.TransformationMethodVisitor.visitEnd(TransformationMethodVisitor.kt:92)
at org.jetbrains.kotlin.codegen.FunctionCodegen.endVisit(FunctionCodegen.java:929)
... 51 more
Caused by: java.lang.AssertionError: AFTER mandatory stack transformations: incorrect bytecode
at org.jetbrains.kotlin.codegen.optimization.MethodVerifier.transform(MethodVerifier.kt:28)
at org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer.transform(CompositeMethodTransformer.kt:25)
at org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor.performTransformations(OptimizationMethodVisitor.kt:62)
at org.jetbrains.kotlin.codegen.TransformationMethodVisitor.visitEnd(TransformationMethodVisitor.kt:70)
... 52 more
Caused by: java.lang.RuntimeException: org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 13: First argument: expected I, but found F
at org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer.runAnalyzer(MethodTransformer.java:34)
at org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer.analyze(MethodTransformer.java:44)
at org.jetbrains.kotlin.codegen.optimization.MethodVerifier.transform(MethodVerifier.kt:26)
... 55 more
Caused by: org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 13: First argument: expected I, but found F
at org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:291)
at org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer.runAnalyzer(MethodTransformer.java:31)
... 57 more
Caused by: org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException: First argument: expected I, but found F
at org.jetbrains.org.objectweb.asm.tree.analysis.BasicVerifier.binaryOperation(BasicVerifier.java:301)
at org.jetbrains.org.objectweb.asm.tree.analysis.BasicVerifier.binaryOperation(BasicVerifier.java:43)
at org.jetbrains.org.objectweb.asm.tree.analysis.Frame.execute(Frame.java:561)
at org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:187)
... 58 more

Error when running tests with coverage

When running unit tests with coverage in Android Studio I'm getting the following error dialog:

image

After clicking OK the following error message appears in Build Output tab (though coverage also appears as expected):

	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.registerExtensionsFromPlugins$cli(KotlinCoreEnvironment.kt:617)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$ProjectEnvironment.registerExtensionsFromPlugins(KotlinCoreEnvironment.kt:130)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.<init>(KotlinCoreEnvironment.kt:170)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.<init>(KotlinCoreEnvironment.kt)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.createForProduction(KotlinCoreEnvironment.kt:431)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.createCoreEnvironment(K2JVMCompiler.kt:226)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:152)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:52)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:90)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:98)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1500)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.AbstractMethodError: Receiver class dev.drewhamilton.poko.PokoComponentRegistrar does not define or inherit an implementation of the resolved method 'abstract void registerProjectComponents(com.intellij.mock.MockProject, org.jetbrains.kotlin.config.CompilerConfiguration)' of interface org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar.
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.registerExtensionsFromPlugins$cli(KotlinCoreEnvironment.kt:609)
	... 28 more


Kotlin: 1.5.10/1.5.21
Poko: 0.8.1
AGP: 4.2.1/4.2.2
Android Studio: Android Studio 4.2.2 - Build #AI-202.7660.26.42.7486908, built on June 24, 2021

Add the ability to generate a builder

One of the original goals of this plugin, described in https://jakewharton.com/public-api-challenges-in-kotlin/.

Now that the compiler plugin API surface is more mature, this may be more feasible than it was a few years ago. And with FIR plugins, it may soon not be necessary to include an IDE plugin with this as well.

Once this is done, it should also be trivial to expose a lambda-based copy API, which would mimic a useful part of data classes while being safe for API evolution:

val copy = thing.copy {
    prop1 = "new"
    prop2 = "improved"
}

Fix 0.8.1 publication

Gradle plugin marker POM is broken. Snapshots repo doesn't care but Central does.

Invalid POM: /dev/drewhamilton/poko/dev.drewhamilton.poko.gradle.plugin/0.8.1/dev.drewhamilton.poko.gradle.plugin-0.8.1.pom: Project name missing, Project description missing, Project URL missing, License information missing, SCM URL missing, Developer information missing

Need to figure out how to configure it; probably similar to how the release POMs are currently configured.

WASM support

I tried adding WASM support quickly now that AssertK released with it, but the test runs fail. Not sure why. Output is pretty useless.

> Task :poko-tests:wasmNodeTest FAILED
Caching disabled for task ':poko-tests:wasmNodeTest' because:
  Build cache is disabled
Task ':poko-tests:wasmNodeTest' is not up-to-date because:
  Task is untracked because: Should always re-run for WASM
Starting process 'command '/Users/jw/.gradle/nodejs/node-v20.0.0-darwin-arm64/bin/node''. Working directory: /Volumes/dev/other/Poko/build/js/packages/Poko-poko-tests-wasm-test Command: /Users/jw/.gradle/nodejs/node-v20.0.0-darwin-arm64/bin/node --experimental-wasm-gc /Volumes/dev/other/Poko/build/js/packages/Poko-poko-tests-wasm-test/kotlin/runUnitTests.mjs --dryRun
Successfully started process 'command '/Users/jw/.gradle/nodejs/node-v20.0.0-darwin-arm64/bin/node''
Could not execute [report metric STATISTICS_COLLECT_METRICS_OVERHEAD]
Could not execute [report metric STATISTICS_COLLECT_METRICS_OVERHEAD]
Could not execute [report metric STATISTICS_COLLECT_METRICS_OVERHEAD]

FAILURE: Build failed with an exception.

Will try again later, but maybe someone else wants to look in the mean time.

Branch here: main...JakeWharton:Poko:jw.wasm.2023-09-14

Do an optimization pass on platform-specific output

We want to optimize for a sweet spot between performance and code size. This is mostly a placeholder issue until #142 is sufficiently resolved that this becomes a priority and the research is done to see where any problems may lie.

Don't include the transitive annotation dependency if it's not needed

This came out of the conversation started on saket/telephoto#79, on the jvm/android at least the annotations only need to be present at compile time, so it would be nice not to pull it in at runtime. I know there were previous issues with setting it to compileOnly before #163.

One thought: have you considered having the consumer declare the dependency instead? I don't think it's too much to ask to do:

plugins {
    id("dev.drewhamilton.poko")
}

dependencies {
    compileOnly("dev.drewhamilton.poko:poko-annotations")
}

and it could be adjusted to whatever configuration is needed.

Compiler error: NoSuchMethod Kotlin 1.6.20

I get the following error when running my gradle build after updating to:

  • Kotlin: 1.6.20
  • KSP: 1.6.20-1.0.5 and 1.6.20-1.0.4
  • Poko: 0.9.0
java.lang.NoSuchMethodError: 'void org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl.<init>(int, int, org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin, org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol, org.jetbrains.kotlin.name.Name, int, org.jetbrains.kotlin.ir.types.IrType, org.jetbrains.kotlin.ir.types.IrType, boolean, boolean, boolean, boolean)'
	at dev.drewhamilton.poko.ir.PokoMembersTransformer.mutateWithNewDispatchReceiverParameterForParentClass(PokoMembersTransformer.kt:348)
	at dev.drewhamilton.poko.ir.PokoMembersTransformer.visitFunctionNew(PokoMembersTransformer.kt:411)
	at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitFunction(IrElementTransformerVoidWithContext.kt:68)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:72)
	at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:73)

The code in question is essentially,

public sealed interface Animal {
    public val name: String 
}

@Poko
public class Dog(override val name: String) : Animal

Option to omit `toString` generation

Generated toString implementations add to the string pool, and can't be removed by optimizers like R8 due to the general widely-used nature of toString. For this reason it is desirable in some contexts to skip toString—only generate equals and hashCode—on internal value types. It would be nice if Poko had a way to do this for some, but not all, classes in a module/project.

In the meantime, it's possible to achieve the desired behavior manually:

@Poko class Thing(
    val property: OtherThing,
) {
    override fun toString(): String = super.toString()
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.