• 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  factory,
18  isBlock,
19  isCallExpression,
20  isCaseClause,
21  isDefaultClause,
22  isElementAccessExpression,
23  isExpressionStatement,
24  isIdentifier,
25  isModuleBlock,
26  isPropertyAccessExpression,
27  isSourceFile,
28  setParentRecursive,
29  visitEachChild
30} from 'typescript';
31
32import type {
33  Block,
34  CaseClause,
35  DefaultClause,
36  LeftHandSideExpression,
37  ModuleBlock,
38  Node,
39  NodeArray,
40  SourceFile,
41  Statement,
42  TransformationContext,
43  Transformer,
44  TransformerFactory
45} from 'typescript';
46
47import type {IOptions} from '../../configs/IOptions';
48import type {TransformPlugin} from '../TransformPlugin';
49import {TransformerOrder} from '../TransformPlugin';
50import { NodeUtils } from '../../utils/NodeUtils';
51import { performancePrinter } from '../../ArkObfuscator';
52import { EventList } from '../../utils/PrinterUtils';
53
54namespace secharmony {
55  export let transformerPlugin: TransformPlugin = {
56    'name': 'disableConsolePlugin',
57    'order': TransformerOrder.DISABLE_CONSOLE_TRANSFORMER,
58    'createTransformerFactory': createDisableConsoleFactory
59  };
60
61  export function createDisableConsoleFactory(option: IOptions): TransformerFactory<Node> {
62    if (!option.mDisableConsole) {
63      return null;
64    }
65
66    return disableConsoleFactory;
67
68    function disableConsoleFactory(context: TransformationContext): Transformer<Node> {
69      return transformer;
70
71      function transformer(node: Node): Node {
72        if (!isSourceFile(node) || NodeUtils.isDeclarationFile(node)) {
73          return node;
74        }
75
76        performancePrinter?.singleFilePrinter?.startEvent(EventList.REMOVE_CONSOLE, performancePrinter.timeSumPrinter);
77        let resultAst: Node = visitAst(node);
78        let parentNodes = setParentRecursive(resultAst, true);
79        performancePrinter?.singleFilePrinter?.endEvent(EventList.REMOVE_CONSOLE, performancePrinter.timeSumPrinter);
80        return parentNodes;
81      }
82
83      /**
84       * delete console log print expression, only support simple format like:
85       *  - console.xxx();
86       *  - console['xxx']();
87       * @param node
88       */
89      function visitAst(node: Node): Node {
90        const visitedAst = visitEachChild(node, visitAst, context);
91
92        if (!(isSourceFile(node) || isBlock(node) || isModuleBlock(node) || isCaseClause(node) || isDefaultClause(node))) {
93          return visitedAst;
94        }
95
96        //@ts-ignore
97        const deletedStatements: Statement[] = deleteConsoleStatement(visitedAst.statements);
98
99        if (isSourceFile(node)) {
100          return factory.updateSourceFile(node, deletedStatements);
101        } else if (isBlock(node)) {
102          return factory.createBlock(deletedStatements, true);
103        } else if (isModuleBlock(node)) {
104          return factory.createModuleBlock(deletedStatements);
105        } else if (isCaseClause(node)) {
106          return factory.createCaseClause(node.expression, deletedStatements);
107        } else {
108          return factory.createDefaultClause(deletedStatements);
109        }
110      }
111
112      function deleteConsoleStatement(statements: NodeArray<Statement>): Statement[] {
113        const reservedStatements: Statement[] = [];
114        statements.forEach((child) => {
115          if (!isSimpleConsoleStatement(child)) {
116            reservedStatements.push(child);
117          }
118        });
119
120        return reservedStatements;
121      }
122
123      function isSimpleConsoleStatement(node: Statement): boolean {
124        if (!isExpressionStatement(node)) {
125          return false;
126        }
127
128        if (!node.expression || !isCallExpression(node.expression)) {
129          return false;
130        }
131
132        const expressionCalled: LeftHandSideExpression = node.expression.expression;
133        if (!expressionCalled) {
134          return false;
135        }
136
137        if (isPropertyAccessExpression(expressionCalled) && expressionCalled.expression) {
138          if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'console') {
139            return true;
140          }
141        }
142
143        if (isElementAccessExpression(expressionCalled) && expressionCalled.expression) {
144          if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'console') {
145            return true;
146          }
147        }
148
149        return false;
150      }
151    }
152  }
153}
154
155export = secharmony;
156