idlize

Форк
0
145 строк · 6.1 Кб
1
/*
2
 * Copyright (c) 2022-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 ts from 'ohos-typescript';
17
import { getDeclarationsByNode } from "./ApiUtils"
18

19

20
export class ImportExport {
21
    constructor(
22
        public typechecker: ts.TypeChecker,
23
        public sourceFile: ts.SourceFile,
24
    ) {}
25

26
    isRealDeclaration(declaration: ts.Declaration): boolean {
27
        if (ts.isImportSpecifier(declaration) ||
28
            ts.isExportSpecifier(declaration) ||
29
            ts.isImportClause(declaration)) return false
30
        return true
31
    }
32

33
    followDefaultImport(identifier: ts.Identifier, importClause: ts.ImportClause): ts.Declaration | undefined {
34
        const moduleSpecifier = importClause.parent.moduleSpecifier
35
        const importSym = this.typechecker.getSymbolAtLocation(moduleSpecifier)
36
        if (importSym === undefined) {
37
            // console.log(`Could not obtain import symbol for ${ts.idText(identifier)}`)
38
            return undefined
39
        }
40
        const exports = this.typechecker.getExportsOfModule(importSym)
41

42
        return arrayAt(exports?.[0]?.declarations, 0)
43
    }
44

45
    followModuleSpecifier(identifier: ts.Identifier, moduleSpecifier: ts.StringLiteral): ts.Declaration | undefined {
46
        const moduleSym = this.typechecker.getSymbolAtLocation(moduleSpecifier)
47
        if (moduleSym === undefined) {
48
            // TODO: The typechecker doesn't give us the symbol in this case
49
            // So be silent for now.
50
            if (moduleSpecifier.text.startsWith("#")) {
51
                return undefined
52
            }
53
            // console.log(`Could not obtain module symbol for ${ts.idText(identifier)}: ${moduleSpecifier.text} `)
54
            return undefined
55
        }
56
        const exports = this.typechecker.getExportsOfModule(moduleSym)
57

58
        const found = exports.find(sym => ts.symbolName(sym) == ts.idText(identifier))
59
        return arrayAt(found?.declarations, 0)
60
    }
61

62
    followImport(identifier: ts.Identifier, declaration: ts.ImportSpecifier): ts.Declaration | undefined {
63
        if (declaration.propertyName) {
64
            const real = getDeclarationsByNode(this.typechecker, declaration.propertyName)[0]
65
            if (real && this.isRealDeclaration(real)) {
66
                return real
67
            } else {
68
                // console.log(`TODO: unexpected declaration: ${real}`)
69
                return undefined
70
            }
71
        }
72

73
        const moduleSpecifier = declaration.parent.parent.parent.moduleSpecifier
74
        if (!ts.isStringLiteral(moduleSpecifier)) {
75
            // console.log("Expected module specifier literal, got: " + ts.SyntaxKind[moduleSpecifier.kind])
76
            return undefined
77
        }
78
        return this.followModuleSpecifier(identifier, moduleSpecifier)
79
    }
80

81
    followExport(identifier: ts.Identifier, exportDeclaration: ts.ExportSpecifier): ts.Declaration | undefined {
82
        const moduleSpecifier = exportDeclaration.parent.parent.moduleSpecifier
83
        const name = exportDeclaration.propertyName ? exportDeclaration.propertyName : identifier
84

85
        if (!moduleSpecifier) {
86
            if (!exportDeclaration.propertyName) {
87
                // This is
88
                // class X{}
89
                // export { X }
90
                // In this case X's declaration is the export directive,
91
                // so need a special way to get to the class declaration.
92
                return this.typechecker.getExportSpecifierLocalTargetSymbol(exportDeclaration)?.declarations?.[0]
93
            } else {
94
                // This is an export from its own module.
95
                return this.findRealDeclaration(exportDeclaration.propertyName)
96
            }
97
        }
98
        if (!ts.isStringLiteral(moduleSpecifier)) {
99
            console.log("Expected module specifier literal, got: " + ts.SyntaxKind[moduleSpecifier.kind])
100
            return undefined
101
        }
102

103
        return this.followModuleSpecifier(name, moduleSpecifier)
104
    }
105

106
    findRealDeclaration(identifier: ts.Identifier): ts.NamedDeclaration | undefined {
107
        const declarations = getDeclarationsByNode(this.typechecker, identifier)
108

109
        if (declarations.length > 1) {
110
            // TODO: shall we support overloads?
111
            // TODO: shall we allow a function and a namespace?
112
            // return error(`TODO: Multiple declarations for ${ts.idText(identifier)} not supported`)
113

114
            return undefined
115
        }
116

117
        let declaration: ts.Declaration | undefined = declarations[0]
118
        while (declaration !== undefined) {
119
            if (this.isRealDeclaration(declaration)) {
120
                return declaration
121
            } else if (ts.isImportSpecifier(declaration)) {
122
                declaration = this.followImport(identifier, declaration)
123
            } else if (ts.isExportSpecifier(declaration)) {
124
                declaration = this.followExport(identifier, declaration)
125
            } else if (ts.isImportClause(declaration)) {
126
                declaration = this.followDefaultImport(identifier, declaration)
127
            } else {
128
                console.log(`Ets plugin non-fatal: Expected a real declaration for ${ts.idText(identifier)}, but got ${declaration ? ts.SyntaxKind[declaration.kind] : undefined}`)
129
                return undefined
130
            }
131
        }
132

133
        return undefined
134
    }
135

136
    findRealMethodDeclaration(member: ts.MemberName): ts.Node | undefined {
137
        if (!ts.isIdentifier(member)) return undefined
138
        const declarations = getDeclarationsByNode(this.typechecker, member)
139
        return declarations[0]
140
    }
141
}
142

143
function arrayAt<T>(array: T[] | undefined, index: number): T | undefined {
144
    return array ? array[index >= 0 ? index : array.length + index] : undefined
145
}

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

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

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

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