* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Language } from "../Language"
import { IndentedPrinter } from "../IndentedPrinter"
import * as idl from "../idl"
import { stringOrNone } from "../util";
import * as fs from "fs"
import { NativeModuleType, RuntimeType } from "./common"
import { ArgConvertor } from "./ArgConvertors";
import { ReferenceResolver } from "../peer-generation/ReferenceResolver";
import { convertDeclaration, withInsideInstanceof } from "./nameConvertor";
import { createDeclarationNameConvertor } from "../peer-generation/idl/IdlNameConvertor";
export interface LanguageExpression {
asString(): string
}
export class TernaryExpression implements LanguageExpression {
constructor(public condition: LanguageExpression,
public trueExpression: LanguageExpression,
public falseExpression: LanguageExpression) {}
asString(): string {
return `(${this.condition.asString()}) ? (${this.trueExpression.asString()}) : (${this.falseExpression.asString()})`
}
}
export class NaryOpExpression implements LanguageExpression {
constructor(public op: string, public args: LanguageExpression[]) { }
asString(): string {
if (this.args.length === 1) return this.args[0].asString()
return `${this.args.map(arg => `(${arg.asString()})`).join(` ${this.op} `)}`
}
}
export class StringExpression implements LanguageExpression {
constructor(public value: string) { }
asString(): string {
return this.value
}
}
export class NewObjectExpression implements LanguageExpression {
constructor(
private objectName: string,
private params: LanguageExpression[]) { }
asString(): string {
return `new ${this.objectName}(${this.params.map(it => it.asString()).join(", ")})`
}
}
export class FunctionCallExpression implements LanguageExpression {
constructor(
protected name: string,
protected params: LanguageExpression[]) { }
asString(): string {
return `${this.name}(${this.params.map(it => it.asString()).join(", ")})`
}
}
export class MethodCallExpression extends FunctionCallExpression {
constructor(
public receiver: string,
method: string,
params: LanguageExpression[],
public nullable = false)
{
super(method, params)
}
asString(): string {
return `${this.receiver}${this.nullable ? "?" : ""}.${super.asString()}`
}
}
export class MethodStaticCallExpression extends MethodCallExpression {
constructor(
public receiver: string,
method: string,
params: LanguageExpression[],
public nullable = false)
{
super(receiver, method, params, nullable)
}
}
export class ThisCallExpression extends FunctionCallExpression {
constructor(params: LanguageExpression[])
{
super("this", params)
}
}
export class FieldAccessExpression {
constructor(
public receiver: string,
public field: string,
public nullable = false)
{ }
asString(): string {
return `${this.receiver}${this.nullable ? "?" : ""}.${this.field}`
}
}
export interface LanguageStatement {
write(writer: LanguageWriter): void
}
export class ProxyStatement implements LanguageStatement {
constructor(private cb: (writer: LanguageWriter) => void) {}
write(writer: LanguageWriter): void {
this.cb(writer)
}
}
export class AssignStatement implements LanguageStatement {
constructor(public variableName: string,
public type: idl.IDLType | undefined,
public expression: LanguageExpression | undefined,
public isDeclared: boolean = true,
protected isConst: boolean = true,
protected options?: MakeAssignOptions) {}
write(writer: LanguageWriter): void {
if (this.isDeclared) {
const typeSpec =
this.options?.overrideTypeName
? `: ${this.options.overrideTypeName}`
: this.type
? `: ${writer.getNodeName(this.type)}${/*SHOULD BE REMOVED*/idl.isOptionalType(this.type) ? "|undefined" : ""}`
: ""
const initValue = this.expression ? `= ${this.expression.asString()}` : ""
const constSpec = this.isConst ? "const" : "let"
writer.print(`${constSpec} ${this.variableName}${typeSpec} ${initValue}`)
} else {
const receiver = this.options?.receiver
const withReceiver = receiver ? `${receiver}.` : ""
writer.print(`${withReceiver}${this.variableName} = ${this.expression?.asString()}`)
}
}
}
export class ExpressionStatement implements LanguageStatement {
constructor(public expression: LanguageExpression) { }
write(writer: LanguageWriter): void {
const text = this.expression.asString()
if (text.length > 0) {
writer.print(`${this.expression.asString()}${writer.maybeSemicolon()}`)
}
}
}
export class BlockStatement implements LanguageStatement {
constructor(public statements: LanguageStatement[], readonly inScope: boolean = true, private newLine: boolean = true) { }
write(writer: LanguageWriter): void {
if (this.inScope) {
this.newLine ? writer.print("{") : writer.printer.appendLastString(" {")
writer.pushIndent()
}
this.statements.forEach(s => s.write(writer))
if (this.inScope) {
writer.popIndent()
writer.print("}")
}
}
}
export class IfStatement implements LanguageStatement {
constructor(public condition: LanguageExpression,
public thenStatement: LanguageStatement,
public elseStatement: LanguageStatement | undefined,
public insideIfOp: (() => void) | undefined,
public insideElseOp: (() => void) | undefined
) { }
write(writer: LanguageWriter): void {
writer.print(`if (${this.condition.asString()})`)
this.writeBody(writer, this.thenStatement, () => {
if (this.insideIfOp) { this.insideIfOp!() }
})
if (this.elseStatement !== undefined) {
if ((this.thenStatement instanceof BlockStatement) && this.thenStatement.inScope) {
writer.printer.appendLastString(" else")
} else {
writer.print("else")
}
this.writeBody(writer, this.elseStatement, () => {
if (this.insideElseOp) { this.insideElseOp!() }
})
}
}
writeBody(writer: LanguageWriter, body:LanguageStatement, op: () => void) {
if (!(body instanceof BlockStatement)) {
writer.pushIndent()
}
body.write(writer)
op()
if (!(body instanceof BlockStatement)) {
writer.popIndent()
}
}
}
export type BranchStatement = {expr: LanguageExpression, stmt: LanguageStatement}
export class MultiBranchIfStatement implements LanguageStatement {
constructor(private readonly statements: BranchStatement[],
private readonly elseStatement: LanguageStatement | undefined) { }
write(writer: LanguageWriter): void {
this.statements.forEach((value, index) => {
const {expr, stmt}= value
if (index == 0) {
writer.print(`if (${expr.asString()}) {`)
} else {
writer.printer.appendLastString(` else if (${expr.asString()}) {`)
}
writer.pushIndent()
stmt.write(writer)
writer.popIndent()
writer.print("}")
})
if (this.statements.length > 0 && this.elseStatement !== undefined) {
writer.printer.appendLastString(" else {")
writer.pushIndent()
this.elseStatement.write(writer)
writer.popIndent()
writer.print("}")
}
}
}
export type EnumMember = { name: string, alias?: string, stringId: string | undefined, numberId: number }
export class TsEnumEntityStatement implements LanguageStatement {
constructor(
private readonly enumEntity: idl.IDLEnum,
private readonly options: { isExport: boolean, isDeclare: boolean },
) {}
write(writer: LanguageWriter) {
let enumName = convertDeclaration(createDeclarationNameConvertor(Language.ARKTS), this.enumEntity)
enumName = enumName.split('.').at(-1)!
const members = this.getMembers()
writer.writeEnum(enumName, members, { isExport: this.options.isExport, isDeclare: this.options.isDeclare })
}
protected getMembers(): EnumMember[] {
const originalStyleNames: EnumMember[] = []
this.enumEntity.elements.forEach((member, index) => {
const initText = member.initializer ?? index
const isTypeString = typeof initText !== "number"
const originalName = idl.getExtAttribute(member, idl.IDLExtendedAttributes.OriginalEnumMemberName)
originalStyleNames.push({
name: originalName ?? member.name,
alias: undefined,
stringId: isTypeString ? initText : undefined,
numberId: isTypeString ? index : (initText as number)
})
})
let members = originalStyleNames
return members
}
}
export class ReturnStatement implements LanguageStatement {
constructor(public expression?: LanguageExpression) { }
write(writer: LanguageWriter): void {
writer.print(this.expression ? `return ${this.expression.asString()}` : "return")
}
}
export abstract class LambdaExpression implements LanguageExpression {
constructor(
private originalWriter: LanguageWriter,
protected signature: MethodSignature,
protected resolver: ReferenceResolver,
private body?: LanguageStatement[]) { }
protected abstract get statementHasSemicolon(): boolean
abstract asString(): string
bodyAsString(isScoped: boolean = false): string {
const writer = this.originalWriter.fork()
if (this.body) {
writer.writeStatement(new BlockStatement(this.body, isScoped, false))
}
writer.features.forEach((feature) => {
if (feature.type === "raw")
this.originalWriter.addFeature(feature.feature, feature.module)
else
this.originalWriter.addFeature(feature.node)
})
return writer.getOutput()
.map(line => (line.endsWith('{') || line.endsWith('}') || line.endsWith(';')) ? line : `${line};`)
.join("\n")
}
}
export enum ArgumentModifier {
OPTIONAL,
}
export enum FieldModifier {
READONLY,
PRIVATE,
PUBLIC,
STATIC,
PROTECTED,
FINAL,
VOLATILE,
INTERNAL,
OVERRIDE,
GET,
SET,
}
export const ACCESS_MODIFIERS_SET = new Set([
FieldModifier.PRIVATE,
FieldModifier.PROTECTED,
FieldModifier.PUBLIC
])
export enum MethodModifier {
PUBLIC,
PRIVATE,
PROTECTED,
STATIC,
NATIVE,
INLINE,
GETTER,
SETTER,
THROWS,
FREE,
FORCE_CONTEXT,
OVERRIDE
}
export enum ClassModifier {
PUBLIC,
PRIVATE,
PROTECTED
}
export enum DelegationType {
THIS,
SUPER,
}
export interface DelegationCall {
delegationType?: DelegationType
delegationName?: string
delegationArgs: LanguageExpression[]
}
export class Field {
constructor(
public name: string,
public type: idl.IDLType,
public modifiers: FieldModifier[] = []
) {}
}
export class Method {
private static knownReferenceTypes = [
'KInt', 'KPointer', 'undefined'
]
constructor(
public name: string,
public signature: MethodSignature,
public modifiers: MethodModifier[]|undefined = undefined,
public generics?: string[],
) {}
}
export class PrintHint {
private constructor(
public hint: string
) {}
static AsPointer = new PrintHint('AsPointer')
static AsConstPointer = new PrintHint('AsConstPointer')
static AsValue = new PrintHint('AsValue')
static AsConstReference = new PrintHint('AsConstReference')
static AsReference = new PrintHint('AsReference')
}
type MethodArgPrintHintOrNone = PrintHint | undefined
export class MethodSignature {
public argsModifiers: ArgumentModifier[][] | undefined
constructor(
public returnType: idl.IDLType,
public args: idl.IDLType[],
public defaults: stringOrNone[]|undefined = undefined,
argsModifiers: (ArgumentModifier[]|ArgumentModifier|undefined)[]|undefined = undefined,
public printHints?: MethodArgPrintHintOrNone[],
public argNames?: string[]
) {
this.argsModifiers = argsModifiers?.map(it => it===undefined ? [] : Array.isArray(it) ? it : [it])
}
argName(index: number): string {
return this?.argNames?.at(index) ?? `arg${index}`
}
argDefault(index: number): string|undefined {
return this.defaults?.[index]
}
isArgOptional(index: number): boolean {
return this.argsModifiers?.[index]?.includes(ArgumentModifier.OPTIONAL) ?? false
}
retHint(): PrintHint | undefined {
return this.printHints?.[0]
}
argHint(index: number): PrintHint | undefined {
return this.printHints?.[index + 1]
}
toString(): string {
return `${this.args.map(it => idl.forceAsNamedNode(it).name)} => ${this.returnType}`
}
}
export class NamedMethodSignature extends MethodSignature {
constructor(
returnType: idl.IDLType,
args: idl.IDLType[] = [],
public argsNames: string[] = [],
defaults: stringOrNone[]|undefined = undefined,
argsModifiers: (ArgumentModifier[]|ArgumentModifier|undefined)[]|undefined = undefined,
printHints?: MethodArgPrintHintOrNone[]
) {
super(returnType, args, defaults, argsModifiers, printHints)
}
static make(returnType: idl.IDLType, args: {name: string, type: idl.IDLType}[]): NamedMethodSignature {
return new NamedMethodSignature(returnType, args.map(it => it.type), args.map(it => it.name))
}
argName(index: number): string {
return this.argsNames[index]
}
}
export interface ObjectArgs {
[name: string]: string
}
export interface PrinterLike {
getOutput(): string[]
}
export interface NamespaceOptions {
ident: boolean,
isDeclared?: boolean,
isDefault?: boolean
}
export abstract class LanguageWriter {
protected namespaceStack: string[] = []
constructor(
public printer: IndentedPrinter,
public resolver: ReferenceResolver,
public language: Language,
) {}
indentDepth(): number {
return this.printer.indentDepth()
}
maybeSemicolon() { return ";" }
features: ({ type: "raw", feature: string, module: string} | { type: "idl", node: idl.IDLEntry | idl.IDLReferenceType })[] = []
addFeature(node: idl.IDLEntry | idl.IDLReferenceType): void
addFeature(feature: string, module:string): void
addFeature(featureOrNode: string | idl.IDLEntry | idl.IDLReferenceType, module?: string): void {
if (typeof featureOrNode === "string")
this.features.push({type: "raw", feature: featureOrNode, module: module! })
else
this.features.push({ type: "idl", node: featureOrNode })
}
abstract get interopModule(): string
abstract writeClass(name: string, op: (writer: this) => void, superClass?: string, interfaces?: string[], generics?: string[], isDeclared?: boolean, isExport?: boolean): void
abstract writeEnum(name: string, members: EnumMember[], options: { isExport: boolean, isDeclare?: boolean }, op?: (writer: this) => void): void
abstract writeInterface(name: string, op: (writer: this) => void, superInterfaces?: string[], generics?: string[], isDeclared?: boolean): void
abstract writeFieldDeclaration(name: string, type: idl.IDLType, modifiers: FieldModifier[]|undefined, optional: boolean, initExpr?: LanguageExpression): void
abstract writeFunctionDeclaration(name: string, signature: MethodSignature, generics?:string[]): void
abstract writeFunctionImplementation(name: string, signature: MethodSignature, op: (writer: this) => void, generics?:string[]): void
abstract writeMethodDeclaration(name: string, signature: MethodSignature, modifiers?: MethodModifier[]): void
abstract writeConstructorImplementation(className: string, signature: MethodSignature, op: (writer: this) => void, delegationCall?: DelegationCall, modifiers?: MethodModifier[]): void
abstract writeMethodImplementation(method: Method, op: (writer: this) => void): void
abstract writeProperty(propName: string, propType: idl.IDLType, modifiers: FieldModifier[], getter?: { method: Method, op?: () => void }, setter?: { method: Method, op: () => void }, initExpr?: LanguageExpression): void
abstract writeTypeDeclaration(decl: idl.IDLTypedef): void
abstract writeConstant(constName: string, constType: idl.IDLType, constVal?: string): void
abstract writeImports(moduleName: string, importedFeatures: string[], aliases: string[]): void
abstract makeAssign(variableName: string, type: idl.IDLType | undefined, expr: LanguageExpression | undefined, isDeclared: boolean, isConst?: boolean, options?:MakeAssignOptions): LanguageStatement
abstract makeLambda(signature: MethodSignature, body?: LanguageStatement[]): LanguageExpression
abstract makeThrowError(message: string): LanguageStatement
abstract makeReturn(expr?: LanguageExpression): LanguageStatement
abstract makeRuntimeType(rt: RuntimeType): LanguageExpression
abstract getObjectAccessor(convertor: ArgConvertor, value: string, args?: ObjectArgs): string
abstract makeCast(value: LanguageExpression, node: idl.IDLNode, options?:MakeCastOptions): LanguageExpression
makeTypeCast(value: LanguageExpression, type: idl.IDLType, options?: MakeCastOptions): LanguageExpression {
return this.makeCast(value, type, options)
}
abstract writePrintLog(message: string): void
abstract makeUndefined(): LanguageExpression
makeUnwrapOptional(expression: LanguageExpression): LanguageExpression {
return expression
}
abstract makeArrayInit(type: idl.IDLContainerType, size?:number|string): LanguageExpression
abstract makeClassInit(type: idl.IDLType, paramenters: LanguageExpression[]): LanguageExpression
abstract makeMapInit(type: idl.IDLType): LanguageExpression
abstract makeMapInsert(keyAccessor: string, key: string, valueAccessor: string, value: string): LanguageStatement
abstract makeSetInit(type: idl.IDLType): LanguageExpression
abstract makeSetSize(setAccessor: string): LanguageExpression
abstract makeSetAdd(setAccessor: string, element: LanguageExpression): LanguageStatement
abstract makeSetForEach(set: string, element: string, body: LanguageStatement[]): LanguageStatement
abstract makeLoop(counter: string, limit: string): LanguageStatement
abstract makeLoop(counter: string, limit: string, statement: LanguageStatement): LanguageStatement
abstract makeMapForEach(map: string, key: string, value: string, body: LanguageStatement[]): LanguageStatement
abstract getTagType(): idl.IDLType
abstract getRuntimeType(): idl.IDLType
abstract makeTupleAssign(receiver: string, tupleFields: string[]): LanguageStatement
abstract get supportedModifiers(): MethodModifier[]
abstract get supportedFieldModifiers(): FieldModifier[]
abstract enumFromI32(value: LanguageExpression, enumEntry: idl.IDLEnum): LanguageExpression
abstract i32FromEnum(value: LanguageExpression, enumEntry: idl.IDLEnum): LanguageExpression
abstract getNodeName(type: idl.IDLNode): string
abstract fork(options?: { resolver?: ReferenceResolver }): LanguageWriter
concat(other: PrinterLike): this {
other.getOutput().forEach(it => this.print(it))
return this
}
printTo(file: string): void {
fs.writeFileSync(file, this.getOutput().join("\n"))
}
writeLines(lines: string | stringOrNone[]): void {
if (typeof lines === 'string')
lines = lines.split("\n")
lines.forEach(it => this.print(it))
}
writeGetterImplementation(method: Method, op: (writer: this) => void): void {
this.writeMethodImplementation(new Method(method.name, method.signature, [MethodModifier.GETTER].concat(method.modifiers ?? [])), op)
}
writeSetterImplementation(method: Method, op: (writer: this) => void): void {
this.writeMethodImplementation(new Method(method.name, method.signature, [MethodModifier.SETTER].concat(method.modifiers ?? [])), op)
}
writeSuperCall(params: string[]): void {
this.printer.print(`super(${params.join(", ")})${this.maybeSemicolon()}`)
}
writeMethodCall(receiver: string, method: string, params: string[], nullable = false): void {
this.printer.print(`${receiver}${nullable ? "?" : ""}.${method}(${params.join(", ")})`)
}
writeStaticMethodCall(receiver: string, method: string, params: string[], nullable = false): void {
this.writeMethodCall(receiver, method, params, nullable)
}
writeStatement(stmt: LanguageStatement) {
stmt.write(this)
}
writeStatements(...statements: LanguageStatement[]) {
statements.forEach(it => this.writeStatement(it))
}
writeExpressionStatement(smth: LanguageExpression) {
this.writeStatement(new ExpressionStatement(smth))
}
writeExpressionStatements(...statements: LanguageExpression[]) {
statements.forEach(it => this.writeExpressionStatement(it))
}
writeStaticBlock(op: (writer: this) => void) {
this.print("static {")
this.pushIndent()
op(this)
this.popIndent()
this.print("}")
}
makeRef(type: idl.IDLType, _options?: MakeRefOptions): idl.IDLType {
return type
}
makeThis(): LanguageExpression {
return new StringExpression("this")
}
makeNull(type?: idl.IDLOptionalType): LanguageExpression {
return new StringExpression("null")
}
makeVoid(): LanguageExpression {
return this.makeUndefined()
}
makeLambdaReturn(expr?: LanguageExpression): LanguageStatement {
return this.makeReturn(expr)
}
makeRuntimeTypeCondition(typeVarName: string, equals: boolean, type: RuntimeType, varName?: string): LanguageExpression {
const op = equals ? "==" : "!="
return this.makeNaryOp(op, [this.makeString(typeVarName), this.makeRuntimeType(type)])
}
makeValueFromOption(value: string, destinationConvertor: ArgConvertor): LanguageExpression {
return this.makeString(`${value}!`)
}
makeNewObject(objectName: string, params: LanguageExpression[] = []): LanguageExpression {
return new NewObjectExpression(objectName, params)
}
makeFunctionCall(name: string | LanguageExpression, params: LanguageExpression[]): LanguageExpression {
if (typeof name === "string") {
return new FunctionCallExpression(name, params)
}
return new FunctionCallExpression(name.asString(), params)
}
makeMethodCall(receiver: string, method: string, params: LanguageExpression[], nullable?: boolean): LanguageExpression {
return new MethodCallExpression(receiver, method, params, nullable)
}
makeThisCall(params: LanguageExpression[]): LanguageExpression {
return new ThisCallExpression(params)
}
makeStaticMethodCall(receiver: string, method: string, params: LanguageExpression[], nullable?: boolean): LanguageExpression {
return new MethodStaticCallExpression(receiver, method, params, nullable)
}
makeFieldAccess(receiver: string, method: string, nullable?: boolean): LanguageExpression {
return new FieldAccessExpression(receiver, method, nullable)
}
makeNativeCall(nativeModule: NativeModuleType, method: string, params: LanguageExpression[], nullable?: boolean): LanguageExpression {
return new MethodCallExpression(this.nativeReceiver(nativeModule), method, params, nullable)
}
makeBlock(statements: LanguageStatement[], inScope: boolean = true) {
return new BlockStatement(statements, inScope)
}
nativeReceiver(nativeModule: NativeModuleType): string {
return nativeModule.name
}
abstract makeDefinedCheck(value: string, type?: idl.IDLOptionalType, isTag?: boolean): LanguageExpression
makeRuntimeTypeDefinedCheck(runtimeType: string): LanguageExpression {
return this.makeRuntimeTypeCondition(runtimeType, false, RuntimeType.UNDEFINED)
}
makeCondition(condition: LanguageExpression, thenStatement: LanguageStatement, elseStatement?: LanguageStatement, insideIfOp?: () => void, insideElseOp?: () => void): LanguageStatement {
return new IfStatement(condition, thenStatement, elseStatement, insideIfOp, insideElseOp)
}
makeMultiBranchCondition(conditions: BranchStatement[], elseStatement?: LanguageStatement): LanguageStatement {
return new MultiBranchIfStatement(conditions, elseStatement)
}
makeTernary(condition: LanguageExpression, trueExpression: LanguageExpression, falseExpression: LanguageExpression): LanguageExpression {
return new TernaryExpression(condition, trueExpression, falseExpression)
}
makeArrayLength(array: string, length?: string): LanguageExpression {
return this.makeString(`${array}.length`)
}
makeArrayAccess(value: string, indexVar: string) {
return this.makeString(`${value}[${indexVar}]`)
}
makeTupleAccess(value: string, index: number): LanguageExpression {
return this.makeString(`${value}[${index}]`)
}
makeUnionSelector(value: string, valueType: string): LanguageStatement {
return this.makeAssign(valueType, undefined, this.makeString(`runtimeType(${value})`), false)
}
makeUnionVariantCast(value: string, type: string, convertor: ArgConvertor, index?: number): LanguageExpression {
return this.makeString(`unsafeCast<${type}>(${value})`)
}
makeUnionTypeDefaultInitializer() {
return this.makeRuntimeType(RuntimeType.UNDEFINED)
}
makeArrayResize(array: string, arrayType: string, length: string, deserializer: string): LanguageStatement {
return new ExpressionStatement(new StringExpression(""))
}
makeMapResize(mapTypeName: string, keyType: idl.IDLType, valueType: idl.IDLType, map: string, size: string, deserializer: string): LanguageStatement {
return new ExpressionStatement(new StringExpression("// TODO: TS map resize"))
}
makeMapSize(map: string): LanguageExpression {
return this.makeString(`${map}.size`)
}
makeTupleAlloc(option: string): LanguageStatement {
return new ExpressionStatement(new StringExpression(""))
}
makeSetUnionSelector(value: string, index: string): LanguageStatement {
return new ExpressionStatement(new StringExpression(""))
}
makeSetOptionTag(value: string, tag: LanguageExpression): LanguageStatement {
return new ExpressionStatement(new StringExpression(""))
}
makeString(value: string): LanguageExpression {
return new StringExpression(value)
}
makeNaryOp(op: string, args: LanguageExpression[]): LanguageExpression {
return new NaryOpExpression(op, args)
}
makeStatement(expr: LanguageExpression): LanguageStatement {
return new ExpressionStatement(expr)
}
writeNativeMethodDeclaration(method: Method): void {
this.writeMethodDeclaration(method.name, method.signature)
}
pushIndent() {
this.printer.pushIndent()
}
popIndent() {
this.printer.popIndent()
}
print(string: stringOrNone) {
this.printer.print(string)
}
getOutput(): string[] {
return this.printer.getOutput()
}
makeSignature(returnType: idl.IDLType, parameters: idl.IDLParameter[]): MethodSignature {
return new MethodSignature(returnType,
parameters.map(it => it.type!))
}
mapFieldModifier(modifier: FieldModifier): string {
return `${FieldModifier[modifier].toLowerCase()}`
}
mapMethodModifier(modifier: MethodModifier): string {
return `${MethodModifier[modifier].toLowerCase()}`
}
* TODO: replace me with {@link makeUnsafeCast_}
*/
makeUnsafeCast(param: string): string {
return `unsafeCast<int32>(${param})`
}
makeUnsafeCast_(value: LanguageExpression, type: idl.IDLType, typeOptions?: PrintHint) {
return `(${value.asString()} as ${this.getNodeName(type)})`
}
runtimeType(param: ArgConvertor, valueType: string, value: string) {
this.writeStatement(this.makeAssign(valueType, idl.IDLI32Type,
this.makeFunctionCall("runtimeType", [this.makeString(value)]), false))
}
makeEnumEntity(enumEntity: idl.IDLEnum, options: { isExport: boolean, isDeclare?: boolean }): LanguageStatement {
return new TsEnumEntityStatement(enumEntity, { isExport: options.isExport, isDeclare: !!options.isDeclare })
}
makeFieldModifiersList(modifiers: FieldModifier[] | undefined, customFieldFilter?: (field :FieldModifier) => boolean) : string {
let allowedModifiers = this.supportedFieldModifiers
let modifierFilter = customFieldFilter ? customFieldFilter : function(field: FieldModifier) {
return allowedModifiers.includes(field)
}
let prefix = modifiers
?.filter(modifierFilter)
.map(it => this.mapFieldModifier(it)).join(" ")
return prefix ? prefix : ""
}
escapeKeyword(keyword: string): string {
return keyword
}
makeCastCustomObject(customName: string, _isGenericType: boolean): LanguageExpression {
return this.makeString(customName)
}
makeHasOwnProperty(value: string,
property: string,
propertyTypeName?: string): LanguageExpression {
const expressions = [this.makeString(`${value}.hasOwnProperty("${property}")`)]
if (propertyTypeName) {
expressions.push(this.makeString(`isInstanceOf("${propertyTypeName}", ${value}.${property})`))
}
return this.makeNaryOp("&&", expressions)
}
discriminate(value: string, index: number, type: idl.IDLType, runtimeTypes: RuntimeType[]): string {
return `${value}.getSelector() == ${index}`
}
makeNot(expr: LanguageExpression): LanguageExpression {
return this.makeString(`!(${expr.asString()})`)
}
makeAnd(...args: LanguageExpression[]): LanguageExpression {
return this.makeNaryOp("&&", args)
}
makeOr(...args: LanguageExpression[]): LanguageExpression {
return this.makeNaryOp("||", args)
}
makeSerializedBufferGetter(serializer: string): LanguageExpression {
return this.makeMethodCall(serializer, `asBuffer`, [])
}
makeEquals(args: LanguageExpression[]): LanguageExpression {
return this.makeNaryOp("===", args)
}
castToInt(value: string, bitness: 8|32): string{ return value }
castToBoolean(value: string): string { return value }
makeCallIsObject(value: string): LanguageExpression {
return this.makeString(`typeof ${value} === "object"`)
}
makeStaticBlock(op: (writer: LanguageWriter) => void) {
op(this)
}
instanceOf(value: string, type: idl.IDLType): LanguageExpression {
return this.makeString(`${value} instanceof ${withInsideInstanceof(true, () => this.getNodeName(type))}`)
}
typeInstanceOf(type: idl.IDLEntry, value: string, members?: string[]): LanguageExpression {
return this.makeString(`${value} instanceof ${withInsideInstanceof(true, () => this.getNodeName(type))}`)
}
* Writes `namespace <namespace> {` and adds extra indent
* @param namespace Namespace to begin
*/
pushNamespace(namespace: string, options: NamespaceOptions) {
this.print(`namespace ${namespace} {`)
if (options.ident) this.pushIndent()
}
* Writes closing brace of namespace block and removes one level of indent
*/
popNamespace(options: { ident: boolean }) {
this.namespaceStack.pop()
if (options.ident) this.popIndent()
this.print(`}`)
}
public static _isReferenceRelativeToNamespaces: boolean = false
public static get isReferenceRelativeToNamespaces(): boolean { return this._isReferenceRelativeToNamespaces }
public static relativeReferences<T>(isRelative: boolean, op: () => T): T {
const prevIsRelative = this.isReferenceRelativeToNamespaces
this._isReferenceRelativeToNamespaces = isRelative
const result = op()
this._isReferenceRelativeToNamespaces = prevIsRelative
return result
}
}
export function mangleMethodName(method: Method, id?: number): string {
return `${method.name}${id ?? ""}`
}
export function printMethodDeclaration(printer: IndentedPrinter, retType: string, methodName: string, apiParameters: string[], postfix: string = "") {
if (apiParameters.length > 1) {
const methodTypeName = `${retType} ${methodName}`
const indent = ` `.repeat(methodTypeName.length + 1)
printer.print(`${methodTypeName}(${apiParameters[0]},`)
for (let i = 1; i < apiParameters.length; i++) {
printer.print(indent + apiParameters[i] + ((i === apiParameters.length - 1) ? `)${postfix}` : ","))
}
} else {
const signature = `${retType} ${methodName}(${apiParameters.join(", ")})${postfix}`
printer.print(signature)
}
}
export function copyMethod(method: Method, overrides: {
name?: string,
signature?: MethodSignature,
modifiers?: MethodModifier[],
generics?: string[],
}) {
return new Method(
overrides.name ?? method.name,
overrides.signature ?? method.signature,
overrides.modifiers ?? method.modifiers,
overrides.generics ?? method.generics,
)
}
export type MakeCastOptions = {
unsafe?: boolean
optional?: boolean
receiver?: string
toRef?: boolean
overrideTypeName?: string
}
export type MakeRefOptions = {
receiver?: string
}
export type MakeAssignOptions = {
receiver?: string,
assignRef?: boolean
overrideTypeName?: string
}
export type ExpressionAssigner = (expression: LanguageExpression) => LanguageStatement