idlize

Форк
0
/
Convertors.ts 
1157 строк · 53.3 Кб
1
/*
2
 * Copyright (c) 2024 Huawei Device Co., Ltd.
3
 * Licensed under the Apache License, Version 2.0 (the "License");
4
 * you may not use this file except in compliance with the License.
5
 * You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 */
15
import { Language, identName, importTypeName } from "../util"
16
import { DeclarationTable, FieldRecord, PrimitiveType } from "./DeclarationTable"
17
import { RuntimeType } from "./PeerGeneratorVisitor"
18
import * as ts from "typescript"
19
import { BlockStatement, BranchStatement, LanguageExpression, LanguageStatement, LanguageWriter, Type } from "./LanguageWriters"
20
import { mapType } from "./TypeNodeNameConvertor"
21

22
function castToInt8(value: string, lang: Language): string {
23
    switch (lang) {
24
        case Language.ARKTS: return `${value} as int32` // FIXME: is there int8 in ARKTS?
25
        default: return value
26
    }
27
}
28

29
function castToInt32(value: string, lang: Language): string {
30
    switch (lang) {
31
        case Language.ARKTS: return `${value} as int32`
32
        default: return value
33
    }
34
}
35

36
export interface ArgConvertor {
37
    param: string
38
    tsTypeName: string
39
    isScoped: boolean
40
    useArray: boolean
41
    runtimeTypes: RuntimeType[]
42
    scopeStart?(param: string, language: Language): string
43
    scopeEnd?(param: string, language: Language): string
44
    convertorArg(param: string, writer: LanguageWriter): string
45
    convertorSerialize(param: string, value: string, writer: LanguageWriter): void
46
    convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement
47
    interopType(language: Language): string
48
    nativeType(impl: boolean): string
49
    targetType(writer: LanguageWriter): Type
50
    isPointerType(): boolean
51
    unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression|undefined
52
    getMembers(): string[]
53
}
54

55
export abstract class BaseArgConvertor implements ArgConvertor {
56
    constructor(
57
        public tsTypeName: string,
58
        public runtimeTypes: RuntimeType[],
59
        public isScoped: boolean,
60
        public useArray: boolean,
61
        public param: string
62
    ) { }
63

64
    nativeType(impl: boolean): string {
65
        throw new Error("Define")
66
    }
67
    isPointerType(): boolean {
68
        throw new Error("Define")
69
    }
70
    interopType(language: Language): string {
71
        throw new Error("Define")
72
    }
73
    targetType(writer: LanguageWriter): Type {
74
        return new Type(writer.mapType(new Type(this.tsTypeName), this))
75
    }
76
    scopeStart?(param: string, language: Language): string
77
    scopeEnd?(param: string, language: Language): string
78
    abstract convertorArg(param: string, writer: LanguageWriter): string
79
    abstract convertorSerialize(param: string, value: string, writer: LanguageWriter): void
80
    abstract convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement
81
    unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression|undefined {
82
        return undefined
83
    }
84
    getMembers(): string[] { return [] }
85
    protected discriminatorFromExpressions(value: string, runtimeType: RuntimeType, writer: LanguageWriter, exprs: LanguageExpression[]) {
86
        return writer.makeNaryOp("&&", [
87
            writer.makeNaryOp("==", [writer.makeRuntimeType(runtimeType), writer.makeString(`${value}_type`)]),
88
            ...exprs
89
        ])
90
    }
91
    protected discriminatorFromFields<T>(value: string, writer: LanguageWriter,
92
        uniqueFields: T[] | undefined, nameAccessor: (field: T) => string, optionalAccessor: (field: T) => boolean)
93
    {
94
        if (!uniqueFields || uniqueFields.length === 0) return undefined
95
        const firstNonOptional = uniqueFields.find(it => !optionalAccessor(it))
96
        return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer, [
97
            writer.makeDiscriminatorFromFields(this, value,
98
                firstNonOptional ? [nameAccessor(firstNonOptional)] : uniqueFields.map(it => nameAccessor(it)))
99
        ])
100
    }
101
}
102

103
export class StringConvertor extends BaseArgConvertor {
104
    private literalValue?: string
105
    constructor(param: string, receiverType: ts.TypeNode) {
106
        super(mapType(receiverType), [RuntimeType.STRING], false, false, param)
107
        if (ts.isLiteralTypeNode(receiverType) && ts.isStringLiteral(receiverType.literal)) {
108
            this.literalValue = receiverType.literal.text
109
        }
110
    }
111
    convertorArg(param: string, writer: LanguageWriter): string {
112
        return writer.language == Language.CPP ? `(const ${PrimitiveType.String.getText()}*)&${param}` : param
113
    }
114
    convertorSerialize(param: string, value: string, writer: LanguageWriter): void {
115
        writer.writeMethodCall(`${param}Serializer`, `writeString`, [value])
116
    }
117
    convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {
118
        const receiver = writer.getObjectAccessor(this, param, value)
119
        return writer.makeAssign(receiver, undefined,
120
            writer.makeCast(writer.makeString(`${param}Deserializer.readString()`),
121
                writer.makeType(this.tsTypeName, false, receiver)),
122
            false)
123
    }
124
    nativeType(impl: boolean): string {
125
        return PrimitiveType.String.getText()
126
    }
127
    interopType(language: Language): string {
128
        return "KStringPtr"
129
    }
130
    isPointerType(): boolean {
131
        return true
132
    }
133
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
134
        return this.literalValue
135
            ? writer.makeString(`${value} === "${this.literalValue}"`)
136
            : undefined
137
    }
138
}
139

140
export class ToStringConvertor extends BaseArgConvertor {
141
    constructor(param: string) {
142
        super("string", [RuntimeType.OBJECT], false, false, param)
143
    }
144
    convertorArg(param: string, writer: LanguageWriter): string {
145
        return writer.language == Language.CPP ? `(const ${PrimitiveType.String.getText()}*)&${param}` : `(${param}).toString()`
146
    }
147
    convertorSerialize(param: string, value: string, writer: LanguageWriter): void {
148
        writer.writeMethodCall(`${param}Serializer`, `writeString`, [
149
            writer.language == Language.CPP ? value : `${value}.toString()`])
150
    }
151
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
152
        return printer.makeAssign(value, undefined, printer.makeString(`${param}Deserializer.readString()`), false)
153
    }
154
    nativeType(impl: boolean): string {
155
        return PrimitiveType.String.getText()
156
    }
157
    interopType(language: Language): string {
158
        return "KStringPtr"
159
    }
160
    isPointerType(): boolean {
161
        return true
162
    }
163
}
164

165
export class BooleanConvertor extends BaseArgConvertor {
166
    constructor(param: string) {
167
        super("boolean", [RuntimeType.BOOLEAN], false, false, param)
168
    }
169
    convertorArg(param: string, writer: LanguageWriter): string {
170
        switch (writer.language) {
171
            case Language.CPP: return param
172
            case Language.TS: return `+${param}`
173
            case Language.ARKTS: return `${param} ? 1 : 0`
174
            case Language.JAVA: return `${param} ? 1 : 0`
175
            default: throw new Error("Unsupported language")
176
        }
177
    }
178
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
179
        printer.writeMethodCall(`${param}Serializer`, "writeBoolean", [value])
180
    }
181
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
182
        const accessor = printer.getObjectAccessor(this, param, value)
183
        return printer.makeAssign(accessor, undefined, printer.makeString(`${param}Deserializer.readBoolean()`), false)
184
    }
185
    nativeType(impl: boolean): string {
186
        return PrimitiveType.Boolean.getText()
187
    }
188
    interopType(language: Language): string {
189
        return language == Language.CPP ? PrimitiveType.Boolean.getText() : "KInt"
190
    }
191
    isPointerType(): boolean {
192
        return false
193
    }
194
}
195

196
export class UndefinedConvertor extends BaseArgConvertor {
197
    constructor(param: string) {
198
        super("undefined", [RuntimeType.UNDEFINED], false, false, param)
199
    }
200
    convertorArg(param: string, writer: LanguageWriter): string {
201
        return writer.makeUndefined().asString()
202
    }
203
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {}
204
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
205
        const accessor = printer.getObjectAccessor(this, param, value)
206
        return printer.makeAssign(accessor, undefined,
207
                printer.makeUndefined(), false)
208
    }
209
    nativeType(impl: boolean): string {
210
        return "Undefined"
211
    }
212
    interopType(language: Language): string {
213
        return PrimitiveType.NativePointer.getText()
214
    }
215
    isPointerType(): boolean {
216
        return false
217
    }
218
}
219

220
export class NullConvertor extends BaseArgConvertor {
221
    constructor(param: string) {
222
        super("null", [RuntimeType.OBJECT], false, false, param)
223
    }
224
    convertorArg(param: string, writer: LanguageWriter): string {
225
        return writer.makeNull().asString()
226
    }
227
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {}
228
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
229
        const accessor = printer.getObjectAccessor(this, param, value)
230
        return printer.makeAssign(accessor, undefined, printer.makeUndefined(), false)
231
    }
232
    nativeType(impl: boolean): string {
233
        return "nullptr"
234
    }
235
    interopType(language: Language): string {
236
        return PrimitiveType.NativePointer.getText()
237
    }
238
    isPointerType(): boolean {
239
        return false
240
    }
241
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
242
        return writer.makeString(`${value} === null`)
243
    }
244
}
245

246
export class EnumConvertor extends BaseArgConvertor {
247
    constructor(param: string,
248
                private enumType: ts.EnumDeclaration,
249
                private readonly isStringEnum: boolean) {
250
        super(isStringEnum ?  "string" : "number",
251
            [isStringEnum ? RuntimeType.STRING : RuntimeType.NUMBER],
252
            false, false, param)
253
    }
254
    convertorArg(param: string, writer: LanguageWriter): string {
255
        return writer.makeUnsafeCast(this, param)
256
    }
257
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
258
        if (this.isStringEnum) {
259
            value = printer.ordinalFromEnum(printer.makeString(value),
260
                identName(this.enumType.name)!).asString()
261
        }
262
        printer.writeMethodCall(`${param}Serializer`, "writeInt32", [this.convertorArg(value, printer)])
263
    }
264
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
265
        let readExpr = printer.makeMethodCall(`${param}Deserializer`, "readInt32", [])
266
        if (this.isStringEnum) {
267
            readExpr = printer.enumFromOrdinal(readExpr, identName(this.enumType.name)!)
268
        }
269
        const receiver = printer.getObjectAccessor(this, param, value)
270
        return printer.makeAssign(receiver, undefined, readExpr, false)
271
    }
272
    nativeType(impl: boolean): string {
273
        return PrimitiveType.Int32.getText()
274
    }
275
    interopType(language: Language): string {
276
        return language == Language.CPP ? PrimitiveType.Int32.getText() : "KInt"
277
    }
278
    isPointerType(): boolean {
279
        return false
280
    }
281
    // TODO: bit clumsy.
282
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
283
        let low: number|undefined = undefined
284
        let high: number|undefined = undefined
285
        // TODO: proper enum value computation for cases where enum members have computed initializers.
286
        this.enumType.members.forEach((member, index) => {
287
            let value = index
288
            if (member.initializer) {
289
                let tsValue = member.initializer
290
                if (ts.isLiteralExpression(tsValue) && !this.isStringEnum) {
291
                    value = parseInt(tsValue.text)
292
                }
293
            }
294
            if (low === undefined || low > value) low = value
295
            if (high === undefined || high < value) high = value
296
        })
297
        const ordinal = this.isStringEnum
298
            ? writer.ordinalFromEnum(writer.makeString(value), identName(this.enumType.name)!)
299
            : writer.makeUnionVariantCast(value, Type.Number, index)
300
        return this.discriminatorFromExpressions(value, this.runtimeTypes[0], writer, [
301
            writer.makeNaryOp(">=", [ordinal, writer.makeString(low!.toString())]),
302
            writer.makeNaryOp("<=",  [ordinal, writer.makeString(high!.toString())])
303
        ])
304
    }
305
}
306

307
export class LengthConvertorScoped extends BaseArgConvertor {
308
    constructor(param: string) {
309
        super("Length", [RuntimeType.NUMBER, RuntimeType.STRING, RuntimeType.OBJECT], false, false, param)
310
    }
311
    scopeStart(param: string): string {
312
        return `withLengthArray(${param}, (${param}Ptr) => {`
313
    }
314
    scopeEnd(param: string): string {
315
        return '})'
316
    }
317
    convertorArg(param: string, writer: LanguageWriter): string {
318
        return param
319
    }
320
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
321
        printer.writeStatement(
322
            printer.makeStatement(
323
                printer.makeMethodCall(`${param}Serializer`, 'writeLength', [printer.makeString(value)])
324
            )
325
        )
326
    }
327
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
328
        return printer.makeAssign(value, undefined,
329
            printer.makeString(`${param}Deserializer.readLength()`), false)
330
    }
331
    nativeType(impl: boolean): string {
332
        return PrimitiveType.Length.getText()
333
    }
334
    interopType(language: Language): string {
335
        switch (language) {
336
            case Language.CPP: return PrimitiveType.ObjectHandle.getText()
337
            case Language.TS: case Language.ARKTS: return 'object'
338
            case Language.JAVA: return 'Object'
339
            default: throw new Error("Unsupported language")
340
        }
341
    }
342
    isPointerType(): boolean {
343
        return true
344
    }
345
}
346

347
export class LengthConvertor extends BaseArgConvertor {
348
    constructor(name: string, param: string) {
349
        super(name, [RuntimeType.NUMBER, RuntimeType.STRING, RuntimeType.OBJECT], false, false, param)
350
    }
351
    convertorArg(param: string, writer: LanguageWriter): string {
352
        return writer.language == Language.CPP ? `(const ${PrimitiveType.Length.getText()}*)&${param}` : param
353
    }
354
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
355
        printer.writeStatement(
356
            printer.makeStatement(
357
                printer.makeMethodCall(`${param}Serializer`, 'writeLength', [printer.makeString(value)])
358
            )
359
        )
360
    }
361
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
362
        const receiver = printer.getObjectAccessor(this, param, value)
363
        return printer.makeAssign(receiver, undefined,
364
            printer.makeCast(
365
                printer.makeString(`${param}Deserializer.readLength()`),
366
                printer.makeType(this.tsTypeName, false, receiver), false), false)
367
    }
368
    nativeType(impl: boolean): string {
369
        return PrimitiveType.Length.getText()
370
    }
371
    interopType(language: Language): string {
372
        switch (language) {
373
            case Language.CPP: return 'KLength'
374
            case Language.TS: case Language.ARKTS: return 'string|number|object'
375
            case Language.JAVA: return 'String'
376
            default: throw new Error("Unsupported language")
377
        }
378
    }
379
    isPointerType(): boolean {
380
        return true
381
    }
382
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
383
        return writer.makeNaryOp("||", [
384
            writer.makeNaryOp("==", [writer.makeRuntimeType(RuntimeType.NUMBER), writer.makeString(`${value}_type`)]),
385
            writer.makeNaryOp("==", [writer.makeRuntimeType(RuntimeType.STRING), writer.makeString(`${value}_type`)]),
386
            writer.makeNaryOp("&&", [
387
                writer.makeNaryOp("==", [writer.makeRuntimeType(RuntimeType.OBJECT), writer.makeString(`${value}_type`)]),
388
                writer.makeCallIsResource(value)
389
            ])])
390
    }
391
}
392

393
export class UnionRuntimeTypeChecker {
394
    private conflictingConvertors: Set<ArgConvertor> = new Set()
395
    private duplicateMembers: Set<string> = new Set()
396
    private discriminators: [LanguageExpression | undefined, ArgConvertor, number][] = []
397

398
    constructor(private convertors: ArgConvertor[]) {
399
        this.checkConflicts()
400
    }
401
    private checkConflicts() {
402
        const runtimeTypeConflicts: Map<RuntimeType, ArgConvertor[]> = new Map()
403
        this.convertors.forEach(conv => {
404
            conv.runtimeTypes.forEach(rtType => {
405
                const convertors = runtimeTypeConflicts.get(rtType)
406
                if (convertors) convertors.push(conv)
407
                else runtimeTypeConflicts.set(rtType, [conv])
408
            })
409
        })
410
        runtimeTypeConflicts.forEach((convertors, rtType) => {
411
            if (convertors.length > 1) {
412
                const allMembers: Set<string> = new Set()
413
                if (rtType === RuntimeType.OBJECT) {
414
                    convertors.forEach(convertor => {
415
                        convertor.getMembers().forEach(member => {
416
                            if (allMembers.has(member)) this.duplicateMembers.add(member)
417
                            allMembers.add(member)
418
                        })
419
                    })
420
                }
421
                convertors.forEach(convertor => {
422
                    this.conflictingConvertors.add(convertor)
423
                })
424
            }
425
        })
426
    }
427
    makeDiscriminator(value: string, index: number, writer: LanguageWriter): LanguageExpression {
428
        const convertor = this.convertors[index]
429
        if (this.conflictingConvertors.has(convertor) && writer.language.needsUnionDiscrimination) {
430
            const discriminator = convertor.unionDiscriminator(value, index, writer, this.duplicateMembers)
431
            this.discriminators.push([discriminator, convertor, index])
432
            if (discriminator) return discriminator
433
        }
434
        return writer.makeNaryOp("||", convertor.runtimeTypes.map(it =>
435
            writer.makeNaryOp("==", [writer.makeUnionVariantCondition(`${value}_type`, RuntimeType[it], index)])))
436
    }
437
    reportConflicts(context: string) {
438
        if (this.discriminators.filter(([discriminator, _, __]) => discriminator === undefined).length > 1) {
439
            console.log(`WARNING: runtime type conflict in "${context}`)
440
            this.discriminators.forEach(([discr, conv, n]) =>
441
                console.log(`   ${n} : ${conv.constructor.name} : ${discr ? discr.asString() : "<undefined>"}`))
442
        }
443
    }
444
}
445
export class UnionConvertor extends BaseArgConvertor {
446
    private memberConvertors: ArgConvertor[]
447
    private unionChecker: UnionRuntimeTypeChecker
448

449
    constructor(param: string, private table: DeclarationTable, private type: ts.UnionTypeNode) {
450
        super(`object`, [], false, true, param)
451
        this.memberConvertors = type
452
            .types
453
            .map(member => table.typeConvertor(param, member))
454
        this.unionChecker = new UnionRuntimeTypeChecker(this.memberConvertors)
455
        this.runtimeTypes = this.memberConvertors.flatMap(it => it.runtimeTypes)
456
    }
457
    convertorArg(param: string, writer: LanguageWriter): string {
458
        throw new Error("Do not use for union")
459
    }
460
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
461
        printer.writeStatement(printer.makeAssign(`${value}_type`, Type.Int32, printer.makeUnionTypeDefaultInitializer(), true, false))
462
        printer.writeStatement(printer.makeUnionSelector(value, `${value}_type`))
463
        this.memberConvertors.forEach((it, index) => {
464
            const maybeElse = (index > 0 && this.memberConvertors[index - 1].runtimeTypes.length > 0) ? "else " : ""
465
            const conditions = this.unionChecker.makeDiscriminator(value, index, printer)
466
            printer.print(`${maybeElse}if (${conditions.asString()}) {`)
467
            printer.pushIndent()
468
            printer.writeMethodCall(`${param}Serializer`, "writeInt8", [castToInt8(index.toString(), printer.language)])
469
            if (!(it instanceof UndefinedConvertor)) {
470
                printer.writeStatement(
471
                    printer.makeAssign(`${value}_${index}`, undefined,
472
                        printer.makeUnionVariantCast(value, it.targetType(printer), index), true))
473
                it.convertorSerialize(param, `${value}_${index}`, printer)
474
            }
475
            printer.popIndent()
476
            printer.print(`}`)
477
        })
478
        this.unionChecker.reportConflicts(this.table.getCurrentContext() ?? "<unknown context>")
479
    }
480
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
481
        let selector = `selector`
482
        const selectorAssign = printer.makeAssign(selector, Type.Int32,
483
            printer.makeString(`${param}Deserializer.readInt8()`), true)
484
        const branches: BranchStatement[] = this.memberConvertors.map((it, index) => {
485
            const receiver = printer.getObjectAccessor(this, param, value, {index: `${index}`})
486
            const expr = printer.makeString(`${selector} == ${index}`)
487
            const stmt = new BlockStatement([
488
                it.convertorDeserialize(param, receiver, printer),
489
                printer.makeSetUnionSelector(value, `${index}`)
490
            ], false)
491
            return { expr, stmt }
492
        })
493
        return new BlockStatement([selectorAssign, printer.makeMultiBranchCondition(branches)], true)
494
    }
495
    nativeType(impl: boolean): string {
496
        return impl
497
            ? `struct { ${PrimitiveType.Int32.getText()} selector; union { ` +
498
            `${this.memberConvertors.map((it, index) => `${it.nativeType(false)} value${index};`).join(" ")}` +
499
            `}; }`
500
            : this.table.getTypeName(this.type)
501
    }
502
    interopType(language: Language): string {
503
        throw new Error("Union")
504
    }
505
    isPointerType(): boolean {
506
        return true
507
    }
508
}
509

510
export class ImportTypeConvertor extends BaseArgConvertor {
511
    private static knownTypes: Map<string, string[]> = new Map([
512
        ["CircleShape", ["isInstanceOf", "\"CircleShape\""]],
513
        ["EllipseShape", ["isInstanceOf", "\"EllipseShape\""]],
514
        ["PathShape", ["isInstanceOf", "\"PathShape\""]],
515
        ["RectShape", ["isInstanceOf", "\"RectShape\""]],
516
        ["ComponentContent", ["isInstanceOf", "\"ComponentContent\""]],
517
        ["DrawableDescriptor", ["isInstanceOf", "\"DrawableDescriptor\""]],
518
        ["SymbolGlyphModifier", ["isInstanceOf", "\"SymbolGlyphModifier\""]],
519
        ["PixelMap", ["isPixelMap"]],
520
        ["Resource", ["isResource"]]])
521
    private importedName: string
522
    constructor(param: string, private table: DeclarationTable, type: ts.ImportTypeNode) {
523
        super("Object", [RuntimeType.OBJECT], false, true, param)
524
        this.importedName = importTypeName(type)
525
    }
526
    convertorArg(param: string, writer: LanguageWriter): string {
527
        throw new Error("Must never be used")
528
    }
529
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
530
        printer.writeMethodCall(`${param}Serializer`, "writeCustomObject", [`"${this.importedName}"`, value])
531
    }
532
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
533
        const accessor = printer.getObjectAccessor(this, param, value)
534
        return printer.makeAssign(accessor, undefined,
535
                printer.makeString(`${param}Deserializer.readCustomObject("${this.importedName}")`), false)
536
    }
537
    nativeType(impl: boolean): string {
538
        // return this.importedName
539
        // treat ImportType as CustomObject
540
        return PrimitiveType.CustomObject.getText()
541
    }
542
    interopType(language: Language): string {
543
        throw new Error("Must never be used")
544
    }
545
    isPointerType(): boolean {
546
        return true
547
    }
548
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
549
        const handler = ImportTypeConvertor.knownTypes.get(this.importedName)
550
        return handler
551
            ? this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,
552
                [writer.makeString(`${handler[0]}(${handler.slice(1).concat(value).join(", ")})`)])
553
            : undefined
554
    }
555
}
556

557
export class CustomTypeConvertor extends BaseArgConvertor {
558
    private static knownTypes: Map<string, [string, boolean][]> = new Map([
559
        ["LinearGradient", [["angle", true], ["direction", true], ["colors", false], ["repeating", true]]]
560
    ])
561
    private customName: string
562
    constructor(param: string, customName: string, tsType?: string) {
563
        super(tsType ?? "Object", [RuntimeType.OBJECT], false, true, param)
564
        this.customName = customName
565
    }
566
    convertorArg(param: string, writer: LanguageWriter): string {
567
        throw new Error("Must never be used")
568
    }
569
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
570
        printer.writeMethodCall(`${param}Serializer`, `writeCustomObject`, [`"${this.customName}"`, value])
571
    }
572
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
573
        const receiver = printer.getObjectAccessor(this, param, value)
574
        return printer.makeAssign(receiver, undefined,
575
                printer.makeCast(printer.makeMethodCall(`${param}Deserializer`,
576
                        "readCustomObject",
577
                        [printer.makeString(`"${this.customName}"`)]),
578
                    printer.makeType(this.tsTypeName, false, receiver)), false)
579
    }
580
    nativeType(impl: boolean): string {
581
        return PrimitiveType.CustomObject.getText()
582
    }
583
    interopType(language: Language): string {
584
        throw new Error("Must never be used")
585
    }
586
    isPointerType(): boolean {
587
        return true
588
    }
589
    override getMembers(): string[] {
590
        return CustomTypeConvertor.knownTypes.get(this.customName)?.map(it => it[0]) ?? super.getMembers()
591
    }
592
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
593
        const uniqueFields = CustomTypeConvertor.knownTypes.get(this.customName)?.filter(it => !duplicates.has(it[0]))
594
        return this.discriminatorFromFields(value, writer, uniqueFields, it => it[0], it => it[1])
595
    }
596
}
597

598
export class OptionConvertor extends BaseArgConvertor {
599
    private typeConvertor: ArgConvertor
600
    // TODO: be smarter here, and for smth like Length|undefined or number|undefined pass without serializer.
601
    constructor(param: string, private table: DeclarationTable, public type: ts.TypeNode) {
602
        let typeConvertor = table.typeConvertor(param, type)
603
        let runtimeTypes = typeConvertor.runtimeTypes;
604
        if (!runtimeTypes.includes(RuntimeType.UNDEFINED)) {
605
            runtimeTypes.push(RuntimeType.UNDEFINED)
606
        }
607
        super(`${typeConvertor.tsTypeName}|undefined`, runtimeTypes, typeConvertor.isScoped, true, param)
608
        this.typeConvertor = typeConvertor
609
    }
610
    convertorArg(param: string, writer: LanguageWriter): string {
611
        throw new Error("Must never be used")
612
    }
613
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
614
        const valueType = `${value}_type`
615
        const serializedType = (printer.language == Language.JAVA ? undefined : Type.Int32)
616
        printer.writeStatement(printer.makeAssign(valueType, serializedType, printer.makeRuntimeType(RuntimeType.UNDEFINED), true, false))
617
        printer.runtimeType(this, valueType, value)
618
        printer.writeMethodCall(`${param}Serializer`, "writeInt8", [castToInt8(valueType, printer.language)])
619
        printer.print(`if (${printer.makeRuntimeTypeCondition(valueType, false, RuntimeType.UNDEFINED).asString()}) {`)
620
        printer.pushIndent()
621
        printer.writeStatement(printer.makeAssign(`${value}_value`, undefined, printer.makeValueFromOption(value, this.typeConvertor), true))
622
        this.typeConvertor.convertorSerialize(param, `${value}_value`, printer)
623
        printer.popIndent()
624
        printer.print(`}`)
625
    }
626
    convertorCArg(param: string): string {
627
        throw new Error("Must never be used")
628
    }
629
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
630
        const runtimeType = `runtimeType`
631
        const accessor = printer.getObjectAccessor(this, param, value)
632
        const thenStatement = new BlockStatement([
633
            this.typeConvertor.convertorDeserialize(param, accessor, printer)
634
        ])
635
        return new BlockStatement([
636
            printer.makeAssign(runtimeType, undefined,
637
                printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),
638
            printer.makeSetOptionTag(value, printer.makeCast(printer.makeString(runtimeType), printer.getTagType())),
639
            printer.makeCondition(printer.makeRuntimeTypeDefinedCheck(runtimeType), thenStatement)
640
        ], true)
641
    }
642
    nativeType(impl: boolean): string {
643
        return impl
644
            ? `struct { ${PrimitiveType.Tag.getText()} tag; ${this.table.getTypeName(this.type, false)} value; }`
645
            : this.table.getTypeName(this.type, true)
646
    }
647
    interopType(language: Language): string {
648
        return language == Language.CPP ? PrimitiveType.NativePointer.getText() : "KNativePointer"
649
    }
650
    isPointerType(): boolean {
651
        return true
652
    }
653
}
654

655
export class AggregateConvertor extends BaseArgConvertor {
656
    private memberConvertors: ArgConvertor[]
657
    private members: [string, boolean][] = []
658
    public readonly aliasName: string | undefined
659

660
    constructor(param: string, private table: DeclarationTable, private type: ts.TypeLiteralNode) {
661
        super(mapType(type), [RuntimeType.OBJECT], false, true, param)
662
        this.aliasName = ts.isTypeAliasDeclaration(this.type.parent) ? identName(this.type.parent.name) : undefined
663
        this.memberConvertors = type
664
            .members
665
            .filter(ts.isPropertySignature)
666
            .map((member, index) => {
667
                this.members[index] = [identName(member.name)!, member.questionToken != undefined]
668
                return table.typeConvertor(param, member.type!, member.questionToken != undefined)
669
            })
670
    }
671
    convertorArg(param: string, writer: LanguageWriter): string {
672
        throw new Error("Do not use for aggregates")
673
    }
674
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
675
        this.memberConvertors.forEach((it, index) => {
676
            let memberName = this.members[index][0]
677
            printer.writeStatement(
678
                printer.makeAssign(`${value}_${memberName}`, undefined,
679
                    printer.makeString(`${value}.${memberName}`), true))
680
            it.convertorSerialize(param, `${value}_${memberName}`, printer)
681
        })
682
    }
683
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
684
        const structAccessor = printer.getObjectAccessor(this, param, value)
685
        let struct = this.table.targetStruct(this.table.toTarget(this.type))
686
        // Typed structs may refer each other, so use indent level to discriminate.
687
        // Somewhat ugly, but works.
688
        const typedStruct = `typedStruct${printer.indentDepth()}`
689
        printer.pushIndent()
690
        const statements = [
691
            printer.makeObjectAlloc(structAccessor, struct.getFields()),
692
            printer.makeAssign(typedStruct, new Type(printer.makeRef(printer.makeType(this.tsTypeName, false, structAccessor).name)),
693
                printer.makeString(structAccessor),true, false
694
            )
695
        ]
696
        this.memberConvertors.forEach((it, index) => {
697
            // TODO: maybe use accessor?
698
            statements.push(
699
                it.convertorDeserialize(param, `${typedStruct}.${struct.getFields()[index].name}`, printer)
700
            )
701
        })
702
        printer.popIndent()
703
        return new BlockStatement(statements, true)
704
    }
705
    nativeType(impl: boolean): string {
706
        return impl
707
            ? `struct { ` +
708
            `${this.memberConvertors.map((it, index) => `${it.nativeType(true)} value${index};`).join(" ")}` +
709
            '} '
710
            : this.table.getTypeName(this.type)
711
    }
712
    interopType(language: Language): string {
713
        throw new Error("Must never be used")
714
    }
715
    isPointerType(): boolean {
716
        return true
717
    }
718
    getMembers(): string[] {
719
        return this.members.map(it => it[0])
720
    }
721
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
722
        const uniqueFields = this.members.filter(it => !duplicates.has(it[0]))
723
        return this.discriminatorFromFields(value, writer, uniqueFields, it => it[0], it => it[1])
724
    }
725
}
726

727
export class InterfaceConvertor extends BaseArgConvertor {
728
    constructor(
729
        name: string,
730
        param: string,
731
        private declaration: ts.InterfaceDeclaration | ts.ClassDeclaration,
732
        protected table: DeclarationTable,
733
        private type: ts.TypeReferenceNode) {
734
        super(name, [RuntimeType.OBJECT], false, true, param)
735
    }
736

737
    convertorArg(param: string, writer: LanguageWriter): string {
738
        throw new Error("Must never be used")
739
    }
740
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
741
        printer.writeMethodCall(`${param}Serializer`, this.table.serializerName(this.tsTypeName, this.type), [value])
742
    }
743
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
744
        const accessor = printer.getObjectAccessor(this, param, value)
745
        return printer.makeAssign(accessor, undefined,
746
                printer.makeMethodCall(`${param}Deserializer`, this.table.deserializerName(this.tsTypeName, this.type), []), false)
747
    }
748
    nativeType(impl: boolean): string {
749
        return this.tsTypeName
750
    }
751
    interopType(language: Language): string {
752
        throw new Error("Must never be used")
753
    }
754
    isPointerType(): boolean {
755
        return true
756
    }
757
    getMembers(): string[] {
758
        return this.table.targetStruct(this.declaration).getFields().map(it => it.name)
759
    }
760
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
761
        if (this.tsTypeName.endsWith("GestureInterface")) {
762
            const gestureType = this.tsTypeName.slice(0, -"GestureInterface".length)
763
            const castExpr = writer.makeCast(writer.makeString(value), new Type("GestureComponent<Object>"))
764
            return writer.makeNaryOp("===", [
765
                writer.makeString(`${castExpr.asString()}.type`),
766
                writer.makeString(`GestureName.${gestureType}`)])
767
        }
768
        const uniqueFields = this.table
769
            .targetStruct(this.declaration)
770
            .getFields()
771
            .filter(it => !duplicates.has(it.name))
772
        return this.discriminatorFromFields(value, writer, uniqueFields, it => it.name, it => it.optional)
773
    }
774
}
775

776
export class ClassConvertor extends InterfaceConvertor {
777
    constructor(name: string, param: string, declaration: ts.ClassDeclaration, table: DeclarationTable, type: ts.TypeReferenceNode) {
778
        super(name, param, declaration, table, type)
779
    }
780
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
781
        // SubTabBarStyle causes inscrutable "SubTabBarStyle is not defined" error
782
        if (this.tsTypeName === "SubTabBarStyle") return undefined
783
        return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,
784
            [writer.makeString(`${value} instanceof ${this.tsTypeName}`)])
785
    }
786
}
787

788
export class FunctionConvertor extends BaseArgConvertor {
789
    constructor(
790
        param: string,
791
        protected table: DeclarationTable,
792
        private type: ts.TypeNode) {
793
        // TODO: pass functions as integers to native side.
794
        super("Function", [RuntimeType.FUNCTION], false, false, param)
795
    }
796
    convertorArg(param: string, writer: LanguageWriter): string {
797
        return writer.language == Language.CPP ? `makeArkFunctionFromId(${param})` : `registerCallback(${param})`
798
    }
799
    convertorSerialize(param: string, value: string, writer: LanguageWriter): void {
800
        writer.writeMethodCall(`${param}Serializer`, "writeFunction", [value])
801
    }
802
    convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {
803
        const accessor = writer.getObjectAccessor(this, param, value)
804
        return writer.makeAssign(accessor, undefined,
805
            writer.makeCast(writer.makeString(`${param}Deserializer.readFunction()`),
806
                writer.makeType(mapType(this.type), true, accessor))
807
            , false)
808
    }
809
    nativeType(impl: boolean): string {
810
        return PrimitiveType.Function.getText()
811
    }
812
    interopType(language: Language): string {
813
        return language == Language.CPP ? PrimitiveType.Int32.getText() : "KInt"
814
    }
815
    isPointerType(): boolean {
816
        return false
817
    }
818
}
819

820
export class TupleConvertor extends BaseArgConvertor {
821
    constructor(param: string, protected table: DeclarationTable, private type: ts.TupleTypeNode) {
822
        super(`[${type.elements.map(it => mapType(it)).join(",")}]`, [RuntimeType.OBJECT], false, true, param)
823
        this.memberConvertors = type
824
            .elements
825
            .map(element => table.typeConvertor(param, element))
826
    }
827
    private memberConvertors: ArgConvertor[]
828
    convertorArg(param: string, writer: LanguageWriter): string {
829
        throw new Error("Must never be used")
830
    }
831
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
832
        printer.writeMethodCall(`${param}Serializer`, "writeInt8", [
833
            castToInt8(printer.makeRuntimeTypeGetterCall(value).asString(), printer.language)
834
        ])
835
        this.memberConvertors.forEach((it, index) => {
836
            printer.writeStatement(
837
                printer.makeAssign(`${value}_${index}`, undefined, printer.makeTupleAccess(value, index), true))
838
            it.convertorSerialize(param, `${value}_${index}`, printer)
839
        })
840
    }
841
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
842
        const runtimeType = `runtimeType`
843
        const receiver = printer.getObjectAccessor(this, param, value)
844
        const statements: LanguageStatement[] = []
845
        const tmpTupleIds: string[] = []
846
        this.memberConvertors.forEach((it, index) => {
847
            const tmpTupleId = `tmpTupleItem${index}`
848
            tmpTupleIds.push(tmpTupleId)
849
            const receiver = printer.getObjectAccessor(this, param, value, {index: `${index}`})
850
            // need to remove the mark '?' from Optional type
851
            const tsTypeName = mapType(this.type.elements[index]).replace("?", "")
852
            statements.push(
853
                printer.makeAssign(tmpTupleId,
854
                    // makeType - creating the correct type for TS(using tsTypeName) or C++(use decltype(receiver))
855
                    printer.makeType(tsTypeName, true, receiver),undefined, true, false),
856
                it.convertorDeserialize(param, tmpTupleId, printer)
857
            )
858
        })
859
        statements.push(printer.makeTupleAssign(receiver, tmpTupleIds))
860
        const thenStatement = new BlockStatement(statements)
861
        return new BlockStatement([
862
            printer.makeAssign(runtimeType, undefined,
863
                printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),
864
            printer.makeCondition(
865
                printer.makeRuntimeTypeDefinedCheck(runtimeType),
866
                thenStatement)
867
        ], true)
868
    }
869
    nativeType(impl: boolean): string {
870
        return impl
871
            ? `struct { ` +
872
            `${this.memberConvertors.map((it, index) => `${it.nativeType(false)} value${index};`).join(" ")}` +
873
            '} '
874
            : this.table.getTypeName(this.type)
875
    }
876
    interopType(language: Language): string {
877
        throw new Error("Must never be used")
878
    }
879
    isPointerType(): boolean {
880
        return true
881
    }
882
}
883

884
export class ArrayConvertor extends BaseArgConvertor {
885
    elementConvertor: ArgConvertor
886
    constructor(param: string, public table: DeclarationTable, private type: ts.TypeNode, private elementType: ts.TypeNode) {
887
        super(`Array<${mapType(elementType)}>`, [RuntimeType.OBJECT], false, true, param)
888
        this.elementConvertor = table.typeConvertor(param, elementType)
889
    }
890
    convertorArg(param: string, writer: LanguageWriter): string {
891
        throw new Error("Must never be used")
892
    }
893
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
894
        // Array length.
895
        printer.writeMethodCall(`${param}Serializer`, "writeInt8", [
896
            castToInt8(printer.makeRuntimeTypeGetterCall(value).asString(), printer.language)])
897
        const valueLength = printer.makeArrayLength(value).asString()
898
        const loopCounter = "i"
899
        printer.writeMethodCall(`${param}Serializer`, "writeInt32", [castToInt32(valueLength, printer.language)])
900
        printer.writeStatement(printer.makeLoop(loopCounter, valueLength))
901
        printer.pushIndent()
902
        printer.writeStatement(
903
            printer.makeAssign(`${value}_element`, undefined, printer.makeArrayAccess(value, loopCounter), true))
904
        this.elementConvertor.convertorSerialize(param, `${value}_element`, printer)
905
        printer.popIndent()
906
        printer.print(`}`)
907
    }
908
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
909
        // Array length.
910
        const runtimeType = `runtimeType`
911
        const arrayLength = `arrayLength`
912
        const forCounterName = `i`
913
        const arrayAccessor = printer.getObjectAccessor(this, param, value)
914
        const accessor = printer.getObjectAccessor(this, param, arrayAccessor, {index: `[${forCounterName}]`})
915
        const thenStatement = new BlockStatement([
916
            // read length
917
            printer.makeAssign(arrayLength, undefined, printer.makeString(`${param}Deserializer.readInt32()`), true),
918
            // prepare object
919
            printer.makeArrayResize(arrayAccessor, mapType(this.type), arrayLength, `${param}Deserializer`),
920
            // store
921
            printer.makeLoop(forCounterName, arrayLength,
922
                this.elementConvertor.convertorDeserialize(param, accessor, printer)),
923
        ])
924
        const statements = [
925
            printer.makeAssign(runtimeType,
926
                undefined,
927
                printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),
928
            printer.makeCondition(printer.makeRuntimeTypeDefinedCheck(runtimeType), thenStatement)
929
        ]
930
        return new BlockStatement(statements, true)
931
    }
932
    nativeType(impl: boolean): string {
933
        return `Array_${this.table.computeTypeName(undefined, this.elementType, false)}`
934
    }
935
    interopType(language: Language): string {
936
        throw new Error("Must never be used")
937
    }
938
    isPointerType(): boolean {
939
        return true
940
    }
941
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
942
        return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,
943
            [writer.makeString(`${value} instanceof ${this.targetType(writer).name}`)])
944
    }
945
    elementTypeName(): string {
946
        return mapType(this.elementType)
947
    }
948
}
949

950
export class MapConvertor extends BaseArgConvertor {
951
    keyConvertor: ArgConvertor
952
    valueConvertor: ArgConvertor
953
    constructor(param: string, public table: DeclarationTable, type: ts.TypeNode, public keyType: ts.TypeNode, public valueType: ts.TypeNode) {
954
        super(`Map<${mapType(keyType)}, ${mapType(valueType)}>`, [RuntimeType.OBJECT], false, true, param)
955
        this.keyConvertor = table.typeConvertor(param, keyType)
956
        this.valueConvertor = table.typeConvertor(param, valueType)
957
    }
958

959
    convertorArg(param: string, writer: LanguageWriter): string {
960
        throw new Error("Must never be used")
961
    }
962
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
963
        // Map size.
964
        printer.writeMethodCall(`${param}Serializer`, "writeInt8", [
965
            castToInt8(printer.makeRuntimeTypeGetterCall(value).asString(), printer.language)])
966
        printer.writeMethodCall(`${param}Serializer`, "writeInt32", [castToInt32(`${value}.size`, printer.language)])
967
        printer.writeStatement(printer.makeMapForEach(value, `${value}_key`, `${value}_value`, () => {
968
            this.keyConvertor.convertorSerialize(param, `${value}_key`, printer)
969
            this.valueConvertor.convertorSerialize(param, `${value}_value`, printer)
970
        }))
971
    }
972
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
973
        // Map size.
974
        const runtimeType = `runtimeType`
975
        const mapSize = `mapSize`
976
        const keyTypeName = printer.makeMapKeyTypeName(this)
977
        const valueTypeName = printer.makeMapValueTypeName(this)
978
        const counterVar = `i`
979
        const keyAccessor = printer.getObjectAccessor(this, param, value, {index: counterVar, field: "keys"})
980
        const valueAccessor = printer.getObjectAccessor(this, param, value, {index: counterVar, field: "values"})
981
        const tmpKey = `tmpKey`
982
        const tmpValue = `tmpValue`
983
        const statements = [
984
            printer.makeAssign(runtimeType, undefined,
985
                printer.makeCast(printer.makeString(`${param}Deserializer.readInt8()`), printer.getRuntimeType()), true),
986
            printer.makeCondition(printer.makeRuntimeTypeDefinedCheck(runtimeType), new BlockStatement([
987
                printer.makeAssign(mapSize, undefined, printer.makeString(`${param}Deserializer.readInt32()`), true),
988
                printer.makeMapResize(keyTypeName, valueTypeName, value, mapSize, `${param}Deserializer`),
989
                printer.makeLoop(counterVar, mapSize, new BlockStatement([
990
                    printer.makeAssign(tmpKey, new Type(keyTypeName), undefined, true, false),
991
                    this.keyConvertor.convertorDeserialize(param, tmpKey, printer),
992
                    printer.makeAssign(tmpValue, new Type(keyTypeName), undefined, true, false),
993
                    this.valueConvertor.convertorDeserialize(param, tmpValue, printer),
994
                    printer.makeMapInsert(keyAccessor, tmpKey, valueAccessor, tmpValue),
995
                ], false)),
996
            ])),
997
        ]
998
        return new BlockStatement(statements, true)
999
    }
1000

1001
    nativeType(impl: boolean): string {
1002
        const keyTypeName = this.table.computeTypeName(undefined, this.keyType, false)
1003
        const valueTypeName = this.table.computeTypeName(undefined, this.valueType, false)
1004
        return `Map_${keyTypeName}_${valueTypeName}`
1005
    }
1006
    interopType(language: Language): string {
1007
        throw new Error("Must never be used")
1008
    }
1009
    isPointerType(): boolean {
1010
        return true
1011
    }
1012
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
1013
        return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,
1014
            [writer.makeString(`${value} instanceof Map`)])
1015
    }
1016
}
1017

1018
export class NumberConvertor extends BaseArgConvertor {
1019
    constructor(param: string) {
1020
        // TODO: as we pass tagged values - request serialization to array for now.
1021
        // Optimize me later!
1022
        super("number", [RuntimeType.NUMBER], false, false, param)
1023
    }
1024
    convertorArg(param: string, writer: LanguageWriter): string {
1025
        return writer.language == Language.CPP ?  `(const ${PrimitiveType.Number.getText()}*)&${param}` : param
1026
    }
1027
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
1028
        printer.writeMethodCall(`${param}Serializer`, "writeNumber", [value])
1029
    }
1030
    convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {
1031
        const receiver = writer.getObjectAccessor(this, param, value)
1032
        return writer.makeAssign(receiver, undefined,
1033
            writer.makeCast(
1034
                writer.makeString(`${param}Deserializer.readNumber()`),
1035
                writer.makeType(this.tsTypeName, false, receiver)), false)
1036
    }
1037
    nativeType(): string {
1038
        return PrimitiveType.Number.getText()
1039
    }
1040
    interopType(language: Language): string {
1041
        return language == Language.CPP ?  "KInteropNumber" : "number"
1042
    }
1043
    isPointerType(): boolean {
1044
        return true
1045
    }
1046
}
1047

1048
export class MaterializedClassConvertor extends BaseArgConvertor {
1049
    constructor(
1050
        name: string,
1051
        param: string,
1052
        protected table: DeclarationTable,
1053
        private type: ts.InterfaceDeclaration | ts.ClassDeclaration,
1054
    ) {
1055
        super(name, [RuntimeType.OBJECT], false, true, param)
1056
    }
1057

1058
    convertorArg(param: string, writer: LanguageWriter): string {
1059
        throw new Error("Must never be used")
1060
    }
1061
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
1062
        printer.writeMethodCall(`${param}Serializer`, "writeMaterialized", [value])
1063
    }
1064
    convertorDeserialize(param: string, value: string, printer: LanguageWriter): LanguageStatement {
1065
        const accessor = printer.getObjectAccessor(this, param, value)
1066
        const readStatement = printer.makeCast(
1067
            printer.makeMethodCall(`${param}Deserializer`, `readMaterialized`, []),
1068
            new Type(this.table.computeTargetName(this.type, false)!),
1069
        )
1070
        return printer.makeAssign(accessor, undefined, readStatement, false)
1071
    }
1072
    nativeType(impl: boolean): string {
1073
        return PrimitiveType.Materialized.getText()
1074
    }
1075
    interopType(language: Language): string {
1076
        throw new Error("Must never be used")
1077
    }
1078
    isPointerType(): boolean {
1079
        return true
1080
    }
1081
    override unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
1082
        return this.discriminatorFromExpressions(value, RuntimeType.OBJECT, writer,
1083
            [writer.makeString(`${value} instanceof ${this.tsTypeName}`)])
1084
    }
1085
}
1086

1087
export class PredefinedConvertor extends BaseArgConvertor {
1088
    constructor(param: string, tsType: string, private convertorName: string, private cType: string) {
1089
        super(tsType, [RuntimeType.OBJECT, RuntimeType.UNDEFINED], false, true, param)
1090
    }
1091
    convertorArg(param: string, writer: LanguageWriter): string {
1092
        throw new Error("unused")
1093
    }
1094
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
1095
        printer.writeMethodCall(`${param}Serializer`, `write${this.convertorName}`, [value])
1096
    }
1097
    convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {
1098
        const accessor = writer.getObjectAccessor(this, param, value)
1099
        return writer.makeAssign(accessor, undefined, writer.makeString(`${param}Deserializer.read${this.convertorName}()`), false)
1100
    }
1101
    nativeType(impl: boolean): string {
1102
        return this.cType
1103
    }
1104
    interopType(language: Language): string {
1105
        return language == Language.CPP ? PrimitiveType.Int32.getText() + "*" :  "Int32ArrayPtr"
1106
    }
1107
    isPointerType(): boolean {
1108
        return true
1109
    }
1110
}
1111

1112
class ProxyConvertor extends BaseArgConvertor {
1113
    constructor(protected convertor: ArgConvertor, suggestedName?: string) {
1114
        super(suggestedName ?? convertor.tsTypeName, convertor.runtimeTypes, convertor.isScoped, convertor.useArray, convertor.param)
1115
    }
1116
    convertorArg(param: string, writer: LanguageWriter): string {
1117
        return this.convertor.convertorArg(param, writer)
1118
    }
1119
    convertorDeserialize(param: string, value: string, writer: LanguageWriter): LanguageStatement {
1120
        return this.convertor.convertorDeserialize(param, value, writer)
1121
    }
1122
    convertorSerialize(param: string, value: string, printer: LanguageWriter): void {
1123
        this.convertor.convertorSerialize(param, value, printer)
1124
    }
1125
    nativeType(impl: boolean): string {
1126
        return this.convertor.nativeType(impl)
1127
    }
1128
    interopType(language: Language): string {
1129
        return this.convertor.interopType(language)
1130
    }
1131
    isPointerType(): boolean {
1132
        return this.convertor.isPointerType()
1133
    }
1134
    unionDiscriminator(value: string, index: number, writer: LanguageWriter, duplicates: Set<string>): LanguageExpression | undefined {
1135
        return this.convertor.unionDiscriminator(value, index, writer, duplicates)
1136
    }
1137
    getMembers(): string[] {
1138
        return this.convertor.getMembers()
1139
    }
1140
}
1141

1142
export class TypeAliasConvertor extends ProxyConvertor {
1143
    constructor(
1144
        param: string,
1145
        private table: DeclarationTable,
1146
        declaration: ts.TypeAliasDeclaration,
1147
        private typeArguments?: ts.NodeArray<ts.TypeNode>
1148
    ) {
1149
        super(table.typeConvertor(param, declaration.type), identName(declaration.name))
1150
    }
1151
}
1152

1153
export interface RetConvertor {
1154
    isVoid: boolean
1155
    nativeType: () => string
1156
    macroSuffixPart: () => string
1157
}
1158

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

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

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

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