13af6ab5fSopenharmony_ci/*
23af6ab5fSopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License.
53af6ab5fSopenharmony_ci * You may obtain a copy of the License at
63af6ab5fSopenharmony_ci *
73af6ab5fSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
83af6ab5fSopenharmony_ci *
93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and
133af6ab5fSopenharmony_ci * limitations under the License.
143af6ab5fSopenharmony_ci */
153af6ab5fSopenharmony_ci
163af6ab5fSopenharmony_ciimport type {
173af6ab5fSopenharmony_ci  Expression,
183af6ab5fSopenharmony_ci  Identifier,
193af6ab5fSopenharmony_ci  Node,
203af6ab5fSopenharmony_ci  ObjectBindingPattern,
213af6ab5fSopenharmony_ci  SourceFile,
223af6ab5fSopenharmony_ci  TypeChecker,
233af6ab5fSopenharmony_ci  TransformationContext,
243af6ab5fSopenharmony_ci  TransformerFactory
253af6ab5fSopenharmony_ci} from 'typescript';
263af6ab5fSopenharmony_ciimport {
273af6ab5fSopenharmony_ci  Symbol,
283af6ab5fSopenharmony_ci  SyntaxKind,
293af6ab5fSopenharmony_ci  getModifiers,
303af6ab5fSopenharmony_ci  isBinaryExpression,
313af6ab5fSopenharmony_ci  isBindingElement,
323af6ab5fSopenharmony_ci  isCallExpression,
333af6ab5fSopenharmony_ci  isClassDeclaration,
343af6ab5fSopenharmony_ci  isClassExpression,
353af6ab5fSopenharmony_ci  isComputedPropertyName,
363af6ab5fSopenharmony_ci  isConstructorDeclaration,
373af6ab5fSopenharmony_ci  isElementAccessExpression,
383af6ab5fSopenharmony_ci  isEnumMember,
393af6ab5fSopenharmony_ci  isGetAccessor,
403af6ab5fSopenharmony_ci  isIdentifier,
413af6ab5fSopenharmony_ci  isMetaProperty,
423af6ab5fSopenharmony_ci  isMethodDeclaration,
433af6ab5fSopenharmony_ci  isMethodSignature,
443af6ab5fSopenharmony_ci  isParameter,
453af6ab5fSopenharmony_ci  isPrivateIdentifier,
463af6ab5fSopenharmony_ci  isPropertyAccessExpression,
473af6ab5fSopenharmony_ci  isPropertyAssignment,
483af6ab5fSopenharmony_ci  isPropertyDeclaration,
493af6ab5fSopenharmony_ci  isPropertySignature,
503af6ab5fSopenharmony_ci  isQualifiedName,
513af6ab5fSopenharmony_ci  isSetAccessor,
523af6ab5fSopenharmony_ci  isVariableDeclaration,
533af6ab5fSopenharmony_ci  visitEachChild
543af6ab5fSopenharmony_ci} from 'typescript';
553af6ab5fSopenharmony_ciimport {
563af6ab5fSopenharmony_ci  getViewPUClassProperties,
573af6ab5fSopenharmony_ci  isParameterPropertyModifier,
583af6ab5fSopenharmony_ci  isViewPUBasedClass,
593af6ab5fSopenharmony_ci  visitEnumInitializer
603af6ab5fSopenharmony_ci} from './OhsUtil';
613af6ab5fSopenharmony_ciimport { Extension } from '../common/type';
623af6ab5fSopenharmony_ciimport { MergedConfig } from '../initialization/ConfigResolver';
633af6ab5fSopenharmony_ci
643af6ab5fSopenharmony_ciexport class NodeUtils {
653af6ab5fSopenharmony_ci  public static isPropertyDeclarationNode(node: Node): boolean {
663af6ab5fSopenharmony_ci    let parent: Node | undefined = node.parent;
673af6ab5fSopenharmony_ci    if (!parent) {
683af6ab5fSopenharmony_ci      return false;
693af6ab5fSopenharmony_ci    }
703af6ab5fSopenharmony_ci
713af6ab5fSopenharmony_ci    /** eg: { 'name'' : 'akira' }, pass */
723af6ab5fSopenharmony_ci    if (isPropertyAssignment(parent)) {
733af6ab5fSopenharmony_ci      return parent.name === node;
743af6ab5fSopenharmony_ci    }
753af6ab5fSopenharmony_ci
763af6ab5fSopenharmony_ci    if (isComputedPropertyName(parent) && parent.expression === node) {
773af6ab5fSopenharmony_ci      return true;
783af6ab5fSopenharmony_ci    }
793af6ab5fSopenharmony_ci
803af6ab5fSopenharmony_ci    /** object binding pattern */
813af6ab5fSopenharmony_ci    if (isBindingElement(parent) && parent.propertyName === node) {
823af6ab5fSopenharmony_ci      return true;
833af6ab5fSopenharmony_ci    }
843af6ab5fSopenharmony_ci
853af6ab5fSopenharmony_ci    /** eg: interface/type inf { 'name' : string}, pass */
863af6ab5fSopenharmony_ci    if (isPropertySignature(parent) && parent.name === node) {
873af6ab5fSopenharmony_ci      return true;
883af6ab5fSopenharmony_ci    }
893af6ab5fSopenharmony_ci
903af6ab5fSopenharmony_ci    /** eg: interface/type T1 { func(arg: string): number;} */
913af6ab5fSopenharmony_ci    if (isMethodSignature(parent) && parent.name === node) {
923af6ab5fSopenharmony_ci      return true;
933af6ab5fSopenharmony_ci    }
943af6ab5fSopenharmony_ci
953af6ab5fSopenharmony_ci    /** eg: enum { xxx = 1}; */
963af6ab5fSopenharmony_ci    if (isEnumMember(parent) && parent.name === node) {
973af6ab5fSopenharmony_ci      return true;
983af6ab5fSopenharmony_ci    }
993af6ab5fSopenharmony_ci
1003af6ab5fSopenharmony_ci    /** class { private name= 1}; */
1013af6ab5fSopenharmony_ci    if (isPropertyDeclaration(parent) && parent.name === node) {
1023af6ab5fSopenharmony_ci      return true;
1033af6ab5fSopenharmony_ci    }
1043af6ab5fSopenharmony_ci
1053af6ab5fSopenharmony_ci    /** class {'getName': function() {}} let _ = { getName() [}} */
1063af6ab5fSopenharmony_ci    if (isMethodDeclaration(parent) && parent.name === node) {
1073af6ab5fSopenharmony_ci      return true;
1083af6ab5fSopenharmony_ci    }
1093af6ab5fSopenharmony_ci
1103af6ab5fSopenharmony_ci    if (isSetAccessor(parent) && parent.name === node) {
1113af6ab5fSopenharmony_ci      return true;
1123af6ab5fSopenharmony_ci    }
1133af6ab5fSopenharmony_ci
1143af6ab5fSopenharmony_ci    return isGetAccessor(parent) && parent.name === node;
1153af6ab5fSopenharmony_ci  }
1163af6ab5fSopenharmony_ci
1173af6ab5fSopenharmony_ci  public static isPropertyOrElementAccessNode(node: Node): boolean {
1183af6ab5fSopenharmony_ci    return this.isPropertyAccessNode(node) || this.isElementAccessNode(node) || false;
1193af6ab5fSopenharmony_ci  }
1203af6ab5fSopenharmony_ci
1213af6ab5fSopenharmony_ci  public static isPropertyAccessNode(node: Node): boolean {
1223af6ab5fSopenharmony_ci    let parent: Node | undefined = node.parent;
1233af6ab5fSopenharmony_ci    if (!parent) {
1243af6ab5fSopenharmony_ci      return false;
1253af6ab5fSopenharmony_ci    }
1263af6ab5fSopenharmony_ci
1273af6ab5fSopenharmony_ci    /** eg: a.b = 1 */
1283af6ab5fSopenharmony_ci    if (isPropertyAccessExpression(parent) && parent.name === node) {
1293af6ab5fSopenharmony_ci      return true;
1303af6ab5fSopenharmony_ci    }
1313af6ab5fSopenharmony_ci    if (isPrivateIdentifier(node) && NodeUtils.isInClassDeclaration(parent)) {
1323af6ab5fSopenharmony_ci      return NodeUtils.isInExpression(parent);
1333af6ab5fSopenharmony_ci    }
1343af6ab5fSopenharmony_ci    return isQualifiedName(parent) && parent.right === node;
1353af6ab5fSopenharmony_ci  }
1363af6ab5fSopenharmony_ci
1373af6ab5fSopenharmony_ci  private static isInClassDeclaration(node: Node | undefined): boolean {
1383af6ab5fSopenharmony_ci    if (!node) {
1393af6ab5fSopenharmony_ci      return false;
1403af6ab5fSopenharmony_ci    }
1413af6ab5fSopenharmony_ci
1423af6ab5fSopenharmony_ci    if (isClassDeclaration(node) || isClassExpression(node)) {
1433af6ab5fSopenharmony_ci      return true;
1443af6ab5fSopenharmony_ci    }
1453af6ab5fSopenharmony_ci
1463af6ab5fSopenharmony_ci    return NodeUtils.isInClassDeclaration(node.parent);
1473af6ab5fSopenharmony_ci  }
1483af6ab5fSopenharmony_ci  
1493af6ab5fSopenharmony_ci  public static isInClassDeclarationForTest(node: Node | undefined): boolean {
1503af6ab5fSopenharmony_ci    return NodeUtils.isInClassDeclaration(node);
1513af6ab5fSopenharmony_ci  }
1523af6ab5fSopenharmony_ci
1533af6ab5fSopenharmony_ci  private static isInExpression(node: Node | undefined): boolean {
1543af6ab5fSopenharmony_ci    return !!node && NodeUtils.isInOperator(node);
1553af6ab5fSopenharmony_ci  }
1563af6ab5fSopenharmony_ci
1573af6ab5fSopenharmony_ci  public static isInExpressionForTest(node: Node | undefined): boolean {
1583af6ab5fSopenharmony_ci    return NodeUtils.isInExpression(node);
1593af6ab5fSopenharmony_ci  }
1603af6ab5fSopenharmony_ci
1613af6ab5fSopenharmony_ci  private static isInOperator(node: Node): boolean {
1623af6ab5fSopenharmony_ci    return isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.InKeyword;
1633af6ab5fSopenharmony_ci  }
1643af6ab5fSopenharmony_ci
1653af6ab5fSopenharmony_ci  public static isInOperatorForTest(node: Node | undefined): boolean {
1663af6ab5fSopenharmony_ci    return NodeUtils.isInOperator(node);
1673af6ab5fSopenharmony_ci  }
1683af6ab5fSopenharmony_ci
1693af6ab5fSopenharmony_ci  public static isElementAccessNode(node: Node): boolean {
1703af6ab5fSopenharmony_ci    let parent: Node | undefined = node.parent;
1713af6ab5fSopenharmony_ci    if (!parent) {
1723af6ab5fSopenharmony_ci      return false;
1733af6ab5fSopenharmony_ci    }
1743af6ab5fSopenharmony_ci
1753af6ab5fSopenharmony_ci    return isElementAccessExpression(parent) && parent.argumentExpression === node;
1763af6ab5fSopenharmony_ci  }
1773af6ab5fSopenharmony_ci
1783af6ab5fSopenharmony_ci  public static isClassPropertyInConstructorParams(node: Node): boolean {
1793af6ab5fSopenharmony_ci    if (!isIdentifier(node)) {
1803af6ab5fSopenharmony_ci      return false;
1813af6ab5fSopenharmony_ci    }
1823af6ab5fSopenharmony_ci
1833af6ab5fSopenharmony_ci    if (!node.parent || !isParameter(node.parent)) {
1843af6ab5fSopenharmony_ci      return false;
1853af6ab5fSopenharmony_ci    }
1863af6ab5fSopenharmony_ci
1873af6ab5fSopenharmony_ci    const modifiers = getModifiers(node.parent);
1883af6ab5fSopenharmony_ci    if (!modifiers || modifiers.length === 0 || !modifiers.find(modifier => isParameterPropertyModifier(modifier))) {
1893af6ab5fSopenharmony_ci      return false;
1903af6ab5fSopenharmony_ci    }
1913af6ab5fSopenharmony_ci
1923af6ab5fSopenharmony_ci    return node.parent.parent && isConstructorDeclaration(node.parent.parent);
1933af6ab5fSopenharmony_ci  }
1943af6ab5fSopenharmony_ci
1953af6ab5fSopenharmony_ci  public static isClassPropertyInConstructorBody(node: Node, constructorParams: Set<string>): boolean {
1963af6ab5fSopenharmony_ci    if (!isIdentifier(node)) {
1973af6ab5fSopenharmony_ci      return false;
1983af6ab5fSopenharmony_ci    }
1993af6ab5fSopenharmony_ci
2003af6ab5fSopenharmony_ci    const id: string = node.escapedText.toString();
2013af6ab5fSopenharmony_ci    let curNode: Node = node.parent;
2023af6ab5fSopenharmony_ci    while (curNode) {
2033af6ab5fSopenharmony_ci      if (isConstructorDeclaration(curNode) && constructorParams.has(id)) {
2043af6ab5fSopenharmony_ci        return true;
2053af6ab5fSopenharmony_ci      }
2063af6ab5fSopenharmony_ci
2073af6ab5fSopenharmony_ci      curNode = curNode.parent;
2083af6ab5fSopenharmony_ci    }
2093af6ab5fSopenharmony_ci
2103af6ab5fSopenharmony_ci    return false;
2113af6ab5fSopenharmony_ci  }
2123af6ab5fSopenharmony_ci
2133af6ab5fSopenharmony_ci  public static isPropertyNode(node: Node): boolean {
2143af6ab5fSopenharmony_ci    if (this.isPropertyOrElementAccessNode(node)) {
2153af6ab5fSopenharmony_ci      return true;
2163af6ab5fSopenharmony_ci    }
2173af6ab5fSopenharmony_ci
2183af6ab5fSopenharmony_ci    return this.isPropertyDeclarationNode(node);
2193af6ab5fSopenharmony_ci  }
2203af6ab5fSopenharmony_ci
2213af6ab5fSopenharmony_ci  public static isObjectBindingPatternAssignment(node: ObjectBindingPattern): boolean {
2223af6ab5fSopenharmony_ci    if (!node || !node.parent || !isVariableDeclaration(node.parent)) {
2233af6ab5fSopenharmony_ci      return false;
2243af6ab5fSopenharmony_ci    }
2253af6ab5fSopenharmony_ci
2263af6ab5fSopenharmony_ci    const initializer: Expression = node.parent.initializer;
2273af6ab5fSopenharmony_ci    return initializer && isCallExpression(initializer);
2283af6ab5fSopenharmony_ci  }
2293af6ab5fSopenharmony_ci
2303af6ab5fSopenharmony_ci  public static isDeclarationFile(node: SourceFile): boolean {
2313af6ab5fSopenharmony_ci    return node.isDeclarationFile;
2323af6ab5fSopenharmony_ci  }
2333af6ab5fSopenharmony_ci
2343af6ab5fSopenharmony_ci  public static getSourceFileOfNode(node: Node): SourceFile {
2353af6ab5fSopenharmony_ci    while (node && node.kind !== SyntaxKind.SourceFile) {
2363af6ab5fSopenharmony_ci      node = node.parent;
2373af6ab5fSopenharmony_ci    }
2383af6ab5fSopenharmony_ci    return <SourceFile>node;
2393af6ab5fSopenharmony_ci  }
2403af6ab5fSopenharmony_ci
2413af6ab5fSopenharmony_ci  public static isDETSFile(node: Node | undefined): boolean {
2423af6ab5fSopenharmony_ci    return !!node && NodeUtils.getSourceFileOfNode(node).fileName.endsWith(Extension.DETS);
2433af6ab5fSopenharmony_ci  }
2443af6ab5fSopenharmony_ci
2453af6ab5fSopenharmony_ci  public static isNewTargetNode(node: Identifier): boolean {
2463af6ab5fSopenharmony_ci    if (isMetaProperty(node.parent) && node.parent.keywordToken === SyntaxKind.NewKeyword && node.escapedText === 'target') {
2473af6ab5fSopenharmony_ci      return true;
2483af6ab5fSopenharmony_ci    }
2493af6ab5fSopenharmony_ci    return false;
2503af6ab5fSopenharmony_ci  }
2513af6ab5fSopenharmony_ci
2523af6ab5fSopenharmony_ci  public static findSymbolOfIdentifier(checker: TypeChecker, node: Identifier): Symbol | undefined {
2533af6ab5fSopenharmony_ci    let sym: Symbol | undefined = checker.getSymbolAtLocation(node);
2543af6ab5fSopenharmony_ci    if (!sym || (sym && sym.name !== 'default')) {
2553af6ab5fSopenharmony_ci      return sym;
2563af6ab5fSopenharmony_ci    }
2573af6ab5fSopenharmony_ci    /* Handle default exports, eg. export default class Ability {};
2583af6ab5fSopenharmony_ci       The expected symbol we want to find to obfuscate is named "Ability",
2593af6ab5fSopenharmony_ci       but `getSymbolAtLocation` will return the symbol named "default", so we need to continue to search.
2603af6ab5fSopenharmony_ci    */
2613af6ab5fSopenharmony_ci    let localSyms: Symbol[] = checker.getSymbolsInScope(node, sym.flags);
2623af6ab5fSopenharmony_ci    for (let i = 0; i < localSyms.length; i++) {
2633af6ab5fSopenharmony_ci      const localSym = localSyms[i];
2643af6ab5fSopenharmony_ci      // `localSym` named "Ability" has property `exportSymbol` named "default" that we find by `getSymbolAtLocation`,
2653af6ab5fSopenharmony_ci      // So the `localSym` is what we want to obfuscate.
2663af6ab5fSopenharmony_ci      if (localSym && localSym.name === node.text && localSym.exportSymbol === sym) {
2673af6ab5fSopenharmony_ci        sym = localSym;
2683af6ab5fSopenharmony_ci        break;
2693af6ab5fSopenharmony_ci      }
2703af6ab5fSopenharmony_ci    }
2713af6ab5fSopenharmony_ci    return sym;
2723af6ab5fSopenharmony_ci  }
2733af6ab5fSopenharmony_ci}
2743af6ab5fSopenharmony_ci
2753af6ab5fSopenharmony_ci/**
2763af6ab5fSopenharmony_ci * When enabling property obfuscation, collect the properties of struct.
2773af6ab5fSopenharmony_ci * When enabling property obfuscation and the compilation output is a TS file,
2783af6ab5fSopenharmony_ci * collect the Identifier names in the initialization expressions of enum members.
2793af6ab5fSopenharmony_ci */
2803af6ab5fSopenharmony_ciexport function collectReservedNameForObf(obfuscationConfig: MergedConfig | undefined, shouldTransformToJs: boolean): TransformerFactory<SourceFile> {
2813af6ab5fSopenharmony_ci  const disableObf = obfuscationConfig?.options === undefined || obfuscationConfig.options.disableObfuscation;
2823af6ab5fSopenharmony_ci  const enablePropertyObf = obfuscationConfig?.options.enablePropertyObfuscation;
2833af6ab5fSopenharmony_ci  // process.env.compiler === 'on': indicates that during the Webpack packaging process,
2843af6ab5fSopenharmony_ci  // the code is executed here for the first time.
2853af6ab5fSopenharmony_ci  // During the Webpack packaging process, this step will be executed twice,
2863af6ab5fSopenharmony_ci  // but only the first time will it perform subsequent operations to prevent repetition.
2873af6ab5fSopenharmony_ci  const shouldCollect = (process.env.compiler === 'on' || process.env.compileTool === 'rollup') &&
2883af6ab5fSopenharmony_ci                        !disableObf && enablePropertyObf;
2893af6ab5fSopenharmony_ci
2903af6ab5fSopenharmony_ci  return (context: TransformationContext) => {
2913af6ab5fSopenharmony_ci    return (node: SourceFile) => {
2923af6ab5fSopenharmony_ci      if (shouldCollect) {
2933af6ab5fSopenharmony_ci        node = visitEachChild(node, collectReservedNames, context);
2943af6ab5fSopenharmony_ci      }
2953af6ab5fSopenharmony_ci      return node;
2963af6ab5fSopenharmony_ci    };
2973af6ab5fSopenharmony_ci
2983af6ab5fSopenharmony_ci    function collectReservedNames(node: Node): Node {
2993af6ab5fSopenharmony_ci      // collect properties of struct
3003af6ab5fSopenharmony_ci      if (isClassDeclaration(node) && isViewPUBasedClass(node)) {
3013af6ab5fSopenharmony_ci        getViewPUClassProperties(node);
3023af6ab5fSopenharmony_ci      }
3033af6ab5fSopenharmony_ci
3043af6ab5fSopenharmony_ci      // collect enum properties
3053af6ab5fSopenharmony_ci      if (!shouldTransformToJs && isEnumMember(node) && node.initializer) {
3063af6ab5fSopenharmony_ci        node.initializer.forEachChild(visitEnumInitializer);
3073af6ab5fSopenharmony_ci        return node;
3083af6ab5fSopenharmony_ci      }
3093af6ab5fSopenharmony_ci
3103af6ab5fSopenharmony_ci      return visitEachChild(node, collectReservedNames, context);
3113af6ab5fSopenharmony_ci    }
3123af6ab5fSopenharmony_ci  };
3133af6ab5fSopenharmony_ci}