UNPKG

nitro-codegen

Version:

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

171 lines (151 loc) 5.84 kB
import { NitroConfig } from '../../config/NitroConfig.js'; import { capitalizeName, indent } from '../../utils.js'; import { includeHeader } from '../c++/includeNitroHeader.js'; import { createFileMetadataString, isNotDuplicate, toReferenceType, } from '../helpers.js'; import {} from '../types/VariantType.js'; import { KotlinCxxBridgedType } from './KotlinCxxBridgedType.js'; export function createKotlinVariant(variant) { const jsName = variant.variants.map((v) => v.getCode('kotlin')).join('|'); const kotlinName = variant.getAliasName('kotlin'); const namespace = `J${kotlinName}_impl`; const innerClasses = variant.cases.map(([label, v]) => { const innerName = capitalizeName(label); return ` @DoNotStrip data class ${innerName}(@DoNotStrip val value: ${v.getCode('kotlin')}): ${kotlinName}() `.trim(); }); const packageName = NitroConfig.getAndroidPackage('java/kotlin'); const getterCases = variant.cases.map(([label]) => { const innerName = capitalizeName(label); return `is ${innerName} -> value as? T`; }); const isFunctions = variant.cases.map(([label]) => { const innerName = capitalizeName(label); return ` val is${innerName}: Boolean get() = this is ${innerName} `.trim(); }); const createFunctions = variant.cases.map(([label, v]) => { const innerName = capitalizeName(label); return ` @JvmStatic @DoNotStrip fun create(value: ${v.getCode('kotlin')}): ${kotlinName} = ${innerName}(value) `.trim(); }); const code = ` ${createFileMetadataString(`${kotlinName}.kt`)} package ${packageName} import com.facebook.proguard.annotations.DoNotStrip /** * Represents the TypeScript variant "${jsName}". */ @Suppress("ClassName") @DoNotStrip sealed class ${kotlinName} { ${indent(innerClasses.join('\n'), ' ')} inline fun <reified T> getAs(): T? = when (this) { ${indent(getterCases.join('\n'), ' ')} } ${indent(isFunctions.join('\n'), ' ')} companion object { ${indent(createFunctions.join('\n'), ' ')} } } `.trim(); const cxxNamespace = NitroConfig.getCxxNamespace('c++'); const jniClassDescriptor = NitroConfig.getAndroidPackage('c++/jni', kotlinName); const cppCreateFuncs = variant.variants.map((v, i) => { const bridge = new KotlinCxxBridgedType(v); return ` static jni::local_ref<J${kotlinName}> create_${i}(${bridge.asJniReferenceType('alias')} value) { static const auto method = javaClassStatic()->getStaticMethod<J${kotlinName}(${bridge.asJniReferenceType('alias')})>("create"); return method(javaClassStatic(), value); } `.trim(); }); const variantCases = variant.variants.map((v, i) => { const bridge = new KotlinCxxBridgedType(v); return `case ${i}: return create_${i}(${bridge.parseFromCppToKotlin(`std::get<${i}>(variant)`, 'c++')});`; }); const cppGetIfs = variant.cases.map(([label, v]) => { const innerName = capitalizeName(label); const bridge = new KotlinCxxBridgedType(v); return ` if (isInstanceOf(${namespace}::${innerName}::javaClassStatic())) { auto jniValue = static_cast<const ${namespace}::${innerName}*>(this)->getValue(); return ${bridge.parseFromKotlinToCpp('jniValue', 'c++')}; } `.trim(); }); const cppInnerClasses = variant.cases.map(([label, v]) => { const bridge = new KotlinCxxBridgedType(v); const innerName = capitalizeName(label); const descriptor = NitroConfig.getAndroidPackage('c++/jni', `${kotlinName}$${innerName}`); return ` class ${innerName}: public jni::JavaClass<${innerName}, J${kotlinName}> { public: static auto constexpr kJavaDescriptor = "L${descriptor};"; [[nodiscard]] ${bridge.asJniReferenceType('local')} getValue() const { static const auto field = javaClassStatic()->getField<${bridge.getTypeCode('c++')}>("value"); return getFieldValue(field); } }; `.trim(); }); const includes = new KotlinCxxBridgedType(variant) .getRequiredImports() .filter((i) => i.name !== `J${kotlinName}.hpp`) .map((i) => includeHeader(i, true)) .filter(isNotDuplicate); const fbjniCode = ` ${createFileMetadataString(`J${kotlinName}.hpp`)} #pragma once #include <fbjni/fbjni.h> #include <variant> ${includes.join('\n')} namespace ${cxxNamespace} { using namespace facebook; /** * The C++ JNI bridge between the C++ std::variant and the Java class "${kotlinName}". */ class J${kotlinName}: public jni::JavaClass<J${kotlinName}> { public: static auto constexpr kJavaDescriptor = "L${jniClassDescriptor};"; ${indent(cppCreateFuncs.join('\n'), ' ')} static jni::local_ref<J${kotlinName}> fromCpp(${toReferenceType(variant.getCode('c++'))} variant) { switch (variant.index()) { ${indent(variantCases.join('\n'), ' ')} default: throw std::invalid_argument("Variant holds unknown index! (" + std::to_string(variant.index()) + ")"); } } [[nodiscard]] ${variant.getCode('c++')} toCpp() const; }; namespace ${namespace} { ${indent(cppInnerClasses.join('\n\n'), ' ')} } // namespace ${namespace} ${variant.getCode('c++')} J${kotlinName}::toCpp() const { ${indent(cppGetIfs.join(' else '), ' ')} throw std::invalid_argument("Variant is unknown Kotlin instance!"); } } // namespace ${cxxNamespace} `.trim(); const files = []; files.push({ content: code, language: 'kotlin', name: `${kotlinName}.kt`, subdirectory: NitroConfig.getAndroidPackageDirectory(), platform: 'android', }); files.push({ content: fbjniCode, language: 'c++', name: `J${kotlinName}.hpp`, subdirectory: [], platform: 'android', }); return files; }