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 } 102 103 if (isBlock(node)) { 104 return factory.createBlock(deletedStatements, true); 105 } 106 107 if (isModuleBlock(node)) { 108 return factory.createModuleBlock(deletedStatements) 109 } 110 111 if (isCaseClause(node)) { 112 return factory.createCaseClause(node.expression, deletedStatements); 113 } 114 115 if (isDefaultClause(node)) { 116 return factory.createDefaultClause(deletedStatements); 117 } 118 } 119 120 function deleteConsoleStatement(statements: NodeArray<Statement>): Statement[] { 121 const reservedStatements: Statement[] = []; 122 statements.forEach((child) => { 123 if (!isSimpleConsoleStatement(child)) { 124 reservedStatements.push(child); 125 } 126 }); 127 128 return reservedStatements; 129 } 130 131 function isSimpleConsoleStatement(node: Statement): boolean { 132 if (!isExpressionStatement(node)) { 133 return false; 134 } 135 136 if (!node.expression || !isCallExpression(node.expression)) { 137 return false; 138 } 139 140 const expressionCalled: LeftHandSideExpression = node.expression.expression; 141 if (!expressionCalled) { 142 return false; 143 } 144 145 if (isPropertyAccessExpression(expressionCalled) && expressionCalled.expression) { 146 if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'console') { 147 return true; 148 } 149 } 150 151 if (isElementAccessExpression(expressionCalled) && expressionCalled.expression) { 152 if (isIdentifier(expressionCalled.expression) && expressionCalled.expression.text === 'console') { 153 return true; 154 } 155 } 156 157 return false; 158 } 159 } 160 } 161} 162 163export = secharmony; 164