nitro-codegen
Version:
The code-generator for react-native-nitro-modules.
171 lines (159 loc) • 5.71 kB
JavaScript
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}.
*/
abstract class ${name.HybridTSpec}: ${kotlinBase}() {
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}
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(`
get() {
return ${indent(bridged.parseFromKotlinToCpp(property.name, 'kotlin'), ' ')}
}
`.trim());
if (!property.isReadonly) {
lines.push(`
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;
}
}