idlize

Форк
0
358 строк · 15.9 Кб
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 path from "path"
17
import { IndentedPrinter } from "../../IndentedPrinter";
18
import { EnumEntity, PeerFile } from "../PeerFile";
19
import { PeerLibrary } from "../PeerLibrary";
20
import { Language, isStatic, renameDtsToPeer, throwException } from "../../util";
21
import { ImportsCollector } from "../ImportsCollector";
22
import { PeerClass, PeerClassBase } from "../PeerClass";
23
import { InheritanceRole, determineParentRole, isHeir, isRoot, isStandalone } from "../inheritance";
24
import { PeerMethod } from "../PeerMethod";
25
import {
26
    LanguageExpression,
27
    LanguageWriter,
28
    Method,
29
    MethodModifier,
30
    MethodSignature,
31
    NamedMethodSignature,
32
    Type,
33
    createLanguageWriter
34
} from "../LanguageWriters";
35
import { MaterializedMethod } from "../Materialized";
36
import { collectDtsImports } from "../DtsImportsGenerator";
37
import { tsCopyrightAndWarning } from "../FileGenerators";
38

39
export function componentToPeerClass(component: string) {
40
    return `Ark${component}Peer`
41
}
42

43
function componentToAttributesClass(component: string) {
44
    return `Ark${component}Attributes`
45
}
46

47
class PeerFileVisitor {
48
    readonly printer: LanguageWriter = createLanguageWriter(this.file.declarationTable.language)
49
    //TODO: Ignore until bugs are fixed in https://rnd-gitlab-msc.huawei.com/rus-os-team/virtual-machines-and-tools/panda/-/issues/17850
50
    private static readonly ArkTsIgnoredMethods = ["testTupleNumberStringEnum", "testTupleOptional", "testTupleUnion"]
51

52
    // Temporary, until other languages supported.
53
    private isTs = this.file.declarationTable.language == Language.TS
54
    private isArkTs = this.file.declarationTable.language == Language.ARKTS
55

56
    constructor(
57
        private readonly library: PeerLibrary,
58
        private readonly file: PeerFile,
59
        private readonly dumpSerialized: boolean,
60
    ) { }
61

62
    get targetBasename() {
63
        return renameDtsToPeer(path.basename(this.file.originalFilename), this.file.declarationTable.language)
64
    }
65

66
    private generatePeerParentName(peer: PeerClass): string {
67
        if (!peer.originalClassName)
68
            throw new Error(`${peer.componentName} is not supported, use 'uselessConstructorInterfaces' for now`)
69
        const parentRole = determineParentRole(peer.originalClassName, peer.parentComponentName)
70
        if ([InheritanceRole.Finalizable, InheritanceRole.PeerNode].includes(parentRole)) {
71
            return InheritanceRole[parentRole]
72
        }
73
        const parent = peer.parentComponentName ?? throwException(`Expected component to have parent`)
74
        return componentToPeerClass(parent)
75
    }
76

77
    private generateAttributesParentClass(peer: PeerClass): string | undefined {
78
        if (!isHeir(peer.originalClassName!)) return undefined
79
        return componentToAttributesClass(peer.parentComponentName!)
80
    }
81

82
    private printImports(): void {
83
        this.getDefaultPeerImports(this.file.declarationTable.language)!.forEach(it => this.printer.print(it))
84
        if (this.file.declarationTable.language == Language.JAVA) {
85
            return
86
        }
87

88
        const imports = new ImportsCollector()
89
        imports.addFilterByBasename(this.targetBasename)
90
        this.file.peersToGenerate.forEach(peer => {
91
            if (determineParentRole(peer.originalClassName, peer.parentComponentName) === InheritanceRole.PeerNode)
92
                imports.addFeatureByBasename('PeerNode', 'PeerNode')
93
            if (peer.originalParentFilename) {
94
                const parentBasename = renameDtsToPeer(path.basename(peer.originalParentFilename), this.file.declarationTable.language)
95
                imports.addFeatureByBasename(this.generatePeerParentName(peer), parentBasename)
96
                const parentAttributesClass = this.generateAttributesParentClass(peer)
97
                if (parentAttributesClass)
98
                    imports.addFeatureByBasename(parentAttributesClass, parentBasename)
99
            }
100
        })
101
        if (this.file.declarationTable.language === Language.TS
102
            || this.file.declarationTable.language === Language.ARKTS) {
103
            this.file.importFeatures.forEach(it => imports.addFeature(it.feature, it.module))
104
            this.file.serializeImportFeatures.forEach(it => imports.addFeature(it.feature, it.module))
105
            imports.addFeature('GestureName', './generated-utils')
106
            imports.addFeature('GestureComponent', './generated-utils')
107
        }
108
        imports.addFeature("unsafeCast", "./generated-utils")
109
        imports.addFeature("registerCallback", "./SerializerBase")
110
        Array.from(this.library.builderClasses.keys())
111
            .forEach((className) => imports.addFeature(className, `./Ark${className}Builder`))
112
        imports.print(this.printer)
113
    }
114

115
    private printAttributes(peer: PeerClass) {
116
        if (!(this.isTs||this.isArkTs)) return
117
        for (const attributeType of peer.attributesTypes)
118
            this.printer.print(attributeType.content)
119

120
        const parent = this.generateAttributesParentClass(peer)
121
        this.printer.writeInterface(componentToAttributesClass(peer.componentName), (writer) => {
122
            for (const field of peer.attributesFields)
123
                writer.print(field)
124
        }, parent ? [parent] : undefined)
125
    }
126

127
    private printPeerConstructor(peer: PeerClass): void {
128
        // TODO: fully switch to writer!
129
        const printer = this.printer
130
        const parentRole = determineParentRole(peer.originalClassName, peer.originalParentName)
131
        const isNode = parentRole !== InheritanceRole.Finalizable
132
        const signature = new NamedMethodSignature(
133
            Type.Void,
134
            [new Type('ArkUINodeType', !isNode), new Type('ComponentBase', true), new Type('int32')],
135
            ['type', 'component', 'flags'],
136
            [undefined, undefined, '0'])
137

138
        printer.writeConstructorImplementation(componentToPeerClass(peer.componentName), signature, (writer) => {
139
            if (parentRole === InheritanceRole.PeerNode) {
140
                writer.writeSuperCall([`type`, 'flags'])
141
                writer.writeMethodCall('component', 'setPeer', ['this'], true)
142
            } else if (parentRole === InheritanceRole.Heir || parentRole === InheritanceRole.Root) {
143
                writer.writeSuperCall([`type`, 'component', 'flags'])
144
            } else {
145
                throwException(`Unexpected parent inheritance role: ${parentRole}`)
146
            }
147
        })
148
    }
149

150
    private printPeerMethod(method: PeerMethod) {
151
        this.library.declarationTable.setCurrentContext(`${method.originalParentName}.${method.overloadedName}`)
152
        writePeerMethod(this.printer, method, this.dumpSerialized, "Attribute", "this.peer.ptr")
153
        this.library.declarationTable.setCurrentContext(undefined)
154
    }
155

156
    private printApplyMethod(peer: PeerClass) {
157
        if (!(this.isTs||this.isArkTs)) return
158
        const name = peer.originalClassName!
159
        const typeParam = componentToAttributesClass(peer.componentName)
160
        if (isRoot(name)) {
161
            this.printer.print(`applyAttributes(attributes: ${typeParam}): void {}`)
162
            return
163
        }
164

165
        this.printer.print(`applyAttributes<T extends ${typeParam}>(attributes: T): void {`)
166
        this.printer.pushIndent()
167
        this.printer.print(`super.applyAttributes(attributes)`)
168
        this.printer.popIndent()
169
        this.printer.print(`}`)
170
    }
171

172
    private printPeer(peer: PeerClass) {
173
        this.printer.writeClass(componentToPeerClass(peer.componentName), (writer) => {
174
            this.printPeerConstructor(peer)
175
            peer.methods.filter((method) =>
176
                writer.language == Language.ARKTS ? !PeerFileVisitor.ArkTsIgnoredMethods.includes(method.overloadedName) : true
177
            ).forEach((method) => this.printPeerMethod(method))
178
            this.printApplyMethod(peer)
179
        }, this.generatePeerParentName(peer))
180
    }
181

182
    printFile(): void {
183
        this.printImports()
184
        this.file.peersToGenerate.forEach(peer => {
185
            this.printPeer(peer)
186
            this.printAttributes(peer)
187
        })
188
    }
189

190
    private getDefaultPeerImports(lang: Language) {
191
        switch(lang) {
192
            case Language.TS: {
193
                return [
194
                    `import { int32 } from "@koalaui/common"`,
195
                    `import { nullptr, KPointer } from "@koalaui/interop"`,
196
                    `import { isPixelMap, isResource, isInstanceOf, runtimeType, RuntimeType, SerializerBase } from "./SerializerBase"`,
197
                    `import { createSerializer, Serializer } from "./Serializer"`,
198
                    `import { nativeModule } from "@koalaui/arkoala"`,
199
                    `import { ArkUINodeType } from "./ArkUINodeType"`,
200
                    `import { ComponentBase } from "./ComponentBase"`,
201
                ]
202
            }
203
            case Language.ARKTS: {
204
                return [
205
                    `import { int32 } from "@koalaui/common"`,
206
                    `import { nullptr, KPointer } from "@koalaui/interop"`,
207
                    `import { isPixelMap, isResource, isInstanceOf, runtimeType, RuntimeType, SerializerBase } from "./SerializerBase"`,
208
                    `import { createSerializer, Serializer } from "./Serializer"`,
209
                    `import { ArkUINodeType } from "./ArkUINodeType"`,
210
                    `import { ComponentBase } from "./ComponentBase"`,
211
                    `import { NativeModule } from "./NativeModule"`,
212
                    `${collectDtsImports().trim()}`
213
                ]
214
            }
215
            case Language.JAVA: {
216
                return [
217
                    "import org.koalaui.arkoala.*;"
218
                ]
219
            }
220
        }
221
    }
222
}
223

224
class PeersVisitor {
225
    readonly peers: Map<string, string[]> = new Map()
226

227
    constructor(
228
        private readonly library: PeerLibrary,
229
        private readonly dumpSerialized: boolean,
230
    ) { }
231

232
    printPeers(): void {
233
        for (const file of this.library.files.values()) {
234
            if (file.peersToGenerate.length) {
235
                const visitor = new PeerFileVisitor(this.library, file, this.dumpSerialized)
236
                visitor.printFile()
237
                this.peers.set(visitor.targetBasename, visitor.printer.getOutput())
238
            }
239
        }
240
    }
241
}
242

243
const returnValName = "retval"  // make sure this doesn't collide with parameter names!
244

245
export function printPeers(peerLibrary: PeerLibrary, dumpSerialized: boolean): Map<string, string> {
246
    const visitor = new PeersVisitor(peerLibrary, dumpSerialized)
247
    visitor.printPeers()
248
    const result = new Map<string, string>()
249
    for (const [key, content] of visitor.peers) {
250
        if (content.length === 0) continue
251
        const text = tsCopyrightAndWarning(content.join('\n'))
252
        result.set(key, text)
253
    }
254
    return result
255
}
256

257
export function printPeerFinalizer(peerClassBase: PeerClassBase, writer: LanguageWriter): void {
258
    const className = peerClassBase.getComponentName()
259
    const finalizer = new Method(
260
        "getFinalizer",
261
        new MethodSignature(Type.Pointer, []),
262
        // TODO: private static getFinalizer() method conflicts with its implementation in the parent class
263
        [MethodModifier.STATIC])
264
    writer.writeMethodImplementation(finalizer, writer => {
265
        writer.writeStatement(
266
            writer.makeReturn(
267
                writer.makeMethodCall("nativeModule()", `_${className}_getFinalizer`, [])))
268
    })
269
}
270

271
export function writePeerMethod(printer: LanguageWriter, method: PeerMethod, dumpSerialized: boolean,
272
                                methodPostfix: string, ptr: string, returnType: Type = Type.Void, generics?: string[]) {
273
    // Not yet!
274
    if (printer.language != Language.TS && printer.language != Language.ARKTS) return
275
    const signature = method.method.signature as NamedMethodSignature
276
    let peerMethod = new Method(
277
        `${method.overloadedName}${methodPostfix}`,
278
        new NamedMethodSignature(returnType, signature.args, signature.argsNames),
279
        method.method.modifiers, method.method.generics)
280
    printer.writeMethodImplementation(peerMethod, (writer) => {
281
        let scopes = method.argConvertors.filter(it => it.isScoped)
282
        scopes.forEach(it => {
283
            writer.pushIndent()
284
            writer.print(it.scopeStart?.(it.param, printer.language))
285
        })
286
        let serializerCreated = false
287
        method.argConvertors.forEach((it, index) => {
288
            if (it.useArray) {
289
                if (!serializerCreated) {
290
                    writer.writeStatement(
291
                        writer.makeAssign(`thisSerializer`, new Type('Serializer'),
292
                            writer.makeMethodCall('SerializerBase', 'get', [
293
                                writer.makeString('createSerializer'), writer.makeString(index.toString())
294
                            ]), true)
295
                    )
296
                    serializerCreated = true
297
                }
298
                it.convertorSerialize(`this`, it.param, writer)
299
            }
300
        })
301
        // Enable to see serialized data.
302
        if (dumpSerialized) {
303
            let arrayNum = 0
304
            method.argConvertors.forEach((it, index) => {
305
                if (it.useArray) {
306
                    writer.writePrintLog(`"${it.param}:", thisSerializer.asArray(), thisSerializer.length())`)
307
                }
308
            })
309
        }
310
        let params: LanguageExpression[] = []
311
        if (method.hasReceiver()) {
312
            params.push(writer.makeString(ptr))
313
        }
314
        let serializerPushed = false
315
        method.argConvertors.forEach(it => {
316
            if (it.useArray) {
317
                if (!serializerPushed) {
318
                    params.push(writer.makeMethodCall(`thisSerializer`, 'asArray', []))
319
                    params.push(writer.makeMethodCall(`thisSerializer`, 'length', []))
320
                    serializerPushed = true
321
                }
322
            } else {
323
                params.push(writer.makeString(it.convertorArg(it.param, writer)))
324
            }
325
        })
326
        let call = writer.makeNativeCall(
327
            `_${method.originalParentName}_${method.overloadedName}`,
328
            params)
329
        if (returnType != Type.Void) {
330
            writer.writeStatement(writer.makeAssign(returnValName, undefined, call, true))
331
        } else {
332
            writer.writeStatement(writer.makeStatement(call))
333
        }
334
        scopes.reverse().forEach(it => {
335
            writer.popIndent()
336
            writer.print(it.scopeEnd!(it.param, writer.language))
337
        })
338
        // TODO: refactor
339
        if (returnType != Type.Void) {
340
            let result = returnValName
341
            if (method.hasReceiver() && returnType === Type.This) {
342
                result = `this`
343
            } else if (method instanceof MaterializedMethod && method.peerMethodName !== "ctor") {
344
                const isStatic = method.method.modifiers?.includes(MethodModifier.STATIC)
345
                if (!method.hasReceiver()) {
346
                    const retType = signature.returnType
347
                    const obj = `new ${retType.name}(${signature.argsNames.map(it => "undefined").join(", ")})`
348
                    writer.writeStatement(writer.makeAssign("obj", retType, writer.makeString(obj), true))
349
                    writer.writeStatement(
350
                        writer.makeAssign("obj.peer", new Type("Finalizable"),
351
                            writer.makeString(`new Finalizable(${returnValName}, ${method.originalParentName}.getFinalizer())`), false))
352
                    result = "obj"
353
                }
354
            }
355
            writer.writeStatement(writer.makeReturn(writer.makeString(result)))
356
        }
357
    })
358
}

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

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

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

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