idlize
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
16import { UndefinedConvertor, UnionRuntimeTypeChecker } from "../Convertors"
17import { Method, MethodSignature, Type, LanguageWriter, MethodModifier, ExpressionStatement, StringExpression, NamedMethodSignature } from "../LanguageWriters";
18import { PeerClass, PeerClassBase } from "../PeerClass";
19import { PeerMethod } from "../PeerMethod";
20import { isDefined } from "../../util";
21import { callbackIdByInfo, canProcessCallback, convertToCallback } from "./EventsPrinter";
22import { PeerLibrary } from "../PeerLibrary";
23
24export function collapseSameNamedMethods(methods: Method[]): Method {
25if (methods.some(it => it.signature.defaults?.length))
26throw "Can not process defaults in collapsed method"
27const maxArgLength = Math.max(...methods.map(it => it.signature.args.length))
28const collapsedArgs: Type[] = Array.from({length: maxArgLength}, (_, argIndex) => {
29const name = methods.map(it => it.signature.args[argIndex]?.name).filter(isDefined).join(' | ')
30const optional = methods.some(it => it.signature.args[argIndex]?.nullable ?? true)
31return new Type(name, optional)
32})
33const maxMethod = methods.find(it => it.signature.args.length === maxArgLength)
34return new Method(
35methods[0].name,
36new NamedMethodSignature(
37methods[0].signature.returnType,
38collapsedArgs,
39(maxMethod?.signature as NamedMethodSignature).argsNames
40),
41methods[0].modifiers,
42methods[0].generics,
43)
44}
45
46export function groupOverloads(peerMethods: PeerMethod[]): PeerMethod[][] {
47const seenNames = new Set<string>()
48const groups: PeerMethod[][] = []
49for (const method of peerMethods) {
50if (seenNames.has(method.method.name))
51continue
52seenNames.add(method.method.name)
53groups.push(peerMethods.filter(it => it.method.name === method.method.name))
54}
55return groups
56}
57
58export class OverloadsPrinter {
59private static undefinedConvertor = new UndefinedConvertor("OverloadsPrinter")
60
61constructor(private printer: LanguageWriter, private library: PeerLibrary, private isComponent: boolean = true) {}
62
63printGroupedComponentOverloads(peer: PeerClassBase, peerMethods: PeerMethod[]) {
64const orderedMethods = Array.from(peerMethods)
65.sort((a, b) => b.argConvertors.length - a.argConvertors.length)
66const collapsedMethod = collapseSameNamedMethods(orderedMethods.map(it => it.method))
67if (this.isComponent) {
68this.printer.print(`/** @memo */`)
69}
70this.printer.writeMethodImplementation(collapsedMethod, (writer) => {
71if (this.isComponent) {
72writer.print(`if (this.checkPriority("${collapsedMethod.name}")) {`)
73this.printer.pushIndent()
74}
75if (orderedMethods.length > 1) {
76const runtimeTypeCheckers = collapsedMethod.signature.args.map((_, argIndex) => {
77const argName = collapsedMethod.signature.argName(argIndex)
78this.printer.print(`const ${argName}_type = runtimeType(${argName})`)
79return new UnionRuntimeTypeChecker(
80orderedMethods.map(m => m.argConvertors[argIndex] ?? OverloadsPrinter.undefinedConvertor))
81})
82orderedMethods.forEach((peerMethod, methodIndex) =>
83this.printComponentOverloadSelector(peer, collapsedMethod, peerMethod, methodIndex, runtimeTypeCheckers))
84writer.print(`throw "Can not select appropriate overload"`)
85} else {
86this.printPeerCallAndReturn(peer, collapsedMethod, orderedMethods[0])
87}
88if (this.isComponent) {
89this.printer.popIndent()
90this.printer.print(`}`)
91this.printer.print("return this")
92}
93})
94}
95
96printComponentOverloadSelector(peer: PeerClassBase, collapsedMethod: Method, peerMethod: PeerMethod, methodIndex: number, runtimeTypeCheckers: UnionRuntimeTypeChecker[]) {
97const argsConditions = collapsedMethod.signature.args.map((_, argIndex) =>
98runtimeTypeCheckers[argIndex].makeDiscriminator(collapsedMethod.signature.argName(argIndex), methodIndex, this.printer))
99this.printer.print(`if (${this.printer.makeNaryOp("&&", argsConditions).asString()}) {`)
100this.printer.pushIndent()
101this.printPeerCallAndReturn(peer, collapsedMethod, peerMethod)
102this.printer.popIndent()
103this.printer.print('}')
104}
105
106private printPeerCallAndReturn(peer: PeerClassBase, collapsedMethod: Method, peerMethod: PeerMethod) {
107const argsNames = peerMethod.argConvertors.map((conv, index) => {
108const argName = collapsedMethod.signature.argName(index)
109const castedArgName = `${argName}_casted`
110const castedType = peerMethod.method.signature.args[index].name
111this.printer.print(`const ${castedArgName} = ${argName} as (${castedType})`)
112return castedArgName
113})
114const isStatic = collapsedMethod.modifiers?.includes(MethodModifier.STATIC)
115const receiver = isStatic
116? peerMethod.originalParentName
117: this.isComponent ? `this.peer` : `this`
118const postfix = this.isComponent ? "Attribute" : "_serialize"
119const methodName = `${peerMethod.overloadedName}${postfix}`
120
121peerMethod.declarationTargets.map((target, index) => {
122if (this.isComponent) { // TBD: Check for materialized classes
123const callback = convertToCallback(peer, peerMethod, target)
124if (!callback || !canProcessCallback(this.library.declarationTable, callback))
125return
126const argName = argsNames[index]
127this.printer.writeStatement(new ExpressionStatement(this.printer.makeFunctionCall(`UseEventsProperties`,[
128new StringExpression(`{${callbackIdByInfo(callback)}: ${argName}}`)
129])))
130}
131})
132
133const returnType = collapsedMethod.signature.returnType
134if (returnType === Type.This || returnType === Type.Void) {
135this.printer.writeMethodCall(receiver, methodName, argsNames, !isStatic)
136if (returnType === Type.This) {
137this.printer.print(`return this`)
138}
139} else {
140this.printer.writeStatement(
141this.printer.makeReturn(
142this.printer.makeMethodCall(receiver, methodName,
143argsNames.map(it => this.printer.makeString(it)))
144))
145}
146}
147}