idlize

Форк
0
/
EventsPrinter.ts 
395 строк · 15.0 Кб
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 { IndentedPrinter } from "../../IndentedPrinter"
18
import { DeclarationTable, DeclarationTarget, PrimitiveType } from "../DeclarationTable"
19
import { BlockStatement, CppLanguageWriter, ExpressionStatement, LanguageWriter, Method, NamedMethodSignature, StringExpression, TSLanguageWriter, Type } from "../LanguageWriters"
20
import { PeerClassBase } from "../PeerClass"
21
import { PeerLibrary } from "../PeerLibrary"
22
import { PeerMethod } from "../PeerMethod"
23
import { makeCEventsArkoalaImpl, makeCEventsLibaceImpl, makePeerEvents } from "../FileGenerators"
24
import { generateEventReceiverName, generateEventSignature } from "./HeaderPrinter"
25
import { Language, asString, identName } from "../../util"
26
import { mapType } from "../TypeNodeNameConvertor"
27
import { PeerGeneratorConfig } from "../PeerGeneratorConfig"
28
import { ImportsCollector } from "../ImportsCollector"
29

30
export const PeerEventsProperties = "PeerEventsProperties"
31
export const PeerEventKind = "PeerEventKind"
32

33
export type CallbackInfo = {
34
    componentName: string,
35
    methodName: string,
36
    args: {name: string, type: ts.TypeNode, nullable: boolean}[],
37
    returnType: ts.TypeNode,
38
}
39

40
export function generateEventsBridgeSignature(language: Language): Method {
41
    let signature: NamedMethodSignature
42
    switch (language) {
43
        case Language.JAVA:
44
        case Language.ARKTS:
45
        case Language.TS:
46
            signature = new NamedMethodSignature(
47
                new Type(`KInt`),
48
                [new Type(`Uint8Array`), new Type(`KInt`)],
49
                [`result`, `size`],
50
            )
51
            break;
52
        case Language.CPP:
53
            signature = new NamedMethodSignature(
54
                new Type(`KInt`),
55
                [new Type(`KUint*`), new Type(`KInt`)],
56
                [`result`, `size`],
57
            )
58
            break;
59
        default:
60
            throw new Error("Not implemented")
61
    }
62
    return new Method(`CheckArkoalaGeneratedEvents`, signature)
63
}
64

65
export function groupCallbacks(callbacks: CallbackInfo[]): Map<string, CallbackInfo[]> {
66
    const receiverToCallbacks = new Map<string, CallbackInfo[]>()
67
    for (const callback of callbacks) {
68
        if (!receiverToCallbacks.has(callback.componentName))
69
            receiverToCallbacks.set(callback.componentName, [callback])
70
        else
71
            receiverToCallbacks.get(callback.componentName)!.push(callback)
72
    }
73
    return receiverToCallbacks
74
}
75

76
export function collectCallbacks(library: PeerLibrary): CallbackInfo[] {
77
    let callbacks: CallbackInfo[] = []
78
    for (const file of library.files) {
79
        for (const peer of file.peers.values()) {
80
            for (const method of peer.methods) {
81
                for (const target of method.declarationTargets) {
82
                    const info = convertToCallback(peer, method, target)
83
                    if (info && canProcessCallback(library.declarationTable, info))
84
                        callbacks.push(info)
85
                }
86
            }
87
        }
88
    }
89
    return callbacks
90
}
91

92
export function canProcessCallback(declarationTable: DeclarationTable, callback: CallbackInfo): boolean {
93
    if (PeerGeneratorConfig.invalidEvents.includes(callback.methodName))
94
        return false
95
    return true
96
}
97

98
export function convertToCallback(peer: PeerClassBase, method: PeerMethod, target: DeclarationTarget): CallbackInfo | undefined {
99
    if (target instanceof PrimitiveType)
100
        return undefined
101
    if (ts.isFunctionTypeNode(target))
102
        return {
103
            componentName: peer.getComponentName(),
104
            methodName: method.method.name,
105
            args: target.parameters.map(it => {return {
106
                name: asString(it.name),
107
                type: it.type!,
108
                nullable: !!it.questionToken
109
            }}),
110
            returnType: target.type,
111
        }
112
    if (ts.isTypeReferenceNode(target) && identName(target.typeName) === "Callback") {
113
        const data = target.typeArguments![0]
114
        const hasData = data.kind !== ts.SyntaxKind.VoidKeyword
115
        return {
116
            componentName: peer.getComponentName(),
117
            methodName: method.method.name,
118
            args: hasData ? [{name: 'data', type: data, nullable: false}] : [],
119
            returnType: target.typeArguments![1] ?? ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)
120
        }
121
    }
122
}
123

124
export function callbackIdByInfo(info: CallbackInfo): string {
125
    return `${info.componentName}_${info.methodName}`
126
}
127

128
export function callbackEventNameByInfo(info: CallbackInfo): string {
129
    return `${callbackIdByInfo(info)}_event`
130
}
131

132
class CEventsVisitor {
133
    readonly impl: LanguageWriter = new CppLanguageWriter(new IndentedPrinter())
134
    readonly receiversList: LanguageWriter = new CppLanguageWriter(new IndentedPrinter())
135

136
    constructor(
137
        private readonly library: PeerLibrary,
138
        private readonly isEmptyImplementation: boolean,
139
    ) {}
140

141
    private printEventsKinds(callbacks: CallbackInfo[]) {
142
        if (this.isEmptyImplementation)
143
            return
144
        this.impl.print(`enum ${PeerEventKind} {`)
145
        this.impl.pushIndent()
146
        callbacks.forEach((callback, index) => {
147
            this.impl.print(`Kind${callbackIdByInfo(callback)} = ${index},`)
148
        })
149
        this.impl.popIndent()
150
        this.impl.print('};\n')
151
    }
152

153
    private printEventImpl(event: CallbackInfo) {
154
        const signature = generateEventSignature(this.library.declarationTable, event)
155
        const args = signature.args.map((type, index) => {
156
            return `${type.name} ${signature.argName(index)}`
157
        })
158
        this.impl.print(`${signature.returnType.name} ${callbackIdByInfo(event)}Impl(${args.join(',')}) {`)
159
        this.impl.pushIndent()
160
        if (this.isEmptyImplementation) {
161
            this.impl.print("// GENERATED EMPTY IMPLEMENTATION")
162
        } else {
163
            this.impl.print(`EventBuffer _eventBuffer;`)
164
            this.impl.print(`Serializer _eventBufferSerializer(_eventBuffer.buffer);`)
165
            this.impl.print(`_eventBufferSerializer.writeInt32(Kind${callbackIdByInfo(event)});`)
166
            this.impl.print(`_eventBufferSerializer.writeInt32(nodeId);`)
167
            for (const arg of event.args) {
168
                const convertor = this.library.declarationTable.typeConvertor(arg.name, arg.type, arg.nullable)
169
                convertor.convertorSerialize(`_eventBuffer`, arg.name, this.impl)
170
            }
171
            this.impl.print(`sendEvent(&_eventBuffer);`)
172
        }
173
        this.impl.popIndent()
174
        this.impl.print('}')
175
    }
176

177
    private printReceiver(componentName: string, callbacks: CallbackInfo[]) {
178
        const receiver = generateEventReceiverName(componentName)
179
        this.impl.print(`const ${receiver}* Get${componentName}EventsReceiver() {`)
180
        this.impl.pushIndent()
181
        this.impl.print(`static const ${receiver} ${receiver}Impl {`)
182
        this.impl.pushIndent()
183
        for (const callback of callbacks) {
184
            this.impl.print(`${callbackIdByInfo(callback)}Impl,`)
185
        }
186
        this.impl.popIndent()
187
        this.impl.print(`};\n`)
188

189
        this.impl.print(`return &${receiver}Impl;`)
190
        this.impl.popIndent()
191
        this.impl.print(`}`)
192
    }
193

194
    private printReceiversList(callbacks: Map<string, CallbackInfo[]>) {
195
        for (const componentName of callbacks.keys()) {
196
            if (this.library.shouldGenerateComponent(componentName))
197
                this.receiversList.print(`Get${componentName}EventsReceiver,`)
198
            else 
199
                this.receiversList.print(`nullptr,`)
200
        }
201
    }
202

203
    print() {
204
        const listedCallbacks = collectCallbacks(this.library)
205
        const groupedCallbacks = groupCallbacks(listedCallbacks)
206
        this.printEventsKinds(listedCallbacks)
207
        for (const [name, callbacks] of groupedCallbacks) {
208
            if (!this.library.shouldGenerateComponent(name))
209
                continue
210
            for (const callback of callbacks) {
211
                this.printEventImpl(callback)
212
            }
213
        }
214
        for (const [name, callbacks] of groupedCallbacks) {
215
            if (!this.library.shouldGenerateComponent(name))
216
                continue
217
            this.printReceiver(name, callbacks)
218
        }
219
        this.printReceiversList(groupedCallbacks)
220
    }
221
}
222

223
class TSEventsVisitor {
224
    readonly printer: LanguageWriter = new TSLanguageWriter(new IndentedPrinter())
225

226
    constructor(
227
        private readonly library: PeerLibrary,
228
    ) {}
229

230
    private printImports() {
231
        const imports = new ImportsCollector()
232
        for (const file of this.library.files) {
233
            file.importFeatures.forEach(it => imports.addFeature(it.feature, it.module))
234
        }
235
        imports.print(this.printer)
236
    }
237

238
    private printEventsClasses(infos: CallbackInfo[]) {
239
        for (const info of infos) {
240
            const eventClassName = callbackEventNameByInfo(info)
241
            this.printer.writeInterface(eventClassName, (writer) => {
242
                writer.writeFieldDeclaration(
243
                    'kind',
244
                    new Type(`${PeerEventKind}.${callbackIdByInfo(info)}`, false),
245
                    ["readonly"],
246
                    false,
247
                )
248
                info.args.forEach(arg => {
249
                    writer.writeFieldDeclaration(
250
                        arg.name,
251
                        new Type(mapType(arg.type), arg.nullable),
252
                        ["readonly"],
253
                        arg.nullable,
254
                    )
255
                })
256
            }, ['PeerEvent'])
257
        }
258
    }
259

260
    private printEventsEnum(infos: CallbackInfo[]) {
261
        this.printer.print(`export enum ${PeerEventKind} {`)
262
        this.printer.pushIndent()
263

264
        infos.forEach((value, index) => {
265
            this.printer.print(`${callbackIdByInfo(value)} = ${index},`)
266
        })
267

268
        this.printer.popIndent()
269
        this.printer.print(`}`)
270
    }
271

272
    private printNameByKindRetriever(infos: CallbackInfo[]) {
273
        this.printer.print(`export function getEventNameByKind(kind: ${PeerEventKind}): string {`)
274
        this.printer.pushIndent()
275
        this.printer.print(`switch (kind) {`)
276
        this.printer.pushIndent()
277
        for (const info of infos) {
278
            this.printer.print(`case ${PeerEventKind}.${callbackIdByInfo(info)}: return "${callbackIdByInfo(info)}"`)
279
        }
280
        this.printer.popIndent()
281
        this.printer.print('}')
282
        this.printer.popIndent()
283
        this.printer.print('}')
284
    }
285

286
    private printParseFunction(infos: CallbackInfo[]) {
287
        this.printer.print(`export function deserializePeerEvent(eventDeserializer: Deserializer): PeerEvent {`)
288
        this.printer.pushIndent()
289
        this.printer.writeStatement(this.printer.makeAssign(
290
            'kind',
291
            new Type(PeerEventKind),
292
            new StringExpression(`eventDeserializer.readInt32()`),
293
            true,
294
        ))
295
        this.printer.writeStatement(this.printer.makeAssign(
296
            'nodeId',
297
            Type.Number,
298
            new StringExpression(`eventDeserializer.readInt32()`),
299
            true,
300
        ))
301

302
        this.printer.writeStatement(this.printer.makeMultiBranchCondition(infos.map(info => {
303
            // TODO wait until TS deserializer is uncomplited
304
            const constructorTypeArgs = [
305
                `kind?: number`,
306
                `nodeId?: ${PeerEventKind}`,
307
                ...info.args.map(arg => {
308
                    return `${arg.name}?: any`
309
                }),
310
            ]
311
            const constructorType = new Type(`{ ${constructorTypeArgs.join(', ')} }`)
312
            
313
            return {
314
                expr: this.printer.makeNaryOp('===', [
315
                    new StringExpression('kind'), 
316
                    new StringExpression(`${PeerEventKind}.${callbackIdByInfo(info)}`),
317
                ]),
318
                stmt: new BlockStatement([
319
                    this.printer.makeAssign(
320
                        `event`,
321
                        constructorType,
322
                        new StringExpression(`{}`),
323
                        true,
324
                    ),
325
                    this.printer.makeAssign(`event.kind`, undefined, new StringExpression(`kind`), false),
326
                    this.printer.makeAssign(`event.nodeId`, undefined, new StringExpression(`nodeId`), false),
327
                    ...info.args.map(arg => {
328
                        const convertor = this.library.declarationTable.typeConvertor(arg.name, arg.type, arg.nullable)
329
                        return convertor.convertorDeserialize('event', `event.${arg.name}`, this.printer)
330
                    }),
331
                    this.printer.makeReturn(this.printer.makeCast(
332
                        new StringExpression(`event`),
333
                        new Type(callbackEventNameByInfo(info)),
334
                    ))
335
                ], false),
336
            }
337
        }), 
338
            new BlockStatement([
339
                new ExpressionStatement(new StringExpression(`throw \`Unknown kind \${kind}\``))
340
            ], false)
341
        ))
342

343
        this.printer.popIndent()
344
        this.printer.print('}')
345
    }
346

347
    private printProperties(infos: CallbackInfo[]) {
348
        this.printer.writeInterface(PeerEventsProperties, writer => {
349
            for (const info of infos) {
350
                const signature = new NamedMethodSignature(
351
                    new Type('void'),
352
                    info.args.map(it => new Type(mapType(it.type))),
353
                    info.args.map(it => it.name),
354
                )
355
                writer.writeMethodDeclaration(callbackIdByInfo(info), signature)
356
            }
357
        })
358
    }
359

360
    print(): void {
361
        const callbacks = collectCallbacks(this.library)
362
            .filter(it => this.library.shouldGenerateComponent(it.componentName))
363
        this.printImports()
364
        this.printEventsClasses(callbacks)
365
        this.printEventsEnum(callbacks)
366
        this.printNameByKindRetriever(callbacks)
367
        this.printParseFunction(callbacks)
368
        this.printProperties(callbacks)
369
    }
370
}
371

372
export function printEvents(library: PeerLibrary): string {
373
    const visitor = new TSEventsVisitor(library)
374
    visitor.print()
375
    return makePeerEvents(visitor.printer.getOutput().join("\n"))
376
}
377

378
export function printEventsCArkoalaImpl(library: PeerLibrary): string {
379
    const visitor = new CEventsVisitor(library, false)
380
    visitor.print()
381
    return makeCEventsArkoalaImpl(
382
        visitor.impl,
383
        visitor.receiversList,
384
    )
385
}
386

387
export function printEventsCLibaceImpl(library: PeerLibrary, options: { namespace: string}): string {
388
    const visitor = new CEventsVisitor(library, true)
389
    visitor.print()
390
    return makeCEventsLibaceImpl(
391
        visitor.impl,
392
        visitor.receiversList,
393
        options.namespace,
394
    )
395
}

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

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

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

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