KMP-NativeCoroutines icon indicating copy to clipboard operation
KMP-NativeCoroutines copied to clipboard

Migrate compiler plugin to KSP

Open rickclephas opened this issue 2 years ago • 6 comments

When the current compiler plugin was created KSP didn't support Kotlin/Native and a custom compiler plugin would allow us to modify classes instead of creating extension functions (which seemed a great idea at the time). However the current compiler plugin has some issues:

  • It can cause recursion errors since it uses non-public APIs to get the required information
    • #4
    • #5
    • #23
    • #27
    • #54
    • #55
  • It's currently incompatible with KSP
    • #50
  • Modifying the classes can cause some issues with inheritance and interface usages from Swift
    • #51
  • The compiler plugin is tied to a specific Kotlin version, so it requires a lot of maintenance
    • #30

Using KSP would solve all these issues. The only downside being that we can no longer modify the classes and would need to generate extension properties/functions, which might not be exposed to ObjC/Swift in the same way.

rickclephas avatar Mar 29 '22 17:03 rickclephas

Instead of generating extension properties/functions (which IIRC aren't handled well in JS), it might also be possible to generate an entirely new class that wraps that just wraps the JVM class:

// original class
class MyClass {
    suspend fun myFun(): String { … }
}

// NativeCoroutines-generated
class MyClassNative(private val delegate: MyClass) {
   suspend fun myFun = delegate.myFun()

    fun myFunNative() = 
        nativeSuspend { delegate.myFun() }
}

Consumers would have to manually convert MyClass to MyClassNative, but they'd be able to avoid relying on extension functions.

ankushg avatar Mar 29 '22 18:03 ankushg

Hmm alright didn't know about any limitations with extensions in JS.

I am actually not a big fan of such wrapper classes. Mainly because:

  • all properties and functions have to be wrapped (not just the coroutines once)
  • using this from Swift/ObjC/JS wouldn't be "straightforward"

I guess it would probably need some more code to get a single wrapper object as well, else you'll be creating a lot of wrapper objects in the client code.

How exactly are extension functions handled in JS? In Swift they are actually exposed as extension functions on classes, the "problem" there is with interfaces (and inline classes).

rickclephas avatar Mar 29 '22 20:03 rickclephas

In Kotlin/JS, you need to annotate everything with @JsExport in order for it to be visible from regular JS code.

@JsExport's documentation says that it doesn't support extension properties 😔

ankushg avatar Mar 29 '22 20:03 ankushg

On further inspection -- it looks like you can @JsExport an extension function, just not extension properties

The way this gets exposed though is by converting the receiver to a parameter. This can cause naming collisions:

class MyClass {
    suspend fun myFun(): String { … }
}

@JsExport
fun MyClass.myFunNative() = nativeSuspend { this.myFun() }
// will generate a method that can be called in JS using `myFunNative(myClassInstance)`

class MyOtherClass {
    suspend fun myFun(): String { … }
}

@JsExport
fun MyOtherClass.myFunNative() = nativeSuspend { this.myFun() }
// will generate conflicting method that can be called in JS using `myFunNative(myOtherClassInstance)`

This likely means that we need to use @JsName to avoid naming collisions, and still doesn't have the same convenient syntax of extension functions in Swift, but is still better than wrapping I suppose

ankushg avatar Mar 30 '22 00:03 ankushg

This would be fantastic. I've been really enjoying this plugin but chasing down all the recursive crashes when I switch from Android to iOS builds are really frustrating. Let me know if there is anything I can do to help push this along.

litclimbing avatar Apr 01 '22 17:04 litclimbing

If the compiler plugin is migrated to KSP we won't be able to modify any of the source code, making #63 impossible. Perhaps a better solution would be to generate the native extensions in the customer compiler (just like KSP would).

rickclephas avatar May 05 '22 16:05 rickclephas