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