UNPKG

nitro-codegen

Version:

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

171 lines (159 loc) 5.71 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 { Method } from '../Method.js'; import { Property } from '../Property.js'; import { createFbjniHybridObject } from './FbjniHybridObject.js'; import { KotlinCxxBridgedType } from './KotlinCxxBridgedType.js'; export function createKotlinHybridObject(spec) { 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 = []; 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 = []; 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) { 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) { const bridged = new KotlinCxxBridgedType(property.type); if (bridged.needsSpecialHandling) { let keyword = property.isReadonly ? 'val' : 'var'; let lines = []; 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; } }