Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@ import { getImportCode, getImportsCode } from '../../syntax/getImportCode.js'
import { createFileMetadataString, isNotDuplicate } from '../../syntax/helpers.js'
import type { HybridObjectSpec } from '../../syntax/HybridObjectSpec.js'
import type { SourceFile } from '../../syntax/SourceFile.js'
import type { JNINativeRegistration } from '../../syntax/kotlin/types/KotlinType.js'
import { indent } from '../../utils.js'
import { createHybridClassRegistration } from '../createHybridClassRegistration.js'

export function createHybridObjectInitializer({ specs, files }: { specs: HybridObjectSpec[]; files: SourceFile[] }): SourceFile[] {
interface HybridObjectInitializerOptions {
specs: readonly HybridObjectSpec[]
jniNativeRegistrations: readonly JNINativeRegistration[]
}

export function createHybridObjectInitializer({ specs, jniNativeRegistrations }: HybridObjectInitializerOptions): SourceFile[] {
const config = NitroConfig.current
const namespace = config.getCxxNamespace('c++')
const library = config.getAndroidCxxLibName()
const name = `${library}OnLoad`
const registrations = specs.map(createHybridClassRegistration)
const natives = files.flatMap((file) => file.jniNativeRegistrations ?? [])
const includes = getImportsCode(
[...natives.map((native) => native.import), ...registrations.flatMap((registration) => registration.imports)],
[...jniNativeRegistrations.map((native) => native.import), ...registrations.flatMap((registration) => registration.imports)],
'implementation'
)
const nativeRegistrations = natives
const nativeRegistrations = jniNativeRegistrations
.map((native) => `${native.namespace.join('::')}::${native.className}::registerNatives();`)
.filter(isNotDuplicate)
const classRegistrations = registrations.map((registration) => registration.registration)
Expand Down
39 changes: 38 additions & 1 deletion packages/nitrogen/src/autolinking/createAndroidAutolinking.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,50 @@
import type { HybridObjectSpec } from '../syntax/HybridObjectSpec.js'
import type { SourceFile } from '../syntax/SourceFile.js'
import { getAllTypes } from '../syntax/getAllTypes.js'
import { getReferencedTypes } from '../syntax/getReferencedTypes.js'
import { getKotlinNativeType } from '../syntax/kotlin/getKotlinNativeType.js'
import { KotlinHybridObjectType } from '../syntax/kotlin/types/KotlinHybridObjectType.js'
import type { KotlinType } from '../syntax/kotlin/types/KotlinType.js'
import { wrapKotlinType } from '../syntax/kotlin/wrapKotlinType.js'
import { HybridObjectType } from '../syntax/types/HybridObjectType.js'
import { KotlinHybridViewType } from '../views/kotlin/KotlinHybridViewType.js'
import type { Autolinking } from './Autolinking.js'
import { createCMakeExtension } from './android/createCMakeExtension.js'
import { createGradleExtension } from './android/createGradleExtension.js'
import { createHybridObjectInitializer } from './android/createHybridObjectInitializer.js'

/** JNI bindings are owned by the Kotlin declarations emitted in this module. */
function getKotlinTypes(specs: readonly HybridObjectSpec[]): KotlinType[] {
return specs.flatMap((spec) => {
if (spec.config.isExternalConfig) return []
switch (spec.language) {
case 'kotlin': {
const baseTypes = getKotlinTypes(spec.baseTypes)
let hybridObject: KotlinHybridObjectType
if (spec.isHybridView) {
hybridObject = new KotlinHybridViewType(spec)
} else {
hybridObject = new KotlinHybridObjectType(new HybridObjectType(spec))
}
// Fabric props start as public C++ types. JNI uses the Kotlin projection.
const cppTypes = getAllTypes(spec).map((type) => getKotlinNativeType(type, spec.config))
const referencedTypes = cppTypes.flatMap(getReferencedTypes)
const kotlinTypes = referencedTypes.map((type) => wrapKotlinType(type))
return [...baseTypes, hybridObject, ...kotlinTypes]
}
case 'c++':
case 'swift':
return []
}
})
}

export function createAndroidAutolinking(specs: HybridObjectSpec[], files: SourceFile[]): Autolinking {
const kotlinTypes = getKotlinTypes(specs)
const jniNativeRegistrations = kotlinTypes.flatMap((type) => type.getJniNativeRegistrations?.() ?? [])
const initializer = createHybridObjectInitializer({ specs: specs, jniNativeRegistrations: jniNativeRegistrations })
return {
platform: 'android',
sourceFiles: [createCMakeExtension(files), createGradleExtension(), ...createHybridObjectInitializer({ specs: specs, files: files })],
sourceFiles: [createCMakeExtension(files), createGradleExtension(), ...initializer],
}
}
5 changes: 1 addition & 4 deletions packages/nitrogen/src/createHybridClassRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,10 @@ function createHybridClassRuntime({ classNames, views = [], outputDirectory }: W
initializer: `NitroModules.getHybridClass('${registryName}')`,
})
const exportsCode = [classExportCode]
// Getters copied by interop helpers share these module-level caches.
const cacheDeclarationsCode = ['let hybridClass']
let declarationsCode: string
let cacheComment: string
if (view != null) {
cacheComment = "// Copies of these getters, e.g. from Babel's interop helpers, share the caches."
const componentInitializerCode = createComponentInitializer(view)
const componentExportCode = createLazyExport({
name: `${name}Component`,
Expand All @@ -142,7 +141,6 @@ function createHybridClassRuntime({ classNames, views = [], outputDirectory }: W
runtimeDirectory: runtimeDirectory,
})
} else {
cacheComment = "// Copies of this getter, e.g. from Babel's interop helpers, share the cached class."
declarationsCode = `
// The spec's \`export declare class ${name}\` types this runtime export.
export {}
Expand All @@ -153,7 +151,6 @@ ${createFileMetadataString(jsFileName)}

const { NitroModules } = require('${NitroConfig.current.getRuntimePackageName()}')

${cacheComment}
${cacheDeclarationsCode.join('\n')}

module.exports = {
Expand Down
4 changes: 2 additions & 2 deletions packages/nitrogen/src/createPlatformSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Property } from './syntax/Property.js'
import { StaticProperty } from './syntax/StaticProperty.js'
import { InstanceMethod } from './syntax/InstanceMethod.js'
import { HybridObjectType } from './syntax/types/HybridObjectType.js'
import { KotlinNativeHybridObjectType } from './syntax/kotlin/types/KotlinNativeHybridObjectType.js'
import { KotlinCxxHybridObjectType } from './syntax/kotlin/types/KotlinCxxHybridObjectType.js'
import { getHybridObjectName } from './syntax/getHybridObjectName.js'
import { StaticMethod } from './syntax/StaticMethod.js'
import { Constructor } from './syntax/Constructor.js'
Expand Down Expand Up @@ -212,7 +212,7 @@ export function getHybridObjectSpec(
bases.map((base) => new HybridObjectType(base)),
config
)
if (target.language === 'kotlin') constructedType = new KotlinNativeHybridObjectType(constructedType)
if (target.language === 'kotlin') constructedType = new KotlinCxxHybridObjectType(constructedType)
constructor = new Constructor(getHybridObjectName(name).HybridTSpec, constructedType, constructorParameters)
}

Expand Down
8 changes: 0 additions & 8 deletions packages/nitrogen/src/syntax/SourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ import type { Type } from './types/Type.js'

export type CxxHeaderVisibility = 'public' | 'private'

export interface JNINativeRegistration {
readonly namespace: readonly string[]
readonly className: string
readonly import: SourceImport<'c++'>
}

/**
* Represents a file with source code, in a specific programming language.
*/
Expand Down Expand Up @@ -38,8 +32,6 @@ export interface SourceFile {
* platform bridge headers default to private.
*/
headerVisibility?: CxxHeaderVisibility
/** Native methods defined by this generated file, registered by its module. */
jniNativeRegistrations?: readonly JNINativeRegistration[]
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/nitrogen/src/syntax/c++/CppHybridObjectAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export function createCppHybridObjectAdapter(spec: HybridObjectSpec, platform: P
return `${converter}::toSwift(${parameter.type.escapedName})`
}
case 'kotlin': {
const converter = getKotlinNativeConverter({ cppType: parameter.type, context: 'parameter-type', nativeConfig: spec.config })
const converter = getKotlinNativeConverter({ cppType: parameter.type, context: 'parameter-type', config: spec.config })
return `${converter}::toKotlin(${parameter.type.escapedName})`
}
case 'c++':
Expand Down Expand Up @@ -208,7 +208,7 @@ return ${converter}::fromSwift(__result);
`.trim()
}
case 'kotlin': {
const converter = getKotlinNativeConverter({ cppType: method.returnType, nativeConfig: spec.config })
const converter = getKotlinNativeConverter({ cppType: method.returnType, config: spec.config })
return `
auto __result = ${call};
return ${converter}::fromKotlin(__result);
Expand Down
10 changes: 5 additions & 5 deletions packages/nitrogen/src/syntax/createType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { ArrayBufferType } from './types/ArrayBufferType.js'
import { EnumType } from './types/EnumType.js'
import { HybridObjectType } from './types/HybridObjectType.js'
import { StructType } from './types/StructType.js'
import { KotlinNativeStructType } from './kotlin/types/KotlinNativeStructType.js'
import { KotlinNativeHybridObjectType } from './kotlin/types/KotlinNativeHybridObjectType.js'
import { KotlinCxxStructType } from './kotlin/types/KotlinCxxStructType.js'
import { KotlinCxxHybridObjectType } from './kotlin/types/KotlinCxxHybridObjectType.js'
import { OptionalType } from './types/OptionalType.js'
import { NamedWrappingType } from './types/NamedWrappingType.js'
import { getInterfaceProperties } from './getInterfaceProperties.js'
Expand Down Expand Up @@ -364,7 +364,7 @@ export function createType(
// It is another HybridObject being referenced!
const reference = getHybridObjectReference(type)
const { hybridObjectName: typename, platforms, sourceConfig } = reference
if (language === 'kotlin') return new KotlinNativeHybridObjectType(reference)
if (language === 'kotlin') return new KotlinCxxHybridObjectType(reference)
if (language !== 'c++') return reference
const options = platforms[platform]
if (options == null) {
Expand Down Expand Up @@ -392,7 +392,7 @@ export function createType(
const typename = symbol.getName()
const properties = getInterfaceProperties(target, type)
const sourceConfig = getHybridObjectNitroModuleConfig(type) ?? NitroConfig.current
if (language === 'kotlin') return new KotlinNativeStructType(typename, properties, sourceConfig)
if (language === 'kotlin') return new KotlinCxxStructType(typename, properties, sourceConfig)
return new StructType(typename, properties, sourceConfig)
} else if (type.isObject()) {
// It is an object. If it has a symbol/name, it is a `type T = ...` declaration, so a `struct`.
Expand All @@ -403,7 +403,7 @@ export function createType(
const typename = symbol.getName()
const properties = getInterfaceProperties(target, type)
const sourceConfig = getHybridObjectNitroModuleConfig(type) ?? NitroConfig.current
if (language === 'kotlin') return new KotlinNativeStructType(typename, properties, sourceConfig)
if (language === 'kotlin') return new KotlinCxxStructType(typename, properties, sourceConfig)
return new StructType(typename, properties, sourceConfig)
} else {
// It's an anonymous object (`{ ... }`)
Expand Down
10 changes: 0 additions & 10 deletions packages/nitrogen/src/syntax/kotlin/FbjniHybridObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,6 @@ namespace ${cxxNamespace} {
})
files.push({
content: cppImplCode,
jniNativeRegistrations: [
{
namespace: cxxNamespace.split('::'),
className: `${name.JHybridTSpec}::CxxPart`,
import: {
language: 'c++',
declaration: spec.config.getCxxHeader(`${name.JHybridTSpec}.hpp`, 'private'),
},
},
],
language: 'c++',
name: `${name.JHybridTSpec}.cpp`,
subdirectory: [],
Expand Down
23 changes: 6 additions & 17 deletions packages/nitrogen/src/syntax/kotlin/KotlinCallback.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { NitroConfig } from '../../config/NitroConfig.js'
import { indent } from '../../utils.js'
import { createFileMetadataString, isNotDuplicate } from '../helpers.js'
import type { SourceFile } from '../SourceFile.js'
Expand All @@ -10,7 +9,7 @@ export function createKotlinCallback(callbackType: KotlinCallbackType): SourceFi
const publicType = callbackType.type
const name = callbackType.specializationName
const jniName = callbackType.jniName
const packageName = NitroConfig.current.getAndroidPackage('java/kotlin')
const packageName = callbackType.config.getAndroidPackage('java/kotlin')
const kotlinReturnType = publicType.returnType.getCode('kotlin', {
context: 'return',
})
Expand Down Expand Up @@ -147,10 +146,10 @@ class ${name}_java(private val callback: ${lambdaSignature}): ${name} {
}
`.trim()

const jniInterfaceDescriptor = NitroConfig.current.getAndroidPackage('c++/jni', name)
const jniClassDescriptor = NitroConfig.current.getAndroidPackage('c++/jni', `${name}_cxx`)
const jniInterfaceDescriptor = callbackType.config.getAndroidPackage('c++/jni', name)
const jniClassDescriptor = callbackType.config.getAndroidPackage('c++/jni', `${name}_cxx`)
const returnType = callbackType.returnType
const cxxNamespace = NitroConfig.current.getCxxNamespace('c++')
const cxxNamespace = callbackType.config.getCxxNamespace('c++')
const typename = publicType.getCode('c++', { context: 'value' })
// Inline and owning variants of a signature share this file, so it always
// accepts an `InlineCallback` and escapes it into the callback it holds.
Expand Down Expand Up @@ -241,7 +240,7 @@ return ${cppResult};
[
{
language: 'c++',
declaration: NitroConfig.current.getCxxHeader(`${jniName}.hpp`, 'private'),
declaration: callbackType.config.getCxxHeader(`${jniName}.hpp`, 'private'),
},
...imports,
],
Expand Down Expand Up @@ -350,7 +349,7 @@ namespace ${cxxNamespace} {
content: kotlinCode,
language: 'kotlin',
name: `${name}.kt`,
subdirectory: NitroConfig.current.getAndroidPackageDirectory(),
subdirectory: callbackType.config.getAndroidPackageDirectory(),
platform: 'android',
})
files.push({
Expand All @@ -362,16 +361,6 @@ namespace ${cxxNamespace} {
})
files.push({
content: fbjniImplementationCode,
jniNativeRegistrations: [
{
namespace: cxxNamespace.split('::'),
className: `${jniName}_cxx`,
import: {
language: 'c++',
declaration: NitroConfig.current.getCxxHeader(`${jniName}.hpp`, 'private'),
},
},
],
language: 'c++',
name: `${jniName}.cpp`,
subdirectory: [],
Expand Down
4 changes: 2 additions & 2 deletions packages/nitrogen/src/syntax/kotlin/KotlinConstructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { HybridObjectSpec } from '../HybridObjectSpec.js'
import { StaticMethod } from '../StaticMethod.js'
import { getHybridObjectName } from '../getHybridObjectName.js'
import { HybridObjectType } from '../types/HybridObjectType.js'
import { KotlinNativeHybridObjectType } from './types/KotlinNativeHybridObjectType.js'
import { KotlinCxxHybridObjectType } from './types/KotlinCxxHybridObjectType.js'
import {
createCppJniCallBody,
createKotlinBridgeBody,
Expand Down Expand Up @@ -31,7 +31,7 @@ export class KotlinConstructor extends Constructor {
private readonly callable: KotlinMethodInfo

constructor({ spec, constructor }: KotlinConstructorOptions) {
const returnType = new KotlinNativeHybridObjectType(new HybridObjectType(spec))
const returnType = new KotlinCxxHybridObjectType(new HybridObjectType(spec))
const nativeFactory = constructor.toStaticMethod(returnType)
const callable = prepareKotlinMethod(nativeFactory)
const parameters = callable.parameters.map((parameter) => parameter.kotlin)
Expand Down
Loading
Loading