idlize

Форк
0
/
DeclarationTable.ts 
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

16
import * as ts from "typescript"
17
import { Language, asString, getDeclarationsByNode, getNameWithoutQualifiersRight, heritageDeclarations,
18
     identName, isStatic, throwException, typeEntityName, identNameWithNamespace} from "../util"
19
import { IndentedPrinter } from "../IndentedPrinter"
20
import { PeerGeneratorConfig } from "./PeerGeneratorConfig"
21
import {
22
    AggregateConvertor, ArgConvertor, ArrayConvertor, BooleanConvertor, ClassConvertor, CustomTypeConvertor,
23
    EnumConvertor, FunctionConvertor, ImportTypeConvertor, InterfaceConvertor, LengthConvertor, MapConvertor,
24
    MaterializedClassConvertor, NullConvertor, NumberConvertor, OptionConvertor, PredefinedConvertor, StringConvertor,
25
    ToStringConvertor, TupleConvertor, TypeAliasConvertor, UndefinedConvertor, UnionConvertor
26
} from "./Convertors"
27
import { DependencySorter } from "./DependencySorter"
28
import { checkDeclarationTargetMaterialized, isMaterialized } from "./Materialized"
29
import { LanguageExpression, LanguageWriter, Method, MethodModifier, NamedMethodSignature, Type } from "./LanguageWriters"
30
import { RuntimeType } from "./PeerGeneratorVisitor"
31
import { TypeNodeConvertor, convertTypeNode } from "./TypeNodeConvertor"
32
import { PeerLibrary } from "./PeerLibrary"
33
import { collectCallbacks } from "./printers/EventsPrinter"
34
import { EnumMember, NodeArray } from "typescript";
35
import { extractBuilderFields } from "./BuilderClass"
36
import { setEngine } from "node:crypto"
37

38
export class PrimitiveType {
39
    constructor(private name: string, public isPointer = false) { }
40
    getText(table?: DeclarationTable): string { return this.name }
41
    static ArkPrefix = "Ark_"
42
    static String = new PrimitiveType(`${PrimitiveType.ArkPrefix}String`, true)
43
    static Number = new PrimitiveType(`${PrimitiveType.ArkPrefix}Number`, true)
44
    static Int32 = new PrimitiveType(`${PrimitiveType.ArkPrefix}Int32`)
45
    static Tag = new PrimitiveType(`${PrimitiveType.ArkPrefix}Tag`)
46
    static RuntimeType = new PrimitiveType(`${PrimitiveType.ArkPrefix}RuntimeType`)
47
    static Boolean = new PrimitiveType(`${PrimitiveType.ArkPrefix}Boolean`)
48
    static Function = new PrimitiveType(`${PrimitiveType.ArkPrefix}Function`, false)
49
    static Materialized = new PrimitiveType(`${PrimitiveType.ArkPrefix}Materialized`, true)
50
    static Undefined = new PrimitiveType(`${PrimitiveType.ArkPrefix}Undefined`)
51
    static NativePointer = new PrimitiveType(`${PrimitiveType.ArkPrefix}NativePointer`)
52
    static ObjectHandle = new PrimitiveType(`${PrimitiveType.ArkPrefix}ObjectHandle`)
53
    static Length = new PrimitiveType(`${PrimitiveType.ArkPrefix}Length`, true)
54
    static Resource = new PrimitiveType(`${PrimitiveType.ArkPrefix}Resource`, true)
55
    static CustomObject = new PrimitiveType(`${PrimitiveType.ArkPrefix}CustomObject`, true)
56
    private static pointersMap = new Map<DeclarationTarget, PointerType>()
57
    static pointerTo(target: DeclarationTarget) {
58
        if (PrimitiveType.pointersMap.has(target)) return PrimitiveType.pointersMap.get(target)!
59
        let result = new PointerType(target)
60
        PrimitiveType.pointersMap.set(target, result)
61
        return result
62
    }
63
    static UndefinedTag = "ARK_TAG_UNDEFINED"
64
    static UndefinedRuntime = "ARK_RUNTIME_UNDEFINED"
65
    static OptionalPrefix = "Opt_"
66
}
67

68
export class PointerType extends PrimitiveType {
69
    constructor(public pointed: DeclarationTarget) {
70
        super("", true)
71
    }
72
    getText(table: DeclarationTable): string {
73
        return `${table.computeTargetName(this.pointed, false)}*`
74
    }
75
}
76

77
export type DeclarationTarget =
78
    ts.ClassDeclaration | ts.InterfaceDeclaration | ts.EnumDeclaration
79
    | ts.UnionTypeNode | ts.TypeLiteralNode | ts.ImportTypeNode | ts.FunctionTypeNode | ts.TupleTypeNode
80
    | ts.TemplateLiteralTypeNode | ts.TypeReferenceNode
81
    | ts.ArrayTypeNode | ts.ParenthesizedTypeNode | ts.OptionalTypeNode | ts.LiteralTypeNode
82
    | PrimitiveType
83

84
export class FieldRecord {
85
    constructor(public declaration: DeclarationTarget, public type: ts.TypeNode | undefined, public name: string, public optional: boolean = false) { }
86
}
87

88
export interface StructVisitor {
89
    visitUnionField(field: FieldRecord, selectorValue: number): void
90
    // visitOptionalField(field?: FieldRecord): void;
91
    visitInseparable(): void
92
}
93

94
class StructDescriptor {
95
    supers: DeclarationTarget[] = []
96
    deps = new Set<DeclarationTarget>()
97
    isPacked: boolean = false
98
    isArray: boolean = false
99
    private fields: FieldRecord[] = []
100
    private seenFields = new Set<string>()
101
    addField(field: FieldRecord) {
102
        if (!this.seenFields.has(field.name)) {
103
            this.seenFields.add(field.name)
104
            // TODO: kind of wrong
105
            if (field.name == `template`) field.name = `template_`
106
            this.fields.push(field)
107
        }
108
    }
109
    getFields(): readonly FieldRecord[] {
110
        return this.fields
111
    }
112
    isEmpty(): boolean {
113
        return this.fields.length == 0
114
    }
115
}
116

117
export class DeclarationTable {
118
    private typeMap = new Map<ts.TypeNode, [DeclarationTarget, string[], boolean]>()
119
    private toTargetConvertor: ToDeclarationTargetConvertor
120
    typeChecker: ts.TypeChecker | undefined = undefined
121
    public language: Language
122

123
    constructor(language: string) {
124
        switch (language) {
125
            case "arkts": this.language = Language.ARKTS; break
126
            case "java": this.language = Language.JAVA; break
127
            case "ts": default: this.language = Language.TS; break
128
        }
129
        console.log(`Emit for ${this.language.toString()}`)
130
        this.toTargetConvertor = new ToDeclarationTargetConvertor(this)
131
    }
132

133
    getTypeName(type: ts.TypeNode, optional: boolean = false): string {
134
        let declaration = this.typeMap.get(type)
135
        let prefix = optional ? PrimitiveType.OptionalPrefix : ""
136
        return declaration !== undefined ? prefix + declaration[1][0] : this.computeTargetName(this.toTarget(type), optional)
137
    }
138

139
    requestType(name: string | undefined, type: ts.TypeNode, useToGenerate: boolean) {
140
        let declaration = this.typeMap.get(type)
141
        if (declaration) {
142
            declaration[2] ||= useToGenerate
143
            if (name && !declaration[1].includes(name)) {
144
                declaration[1].push(name)
145
            }
146
            return
147
        }
148
        name = this.computeTypeName(name, type, false)
149
        let target = this.toTarget(type)
150
        if (!target) throw new Error(`Cannot find declaration: ${type.getText()}`)
151
        this.typeMap.set(type, [target, [name], useToGenerate])
152
    }
153

154
    private isDeclarationTarget(type: ts.TypeNode): boolean {
155
        if (ts.isUnionTypeNode(type)) return true
156
        if (ts.isTypeLiteralNode(type)) return true
157
        if (ts.isLiteralTypeNode(type)) return true
158
        if (ts.isTupleTypeNode(type)) return true
159
        if (ts.isArrayTypeNode(type)) return true
160
        if (ts.isOptionalTypeNode(type)) return true
161
        if (ts.isFunctionTypeNode(type)) return true
162
        // TODO: shall we map it to string type here or later?
163
        if (ts.isTemplateLiteralTypeNode(type)) return true
164
        return false
165
    }
166

167
    computeTypeName(suggestedName: string | undefined, type: ts.TypeNode, optional: boolean = false): string {
168
        return this.computeTypeNameImpl(suggestedName, type, optional)
169
    }
170

171
    toTarget(node: ts.TypeNode): DeclarationTarget {
172
        return convertTypeNode(this.toTargetConvertor, node)
173
    }
174

175
    computeTargetName(target: DeclarationTarget, optional: boolean): string {
176
        return this.computeTargetNameImpl(target, optional)
177
    }
178

179
    computeTargetNameImpl(target: DeclarationTarget, optional: boolean): string {
180
        const prefix = optional ? PrimitiveType.OptionalPrefix : ""
181
        if (target instanceof PrimitiveType) {
182
            return prefix + target.getText(this)
183
        }
184
        if (ts.isTypeLiteralNode(target)) {
185
            if (target.members.some(ts.isIndexSignatureDeclaration)) {
186
                // For indexed access we just replace the whole type to a custom accessor.
187
                return prefix + `CustomMap`
188
            }
189
            return prefix + `Literal_${target.members.map(member => {
190
                if (ts.isPropertySignature(member)) {
191
                    let target = this.toTarget(member.type!)
192
                    let field = identName(member.name)
193
                    return `${field}_${this.computeTargetName(target, member.questionToken != undefined)}`
194
                } else {
195
                    return undefined
196
                }
197
            })
198
                .filter(it => it != undefined)
199
                .join("_")}`
200
        }
201
        if (ts.isLiteralTypeNode(target)) {
202
            const literal = target.literal
203
            if (ts.isStringLiteral(literal) || ts.isNoSubstitutionTemplateLiteral(literal) || ts.isRegularExpressionLiteral(literal)) {
204
                return prefix + PrimitiveType.String.getText()
205
            }
206
            if (ts.isNumericLiteral(literal)) {
207
                return prefix + PrimitiveType.Number.getText()
208
            }
209
            if (literal.kind == ts.SyntaxKind.NullKeyword) {
210
                // TODO: Is it correct to have undefined for null?
211
                return PrimitiveType.Undefined.getText()
212
            }
213
        }
214
        if (ts.isTemplateLiteralTypeNode(target)) {
215
            // TODO: likely incorrect
216
            return prefix + PrimitiveType.String.getText()
217
        }
218
        if (ts.isTypeParameterDeclaration(target)) {
219
            // TODO: likely incorrect
220
            return prefix + PrimitiveType.CustomObject.getText()
221
        }
222
        if (ts.isEnumDeclaration(target)) {
223
            return prefix + this.enumName(target.name)
224
        }
225
        if (ts.isUnionTypeNode(target)) {
226
            return prefix + `Union_${target.types.map(it => this.computeTargetName(this.toTarget(it), false)).join("_")}`
227
        }
228
        if (ts.isInterfaceDeclaration(target) || ts.isClassDeclaration(target)) {
229
            let name = identName(target.name)
230
            if (name == "Function")
231
                return prefix + PrimitiveType.Function.getText()
232
            return prefix + name
233
        }
234
        if (ts.isFunctionTypeNode(target)) {
235
            return prefix + PrimitiveType.Function.getText()
236
        }
237
        if (ts.isTupleTypeNode(target)) {
238
            return prefix + `Tuple_${target.elements.map(it => {
239
                if (ts.isNamedTupleMember(it)) {
240
                    return this.computeTargetName(this.toTarget(it.type), it.questionToken != undefined)
241
                } else {
242
                    return this.computeTargetName(this.toTarget(it), false)
243
                }
244
            }).join("_")}`
245
        }
246
        if (ts.isArrayTypeNode(target)) {
247
            return prefix + `Array_` + this.computeTargetName(this.toTarget(target.elementType), false)
248
        }
249
        if (ts.isImportTypeNode(target)) {
250
            return prefix + this.mapImportType(target).getText()
251
        }
252
        if (ts.isOptionalTypeNode(target)) {
253
            let name = this.computeTargetName(this.toTarget(target.type), false)
254
            return `${PrimitiveType.OptionalPrefix}${name}`
255
        }
256
        if (ts.isParenthesizedTypeNode(target)) {
257
            return this.computeTargetName(this.toTarget(target.type), optional)
258
        }
259
        if (ts.isEnumMember(target)) {
260
            return this.computeTargetName((target as any).parent as DeclarationTarget, optional)
261
        }
262
        if (ts.isTypeReferenceNode(target)) {
263
            let name = identName(target.typeName)
264
            if (!target.typeArguments) throw new Error("Only type references with type arguments allowed here: " + name)
265
            if (name == "Optional")
266
                return this.computeTargetName(this.toTarget(target.typeArguments[0]), true)
267
            if (name == "Array")
268
                return prefix + `Array_` + this.computeTargetName(this.toTarget(target.typeArguments[0]), false)
269
            if (name == "Map")
270
                return prefix + `Map_` + this.computeTargetName(this.toTarget(target.typeArguments[0]), false) + '_' + this.computeTargetName(this.toTarget(target.typeArguments[1]), false)
271
            if (name == "Callback")
272
                return prefix + PrimitiveType.Function.getText()
273
            if (PeerGeneratorConfig.isKnownParametrized(name))
274
                return prefix + PrimitiveType.CustomObject.getText()
275
        }
276
        throw new Error(`Cannot compute target name: ${(target as any).getText()} ${(target as any).kind}`)
277
    }
278

279
    private mapImportType(type: ts.ImportTypeNode): DeclarationTarget {
280
        let name = identName(type.qualifier)!
281
        switch (name) {
282
            case "Resource": return PrimitiveType.Resource
283
            case "Callback": return PrimitiveType.Function
284
            default: return PrimitiveType.CustomObject
285
        }
286
    }
287

288
    private computeTypeNameImpl(suggestedName: string | undefined, type: ts.TypeNode, optional: boolean): string {
289
        const prefix = optional ? PrimitiveType.OptionalPrefix : ""
290
        if (ts.isImportTypeNode(type)) {
291
            return prefix + this.mapImportType(type).getText()
292
        }
293
        if (ts.isTypeReferenceNode(type)) {
294
            const typeName = identName(type.typeName)
295
            let declaration = this.toTarget(type)
296
            if (!(declaration instanceof PrimitiveType) && ts.isEnumDeclaration(declaration))
297
                return this.enumName(declaration.name)
298
            if (typeName === "Array") {
299
                const elementTypeName = this.computeTypeNameImpl(undefined, type.typeArguments![0], false)
300
                return `${prefix}Array_${elementTypeName}`
301
            } else if (typeName === "Map") {
302
                const keyTypeName = this.computeTypeNameImpl(undefined, type.typeArguments![0], false)
303
                const valueTypeName = this.computeTypeNameImpl(undefined, type.typeArguments![1], false)
304
                return `${prefix}Map_${keyTypeName}_${valueTypeName}`
305
            } else if (typeName === "Resource") {
306
                return `${prefix}${PrimitiveType.Resource.getText()}`
307
            }
308
            return prefix + typeName
309
        }
310
        if (ts.isUnionTypeNode(type)) {
311
            if (suggestedName) return suggestedName
312
            return prefix + `Union_${type.types.map(it => this.computeTypeNameImpl(undefined, it, optional)).join("_")}`
313
        }
314
        if (ts.isOptionalTypeNode(type)) {
315
            if (suggestedName) return suggestedName
316
            return PrimitiveType.OptionalPrefix + this.computeTypeNameImpl(undefined, type.type, false)
317
        }
318
        if (ts.isTupleTypeNode(type)) {
319
            if (suggestedName) return suggestedName
320
            return prefix + `Tuple_${type.elements.map(it => {
321
                if (ts.isNamedTupleMember(it)) {
322
                    return this.computeTypeNameImpl(undefined, it.type, optional)
323
                } else {
324
                    return this.computeTypeNameImpl(undefined, it, optional)
325
                }
326

327
            }).join("_")}`
328
        }
329
        if (ts.isParenthesizedTypeNode(type)) {
330
            return this.computeTypeNameImpl(suggestedName, type.type!, optional)
331
        }
332
        if (ts.isTypeLiteralNode(type)) {
333
            if (suggestedName) return suggestedName
334
            return prefix + `Literal_${type.members.map(member => {
335
                if (ts.isPropertySignature(member)) {
336
                    let field = identName(member.name)
337
                    return `${field}_${this.computeTypeNameImpl(undefined, member.type!, member.questionToken != undefined)}`
338
                } else {
339
                    return undefined
340
                }
341
            })
342
                .filter(it => it != undefined)
343
                .join("_")}`
344
        }
345
        if (ts.isLiteralTypeNode(type)) {
346
            const literal = type.literal
347
            if (ts.isStringLiteral(literal) || ts.isNoSubstitutionTemplateLiteral(literal) || ts.isRegularExpressionLiteral(literal)) {
348
                return PrimitiveType.String.getText()
349
            }
350
            if (ts.isNumericLiteral(literal)) {
351
                return PrimitiveType.Number.getText()
352
            }
353
            if (literal.kind == ts.SyntaxKind.NullKeyword) {
354
                return PrimitiveType.Undefined.getText()
355
            }
356
            throw new Error(`Unknown literal type: ${type.getText()}`)
357
        }
358
        if (ts.isTemplateLiteralTypeNode(type)) {
359
            return prefix + PrimitiveType.String.getText()
360
        }
361
        if (ts.isFunctionTypeNode(type)) {
362
            return prefix + PrimitiveType.Function.getText()
363
        }
364
        if (ts.isArrayTypeNode(type)) {
365
            if (suggestedName) return suggestedName
366
            return prefix + `Array_` + this.computeTypeNameImpl(undefined, type.elementType, false)
367
        }
368
        if (type.kind == ts.SyntaxKind.NumberKeyword) {
369
            return prefix + PrimitiveType.Number.getText()
370
        }
371
        if (
372
            type.kind == ts.SyntaxKind.UndefinedKeyword ||
373
            type.kind == ts.SyntaxKind.NullKeyword ||
374
            type.kind == ts.SyntaxKind.VoidKeyword
375
        ) {
376
            return PrimitiveType.Undefined.getText()
377
        }
378
        if (type.kind == ts.SyntaxKind.StringKeyword) {
379
            return prefix + PrimitiveType.String.getText()
380
        }
381
        if (type.kind == ts.SyntaxKind.BooleanKeyword) {
382
            return prefix + PrimitiveType.Boolean.getText()
383
        }
384
        if (type.kind == ts.SyntaxKind.ObjectKeyword ||
385
            type.kind == ts.SyntaxKind.UnknownKeyword) {
386
            return prefix + PrimitiveType.CustomObject.getText()
387
        }
388
        if (type.kind == ts.SyntaxKind.AnyKeyword) {
389
            return prefix + PrimitiveType.CustomObject.getText()
390
        }
391
        if (ts.isTypeParameterDeclaration(type)) {
392
            return prefix + PrimitiveType.CustomObject.getText()
393
        }
394
        if (ts.isIndexedAccessTypeNode(type)) {
395
            return prefix + PrimitiveType.CustomObject.getText()
396
        }
397
        if (ts.isEnumMember(type)) {
398
            return prefix + this.enumName(type.name)
399
        }
400
        throw new Error(`Cannot compute type name: ${type.getText()} ${type.kind}`)
401
    }
402

403
    public enumName(name: ts.PropertyName): string {
404
        // TODO: support namespaces in other declarations.
405
        return `${PrimitiveType.ArkPrefix}${identNameWithNamespace(name)}`
406
    }
407

408
    public get orderedDependencies(): DeclarationTarget[] {
409
        return this._orderedDependencies
410
    }
411
    private _orderedDependencies: DeclarationTarget[] = []
412

413
    public get orderedDependenciesToGenerate(): DeclarationTarget[] {
414
        return this._orderedDependenciesToGenerate
415
    }
416
    private _orderedDependenciesToGenerate: DeclarationTarget[] = []
417
    analyze(library: PeerLibrary) {
418
        const callbacks = collectCallbacks(library)
419
        for (const callback of callbacks) {
420
            callback.args.forEach(arg => {
421
                const useToGenerate = library.shouldGenerateComponent(callback.componentName)
422
                this.requestType(undefined, arg.type, useToGenerate)
423
            })
424
        }
425

426
        let orderer = new DependencySorter(this)
427
        for (let declaration of this.typeMap.values()) {
428
            orderer.addDep(declaration[0])
429
        }
430
        this._orderedDependencies = orderer.getToposorted()
431

432
        let toGenerateOrderer = new DependencySorter(this)
433
        for (let declaration of this.typeMap.values()) {
434
            if (declaration[2])
435
                toGenerateOrderer.addDep(declaration[0])
436
        }
437
        this._orderedDependenciesToGenerate = toGenerateOrderer.getToposorted()
438
    }
439

440
    serializerName(name: string, type: ts.TypeNode): string {
441
        return `write${name}`
442
    }
443

444
    deserializerName(name: string, type: ts.TypeNode): string {
445
        return `read${name}`
446
    }
447

448
    declTargetConvertor(param: string, target: DeclarationTarget, isOptionalParam = false): ArgConvertor {
449
        if (target instanceof PrimitiveType) {
450
            if (target == PrimitiveType.Number || target == PrimitiveType.Int32) {
451
                return new NumberConvertor(param)
452
            }
453
            if (target == PrimitiveType.Boolean) {
454
                return new BooleanConvertor(param)
455
            }
456
            throw new Error("Unsupported primitive type: " + target.getText())
457
        }
458
        throw new Error("Unsupported type: " + target.getText())
459
    }
460

461
    typeConvertor(param: string, type: ts.TypeNode, isOptionalParam = false): ArgConvertor {
462
        if (!type) throw new Error("Impossible")
463
        if (isOptionalParam) {
464
            return new OptionConvertor(param, this, type)
465
        }
466
        if (type.kind == ts.SyntaxKind.ObjectKeyword) {
467
            return new CustomTypeConvertor(param, "Object")
468
        }
469
        if (type.kind == ts.SyntaxKind.UndefinedKeyword || type.kind == ts.SyntaxKind.VoidKeyword) {
470
            return new UndefinedConvertor(param)
471
        }
472
        if (type.kind == ts.SyntaxKind.NullKeyword) {
473
            throw new Error("Unsupported null")
474
        }
475
        if (type.kind == ts.SyntaxKind.NumberKeyword) {
476
            return new NumberConvertor(param)
477
        }
478
        if (type.kind == ts.SyntaxKind.StringKeyword) {
479
            return new StringConvertor(param, type)
480
        }
481
        if (type.kind == ts.SyntaxKind.BooleanKeyword) {
482
            return new BooleanConvertor(param)
483
        }
484
        if (ts.isImportTypeNode(type)) {
485
            if (identName(type.qualifier) === "Callback") {
486
                return new FunctionConvertor(param, this, type)
487
            }
488
            return new ImportTypeConvertor(param, this, type)
489
        }
490
        if (ts.isTypeReferenceNode(type)) {
491
            const declaration = getDeclarationsByNode(this.typeChecker!, type.typeName)[0]
492
            return this.declarationConvertor(param, type, declaration)
493
        }
494
        if (ts.isEnumMember(type)) {
495
            return new EnumConvertor(param, type.parent, this.isStringEnum(type.parent.members))
496
        }
497
        if (ts.isUnionTypeNode(type)) {
498
            return new UnionConvertor(param, this, type)
499
        }
500
        if (ts.isTypeLiteralNode(type)) {
501
            return new AggregateConvertor(param, this, type)
502
        }
503
        if (ts.isArrayTypeNode(type)) {
504
            return new ArrayConvertor(param, this, type, type.elementType)
505
        }
506
        if (ts.isLiteralTypeNode(type)) {
507
            if (type.literal.kind == ts.SyntaxKind.NullKeyword) {
508
                return new NullConvertor(param)
509
            }
510
            if (type.literal.kind == ts.SyntaxKind.StringLiteral) {
511
                return new StringConvertor(param, type)
512
            }
513
            throw new Error(`Unsupported literal type: ${type.literal.kind}` + type.getText())
514
        }
515
        if (ts.isTupleTypeNode(type)) {
516
            return new TupleConvertor(param, this, type)
517
        }
518
        if (ts.isFunctionTypeNode(type)) {
519
            return new FunctionConvertor(param, this, type)
520
        }
521
        if (ts.isParenthesizedTypeNode(type)) {
522
            return this.typeConvertor(param, type.type)
523
        }
524
        if (ts.isOptionalTypeNode(type)) {
525
            return new OptionConvertor(param, this, type.type)
526
        }
527
        if (ts.isTemplateLiteralTypeNode(type)) {
528
            return new StringConvertor(param, type)
529
        }
530
        if (ts.isNamedTupleMember(type)) {
531
            return this.typeConvertor(param, type.type)
532
        }
533
        if (type.kind == ts.SyntaxKind.AnyKeyword ||
534
            type.kind == ts.SyntaxKind.UnknownKeyword ||
535
            ts.isIndexedAccessTypeNode(type)
536
        ) {
537
            return new CustomTypeConvertor(param, "Any")
538
        }
539
        if (ts.isTypeParameterDeclaration(type)) {
540
            // TODO: unlikely correct.
541
            return new CustomTypeConvertor(param, identName(type.name)!)
542
        }
543
        console.log(type)
544
        throw new Error(`Cannot convert: ${asString(type)} ${type.getText()} ${type.kind}`)
545
    }
546

547
    private _currentContext: string | undefined = undefined
548
    getCurrentContext(): string | undefined {
549
        return this._currentContext
550
    }
551
    setCurrentContext(context: string | undefined) {
552
        this._currentContext = context
553
    }
554

555
    private customConvertor(typeName: ts.EntityName | undefined, param: string, type: ts.TypeReferenceNode | ts.ImportTypeNode): ArgConvertor | undefined {
556
        let name = getNameWithoutQualifiersRight(typeName)
557
        switch (name) {
558
            case `Dimension`:
559
            case `Length`:
560
                return new LengthConvertor(name, param)
561
            case `Date`:
562
                return new CustomTypeConvertor(param, name, name)
563
            case `AttributeModifier`:
564
                return new PredefinedConvertor(param, "AttributeModifier<any>", "AttributeModifier", "CustomObject")
565
            case `AnimationRange`:
566
                return new CustomTypeConvertor(param, "AnimationRange", "AnimationRange<number>")
567
            case `ContentModifier`:
568
                return new CustomTypeConvertor(param, "ContentModifier", "ContentModifier<any>")
569
            case `Record`:
570
                return new CustomTypeConvertor(param, "Record", "Record<string, string>")
571
            case `Array`:
572
                return new ArrayConvertor(param, this, type, type.typeArguments![0])
573
            case `Map`:
574
                return new MapConvertor(param, this, type, type.typeArguments![0], type.typeArguments![1])
575
            case `Callback`:
576
                return new FunctionConvertor(param, this, type)
577
            case `Optional`:
578
                if (type.typeArguments && type.typeArguments.length == 1)
579
                    return new OptionConvertor(param, this, type.typeArguments![0])
580
        }
581
        return undefined
582
    }
583

584
    isPointerDeclaration(target: DeclarationTarget, isOptional: boolean = false): boolean {
585
        if (isOptional) return true
586
        if (target instanceof PrimitiveType) return target.isPointer
587
        if (ts.isEnumDeclaration(target)) return false
588
        if (ts.isInterfaceDeclaration(target) || ts.isClassDeclaration(target)) return true
589
        return true
590
    }
591

592
    declarationConvertor(param: string, type: ts.TypeReferenceNode, declaration: ts.NamedDeclaration | undefined): ArgConvertor {
593
        const entityName = typeEntityName(type)
594
        if (!declaration) {
595
            return this.customConvertor(entityName, param, type) ?? throwException(`Declaration not found for: ${type.getText()}`)
596
        }
597
        if (PeerGeneratorConfig.isConflictedDeclaration(declaration))
598
            return new CustomTypeConvertor(param, identName(declaration.name)!)
599
        const declarationName = identName(declaration.name)!
600
        let customConvertor = this.customConvertor(entityName, param, type)
601
        if (customConvertor) {
602
            return customConvertor
603
        }
604
        if (ts.isEnumDeclaration(declaration)) {
605
            return new EnumConvertor(param, declaration, this.isStringEnum(declaration.members))
606
        }
607
        if (ts.isEnumMember(declaration)) {
608
            return new EnumConvertor(param, declaration.parent, this.isStringEnum(declaration.parent.members))
609
        }
610
        if (ts.isTypeAliasDeclaration(declaration)) {
611
            return new TypeAliasConvertor(param, this, declaration, type.typeArguments)
612
        }
613
        if (ts.isInterfaceDeclaration(declaration)) {
614
            if (isMaterialized(declaration)) {
615
                return new MaterializedClassConvertor(declarationName, param, this, declaration)
616
            }
617
            return new InterfaceConvertor(declarationName, param, declaration, this, type)
618
        }
619
        if (ts.isClassDeclaration(declaration)) {
620
            if (isMaterialized(declaration)) {
621
                return new MaterializedClassConvertor(declarationName, param, this, declaration)
622
            }
623
            return new ClassConvertor(declarationName, param, declaration, this, type)
624
        }
625
        if (ts.isTypeParameterDeclaration(declaration)) {
626
            // TODO: incorrect, we must use actual, not formal type parameter.
627
            return new CustomTypeConvertor(param, identName(declaration.name)!)
628
        }
629
        console.log(`${declaration.getText()}`)
630
        throw new Error(`Unknown kind: ${declaration.kind}`)
631
    }
632

633
    private printStructsCHead(name: string, descriptor: StructDescriptor, structs: IndentedPrinter) {
634
        if (descriptor.isArray) {
635
            // Forward declaration of element type.
636
            let elementTypePointer = descriptor.getFields()[0].declaration
637
            if (!(elementTypePointer instanceof PointerType))
638
                throw new Error(`Unexpected ${this.computeTargetName(elementTypePointer, false)}`)
639
            let elementType = elementTypePointer.pointed
640
            if (!(elementType instanceof PrimitiveType)) {
641
                let name = this.computeTargetName(elementType, false)
642
                if (ts.isEnumDeclaration(elementType)) {
643
                    structs.print(`typedef int32_t ${this.enumName(elementType.name)};`)
644
                }
645
            }
646
        }
647
        if (descriptor.isPacked) {
648
            structs.print(`#ifdef _MSC_VER`)
649
            structs.print(`#pragma pack(push, 1)`)
650
            structs.print(`#endif`)
651
        }
652
        structs.print(`typedef struct ${name} {`)
653
        structs.pushIndent()
654
    }
655

656

657
    private printStructsCTail(name: string, needPacked: boolean, structs: IndentedPrinter) {
658
        structs.popIndent()
659
        if (needPacked) {
660
            structs.print(`#ifdef _MSC_VER`)
661
            structs.print(`}`)
662
            structs.print(`#pragma pack(pop)`)
663
            structs.print(`#else`)
664
            structs.print(`} __attribute__((packed))`)
665
            structs.print(`#endif`)
666
            structs.print(`${name};`)
667
        } else {
668
            structs.print(`} ${name};`)
669
        }
670
    }
671

672
    private printStructField(structs: IndentedPrinter, field: FieldRecord) {
673
        structs.print(`${this.cFieldKind(field.declaration)}${field.optional ? PrimitiveType.OptionalPrefix : ""}${this.computeTargetName(field.declaration, false)} ${field.name};`)
674
    }
675

676
    allOptionalTypes(): Set<string> {
677
        const seenNames = new Set<string>()
678
        seenNames.clear()
679
        for (let target of this.orderedDependencies) {
680
            if (target instanceof PointerType) continue
681
            let nameAssigned = this.computeTargetName(target, false)
682
            if (nameAssigned === PrimitiveType.Tag.getText(this)) {
683
                continue
684
            }
685
            if (!nameAssigned) {
686
                throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)
687
            }
688
            if (seenNames.has(nameAssigned)) continue
689
            let nameOptional = PrimitiveType.OptionalPrefix + nameAssigned
690
            seenNames.add(nameOptional)
691
        }
692
        return seenNames
693
    }
694

695
    allLiteralTypes(): Map<string, string> {
696
        const literals = new Map<string, string>()
697
        for (let target of this.orderedDependencies) {
698

699
            let nameAssigned = this.computeTargetName(target, false)
700
            if (nameAssigned === PrimitiveType.Tag.getText(this)) {
701
                continue
702
            }
703
            if (!nameAssigned) {
704
                throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)
705
            }
706
            if (literals.has(nameAssigned)) continue
707
            if (nameAssigned.startsWith("Literal_")) {
708
                const type = nameAssigned.split("_").at(1)!
709
                literals.set(nameAssigned, type)
710
            }
711
            
712
        }
713
        return literals
714
    }
715

716
    allUnionTypes() {
717

718
        type Selector = {
719
            id: number;
720
            name: string;
721
        }
722

723
        const unions = new Map<string, Selector[]>()
724
        for (let target of this.orderedDependencies) {
725
            let nameAssigned = this.computeTargetName(target, false)
726
            if (nameAssigned === PrimitiveType.Tag.getText(this)) {
727
                continue
728
            }
729
            if (!nameAssigned) {
730
                throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)
731
            }
732

733
            if (this.isMaybeWrapped(target, ts.isUnionTypeNode)) {
734
                const selectors: Selector[] = []
735
                this.targetStruct(target).getFields().forEach((field, index) => {
736
                    if (index === 0) return
737
                    selectors.push({ id: index, name: field.name })
738
                })
739

740
                unions.set(nameAssigned, selectors )
741
            }
742
        }
743
        return unions
744
    }
745

746
    generateStructs(structs: IndentedPrinter, typedefs: IndentedPrinter, writeToString: LanguageWriter) {
747
        const seenNames = new Set<string>()
748
        seenNames.clear()
749
        let noDeclaration = [PrimitiveType.Int32, PrimitiveType.Tag, PrimitiveType.Number, PrimitiveType.Boolean, PrimitiveType.String]
750
        for (let target of this.orderedDependencies) {
751
            let nameAssigned = this.computeTargetName(target, false)
752
            if (nameAssigned === PrimitiveType.Tag.getText(this)) {
753
                continue
754
            }
755
            if (!nameAssigned) {
756
                throw new Error(`No assigned name for ${(target as ts.TypeNode).getText()} shall be ${this.computeTargetName(target, false)}`)
757
            }
758
            if (seenNames.has(nameAssigned)) continue
759
            seenNames.add(nameAssigned)
760
            let isPointer = this.isPointerDeclaration(target)
761
            let isEnum = !(target instanceof PrimitiveType) && ts.isEnumDeclaration(target)
762
            let isAccessor = checkDeclarationTargetMaterialized(target)
763
            let noBasicDecl = isAccessor || (target instanceof PrimitiveType && noDeclaration.includes(target))
764
            let nameOptional = PrimitiveType.OptionalPrefix + nameAssigned
765
            let isUnion = this.isMaybeWrapped(target, ts.isUnionTypeNode)
766
            if (isEnum) {
767
                structs.print(`typedef ${PrimitiveType.Int32.getText()} ${nameAssigned};`)
768
                if (!seenNames.has(nameOptional)) {
769
                    seenNames.add(nameOptional)
770
                    structs.print(`typedef struct ${nameOptional} { enum ${PrimitiveType.Tag.getText()} tag; ${nameAssigned} value; } ${nameOptional};`)
771
                    this.writeOptional(nameOptional, writeToString, isPointer)
772
                    this.writeRuntimeType(target, nameOptional, true, writeToString)
773
                }
774
                continue
775
            }
776
            const structDescriptor = this.targetStruct(target)
777
            if (!noBasicDecl && !this.ignoreTarget(target)) {
778

779
                // TODO: fix it to define array type after its elements types
780
                if (nameAssigned === "Array_GestureRecognizer") {
781
                    structs.print("typedef Ark_Materialized GestureRecognizer;")
782
                }
783

784
                this.printStructsCHead(nameAssigned, structDescriptor, structs)
785
                if (isUnion) {
786
                    const selector = structDescriptor.getFields().find(value => {return value.name === "selector"})
787
                    if (selector) {
788
                        this.printStructField(structs, selector)
789
                    }
790
                    structs.print("union {")
791
                    structs.pushIndent()
792
                    structDescriptor.getFields().filter(value => value.name !== "selector")
793
                        .forEach(it => this.printStructField(structs, it))
794
                    structs.popIndent()
795
                    structs.print("};")
796
                } else {
797
                    structDescriptor.getFields().forEach(it => this.printStructField(structs, it))
798
                }
799
                this.printStructsCTail(nameAssigned, structDescriptor.isPacked, structs)
800
            }
801
            if (isAccessor) {
802
                structs.print(`typedef Ark_Materialized ${nameAssigned};`)
803
            }
804
            let skipWriteToString = (target instanceof PrimitiveType) || ts.isEnumDeclaration(target) || ts.isFunctionTypeNode(target)
805
            if (!noBasicDecl && !skipWriteToString) {
806
                this.generateWriteToString(nameAssigned, target, writeToString, isPointer)
807
            }
808
            this.writeRuntimeType(target, nameAssigned, false, writeToString)
809
            if (seenNames.has(nameOptional)) continue
810
            seenNames.add(nameOptional)
811
            if (!(target instanceof PointerType) && nameAssigned != "Optional" && nameAssigned != "RelativeIndexable") {
812
                this.printStructsCHead(nameOptional, structDescriptor, structs)
813
                structs.print(`enum ${PrimitiveType.Tag.getText()} tag;`)
814
                structs.print(`${nameAssigned} value;`)
815
                this.printStructsCTail(nameOptional, structDescriptor.isPacked, structs)
816
                this.writeOptional(nameOptional, writeToString, isPointer)
817
                this.writeRuntimeType(target, nameOptional, true, writeToString)
818
            }
819
        }
820
        for (let declarationTarget of this.typeMap.values()) {
821
            let target = declarationTarget[0]
822
            let aliasNames = declarationTarget[1]
823
            let declarationName = this.computeTargetName(target, false)
824
            aliasNames.forEach(aliasName => this.addNameAlias(target, declarationName, aliasName, seenNames, typedefs))
825
        }
826
        // TODO: hack, remove me!
827
        typedefs.print(`typedef ${PrimitiveType.OptionalPrefix}Ark_Length ${PrimitiveType.OptionalPrefix}Dimension;`)
828
        typedefs.print(`typedef ${PrimitiveType.OptionalPrefix}Ark_Length ${PrimitiveType.OptionalPrefix}Length;`)
829
    }
830

831
    private writeRuntimeType(target: DeclarationTarget, targetTypeName: string, isOptional: boolean, writer: LanguageWriter) {
832
        const resultType = new Type("Ark_RuntimeType")
833
        const op = this.writeRuntimeTypeOp(target, targetTypeName, resultType, isOptional, writer)
834
        if (op) {
835
            writer.print("template <>")
836
            writer.writeMethodImplementation(
837
                new Method("runtimeType",
838
                    new NamedMethodSignature(resultType, [new Type(`const ${targetTypeName}&`)], ["value"]),
839
                    [MethodModifier.INLINE]),
840
                op)
841
        }
842
    }
843

844
    private writeRuntimeTypeOp(
845
        target: DeclarationTarget, targetTypeName: string, resultType: Type, isOptional: boolean, writer: LanguageWriter
846
    ) : ((writer: LanguageWriter) => void) | undefined
847
    {
848
        let result: LanguageExpression
849
        if (isOptional) {
850
            result = writer.makeTernary(writer.makeDefinedCheck("value.tag"),
851
                writer.makeRuntimeType(RuntimeType.OBJECT), writer.makeRuntimeType(RuntimeType.UNDEFINED))
852
        } else if (target instanceof PointerType) {
853
            return
854
        } else if (target instanceof PrimitiveType) {
855
            switch (target) {
856
                case PrimitiveType.Boolean:
857
                    result = writer.makeRuntimeType(RuntimeType.BOOLEAN)
858
                    break
859
                case PrimitiveType.CustomObject:
860
                case PrimitiveType.Materialized:
861
                case PrimitiveType.NativePointer:
862
                case PrimitiveType.Resource:
863
                case PrimitiveType.Tag:
864
                    return undefined
865
                case PrimitiveType.Function:
866
                    result = writer.makeRuntimeType(RuntimeType.FUNCTION)
867
                    break
868
                case PrimitiveType.Int32:
869
                case PrimitiveType.Number:
870
                    result = writer.makeRuntimeType(RuntimeType.NUMBER)
871
                    break
872
                case PrimitiveType.Length:
873
                    result = writer.makeCast(writer.makeString("value.type"), resultType)
874
                    break
875
                case PrimitiveType.String:
876
                    result = writer.makeRuntimeType(RuntimeType.STRING)
877
                    break
878
                case PrimitiveType.Undefined:
879
                    result = writer.makeRuntimeType(RuntimeType.UNDEFINED)
880
                    break
881
                default:
882
                    throw new Error(`Unexpected PrimitiveType ${target.getText()}`)
883
            }
884
        } else if (ts.isEnumDeclaration(target)) {
885
            result = writer.makeRuntimeType(RuntimeType.NUMBER)
886
        } else if (checkDeclarationTargetMaterialized(target)) {
887
            return undefined
888
        } else if (ts.isOptionalTypeNode(target)) {
889
            result = writer.makeTernary(writer.makeDefinedCheck("value.tag"),
890
                writer.makeRuntimeType(RuntimeType.OBJECT), writer.makeRuntimeType(RuntimeType.UNDEFINED))
891
        } else if (ts.isUnionTypeNode(target)) {
892
            return writer => {
893
                writer.print("switch (value.selector) {")
894
                writer.pushIndent()
895
                for (let i = 0; i < target.types.length; i++) {
896
                    writer.print(`case ${i}: return runtimeType(value.value${i});`)
897
                }
898
                writer.print(`default: throw "Bad selector in ${targetTypeName}: " + std::to_string(value.selector);`)
899
                writer.popIndent()
900
                writer.print("}")
901
            }
902
        } else {
903
            result = writer.makeRuntimeType(RuntimeType.OBJECT)
904
        }
905
        return writer => writer.writeStatement(writer.makeReturn(result))
906
    }
907

908
    private addNameAlias(target: DeclarationTarget, declarationName: string, aliasName: string,
909
        seenNames: Set<string>, typedefs: IndentedPrinter): void {
910
        if (seenNames.has(aliasName)) return
911
        if (this.ignoreTarget(target) && target != PrimitiveType.CustomObject) return
912
        seenNames.add(aliasName)
913
        typedefs.print(`typedef ${declarationName} ${aliasName};`)
914
        // TODO: hacky
915
        let optAliasName = `${PrimitiveType.OptionalPrefix}${aliasName}`
916
        if (!declarationName.startsWith(PrimitiveType.OptionalPrefix) && !seenNames.has(optAliasName)) {
917
            seenNames.add(optAliasName)
918
            typedefs.print(`typedef ${PrimitiveType.OptionalPrefix}${declarationName} ${optAliasName};`)
919
        }
920
    }
921

922
    cFieldKind(declaration: DeclarationTarget): string {
923
        if (declaration instanceof PointerType) return this.cFieldKind(declaration.pointed)
924
        if (declaration instanceof PrimitiveType) return ""
925
        if (ts.isEnumDeclaration(declaration)) return ""
926
        if (ts.isImportTypeNode(declaration)) return ""
927
        if (checkDeclarationTargetMaterialized(declaration)) return ""
928
        return `struct `
929
    }
930

931
    writeOptional(nameOptional: string, printer: LanguageWriter, isPointer: boolean) {
932
        printer.print(`template <>`)
933
        printer.print(`inline void WriteToString(string* result, const ${nameOptional}* value) {`)
934
        printer.print(`result->append("{.tag=");`)
935
        printer.print(`result->append(tagNameExact((${PrimitiveType.Tag.getText()})(value->tag)));`)
936
        printer.print(`result->append(", .value=");`)
937
        printer.pushIndent()
938
        printer.print(`if (value->tag != ${PrimitiveType.UndefinedTag}) {`)
939
        printer.pushIndent()
940
        printer.print(`WriteToString(result, ${isPointer ? "&" : ""}value->value);`)
941
        printer.popIndent()
942
        printer.print(`} else {`)
943
        printer.pushIndent()
944
        printer.print(`${PrimitiveType.Undefined.getText()} undefined = { 0 };`)
945
        printer.print(`WriteToString(result, undefined);`)
946
        printer.popIndent()
947
        printer.print(`}`)
948
        printer.popIndent()
949
        printer.print(`result->append("}");`)
950
        printer.print(`}`)
951
    }
952

953
    writeOptionalConvertor(nameOptional: string, printer: LanguageWriter, isPointer: boolean) {
954
        printer.print(`template <>`)
955
        printer.print(`inline void convertor(const ${nameOptional}* value) {`)
956
        printer.pushIndent()
957
        printer.print(`if (value->tag != ${PrimitiveType.UndefinedTag}) {`)
958
        printer.pushIndent()
959
        printer.print(`convertor(${isPointer ? "&" : ""}value->value);`)
960
        printer.popIndent()
961
        printer.print(`} else {`)
962
        printer.pushIndent()
963
        printer.print(`${PrimitiveType.Undefined.getText()} undefined = { 0 };`)
964
        printer.print(`convertor(undefined);`)
965
        printer.popIndent()
966
        printer.print(`}`)
967
        printer.popIndent()
968
        printer.print(`}`)
969
    }
970

971
    visitDeclaration(
972
        target: DeclarationTarget,
973
        visitor: StructVisitor,
974
    ): void {
975
        if (this.isMaybeWrapped(target, ts.isUnionTypeNode)) {
976
            this.targetStruct(target).getFields().forEach((field, index) => {
977
                if (index === 0) return
978
                visitor.visitUnionField(field, index - 1)
979
            })
980
        } else {
981
            visitor.visitInseparable()
982
        }
983
    }
984

985
    private isMaybeWrapped(target: DeclarationTarget, predicate: (type: ts.Node) => boolean): boolean {
986
        if (target instanceof PrimitiveType) return false
987
        return predicate(target) ||
988
            ts.isParenthesizedTypeNode(target) &&
989
            this.isDeclarationTarget(target.type) &&
990
            predicate(target.type)
991
    }
992

993
    private generateArrayWriteToString(name: string, target: DeclarationTarget, printer: LanguageWriter) {
994
        if (target instanceof PrimitiveType) throw new Error("Impossible")
995
        let elementType = ts.isArrayTypeNode(target)
996
            ? target.elementType
997
            : ts.isTypeReferenceNode(target) && target.typeArguments
998
                ? target.typeArguments[0]
999
                : undefined
1000

1001
        if (!elementType) throw new Error("Impossible")
1002
        let convertor = this.typeConvertor("param", elementType)
1003
        let isPointerField = convertor.isPointerType()
1004
        let elementNativeType = convertor.nativeType(false)
1005
        let constCast = isPointerField ? `(const ${elementNativeType}*)` : ``
1006

1007
        printer.print(`inline void WriteToString(string* result, const ${name}* value, const std::string& ptrName = std::string()) {`)
1008
        printer.pushIndent()
1009
        printer.print(`result->append("{");`)
1010
        printer.print(`if (ptrName.empty()) {`)
1011
        printer.pushIndent()
1012
        printer.print(`int32_t count = value->length;`)
1013
        printer.print(`if (count > 0) result->append("{");`)
1014
        printer.print(`for (int i = 0; i < count; i++) {`)
1015
        printer.pushIndent()
1016
        printer.print(`if (i > 0) result->append(", ");`)
1017
        printer.print(`WriteToString(result, ${constCast}${isPointerField ? "&" : ""}value->array[i]);`)
1018
        printer.popIndent()
1019
        printer.print(`}`)
1020
        printer.print(`if (count == 0) result->append("{}");`)
1021
        printer.print(`if (count > 0) result->append("}");`)
1022
        printer.popIndent()
1023
        printer.print(`} else {`)
1024
        printer.pushIndent()
1025
        printer.print(`result->append(ptrName + ".data()");`)
1026
        printer.popIndent()
1027
        printer.print(`}`)
1028
        printer.print(`result->append(", .length=");`)
1029
        printer.print(`result->append(std::to_string(value->length));`)
1030
        printer.print(`result->append("}");`)
1031
        printer.popIndent()
1032
        printer.print(`}`)
1033
    }
1034

1035
    private generateStdArrayDefinition(name: string, target: DeclarationTarget, printer: LanguageWriter) {
1036
        if (target instanceof PrimitiveType) throw new Error("Impossible")
1037
        let elementType = ts.isArrayTypeNode(target)
1038
            ? target.elementType
1039
            : ts.isTypeReferenceNode(target) && target.typeArguments
1040
                ? target.typeArguments[0]
1041
                : undefined
1042

1043
        if (!elementType) throw new Error("Impossible")
1044
        let convertor = this.typeConvertor("param", elementType)
1045
        let isPointerField = convertor.isPointerType()
1046
        let elementNativeType = convertor.nativeType(false)
1047
        let constCast = isPointerField ? `(const ${elementNativeType}*)` : ``
1048

1049
        // Provide prototype of element printer.
1050
        printer.print(`template <>`)
1051
        printer.print(`inline void WriteToString(string* result, const ${elementNativeType}${isPointerField ? "*" : ""} value);`)
1052

1053
        // Printer.
1054
        printer.print(`inline void generateStdArrayDefinition(string* result, const ${name}* value) {`)
1055
        printer.pushIndent()
1056
        printer.print(`int32_t count = value->length;`)
1057
        printer.print(`result->append("std::array<${elementNativeType}, " + std::to_string(count) + ">{{");`)
1058
        printer.print(`for (int i = 0; i < count; i++) {`);
1059
        printer.pushIndent()
1060
        printer.print(`std::string tmp;`)
1061
        printer.print(`WriteToString(result, ${constCast}${isPointerField ? "&" : ""}value->array[i]);`)
1062
        printer.print(`result->append(tmp);`);
1063
        printer.print(`result->append(", ");`)
1064
        printer.popIndent()
1065
        printer.print(`}`)
1066
        printer.print(`result->append("}}");`)
1067
        printer.popIndent()
1068
        printer.print(`}`)
1069
    }
1070

1071
    private generateMapWriteToString(name: string, target: DeclarationTarget, printer: LanguageWriter) {
1072
        if (target instanceof PrimitiveType)
1073
            throw new Error("Impossible")
1074
        const [keyType, valueType] = ts.isTypeReferenceNode(target) && target.typeArguments
1075
            ? target.typeArguments
1076
            : [undefined, undefined]
1077
        if (!keyType || !valueType)
1078
            throw new Error("Impossible")
1079
        const keyConvertor = this.typeConvertor("_", keyType)
1080
        const valueConvertor = this.typeConvertor("_", valueType)
1081
        let isPointerKeyField = keyConvertor.isPointerType()
1082
        let isPointerValueField = valueConvertor.isPointerType()
1083
        let keyNativeType = keyConvertor.nativeType(false)
1084
        let valueNativeType = valueConvertor.nativeType(false)
1085
        let keyConstCast = isPointerKeyField ? `(const ${keyNativeType}*)` : ``
1086
        let valueConstCast = isPointerValueField ? `(const ${valueNativeType}*)` : ``
1087

1088
        // Provide prototype of keys printer.
1089
        printer.print(`template <>`)
1090
        printer.print(`inline void WriteToString(string* result, const ${keyNativeType}${isPointerKeyField ? "*" : ""} value);`)
1091
        // Provide prototype of values printer.
1092
        printer.print(`template <>`)
1093
        printer.print(`inline void WriteToString(string* result, const ${valueNativeType}${isPointerValueField ? "*" : ""} value);`)
1094

1095
        // Printer.
1096
        printer.print(`template <>`)
1097
        printer.print(`inline void WriteToString(string* result, const ${name}* value) {`)
1098
        printer.pushIndent()
1099
        printer.print(`result->append("{");`)
1100
        printer.print(`int32_t count = value->size;`)
1101
        printer.print(`for (int i = 0; i < count; i++) {`)
1102
        printer.pushIndent()
1103
        printer.print(`if (i > 0) result->append(", ");`)
1104
        printer.print(`WriteToString(result, ${keyConstCast}${isPointerKeyField ? "&" : ""}value->keys[i]);`)
1105
        printer.print(`result->append(": ");`)
1106
        printer.print(`WriteToString(result, ${valueConstCast}${isPointerValueField ? "&" : ""}value->values[i]);`)
1107
        printer.popIndent()
1108
        printer.print(`}`)
1109
        printer.print(`result->append("}");`)
1110
        printer.popIndent()
1111
        printer.print(`}`)
1112
    }
1113

1114
    private generateWriteToString(name: string, target: DeclarationTarget, printer: LanguageWriter, isPointer: boolean) {
1115
        if (target instanceof PrimitiveType) throw new Error("Impossible")
1116

1117
        this.setCurrentContext(`writeToString(${name})`)
1118
        let isUnion = this.isMaybeWrapped(target, ts.isUnionTypeNode)
1119
        let isArray = this.isMaybeWrapped(target, ts.isArrayTypeNode)
1120
        let isMap = ts.isTypeReferenceNode(target) && identName(target.typeName) === "Map"
1121
        let isOptional = this.isMaybeWrapped(target, ts.isOptionalTypeNode)
1122
        let isTuple = this.isMaybeWrapped(target, ts.isTupleTypeNode)
1123
        let access = isPointer ? "->" : "."
1124

1125
        // treat Array<T> as array
1126
        if (!isArray && ts.isTypeReferenceNode(target)) {
1127
            isArray = identName(target.typeName) === "Array"
1128
        }
1129
        if (isArray) {
1130
            this.generateStdArrayDefinition(name, target, printer)
1131
            this.generateArrayWriteToString(name, target, printer)
1132
        } else if (isMap) {
1133
            this.generateMapWriteToString(name, target, printer)
1134
        } else {
1135
            printer.print(`template <>`)
1136
            printer.print(`inline void WriteToString(string* result, const ${name}${isPointer ? "*" : ""} value) {`)
1137
            printer.pushIndent()
1138

1139
            if (isUnion) {
1140
                printer.print(`result->append("{");`);
1141
                printer.print(`result->append(".selector=");`)
1142
                printer.print(`result->append(std::to_string(value->selector));`);
1143
                printer.print(`result->append(", ");`);
1144
                this.targetStruct(target).getFields().forEach((field, index) => {
1145
                    let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)
1146
                    if (index != 0) printer.print(`// ${this.computeTargetName(field.declaration, false)}`)
1147
                    printer.print(`if (value${access}selector == ${index - 1}) {`)
1148
                    printer.pushIndent()
1149
                    printer.print(`result->append(".${field.name}=");`);
1150
                    printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)
1151
                    printer.popIndent()
1152
                    printer.print(`}`)
1153
                })
1154
                if (false) {
1155
                    printer.print(`result->append(" /* ${name} [variant ");`)
1156
                    printer.print(`result->append(std::to_string(value${access}selector));`)
1157
                    printer.print(`result->append("]*/");`)
1158
                }
1159
                printer.print(`result->append("}");`);
1160
            } else if (isTuple) {
1161
                printer.print(`result->append("{");`)
1162
                const fields = this.targetStruct(target).getFields()
1163
                fields.forEach((field, index) => {
1164
                    printer.print(`// ${this.computeTargetName(field.declaration, false)}`)
1165
                    let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)
1166
                    if (index > 0) printer.print(`result->append(", ");`)
1167
                    printer.print(`result->append(".${field.name}=");`)
1168
                    printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)
1169
                })
1170
                printer.print(`result->append("}");`)
1171
            } else if (isOptional) {
1172
                printer.print(`result->append("{");`)
1173
                const fields = this.targetStruct(target).getFields()
1174
                fields.forEach((field, index) => {
1175
                    printer.print(`// ${this.computeTargetName(field.declaration, false)}`)
1176
                    if (index > 0) printer.print(`result->append(", ");`)
1177
                    printer.print(`result->append("${field.name}: ");`)
1178
                    let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)
1179
                    printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)
1180
                    if (index == 0) {
1181
                        printer.print(`if (value${access}${field.name} != ${PrimitiveType.UndefinedTag}) {`)
1182
                        printer.pushIndent()
1183
                    }
1184
                    if (index == fields.length - 1) {
1185
                        printer.popIndent()
1186
                        printer.print("}")
1187
                    }
1188
                })
1189
                printer.print(`result->append("}");`)
1190
            } else {
1191
                printer.print(`result->append("{");`)
1192
                this.targetStruct(target).getFields().forEach((field, index) => {
1193
                    printer.print(`// ${this.computeTargetName(field.declaration, false)}`)
1194
                    if (index > 0) printer.print(`result->append(", ");`)
1195
                    printer.print(`result->append(".${field.name}=");`)
1196
                    let isPointerField = this.isPointerDeclaration(field.declaration, field.optional)
1197
                    printer.print(`WriteToString(result, ${isPointerField ? "&" : ""}value${access}${field.name});`)
1198
                })
1199
                printer.print(`result->append("}");`)
1200
            }
1201
            printer.popIndent()
1202
            printer.print(`}`)
1203
        }
1204
        this.setCurrentContext(undefined)
1205
    }
1206

1207
    private fieldsForClass(clazz: ts.ClassDeclaration | ts.InterfaceDeclaration, result: StructDescriptor) {
1208
        clazz.heritageClauses?.forEach(it => {
1209
            heritageDeclarations(this.typeChecker!, it).forEach(it => {
1210
                if (ts.isClassDeclaration(it) || ts.isInterfaceDeclaration(it)) {
1211
                    result.supers.push(it)
1212
                    result.isPacked = false
1213
                    this.fieldsForClass(it, result)
1214
                }
1215
            })
1216
        })
1217
        if (ts.isClassDeclaration(clazz)) {
1218
            clazz
1219
                .members
1220
                .filter(ts.isPropertyDeclaration)
1221
                .filter(it => !isStatic(it.modifiers))
1222
                .forEach(it => {
1223
                    result.addField(new FieldRecord(this.toTarget(it.type!), it.type!, identName(it.name)!, it.questionToken != undefined))
1224
                })
1225
                extractBuilderFields(clazz, this).forEach(field => {
1226
                    result.addField(field)
1227
                })
1228
        } else {
1229
            clazz
1230
                .members
1231
                .filter(ts.isPropertySignature)
1232
                .filter(it => !isStatic(it.modifiers))
1233
                .forEach(it => {
1234
                    result.addField(new FieldRecord(this.toTarget(it.type!), it.type!, identName(it.name)!, it.questionToken != undefined))
1235
                })
1236
        }
1237
    }
1238

1239
    targetStruct(target: DeclarationTarget): StructDescriptor {
1240
        let result = new StructDescriptor()
1241
        if (target instanceof PointerType) {
1242
            // Break the dependency cycle.
1243
            // result.deps.add(target.pointed)
1244
            return result
1245
        }
1246

1247
        if (target instanceof PrimitiveType) {
1248
            return result
1249
        }
1250
        else if (ts.isArrayTypeNode(target)) {
1251
            result.isArray = true
1252
            let element = this.toTarget(target.elementType)
1253
            result.addField(new FieldRecord(PrimitiveType.pointerTo(element), target, "array"))
1254
            result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "length"))
1255
        }
1256
        else if (ts.isInterfaceDeclaration(target)) {
1257
            this.fieldsForClass(target, result)
1258
        }
1259
        else if (ts.isClassDeclaration(target)) {
1260
            this.fieldsForClass(target, result)
1261
        }
1262
        else if (ts.isUnionTypeNode(target)) {
1263
            result.addField(new FieldRecord(PrimitiveType.Int32, undefined, `selector`, false))
1264
            target
1265
                .types
1266
                .forEach((it, index) => {
1267
                    result.addField(new FieldRecord(this.toTarget(it), it, `value${index}`, false))
1268
                })
1269
        }
1270
        else if (ts.isTypeLiteralNode(target)) {
1271
            if (target.members.some(ts.isIndexSignatureDeclaration)) {
1272
                // For indexed access we just replace the whole type to a custom accessor.
1273
                result.addField(new FieldRecord(PrimitiveType.CustomObject, undefined, "keyAccessor", false))
1274
            } else {
1275
                target
1276
                    .members
1277
                    .forEach(it => {
1278
                        if (ts.isPropertySignature(it))
1279
                            result.addField(new FieldRecord(this.toTarget(it.type!), it.type, identName(it.name)!, it.questionToken != undefined))
1280
                    })
1281
            }
1282
        }
1283
        else if (ts.isTupleTypeNode(target)) {
1284
            target
1285
                .elements
1286
                .forEach((it, index) => {
1287
                    if (ts.isNamedTupleMember(it)) {
1288
                        result.addField(new FieldRecord(this.toTarget(it.type!), it.type!, identName(it.name)!, it.questionToken != undefined))
1289
                    } else {
1290
                        result.addField(new FieldRecord(this.toTarget(it), it, `value${index}`, false))
1291
                    }
1292
                })
1293
        }
1294
        else if (ts.isOptionalTypeNode(target)) {
1295
            result.addField(new FieldRecord(PrimitiveType.Tag, undefined, "tag"))
1296
            result.addField(new FieldRecord(this.toTarget(target.type), target.type, "value"))
1297
        }
1298
        else if (ts.isParenthesizedTypeNode(target)) {
1299
            // TODO: is it correct?
1300
            return this.targetStruct(this.toTarget(target.type))
1301
        }
1302
        else if (ts.isEnumDeclaration(target) || ts.isEnumMember(target)) {
1303
            result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "value"))
1304
        }
1305
        else if (ts.isFunctionTypeNode(target)) {
1306
        }
1307
        else if (ts.isImportTypeNode(target)) {
1308
        }
1309
        else if (ts.isTemplateLiteralTypeNode(target)) {
1310
        }
1311
        else if (ts.isLiteralTypeNode(target)) {
1312
        }
1313
        else if (ts.isTypeParameterDeclaration(target)) {
1314
            // TODO: is it really correct
1315
        }
1316
        else if (ts.isTypeReferenceNode(target)) {
1317
            if (!target.typeArguments) throw new Error("Only type references with type arguments allowed")
1318
            let name = identName(target.typeName)
1319
            if (name == "Optional") {
1320
                let type = target.typeArguments[0]
1321
                result.addField(new FieldRecord(PrimitiveType.Tag, undefined, "tag"))
1322
                result.addField(new FieldRecord(this.toTarget(type), type, "value"))
1323
            } else if (name == "Array") {
1324
                let type = target.typeArguments[0]
1325
                result.isArray = true
1326
                result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(type)), undefined, "array"))
1327
                result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "length"))
1328
            } else if (name == "Map") {
1329
                let keyType = target.typeArguments[0]
1330
                let valueType = target.typeArguments[1]
1331
                result.addField(new FieldRecord(PrimitiveType.Int32, undefined, "size"))
1332
                result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(keyType)), undefined, "keys"))
1333
                result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(valueType)), undefined, "values"))
1334
            } else if (name == "ContentModifier") {
1335
                let type = target.typeArguments[0]
1336
                result.addField(new FieldRecord(PrimitiveType.pointerTo(this.toTarget(type)), undefined, "config"))
1337
            } else if (name == "Callback") {
1338
                result.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 {
1345
                throw new Error(`Parametrized type unknown: ${name} ${(target as any).getText()}`)
1346
            }
1347
        }
1348
        else {
1349
            throw new Error(`Unsupported field getter: ${asString(target)} ${(target as any).getText()}`)
1350
        }
1351
        return result
1352
    }
1353

1354
    private ignoreTarget(target: DeclarationTarget): target is PrimitiveType | ts.EnumDeclaration {
1355
        const name = this.computeTargetName(target, false)
1356
        if (PeerGeneratorConfig.ignoreSerialization.includes(name)) return true
1357
        if (target instanceof PrimitiveType) return true
1358
        if (ts.isEnumDeclaration(target)) return true
1359
        if (ts.isFunctionTypeNode(target)) return true
1360
        if (ts.isImportTypeNode(target)) return true
1361
        if (ts.isTemplateLiteralTypeNode(target)) return true
1362
        return false
1363
    }
1364

1365
    private isStringEnum(members: NodeArray<EnumMember>): boolean {
1366
        return members.find((value) => {
1367
            return value.initializer && ts.isStringLiteral(value.initializer)
1368
        }) != undefined
1369
    }
1370
}
1371

1372
class ToDeclarationTargetConvertor implements TypeNodeConvertor<DeclarationTarget> {
1373
    constructor(
1374
        private readonly table: DeclarationTable,
1375
    ) {}
1376

1377
    convertUnion(node: ts.UnionTypeNode): DeclarationTarget {
1378
        return node
1379
    }
1380
    convertTypeLiteral(node: ts.TypeLiteralNode): DeclarationTarget {
1381
        return node
1382
    }
1383
    convertLiteralType(node: ts.LiteralTypeNode): DeclarationTarget {
1384
        return node
1385
    }
1386
    convertTuple(node: ts.TupleTypeNode): DeclarationTarget {
1387
        return node
1388
    }
1389
    convertArray(node: ts.ArrayTypeNode): DeclarationTarget {
1390
        return node
1391
    }
1392
    convertOptional(node: ts.OptionalTypeNode): DeclarationTarget {
1393
        return node
1394
    }
1395
    convertFunction(node: ts.FunctionTypeNode): DeclarationTarget {
1396
        return node
1397
    }
1398
    convertTemplateLiteral(node: ts.TemplateLiteralTypeNode): DeclarationTarget {
1399
        return node
1400
    }
1401
    convertImport(node: ts.ImportTypeNode): DeclarationTarget {
1402
        let name = identName(node.qualifier)!
1403
        switch (name) {
1404
            case "Resource": return PrimitiveType.Resource
1405
            case "Callback": return PrimitiveType.Function
1406
            default: return PrimitiveType.CustomObject
1407
        }
1408
    }
1409
    convertTypeReference(node: ts.TypeReferenceNode): DeclarationTarget {
1410
        let name = identName(node)
1411
        switch (name) {
1412
            case `Dimension`: case `Length`: return PrimitiveType.Length
1413
            case `AnimationRange`: return PrimitiveType.CustomObject
1414
            case `ContentModifier`: return PrimitiveType.CustomObject
1415
            case `Date`: return PrimitiveType.CustomObject
1416
            // stub required to compile arkoala patched sdk
1417
            case `Function`: return PrimitiveType.Function
1418
        }
1419
        // Types with type arguments are declarations!
1420
        if (node.typeArguments) {
1421
            return node
1422
        }
1423

1424
        let declarations = getDeclarationsByNode(this.table.typeChecker!, node.typeName)
1425
        if (declarations.length == 0) {
1426
            throw new Error(`No declaration for ${node.getText()} ${asString(node)}`)
1427
        }
1428
        let declaration = declarations[0]
1429
        if (PeerGeneratorConfig.isConflictedDeclaration(declaration))
1430
            return PrimitiveType.CustomObject
1431
        if (ts.isTypeAliasDeclaration(declaration)) {
1432
            const node = declaration.type
1433
            this.table.requestType(identName(declaration.name), node, false)
1434
            return convertTypeNode(this, node)
1435
        }
1436
        if (ts.isEnumMember(declaration)) {
1437
            return declaration.parent
1438
        }
1439
        if (ts.isTypeParameterDeclaration(declaration)) {
1440
            return PrimitiveType.CustomObject
1441
        }
1442
        if (ts.isClassDeclaration(declaration) ||
1443
            ts.isInterfaceDeclaration(declaration) ||
1444
            ts.isEnumDeclaration(declaration))
1445
            return declaration
1446
        throw new Error(`Unknown declaration type ${ts.SyntaxKind[declaration.kind]}`)
1447
    }
1448
    convertParenthesized(node: ts.ParenthesizedTypeNode): DeclarationTarget {
1449
        return convertTypeNode(this, node.type)
1450
    }
1451
    convertIndexedAccess(node: ts.IndexedAccessTypeNode): DeclarationTarget {
1452
        return PrimitiveType.CustomObject
1453
    }
1454
    convertStringKeyword(node: ts.TypeNode): DeclarationTarget {
1455
        return PrimitiveType.String
1456
    }
1457
    convertNumberKeyword(node: ts.TypeNode): DeclarationTarget {
1458
        return PrimitiveType.Number
1459
    }
1460
    convertBooleanKeyword(node: ts.TypeNode): DeclarationTarget {
1461
        return PrimitiveType.Boolean
1462
    }
1463
    convertUndefinedKeyword(node: ts.TypeNode): DeclarationTarget {
1464
        return PrimitiveType.Undefined
1465
    }
1466
    convertVoidKeyword(node: ts.TypeNode): DeclarationTarget {
1467
        // TODO: shall it be distinct type.
1468
        return PrimitiveType.Undefined
1469
    }
1470
    convertObjectKeyword(node: ts.TypeNode): DeclarationTarget {
1471
        return PrimitiveType.CustomObject
1472
    }
1473
    convertAnyKeyword(node: ts.TypeNode): DeclarationTarget {
1474
        return PrimitiveType.CustomObject
1475
    }
1476
    convertUnknownKeyword(node: ts.TypeNode): DeclarationTarget {
1477
        return PrimitiveType.CustomObject
1478
    }
1479
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.