UNPKG

nitro-codegen

Version:

The code-generator for react-native-nitro-modules.

194 lines (169 loc) 5.68 kB
import { NitroConfig } from '../../config/NitroConfig.js' import { indent } from '../../utils.js' import { createKotlinHybridViewManager } from '../../views/kotlin/KotlinHybridViewManager.js' import { getAllTypes } from '../getAllTypes.js' import { getHybridObjectName } from '../getHybridObjectName.js' import { createFileMetadataString } from '../helpers.js' import type { HybridObjectSpec } from '../HybridObjectSpec.js' import { Method } from '../Method.js' import { Property } from '../Property.js' import type { SourceFile } from '../SourceFile.js' import { createFbjniHybridObject } from './FbjniHybridObject.js' import { KotlinCxxBridgedType } from './KotlinCxxBridgedType.js' export function createKotlinHybridObject(spec: HybridObjectSpec): SourceFile[] { const name = getHybridObjectName(spec.name) const properties = spec.properties .map((p) => getPropertyForwardImplementation(p)) .join('\n\n') const methods = spec.methods .map((m) => getMethodForwardImplementation(m)) .join('\n\n') const javaPackage = NitroConfig.getAndroidPackage('java/kotlin') let kotlinBase = spec.isHybridView ? 'HybridView' : 'HybridObject' if (spec.baseTypes.length > 0) { if (spec.baseTypes.length > 1) { throw new Error( `${name.T}: Inheriting from multiple HybridObject bases is not yet supported in Kotlin!` ) } const base = spec.baseTypes[0]!.name kotlinBase = getHybridObjectName(base).HybridTSpec } const imports: string[] = [] imports.push('import com.margelo.nitro.core.*') if (spec.isHybridView) { imports.push('import com.margelo.nitro.views.*') } // 1. Create Kotlin abstract class definition const abstractClassCode = ` ${createFileMetadataString(`${name.HybridTSpec}.kt`)} package ${javaPackage} import androidx.annotation.Keep import com.facebook.jni.HybridData import com.facebook.proguard.annotations.DoNotStrip ${imports.join('\n')} /** * A Kotlin class representing the ${spec.name} HybridObject. * Implement this abstract class to create Kotlin-based instances of ${spec.name}. */ @DoNotStrip @Keep @Suppress( "KotlinJniMissingFunction", "unused", "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" ) abstract class ${name.HybridTSpec}: ${kotlinBase}() { @DoNotStrip private var mHybridData: HybridData = initHybrid() init { super.updateNative(mHybridData) } override fun updateNative(hybridData: HybridData) { mHybridData = hybridData super.updateNative(hybridData) } // Properties ${indent(properties, ' ')} // Methods ${indent(methods, ' ')} private external fun initHybrid(): HybridData companion object { private const val TAG = "${name.HybridTSpec}" } } `.trim() // 2. Create C++ (fbjni) bindings const cppFiles = createFbjniHybridObject(spec) // 3. Create enums or structs in Kotlin const extraFiles = getAllTypes(spec) .map((t) => new KotlinCxxBridgedType(t)) .flatMap((b) => b.getExtraFiles()) const files: SourceFile[] = [] files.push({ content: abstractClassCode, language: 'kotlin', name: `${name.HybridTSpec}.kt`, subdirectory: NitroConfig.getAndroidPackageDirectory(), platform: 'android', }) files.push(...cppFiles) files.push(...extraFiles) if (spec.isHybridView) { const viewFiles = createKotlinHybridViewManager(spec) files.push(...viewFiles) } return files } function getMethodForwardImplementation(method: Method): string { const bridgedReturn = new KotlinCxxBridgedType(method.returnType) const requiresBridge = bridgedReturn.needsSpecialHandling || method.parameters.some((p) => { const bridged = new KotlinCxxBridgedType(p.type) return bridged.needsSpecialHandling }) if (requiresBridge) { const paramsSignature = method.parameters.map((p) => { const bridge = new KotlinCxxBridgedType(p.type) return `${p.name}: ${bridge.getTypeCode('kotlin')}` }) const paramsForward = method.parameters.map((p) => { const bridge = new KotlinCxxBridgedType(p.type) return bridge.parseFromCppToKotlin(p.name, 'kotlin') }) const returnForward = bridgedReturn.parseFromKotlinToCpp( '__result', 'kotlin' ) const code = method.getCode('kotlin', { virtual: true }) return ` ${code} @DoNotStrip @Keep private fun ${method.name}_cxx(${paramsSignature.join(', ')}): ${bridgedReturn.getTypeCode('kotlin')} { val __result = ${method.name}(${paramsForward.join(', ')}) return ${returnForward} } `.trim() } else { const code = method.getCode('kotlin', { doNotStrip: true, virtual: true }) return code } } function getPropertyForwardImplementation(property: Property): string { const bridged = new KotlinCxxBridgedType(property.type) if (bridged.needsSpecialHandling) { let keyword = property.isReadonly ? 'val' : 'var' let lines: string[] = [] lines.push( ` @Keep @DoNotStrip get() { return ${indent(bridged.parseFromKotlinToCpp(property.name, 'kotlin'), ' ')} } `.trim() ) if (!property.isReadonly) { lines.push( ` @Keep @DoNotStrip set(value) { ${property.name} = ${indent(bridged.parseFromCppToKotlin('value', 'kotlin'), ' ')} } `.trim() ) } const code = property.getCode('kotlin', { virtual: true }) return ` ${code} private ${keyword} ${property.name}_cxx: ${bridged.getTypeCode('kotlin')} ${indent(lines.join('\n'), ' ')} `.trim() } else { const code = property.getCode('kotlin', { doNotStrip: true, virtual: true }) return code } }