@file:Suppress("FunctionName") package expo.modules.kotlin.classcomponent import expo.modules.kotlin.AppContext import expo.modules.kotlin.Promise import expo.modules.kotlin.component6 import expo.modules.kotlin.component7 import expo.modules.kotlin.component8 import expo.modules.kotlin.functions.AsyncFunctionBuilder import expo.modules.kotlin.functions.AsyncFunctionComponent import expo.modules.kotlin.functions.AsyncFunctionWithPromiseComponent import expo.modules.kotlin.functions.FunctionBuilder import expo.modules.kotlin.functions.SyncFunctionComponent import expo.modules.kotlin.functions.createAsyncFunctionComponent import expo.modules.kotlin.objects.ObjectDefinitionBuilder import expo.modules.kotlin.objects.PropertyComponentBuilderWithThis import expo.modules.kotlin.sharedobjects.SharedObject import expo.modules.kotlin.sharedobjects.isSharedObjectClass import expo.modules.kotlin.sharedobjects.isSharedRefClass import expo.modules.kotlin.traits.Trait import expo.modules.kotlin.types.AnyType import expo.modules.kotlin.types.TypeConverterProvider import expo.modules.kotlin.types.enforceType import expo.modules.kotlin.types.toAnyType import expo.modules.kotlin.types.toArgsArray import expo.modules.kotlin.types.toReturnType import kotlin.reflect.KClass class ClassComponentBuilder( private val appContext: AppContext, val name: String, private val ownerClass: KClass, val ownerType: AnyType, converters: TypeConverterProvider? = null ) : ObjectDefinitionBuilder(converters) { var constructor: SyncFunctionComponent? = null val traits = mutableListOf>() @PublishedApi internal var staticSyncFunctions = mutableMapOf() @PublishedApi internal var staticSyncFunctionBuilder = mutableMapOf() @PublishedApi internal var staticAsyncFunctions = mutableMapOf() private var staticAsyncFunctionBuilders = mutableMapOf() fun buildClass(): ClassDefinitionData { val hasOwnerType = ownerClass != Unit::class val isSharedObject = hasOwnerType && ownerClass.isSharedObjectClass() val isSharedRef = hasOwnerType && ownerClass.isSharedRefClass() if (eventsDefinition != null && isSharedObject) { listOf("__expo_onStartListeningToEvent" to SharedObject::onStartListeningToEvent, "__expo_onStopListeningToEvent" to SharedObject::onStopListeningToEvent) .forEach { (name, listener) -> SyncFunctionComponent(name, arrayOf(ownerType, toAnyType()), toReturnType()) { (self, eventName) -> enforceType(self, eventName) listener.invoke(self, eventName) }.also { function -> function.enumerable(false) syncFunctions[name] = function } } } val objectData = buildObject() + traits.map { t -> t.export(appContext) }.reduceOrNull { t1, t2 -> t1 + t2 } objectData.functions.forEach { it.ownerType = ownerType.typeDescriptor it.canTakeOwner = true } // TODO: Add an empty constructor that throws when called from JS if (hasOwnerType && constructor == null && !isSharedRef) { throw IllegalArgumentException("constructor cannot be null") } val constructor = constructor ?: SyncFunctionComponent( "constructor", emptyArray(), toReturnType() ) {} constructor.canTakeOwner = true constructor.ownerType = ownerType.typeDescriptor return ClassDefinitionData( name, constructor, staticSyncFunctions + staticSyncFunctionBuilder.mapValues { (_, value) -> value.build() }, staticAsyncFunctions + staticAsyncFunctionBuilders.mapValues { (_, value) -> value.build() }, objectData, isSharedRef ) } fun UseTrait(trait: Trait) { traits.add(trait) } inline fun Constructor( crossinline body: () -> SharedObjectType ): SyncFunctionComponent { // TODO(@lukmccall): figure out how to pass `SharedObjectType` to the function component as a return type return SyncFunctionComponent("constructor", emptyArray(), toReturnType()) { body() }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0) -> enforceType(p0) body(p0) }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0, p1: P1) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1) -> enforceType(p0, p1) body(p0, p1) }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0, p1: P1, p2: P2) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2) -> enforceType(p0, p1, p2) body(p0, p1, p2) }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3) -> enforceType(p0, p1, p2, p3) body(p0, p1, p2, p3) }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4) -> enforceType(p0, p1, p2, p3, p4) body(p0, p1, p2, p3, p4) }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4, p5) -> enforceType(p0, p1, p2, p3, p4, p5) body(p0, p1, p2, p3, p4, p5) }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4, p5, p6) -> enforceType(p0, p1, p2, p3, p4, p5, p6) body(p0, p1, p2, p3, p4, p5, p6) }.also { constructor = it } } inline fun Constructor( crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> SharedObjectType ): SyncFunctionComponent { return SyncFunctionComponent("constructor", toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4, p5, p6, p7) -> enforceType(p0, p1, p2, p3, p4, p5, p6, p7) body(p0, p1, p2, p3, p4, p5, p6, p7) }.also { constructor = it } } /** * Creates the read-only property whose getter takes the caller as an argument. */ inline fun Property(name: String, crossinline body: (owner: SharedObjectType) -> T): PropertyComponentBuilderWithThis { return PropertyComponentBuilderWithThis(ownerType.typeDescriptor, name).also { it.get(body) properties[name] = it } } override fun Property(name: String): PropertyComponentBuilderWithThis { return PropertyComponentBuilderWithThis(ownerType.typeDescriptor, name).also { properties[name] = it } } fun StaticFunction( name: String ) = FunctionBuilder(name).also { staticSyncFunctionBuilder[name] = it } @JvmName("StaticFunctionWithoutArgs") inline fun StaticFunction( name: String, crossinline body: () -> Any? ): SyncFunctionComponent { return SyncFunctionComponent(name, emptyArray(), toReturnType()) { body() }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: () -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, emptyArray(), toReturnType()) { body() }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0) -> enforceType(p0) body(p0) }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0, p1: P1) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1) -> enforceType(p0, p1) body(p0, p1) }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2) -> enforceType(p0, p1, p2) body(p0, p1, p2) }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3) -> enforceType(p0, p1, p2, p3) body(p0, p1, p2, p3) }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4) -> enforceType(p0, p1, p2, p3, p4) body(p0, p1, p2, p3, p4) }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4, p5) -> enforceType(p0, p1, p2, p3, p4, p5) body(p0, p1, p2, p3, p4, p5) }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4, p5, p6) -> enforceType(p0, p1, p2, p3, p4, p5, p6) body(p0, p1, p2, p3, p4, p5, p6) }.also { staticSyncFunctions[name] = it } } inline fun StaticFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R ): SyncFunctionComponent { return SyncFunctionComponent(name, toArgsArray(converterProvider = converters), toReturnType()) { (p0, p1, p2, p3, p4, p5, p6, p7) -> enforceType(p0, p1, p2, p3, p4, p5, p6, p7) body(p0, p1, p2, p3, p4, p5, p6, p7) }.also { staticSyncFunctions[name] = it } } fun StaticAsyncFunction( name: String ) = AsyncFunctionBuilder(name, converters).also { staticAsyncFunctionBuilders[name] = it } @JvmName("StaticAsyncFunctionWithoutArgs") inline fun StaticAsyncFunction( name: String, crossinline body: () -> Any? ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, emptyArray()) { body() }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: () -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, emptyArray()) { body() }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0) -> R ): AsyncFunctionComponent { // We can't split that function, because that introduces a ambiguity when creating DSL component without parameters. return if (P0::class.java == Promise::class.java) { AsyncFunctionWithPromiseComponent(name, arrayOf()) { _, promise -> body(promise as P0) } } else { createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0) -> enforceType(p0) body(p0) } }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1) -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1) -> enforceType(p0, p1) body(p0, p1) }.also { staticAsyncFunctions[name] = it } } @JvmName("StaticAsyncFunctionWithPromise") inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: Promise) -> R ): AsyncFunctionComponent { return AsyncFunctionWithPromiseComponent(name, toArgsArray(converterProvider = converters)) { (p0), promise -> enforceType(p0) body(p0, promise) }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2) -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2) -> enforceType(p0, p1, p2) body(p0, p1, p2) }.also { staticAsyncFunctions[name] = it } } @JvmName("StaticAsyncFunctionWithPromise") inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: Promise) -> R ): AsyncFunctionComponent { return AsyncFunctionWithPromiseComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1), promise -> enforceType(p0, p1) body(p0, p1, promise) }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3) -> enforceType(p0, p1, p2, p3) body(p0, p1, p2, p3) }.also { staticAsyncFunctions[name] = it } } @JvmName("StaticAsyncFunctionWithPromise") inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: Promise) -> R ): AsyncFunctionComponent { return AsyncFunctionWithPromiseComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2), promise -> enforceType(p0, p1, p2) body(p0, p1, p2, promise) }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3, p4) -> enforceType(p0, p1, p2, p3, p4) body(p0, p1, p2, p3, p4) }.also { staticAsyncFunctions[name] = it } } @JvmName("StaticAsyncFunctionWithPromise") inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: Promise) -> R ): AsyncFunctionComponent { return AsyncFunctionWithPromiseComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3), promise -> enforceType(p0, p1, p2, p3) body(p0, p1, p2, p3, promise) }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5) -> enforceType(p0, p1, p2, p3, p4, p5) body(p0, p1, p2, p3, p4, p5) }.also { staticAsyncFunctions[name] = it } } @JvmName("StaticAsyncFunctionWithPromise") inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: Promise) -> R ): AsyncFunctionComponent { return AsyncFunctionWithPromiseComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3, p4), promise -> enforceType(p0, p1, p2, p3, p4) body(p0, p1, p2, p3, p4, promise) }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6) -> enforceType(p0, p1, p2, p3, p4, p5, p6) body(p0, p1, p2, p3, p4, p5, p6) }.also { staticAsyncFunctions[name] = it } } @JvmName("StaticAsyncFunctionWithPromise") inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: Promise) -> R ): AsyncFunctionComponent { return AsyncFunctionWithPromiseComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5), promise -> enforceType(p0, p1, p2, p3, p4, p5) body(p0, p1, p2, p3, p4, p5, promise) }.also { staticAsyncFunctions[name] = it } } inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R ): AsyncFunctionComponent { return createAsyncFunctionComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6, p7) -> enforceType(p0, p1, p2, p3, p4, p5, p6, p7) body(p0, p1, p2, p3, p4, p5, p6, p7) }.also { staticAsyncFunctions[name] = it } } @JvmName("StaticAsyncFunctionWithPromise") inline fun StaticAsyncFunction( name: String, crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: Promise) -> R ): AsyncFunctionComponent { return AsyncFunctionWithPromiseComponent(name, toArgsArray(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6), promise -> enforceType(p0, p1, p2, p3, p4, p5, p6) body(p0, p1, p2, p3, p4, p5, p6, promise) }.also { staticAsyncFunctions[name] = it } } }