idlize
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
16import * as ts from 'typescript';
17import { AbstractVisitor } from "./AbstractVisitor"
18import { Rewrite } from './transformation-context';
19import {
20FunctionKind,
21Tracer,
22findSourceFile,
23error,
24asString,
25RuntimeNames,
26isFunctionOrMethod,
27getComment,
28arrayAt,
29getDeclarationsByNode,
30findFunctionDeclaration,
31isMemoEntry
32} from "./util"
33import { ImportExport } from './import-export';
34
35
36function parseComment(comment: string): FunctionKind {
37let kind = FunctionKind.REGULAR
38if (comment.includes(RuntimeNames.ANNOTATION_INTRINSIC)) {
39kind = FunctionKind.MEMO_INTRINSIC
40} else if (comment.includes(RuntimeNames.ANNOTATION_ENTRY)) {
41// Do nothing
42} else if (comment.includes(RuntimeNames.ANNOTATION)) {
43kind = FunctionKind.MEMO
44}
45return kind
46}
47
48export class AnalysisVisitor extends AbstractVisitor {
49constructor(
50public tracer: Tracer,
51public typechecker: ts.TypeChecker,
52public sourceFile: ts.SourceFile,
53public rewrite: Rewrite,
54ctx: ts.TransformationContext
55) {
56super(ctx)
57}
58
59private importExport = new ImportExport(this.typechecker, this.sourceFile)
60
61trace(msg: any) {
62this.tracer.trace(msg)
63}
64
65traceAnnotation(nodeName: string, kind: FunctionKind, node: ts.Node) {
66if (kind) {
67this.trace(`${nodeName} HAS annotation: ${asString(node)} to ${FunctionKind[kind]}`)
68} else {
69this.trace(`${nodeName} doesn't have annotations: ${asString(node)}`)
70}
71}
72
73immediateFunctionKind(source: ts.SourceFile, node: ts.Node, name: string): FunctionKind {
74const comment = getComment(source, node)
75let kind = parseComment(comment)
76this.traceAnnotation(name, kind, node)
77return kind
78}
79
80callIsEligible(identifier: ts.Identifier|undefined): FunctionKind {
81
82if (!identifier) return FunctionKind.REGULAR
83
84const decl = this.importExport.findRealDeclaration(identifier)
85if (!decl) return FunctionKind.REGULAR
86
87const source = findSourceFile(decl)
88if (!source) return FunctionKind.REGULAR
89
90return this.variableDeclarationAnnotation(source, decl)
91}
92
93methodCallIsEligible(name: ts.MemberName): FunctionKind {
94
95if (!ts.isIdentifier(name)) return FunctionKind.REGULAR
96
97const decl = this.findRealMethodDeclaration(name)
98if (!decl) return FunctionKind.REGULAR
99
100const source = findSourceFile(decl)
101if (!source) return FunctionKind.REGULAR
102
103return this.immediateFunctionKind(source, decl, "METHOD CALL")
104}
105
106isInMemoEntry(node: ts.CallExpression): boolean {
107const enclosingFunction = findFunctionDeclaration(node)
108if (enclosingFunction === undefined) return false
109return isMemoEntry(this.sourceFile, enclosingFunction)
110}
111
112parameterHasAnnotation(parameter: ts.ParameterDeclaration): FunctionKind {
113const source = findSourceFile(parameter)
114if (!source) return FunctionKind.REGULAR
115
116return this.immediateFunctionKind(source, parameter, "Parameter")
117}
118
119parameterDeclarationHasAnnotation(functionIdentifier: ts.Identifier|undefined, parameterIndex: number): FunctionKind {
120if (!functionIdentifier) return FunctionKind.REGULAR
121
122const functionDeclaration = this.importExport.findRealDeclaration(functionIdentifier)
123if (!functionDeclaration) return FunctionKind.REGULAR
124if (!isFunctionOrMethod(functionDeclaration)) return FunctionKind.REGULAR
125
126const parameterDeclaration = functionDeclaration.parameters[parameterIndex]
127return this.parameterHasAnnotation(parameterDeclaration)
128}
129
130variableDeclarationAnnotation(sourceFile: ts.SourceFile, variable: ts.Node): FunctionKind {
131let immediateFunctionKind = this.immediateFunctionKind(sourceFile, variable, "Variable")
132if (immediateFunctionKind != FunctionKind.REGULAR) {
133return immediateFunctionKind
134}
135
136let parent = variable.parent
137if (!ts.isVariableDeclarationList(parent)) {
138return FunctionKind.REGULAR
139}
140
141return this.immediateFunctionKind(sourceFile, parent, "VariableDeclarationList")
142}
143
144variableDeclarationListAnnotation(sourceFile: ts.SourceFile, variableList: ts.VariableDeclarationList): FunctionKind {
145return this.immediateFunctionKind(this.sourceFile, variableList, "VariableDeclarationList")
146}
147
148propertyHasAnnotation(sourceFile: ts.SourceFile, property: ts.PropertyDeclaration): FunctionKind {
149return this.immediateFunctionKind(sourceFile, property, "Property")
150}
151
152propertySignatureHasAnnotation(sourceFile: ts.SourceFile, property: ts.PropertySignature): FunctionKind {
153return this.immediateFunctionKind(sourceFile, property, "Property signature")
154}
155
156functionTypeHasAnnotation(sourceFile: ts.SourceFile, functionType: ts.FunctionTypeNode): FunctionKind {
157return this.immediateFunctionKind(sourceFile, functionType, "Function type")
158}
159
160methodSignatureHasAnnotation(sourceFile: ts.SourceFile, signature: ts.MethodSignature): FunctionKind {
161return this.immediateFunctionKind(sourceFile, signature, "Method signature")
162}
163
164getterHasAnnotation(sourceFile: ts.SourceFile, getter: ts.GetAccessorDeclaration): FunctionKind {
165return this.immediateFunctionKind(sourceFile, getter, "Getter")
166}
167
168setterHasAnnotation(sourceFile: ts.SourceFile, setter: ts.SetAccessorDeclaration): FunctionKind {
169return this.immediateFunctionKind(sourceFile, setter, "Setter")
170}
171
172isDirectMemoVariableInitializer(sourceFile: ts.SourceFile, node: ts.Node): boolean {
173if (!ts.isVariableDeclaration(node.parent)) return false
174const variable = node.parent
175let kind = this.variableDeclarationAnnotation(sourceFile, variable)
176return kind != FunctionKind.REGULAR
177}
178
179isDirectArgumentForMemoParameter(sourceFile: ts.SourceFile, node: ts.Node): boolean {
180const parent = node.parent
181if (!ts.isCallExpression(parent)) return false
182
183let callable: ts.Identifier
184if (ts.isIdentifier(parent.expression)) {
185callable = parent.expression
186} else if (ts.isPropertyAccessExpression(parent.expression)) {
187if (ts.isPrivateIdentifier(parent.expression.name)) return false
188callable = parent.expression.name
189} else {
190return false
191}
192
193const index = parent.arguments.findIndex(it => it === node)
194if (index < 0) return false
195
196return this.parameterDeclarationHasAnnotation(callable, index) != FunctionKind.REGULAR
197
198}
199
200getAssigneeTransformation(sourceFile: ts.SourceFile, node: ts.Node): FunctionKind {
201if (this.isDirectMemoVariableInitializer(sourceFile, node)) return FunctionKind.MEMO
202if (this.isDirectArgumentForMemoParameter(sourceFile, node)) return FunctionKind.MEMO
203else return FunctionKind.REGULAR
204}
205
206declarationTransformKind(node: ts.FunctionLikeDeclarationBase|undefined): FunctionKind {
207if (node === undefined) return FunctionKind.REGULAR
208
209const name = node.name
210const nameString = (name && ts.isIdentifier(name)) ?
211ts.idText(name) :
212"Couldn't take declaration name"
213
214let immediateFunctionKind = this.immediateFunctionKind(this.sourceFile, node, "Declaration")
215if (immediateFunctionKind != FunctionKind.REGULAR) {
216return immediateFunctionKind
217}
218
219let transform = this.getAssigneeTransformation(this.sourceFile, node)
220
221if (transform != FunctionKind.REGULAR) {
222this.trace(`DECLARATION is ELIGIBLE to ${FunctionKind[transform]}: ${nameString}`)
223}
224
225return transform
226}
227
228findRealMethodDeclaration(member: ts.MemberName): ts.Node|undefined {
229if (!ts.isIdentifier(member)) return undefined
230const declarations = getDeclarationsByNode(this.typechecker, member)
231return declarations[0]
232}
233
234visitor(node: ts.Node): ts.Node {
235if (ts.getOriginalNode(node) !== node) throw new Error("Analysis phase is expected to work on original nodes")
236
237if (ts.isCallExpression(node)) {
238if (ts.isIdentifier(node.expression)) {
239const kind = this.callIsEligible(node.expression)
240if (kind) {
241this.rewrite.callTable.set(node, kind)
242}
243} else if (ts.isPropertyAccessExpression(node.expression)) {
244const member = node.expression.name
245const kind = this.methodCallIsEligible(member)
246if (kind) {
247this.rewrite.callTable.set(node, kind)
248}
249}
250if (this.isInMemoEntry(node)) {
251this.rewrite.entryTable.add(node)
252}
253} else if (ts.isFunctionDeclaration(node)) {
254switch (this.declarationTransformKind(node)) {
255case FunctionKind.MEMO:
256this.rewrite.functionTable.set(node, FunctionKind.MEMO)
257break;
258case FunctionKind.MEMO_INTRINSIC:
259this.rewrite.functionTable.set(node, FunctionKind.MEMO_INTRINSIC)
260break;
261}
262} else if (ts.isMethodDeclaration(node)) {
263switch (this.declarationTransformKind(node)) {
264case FunctionKind.MEMO:
265this.rewrite.functionTable.set(node, FunctionKind.MEMO)
266break;
267case FunctionKind.MEMO_INTRINSIC:
268this.rewrite.functionTable.set(node, FunctionKind.MEMO_INTRINSIC)
269break;
270}
271} else if (ts.isArrowFunction(node)) {
272if (this.declarationTransformKind(node) == FunctionKind.MEMO) {
273this.rewrite.functionTable.set(node, FunctionKind.MEMO)
274}
275} else if (ts.isFunctionExpression(node)) {
276if (this.declarationTransformKind(node) == FunctionKind.MEMO) {
277this.rewrite.functionTable.set(node, FunctionKind.MEMO)
278}
279} else if (ts.isParameter(node)) {
280if (this.parameterHasAnnotation(node) == FunctionKind.MEMO) {
281this.rewrite.variableTable.set(node, FunctionKind.MEMO)
282}
283} else if (ts.isVariableDeclaration(node)) {
284if (this.variableDeclarationAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
285this.rewrite.variableTable.set(node, FunctionKind.MEMO)
286}
287} else if (ts.isVariableDeclarationList(node)) {
288if (this.variableDeclarationListAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
289node.declarations.forEach(declaration => {
290this.rewrite.variableTable.set(declaration, FunctionKind.MEMO)
291})
292}
293} else if (ts.isPropertyDeclaration(node)) {
294if (this.propertyHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
295this.rewrite.variableTable.set(node, FunctionKind.MEMO)
296}
297} else if (ts.isPropertySignature(node)) {
298if (this.propertySignatureHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
299this.rewrite.variableTable.set(node, FunctionKind.MEMO)
300}
301} else if (ts.isFunctionTypeNode(node)) {
302if (this.functionTypeHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
303this.rewrite.functionTable.set(node, FunctionKind.MEMO)
304}
305} else if (ts.isMethodSignature(node)) {
306if (this.methodSignatureHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
307this.rewrite.functionTable.set(node, FunctionKind.MEMO)
308}
309} else if (ts.isGetAccessorDeclaration(node)) {
310if (this.getterHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
311this.rewrite.functionTable.set(node, FunctionKind.MEMO)
312}
313} else if (ts.isSetAccessorDeclaration(node)) {
314if (this.setterHasAnnotation(this.sourceFile, node) == FunctionKind.MEMO) {
315this.rewrite.functionTable.set(node, FunctionKind.MEMO)
316}
317}
318return this.visitEachChild(node)
319}
320}
321