idlize
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
16import * as ts from "typescript"
17import { IndentedPrinter } from "../../IndentedPrinter"
18import { DeclarationTable, DeclarationTarget, PrimitiveType } from "../DeclarationTable"
19import { BlockStatement, CppLanguageWriter, ExpressionStatement, LanguageWriter, Method, NamedMethodSignature, StringExpression, TSLanguageWriter, Type } from "../LanguageWriters"
20import { PeerClassBase } from "../PeerClass"
21import { PeerLibrary } from "../PeerLibrary"
22import { PeerMethod } from "../PeerMethod"
23import { makeCEventsArkoalaImpl, makeCEventsLibaceImpl, makePeerEvents } from "../FileGenerators"
24import { generateEventReceiverName, generateEventSignature } from "./HeaderPrinter"
25import { Language, asString, identName } from "../../util"
26import { mapType } from "../TypeNodeNameConvertor"
27import { PeerGeneratorConfig } from "../PeerGeneratorConfig"
28import { ImportsCollector } from "../ImportsCollector"
29
30export const PeerEventsProperties = "PeerEventsProperties"
31export const PeerEventKind = "PeerEventKind"
32
33export type CallbackInfo = {
34componentName: string,
35methodName: string,
36args: {name: string, type: ts.TypeNode, nullable: boolean}[],
37returnType: ts.TypeNode,
38}
39
40export function generateEventsBridgeSignature(language: Language): Method {
41let signature: NamedMethodSignature
42switch (language) {
43case Language.JAVA:
44case Language.ARKTS:
45case Language.TS:
46signature = new NamedMethodSignature(
47new Type(`KInt`),
48[new Type(`Uint8Array`), new Type(`KInt`)],
49[`result`, `size`],
50)
51break;
52case Language.CPP:
53signature = new NamedMethodSignature(
54new Type(`KInt`),
55[new Type(`KUint*`), new Type(`KInt`)],
56[`result`, `size`],
57)
58break;
59default:
60throw new Error("Not implemented")
61}
62return new Method(`CheckArkoalaGeneratedEvents`, signature)
63}
64
65export function groupCallbacks(callbacks: CallbackInfo[]): Map<string, CallbackInfo[]> {
66const receiverToCallbacks = new Map<string, CallbackInfo[]>()
67for (const callback of callbacks) {
68if (!receiverToCallbacks.has(callback.componentName))
69receiverToCallbacks.set(callback.componentName, [callback])
70else
71receiverToCallbacks.get(callback.componentName)!.push(callback)
72}
73return receiverToCallbacks
74}
75
76export function collectCallbacks(library: PeerLibrary): CallbackInfo[] {
77let callbacks: CallbackInfo[] = []
78for (const file of library.files) {
79for (const peer of file.peers.values()) {
80for (const method of peer.methods) {
81for (const target of method.declarationTargets) {
82const info = convertToCallback(peer, method, target)
83if (info && canProcessCallback(library.declarationTable, info))
84callbacks.push(info)
85}
86}
87}
88}
89return callbacks
90}
91
92export function canProcessCallback(declarationTable: DeclarationTable, callback: CallbackInfo): boolean {
93if (PeerGeneratorConfig.invalidEvents.includes(callback.methodName))
94return false
95return true
96}
97
98export function convertToCallback(peer: PeerClassBase, method: PeerMethod, target: DeclarationTarget): CallbackInfo | undefined {
99if (target instanceof PrimitiveType)
100return undefined
101if (ts.isFunctionTypeNode(target))
102return {
103componentName: peer.getComponentName(),
104methodName: method.method.name,
105args: target.parameters.map(it => {return {
106name: asString(it.name),
107type: it.type!,
108nullable: !!it.questionToken
109}}),
110returnType: target.type,
111}
112if (ts.isTypeReferenceNode(target) && identName(target.typeName) === "Callback") {
113const data = target.typeArguments![0]
114const hasData = data.kind !== ts.SyntaxKind.VoidKeyword
115return {
116componentName: peer.getComponentName(),
117methodName: method.method.name,
118args: hasData ? [{name: 'data', type: data, nullable: false}] : [],
119returnType: target.typeArguments![1] ?? ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)
120}
121}
122}
123
124export function callbackIdByInfo(info: CallbackInfo): string {
125return `${info.componentName}_${info.methodName}`
126}
127
128export function callbackEventNameByInfo(info: CallbackInfo): string {
129return `${callbackIdByInfo(info)}_event`
130}
131
132class CEventsVisitor {
133readonly impl: LanguageWriter = new CppLanguageWriter(new IndentedPrinter())
134readonly receiversList: LanguageWriter = new CppLanguageWriter(new IndentedPrinter())
135
136constructor(
137private readonly library: PeerLibrary,
138private readonly isEmptyImplementation: boolean,
139) {}
140
141private printEventsKinds(callbacks: CallbackInfo[]) {
142if (this.isEmptyImplementation)
143return
144this.impl.print(`enum ${PeerEventKind} {`)
145this.impl.pushIndent()
146callbacks.forEach((callback, index) => {
147this.impl.print(`Kind${callbackIdByInfo(callback)} = ${index},`)
148})
149this.impl.popIndent()
150this.impl.print('};\n')
151}
152
153private printEventImpl(event: CallbackInfo) {
154const signature = generateEventSignature(this.library.declarationTable, event)
155const args = signature.args.map((type, index) => {
156return `${type.name} ${signature.argName(index)}`
157})
158this.impl.print(`${signature.returnType.name} ${callbackIdByInfo(event)}Impl(${args.join(',')}) {`)
159this.impl.pushIndent()
160if (this.isEmptyImplementation) {
161this.impl.print("// GENERATED EMPTY IMPLEMENTATION")
162} else {
163this.impl.print(`EventBuffer _eventBuffer;`)
164this.impl.print(`Serializer _eventBufferSerializer(_eventBuffer.buffer);`)
165this.impl.print(`_eventBufferSerializer.writeInt32(Kind${callbackIdByInfo(event)});`)
166this.impl.print(`_eventBufferSerializer.writeInt32(nodeId);`)
167for (const arg of event.args) {
168const convertor = this.library.declarationTable.typeConvertor(arg.name, arg.type, arg.nullable)
169convertor.convertorSerialize(`_eventBuffer`, arg.name, this.impl)
170}
171this.impl.print(`sendEvent(&_eventBuffer);`)
172}
173this.impl.popIndent()
174this.impl.print('}')
175}
176
177private printReceiver(componentName: string, callbacks: CallbackInfo[]) {
178const receiver = generateEventReceiverName(componentName)
179this.impl.print(`const ${receiver}* Get${componentName}EventsReceiver() {`)
180this.impl.pushIndent()
181this.impl.print(`static const ${receiver} ${receiver}Impl {`)
182this.impl.pushIndent()
183for (const callback of callbacks) {
184this.impl.print(`${callbackIdByInfo(callback)}Impl,`)
185}
186this.impl.popIndent()
187this.impl.print(`};\n`)
188
189this.impl.print(`return &${receiver}Impl;`)
190this.impl.popIndent()
191this.impl.print(`}`)
192}
193
194private printReceiversList(callbacks: Map<string, CallbackInfo[]>) {
195for (const componentName of callbacks.keys()) {
196if (this.library.shouldGenerateComponent(componentName))
197this.receiversList.print(`Get${componentName}EventsReceiver,`)
198else
199this.receiversList.print(`nullptr,`)
200}
201}
202
203print() {
204const listedCallbacks = collectCallbacks(this.library)
205const groupedCallbacks = groupCallbacks(listedCallbacks)
206this.printEventsKinds(listedCallbacks)
207for (const [name, callbacks] of groupedCallbacks) {
208if (!this.library.shouldGenerateComponent(name))
209continue
210for (const callback of callbacks) {
211this.printEventImpl(callback)
212}
213}
214for (const [name, callbacks] of groupedCallbacks) {
215if (!this.library.shouldGenerateComponent(name))
216continue
217this.printReceiver(name, callbacks)
218}
219this.printReceiversList(groupedCallbacks)
220}
221}
222
223class TSEventsVisitor {
224readonly printer: LanguageWriter = new TSLanguageWriter(new IndentedPrinter())
225
226constructor(
227private readonly library: PeerLibrary,
228) {}
229
230private printImports() {
231const imports = new ImportsCollector()
232for (const file of this.library.files) {
233file.importFeatures.forEach(it => imports.addFeature(it.feature, it.module))
234}
235imports.print(this.printer)
236}
237
238private printEventsClasses(infos: CallbackInfo[]) {
239for (const info of infos) {
240const eventClassName = callbackEventNameByInfo(info)
241this.printer.writeInterface(eventClassName, (writer) => {
242writer.writeFieldDeclaration(
243'kind',
244new Type(`${PeerEventKind}.${callbackIdByInfo(info)}`, false),
245["readonly"],
246false,
247)
248info.args.forEach(arg => {
249writer.writeFieldDeclaration(
250arg.name,
251new Type(mapType(arg.type), arg.nullable),
252["readonly"],
253arg.nullable,
254)
255})
256}, ['PeerEvent'])
257}
258}
259
260private printEventsEnum(infos: CallbackInfo[]) {
261this.printer.print(`export enum ${PeerEventKind} {`)
262this.printer.pushIndent()
263
264infos.forEach((value, index) => {
265this.printer.print(`${callbackIdByInfo(value)} = ${index},`)
266})
267
268this.printer.popIndent()
269this.printer.print(`}`)
270}
271
272private printNameByKindRetriever(infos: CallbackInfo[]) {
273this.printer.print(`export function getEventNameByKind(kind: ${PeerEventKind}): string {`)
274this.printer.pushIndent()
275this.printer.print(`switch (kind) {`)
276this.printer.pushIndent()
277for (const info of infos) {
278this.printer.print(`case ${PeerEventKind}.${callbackIdByInfo(info)}: return "${callbackIdByInfo(info)}"`)
279}
280this.printer.popIndent()
281this.printer.print('}')
282this.printer.popIndent()
283this.printer.print('}')
284}
285
286private printParseFunction(infos: CallbackInfo[]) {
287this.printer.print(`export function deserializePeerEvent(eventDeserializer: Deserializer): PeerEvent {`)
288this.printer.pushIndent()
289this.printer.writeStatement(this.printer.makeAssign(
290'kind',
291new Type(PeerEventKind),
292new StringExpression(`eventDeserializer.readInt32()`),
293true,
294))
295this.printer.writeStatement(this.printer.makeAssign(
296'nodeId',
297Type.Number,
298new StringExpression(`eventDeserializer.readInt32()`),
299true,
300))
301
302this.printer.writeStatement(this.printer.makeMultiBranchCondition(infos.map(info => {
303// TODO wait until TS deserializer is uncomplited
304const constructorTypeArgs = [
305`kind?: number`,
306`nodeId?: ${PeerEventKind}`,
307...info.args.map(arg => {
308return `${arg.name}?: any`
309}),
310]
311const constructorType = new Type(`{ ${constructorTypeArgs.join(', ')} }`)
312
313return {
314expr: this.printer.makeNaryOp('===', [
315new StringExpression('kind'),
316new StringExpression(`${PeerEventKind}.${callbackIdByInfo(info)}`),
317]),
318stmt: new BlockStatement([
319this.printer.makeAssign(
320`event`,
321constructorType,
322new StringExpression(`{}`),
323true,
324),
325this.printer.makeAssign(`event.kind`, undefined, new StringExpression(`kind`), false),
326this.printer.makeAssign(`event.nodeId`, undefined, new StringExpression(`nodeId`), false),
327...info.args.map(arg => {
328const convertor = this.library.declarationTable.typeConvertor(arg.name, arg.type, arg.nullable)
329return convertor.convertorDeserialize('event', `event.${arg.name}`, this.printer)
330}),
331this.printer.makeReturn(this.printer.makeCast(
332new StringExpression(`event`),
333new Type(callbackEventNameByInfo(info)),
334))
335], false),
336}
337}),
338new BlockStatement([
339new ExpressionStatement(new StringExpression(`throw \`Unknown kind \${kind}\``))
340], false)
341))
342
343this.printer.popIndent()
344this.printer.print('}')
345}
346
347private printProperties(infos: CallbackInfo[]) {
348this.printer.writeInterface(PeerEventsProperties, writer => {
349for (const info of infos) {
350const signature = new NamedMethodSignature(
351new Type('void'),
352info.args.map(it => new Type(mapType(it.type))),
353info.args.map(it => it.name),
354)
355writer.writeMethodDeclaration(callbackIdByInfo(info), signature)
356}
357})
358}
359
360print(): void {
361const callbacks = collectCallbacks(this.library)
362.filter(it => this.library.shouldGenerateComponent(it.componentName))
363this.printImports()
364this.printEventsClasses(callbacks)
365this.printEventsEnum(callbacks)
366this.printNameByKindRetriever(callbacks)
367this.printParseFunction(callbacks)
368this.printProperties(callbacks)
369}
370}
371
372export function printEvents(library: PeerLibrary): string {
373const visitor = new TSEventsVisitor(library)
374visitor.print()
375return makePeerEvents(visitor.printer.getOutput().join("\n"))
376}
377
378export function printEventsCArkoalaImpl(library: PeerLibrary): string {
379const visitor = new CEventsVisitor(library, false)
380visitor.print()
381return makeCEventsArkoalaImpl(
382visitor.impl,
383visitor.receiversList,
384)
385}
386
387export function printEventsCLibaceImpl(library: PeerLibrary, options: { namespace: string}): string {
388const visitor = new CEventsVisitor(library, true)
389visitor.print()
390return makeCEventsLibaceImpl(
391visitor.impl,
392visitor.receiversList,
393options.namespace,
394)
395}