idlize

Форк
0
/
OverloadsPrinter.ts 
147 строк · 7.1 Кб
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 { UndefinedConvertor, UnionRuntimeTypeChecker } from "../Convertors"
17
import { Method, MethodSignature, Type, LanguageWriter, MethodModifier, ExpressionStatement, StringExpression, NamedMethodSignature } from "../LanguageWriters";
18
import { PeerClass, PeerClassBase } from "../PeerClass";
19
import { PeerMethod } from "../PeerMethod";
20
import { isDefined } from "../../util";
21
import { callbackIdByInfo, canProcessCallback, convertToCallback } from "./EventsPrinter";
22
import { PeerLibrary } from "../PeerLibrary";
23

24
export function collapseSameNamedMethods(methods: Method[]): Method {
25
    if (methods.some(it => it.signature.defaults?.length))
26
        throw "Can not process defaults in collapsed method"
27
    const maxArgLength = Math.max(...methods.map(it => it.signature.args.length))
28
    const collapsedArgs: Type[] = Array.from({length: maxArgLength}, (_, argIndex) => {
29
        const name = methods.map(it => it.signature.args[argIndex]?.name).filter(isDefined).join(' | ')
30
        const optional = methods.some(it => it.signature.args[argIndex]?.nullable ?? true)
31
        return new Type(name, optional)
32
    })
33
    const maxMethod = methods.find(it => it.signature.args.length === maxArgLength)
34
    return new Method(
35
        methods[0].name,
36
        new NamedMethodSignature(
37
            methods[0].signature.returnType,
38
            collapsedArgs,
39
            (maxMethod?.signature as NamedMethodSignature).argsNames
40
        ),
41
        methods[0].modifiers,
42
        methods[0].generics,
43
    )
44
}
45

46
export function groupOverloads(peerMethods: PeerMethod[]): PeerMethod[][] {
47
    const seenNames = new Set<string>()
48
    const groups: PeerMethod[][] = []
49
    for (const method of peerMethods) {
50
        if (seenNames.has(method.method.name))
51
            continue
52
        seenNames.add(method.method.name)
53
        groups.push(peerMethods.filter(it => it.method.name === method.method.name))
54
    }
55
    return groups
56
}
57

58
export class OverloadsPrinter {
59
    private static undefinedConvertor = new UndefinedConvertor("OverloadsPrinter")
60

61
    constructor(private printer: LanguageWriter, private library: PeerLibrary, private isComponent: boolean = true) {}
62

63
    printGroupedComponentOverloads(peer: PeerClassBase, peerMethods: PeerMethod[]) {
64
        const orderedMethods = Array.from(peerMethods)
65
            .sort((a, b) => b.argConvertors.length - a.argConvertors.length)
66
        const collapsedMethod = collapseSameNamedMethods(orderedMethods.map(it => it.method))
67
        if (this.isComponent) {
68
            this.printer.print(`/** @memo */`)
69
        }
70
        this.printer.writeMethodImplementation(collapsedMethod, (writer) => {
71
            if (this.isComponent) {
72
                writer.print(`if (this.checkPriority("${collapsedMethod.name}")) {`)
73
                this.printer.pushIndent()
74
            }
75
            if (orderedMethods.length > 1) {
76
                const runtimeTypeCheckers = collapsedMethod.signature.args.map((_, argIndex) => {
77
                    const argName = collapsedMethod.signature.argName(argIndex)
78
                    this.printer.print(`const ${argName}_type = runtimeType(${argName})`)
79
                    return new UnionRuntimeTypeChecker(
80
                        orderedMethods.map(m => m.argConvertors[argIndex] ?? OverloadsPrinter.undefinedConvertor))
81
                })
82
                orderedMethods.forEach((peerMethod, methodIndex) =>
83
                    this.printComponentOverloadSelector(peer, collapsedMethod, peerMethod, methodIndex, runtimeTypeCheckers))
84
                writer.print(`throw "Can not select appropriate overload"`)
85
            } else {
86
                this.printPeerCallAndReturn(peer, collapsedMethod, orderedMethods[0])
87
            }
88
            if (this.isComponent) {
89
                this.printer.popIndent()
90
                this.printer.print(`}`)
91
                this.printer.print("return this")
92
            }
93
        })
94
    }
95

96
    printComponentOverloadSelector(peer: PeerClassBase, collapsedMethod: Method, peerMethod: PeerMethod, methodIndex: number, runtimeTypeCheckers: UnionRuntimeTypeChecker[]) {
97
        const argsConditions = collapsedMethod.signature.args.map((_, argIndex) =>
98
            runtimeTypeCheckers[argIndex].makeDiscriminator(collapsedMethod.signature.argName(argIndex), methodIndex, this.printer))
99
        this.printer.print(`if (${this.printer.makeNaryOp("&&", argsConditions).asString()}) {`)
100
        this.printer.pushIndent()
101
        this.printPeerCallAndReturn(peer, collapsedMethod, peerMethod)
102
        this.printer.popIndent()
103
        this.printer.print('}')
104
    }
105

106
    private printPeerCallAndReturn(peer: PeerClassBase, collapsedMethod: Method, peerMethod: PeerMethod) {
107
        const argsNames = peerMethod.argConvertors.map((conv, index) => {
108
            const argName = collapsedMethod.signature.argName(index)
109
            const castedArgName = `${argName}_casted`
110
            const castedType = peerMethod.method.signature.args[index].name
111
            this.printer.print(`const ${castedArgName} = ${argName} as (${castedType})`)
112
            return castedArgName
113
        })
114
        const isStatic = collapsedMethod.modifiers?.includes(MethodModifier.STATIC)
115
        const receiver = isStatic
116
            ? peerMethod.originalParentName
117
            : this.isComponent ? `this.peer` : `this`
118
        const postfix = this.isComponent ? "Attribute" : "_serialize"
119
        const methodName = `${peerMethod.overloadedName}${postfix}`
120

121
        peerMethod.declarationTargets.map((target, index) => {
122
            if (this.isComponent) { // TBD: Check for materialized classes
123
                const callback = convertToCallback(peer, peerMethod, target)
124
                if (!callback || !canProcessCallback(this.library.declarationTable, callback))
125
                    return
126
                const argName = argsNames[index]
127
                this.printer.writeStatement(new ExpressionStatement(this.printer.makeFunctionCall(`UseEventsProperties`,[
128
                    new StringExpression(`{${callbackIdByInfo(callback)}: ${argName}}`)
129
                ])))
130
            }
131
        })
132

133
        const returnType = collapsedMethod.signature.returnType
134
        if (returnType === Type.This || returnType === Type.Void) {
135
            this.printer.writeMethodCall(receiver, methodName, argsNames, !isStatic)
136
            if (returnType === Type.This) {
137
                this.printer.print(`return this`)
138
            }
139
        } else {
140
            this.printer.writeStatement(
141
                this.printer.makeReturn(
142
                    this.printer.makeMethodCall(receiver, methodName,
143
                        argsNames.map(it => this.printer.makeString(it)))
144
                ))
145
        }
146
    }
147
}

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

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

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

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