idlize
1157 строк · 53.3 Кб
1/*
2* Copyright (c) 2024 Huawei Device Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15import { Language, identName, importTypeName } from "../util"16import { DeclarationTable, FieldRecord, PrimitiveType } from "./DeclarationTable"17import { RuntimeType } from "./PeerGeneratorVisitor"18import * as ts from "typescript"19import { BlockStatement, BranchStatement, LanguageExpression, LanguageStatement, LanguageWriter, Type } from "./LanguageWriters"20import { mapType } from "./TypeNodeNameConvertor"21
22function castToInt8(value: string, lang: Language): string {23switch (lang) {24case Language.ARKTS: return `${value} as int32` // FIXME: is there int8 in ARKTS?25default: return value26}27}
28
29function castToInt32(value: string, lang: Language): string {30switch (lang) {31case Language.ARKTS: return `${value} as int32`32default: return value33}34}
35
36export interface ArgConvertor {37param: string38tsTypeName: string39isScoped: boolean40useArray: boolean41runtimeTypes: RuntimeType[]42scopeStart?(param: string, language: Language): string43scopeEnd?(param: string, language: Language): string44convertorArg(param: string, writer: LanguageWriter): string45convertorSerialize(param: string, value: string, writer: LanguageWriter): void46convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement47interopType(language: Language): string48nativeType(impl: boolean): string49targetType(writer: LanguageWriter): Type50isPointerType(): boolean51unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression|undefined52getMembers(): string[]53}
54
55export abstract class BaseArgConvertor implements ArgConvertor {56constructor(57public tsTypeName: string,58public runtimeTypes: RuntimeType[],59public isScoped: boolean,60public useArray: boolean,61public param: string62) { }63
64nativeType(impl: boolean): string {65throw new Error("Define")66}67isPointerType(): boolean {68throw new Error("Define")69}70interopType(language: Language): string {71throw new Error("Define")72}73targetType(writer: LanguageWriter): Type {74return new Type(writer.mapType(new Type(this.tsTypeName), this))75}76scopeStart?(param: string, language: Language): string77scopeEnd?(param: string, language: Language): string78abstract convertorArg(param: string, writer: LanguageWriter): string79abstract convertorSerialize(param: string, value: string, writer: LanguageWriter): void80abstract convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement81unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression|undefined {82return undefined83}84getMembers(): string[] { return [] }85protected discriminatorFromExpressions(value: string, runtimeType: RuntimeType, writer: LanguageWriter, exprs: LanguageExpression[]) {86return writer.makeNaryOp("&&", [87writer.makeNaryOp("==", [writer.makeRuntimeType(runtimeType), writer.makeString(`${value}_type`)]),88...exprs89])90}91protected discriminatorFromFields<T>(value: string, writer: LanguageWriter,92uniqueFields: T[] | undefined, nameAccessor: (field: T) => string, optionalAccessor: (field: T) => boolean)93{94if (!uniqueFields || uniqueFields.length === 0) return undefined95const firstNonOptional = uniqueFields.find(it => !optionalAccessor(it))96return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer, [97writer.makeDiscriminatorFromFields(this, value,98firstNonOptional ? [nameAccessor(firstNonOptional)] : uniqueFields.map(it => nameAccessor(it)))99])100}101}
102
103export class StringConvertor extends BaseArgConvertor {104private literalValue?: string105constructor(param: string, receiverType: ts.TypeNode) {106super(mapType(receiverType), [RuntimeType.STRING], false, false, param)107if (ts.isLiteralTypeNode(receiverType) && ts.isStringLiteral(receiverType.literal)) {108this.literalValue = receiverType.literal.text109}110}111convertorArg(param: string, writer: LanguageWriter): string {112return writer.language == Language.CPP ? `(const ${PrimitiveType.String.getText()}*)&${param}` : param113}114convertorSerialize(param: string, value: string, writer: LanguageWriter): void {115writer.writeMethodCall(`${param}Serializer`, `writeString`, [value])116}117convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {118const receiver = writer.getObjectAccessor(this, param, value)119return writer.makeAssign(receiver, undefined,120writer.makeCast(writer.makeString(`${param}Deserializer.readString()`),121writer.makeType(this.tsTypeName, false, receiver)),122false)123}124nativeType(impl: boolean): string {125return PrimitiveType.String.getText()126}127interopType(language: Language): string {128return "KStringPtr"129}130isPointerType(): boolean {131return true132}133override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {134return this.literalValue135? writer.makeString(`${value} === "${this.literalValue}"`)136: undefined137}138}
139
140export class ToStringConvertor extends BaseArgConvertor {141constructor(param: string) {142super("string", [RuntimeType.OBJECT], false, false, param)143}144convertorArg(param: string, writer: LanguageWriter): string {145return writer.language == Language.CPP ? `(const ${PrimitiveType.String.getText()}*)&${param}` : `(${param}).toString()`146}147convertorSerialize(param: string, value: string, writer: LanguageWriter): void {148writer.writeMethodCall(`${param}Serializer`, `writeString`, [149writer.language == Language.CPP ? value : `${value}.toString()`])150}151convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {152return printer.makeAssign(value, undefined, printer.makeString(`${param}Deserializer.readString()`), false)153}154nativeType(impl: boolean): string {155return PrimitiveType.String.getText()156}157interopType(language: Language): string {158return "KStringPtr"159}160isPointerType(): boolean {161return true162}163}
164
165export class BooleanConvertor extends BaseArgConvertor {166constructor(param: string) {167super("boolean", [RuntimeType.BOOLEAN], false, false, param)168}169convertorArg(param: string, writer: LanguageWriter): string {170switch (writer.language) {171case Language.CPP: return param172case Language.TS: return `+${param}`173case Language.ARKTS: return `${param} ? 1 : 0`174case Language.JAVA: return `${param} ? 1 : 0`175default: throw new Error("Unsupported language")176}177}178convertorSerialize(param: string, value: string, printer: LanguageWriter): void {179printer.writeMethodCall(`${param}Serializer`, "writeBoolean", [value])180}181convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {182const accessor = printer.getObjectAccessor(this, param, value)183return printer.makeAssign(accessor, undefined, printer.makeString(`${param}Deserializer.readBoolean()`), false)184}185nativeType(impl: boolean): string {186return PrimitiveType.Boolean.getText()187}188interopType(language: Language): string {189return language == Language.CPP ? PrimitiveType.Boolean.getText() : "KInt"190}191isPointerType(): boolean {192return false193}194}
195
196export class UndefinedConvertor extends BaseArgConvertor {197constructor(param: string) {198super("undefined", [RuntimeType.UNDEFINED], false, false, param)199}200convertorArg(param: string, writer: LanguageWriter): string {201return writer.makeUndefined().asString()202}203convertorSerialize(param: string, value: string, printer: LanguageWriter): void {}204convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {205const accessor = printer.getObjectAccessor(this, param, value)206return printer.makeAssign(accessor, undefined,207printer.makeUndefined(), false)208}209nativeType(impl: boolean): string {210return "Undefined"211}212interopType(language: Language): string {213return PrimitiveType.NativePointer.getText()214}215isPointerType(): boolean {216return false217}218}
219
220export class NullConvertor extends BaseArgConvertor {221constructor(param: string) {222super("null", [RuntimeType.OBJECT], false, false, param)223}224convertorArg(param: string, writer: LanguageWriter): string {225return writer.makeNull().asString()226}227convertorSerialize(param: string, value: string, printer: LanguageWriter): void {}228convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {229const accessor = printer.getObjectAccessor(this, param, value)230return printer.makeAssign(accessor, undefined, printer.makeUndefined(), false)231}232nativeType(impl: boolean): string {233return "nullptr"234}235interopType(language: Language): string {236return PrimitiveType.NativePointer.getText()237}238isPointerType(): boolean {239return false240}241override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {242return writer.makeString(`${value} === null`)243}244}
245
246export class EnumConvertor extends BaseArgConvertor {247constructor(param: string,248private enumType: ts.EnumDeclaration,249private readonly isStringEnum: boolean) {250super(isStringEnum ? "string" : "number",251[isStringEnum ? RuntimeType.STRING : RuntimeType.NUMBER],252false, false, param)253}254convertorArg(param: string, writer: LanguageWriter): string {255return writer.makeUnsafeCast(this, param)256}257convertorSerialize(param: string, value: string, printer: LanguageWriter): void {258if (this.isStringEnum) {259value = printer.ordinalFromEnum(printer.makeString(value),260identName(this.enumType.name)!).asString()261}262printer.writeMethodCall(`${param}Serializer`, "writeInt32", [this.convertorArg(value, printer)])263}264convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {265let readExpr = printer.makeMethodCall(`${param}Deserializer`, "readInt32", [])266if (this.isStringEnum) {267readExpr = printer.enumFromOrdinal(readExpr, identName(this.enumType.name)!)268}269const receiver = printer.getObjectAccessor(this, param, value)270return printer.makeAssign(receiver, undefined, readExpr, false)271}272nativeType(impl: boolean): string {273return PrimitiveType.Int32.getText()274}275interopType(language: Language): string {276return language == Language.CPP ? PrimitiveType.Int32.getText() : "KInt"277}278isPointerType(): boolean {279return false280}281// TODO: bit clumsy.282override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {283let low: number|undefined = undefined284let high: number|undefined = undefined285// TODO: proper enum value computation for cases where enum members have computed initializers.286this.enumType.members.forEach((member, index) => {287let value = index288if (member.initializer) {289let tsValue = member.initializer290if (ts.isLiteralExpression(tsValue) && !this.isStringEnum) {291value = parseInt(tsValue.text)292}293}294if (low === undefined || low > value) low = value295if (high === undefined || high < value) high = value296})297const ordinal = this.isStringEnum298? writer.ordinalFromEnum(writer.makeString(value), identName(this.enumType.name)!)299: writer.makeUnionVariantCast(value, Type.Number, index)300return this.discriminatorFromExpressions(value, this.runtimeTypes[0], writer, [301writer.makeNaryOp(">=", [ordinal, writer.makeString(low!.toString())]),302writer.makeNaryOp("<=", [ordinal, writer.makeString(high!.toString())])303])304}305}
306
307export class LengthConvertorScoped extends BaseArgConvertor {308constructor(param: string) {309super("Length", [RuntimeType.NUMBER, RuntimeType.STRING, RuntimeType.OBJECT], false, false, param)310}311scopeStart(param: string): string {312return `withLengthArray(${param}, (${param}Ptr) => {`313}314scopeEnd(param: string): string {315return '})'316}317convertorArg(param: string, writer: LanguageWriter): string {318return param319}320convertorSerialize(param: string, value: string, printer: LanguageWriter): void {321printer.writeStatement(322printer.makeStatement(323printer.makeMethodCall(`${param}Serializer`, 'writeLength', [printer.makeString(value)])324)325)326}327convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {328return printer.makeAssign(value, undefined,329printer.makeString(`${param}Deserializer.readLength()`), false)330}331nativeType(impl: boolean): string {332return PrimitiveType.Length.getText()333}334interopType(language: Language): string {335switch (language) {336case Language.CPP: return PrimitiveType.ObjectHandle.getText()337case Language.TS: case Language.ARKTS: return 'object'338case Language.JAVA: return 'Object'339default: throw new Error("Unsupported language")340}341}342isPointerType(): boolean {343return true344}345}
346
347export class LengthConvertor extends BaseArgConvertor {348constructor(name: string, param: string) {349super(name, [RuntimeType.NUMBER, RuntimeType.STRING, RuntimeType.OBJECT], false, false, param)350}351convertorArg(param: string, writer: LanguageWriter): string {352return writer.language == Language.CPP ? `(const ${PrimitiveType.Length.getText()}*)&${param}` : param353}354convertorSerialize(param: string, value: string, printer: LanguageWriter): void {355printer.writeStatement(356printer.makeStatement(357printer.makeMethodCall(`${param}Serializer`, 'writeLength', [printer.makeString(value)])358)359)360}361convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {362const receiver = printer.getObjectAccessor(this, param, value)363return printer.makeAssign(receiver, undefined,364printer.makeCast(365printer.makeString(`${param}Deserializer.readLength()`),366printer.makeType(this.tsTypeName, false, receiver), false), false)367}368nativeType(impl: boolean): string {369return PrimitiveType.Length.getText()370}371interopType(language: Language): string {372switch (language) {373case Language.CPP: return 'KLength'374case Language.TS: case Language.ARKTS: return 'string|number|object'375case Language.JAVA: return 'String'376default: throw new Error("Unsupported language")377}378}379isPointerType(): boolean {380return true381}382override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {383return writer.makeNaryOp("||", [384writer.makeNaryOp("==", [writer.makeRuntimeType(RuntimeType.NUMBER), writer.makeString(`${value}_type`)]),385writer.makeNaryOp("==", [writer.makeRuntimeType(RuntimeType.STRING), writer.makeString(`${value}_type`)]),386writer.makeNaryOp("&&", [387writer.makeNaryOp("==", [writer.makeRuntimeType(RuntimeType.OBJECT), writer.makeString(`${value}_type`)]),388writer.makeCallIsResource(value)389])])390}391}
392
393export class UnionRuntimeTypeChecker {394private conflictingConvertors: Set<ArgConvertor> = new Set()395private duplicateMembers: Set<string> = new Set()396private discriminators: [LanguageExpression | undefined, ArgConvertor, number][] = []397
398constructor(private convertors: ArgConvertor[]) {399this.checkConflicts()400}401private checkConflicts() {402const runtimeTypeConflicts: Map<RuntimeType, ArgConvertor[]> = new Map()403this.convertors.forEach(conv => {404conv.runtimeTypes.forEach(rtType => {405const convertors = runtimeTypeConflicts.get(rtType)406if (convertors) convertors.push(conv)407else runtimeTypeConflicts.set(rtType, [conv])408})409})410runtimeTypeConflicts.forEach((convertors, rtType) => {411if (convertors.length > 1) {412const allMembers: Set<string> = new Set()413if (rtType === RuntimeType.OBJECT) {414convertors.forEach(convertor => {415convertor.getMembers().forEach(member => {416if (allMembers.has(member)) this.duplicateMembers.add(member)417allMembers.add(member)418})419})420}421convertors.forEach(convertor => {422this.conflictingConvertors.add(convertor)423})424}425})426}427makeDiscriminator(value: string, index: number, writer: LanguageWriter): LanguageExpression {428const convertor = this.convertors[index]429if (this.conflictingConvertors.has(convertor) && writer.language.needsUnionDiscrimination) {430const discriminator = convertor.unionDiscriminator(value, index, writer, this.duplicateMembers)431this.discriminators.push([discriminator, convertor, index])432if (discriminator) return discriminator433}434return writer.makeNaryOp("||", convertor.runtimeTypes.map(it =>435writer.makeNaryOp("==", [writer.makeUnionVariantCondition(`${value}_type`, RuntimeType[it], index)])))436}437reportConflicts(context: string) {438if (this.discriminators.filter(([discriminator, _, __]) => discriminator === undefined).length > 1) {439console.log(`WARNING: runtime type conflict in "${context}`)440this.discriminators.forEach(([discr, conv, n]) =>441console.log(` ${n} : ${conv.constructor.name} : ${discr ? discr.asString() : "<undefined>"}`))442}443}444}
445export class UnionConvertor extends BaseArgConvertor {446private memberConvertors: ArgConvertor[]447private unionChecker: UnionRuntimeTypeChecker448
449constructor(param: string, private table: DeclarationTable, private type: ts.UnionTypeNode) {450super(`object`, [], false, true, param)451this.memberConvertors = type452.types453.map(member => table.typeConvertor(param, member))454this.unionChecker = new UnionRuntimeTypeChecker(this.memberConvertors)455this.runtimeTypes = this.memberConvertors.flatMap(it => it.runtimeTypes)456}457convertorArg(param: string, writer: LanguageWriter): string {458throw new Error("Do not use for union")459}460convertorSerialize(param: string, value: string, printer: LanguageWriter): void {461printer.writeStatement(printer.makeAssign(`${value}_type`, Type.Int32, printer.makeUnionTypeDefaultInitializer(), true, false))462printer.writeStatement(printer.makeUnionSelector(value, `${value}_type`))463this.memberConvertors.forEach((it, index) => {464const maybeElse = (index > 0 && this.memberConvertors[index - 1].runtimeTypes.length > 0) ? "else " : ""465const conditions = this.unionChecker.makeDiscriminator(value, index, printer)466printer.print(`${maybeElse}if (${conditions.asString()}) {`)467printer.pushIndent()468printer.writeMethodCall(`${param}Serializer`, "writeInt8", [castToInt8(index.toString(), printer.language)])469if (!(it instanceof UndefinedConvertor)) {470printer.writeStatement(471printer.makeAssign(`${value}_${index}`, undefined,472printer.makeUnionVariantCast(value, it.targetType(printer), index), true))473it.convertorSerialize(param, `${value}_${index}`, printer)474}475printer.popIndent()476printer.print(`}`)477})478this.unionChecker.reportConflicts(this.table.getCurrentContext() ?? "<unknown context>")479}480convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {481let selector = `selector`482const selectorAssign = printer.makeAssign(selector, Type.Int32,483printer.makeString(`${param}Deserializer.readInt8()`), true)484const branches: BranchStatement[] = this.memberConvertors.map((it, index) => {485const receiver = printer.getObjectAccessor(this, param, value, {index: `${index}`})486const expr = printer.makeString(`${selector} == ${index}`)487const stmt = new BlockStatement([488it.convertorDeserialize(param, receiver, printer),489printer.makeSetUnionSelector(value, `${index}`)490], false)491return { expr, stmt }492})493return new BlockStatement([selectorAssign, printer.makeMultiBranchCondition(branches)], true)494}495nativeType(impl: boolean): string {496return impl497? `struct { ${PrimitiveType.Int32.getText()} selector; union { ` +498`${this.memberConvertors.map((it, index) => `${it.nativeType(false)} value${index};`).join(" ")}` +499`}; }`500: this.table.getTypeName(this.type)501}502interopType(language: Language): string {503throw new Error("Union")504}505isPointerType(): boolean {506return true507}508}
509
510export class ImportTypeConvertor extends BaseArgConvertor {511private static knownTypes: Map<string, string[]> = new Map([512["CircleShape", ["isInstanceOf", "\"CircleShape\""]],513["EllipseShape", ["isInstanceOf", "\"EllipseShape\""]],514["PathShape", ["isInstanceOf", "\"PathShape\""]],515["RectShape", ["isInstanceOf", "\"RectShape\""]],516["ComponentContent", ["isInstanceOf", "\"ComponentContent\""]],517["DrawableDescriptor", ["isInstanceOf", "\"DrawableDescriptor\""]],518["SymbolGlyphModifier", ["isInstanceOf", "\"SymbolGlyphModifier\""]],519["PixelMap", ["isPixelMap"]],520["Resource", ["isResource"]]])521private importedName: string522constructor(param: string, private table: DeclarationTable, type: ts.ImportTypeNode) {523super("Object", [RuntimeType.OBJECT], false, true, param)524this.importedName = importTypeName(type)525}526convertorArg(param: string, writer: LanguageWriter): string {527throw new Error("Must never be used")528}529convertorSerialize(param: string, value: string, printer: LanguageWriter): void {530printer.writeMethodCall(`${param}Serializer`, "writeCustomObject", [`"${this.importedName}"`, value])531}532convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {533const accessor = printer.getObjectAccessor(this, param, value)534return printer.makeAssign(accessor, undefined,535printer.makeString(`${param}Deserializer.readCustomObject("${this.importedName}")`), false)536}537nativeType(impl: boolean): string {538// return this.importedName539// treat ImportType as CustomObject540return PrimitiveType.CustomObject.getText()541}542interopType(language: Language): string {543throw new Error("Must never be used")544}545isPointerType(): boolean {546return true547}548override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {549const handler = ImportTypeConvertor.knownTypes.get(this.importedName)550return handler551? this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,552[writer.makeString(`${handler[0]}(${handler.slice(1).concat(value).join(", ")})`)])553: undefined554}555}
556
557export class CustomTypeConvertor extends BaseArgConvertor {558private static knownTypes: Map<string, [string, boolean][]> = new Map([559["LinearGradient", [["angle", true], ["direction", true], ["colors", false], ["repeating", true]]]560])561private customName: string562constructor(param: string, customName: string, tsType?: string) {563super(tsType ?? "Object", [RuntimeType.OBJECT], false, true, param)564this.customName = customName565}566convertorArg(param: string, writer: LanguageWriter): string {567throw new Error("Must never be used")568}569convertorSerialize(param: string, value: string, printer: LanguageWriter): void {570printer.writeMethodCall(`${param}Serializer`, `writeCustomObject`, [`"${this.customName}"`, value])571}572convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {573const receiver = printer.getObjectAccessor(this, param, value)574return printer.makeAssign(receiver, undefined,575printer.makeCast(printer.makeMethodCall(`${param}Deserializer`,576"readCustomObject",577[printer.makeString(`"${this.customName}"`)]),578printer.makeType(this.tsTypeName, false, receiver)), false)579}580nativeType(impl: boolean): string {581return PrimitiveType.CustomObject.getText()582}583interopType(language: Language): string {584throw new Error("Must never be used")585}586isPointerType(): boolean {587return true588}589override getMembers(): string[] {590return CustomTypeConvertor.knownTypes.get(this.customName)?.map(it => it[0]) ?? super.getMembers()591}592override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {593const uniqueFields = CustomTypeConvertor.knownTypes.get(this.customName)?.filter(it => !duplicates.has(it[0]))594return this.discriminatorFromFields(value, writer, uniqueFields, it => it[0], it => it[1])595}596}
597
598export class OptionConvertor extends BaseArgConvertor {599private typeConvertor: ArgConvertor600// TODO: be smarter here, and for smth like Length|undefined or number|undefined pass without serializer.601constructor(param: string, private table: DeclarationTable, public type: ts.TypeNode) {602let typeConvertor = table.typeConvertor(param, type)603let runtimeTypes = typeConvertor.runtimeTypes;604if (!runtimeTypes.includes(RuntimeType.UNDEFINED)) {605runtimeTypes.push(RuntimeType.UNDEFINED)606}607super(`${typeConvertor.tsTypeName}|undefined`, runtimeTypes, typeConvertor.isScoped, true, param)608this.typeConvertor = typeConvertor609}610convertorArg(param: string, writer: LanguageWriter): string {611throw new Error("Must never be used")612}613convertorSerialize(param: string, value: string, printer: LanguageWriter): void {614const valueType = `${value}_type`615const serializedType = (printer.language == Language.JAVA ? undefined : Type.Int32)616printer.writeStatement(printer.makeAssign(valueType, serializedType, printer.makeRuntimeType(RuntimeType.UNDEFINED), true, false))617printer.runtimeType(this, valueType, value)618printer.writeMethodCall(`${param}Serializer`, "writeInt8", [castToInt8(valueType, printer.language)])619printer.print(`if (${printer.makeRuntimeTypeCondition(valueType, false, RuntimeType.UNDEFINED).asString()}) {`)620printer.pushIndent()621printer.writeStatement(printer.makeAssign(`${value}_value`, undefined, printer.makeValueFromOption(value, this.typeConvertor), true))622this.typeConvertor.convertorSerialize(param, `${value}_value`, printer)623printer.popIndent()624printer.print(`}`)625}626convertorCArg(param: string): string {627throw new Error("Must never be used")628}629convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {630const runtimeType = `runtimeType`631const accessor = printer.getObjectAccessor(this, param, value)632const thenStatement = new BlockStatement([633this.typeConvertor.convertorDeserialize(param, accessor, printer)634])635return new BlockStatement([636printer.makeAssign(runtimeType, undefined,637printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),638printer.makeSetOptionTag(value, printer.makeCast(printer.makeString(runtimeType), printer.getTagType())),639printer.makeCondition(printer.makeRuntimeTypeDefinedCheck(runtimeType), thenStatement)640], true)641}642nativeType(impl: boolean): string {643return impl644? `struct { ${PrimitiveType.Tag.getText()} tag; ${this.table.getTypeName(this.type, false)} value; }`645: this.table.getTypeName(this.type, true)646}647interopType(language: Language): string {648return language == Language.CPP ? PrimitiveType.NativePointer.getText() : "KNativePointer"649}650isPointerType(): boolean {651return true652}653}
654
655export class AggregateConvertor extends BaseArgConvertor {656private memberConvertors: ArgConvertor[]657private members: [string, boolean][] = []658public readonly aliasName: string | undefined659
660constructor(param: string, private table: DeclarationTable, private type: ts.TypeLiteralNode) {661super(mapType(type), [RuntimeType.OBJECT], false, true, param)662this.aliasName = ts.isTypeAliasDeclaration(this.type.parent) ? identName(this.type.parent.name) : undefined663this.memberConvertors = type664.members665.filter(ts.isPropertySignature)666.map((member, index) => {667this.members[index] = [identName(member.name)!, member.questionToken != undefined]668return table.typeConvertor(param, member.type!, member.questionToken != undefined)669})670}671convertorArg(param: string, writer: LanguageWriter): string {672throw new Error("Do not use for aggregates")673}674convertorSerialize(param: string, value: string, printer: LanguageWriter): void {675this.memberConvertors.forEach((it, index) => {676let memberName = this.members[index][0]677printer.writeStatement(678printer.makeAssign(`${value}_${memberName}`, undefined,679printer.makeString(`${value}.${memberName}`), true))680it.convertorSerialize(param, `${value}_${memberName}`, printer)681})682}683convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {684const structAccessor = printer.getObjectAccessor(this, param, value)685let struct = this.table.targetStruct(this.table.toTarget(this.type))686// Typed structs may refer each other, so use indent level to discriminate.687// Somewhat ugly, but works.688const typedStruct = `typedStruct${printer.indentDepth()}`689printer.pushIndent()690const statements = [691printer.makeObjectAlloc(structAccessor, struct.getFields()),692printer.makeAssign(typedStruct, new Type(printer.makeRef(printer.makeType(this.tsTypeName, false, structAccessor).name)),693printer.makeString(structAccessor),true, false694)695]696this.memberConvertors.forEach((it, index) => {697// TODO: maybe use accessor?698statements.push(699it.convertorDeserialize(param, `${typedStruct}.${struct.getFields()[index].name}`, printer)700)701})702printer.popIndent()703return new BlockStatement(statements, true)704}705nativeType(impl: boolean): string {706return impl707? `struct { ` +708`${this.memberConvertors.map((it, index) => `${it.nativeType(true)} value${index};`).join(" ")}` +709'} '710: this.table.getTypeName(this.type)711}712interopType(language: Language): string {713throw new Error("Must never be used")714}715isPointerType(): boolean {716return true717}718getMembers(): string[] {719return this.members.map(it => it[0])720}721override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {722const uniqueFields = this.members.filter(it => !duplicates.has(it[0]))723return this.discriminatorFromFields(value, writer, uniqueFields, it => it[0], it => it[1])724}725}
726
727export class InterfaceConvertor extends BaseArgConvertor {728constructor(729name: string,730param: string,731private declaration: ts.InterfaceDeclaration | ts.ClassDeclaration,732protected table: DeclarationTable,733private type: ts.TypeReferenceNode) {734super(name, [RuntimeType.OBJECT], false, true, param)735}736
737convertorArg(param: string, writer: LanguageWriter): string {738throw new Error("Must never be used")739}740convertorSerialize(param: string, value: string, printer: LanguageWriter): void {741printer.writeMethodCall(`${param}Serializer`, this.table.serializerName(this.tsTypeName, this.type), [value])742}743convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {744const accessor = printer.getObjectAccessor(this, param, value)745return printer.makeAssign(accessor, undefined,746printer.makeMethodCall(`${param}Deserializer`, this.table.deserializerName(this.tsTypeName, this.type), []), false)747}748nativeType(impl: boolean): string {749return this.tsTypeName750}751interopType(language: Language): string {752throw new Error("Must never be used")753}754isPointerType(): boolean {755return true756}757getMembers(): string[] {758return this.table.targetStruct(this.declaration).getFields().map(it => it.name)759}760override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {761if (this.tsTypeName.endsWith("GestureInterface")) {762const gestureType = this.tsTypeName.slice(0, -"GestureInterface".length)763const castExpr = writer.makeCast(writer.makeString(value), new Type("GestureComponent<Object>"))764return writer.makeNaryOp("===", [765writer.makeString(`${castExpr.asString()}.type`),766writer.makeString(`GestureName.${gestureType}`)])767}768const uniqueFields = this.table769.targetStruct(this.declaration)770.getFields()771.filter(it => !duplicates.has(it.name))772return this.discriminatorFromFields(value, writer, uniqueFields, it => it.name, it => it.optional)773}774}
775
776export class ClassConvertor extends InterfaceConvertor {777constructor(name: string, param: string, declaration: ts.ClassDeclaration, table: DeclarationTable, type: ts.TypeReferenceNode) {778super(name, param, declaration, table, type)779}780override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {781// SubTabBarStyle causes inscrutable "SubTabBarStyle is not defined" error782if (this.tsTypeName === "SubTabBarStyle") return undefined783return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,784[writer.makeString(`${value} instanceof ${this.tsTypeName}`)])785}786}
787
788export class FunctionConvertor extends BaseArgConvertor {789constructor(790param: string,791protected table: DeclarationTable,792private type: ts.TypeNode) {793// TODO: pass functions as integers to native side.794super("Function", [RuntimeType.FUNCTION], false, false, param)795}796convertorArg(param: string, writer: LanguageWriter): string {797return writer.language == Language.CPP ? `makeArkFunctionFromId(${param})` : `registerCallback(${param})`798}799convertorSerialize(param: string, value: string, writer: LanguageWriter): void {800writer.writeMethodCall(`${param}Serializer`, "writeFunction", [value])801}802convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {803const accessor = writer.getObjectAccessor(this, param, value)804return writer.makeAssign(accessor, undefined,805writer.makeCast(writer.makeString(`${param}Deserializer.readFunction()`),806writer.makeType(mapType(this.type), true, accessor))807, false)808}809nativeType(impl: boolean): string {810return PrimitiveType.Function.getText()811}812interopType(language: Language): string {813return language == Language.CPP ? PrimitiveType.Int32.getText() : "KInt"814}815isPointerType(): boolean {816return false817}818}
819
820export class TupleConvertor extends BaseArgConvertor {821constructor(param: string, protected table: DeclarationTable, private type: ts.TupleTypeNode) {822super(`[${type.elements.map(it => mapType(it)).join(",")}]`, [RuntimeType.OBJECT], false, true, param)823this.memberConvertors = type824.elements825.map(element => table.typeConvertor(param, element))826}827private memberConvertors: ArgConvertor[]828convertorArg(param: string, writer: LanguageWriter): string {829throw new Error("Must never be used")830}831convertorSerialize(param: string, value: string, printer: LanguageWriter): void {832printer.writeMethodCall(`${param}Serializer`, "writeInt8", [833castToInt8(printer.makeRuntimeTypeGetterCall(value).asString(), printer.language)834])835this.memberConvertors.forEach((it, index) => {836printer.writeStatement(837printer.makeAssign(`${value}_${index}`, undefined, printer.makeTupleAccess(value, index), true))838it.convertorSerialize(param, `${value}_${index}`, printer)839})840}841convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {842const runtimeType = `runtimeType`843const receiver = printer.getObjectAccessor(this, param, value)844const statements: LanguageStatement[] = []845const tmpTupleIds: string[] = []846this.memberConvertors.forEach((it, index) => {847const tmpTupleId = `tmpTupleItem${index}`848tmpTupleIds.push(tmpTupleId)849const receiver = printer.getObjectAccessor(this, param, value, {index: `${index}`})850// need to remove the mark '?' from Optional type851const tsTypeName = mapType(this.type.elements[index]).replace("?", "")852statements.push(853printer.makeAssign(tmpTupleId,854// makeType - creating the correct type for TS(using tsTypeName) or C++(use decltype(receiver))855printer.makeType(tsTypeName, true, receiver),undefined, true, false),856it.convertorDeserialize(param, tmpTupleId, printer)857)858})859statements.push(printer.makeTupleAssign(receiver, tmpTupleIds))860const thenStatement = new BlockStatement(statements)861return new BlockStatement([862printer.makeAssign(runtimeType, undefined,863printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),864printer.makeCondition(865printer.makeRuntimeTypeDefinedCheck(runtimeType),866thenStatement)867], true)868}869nativeType(impl: boolean): string {870return impl871? `struct { ` +872`${this.memberConvertors.map((it, index) => `${it.nativeType(false)} value${index};`).join(" ")}` +873'} '874: this.table.getTypeName(this.type)875}876interopType(language: Language): string {877throw new Error("Must never be used")878}879isPointerType(): boolean {880return true881}882}
883
884export class ArrayConvertor extends BaseArgConvertor {885elementConvertor: ArgConvertor886constructor(param: string, public table: DeclarationTable, private type: ts.TypeNode, private elementType: ts.TypeNode) {887super(`Array<${mapType(elementType)}>`, [RuntimeType.OBJECT], false, true, param)888this.elementConvertor = table.typeConvertor(param, elementType)889}890convertorArg(param: string, writer: LanguageWriter): string {891throw new Error("Must never be used")892}893convertorSerialize(param: string, value: string, printer: LanguageWriter): void {894// Array length.895printer.writeMethodCall(`${param}Serializer`, "writeInt8", [896castToInt8(printer.makeRuntimeTypeGetterCall(value).asString(), printer.language)])897const valueLength = printer.makeArrayLength(value).asString()898const loopCounter = "i"899printer.writeMethodCall(`${param}Serializer`, "writeInt32", [castToInt32(valueLength, printer.language)])900printer.writeStatement(printer.makeLoop(loopCounter, valueLength))901printer.pushIndent()902printer.writeStatement(903printer.makeAssign(`${value}_element`, undefined, printer.makeArrayAccess(value, loopCounter), true))904this.elementConvertor.convertorSerialize(param, `${value}_element`, printer)905printer.popIndent()906printer.print(`}`)907}908convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {909// Array length.910const runtimeType = `runtimeType`911const arrayLength = `arrayLength`912const forCounterName = `i`913const arrayAccessor = printer.getObjectAccessor(this, param, value)914const accessor = printer.getObjectAccessor(this, param, arrayAccessor, {index: `[${forCounterName}]`})915const thenStatement = new BlockStatement([916// read length917printer.makeAssign(arrayLength, undefined, printer.makeString(`${param}Deserializer.readInt32()`), true),918// prepare object919printer.makeArrayResize(arrayAccessor, mapType(this.type), arrayLength, `${param}Deserializer`),920// store921printer.makeLoop(forCounterName, arrayLength,922this.elementConvertor.convertorDeserialize(param, accessor, printer)),923])924const statements = [925printer.makeAssign(runtimeType,926undefined,927printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),928printer.makeCondition(printer.makeRuntimeTypeDefinedCheck(runtimeType), thenStatement)929]930return new BlockStatement(statements, true)931}932nativeType(impl: boolean): string {933return `Array_${this.table.computeTypeName(undefined, this.elementType, false)}`934}935interopType(language: Language): string {936throw new Error("Must never be used")937}938isPointerType(): boolean {939return true940}941override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {942return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,943[writer.makeString(`${value} instanceof ${this.targetType(writer).name}`)])944}945elementTypeName(): string {946return mapType(this.elementType)947}948}
949
950export class MapConvertor extends BaseArgConvertor {951keyConvertor: ArgConvertor952valueConvertor: ArgConvertor953constructor(param: string, public table: DeclarationTable, type: ts.TypeNode, public keyType: ts.TypeNode, public valueType: ts.TypeNode) {954super(`Map<${mapType(keyType)}, ${mapType(valueType)}>`, [RuntimeType.OBJECT], false, true, param)955this.keyConvertor = table.typeConvertor(param, keyType)956this.valueConvertor = table.typeConvertor(param, valueType)957}958
959convertorArg(param: string, writer: LanguageWriter): string {960throw new Error("Must never be used")961}962convertorSerialize(param: string, value: string, printer: LanguageWriter): void {963// Map size.964printer.writeMethodCall(`${param}Serializer`, "writeInt8", [965castToInt8(printer.makeRuntimeTypeGetterCall(value).asString(), printer.language)])966printer.writeMethodCall(`${param}Serializer`, "writeInt32", [castToInt32(`${value}.size`, printer.language)])967printer.writeStatement(printer.makeMapForEach(value, `${value}_key`, `${value}_value`, () => {968this.keyConvertor.convertorSerialize(param, `${value}_key`, printer)969this.valueConvertor.convertorSerialize(param, `${value}_value`, printer)970}))971}972convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {973// Map size.974const runtimeType = `runtimeType`975const mapSize = `mapSize`976const keyTypeName = printer.makeMapKeyTypeName(this)977const valueTypeName = printer.makeMapValueTypeName(this)978const counterVar = `i`979const keyAccessor = printer.getObjectAccessor(this, param, value, {index: counterVar, field: "keys"})980const valueAccessor = printer.getObjectAccessor(this, param, value, {index: counterVar, field: "values"})981const tmpKey = `tmpKey`982const tmpValue = `tmpValue`983const statements = [984printer.makeAssign(runtimeType, undefined,985printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),986printer.makeCondition(printer.makeRuntimeTypeDefinedCheck(runtimeType), new BlockStatement([987printer.makeAssign(mapSize, undefined, printer.makeString(`${param}Deserializer.readInt32()`), true),988printer.makeMapResize(keyTypeName, valueTypeName, value, mapSize, `${param}Deserializer`),989printer.makeLoop(counterVar, mapSize, new BlockStatement([990printer.makeAssign(tmpKey, new Type(keyTypeName), undefined, true, false),991this.keyConvertor.convertorDeserialize(param, tmpKey, printer),992printer.makeAssign(tmpValue, new Type(keyTypeName), undefined, true, false),993this.valueConvertor.convertorDeserialize(param, tmpValue, printer),994printer.makeMapInsert(keyAccessor, tmpKey, valueAccessor, tmpValue),995], false)),996])),997]998return new BlockStatement(statements, true)999}1000
1001nativeType(impl: boolean): string {1002const keyTypeName = this.table.computeTypeName(undefined, this.keyType, false)1003const valueTypeName = this.table.computeTypeName(undefined, this.valueType, false)1004return `Map_${keyTypeName}_${valueTypeName}`1005}1006interopType(language: Language): string {1007throw new Error("Must never be used")1008}1009isPointerType(): boolean {1010return true1011}1012override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {1013return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,1014[writer.makeString(`${value} instanceof Map`)])1015}1016}
1017
1018export class NumberConvertor extends BaseArgConvertor {1019constructor(param: string) {1020// TODO: as we pass tagged values - request serialization to array for now.1021// Optimize me later!1022super("number", [RuntimeType.NUMBER], false, false, param)1023}1024convertorArg(param: string, writer: LanguageWriter): string {1025return writer.language == Language.CPP ? `(const ${PrimitiveType.Number.getText()}*)&${param}` : param1026}1027convertorSerialize(param: string, value: string, printer: LanguageWriter): void {1028printer.writeMethodCall(`${param}Serializer`, "writeNumber", [value])1029}1030convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {1031const receiver = writer.getObjectAccessor(this, param, value)1032return writer.makeAssign(receiver, undefined,1033writer.makeCast(1034writer.makeString(`${param}Deserializer.readNumber()`),1035writer.makeType(this.tsTypeName, false, receiver)), false)1036}1037nativeType(): string {1038return PrimitiveType.Number.getText()1039}1040interopType(language: Language): string {1041return language == Language.CPP ? "KInteropNumber" : "number"1042}1043isPointerType(): boolean {1044return true1045}1046}
1047
1048export class MaterializedClassConvertor extends BaseArgConvertor {1049constructor(1050name: string,1051param: string,1052protected table: DeclarationTable,1053private type: ts.InterfaceDeclaration | ts.ClassDeclaration,1054) {1055super(name, [RuntimeType.OBJECT], false, true, param)1056}1057
1058convertorArg(param: string, writer: LanguageWriter): string {1059throw new Error("Must never be used")1060}1061convertorSerialize(param: string, value: string, printer: LanguageWriter): void {1062printer.writeMethodCall(`${param}Serializer`, "writeMaterialized", [value])1063}1064convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {1065const accessor = printer.getObjectAccessor(this, param, value)1066const readStatement = printer.makeCast(1067printer.makeMethodCall(`${param}Deserializer`, `readMaterialized`, []),1068new Type(this.table.computeTargetName(this.type, false)!),1069)1070return printer.makeAssign(accessor, undefined, readStatement, false)1071}1072nativeType(impl: boolean): string {1073return PrimitiveType.Materialized.getText()1074}1075interopType(language: Language): string {1076throw new Error("Must never be used")1077}1078isPointerType(): boolean {1079return true1080}1081override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {1082return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,1083[writer.makeString(`${value} instanceof ${this.tsTypeName}`)])1084}1085}
1086
1087export class PredefinedConvertor extends BaseArgConvertor {1088constructor(param: string, tsType: string, private convertorName: string, private cType: string) {1089super(tsType, [RuntimeType.OBJECT, RuntimeType.UNDEFINED], false, true, param)1090}1091convertorArg(param: string, writer: LanguageWriter): string {1092throw new Error("unused")1093}1094convertorSerialize(param: string, value: string, printer: LanguageWriter): void {1095printer.writeMethodCall(`${param}Serializer`, `write${this.convertorName}`, [value])1096}1097convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {1098const accessor = writer.getObjectAccessor(this, param, value)1099return writer.makeAssign(accessor, undefined, writer.makeString(`${param}Deserializer.read${this.convertorName}()`), false)1100}1101nativeType(impl: boolean): string {1102return this.cType1103}1104interopType(language: Language): string {1105return language == Language.CPP ? PrimitiveType.Int32.getText() + "*" : "Int32ArrayPtr"1106}1107isPointerType(): boolean {1108return true1109}1110}
1111
1112class ProxyConvertor extends BaseArgConvertor {1113constructor(protected convertor: ArgConvertor, suggestedName?: string) {1114super(suggestedName ?? convertor.tsTypeName, convertor.runtimeTypes, convertor.isScoped, convertor.useArray, convertor.param)1115}1116convertorArg(param: string, writer: LanguageWriter): string {1117return this.convertor.convertorArg(param, writer)1118}1119convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {1120return this.convertor.convertorDeserialize(param, value, writer)1121}1122convertorSerialize(param: string, value: string, printer: LanguageWriter): void {1123this.convertor.convertorSerialize(param, value, printer)1124}1125nativeType(impl: boolean): string {1126return this.convertor.nativeType(impl)1127}1128interopType(language: Language): string {1129return this.convertor.interopType(language)1130}1131isPointerType(): boolean {1132return this.convertor.isPointerType()1133}1134unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {1135return this.convertor.unionDiscriminator(value, index, writer, duplicates)1136}1137getMembers(): string[] {1138return this.convertor.getMembers()1139}1140}
1141
1142export class TypeAliasConvertor extends ProxyConvertor {1143constructor(1144param: string,1145private table: DeclarationTable,1146declaration: ts.TypeAliasDeclaration,1147private typeArguments?: ts.NodeArray<ts.TypeNode>1148) {1149super(table.typeConvertor(param, declaration.type), identName(declaration.name))1150}1151}
1152
1153export interface RetConvertor {1154isVoid: boolean1155nativeType: () => string1156macroSuffixPart: () => string1157}
1158