idlize
144 строки · 5.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 { IndentedPrinter } from "../IndentedPrinter";
17import * as ts from "typescript"
18import { asString, getDeclarationsByNode, getNameWithoutQualifiersRight, heritageTypes, stringOrNone } from "../util";
19
20export class SortingEmitter extends IndentedPrinter {
21currentPrinter?: IndentedPrinter
22emitters = new Map<string, IndentedPrinter>()
23deps = new Map<string, Set<string>>()
24
25constructor() {
26super()
27}
28
29private fillDeps(typeChecker: ts.TypeChecker, type: ts.TypeNode | undefined, seen: Set<string>) {
30if (!type) return
31if (ts.isTypeReferenceNode(type)) {
32if (seen.has(this.repr(type))) return
33seen.add(this.repr(type))
34let decls = getDeclarationsByNode(typeChecker, type.typeName)
35if (decls.length > 0) {
36let decl = decls[0]
37if (ts.isInterfaceDeclaration(decl)) {
38decl.members
39.filter(ts.isPropertySignature)
40.forEach(it => this.fillDeps(typeChecker, it.type, seen))
41decl.heritageClauses?.forEach(it => {
42heritageTypes(typeChecker, it).forEach(it => this.fillDeps(typeChecker, it, seen))
43})
44}
45if (ts.isClassDeclaration(decl)) {
46decl.members
47.filter(ts.isPropertyDeclaration)
48.forEach(it => this.fillDeps(typeChecker, it.type, seen))
49decl.heritageClauses?.forEach(it => {
50heritageTypes(typeChecker, it).forEach(it => this.fillDeps(typeChecker, it, seen))
51})
52}
53if (ts.isUnionTypeNode(decl)) {
54decl.types
55.forEach(it => this.fillDeps(typeChecker, it, seen))
56}
57/*
58if (ts.isTypeLiteralNode(decl)) {
59decl.members
60.filter(ts.isPropertyAssignment)
61.forEach(it => this.fillDeps(typeChecker, i, seen))
62} */
63} else {
64console.log(`no decl for ${asString(type.typeName)}`)
65}
66} else if (ts.isUnionTypeNode(type)) {
67type.types.forEach(it => this.fillDeps(typeChecker, it, seen))
68}
69}
70
71startEmit(typeChecker: ts.TypeChecker, type: ts.TypeNode, name: string | undefined = undefined) {
72const repr = this.repr(type, name)
73if (this.emitters.has(repr)) throw new Error(`Already emitted ${type.getText()}`)
74let next = new IndentedPrinter()
75let seen = new Set<string>()
76this.fillDeps(typeChecker, type, seen)
77seen.delete(repr)
78this.deps.set(repr, seen)
79this.emitters.set(repr, next)
80this.currentPrinter = next
81if (seen.size > 0)
82console.log(`${repr}: depends on ${Array.from(seen.keys()).join(",")}`)
83}
84
85repr(type: ts.TypeNode, name: string | undefined = undefined): string {
86return ts.isTypeReferenceNode(type) ? getNameWithoutQualifiersRight(type.typeName)! : name!
87}
88
89printType(type: ts.TypeNode): string {
90return ts.isTypeReferenceNode(type)
91? asString(type.typeName)
92: `${type.kind}:${ts.SyntaxKind[type.kind]}`
93}
94
95print(value: stringOrNone) {
96// console.log("print", this.currentPrinter, value)
97if (!this.currentPrinter) throw new Error("startEmit() first")
98if (value) this.currentPrinter.print(value)
99}
100
101pushIndent(): void {
102this.currentPrinter?.pushIndent()
103}
104
105popIndent(): void {
106this.currentPrinter?.popIndent()
107}
108
109getOutput(): string[] {
110let result: string[] = []
111let sortedTypes = this.getToposorted()
112sortedTypes.forEach(type => {
113let next = this.emitters.get(type)!.getOutput()
114result = result.concat(next)
115})
116return result
117}
118
119getToposorted(): Array<string> {
120// Not exactly correct for non-named types.
121let source = new Set(Array.from(this.emitters.keys()))
122let result: string[] = []
123// N^2, but nobody cares
124let added: Set<string> = new Set()
125while (source.size > added.size) {
126source.forEach(it => {
127if (added.has(it)) return
128let deps = this.deps.get(it)!
129let canAdd = true
130deps.forEach(dep => {
131//console.log(`CHECK ${it} ${dep} ${source.has(dep)} ${!added.has(dep)}`)
132if (source.has(dep) && !added.has(dep)) canAdd = false
133})
134if (canAdd && !added.has(it)) {
135result.push(it)
136added.add(it)
137}
138//console.log(`${it}: ${canAdd} depends on ${Array.from(deps).join(",")}`)
139})
140}
141// console.log(`DEPS [${result.join(", ")}]`)
142return result
143}
144}