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