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