idlize
344 строки · 11.5 Кб
1/*
2* Copyright (c) 2024 Huawei Device Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15
16import * as path from "path";
17import * as fs from "fs";
18import { IndentedPrinter } from "../../IndentedPrinter";
19import { DeclarationTable, DeclarationTarget, FieldRecord, PrimitiveType } from "../DeclarationTable";
20import { cStyleCopyright, completeDelegatesImpl, makeFileNameFromClassName, makeIncludeGuardDefine, warning } from "../FileGenerators";
21import { PeerLibrary } from "../PeerLibrary";
22import { MethodSeparatorVisitor, PeerMethod } from "../PeerMethod";
23import { PeerClass } from "../PeerClass";
24import { MaterializedClass } from "../Materialized";
25import { CppLanguageWriter, PrinterLike } from "../LanguageWriters";
26import { LibaceInstall } from "../../Install";
27
28export class DelegateSignatureBuilder {
29constructor(
30private readonly declarationTable: DeclarationTable,
31private readonly method: PeerMethod
32) {
33this.args = method.argConvertors.map((convertor, index) => {
34return [{
35argName: convertor.param,
36argType: convertor.nativeType(false),
37isPointerType: convertor.isPointerType(),
38}]
39})
40}
41
42private readonly args: {
43argName: string,
44argType: string,
45isPointerType: boolean,
46exists?: boolean,
47}[][]
48
49pushUnionScope(argIndex: number, field: FieldRecord): void {
50this.args[argIndex].push({
51argName: field.name,
52argType: this.declarationTable.computeTargetName(field.declaration, false),
53isPointerType: false,
54})
55}
56
57pushOptionScope(argIndex: number, target: DeclarationTarget, exists: boolean): void {
58const arg = this.args[argIndex]
59arg.push({
60argName: arg[arg.length - 1].argName,
61argType: this.declarationTable.computeTargetName(exists ? target : PrimitiveType.Undefined, false),
62isPointerType: false,
63exists: exists,
64})
65}
66
67popScope(argIndex: number): void {
68this.args[argIndex].pop()
69}
70
71buildIdentifier(): string {
72const argsPostfix = this.args
73.map(argStack => argStack.map(argStackItem => {
74return argStackItem.exists !== undefined
75? (argStackItem.exists ? "Def" : "Undef")
76: argStackItem.argType
77}))
78.map(argStack => argStack.join('_'))
79.join('__')
80return `${this.method.implName}__${argsPostfix}`
81}
82
83buildSignature(): string {
84let args = this.args
85.map(argStack => argStack[argStack.length - 1])
86.map((arg, index) => {
87return arg.isPointerType
88? `const ${arg.argType} *arg_${index}`
89: `const ${arg.argType} &arg_${index}`
90})
91if (this.method.hasReceiver()) {
92const receiver = this.method.generateReceiver()!
93args = [`${receiver.argType} ${receiver.argName}`, ...args]
94}
95return `${this.method.retType} ${this.buildIdentifier()}(${args.join(', ')})`
96}
97}
98
99class MethodDelegatePrinter extends MethodSeparatorVisitor {
100public readonly declPrinter = new IndentedPrinter()
101public readonly implPrinter = new IndentedPrinter()
102private delegateSignatureBuilder: DelegateSignatureBuilder
103private emittedSignatures = new Set();
104constructor(
105declarationTable: DeclarationTable,
106method: PeerMethod,
107) {
108super(declarationTable, method)
109this.delegateSignatureBuilder = new DelegateSignatureBuilder(declarationTable, method)
110}
111
112protected override onPushUnionScope(argIndex: number, field: FieldRecord, selectorValue: number): void {
113super.onPushUnionScope(argIndex, field, selectorValue)
114this.delegateSignatureBuilder!.pushUnionScope(argIndex, field)
115}
116
117protected override onPopUnionScope(argIndex: number): void {
118super.onPopUnionScope(argIndex)
119this.delegateSignatureBuilder.popScope(argIndex)
120}
121
122protected override onPushOptionScope(argIndex: number, target: DeclarationTarget, exists: boolean): void {
123super.onPushOptionScope(argIndex, target, exists)
124this.delegateSignatureBuilder.pushOptionScope(argIndex, target, exists)
125}
126
127protected override onPopOptionScope(argIndex: number): void {
128this.delegateSignatureBuilder.popScope(argIndex)
129}
130
131onVisitInseparable(): void {
132const signature = this.delegateSignatureBuilder.buildSignature()
133if (!this.emittedSignatures.has(signature)) {
134this.declPrinter.print(`${signature};`)
135const retStatement = this.method.retConvertor.isVoid ? "" :`return 0;`
136this.implPrinter.print(`${signature} { ${retStatement} }`)
137this.emittedSignatures.add(signature)
138}
139}
140}
141
142class DelegateVisitor {
143readonly api: IndentedPrinter = new IndentedPrinter()
144readonly impl: IndentedPrinter = new IndentedPrinter()
145
146constructor(
147private readonly library: PeerLibrary,
148) {}
149
150private printMethod(method: PeerMethod) {
151const visitor = new MethodDelegatePrinter(
152this.library.declarationTable,
153method,
154)
155visitor.visit()
156visitor.declPrinter.getOutput().forEach(it => this.api.print(it))
157visitor.implPrinter.getOutput().forEach(it => this.impl.print(it))
158}
159
160print(): void {
161for (const file of this.library.files) {
162for (const peer of file.peers.values()) {
163for (const method of peer.methods) {
164this.printMethod(method)
165}
166}
167}
168for (const materialized of this.library.materializedClasses.values()) {
169this.printMethod(materialized.ctor)
170this.printMethod(materialized.finalizer)
171for (const method of materialized.methods) {
172this.printMethod(method)
173}
174}
175}
176}
177
178export function printDelegatesHeaders(library: PeerLibrary): string {
179const visitor = new DelegateVisitor(library)
180visitor.api.print(`#pragma once
181
182#include "arkoala_api.h"
183`)
184visitor.print()
185// TODO here can be conflicts between different union filds with same types
186const uniqueDeclarations = Array.from(new Set(visitor.api.getOutput()))
187return uniqueDeclarations.join('\n')
188}
189
190export function printDelegatesImplementation(library: PeerLibrary): string {
191const visitor = new DelegateVisitor(library)
192visitor.print()
193// TODO here can be conflicts between different union filds with same types
194const uniqueDeclarations = Array.from(new Set(visitor.impl.getOutput()))
195return completeDelegatesImpl(uniqueDeclarations.join('\n'))
196}
197
198export function printDelegatesAsMultipleFiles(library: PeerLibrary, libace: LibaceInstall, options: DelegateFileOptions = {}) {
199const visitor = new MultiFileDelegateVisitor(library)
200visitor.print()
201visitor.emitSync(libace, options)
202}
203
204
205interface MultiFileDelegatePrinters {
206api: IndentedPrinter
207impl: IndentedPrinter
208}
209
210class MultiFileDelegateVisitor {
211private readonly printers: Map<string, MultiFileDelegatePrinters> = new Map();
212private api?: IndentedPrinter
213private impl?: IndentedPrinter
214
215constructor(
216private readonly library: PeerLibrary,
217) {}
218
219private printMethod(method: PeerMethod) {
220const visitor = new MethodDelegatePrinter(
221this.library.declarationTable,
222method,
223)
224visitor.visit()
225visitor.declPrinter.getOutput().forEach(it => this.api!.print(it))
226visitor.implPrinter.getOutput().forEach(it => this.impl!.print(it))
227}
228
229private onPeerStart(clazz: PeerClass) {
230let slug = makeFileNameFromClassName(clazz.componentName)
231this.pushPrinters(slug)
232}
233
234private onPeerEnd(_clazz: PeerClass) {
235this.api = this.impl = undefined
236}
237
238private onMaterializedClassStart(clazz: MaterializedClass) {
239let slug = makeFileNameFromClassName(clazz.className)
240this.pushPrinters(slug)
241}
242
243private onMaterializedClassEnd(_clazz: MaterializedClass) {
244this.api = this.impl = undefined
245}
246
247private pushPrinters(slug: string) {
248let printers = this.printers.get(slug)
249if (printers) {
250this.api = printers.api
251this.impl = printers.impl
252return
253}
254let api = this.api = new IndentedPrinter()
255let impl = this.impl = new IndentedPrinter()
256this.printers.set(slug, { api, impl })
257}
258
259print() {
260for (const file of this.library.files) {
261for (const peer of file.peers.values()) {
262this.onPeerStart(peer)
263for (const method of peer.methods) {
264this.printMethod(method)
265}
266this.onPeerEnd(peer)
267}
268}
269for (const materialized of this.library.materializedClasses.values()) {
270this.onMaterializedClassStart(materialized)
271this.printMethod(materialized.ctor)
272this.printMethod(materialized.finalizer)
273for (const method of materialized.methods) {
274this.printMethod(method)
275}
276this.onMaterializedClassEnd(materialized)
277}
278}
279
280emitSync(libace: LibaceInstall, options: DelegateFileOptions): void {
281for (const [slug, { api, impl }] of this.printers) {
282printDelegateImplementation(libace.delegateCpp(slug), impl, options);
283printDelegateHeader(libace.delegateHeader(slug), api, options);
284}
285}
286}
287
288export interface DelegateFileOptions {
289namespace?: string
290}
291
292function printDelegateImplementation(filePath: string, source: PrinterLike, options: DelegateFileOptions) {
293const writer = new CppLanguageWriter(new IndentedPrinter())
294writer.writeLines(cStyleCopyright)
295writer.writeMultilineCommentBlock(warning)
296writer.print("")
297
298
299const headerName = path.basename(filePath, ".cpp") + ".h"
300writer.writeInclude(`../generated/interface/${headerName}`)
301writer.print("")
302
303if (options.namespace) {
304writer.pushNamespace(options.namespace)
305}
306
307writer.concat(source)
308
309if (options.namespace) {
310writer.popNamespace()
311}
312
313writer.print("")
314writer.printTo(filePath)
315}
316
317
318function printDelegateHeader(filePath: string, source: PrinterLike, options: DelegateFileOptions) {
319const writer = new CppLanguageWriter(new IndentedPrinter())
320writer.writeLines(cStyleCopyright)
321writer.writeMultilineCommentBlock(warning)
322writer.print("")
323
324const includeGuardDefine = makeIncludeGuardDefine(filePath)
325writer.print(`#ifndef ${includeGuardDefine}`)
326writer.print(`#define ${includeGuardDefine}`)
327writer.print("")
328
329writer.writeInclude("arkoala_api_generated.h")
330writer.print("")
331
332if (options.namespace) {
333writer.pushNamespace(options.namespace)
334}
335
336writer.concat(source)
337
338if (options.namespace) {
339writer.popNamespace()
340}
341writer.print(`\n#endif // ${includeGuardDefine}`)
342writer.print("")
343writer.printTo(filePath)
344}
345