NodeUtils.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import * as escodegen from '@javascript-obfuscator/escodegen';
  2. import * as estraverse from '@javascript-obfuscator/estraverse';
  3. import * as ESTree from 'estree';
  4. import { ecmaVersion } from '../constants/EcmaVersion';
  5. import { ASTParserFacade } from '../ASTParserFacade';
  6. import { NodeGuards } from './NodeGuards';
  7. import { NodeMetadata } from './NodeMetadata';
  8. export class NodeUtils {
  9. /**
  10. * @param {ESTree.Literal} literalNode
  11. * @returns {ESTree.Literal}
  12. */
  13. public static addXVerbatimPropertyTo (literalNode: ESTree.Literal): ESTree.Literal {
  14. literalNode['x-verbatim-property'] = {
  15. content: literalNode.raw,
  16. precedence: escodegen.Precedence.Primary
  17. };
  18. return literalNode;
  19. }
  20. /**
  21. * @param {T} astTree
  22. * @returns {T}
  23. */
  24. public static clone <T extends ESTree.Node = ESTree.Node> (astTree: T): T {
  25. return NodeUtils.parentizeAst(NodeUtils.cloneRecursive(astTree));
  26. }
  27. /**
  28. * @param {string} code
  29. * @returns {ESTree.Statement[]}
  30. */
  31. public static convertCodeToStructure (code: string): ESTree.Statement[] {
  32. const structure: ESTree.Program = ASTParserFacade.parse(
  33. code,
  34. {
  35. ecmaVersion,
  36. sourceType: 'script'
  37. }
  38. );
  39. estraverse.replace(structure, {
  40. enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node => {
  41. NodeUtils.parentizeNode(node, parentNode);
  42. if (NodeGuards.isLiteralNode(node)) {
  43. NodeUtils.addXVerbatimPropertyTo(node);
  44. }
  45. NodeMetadata.set(node, { ignoredNode: false });
  46. return node;
  47. }
  48. });
  49. return <ESTree.Statement[]>structure.body;
  50. }
  51. /**
  52. * @param {NodeGuards[]} structure
  53. * @returns {string}
  54. */
  55. public static convertStructureToCode (structure: ESTree.Node[]): string {
  56. return structure.reduce((code: string, node: ESTree.Node) => {
  57. return code + escodegen.generate(node, {
  58. sourceMapWithCode: true
  59. }).code;
  60. }, '');
  61. }
  62. /**
  63. * @param {UnaryExpression} unaryExpressionNode
  64. * @returns {NodeGuards}
  65. */
  66. public static getUnaryExpressionArgumentNode (unaryExpressionNode: ESTree.UnaryExpression): ESTree.Node {
  67. if (NodeGuards.isUnaryExpressionNode(unaryExpressionNode.argument)) {
  68. return NodeUtils.getUnaryExpressionArgumentNode(unaryExpressionNode.argument);
  69. }
  70. return unaryExpressionNode.argument;
  71. }
  72. /**
  73. * @param {T} astTree
  74. * @returns {T}
  75. */
  76. public static parentizeAst <T extends ESTree.Node = ESTree.Node> (astTree: T): T {
  77. const parentNode: ESTree.Node | null = astTree.parentNode ?? null;
  78. estraverse.replace(astTree, {
  79. enter: NodeUtils.parentizeNode
  80. });
  81. if (parentNode) {
  82. astTree.parentNode = parentNode;
  83. }
  84. return astTree;
  85. }
  86. /**
  87. * @param {T} node
  88. * @param {Node} parentNode
  89. * @returns {T}
  90. */
  91. public static parentizeNode <T extends ESTree.Node = ESTree.Node> (node: T, parentNode: ESTree.Node | null): T {
  92. node.parentNode = parentNode ?? node;
  93. return node;
  94. }
  95. /**
  96. * @param {T} node
  97. * @returns {T}
  98. */
  99. private static cloneRecursive <T> (node: T): T {
  100. if (node === null) {
  101. return node;
  102. }
  103. const copy: Partial<T> = {};
  104. const nodeKeys: (keyof T)[] = <(keyof T)[]>Object.keys(node);
  105. nodeKeys
  106. .forEach((property: keyof T) => {
  107. if (property === 'parentNode') {
  108. return;
  109. }
  110. const value: T[keyof T] | T[keyof T][] | null = node[property] ?? null;
  111. let clonedValue: T[keyof T] | T[keyof T][] | null;
  112. if (value === null || value instanceof RegExp) {
  113. clonedValue = value;
  114. } else if (value instanceof Array) {
  115. clonedValue = value.map(NodeUtils.cloneRecursive);
  116. } else if (typeof value === 'object') {
  117. clonedValue = NodeUtils.cloneRecursive(value);
  118. } else {
  119. clonedValue = value;
  120. }
  121. copy[property] = <T[keyof T]>clonedValue;
  122. });
  123. return <T>copy;
  124. }
  125. }