idlize

Форк
0
320 строк · 12.8 Кб
1
/*
2
 * Copyright (c) 2022-2023 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 'typescript';
17
import { AbstractVisitor } from "./AbstractVisitor"
18
import { Rewrite } from './transformation-context';
19
import {
20
    FunctionKind,
21
    Tracer,
22
    findSourceFile,
23
    error,
24
    asString,
25
    RuntimeNames,
26
    isFunctionOrMethod,
27
    getComment,
28
    arrayAt,
29
    getDeclarationsByNode,
30
    findFunctionDeclaration,
31
    isMemoEntry
32
} from "./util"
33
import { ImportExport } from './import-export';
34

35

36
function parseComment(comment: string): FunctionKind {
37
    let kind = FunctionKind.REGULAR
38
    if (comment.includes(RuntimeNames.ANNOTATION_INTRINSIC)) {
39
        kind = FunctionKind.MEMO_INTRINSIC
40
    } else if (comment.includes(RuntimeNames.ANNOTATION_ENTRY)) {
41
        // Do nothing
42
    } else if (comment.includes(RuntimeNames.ANNOTATION)) {
43
        kind = FunctionKind.MEMO
44
    }
45
    return kind
46
}
47

48
export class AnalysisVisitor extends AbstractVisitor {
49
    constructor(
50
        public tracer: Tracer,
51
        public typechecker: ts.TypeChecker,
52
        public sourceFile: ts.SourceFile,
53
        public rewrite: Rewrite,
54
        ctx: ts.TransformationContext
55
    ) {
56
        super(ctx)
57
    }
58

59
    private importExport = new ImportExport(this.typechecker, this.sourceFile)
60

61
    trace(msg: any) {
62
        this.tracer.trace(msg)
63
    }
64

65
    traceAnnotation(nodeName: string, kind: FunctionKind, node: ts.Node) {
66
        if (kind) {
67
            this.trace(`${nodeName} HAS annotation: ${asString(node)} to ${FunctionKind[kind]}`)
68
        } else {
69
            this.trace(`${nodeName} doesn't have annotations: ${asString(node)}`)
70
        }
71
    }
72

73
    immediateFunctionKind(source: ts.SourceFile, node: ts.Node, name: string): FunctionKind {
74
        const comment = getComment(source, node)
75
        let kind = parseComment(comment)
76
        this.traceAnnotation(name, kind, node)
77
        return kind
78
    }
79

80
    callIsEligible(identifier: ts.Identifier|undefined): FunctionKind {
81

82
        if (!identifier) return FunctionKind.REGULAR
83

84
        const decl = this.importExport.findRealDeclaration(identifier)
85
        if (!decl) return FunctionKind.REGULAR
86

87
        const source = findSourceFile(decl)
88
        if (!source) return FunctionKind.REGULAR
89

90
        return this.variableDeclarationAnnotation(source, decl)
91
    }
92

93
    methodCallIsEligible(name: ts.MemberName): FunctionKind {
94

95
        if (!ts.isIdentifier(name)) return FunctionKind.REGULAR
96

97
        const decl = this.findRealMethodDeclaration(name)
98
        if (!decl) return FunctionKind.REGULAR
99

100
        const source = findSourceFile(decl)
101
        if (!source) return FunctionKind.REGULAR
102

103
        return this.immediateFunctionKind(source, decl, "METHOD CALL")
104
    }
105

106
    isInMemoEntry(node: ts.CallExpression): boolean {
107
        const enclosingFunction = findFunctionDeclaration(node)
108
        if (enclosingFunction === undefined) return false
109
        return isMemoEntry(this.sourceFile, enclosingFunction)
110
    }
111

112
    parameterHasAnnotation(parameter: ts.ParameterDeclaration): FunctionKind {
113
        const source = findSourceFile(parameter)
114
        if (!source) return FunctionKind.REGULAR
115

116
        return this.immediateFunctionKind(source, parameter, "Parameter")
117
    }
118

119
    parameterDeclarationHasAnnotation(functionIdentifier: ts.Identifier|undefined, parameterIndex: number): FunctionKind {
120
        if (!functionIdentifier) return FunctionKind.REGULAR
121

122
        const functionDeclaration = this.importExport.findRealDeclaration(functionIdentifier)
123
        if (!functionDeclaration) return FunctionKind.REGULAR
124
        if (!isFunctionOrMethod(functionDeclaration)) return FunctionKind.REGULAR
125

126
        const parameterDeclaration = functionDeclaration.parameters[parameterIndex]
127
        return this.parameterHasAnnotation(parameterDeclaration)
128
    }
129

130
    variableDeclarationAnnotation(sourceFile: ts.SourceFile, variable: ts.Node): FunctionKind {
131
        let immediateFunctionKind = this.immediateFunctionKind(sourceFile, variable, "Variable")
132
        if (immediateFunctionKind != FunctionKind.REGULAR) {
133
            return immediateFunctionKind
134
        }
135

136
        let parent = variable.parent
137
        if (!ts.isVariableDeclarationList(parent)) {
138
            return FunctionKind.REGULAR
139
        }
140

141
        return this.immediateFunctionKind(sourceFile, parent, "VariableDeclarationList")
142
    }
143

144
    variableDeclarationListAnnotation(sourceFile: ts.SourceFile, variableList: ts.VariableDeclarationList): FunctionKind {
145
        return this.immediateFunctionKind(this.sourceFile, variableList, "VariableDeclarationList")
146
    }
147

148
    propertyHasAnnotation(sourceFile: ts.SourceFile, property: ts.PropertyDeclaration): FunctionKind {
149
        return this.immediateFunctionKind(sourceFile, property, "Property")
150
    }
151

152
    propertySignatureHasAnnotation(sourceFile: ts.SourceFile, property: ts.PropertySignature): FunctionKind {
153
        return this.immediateFunctionKind(sourceFile, property, "Property signature")
154
    }
155

156
    functionTypeHasAnnotation(sourceFile: ts.SourceFile, functionType: ts.FunctionTypeNode): FunctionKind {
157
        return this.immediateFunctionKind(sourceFile, functionType, "Function type")
158
    }
159

160
    methodSignatureHasAnnotation(sourceFile: ts.SourceFile, signature: ts.MethodSignature): FunctionKind {
161
        return this.immediateFunctionKind(sourceFile, signature, "Method signature")
162
    }
163

164
    getterHasAnnotation(sourceFile: ts.SourceFile, getter: ts.GetAccessorDeclaration): FunctionKind {
165
        return this.immediateFunctionKind(sourceFile, getter, "Getter")
166
    }
167

168
    setterHasAnnotation(sourceFile: ts.SourceFile, setter: ts.SetAccessorDeclaration): FunctionKind {
169
        return this.immediateFunctionKind(sourceFile, setter, "Setter")
170
    }
171

172
    isDirectMemoVariableInitializer(sourceFile: ts.SourceFile, node: ts.Node): boolean {
173
        if (!ts.isVariableDeclaration(node.parent)) return false
174
        const variable = node.parent
175
        let kind = this.variableDeclarationAnnotation(sourceFile, variable)
176
        return kind != FunctionKind.REGULAR
177
    }
178

179
    isDirectArgumentForMemoParameter(sourceFile: ts.SourceFile, node: ts.Node): boolean {
180
        const parent = node.parent
181
        if (!ts.isCallExpression(parent)) return false
182

183
        let callable: ts.Identifier
184
        if (ts.isIdentifier(parent.expression)) {
185
            callable = parent.expression
186
        } else if (ts.isPropertyAccessExpression(parent.expression)) {
187
            if (ts.isPrivateIdentifier(parent.expression.name)) return false
188
            callable = parent.expression.name
189
        } else {
190
            return false
191
        }
192

193
        const index = parent.arguments.findIndex(it => it === node)
194
        if (index < 0) return false
195

196
        return this.parameterDeclarationHasAnnotation(callable, index) != FunctionKind.REGULAR
197

198
    }
199

200
    getAssigneeTransformation(sourceFile: ts.SourceFile, node: ts.Node): FunctionKind {
201
        if (this.isDirectMemoVariableInitializer(sourceFile, node)) return FunctionKind.MEMO
202
        if (this.isDirectArgumentForMemoParameter(sourceFile, node)) return FunctionKind.MEMO
203
        else return FunctionKind.REGULAR
204
    }
205

206
    declarationTransformKind(node: ts.FunctionLikeDeclarationBase|undefined): FunctionKind {
207
        if (node === undefined) return FunctionKind.REGULAR
208

209
        const name = node.name
210
        const nameString = (name && ts.isIdentifier(name)) ?
211
            ts.idText(name) :
212
            "Couldn't take declaration name"
213

214
        let immediateFunctionKind = this.immediateFunctionKind(this.sourceFile, node, "Declaration")
215
        if (immediateFunctionKind != FunctionKind.REGULAR) {
216
            return immediateFunctionKind
217
        }
218

219
        let transform = this.getAssigneeTransformation(this.sourceFile, node)
220

221
        if (transform != FunctionKind.REGULAR) {
222
            this.trace(`DECLARATION is ELIGIBLE to ${FunctionKind[transform]}: ${nameString}`)
223
        }
224

225
        return transform
226
    }
227

228
    findRealMethodDeclaration(member: ts.MemberName): ts.Node|undefined {
229
        if (!ts.isIdentifier(member)) return undefined
230
        const declarations = getDeclarationsByNode(this.typechecker, member)
231
        return declarations[0]
232
    }
233

234
    visitor(node: ts.Node): ts.Node {
235
        if (ts.getOriginalNode(node) !== node) throw new Error("Analysis phase is expected to work on original nodes")
236

237
        if (ts.isCallExpression(node)) {
238
            if (ts.isIdentifier(node.expression)) {
239
                const kind = this.callIsEligible(node.expression)
240
                if (kind) {
241
                    this.rewrite.callTable.set(node, kind)
242
                }
243
            } else if (ts.isPropertyAccessExpression(node.expression)) {
244
                const member = node.expression.name
245
                const kind = this.methodCallIsEligible(member)
246
                if (kind) {
247
                    this.rewrite.callTable.set(node, kind)
248
                }
249
            }
250
            if (this.isInMemoEntry(node)) {
251
                this.rewrite.entryTable.add(node)
252
            }
253
        } else if (ts.isFunctionDeclaration(node)) {
254
            switch (this.declarationTransformKind(node)) {
255
                case FunctionKind.MEMO:
256
                    this.rewrite.functionTable.set(node, FunctionKind.MEMO)
257
                    break;
258
                case FunctionKind.MEMO_INTRINSIC:
259
                    this.rewrite.functionTable.set(node, FunctionKind.MEMO_INTRINSIC)
260
                    break;
261
            }
262
        } else if (ts.isMethodDeclaration(node)) {
263
            switch (this.declarationTransformKind(node)) {
264
                case FunctionKind.MEMO:
265
                    this.rewrite.functionTable.set(node, FunctionKind.MEMO)
266
                    break;
267
                case FunctionKind.MEMO_INTRINSIC:
268
                    this.rewrite.functionTable.set(node, FunctionKind.MEMO_INTRINSIC)
269
                    break;
270
            }
271
        } else if (ts.isArrowFunction(node)) {
272
            if (this.declarationTransformKind(node) == FunctionKind.MEMO) {
273
                this.rewrite.functionTable.set(node, FunctionKind.MEMO)
274
            }
275
        } else if (ts.isFunctionExpression(node)) {
276
            if (this.declarationTransformKind(node) == FunctionKind.MEMO) {
277
                this.rewrite.functionTable.set(node, FunctionKind.MEMO)
278
            }
279
        } else if (ts.isParameter(node)) {
280
            if (this.parameterHasAnnotation(node) == FunctionKind.MEMO) {
281
                this.rewrite.variableTable.set(node, FunctionKind.MEMO)
282
            }
283
        } else if (ts.isVariableDeclaration(node)) {
284
            if (this.variableDeclarationAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
285
                this.rewrite.variableTable.set(node, FunctionKind.MEMO)
286
            }
287
        } else if (ts.isVariableDeclarationList(node)) {
288
            if (this.variableDeclarationListAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
289
                node.declarations.forEach(declaration => {
290
                    this.rewrite.variableTable.set(declaration, FunctionKind.MEMO)
291
                })
292
            }
293
        } else if (ts.isPropertyDeclaration(node)) {
294
            if (this.propertyHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
295
                this.rewrite.variableTable.set(node, FunctionKind.MEMO)
296
            }
297
        } else if (ts.isPropertySignature(node)) {
298
            if (this.propertySignatureHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
299
                this.rewrite.variableTable.set(node, FunctionKind.MEMO)
300
            }
301
        } else if (ts.isFunctionTypeNode(node)) {
302
            if (this.functionTypeHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
303
                this.rewrite.functionTable.set(node, FunctionKind.MEMO)
304
            }
305
        } else if (ts.isMethodSignature(node)) {
306
            if (this.methodSignatureHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
307
                this.rewrite.functionTable.set(node, FunctionKind.MEMO)
308
            }
309
        } else if (ts.isGetAccessorDeclaration(node)) {
310
            if (this.getterHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
311
                this.rewrite.functionTable.set(node, FunctionKind.MEMO)
312
            }
313
        } else if (ts.isSetAccessorDeclaration(node)) {
314
            if (this.setterHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
315
                this.rewrite.functionTable.set(node, FunctionKind.MEMO)
316
            }
317
        }
318
        return this.visitEachChild(node)
319
    }
320
}
321

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

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

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

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