• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 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 {
17  ScriptTarget,
18  SymbolFlags,
19  createCompilerHost,
20  createProgram,
21  createSourceFile
22} from 'typescript';
23
24import type {
25  CompilerHost,
26  CompilerOptions,
27  Program,
28  SourceFile,
29  Symbol,
30  TypeChecker
31} from 'typescript';
32import { Extension, PathAndExtension } from '../common/type';
33import { FileUtils } from './FileUtils';
34import { EventList, performancePrinter } from '../ArkObfuscator';
35import { endSingleFileEvent, startSingleFileEvent } from './PrinterUtils';
36import { exportSymbolAliasMap } from './ScopeAnalyzer';
37
38export class TypeUtils {
39  /**
40   * Create .d.ets, .d.ts, .ts ast from .d.ets, .d.ts, .ts content.
41   * Create .ts ast from .ets, .js content
42   * @param {string} sourceFilePath
43   * @param {string} content - The content in sourceFilePath
44   */
45  public static createObfSourceFile(sourceFilePath: string, content: string): SourceFile {
46    const pathOrExtension: PathAndExtension = FileUtils.getFileSuffix(sourceFilePath);
47    const fileSuffix = pathOrExtension.ext;
48
49    if (fileSuffix === Extension.JS) {
50      sourceFilePath = pathOrExtension.path + Extension.TS;
51    }
52
53    return createSourceFile(sourceFilePath, content, ScriptTarget.ES2015, true);
54  }
55
56  public static tsToJs(ast: SourceFile): void {
57    const pathOrExtension: PathAndExtension = FileUtils.getFileSuffix(ast.fileName);
58    const fileSuffix = Extension.JS;
59    const targetName: string = pathOrExtension.path + fileSuffix;
60    ast.fileName = targetName;
61  }
62
63  public static createChecker(ast: SourceFile): TypeChecker {
64    const host: CompilerHost = createCompilerHost({});
65
66    const customHost: CompilerHost = {
67      getSourceFile(name, languageVersion): SourceFile | undefined {
68        if (name === ast.fileName) {
69          return ast;
70        } else {
71          return undefined;
72        }
73      },
74      // optional
75      getDefaultLibLocation: () => '',
76      getDefaultLibFileName: () => '',
77      writeFile: (filename, data) => {
78      },
79      getCurrentDirectory: () => '',
80      useCaseSensitiveFileNames: host.useCaseSensitiveFileNames,
81      getCanonicalFileName: host.getCanonicalFileName,
82      getNewLine: host.getNewLine,
83      fileExists: () => true,
84      readFile: (name): string => {
85        return name === ast.fileName ? ast.text : undefined;
86      },
87      // must, read program.ts => createCompilerHost
88      directoryExists: undefined,
89      getEnvironmentVariable: undefined,
90      getDirectories: undefined,
91    };
92
93    let option: CompilerOptions = {
94      'alwaysStrict': true
95    };
96    if (ast.fileName.endsWith('.js')) {
97      option.allowJs = true;
98    }
99
100    startSingleFileEvent(EventList.CREATE_PROGRAM, performancePrinter.timeSumPrinter);
101    let program: Program = createProgram([ast.fileName], option, customHost);
102    endSingleFileEvent(EventList.CREATE_PROGRAM, performancePrinter.timeSumPrinter);
103
104    startSingleFileEvent(EventList.GET_CHECKER, performancePrinter.timeSumPrinter);
105    let typeChecker: TypeChecker = program.getTypeChecker();
106    endSingleFileEvent(EventList.GET_CHECKER, performancePrinter.timeSumPrinter);
107    return typeChecker;
108  }
109
110  /**
111   * Retrieves the symbol associated with the declaration site of the given symbol.
112   *
113   * This method resolves the symbol to its declaration site to ensure consistency
114   * in obfuscated naming. Obfuscation names are bound to the symbol at the declaration
115   * site. If the symbol at the declaration site differs from the symbol at the usage
116   * site, discrepancies in obfuscated names may occur. By using this method, the
117   * obfuscation name can be consistently retrieved from the declaration site symbol,
118   * ensuring uniformity between the declaration and usage sites.
119   */
120  public static getOriginalSymbol(symbol: Symbol, checker: TypeChecker): Symbol {
121    if (!(symbol.getFlags() & SymbolFlags.Alias)) {
122      return symbol;
123    }
124
125    if (exportSymbolAliasMap.has(symbol)) {
126      return exportSymbolAliasMap.get(symbol);
127    }
128
129    // This method helps determine if the original symbol obtained for a given symbol is valid and usable.
130    // Sometimes the original symbol's name may differ from the current symbol's name due to aliasing.
131    // For example:
132    // `let A = 1; export { A as B };`
133    // The `originalSymbol` for `B` refers to the symbol for `A`, but the names are different.
134    // However, for obfuscation purposes, we want to ensure that the symbol used in the declaration (i.e., `A` in this case)
135    // aligns with the current symbol (i.e., `B`), because this allows us to apply the declaration's obfuscation name to `B`.
136    // If the names don't match, we should avoid applying the declaration's obfuscation name to the current symbol.
137    const isValidOriginalSymbol = (originalSymbol: Symbol | undefined): boolean =>
138      originalSymbol !== undefined && originalSymbol.name === symbol.name;
139
140    const originalSymbol: Symbol | undefined = checker.getAliasedSymbol(symbol);
141    if (isValidOriginalSymbol(originalSymbol)) {
142      return originalSymbol!;
143    }
144
145    const immediateSymbol: Symbol | undefined = checker.getImmediateAliasedSymbol(symbol);
146    if (isValidOriginalSymbol(immediateSymbol)) {
147      return immediateSymbol!;
148    }
149
150    return symbol;
151  }
152}
153