NodeUtils.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import * as escodegen from 'escodegen';
  2. import * as esprima from 'esprima';
  3. import * as estraverse from 'estraverse';
  4. import * as ESTree from 'estree';
  5. import { TNodeWithBlockStatement } from './types/TNodeWithBlockStatement';
  6. import { TStatement } from './types/TStatement';
  7. import { NodeType } from './enums/NodeType';
  8. import { Nodes } from './Nodes';
  9. import { Utils } from './Utils';
  10. export class NodeUtils {
  11. /**
  12. * @type {string[]}
  13. */
  14. private static nodesWithBlockScope: string[] = [
  15. NodeType.ArrowFunctionExpression,
  16. NodeType.FunctionDeclaration,
  17. NodeType.FunctionExpression,
  18. NodeType.MethodDefinition,
  19. NodeType.Program
  20. ];
  21. /**
  22. * @param node
  23. */
  24. public static addXVerbatimPropertyToLiterals (node: ESTree.Node): void {
  25. NodeUtils.typedReplace(node, NodeType.Literal, {
  26. leave: (node: ESTree.Literal) => {
  27. node['x-verbatim-property'] = {
  28. content : node.raw,
  29. precedence: escodegen.Precedence.Primary
  30. };
  31. }
  32. });
  33. }
  34. /**
  35. * @param code
  36. * @returns {TStatement[]}
  37. */
  38. public static convertCodeToStructure (code: string): TStatement[] {
  39. let structure: ESTree.Program = esprima.parse(code);
  40. NodeUtils.addXVerbatimPropertyToLiterals(structure);
  41. NodeUtils.parentize(structure);
  42. return <TStatement[]>structure.body;
  43. }
  44. /**
  45. * @param node
  46. * @param index
  47. * @returns {ESTree.Node}
  48. */
  49. public static getBlockStatementNodeByIndex (node: ESTree.Node, index: number = 0): ESTree.Node {
  50. if (Nodes.isNodeHasBlockStatement(node)) {
  51. if (node.body[index] === undefined) {
  52. throw new ReferenceError(`Wrong index \`${index}\`. Block-statement body length is \`${node.body.length}\``);
  53. }
  54. return node.body[index];
  55. }
  56. throw new TypeError('The specified node have no a block-statement');
  57. }
  58. /**
  59. * @param node
  60. * @param depth
  61. * @returns {ESTree.Node}
  62. */
  63. public static getBlockScopeOfNode (node: ESTree.Node, depth: number = 0): TNodeWithBlockStatement {
  64. let parentNode: ESTree.Node | undefined = node.parentNode;
  65. if (!parentNode) {
  66. throw new ReferenceError('`parentNode` property of given node is `undefined`');
  67. }
  68. if (Nodes.isBlockStatementNode(parentNode)) {
  69. if (!parentNode.parentNode) {
  70. throw new ReferenceError('`parentNode` property of `parentNode` of given node is `undefined`');
  71. }
  72. if (!Utils.arrayContains(NodeUtils.nodesWithBlockScope, parentNode.parentNode.type)) {
  73. return NodeUtils.getBlockScopeOfNode(parentNode, depth);
  74. } else if (depth > 0) {
  75. return NodeUtils.getBlockScopeOfNode(parentNode, --depth);
  76. }
  77. return parentNode;
  78. }
  79. if (Nodes.isProgramNode(parentNode)) {
  80. return parentNode;
  81. }
  82. return NodeUtils.getBlockScopeOfNode(parentNode);
  83. }
  84. /**
  85. * @param node
  86. */
  87. public static parentize (node: ESTree.Node): void {
  88. let isRootNode: boolean = true;
  89. estraverse.replace(node, {
  90. enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  91. let value: ESTree.Node;
  92. if (isRootNode) {
  93. if (node.type === NodeType.Program) {
  94. value = node;
  95. } else {
  96. value = Nodes.getProgramNode(<TStatement[]>[node]);
  97. value['parentNode'] = value;
  98. }
  99. isRootNode = false;
  100. } else {
  101. value = parentNode || node;
  102. }
  103. node['parentNode'] = value;
  104. node['obfuscated'] = false;
  105. }
  106. });
  107. }
  108. /**
  109. * @param node
  110. * @param nodeType
  111. * @param visitor
  112. */
  113. public static typedReplace (
  114. node: ESTree.Node,
  115. nodeType: string,
  116. visitor: {enter?: (node: ESTree.Node) => void, leave?: (node: ESTree.Node) => void},
  117. ): void {
  118. NodeUtils.typedTraverse(node, nodeType, visitor, 'replace');
  119. }
  120. /**
  121. * @param node
  122. * @param nodeType
  123. * @param visitor
  124. * @param traverseType
  125. */
  126. public static typedTraverse (
  127. node: ESTree.Node,
  128. nodeType: string,
  129. visitor: estraverse.Visitor,
  130. traverseType: string = 'traverse'
  131. ): void {
  132. (<any>estraverse)[traverseType](node, {
  133. enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  134. if (node.type === nodeType && visitor.enter) {
  135. visitor.enter(node, parentNode);
  136. }
  137. },
  138. leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  139. if (node.type === nodeType && visitor.leave) {
  140. visitor.leave(node, parentNode);
  141. }
  142. }
  143. });
  144. }
  145. }