• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2022 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 {
18    ArrayType,
19    BuiltinContainerType,
20    BuiltinType,
21    ClassInstType,
22    ClassType,
23    ExternalType,
24    FunctionType,
25    InterfaceType,
26    ObjectType,
27    PrimitiveType,
28    UnionType
29} from "./base/typeSystem";
30import * as jshelpers from "./jshelpers";
31import { LOGD } from "./log";
32import { ModuleStmt } from "./modules";
33import { isGlobalDeclare } from "./strictMode";
34import { TypeRecorder } from "./typeRecorder";
35
36export class TypeChecker {
37    private static instance: TypeChecker;
38    private compiledTypeChecker: any = null;
39    // After ets runtime adapt to parse BuiltinContainerType, set the flag as true.
40    private needRecordBuiltinContainer: boolean = false;
41    private constructor() { }
42
43    public static getInstance(): TypeChecker {
44        if (!TypeChecker.instance) {
45            TypeChecker.instance = new TypeChecker();
46        }
47        return TypeChecker.instance;
48    }
49
50    public setTypeChecker(typeChecker: ts.TypeChecker) {
51        this.compiledTypeChecker = typeChecker;
52    }
53
54    public getTypeChecker(): ts.TypeChecker {
55        return this.compiledTypeChecker;
56    }
57
58    public getTypeAtLocation(node: ts.Node) {
59        if (!node) {
60            return undefined;
61        }
62        try {
63            return this.compiledTypeChecker.getTypeAtLocation(node);
64        } catch {
65            LOGD("Get getTypeAtLocation filed for : " + node.getFullText());
66            return undefined;
67        }
68    }
69
70    public getTypeDeclForIdentifier(node: ts.Node) {
71        if (!node) {
72            return undefined;
73        }
74        let symbol;
75        try {
76            symbol = this.compiledTypeChecker.getSymbolAtLocation(node);
77        } catch {
78            LOGD("Get getSymbolAtLocation filed for : " + node.getFullText());
79            return undefined;
80        }
81        if (symbol && symbol.declarations) {
82            return symbol.declarations[0];
83        }
84        return undefined;
85    }
86
87    public hasExportKeyword(node: ts.Node): boolean {
88        if (node.modifiers) {
89            for (let modifier of node.modifiers) {
90                if (modifier.kind === ts.SyntaxKind.ExportKeyword) {
91                    return true;
92                }
93            }
94        }
95        return false;
96    }
97
98    public hasDeclareKeyword(node: ts.Node): boolean {
99        if (node.modifiers) {
100            for (let modifier of node.modifiers) {
101                if (modifier.kind === ts.SyntaxKind.DeclareKeyword) {
102                    return true;
103                }
104            }
105        }
106        return false;
107    }
108
109    public getDeclNodeForInitializer(initializer: ts.Node) {
110        switch (initializer.kind) {
111            case ts.SyntaxKind.Identifier:
112                return this.getTypeDeclForIdentifier(initializer);
113            case ts.SyntaxKind.NewExpression:
114                let initializerExpression = <ts.NewExpression>initializer;
115                let expression = initializerExpression.expression;
116                if (expression.kind == ts.SyntaxKind.ClassExpression) {
117                    return expression;
118                }
119                return this.getTypeDeclForIdentifier(expression);
120            case ts.SyntaxKind.ClassExpression:
121                return initializer;
122            case ts.SyntaxKind.PropertyAccessExpression:
123                return initializer;
124            default:
125                return undefined;
126        }
127    }
128
129    public getTypeForClassDeclOrExp(typeDeclNode: ts.Node, getTypeForInstace: boolean): number {
130        if (this.isFromDefaultLib(typeDeclNode)) {
131            return PrimitiveType.ANY;
132        }
133        let classTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode);
134        if (classTypeIndex == PrimitiveType.ANY) {
135            let classDeclNode = <ts.ClassDeclaration>typeDeclNode;
136            let className = "";
137            let classNameNode = classDeclNode.name;
138            if (classNameNode) {
139                className = jshelpers.getTextOfIdentifierOrLiteral(classNameNode).replace(/\s/g, "");
140            }
141            let classType = new ClassType(classDeclNode, BuiltinType[className]);
142            classTypeIndex = classType.shiftedTypeIndex;
143        }
144        if (getTypeForInstace) {
145            classTypeIndex = this.getOrCreateInstanceType(classTypeIndex);
146        }
147        return classTypeIndex;
148    }
149
150    public getTypeForPropertyAccessExpression(typeDeclNode: ts.Node) {
151        let propertyAccessExpression = <ts.PropertyAccessExpression>typeDeclNode;
152        let localName = jshelpers.getTextOfIdentifierOrLiteral(propertyAccessExpression.expression);
153        let externalName = jshelpers.getTextOfIdentifierOrLiteral(propertyAccessExpression.name);
154        if (TypeRecorder.getInstance().inNampespaceMap(localName)) {
155            let redirectPath = TypeRecorder.getInstance().getPathForNamespace(localName)!;
156            let externalType = new ExternalType(externalName, redirectPath);
157            let ImportTypeIndex = externalType.shiftedTypeIndex;
158            return ImportTypeIndex;
159        }
160        return PrimitiveType.ANY;
161    }
162
163    public getInterfaceDeclaration(typeDeclNode: ts.Node) {
164        if (this.isFromDefaultLib(typeDeclNode)) {
165            return PrimitiveType.ANY;
166        }
167        let interfaceTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode);
168        if (interfaceTypeIndex == PrimitiveType.ANY) {
169            let interefaceType = new InterfaceType(<ts.InterfaceDeclaration>typeDeclNode);
170            interfaceTypeIndex = interefaceType.shiftedTypeIndex;
171        }
172        return interfaceTypeIndex;
173    }
174
175    public getTypeFromDecl(typeDeclNode: ts.Node, getTypeForInstace: boolean): number {
176        if (!typeDeclNode) {
177            return PrimitiveType.ANY;
178        }
179        switch (typeDeclNode.kind) {
180            // Type found to be defined a classDeclaration or classExpression
181            case ts.SyntaxKind.ClassDeclaration:
182            case ts.SyntaxKind.ClassExpression:
183                return this.getTypeForClassDeclOrExp(typeDeclNode, getTypeForInstace);
184            case ts.SyntaxKind.ImportSpecifier:
185            case ts.SyntaxKind.ImportClause:
186                let ImportTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode);
187                if (ImportTypeIndex != PrimitiveType.ANY) {
188                    return ImportTypeIndex;
189                }
190                return PrimitiveType.ANY;
191            case ts.SyntaxKind.PropertyAccessExpression:
192                return this.getTypeForPropertyAccessExpression(typeDeclNode);
193            case ts.SyntaxKind.InterfaceDeclaration:
194                return this.getInterfaceDeclaration(typeDeclNode);
195            default:
196                return PrimitiveType.ANY;
197        }
198    }
199
200    public getTypeForLiteralTypeNode(node: ts.Node) {
201        switch (node.kind) {
202            case ts.SyntaxKind.NumericLiteral:
203                return PrimitiveType.NUMBER;
204            case ts.SyntaxKind.TrueKeyword:
205            case ts.SyntaxKind.FalseKeyword:
206                return PrimitiveType.BOOLEAN;
207            case ts.SyntaxKind.StringLiteral:
208                return PrimitiveType.STRING;
209            case ts.SyntaxKind.NullKeyword:
210                return PrimitiveType.NULL;
211            default:
212                return PrimitiveType.ANY;
213        }
214    }
215
216    public getTypeFromAnotation(typeNode: ts.TypeNode | undefined) {
217        if (!typeNode) {
218            return PrimitiveType.ANY;
219        }
220        switch (typeNode.kind) {
221            case ts.SyntaxKind.StringKeyword:
222            case ts.SyntaxKind.NumberKeyword:
223            case ts.SyntaxKind.BooleanKeyword:
224            case ts.SyntaxKind.SymbolKeyword:
225            case ts.SyntaxKind.UndefinedKeyword:
226            case ts.SyntaxKind.VoidKeyword:
227                let typeName = typeNode.getText().toUpperCase();
228                let typeIndex = PrimitiveType.ANY;
229                if (typeName && typeName in PrimitiveType) {
230                    typeIndex = PrimitiveType[typeName as keyof typeof PrimitiveType];
231                }
232                return typeIndex;
233            case ts.SyntaxKind.LiteralType:
234                let literalType = (<ts.LiteralTypeNode>typeNode).literal;
235                return this.getTypeForLiteralTypeNode(literalType);
236            case ts.SyntaxKind.UnionType:
237                let unionType = new UnionType(typeNode);
238                return unionType.shiftedTypeIndex;
239            case ts.SyntaxKind.ArrayType:
240                let arrayType = new ArrayType(typeNode);
241                return arrayType.shiftedTypeIndex;
242            case ts.SyntaxKind.ParenthesizedType:
243                let subType = (<ts.ParenthesizedTypeNode>typeNode).type
244                if (subType.kind == ts.SyntaxKind.UnionType) {
245                    let unionType = new UnionType(subType);
246                    return unionType.shiftedTypeIndex;
247                }
248                return PrimitiveType.ANY;
249            case ts.SyntaxKind.TypeLiteral:
250                let objectType = new ObjectType(<ts.TypeLiteralNode>typeNode);
251                return objectType.shiftedTypeIndex;
252            case ts.SyntaxKind.TypeReference:
253                let typeIdentifier = (<ts.TypeReferenceNode>typeNode).typeName;
254                let typeIdentifierName = jshelpers.getTextOfIdentifierOrLiteral(typeIdentifier);
255                if (BuiltinType[typeIdentifierName]) {
256                    let declNode = this.getDeclNodeForInitializer(typeIdentifier);
257                    if (declNode && (ts.isClassLike(declNode) || declNode.kind == ts.SyntaxKind.InterfaceDeclaration)) {
258                        return this.getBuiltinTypeIndex(<ts.TypeReferenceNode>typeNode, typeIdentifierName);
259                    } else {
260                        return BuiltinType[typeIdentifierName];
261                    }
262                }
263            default:
264                return PrimitiveType.ANY;
265        }
266    }
267
268    isBuiltinType(expr: ts.NewExpression) {
269        let name = expr.expression.getFullText().replace(/\s/g, "");
270        return name in BuiltinType;
271    }
272
273    isFromDefaultLib(node: ts.Node) {
274        return node.getSourceFile().hasNoDefaultLib;
275    }
276
277    getOrCreateInstanceType(classTypeIdx: number) {
278        let typeRec = TypeRecorder.getInstance();
279        if (typeRec.hasClass2InstanceMap(classTypeIdx)) {
280            return typeRec.getClass2InstanceMap(classTypeIdx);
281        }
282        let instanceType = new ClassInstType(classTypeIdx);
283        return instanceType.shiftedTypeIndex;
284    }
285
286    getOrCreateInstanceTypeForBuiltinContainer(builtinContainerSignature: object) {
287        let typeRec = TypeRecorder.getInstance();
288        if (typeRec.hasBuiltinContainer2InstanceMap(builtinContainerSignature)) {
289            return typeRec.getBuiltinContainer2InstanceMap(builtinContainerSignature);
290        }
291        let builtinContainerType = new BuiltinContainerType(builtinContainerSignature);
292        let builtinContainerTypeIdx = builtinContainerType.shiftedTypeIndex;
293        return this.getOrCreateInstanceType(builtinContainerTypeIdx);
294    }
295
296    getBuiltinTypeIndex(node: ts.NewExpression | ts.TypeReferenceNode, name: string) {
297        let typeArguments = node.typeArguments;
298        if (typeArguments && this.needRecordBuiltinContainer) {
299            let typeArgIdxs = new Array<number>();
300            for(let typeArg of typeArguments) {
301                let typeArgIdx = this.getOrCreateRecordForTypeNode(typeArg);
302                typeArgIdxs.push(typeArgIdx);
303            }
304            let builtinContainerSignature = {
305                "typeIndex": BuiltinType[name],
306                "typeArgIdxs": typeArgIdxs
307            }
308            return this.getOrCreateInstanceTypeForBuiltinContainer(builtinContainerSignature);
309        }
310        return this.getOrCreateInstanceType(BuiltinType[name]);
311    }
312
313    getBuiltinTypeIndexForExpr(expr: ts.NewExpression) {
314        let origExprNode = <ts.NewExpression>ts.getOriginalNode(expr);
315        let name = origExprNode.expression.getFullText().replace(/\s/g, "");
316        return this.getBuiltinTypeIndex(origExprNode, name);
317    }
318
319    public getOrCreateRecordForDeclNode(initializer: ts.Node | undefined, variableNode?: ts.Node) {
320        if (!initializer) {
321            return PrimitiveType.ANY;
322        }
323
324        let typeIndex = PrimitiveType.ANY;
325        if (initializer.kind == ts.SyntaxKind.NewExpression && this.isBuiltinType(<ts.NewExpression>initializer)) {
326            typeIndex = this.getBuiltinTypeIndexForExpr(<ts.NewExpression>initializer);
327        } else {
328            let declNode = this.getDeclNodeForInitializer(initializer);
329            typeIndex = this.getTypeFromDecl(declNode, initializer.kind == ts.SyntaxKind.NewExpression);
330        }
331
332        if (variableNode) {
333            TypeRecorder.getInstance().setVariable2Type(variableNode, typeIndex);
334        }
335        return typeIndex;
336    }
337
338    public getOrCreateRecordForTypeNode(typeNode: ts.TypeNode | undefined, variableNode?: ts.Node) {
339        if (!typeNode) {
340            return PrimitiveType.ANY;
341        }
342        let typeIndex = PrimitiveType.ANY;
343        typeIndex = this.getTypeFromAnotation(typeNode);
344        if (typeIndex == PrimitiveType.ANY && typeNode.kind == ts.SyntaxKind.TypeReference) {
345            let typeName = typeNode.getChildAt(0);
346            let typeDecl = this.getDeclNodeForInitializer(typeName);
347            typeIndex = this.getTypeFromDecl(typeDecl, true);
348        }
349        if (variableNode) {
350            TypeRecorder.getInstance().setVariable2Type(variableNode, typeIndex);
351        }
352        return typeIndex;
353    }
354
355    public formatVariableStatement(variableStatementNode: ts.VariableStatement) {
356        let decList = variableStatementNode.declarationList;
357        decList.declarations.forEach(declaration => {
358            let variableNode = declaration.name;
359            let typeNode = declaration.type;
360            let initializer = declaration.initializer;
361            let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, variableNode);
362            if (typeIndex == PrimitiveType.ANY) {
363                typeIndex = this.getOrCreateRecordForDeclNode(initializer, variableNode);
364            }
365            if (this.hasExportKeyword(variableStatementNode) && typeIndex != PrimitiveType.ANY) {
366                let exportedName = jshelpers.getTextOfIdentifierOrLiteral(variableNode);
367                TypeRecorder.getInstance().setExportedType(exportedName, typeIndex);
368            }
369        });
370    }
371
372    public formatClassDeclaration(classDeclNode: ts.ClassDeclaration) {
373        if (this.isFromDefaultLib(classDeclNode)) {
374            return;
375        }
376        let classNameNode = classDeclNode.name;
377        let className = "default";
378        if (classNameNode) {
379            className = jshelpers.getTextOfIdentifierOrLiteral(classNameNode).replace(/\s/g, "");
380        }
381
382        let typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(classDeclNode);
383        if (typeIndex == PrimitiveType.ANY) {
384            let classType = new ClassType(classDeclNode, BuiltinType[className]);
385            typeIndex = classType.shiftedTypeIndex;
386        }
387
388        if (this.hasExportKeyword(classDeclNode)) {
389            TypeRecorder.getInstance().setExportedType(className, typeIndex);
390        } else if (this.hasDeclareKeyword(classDeclNode) && isGlobalDeclare()) {
391            TypeRecorder.getInstance().setDeclaredType(className, typeIndex);
392        }
393    }
394
395    // Entry for type recording
396    public formatNodeType(node: ts.Node, importOrExportStmt?: ModuleStmt) {
397        if (this.compiledTypeChecker === null) {
398            return;
399        }
400        switch (node.kind) {
401            case ts.SyntaxKind.VariableStatement:
402                let variableStatementNode = <ts.VariableStatement>ts.getOriginalNode(node);
403                if (variableStatementNode.kind == ts.SyntaxKind.VariableStatement) {
404                    this.formatVariableStatement(variableStatementNode);
405                }
406                break;
407            case ts.SyntaxKind.FunctionDeclaration:
408                let functionDeclNode = <ts.FunctionDeclaration>ts.getOriginalNode(node);
409                if (this.isFromDefaultLib(functionDeclNode)) {
410                    break;
411                }
412                let functionName = functionDeclNode.name ? functionDeclNode.name : undefined;
413                let funcTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(functionDeclNode);
414                if (funcTypeIndex == PrimitiveType.ANY) {
415                    let functionnameText = "";
416                    if (functionName) {
417                        functionnameText = jshelpers.getTextOfIdentifierOrLiteral(functionName);
418                    }
419                    let funcType = new FunctionType(functionDeclNode, BuiltinType[functionnameText]);
420                    funcTypeIndex = funcType.shiftedTypeIndex;
421                }
422                if (functionName) {
423                    TypeRecorder.getInstance().setVariable2Type(functionName, funcTypeIndex);
424                }
425                break;
426            case ts.SyntaxKind.ClassDeclaration:
427                // Create the type if it is exported or declared. If not, also create type in case there's
428                // static property access of this class.
429                let classDeclNode = <ts.ClassDeclaration>ts.getOriginalNode(node);
430                this.formatClassDeclaration(classDeclNode);
431                break;
432            case ts.SyntaxKind.InterfaceDeclaration:
433                if (isGlobalDeclare()) {
434                    let interfaceDeclNode = <ts.InterfaceDeclaration>ts.getOriginalNode(node);
435                    if (this.isFromDefaultLib(interfaceDeclNode)) {
436                        break;
437                    }
438                    let interfaceType = new InterfaceType(interfaceDeclNode);
439                    let interfaceName = interfaceDeclNode.name;
440                    if (interfaceName) {
441                        let name = jshelpers.getTextOfIdentifierOrLiteral(interfaceName);
442                        TypeRecorder.getInstance().setDeclaredType(name, interfaceType.shiftedTypeIndex);
443                    }
444                }
445                break;
446            case ts.SyntaxKind.ExportDeclaration:
447                if (importOrExportStmt) {
448                    TypeRecorder.getInstance().addExportedType(importOrExportStmt);
449                }
450                break;
451            case ts.SyntaxKind.ImportDeclaration:
452                if (importOrExportStmt) {
453                    TypeRecorder.getInstance().addImportedType(importOrExportStmt);
454                }
455                break;
456            case ts.SyntaxKind.ExportAssignment:
457                let expression = (<ts.ExportAssignment>node).expression;
458                let exportedName = "default";
459                let expressionType = this.getTypeAtLocation(expression);
460                if (expressionType) {
461                    let typeNode = expressionType.getSymbol()?.valueDeclaration;
462                    TypeRecorder.getInstance().addNonReExportedType(exportedName, typeNode, expression);
463                }
464                break;
465            default:
466                break;
467        }
468    }
469
470
471}
472