nitro-codegen
Version:
The code-generator for react-native-nitro-modules.
153 lines (137 loc) • 4.77 kB
text/typescript
import type { CodeNode } from './CodeNode.js'
import type { Language } from '../getPlatformSpecs.js'
import { type SourceFile, type SourceImport } from './SourceFile.js'
import { Parameter } from './Parameter.js'
import type { Type } from './types/Type.js'
import { indent } from '../utils.js'
export type MethodBody = string
export interface MethodModifiers {
/**
* The name of the class that defines this C++ method.
* Example: `Person` -> `void Person::sayHi()`
*/
classDefinitionName?: string
/**
* Whether the function should be marked as inlineable.
*/
inline?: boolean
/*+
* Whether the func is a pure virtual C++ function.
*/
virtual?: boolean
/**
* Whether the function is marked as `noexcept` (doesn't throw) or not.
*/
noexcept?: boolean
/**
* Whether this function overrides a base/super function.
*/
override?: boolean
/**
* Whether this method has a `@DoNotStrip` and `@Keep` attribute to avoid
* it from being stripped from the binary by the Java compiler or ProGuard.
*/
doNotStrip?: boolean
}
export class Method implements CodeNode {
readonly name: string
readonly returnType: Type
readonly parameters: Parameter[]
constructor(name: string, returnType: Type, parameters: Parameter[]) {
this.name = name
this.returnType = returnType
this.parameters = parameters
if (this.name.startsWith('__')) {
throw new Error(
`Method names are not allowed to start with two underscores (__)! (In ${this.jsSignature})`
)
}
}
get jsSignature(): string {
const returnType = this.returnType.kind
const params = this.parameters.map((p) => `${p.name}: ${p.type.kind}`)
return `${this.name}(${params.join(', ')}): ${returnType}`
}
getCode(
language: Language,
modifiers?: MethodModifiers,
body?: MethodBody
): string {
body = body?.trim()
switch (language) {
case 'c++': {
const returnType = this.returnType.getCode('c++')
const params = this.parameters.map((p) => p.getCode('c++'))
// C++ modifiers start in the beginning
const name = modifiers?.classDefinitionName
? `${modifiers.classDefinitionName}::${this.name}`
: this.name
let signature = `${returnType} ${name}(${params.join(', ')})`
if (modifiers?.inline) signature = `inline ${signature}`
if (modifiers?.virtual) signature = `virtual ${signature}`
if (modifiers?.noexcept) signature = `${signature} noexcept`
if (modifiers?.override) signature = `${signature} override`
if (body == null) {
// It's a function declaration (no body)
if (modifiers?.virtual) {
// if it is a virtual function, we have no implementation (= 0)
signature = `${signature} = 0`
}
return `${signature};`
} else {
return `
${signature} {
${indent(body, ' ')}
}`.trim()
}
}
case 'swift': {
const params = this.parameters.map((p) => p.getCode('swift'))
const returnType = this.returnType.getCode('swift')
let signature = `func ${this.name}(${params.join(', ')}) throws -> ${returnType}`
if (modifiers?.inline) signature = `@inline(__always)\n${signature}`
if (body == null) {
return signature
} else {
return `
${signature} {
${indent(body, ' ')}
}`.trim()
}
}
case 'kotlin': {
const params = this.parameters.map((p) => p.getCode('kotlin'))
const returnType = this.returnType.getCode('kotlin')
let signature = `fun ${this.name}(${params.join(', ')}): ${returnType}`
if (modifiers?.inline) signature = `inline ${signature}`
if (modifiers?.override) signature = `override ${signature}`
if (modifiers?.virtual) signature = `abstract ${signature}`
if (modifiers?.doNotStrip)
signature = `@DoNotStrip\n@Keep\n${signature}`
if (body == null) {
return signature
} else {
return `
${signature} {
${indent(body, ' ')}
}
`.trim()
}
}
default:
throw new Error(
`Language ${language} is not yet supported for property getters!`
)
}
}
getExtraFiles(): SourceFile[] {
const returnTypeExtraFiles = this.returnType.getExtraFiles()
const paramsExtraFiles = this.parameters.flatMap((p) => p.getExtraFiles())
return [...returnTypeExtraFiles, ...paramsExtraFiles]
}
getRequiredImports(): SourceImport[] {
const returnTypeFiles = this.returnType.getRequiredImports()
const paramsImports = this.parameters.flatMap((p) => p.getRequiredImports())
return [...returnTypeFiles, ...paramsImports]
}
}