idlize
1479 строк · 67.5 Кб
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*/
15
16import * as ts from "typescript"17import { Language, asString, getDeclarationsByNode, getNameWithoutQualifiersRight, heritageDeclarations,18identName, isStatic, throwException, typeEntityName, identNameWithNamespace} from "../util"19import { IndentedPrinter } from "../IndentedPrinter"20import { PeerGeneratorConfig } from "./PeerGeneratorConfig"21import {22AggregateConvertor, ArgConvertor, ArrayConvertor, BooleanConvertor, ClassConvertor, CustomTypeConvertor,23EnumConvertor, FunctionConvertor, ImportTypeConvertor, InterfaceConvertor, LengthConvertor, MapConvertor,24MaterializedClassConvertor, NullConvertor, NumberConvertor, OptionConvertor, PredefinedConvertor, StringConvertor,25ToStringConvertor, TupleConvertor, TypeAliasConvertor, UndefinedConvertor, UnionConvertor26} from "./Convertors"27import { DependencySorter } from "./DependencySorter"28import { checkDeclarationTargetMaterialized, isMaterialized } from "./Materialized"29import { LanguageExpression, LanguageWriter, Method, MethodModifier, NamedMethodSignature, Type } from "./LanguageWriters"30import { RuntimeType } from "./PeerGeneratorVisitor"31import { TypeNodeConvertor, convertTypeNode } from "./TypeNodeConvertor"32import { PeerLibrary } from "./PeerLibrary"33import { collectCallbacks } from "./printers/EventsPrinter"34import { EnumMember, NodeArray } from "typescript";35import { extractBuilderFields } from "./BuilderClass"36import { setEngine } from "node:crypto"37
38export class PrimitiveType {39constructor(private name: string, public isPointer = false) { }40getText(table?: DeclarationTable): string { return this.name }41static ArkPrefix = "Ark_"42static String = new PrimitiveType(`${PrimitiveType.ArkPrefix}String`, true)43static Number = new PrimitiveType(`${PrimitiveType.ArkPrefix}Number`, true)44static Int32 = new PrimitiveType(`${PrimitiveType.ArkPrefix}Int32`)45static Tag = new PrimitiveType(`${PrimitiveType.ArkPrefix}Tag`)46static RuntimeType = new PrimitiveType(`${PrimitiveType.ArkPrefix}RuntimeType`)47static Boolean = new PrimitiveType(`${PrimitiveType.ArkPrefix}Boolean`)48static Function = new PrimitiveType(`${PrimitiveType.ArkPrefix}Function`, false)49static Materialized = new PrimitiveType(`${PrimitiveType.ArkPrefix}Materialized`, true)50static Undefined = new PrimitiveType(`${PrimitiveType.ArkPrefix}Undefined`)51static NativePointer = new PrimitiveType(`${PrimitiveType.ArkPrefix}NativePointer`)52static ObjectHandle = new PrimitiveType(`${PrimitiveType.ArkPrefix}ObjectHandle`)53static Length = new PrimitiveType(`${PrimitiveType.ArkPrefix}Length`, true)54static Resource = new PrimitiveType(`${PrimitiveType.ArkPrefix}Resource`, true)55static CustomObject = new PrimitiveType(`${PrimitiveType.ArkPrefix}CustomObject`, true)56private static pointersMap = new Map<DeclarationTarget, PointerType>()57static pointerTo(target: DeclarationTarget) {58if (PrimitiveType.pointersMap.has(target)) return PrimitiveType.pointersMap.get(target)!59let result = new PointerType(target)60PrimitiveType.pointersMap.set(target, result)61return result62}63static UndefinedTag = "ARK_TAG_UNDEFINED"64static UndefinedRuntime = "ARK_RUNTIME_UNDEFINED"65static OptionalPrefix = "Opt_"66}
67
68export class PointerType extends PrimitiveType {69constructor(public pointed: DeclarationTarget) {70super("", true)71}72getText(table: DeclarationTable): string {73return `${table.computeTargetName(this.pointed, false)}*`74}75}
76
77export type DeclarationTarget =78ts.ClassDeclaration | ts.InterfaceDeclaration | ts.EnumDeclaration79| ts.UnionTypeNode | ts.TypeLiteralNode | ts.ImportTypeNode | ts.FunctionTypeNode | ts.TupleTypeNode80| ts.TemplateLiteralTypeNode | ts.TypeReferenceNode81| ts.ArrayTypeNode | ts.ParenthesizedTypeNode | ts.OptionalTypeNode | ts.LiteralTypeNode82| PrimitiveType83
84export class FieldRecord {85constructor(public declaration: DeclarationTarget, public type: ts.TypeNode | undefined, public name: string, public optional: boolean = false) { }86}
87
88export interface StructVisitor {89visitUnionField(field: FieldRecord, selectorValue: number): void90// visitOptionalField(field?: FieldRecord): void;91visitInseparable(): void92}
93
94class StructDescriptor {95supers: DeclarationTarget[] = []96deps = new Set<DeclarationTarget>()97isPacked: boolean = false98isArray: boolean = false99private fields: FieldRecord[] = []100private seenFields = new Set<string>()101addField(field: FieldRecord) {102if (!this.seenFields.has(field.name)) {103this.seenFields.add(field.name)104// TODO: kind of wrong105if (field.name == `template`) field.name = `template_`106this.fields.push(field)107}108}109getFields(): readonly FieldRecord[] {110return this.fields111}112isEmpty(): boolean {113return this.fields.length == 0114}115}
116
117export class DeclarationTable {118private typeMap = new Map<ts.TypeNode, [DeclarationTarget, string[], boolean]>()119private toTargetConvertor: ToDeclarationTargetConvertor120typeChecker: ts.TypeChecker | undefined = undefined121public language: Language122
123constructor(language: string) {124switch (language) {125case "arkts": this.language = Language.ARKTS; break126case "java": this.language = Language.JAVA; break127case "ts": default: this.language = Language.TS; break128}129console.log(`Emit for ${this.language.toString()}`)130this.toTargetConvertor = new ToDeclarationTargetConvertor(this)131}132
133getTypeName(type: ts.TypeNode, optional: boolean = false): string {134let declaration = this.typeMap.get(type)135let prefix = optional ? PrimitiveType.OptionalPrefix : ""136return declaration !== undefined ? prefix + declaration[1][0] : this.computeTargetName(this.toTarget(type), optional)137}138
139requestType(name: string | undefined, type: ts.TypeNode, useToGenerate: boolean) {140let declaration = this.typeMap.get(type)141if (declaration) {142declaration[2] ||= useToGenerate143if (name && !declaration[1].includes(name)) {144declaration[1].push(name)145}146return147}148name = this.computeTypeName(name, type, false)149let target = this.toTarget(type)150if (!target) throw new Error(`Cannot find declaration: ${type.getText()}`)151this.typeMap.set(type, [target, [name], useToGenerate])152}153
154private isDeclarationTarget(type: ts.TypeNode): boolean {155if (ts.isUnionTypeNode(type)) return true156if (ts.isTypeLiteralNode(type)) return true157if (ts.isLiteralTypeNode(type)) return true158if (ts.isTupleTypeNode(type)) return true159if (ts.isArrayTypeNode(type)) return true160if (ts.isOptionalTypeNode(type)) return true161if (ts.isFunctionTypeNode(type)) return true162// TODO: shall we map it to string type here or later?163if (ts.isTemplateLiteralTypeNode(type)) return true164return false165}166
167computeTypeName(suggestedName: string | undefined, type: ts.TypeNode, optional: boolean = false): string {168return this.computeTypeNameImpl(suggestedName, type, optional)169}170
171toTarget(node: ts.TypeNode): DeclarationTarget {172return convertTypeNode(this.toTargetConvertor, node)173}174
175computeTargetName(target: DeclarationTarget, optional: boolean): string {176return this.computeTargetNameImpl(target, optional)177}178
179computeTargetNameImpl(target: DeclarationTarget, optional: boolean): string {180const prefix = optional ? PrimitiveType.OptionalPrefix : ""181if (target instanceof PrimitiveType) {182return prefix + target.getText(this)183}184if (ts.isTypeLiteralNode(target)) {185if (target.members.some(ts.isIndexSignatureDeclaration)) {186// For indexed access we just replace the whole type to a custom accessor.187return prefix + `CustomMap`188}189return prefix + `Literal_${target.members.map(member => {190if (ts.isPropertySignature(member)) {191let target = this.toTarget(member.type!)192let field = identName(member.name)193return `${field}_${this.computeTargetName(target, member.questionToken != undefined)}`194} else {195return undefined196}197})198.filter(it => it != undefined)
199.join("_")}`200}201if (ts.isLiteralTypeNode(target)) {202const literal = target.literal203if (ts.isStringLiteral(literal) || ts.isNoSubstitutionTemplateLiteral(literal) || ts.isRegularExpressionLiteral(literal)) {204return prefix + PrimitiveType.String.getText()205}206if (ts.isNumericLiteral(literal)) {207return prefix + PrimitiveType.Number.getText()208}209if (literal.kind == ts.SyntaxKind.NullKeyword) {210// TODO: Is it correct to have undefined for null?211return PrimitiveType.Undefined.getText()212}213}214if (ts.isTemplateLiteralTypeNode(target)) {215// TODO: likely incorrect216return prefix + PrimitiveType.String.getText()217}218if (ts.isTypeParameterDeclaration(target)) {219// TODO: likely incorrect220return prefix + PrimitiveType.CustomObject.getText()221}222if (ts.isEnumDeclaration(target)) {223return prefix + this.enumName(target.name)224}225if (ts.isUnionTypeNode(target)) {226return prefix + `Union_${target.types.map(it => this.computeTargetName(this.toTarget(it), false)).join("_")}`227}228if (ts.isInterfaceDeclaration(target) || ts.isClassDeclaration(target)) {229let name = identName(target.name)230if (name == "Function")231return prefix + PrimitiveType.Function.getText()232return prefix + name233}234if (ts.isFunctionTypeNode(target)) {235return prefix + PrimitiveType.Function.getText()236}237if (ts.isTupleTypeNode(target)) {238return prefix + `Tuple_${target.elements.map(it => {239if (ts.isNamedTupleMember(it)) {240return this.computeTargetName(this.toTarget(it.type), it.questionToken != undefined)241} else {242return this.computeTargetName(this.toTarget(it), false)243}244}).join("_")}`245}246if (ts.isArrayTypeNode(target)) {247return prefix + `Array_` + this.computeTargetName(this.toTarget(target.elementType), false)248}249if (ts.isImportTypeNode(target)) {250return prefix + this.mapImportType(target).getText()251}252if (ts.isOptionalTypeNode(target)) {253let name = this.computeTargetName(this.toTarget(target.type), false)254return `${PrimitiveType.OptionalPrefix}${name}`255}256if (ts.isParenthesizedTypeNode(target)) {257return this.computeTargetName(this.toTarget(target.type), optional)258}259if (ts.isEnumMember(target)) {260return this.computeTargetName((target as any).parent as DeclarationTarget, optional)261}262if (ts.isTypeReferenceNode(target)) {263let name = identName(target.typeName)264if (!target.typeArguments) throw new Error("Only type references with type arguments allowed here: " + name)265if (name == "Optional")266return this.computeTargetName(this.toTarget(target.typeArguments[0]), true)267if (name == "Array")268return prefix + `Array_` + this.computeTargetName(this.toTarget(target.typeArguments[0]), false)269if (name == "Map")270return prefix + `Map_` + this.computeTargetName(this.toTarget(target.typeArguments[0]), false) + '_' + this.computeTargetName(this.toTarget(target.typeArguments[1]), false)271if (name == "Callback")272return prefix + PrimitiveType.Function.getText()273if (PeerGeneratorConfig.isKnownParametrized(name))274return prefix + PrimitiveType.CustomObject.getText()275}276throw new Error(`Cannot compute target name: ${(target as any).getText()} ${(target as any).kind}`)277}278
279private mapImportType(type: ts.ImportTypeNode): DeclarationTarget {280let name = identName(type.qualifier)!281switch (name) {282case "Resource": return PrimitiveType.Resource283case "Callback": return PrimitiveType.Function284default: return PrimitiveType.CustomObject285}286}287
288private computeTypeNameImpl(suggestedName: string | undefined, type: ts.TypeNode, optional: boolean): string {289const prefix = optional ? PrimitiveType.OptionalPrefix : ""290if (ts.isImportTypeNode(type)) {291return prefix + this.mapImportType(type).getText()292}293if (ts.isTypeReferenceNode(type)) {294const typeName = identName(type.typeName)295let declaration = this.toTarget(type)296if (!(declaration instanceof PrimitiveType) && ts.isEnumDeclaration(declaration))297return this.enumName(declaration.name)298if (typeName === "Array") {299const elementTypeName = this.computeTypeNameImpl(undefined, type.typeArguments![0], false)300return `${prefix}Array_${elementTypeName}`301} else if (typeName === "Map") {302const keyTypeName = this.computeTypeNameImpl(undefined, type.typeArguments![0], false)303const valueTypeName = this.computeTypeNameImpl(undefined, type.typeArguments![1], false)304return `${prefix}Map_${keyTypeName}_${valueTypeName}`305} else if (typeName === "Resource") {306return `${prefix}${PrimitiveType.Resource.getText()}`307}308return prefix + typeName309}310if (ts.isUnionTypeNode(type)) {311if (suggestedName) return suggestedName312return prefix + `Union_${type.types.map(it => this.computeTypeNameImpl(undefined, it, optional)).join("_")}`313}314if (ts.isOptionalTypeNode(type)) {315if (suggestedName) return suggestedName316return PrimitiveType.OptionalPrefix + this.computeTypeNameImpl(undefined, type.type, false)317}318if (ts.isTupleTypeNode(type)) {319if (suggestedName) return suggestedName320return prefix + `Tuple_${type.elements.map(it => {321if (ts.isNamedTupleMember(it)) {322return this.computeTypeNameImpl(undefined, it.type, optional)323} else {324return this.computeTypeNameImpl(undefined, it, optional)325}326
327}).join("_")}`328}329if (ts.isParenthesizedTypeNode(type)) {330return this.computeTypeNameImpl(suggestedName, type.type!, optional)331}332if (ts.isTypeLiteralNode(type)) {333if (suggestedName) return suggestedName334return prefix + `Literal_${type.members.map(member => {335if (ts.isPropertySignature(member)) {336let field = identName(member.name)337return `${field}_${this.computeTypeNameImpl(undefined, member.type!, member.questionToken != undefined)}`338} else {339return undefined340}341})342.filter(it => it != undefined)
343.join("_")}`344}345if (ts.isLiteralTypeNode(type)) {346const literal = type.literal347if (ts.isStringLiteral(literal) || ts.isNoSubstitutionTemplateLiteral(literal) || ts.isRegularExpressionLiteral(literal)) {348return PrimitiveType.String.getText()349}350if (ts.isNumericLiteral(literal)) {351return PrimitiveType.Number.getText()352}353if (literal.kind == ts.SyntaxKind.NullKeyword) {354return PrimitiveType.Undefined.getText()355}356throw new Error(`Unknown literal type: ${type.getText()}`)357}358if (ts.isTemplateLiteralTypeNode(type)) {359return prefix + PrimitiveType.String.getText()360}361if (ts.isFunctionTypeNode(type)) {362return prefix + PrimitiveType.Function.getText()363}364if (ts.isArrayTypeNode(type)) {365if (suggestedName) return suggestedName366return prefix + `Array_` + this.computeTypeNameImpl(undefined, type.elementType, false)367}368if (type.kind == ts.SyntaxKind.NumberKeyword) {369return prefix + PrimitiveType.Number.getText()370}371if (372type.kind == ts.SyntaxKind.UndefinedKeyword ||373type.kind == ts.SyntaxKind.NullKeyword ||374type.kind == ts.SyntaxKind.VoidKeyword375) {376return PrimitiveType.Undefined.getText()377}378if (type.kind == ts.SyntaxKind.StringKeyword) {379return prefix + PrimitiveType.String.getText()380}381if (type.kind == ts.SyntaxKind.BooleanKeyword) {382return prefix + PrimitiveType.Boolean.getText()383}384if (type.kind == ts.SyntaxKind.ObjectKeyword ||385type.kind == ts.SyntaxKind.UnknownKeyword) {386return prefix + PrimitiveType.CustomObject.getText()387}388if (type.kind == ts.SyntaxKind.AnyKeyword) {389return prefix + PrimitiveType.CustomObject.getText()390}391if (ts.isTypeParameterDeclaration(type)) {392return prefix + PrimitiveType.CustomObject.getText()393}394if (ts.isIndexedAccessTypeNode(type)) {395return prefix + PrimitiveType.CustomObject.getText()396}397if (ts.isEnumMember(type)) {398return prefix + this.enumName(type.name)399}400throw new Error(`Cannot compute type name: ${type.getText()} ${type.kind}`)401}402
403public enumName(name: ts.PropertyName): string {404// TODO: support namespaces in other declarations.405return `${PrimitiveType.ArkPrefix}${identNameWithNamespace(name)}`406}407
408public get orderedDependencies(): DeclarationTarget[] {409return this._orderedDependencies410}411private _orderedDependencies: DeclarationTarget[] = []412
413public get orderedDependenciesToGenerate(): DeclarationTarget[] {414return this._orderedDependenciesToGenerate415}416private _orderedDependenciesToGenerate: DeclarationTarget[] = []417analyze(library: PeerLibrary) {418const callbacks = collectCallbacks(library)419for (const callback of callbacks) {420callback.args.forEach(arg => {421const useToGenerate = library.shouldGenerateComponent(callback.componentName)422this.requestType(undefined, arg.type, useToGenerate)423})424}425
426let orderer = new DependencySorter(this)427for (let declaration of this.typeMap.values()) {428orderer.addDep(declaration[0])429}430this._orderedDependencies = orderer.getToposorted()431
432let toGenerateOrderer = new DependencySorter(this)433for (let declaration of this.typeMap.values()) {434if (declaration[2])435toGenerateOrderer.addDep(declaration[0])436}437this._orderedDependenciesToGenerate = toGenerateOrderer.getToposorted()438}439
440serializerName(name: string, type: ts.TypeNode): string {441return `write${name}`442}443
444deserializerName(name: string, type: ts.TypeNode): string {445return `read${name}`446}447
448declTargetConvertor(param: string, target: DeclarationTarget, isOptionalParam = false): ArgConvertor {449if (target instanceof PrimitiveType) {450if (target == PrimitiveType.Number || target == PrimitiveType.Int32) {451return new NumberConvertor(param)452}453if (target == PrimitiveType.Boolean) {454return new BooleanConvertor(param)455}456throw new Error("Unsupported primitive type: " + target.getText())457}458throw new Error("Unsupported type: " + target.getText())459}460
461typeConvertor(param: string, type: ts.TypeNode, isOptionalParam = false): ArgConvertor {462if (!type) throw new Error("Impossible")463if (isOptionalParam) {464return new OptionConvertor(param, this, type)465}466if (type.kind == ts.SyntaxKind.ObjectKeyword) {467return new CustomTypeConvertor(param, "Object")468}469if (type.kind == ts.SyntaxKind.UndefinedKeyword || type.kind == ts.SyntaxKind.VoidKeyword) {470return new UndefinedConvertor(param)471}472if (type.kind == ts.SyntaxKind.NullKeyword) {473throw new Error("Unsupported null")474}475if (type.kind == ts.SyntaxKind.NumberKeyword) {476return new NumberConvertor(param)477}478if (type.kind == ts.SyntaxKind.StringKeyword) {479return new StringConvertor(param, type)480}481if (type.kind == ts.SyntaxKind.BooleanKeyword) {482return new BooleanConvertor(param)483}484if (ts.isImportTypeNode(type)) {485if (identName(type.qualifier) === "Callback") {486return new FunctionConvertor(param, this, type)487}488return new ImportTypeConvertor(param, this, type)489}490if (ts.isTypeReferenceNode(type)) {491const declaration = getDeclarationsByNode(this.typeChecker!, type.typeName)[0]492return this.declarationConvertor(param, type, declaration)493}494if (ts.isEnumMember(type)) {495return new EnumConvertor(param, type.parent, this.isStringEnum(type.parent.members))496}497if (ts.isUnionTypeNode(type)) {498return new UnionConvertor(param, this, type)499}500if (ts.isTypeLiteralNode(type)) {501return new AggregateConvertor(param, this, type)502}503if (ts.isArrayTypeNode(type)) {504return new ArrayConvertor(param, this, type, type.elementType)505}506if (ts.isLiteralTypeNode(type)) {507if (type.literal.kind == ts.SyntaxKind.NullKeyword) {508return new NullConvertor(param)509}510if (type.literal.kind == ts.SyntaxKind.StringLiteral) {511return new StringConvertor(param, type)512}513throw new Error(`Unsupported literal type: ${type.literal.kind}` + type.getText())514}515if (ts.isTupleTypeNode(type)) {516return new TupleConvertor(param, this, type)517}518if (ts.isFunctionTypeNode(type)) {519return new FunctionConvertor(param, this, type)520}521if (ts.isParenthesizedTypeNode(type)) {522return this.typeConvertor(param, type.type)523}524if (ts.isOptionalTypeNode(type)) {525return new OptionConvertor(param, this, type.type)526}527if (ts.isTemplateLiteralTypeNode(type)) {528return new StringConvertor(param, type)529}530if (ts.isNamedTupleMember(type)) {531return this.typeConvertor(param, type.type)532}533if (type.kind == ts.SyntaxKind.AnyKeyword ||534type.kind == ts.SyntaxKind.UnknownKeyword ||535ts.isIndexedAccessTypeNode(type)536) {537return new CustomTypeConvertor(param, "Any")538}539if (ts.isTypeParameterDeclaration(type)) {540// TODO: unlikely correct.541return new CustomTypeConvertor(param, identName(type.name)!)542}543console.log(type)544throw new Error(`Cannot convert: ${asString(type)} ${type.getText()} ${type.kind}`)545}546
547private _currentContext: string | undefined = undefined548getCurrentContext(): string | undefined {549return this._currentContext550}551setCurrentContext(context: string | undefined) {552this._currentContext = context553}554
555private customConvertor(typeName: ts.EntityName | undefined, param: string, type: ts.TypeReferenceNode | ts.ImportTypeNode): ArgConvertor | undefined {556let name = getNameWithoutQualifiersRight(typeName)557switch (name) {558case `Dimension`:559case `Length`:560return new LengthConvertor(name, param)561case `Date`:562return new CustomTypeConvertor(param, name, name)563case `AttributeModifier`:564return new PredefinedConvertor(param, "AttributeModifier<any>", "AttributeModifier", "CustomObject")565case `AnimationRange`:566return new CustomTypeConvertor(param, "AnimationRange", "AnimationRange<number>")567case `ContentModifier`:568return new CustomTypeConvertor(param, "ContentModifier", "ContentModifier<any>")569case `Record`:570return new CustomTypeConvertor(param, "Record", "Record<string, string>")571case `Array`:572return new ArrayConvertor(param, this, type, type.typeArguments![0])573case `Map`:574return new MapConvertor(param, this, type, type.typeArguments![0], type.typeArguments![1])575case `Callback`:576return new FunctionConvertor(param, this, type)577case `Optional`:578if (type.typeArguments && type.typeArguments.length == 1)579return new OptionConvertor(param, this, type.typeArguments![0])580}581return undefined582}583
584isPointerDeclaration(target: DeclarationTarget, isOptional: boolean = false): boolean {585if (isOptional) return true586if (target instanceof PrimitiveType) return target.isPointer587if (ts.isEnumDeclaration(target)) return false588if (ts.isInterfaceDeclaration(target) || ts.isClassDeclaration(target)) return true589return true590}591
592declarationConvertor(param: string, type: ts.TypeReferenceNode, declaration: ts.NamedDeclaration | undefined): ArgConvertor {593const entityName = typeEntityName(type)594if (!declaration) {595return this.customConvertor(entityName, param, type) ?? throwException(`Declaration not found for: ${type.getText()}`)596}597if (PeerGeneratorConfig.isConflictedDeclaration(declaration))598return new CustomTypeConvertor(param, identName(declaration.name)!)599const declarationName = identName(declaration.name)!600let customConvertor = this.customConvertor(entityName, param, type)601if (customConvertor) {602return customConvertor603}604if (ts.isEnumDeclaration(declaration)) {605return new EnumConvertor(param, declaration, this.isStringEnum(declaration.members))606}607if (ts.isEnumMember(declaration)) {608return new EnumConvertor(param, declaration.parent, this.isStringEnum(declaration.parent.members))609}610if (ts.isTypeAliasDeclaration(declaration)) {611return new TypeAliasConvertor(param, this, declaration, type.typeArguments)612}613if (ts.isInterfaceDeclaration(declaration)) {614if (isMaterialized(declaration)) {615return new MaterializedClassConvertor(declarationName, param, this, declaration)616}617return new InterfaceConvertor(declarationName, param, declaration, this, type)618}619if (ts.isClassDeclaration(declaration)) {620if (isMaterialized(declaration)) {621return new MaterializedClassConvertor(declarationName, param, this, declaration)622}623return new ClassConvertor(declarationName, param, declaration, this, type)624}625if (ts.isTypeParameterDeclaration(declaration)) {626// TODO: incorrect, we must use actual, not formal type parameter.627return new CustomTypeConvertor(param, identName(declaration.name)!)628}629console.log(`${declaration.getText()}`)630throw new Error(`Unknown kind: ${declaration.kind}`)631}632
633private printStructsCHead(name: string, descriptor: StructDescriptor, structs: IndentedPrinter) {634if (descriptor.isArray) {635// Forward declaration of element type.636let elementTypePointer = descriptor.getFields()[0].declaration637if (!(elementTypePointer instanceof PointerType))638throw new Error(`Unexpected ${this.computeTargetName(elementTypePointer, false)}`)639let elementType = elementTypePointer.pointed640if (!(elementType instanceof PrimitiveType)) {641let name = this.computeTargetName(elementType, false)642if (ts.isEnumDeclaration(elementType)) {643structs.print(`typedef int32_t ${this.enumName(elementType.name)};`)644}645}646}647if (descriptor.isPacked) {648structs.print(`#ifdef _MSC_VER`)649structs.print(`#pragma pack(push, 1)`)650structs.print(`#endif`)651}652structs.print(`typedef struct ${name} {`)653structs.pushIndent()654}655
656
657private printStructsCTail(name: string, needPacked: boolean, structs: IndentedPrinter) {658structs.popIndent()659if (needPacked) {660structs.print(`#ifdef _MSC_VER`)661structs.print(`}`)662structs.print(`#pragma pack(pop)`)663structs.print(`#else`)664structs.print(`} __attribute__((packed))`)665structs.print(`#endif`)666structs.print(`${name};`)667} else {668structs.print(`} ${name};`)669}670}671
672private printStructField(structs: IndentedPrinter, field: FieldRecord) {673structs.print(`${this.cFieldKind(field.declaration)}${field.optional ? PrimitiveType.OptionalPrefix : ""}${this.computeTargetName(field.declaration, false)} ${field.name};`)674}675
676allOptionalTypes(): Set<string> {677const seenNames = new Set<string>()678seenNames.clear()679for (let target of this.orderedDependencies) {680if (target instanceof PointerType) continue681let nameAssigned = this.computeTargetName(target, false)682if (nameAssigned === PrimitiveType.Tag.getText(this)) {683continue684}685if (!nameAssigned) {686throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)687}688if (seenNames.has(nameAssigned)) continue689let nameOptional = PrimitiveType.OptionalPrefix + nameAssigned690seenNames.add(nameOptional)691}692return seenNames693}694
695allLiteralTypes(): Map<string, string> {696const literals = new Map<string, string>()697for (let target of this.orderedDependencies) {698
699let nameAssigned = this.computeTargetName(target, false)700if (nameAssigned === PrimitiveType.Tag.getText(this)) {701continue702}703if (!nameAssigned) {704throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)705}706if (literals.has(nameAssigned)) continue707if (nameAssigned.startsWith("Literal_")) {708const type = nameAssigned.split("_").at(1)!709literals.set(nameAssigned, type)710}711
712}713return literals714}715
716allUnionTypes() {717
718type Selector = {719id: number;720name: string;721}722
723const unions = new Map<string, Selector[]>()724for (let target of this.orderedDependencies) {725let nameAssigned = this.computeTargetName(target, false)726if (nameAssigned === PrimitiveType.Tag.getText(this)) {727continue728}729if (!nameAssigned) {730throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)731}732
733if (this.isMaybeWrapped(target, ts.isUnionTypeNode)) {734const selectors: Selector[] = []735this.targetStruct(target).getFields().forEach((field, index) => {736if (index === 0) return737selectors.push({ id: index, name: field.name })738})739
740unions.set(nameAssigned, selectors )741}742}743return unions744}745
746generateStructs(structs: IndentedPrinter, typedefs: IndentedPrinter, writeToString: LanguageWriter) {747const seenNames = new Set<string>()748seenNames.clear()749let noDeclaration = [PrimitiveType.Int32, PrimitiveType.Tag, PrimitiveType.Number, PrimitiveType.Boolean, PrimitiveType.String]750for (let target of this.orderedDependencies) {751let nameAssigned = this.computeTargetName(target, false)752if (nameAssigned === PrimitiveType.Tag.getText(this)) {753continue754}755if (!nameAssigned) {756throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)757}758if (seenNames.has(nameAssigned)) continue759seenNames.add(nameAssigned)760let isPointer = this.isPointerDeclaration(target)761let isEnum = !(target instanceof PrimitiveType) && ts.isEnumDeclaration(target)762let isAccessor = checkDeclarationTargetMaterialized(target)763let noBasicDecl = isAccessor || (target instanceof PrimitiveType && noDeclaration.includes(target))764let nameOptional = PrimitiveType.OptionalPrefix + nameAssigned765let isUnion = this.isMaybeWrapped(target, ts.isUnionTypeNode)766if (isEnum) {767structs.print(`typedef ${PrimitiveType.Int32.getText()} ${nameAssigned};`)768if (!seenNames.has(nameOptional)) {769seenNames.add(nameOptional)770structs.print(`typedef struct ${nameOptional} { enum ${PrimitiveType.Tag.getText()} tag; ${nameAssigned} value; } ${nameOptional};`)771this.writeOptional(nameOptional, writeToString, isPointer)772this.writeRuntimeType(target, nameOptional, true, writeToString)773}774continue775}776const structDescriptor = this.targetStruct(target)777if (!noBasicDecl && !this.ignoreTarget(target)) {778
779// TODO: fix it to define array type after its elements types780if (nameAssigned === "Array_GestureRecognizer") {781structs.print("typedef Ark_Materialized GestureRecognizer;")782}783
784this.printStructsCHead(nameAssigned, structDescriptor, structs)785if (isUnion) {786const selector = structDescriptor.getFields().find(value => {return value.name === "selector"})787if (selector) {788this.printStructField(structs, selector)789}790structs.print("union {")791structs.pushIndent()792structDescriptor.getFields().filter(value => value.name !== "selector")793.forEach(it => this.printStructField(structs, it))794structs.popIndent()795structs.print("};")796} else {797structDescriptor.getFields().forEach(it => this.printStructField(structs, it))798}799this.printStructsCTail(nameAssigned, structDescriptor.isPacked, structs)800}801if (isAccessor) {802structs.print(`typedef Ark_Materialized ${nameAssigned};`)803}804let skipWriteToString = (target instanceof PrimitiveType) || ts.isEnumDeclaration(target) || ts.isFunctionTypeNode(target)805if (!noBasicDecl && !skipWriteToString) {806this.generateWriteToString(nameAssigned, target, writeToString, isPointer)807}808this.writeRuntimeType(target, nameAssigned, false, writeToString)809if (seenNames.has(nameOptional)) continue810seenNames.add(nameOptional)811if (!(target instanceof PointerType) && nameAssigned != "Optional" && nameAssigned != "RelativeIndexable") {812this.printStructsCHead(nameOptional, structDescriptor, structs)813structs.print(`enum ${PrimitiveType.Tag.getText()} tag;`)814structs.print(`${nameAssigned} value;`)815this.printStructsCTail(nameOptional, structDescriptor.isPacked, structs)816this.writeOptional(nameOptional, writeToString, isPointer)817this.writeRuntimeType(target, nameOptional, true, writeToString)818}819}820for (let declarationTarget of this.typeMap.values()) {821let target = declarationTarget[0]822let aliasNames = declarationTarget[1]823let declarationName = this.computeTargetName(target, false)824aliasNames.forEach(aliasName => this.addNameAlias(target, declarationName, aliasName, seenNames, typedefs))825}826// TODO: hack, remove me!827typedefs.print(`typedef ${PrimitiveType.OptionalPrefix}Ark_Length ${PrimitiveType.OptionalPrefix}Dimension;`)828typedefs.print(`typedef ${PrimitiveType.OptionalPrefix}Ark_Length ${PrimitiveType.OptionalPrefix}Length;`)829}830
831private writeRuntimeType(target: DeclarationTarget, targetTypeName: string, isOptional: boolean, writer: LanguageWriter) {832const resultType = new Type("Ark_RuntimeType")833const op = this.writeRuntimeTypeOp(target, targetTypeName, resultType, isOptional, writer)834if (op) {835writer.print("template <>")836writer.writeMethodImplementation(837new Method("runtimeType",838new NamedMethodSignature(resultType, [new Type(`const ${targetTypeName}&`)], ["value"]),839[MethodModifier.INLINE]),840op)841}842}843
844private writeRuntimeTypeOp(845target: DeclarationTarget, targetTypeName: string, resultType: Type, isOptional: boolean, writer: LanguageWriter846) : ((writer: LanguageWriter) => void) | undefined847{848let result: LanguageExpression849if (isOptional) {850result = writer.makeTernary(writer.makeDefinedCheck("value.tag"),851writer.makeRuntimeType(RuntimeType.OBJECT), writer.makeRuntimeType(RuntimeType.UNDEFINED))852} else if (target instanceof PointerType) {853return854} else if (target instanceof PrimitiveType) {855switch (target) {856case PrimitiveType.Boolean:857result = writer.makeRuntimeType(RuntimeType.BOOLEAN)858break859case PrimitiveType.CustomObject:860case PrimitiveType.Materialized:861case PrimitiveType.NativePointer:862case PrimitiveType.Resource:863case PrimitiveType.Tag:864return undefined865case PrimitiveType.Function:866result = writer.makeRuntimeType(RuntimeType.FUNCTION)867break868case PrimitiveType.Int32:869case PrimitiveType.Number:870result = writer.makeRuntimeType(RuntimeType.NUMBER)871break872case PrimitiveType.Length:873result = writer.makeCast(writer.makeString("value.type"), resultType)874break875case PrimitiveType.String:876result = writer.makeRuntimeType(RuntimeType.STRING)877break878case PrimitiveType.Undefined:879result = writer.makeRuntimeType(RuntimeType.UNDEFINED)880break881default:882throw new Error(`Unexpected PrimitiveType ${target.getText()}`)883}884} else if (ts.isEnumDeclaration(target)) {885result = writer.makeRuntimeType(RuntimeType.NUMBER)886} else if (checkDeclarationTargetMaterialized(target)) {887return undefined888} else if (ts.isOptionalTypeNode(target)) {889result = writer.makeTernary(writer.makeDefinedCheck("value.tag"),890writer.makeRuntimeType(RuntimeType.OBJECT), writer.makeRuntimeType(RuntimeType.UNDEFINED))891} else if (ts.isUnionTypeNode(target)) {892return writer => {893writer.print("switch (value.selector) {")894writer.pushIndent()895for (let i = 0; i < target.types.length; i++) {896writer.print(`case ${i}: return runtimeType(value.value${i});`)897}898writer.print(`default: throw "Bad selector in ${targetTypeName}: " + std::to_string(value.selector);`)899writer.popIndent()900writer.print("}")901}902} else {903result = writer.makeRuntimeType(RuntimeType.OBJECT)904}905return writer => writer.writeStatement(writer.makeReturn(result))906}907
908private addNameAlias(target: DeclarationTarget, declarationName: string, aliasName: string,909seenNames: Set<string>, typedefs: IndentedPrinter): void {910if (seenNames.has(aliasName)) return911if (this.ignoreTarget(target) && target != PrimitiveType.CustomObject) return912seenNames.add(aliasName)913typedefs.print(`typedef ${declarationName} ${aliasName};`)914// TODO: hacky915let optAliasName = `${PrimitiveType.OptionalPrefix}${aliasName}`916if (!declarationName.startsWith(PrimitiveType.OptionalPrefix) && !seenNames.has(optAliasName)) {917seenNames.add(optAliasName)918typedefs.print(`typedef ${PrimitiveType.OptionalPrefix}${declarationName} ${optAliasName};`)919}920}921
922cFieldKind(declaration: DeclarationTarget): string {923if (declaration instanceof PointerType) return this.cFieldKind(declaration.pointed)924if (declaration instanceof PrimitiveType) return ""925if (ts.isEnumDeclaration(declaration)) return ""926if (ts.isImportTypeNode(declaration)) return ""927if (checkDeclarationTargetMaterialized(declaration)) return ""928return `struct `929}930
931writeOptional(nameOptional: string, printer: LanguageWriter, isPointer: boolean) {932printer.print(`template <>`)933printer.print(`inline void WriteToString(string* result, const ${nameOptional}* value) {`)934printer.print(`result->append("{.tag=");`)935printer.print(`result->append(tagNameExact((${PrimitiveType.Tag.getText()})(value->tag)));`)936printer.print(`result->append(", .value=");`)937printer.pushIndent()938printer.print(`if (value->tag != ${PrimitiveType.UndefinedTag}) {`)939printer.pushIndent()940printer.print(`WriteToString(result, ${isPointer ? "&" : ""}value->value);`)941printer.popIndent()942printer.print(`} else {`)943printer.pushIndent()944printer.print(`${PrimitiveType.Undefined.getText()} undefined = { 0 };`)945printer.print(`WriteToString(result, undefined);`)946printer.popIndent()947printer.print(`}`)948printer.popIndent()949printer.print(`result->append("}");`)950printer.print(`}`)951}952
953writeOptionalConvertor(nameOptional: string, printer: LanguageWriter, isPointer: boolean) {954printer.print(`template <>`)955printer.print(`inline void convertor(const ${nameOptional}* value) {`)956printer.pushIndent()957printer.print(`if (value->tag != ${PrimitiveType.UndefinedTag}) {`)958printer.pushIndent()959printer.print(`convertor(${isPointer ? "&" : ""}value->value);`)960printer.popIndent()961printer.print(`} else {`)962printer.pushIndent()963printer.print(`${PrimitiveType.Undefined.getText()} undefined = { 0 };`)964printer.print(`convertor(undefined);`)965printer.popIndent()966printer.print(`}`)967printer.popIndent()968printer.print(`}`)969}970
971visitDeclaration(972target: DeclarationTarget,973visitor: StructVisitor,974): void {975if (this.isMaybeWrapped(target, ts.isUnionTypeNode)) {976this.targetStruct(target).getFields().forEach((field, index) => {977if (index === 0) return978visitor.visitUnionField(field, index - 1)979})980} else {981visitor.visitInseparable()982}983}984
985private isMaybeWrapped(target: DeclarationTarget, predicate: (type: ts.Node) => boolean): boolean {986if (target instanceof PrimitiveType) return false987return predicate(target) ||988ts.isParenthesizedTypeNode(target) &&989this.isDeclarationTarget(target.type) &&990predicate(target.type)991}992
993private generateArrayWriteToString(name: string, target: DeclarationTarget, printer: LanguageWriter) {994if (target instanceof PrimitiveType) throw new Error("Impossible")995let elementType = ts.isArrayTypeNode(target)996? target.elementType997: ts.isTypeReferenceNode(target) && target.typeArguments998? target.typeArguments[0]999: undefined1000
1001if (!elementType) throw new Error("Impossible")1002let convertor = this.typeConvertor("param", elementType)1003let isPointerField = convertor.isPointerType()1004let elementNativeType = convertor.nativeType(false)1005let constCast = isPointerField ? `(const ${elementNativeType}*)` : ``1006
1007printer.print(`inline void WriteToString(string* result, const ${name}* value, const std::string& ptrName = std::string()) {`)1008printer.pushIndent()1009printer.print(`result->append("{");`)1010printer.print(`if (ptrName.empty()) {`)1011printer.pushIndent()1012printer.print(`int32_t count = value->length;`)1013printer.print(`if (count > 0) result->append("{");`)1014printer.print(`for (int i = 0; i < count; i++) {`)1015printer.pushIndent()1016printer.print(`if (i > 0) result->append(", ");`)1017printer.print(`WriteToString(result, ${constCast}${isPointerField ? "&" : ""}value->array[i]);`)1018printer.popIndent()1019printer.print(`}`)1020printer.print(`if (count == 0) result->append("{}");`)1021printer.print(`if (count > 0) result->append("}");`)1022printer.popIndent()1023printer.print(`} else {`)1024printer.pushIndent()1025printer.print(`result->append(ptrName + ".data()");`)1026printer.popIndent()1027printer.print(`}`)1028printer.print(`result->append(", .length=");`)1029printer.print(`result->append(std::to_string(value->length));`)1030printer.print(`result->append("}");`)1031printer.popIndent()1032printer.print(`}`)1033}1034
1035private generateStdArrayDefinition(name: string, target: DeclarationTarget, printer: LanguageWriter) {1036if (target instanceof PrimitiveType) throw new Error("Impossible")1037let elementType = ts.isArrayTypeNode(target)1038? target.elementType1039: ts.isTypeReferenceNode(target) && target.typeArguments1040? target.typeArguments[0]1041: undefined1042
1043if (!elementType) throw new Error("Impossible")1044let convertor = this.typeConvertor("param", elementType)1045let isPointerField = convertor.isPointerType()1046let elementNativeType = convertor.nativeType(false)1047let constCast = isPointerField ? `(const ${elementNativeType}*)` : ``1048
1049// Provide prototype of element printer.1050printer.print(`template <>`)1051printer.print(`inline void WriteToString(string* result, const ${elementNativeType}${isPointerField ? "*" : ""} value);`)1052
1053// Printer.1054printer.print(`inline void generateStdArrayDefinition(string* result, const ${name}* value) {`)1055printer.pushIndent()1056printer.print(`int32_t count = value->length;`)1057printer.print(`result->append("std::array<${elementNativeType}, " + std::to_string(count) + ">{{");`)1058printer.print(`for (int i = 0; i < count; i++) {`);1059printer.pushIndent()1060printer.print(`std::string tmp;`)1061printer.print(`WriteToString(result, ${constCast}${isPointerField ? "&" : ""}value->array[i]);`)1062printer.print(`result->append(tmp);`);1063printer.print(`result->append(", ");`)1064printer.popIndent()1065printer.print(`}`)1066printer.print(`result->append("}}");`)1067printer.popIndent()1068printer.print(`}`)1069}1070
1071private generateMapWriteToString(name: string, target: DeclarationTarget, printer: LanguageWriter) {1072if (target instanceof PrimitiveType)1073throw new Error("Impossible")1074const [keyType, valueType] = ts.isTypeReferenceNode(target) && target.typeArguments1075? target.typeArguments1076: [undefined, undefined]1077if (!keyType || !valueType)1078throw new Error("Impossible")1079const keyConvertor = this.typeConvertor("_", keyType)1080const valueConvertor = this.typeConvertor("_", valueType)1081let isPointerKeyField = keyConvertor.isPointerType()1082let isPointerValueField = valueConvertor.isPointerType()1083let keyNativeType = keyConvertor.nativeType(false)1084let valueNativeType = valueConvertor.nativeType(false)1085let keyConstCast = isPointerKeyField ? `(const ${keyNativeType}*)` : ``1086let valueConstCast = isPointerValueField ? `(const ${valueNativeType}*)` : ``1087
1088// Provide prototype of keys printer.1089printer.print(`template <>`)1090printer.print(`inline void WriteToString(string* result, const ${keyNativeType}${isPointerKeyField ? "*" : ""} value);`)1091// Provide prototype of values printer.1092printer.print(`template <>`)1093printer.print(`inline void WriteToString(string* result, const ${valueNativeType}${isPointerValueField ? "*" : ""} value);`)1094
1095// Printer.1096printer.print(`template <>`)1097printer.print(`inline void WriteToString(string* result, const ${name}* value) {`)1098printer.pushIndent()1099printer.print(`result->append("{");`)1100printer.print(`int32_t count = value->size;`)1101printer.print(`for (int i = 0; i < count; i++) {`)1102printer.pushIndent()1103printer.print(`if (i > 0) result->append(", ");`)1104printer.print(`WriteToString(result, ${keyConstCast}${isPointerKeyField ? "&" : ""}value->keys[i]);`)1105printer.print(`result->append(": ");`)1106printer.print(`WriteToString(result, ${valueConstCast}${isPointerValueField ? "&" : ""}value->values[i]);`)1107printer.popIndent()1108printer.print(`}`)1109printer.print(`result->append("}");`)1110printer.popIndent()1111printer.print(`}`)1112}1113
1114private generateWriteToString(name: string, target: DeclarationTarget, printer: LanguageWriter, isPointer: boolean) {1115if (target instanceof PrimitiveType) throw new Error("Impossible")1116
1117this.setCurrentContext(`writeToString(${name})`)1118let isUnion = this.isMaybeWrapped(target, ts.isUnionTypeNode)1119let isArray = this.isMaybeWrapped(target, ts.isArrayTypeNode)1120let isMap = ts.isTypeReferenceNode(target) && identName(target.typeName) === "Map"1121let isOptional = this.isMaybeWrapped(target, ts.isOptionalTypeNode)1122let isTuple = this.isMaybeWrapped(target, ts.isTupleTypeNode)1123let access = isPointer ? "->" : "."1124
1125// treat Array<T> as array1126if (!isArray && ts.isTypeReferenceNode(target)) {1127isArray = identName(target.typeName) === "Array"1128}1129if (isArray) {1130this.generateStdArrayDefinition(name, target, printer)1131this.generateArrayWriteToString(name, target, printer)1132} else if (isMap) {1133this.generateMapWriteToString(name, target, printer)1134} else {1135printer.print(`template <>`)1136printer.print(`inline void WriteToString(string* result, const ${name}${isPointer ? "*" : ""} value) {`)1137printer.pushIndent()1138
1139if (isUnion) {1140printer.print(`result->append("{");`);1141printer.print(`result->append(".selector=");`)1142printer.print(`result->append(std::to_string(value->selector));`);1143printer.print(`result->append(", ");`);1144this.targetStruct(target).getFields().forEach((field, index) => {1145let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)1146if (index != 0) printer.print(`// ${this.computeTargetName(field.declaration, false)}`)1147printer.print(`if (value${access}selector == ${index - 1}) {`)1148printer.pushIndent()1149printer.print(`result->append(".${field.name}=");`);1150printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)1151printer.popIndent()1152printer.print(`}`)1153})1154if (false) {1155printer.print(`result->append(" /* ${name} [variant ");`)1156printer.print(`result->append(std::to_string(value${access}selector));`)1157printer.print(`result->append("]*/");`)1158}1159printer.print(`result->append("}");`);1160} else if (isTuple) {1161printer.print(`result->append("{");`)1162const fields = this.targetStruct(target).getFields()1163fields.forEach((field, index) => {1164printer.print(`// ${this.computeTargetName(field.declaration, false)}`)1165let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)1166if (index > 0) printer.print(`result->append(", ");`)1167printer.print(`result->append(".${field.name}=");`)1168printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)1169})1170printer.print(`result->append("}");`)1171} else if (isOptional) {1172printer.print(`result->append("{");`)1173const fields = this.targetStruct(target).getFields()1174fields.forEach((field, index) => {1175printer.print(`// ${this.computeTargetName(field.declaration, false)}`)1176if (index > 0) printer.print(`result->append(", ");`)1177printer.print(`result->append("${field.name}: ");`)1178let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)1179printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)1180if (index == 0) {1181printer.print(`if (value${access}${field.name} != ${PrimitiveType.UndefinedTag}) {`)1182printer.pushIndent()1183}1184if (index == fields.length - 1) {1185printer.popIndent()1186printer.print("}")1187}1188})1189printer.print(`result->append("}");`)1190} else {1191printer.print(`result->append("{");`)1192this.targetStruct(target).getFields().forEach((field, index) => {1193printer.print(`// ${this.computeTargetName(field.declaration, false)}`)1194if (index > 0) printer.print(`result->append(", ");`)1195printer.print(`result->append(".${field.name}=");`)1196let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)1197printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)1198})1199printer.print(`result->append("}");`)1200}1201printer.popIndent()1202printer.print(`}`)1203}1204this.setCurrentContext(undefined)1205}1206
1207private fieldsForClass(clazz: ts.ClassDeclaration | ts.InterfaceDeclaration, result: StructDescriptor) {1208clazz.heritageClauses?.forEach(it => {1209heritageDeclarations(this.typeChecker!, it).forEach(it => {1210if (ts.isClassDeclaration(it) || ts.isInterfaceDeclaration(it)) {1211result.supers.push(it)1212result.isPacked = false1213this.fieldsForClass(it, result)1214}1215})1216})1217if (ts.isClassDeclaration(clazz)) {1218clazz
1219.members1220.filter(ts.isPropertyDeclaration)1221.filter(it => !isStatic(it.modifiers))1222.forEach(it => {1223result.addField(new FieldRecord(this.toTarget(it.type!), it.type!, identName(it.name)!, it.questionToken != undefined))1224})1225extractBuilderFields(clazz, this).forEach(field => {1226result.addField(field)1227})1228} else {1229clazz
1230.members1231.filter(ts.isPropertySignature)1232.filter(it => !isStatic(it.modifiers))1233.forEach(it => {1234result.addField(new FieldRecord(this.toTarget(it.type!), it.type!, identName(it.name)!, it.questionToken != undefined))1235})1236}1237}1238
1239targetStruct(target: DeclarationTarget): StructDescriptor {1240let result = new StructDescriptor()1241if (target instanceof PointerType) {1242// Break the dependency cycle.1243// result.deps.add(target.pointed)1244return result1245}1246
1247if (target instanceof PrimitiveType) {1248return result1249}1250else if (ts.isArrayTypeNode(target)) {1251result.isArray = true1252let element = this.toTarget(target.elementType)1253result.addField(new FieldRecord(PrimitiveType.pointerTo(element), target, "array"))1254result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "length"))1255}1256else if (ts.isInterfaceDeclaration(target)) {1257this.fieldsForClass(target, result)1258}1259else if (ts.isClassDeclaration(target)) {1260this.fieldsForClass(target, result)1261}1262else if (ts.isUnionTypeNode(target)) {1263result.addField(new FieldRecord(PrimitiveType.Int32, undefined, `selector`, false))1264target
1265.types1266.forEach((it, index) => {1267result.addField(new FieldRecord(this.toTarget(it), it, `value${index}`, false))1268})1269}1270else if (ts.isTypeLiteralNode(target)) {1271if (target.members.some(ts.isIndexSignatureDeclaration)) {1272// For indexed access we just replace the whole type to a custom accessor.1273result.addField(new FieldRecord(PrimitiveType.CustomObject, undefined, "keyAccessor", false))1274} else {1275target
1276.members1277.forEach(it => {1278if (ts.isPropertySignature(it))1279result.addField(new FieldRecord(this.toTarget(it.type!), it.type, identName(it.name)!, it.questionToken != undefined))1280})1281}1282}1283else if (ts.isTupleTypeNode(target)) {1284target
1285.elements1286.forEach((it, index) => {1287if (ts.isNamedTupleMember(it)) {1288result.addField(new FieldRecord(this.toTarget(it.type!), it.type!, identName(it.name)!, it.questionToken != undefined))1289} else {1290result.addField(new FieldRecord(this.toTarget(it), it, `value${index}`, false))1291}1292})1293}1294else if (ts.isOptionalTypeNode(target)) {1295result.addField(new FieldRecord(PrimitiveType.Tag, undefined, "tag"))1296result.addField(new FieldRecord(this.toTarget(target.type), target.type, "value"))1297}1298else if (ts.isParenthesizedTypeNode(target)) {1299// TODO: is it correct?1300return this.targetStruct(this.toTarget(target.type))1301}1302else if (ts.isEnumDeclaration(target) || ts.isEnumMember(target)) {1303result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "value"))1304}1305else if (ts.isFunctionTypeNode(target)) {1306}1307else if (ts.isImportTypeNode(target)) {1308}1309else if (ts.isTemplateLiteralTypeNode(target)) {1310}1311else if (ts.isLiteralTypeNode(target)) {1312}1313else if (ts.isTypeParameterDeclaration(target)) {1314// TODO: is it really correct1315}1316else if (ts.isTypeReferenceNode(target)) {1317if (!target.typeArguments) throw new Error("Only type references with type arguments allowed")1318let name = identName(target.typeName)1319if (name == "Optional") {1320let type = target.typeArguments[0]1321result.addField(new FieldRecord(PrimitiveType.Tag, undefined, "tag"))1322result.addField(new FieldRecord(this.toTarget(type), type, "value"))1323} else if (name == "Array") {1324let type = target.typeArguments[0]1325result.isArray = true1326result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(type)), undefined, "array"))1327result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "length"))1328} else if (name == "Map") {1329let keyType = target.typeArguments[0]1330let valueType = target.typeArguments[1]1331result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "size"))1332result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(keyType)), undefined, "keys"))1333result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(valueType)), undefined, "values"))1334} else if (name == "ContentModifier") {1335let type = target.typeArguments[0]1336result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(type)), undefined, "config"))1337} else if (name == "Callback") {1338result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "id"))1339} else if (PeerGeneratorConfig.isKnownParametrized(name)) {1340// TODO: not this way yet!1341// let type = target.typeArguments[0]1342// result.addField(new FieldRecord(this.toTarget(type), type, "value0"))1343// result.addField(new FieldRecord(this.toTarget(type), type, "value1"))1344} else {1345throw new Error(`Parametrized type unknown: ${name} ${(target as any).getText()}`)1346}1347}1348else {1349throw new Error(`Unsupported field getter: ${asString(target)} ${(target as any).getText()}`)1350}1351return result1352}1353
1354private ignoreTarget(target: DeclarationTarget): target is PrimitiveType | ts.EnumDeclaration {1355const name = this.computeTargetName(target, false)1356if (PeerGeneratorConfig.ignoreSerialization.includes(name)) return true1357if (target instanceof PrimitiveType) return true1358if (ts.isEnumDeclaration(target)) return true1359if (ts.isFunctionTypeNode(target)) return true1360if (ts.isImportTypeNode(target)) return true1361if (ts.isTemplateLiteralTypeNode(target)) return true1362return false1363}1364
1365private isStringEnum(members: NodeArray<EnumMember>): boolean {1366return members.find((value) => {1367return value.initializer && ts.isStringLiteral(value.initializer)1368}) != undefined1369}1370}
1371
1372class ToDeclarationTargetConvertor implements TypeNodeConvertor<DeclarationTarget> {1373constructor(1374private readonly table: DeclarationTable,1375) {}1376
1377convertUnion(node: ts.UnionTypeNode): DeclarationTarget {1378return node1379}1380convertTypeLiteral(node: ts.TypeLiteralNode): DeclarationTarget {1381return node1382}1383convertLiteralType(node: ts.LiteralTypeNode): DeclarationTarget {1384return node1385}1386convertTuple(node: ts.TupleTypeNode): DeclarationTarget {1387return node1388}1389convertArray(node: ts.ArrayTypeNode): DeclarationTarget {1390return node1391}1392convertOptional(node: ts.OptionalTypeNode): DeclarationTarget {1393return node1394}1395convertFunction(node: ts.FunctionTypeNode): DeclarationTarget {1396return node1397}1398convertTemplateLiteral(node: ts.TemplateLiteralTypeNode): DeclarationTarget {1399return node1400}1401convertImport(node: ts.ImportTypeNode): DeclarationTarget {1402let name = identName(node.qualifier)!1403switch (name) {1404case "Resource": return PrimitiveType.Resource1405case "Callback": return PrimitiveType.Function1406default: return PrimitiveType.CustomObject1407}1408}1409convertTypeReference(node: ts.TypeReferenceNode): DeclarationTarget {1410let name = identName(node)1411switch (name) {1412case `Dimension`: case `Length`: return PrimitiveType.Length1413case `AnimationRange`: return PrimitiveType.CustomObject1414case `ContentModifier`: return PrimitiveType.CustomObject1415case `Date`: return PrimitiveType.CustomObject1416// stub required to compile arkoala patched sdk1417case `Function`: return PrimitiveType.Function1418}1419// Types with type arguments are declarations!1420if (node.typeArguments) {1421return node1422}1423
1424let declarations = getDeclarationsByNode(this.table.typeChecker!, node.typeName)1425if (declarations.length == 0) {1426throw new Error(`No declaration for ${node.getText()} ${asString(node)}`)1427}1428let declaration = declarations[0]1429if (PeerGeneratorConfig.isConflictedDeclaration(declaration))1430return PrimitiveType.CustomObject1431if (ts.isTypeAliasDeclaration(declaration)) {1432const node = declaration.type1433this.table.requestType(identName(declaration.name), node, false)1434return convertTypeNode(this, node)1435}1436if (ts.isEnumMember(declaration)) {1437return declaration.parent1438}1439if (ts.isTypeParameterDeclaration(declaration)) {1440return PrimitiveType.CustomObject1441}1442if (ts.isClassDeclaration(declaration) ||1443ts.isInterfaceDeclaration(declaration) ||1444ts.isEnumDeclaration(declaration))1445return declaration1446throw new Error(`Unknown declaration type ${ts.SyntaxKind[declaration.kind]}`)1447}1448convertParenthesized(node: ts.ParenthesizedTypeNode): DeclarationTarget {1449return convertTypeNode(this, node.type)1450}1451convertIndexedAccess(node: ts.IndexedAccessTypeNode): DeclarationTarget {1452return PrimitiveType.CustomObject1453}1454convertStringKeyword(node: ts.TypeNode): DeclarationTarget {1455return PrimitiveType.String1456}1457convertNumberKeyword(node: ts.TypeNode): DeclarationTarget {1458return PrimitiveType.Number1459}1460convertBooleanKeyword(node: ts.TypeNode): DeclarationTarget {1461return PrimitiveType.Boolean1462}1463convertUndefinedKeyword(node: ts.TypeNode): DeclarationTarget {1464return PrimitiveType.Undefined1465}1466convertVoidKeyword(node: ts.TypeNode): DeclarationTarget {1467// TODO: shall it be distinct type.1468return PrimitiveType.Undefined1469}1470convertObjectKeyword(node: ts.TypeNode): DeclarationTarget {1471return PrimitiveType.CustomObject1472}1473convertAnyKeyword(node: ts.TypeNode): DeclarationTarget {1474return PrimitiveType.CustomObject1475}1476convertUnknownKeyword(node: ts.TypeNode): DeclarationTarget {1477return PrimitiveType.CustomObject1478}1479}