123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- import * as escodegen from 'escodegen-wallaby';
- import * as esprima from 'esprima';
- import * as estraverse from 'estraverse';
- import * as ESTree from 'estree';
- import { TNodeWithBlockStatement } from '../types/node/TNodeWithBlockStatement';
- import { TStatement } from '../types/node/TStatement';
- import { NodeType } from '../enums/NodeType';
- import { Node } from './Node';
- import { Nodes } from './Nodes';
- export class NodeUtils {
- /**
- * @type {string[]}
- */
- private static readonly nodesWithBlockScope: string[] = [
- NodeType.ArrowFunctionExpression,
- NodeType.FunctionDeclaration,
- NodeType.FunctionExpression,
- NodeType.MethodDefinition,
- NodeType.Program
- ];
- /**
- * @param {T} astTree
- * @returns {T}
- */
- public static addXVerbatimPropertyToLiterals <T extends ESTree.Node> (astTree: T): T {
- NodeUtils.typedReplace(astTree, NodeType.Literal, {
- leave: (literalNode: ESTree.Literal) => {
- literalNode['x-verbatim-property'] = {
- content : literalNode.raw,
- precedence: escodegen.Precedence.Primary
- };
- }
- });
- return astTree;
- }
- /**
- * @param {T} astTree
- * @returns {T}
- */
- public static clone <T extends ESTree.Node> (astTree: T): T {
- /**
- * @param {T} node
- * @returns {T}
- */
- const cloneRecursive: (node: T) => T = (node: T) => {
- if (node === null) {
- return node;
- }
- const copy: {[key: string]: any} = {};
- Object
- .keys(node)
- .filter((property: string) => property !== 'parentNode')
- .forEach((property: string): void => {
- const value: any = (<{[key: string]: any}>node)[property];
- let clonedValue: any | null;
- if (value === null || value instanceof RegExp) {
- clonedValue = value;
- } else if (Array.isArray(value)) {
- clonedValue = value.map(cloneRecursive);
- } else if (typeof value === 'object') {
- clonedValue = cloneRecursive(value);
- } else {
- clonedValue = value;
- }
- copy[property] = clonedValue;
- });
- return <T>copy;
- };
- return NodeUtils.parentize(cloneRecursive(astTree));
- }
- /**
- * @param {string} code
- * @returns {TStatement[]}
- */
- public static convertCodeToStructure (code: string): TStatement[] {
- let structure: ESTree.Program = esprima.parse(code);
- structure = NodeUtils.addXVerbatimPropertyToLiterals(structure);
- structure = NodeUtils.parentize(structure);
- return structure.body;
- }
- /**
- * @param {Node[]} structure
- * @returns {string}
- */
- public static convertStructureToCode (structure: ESTree.Node[]): string {
- let code: string = '';
- structure.forEach((node: ESTree.Node) => {
- code += escodegen.generate(node, {
- sourceMapWithCode: true
- }).code;
- });
- return code;
- }
- /**
- * @param {Node} node
- * @param {number} index
- * @returns {Node}
- */
- public static getBlockStatementNodeByIndex (node: ESTree.Node, index: number = 0): ESTree.Node {
- if (Node.isNodeHasBlockStatement(node)) {
- if (node.body[index] === undefined) {
- throw new ReferenceError(`Wrong index \`${index}\`. Block-statement body length is \`${node.body.length}\``);
- }
- return node.body[index];
- }
- throw new TypeError('The specified node have no a block-statement');
- }
- /**
- * @param {Node} node
- * @param {TNodeWithBlockStatement[]} blockScopes
- * @returns {TNodeWithBlockStatement[]}
- */
- public static getBlockScopesOfNode (node: ESTree.Node, blockScopes: TNodeWithBlockStatement[] = []): TNodeWithBlockStatement[] {
- const parentNode: ESTree.Node | undefined = node.parentNode;
- if (!parentNode) {
- throw new ReferenceError('`parentNode` property of given node is `undefined`');
- }
- if (Node.isBlockStatementNode(parentNode)) {
- if (!parentNode.parentNode) {
- throw new ReferenceError('`parentNode` property of `parentNode` of given node is `undefined`');
- }
- if (NodeUtils.nodesWithBlockScope.includes(parentNode.parentNode.type)) {
- blockScopes.push(parentNode);
- }
- }
- if (!Node.isProgramNode(parentNode)) {
- return NodeUtils.getBlockScopesOfNode(parentNode, blockScopes);
- }
- blockScopes.push(parentNode);
- return blockScopes;
- }
- /**
- * @param {Node} node
- * @param {number} depth
- * @returns {number}
- */
- public static getNodeBlockScopeDepth (node: ESTree.Node, depth: number = 0): number {
- const parentNode: ESTree.Node | undefined = node.parentNode;
- if (!parentNode) {
- throw new ReferenceError('`parentNode` property of given node is `undefined`');
- }
- if (Node.isProgramNode(parentNode)) {
- return depth;
- }
- if (Node.isBlockStatementNode(node) && NodeUtils.nodesWithBlockScope.includes(parentNode.type)) {
- return NodeUtils.getNodeBlockScopeDepth(parentNode, ++depth);
- }
- return NodeUtils.getNodeBlockScopeDepth(parentNode, depth);
- }
- /**
- * @param {UnaryExpression} unaryExpressionNode
- * @returns {Node}
- */
- public static getUnaryExpressionArgumentNode (unaryExpressionNode: ESTree.UnaryExpression): ESTree.Node {
- if (Node.isUnaryExpressionNode(unaryExpressionNode.argument)) {
- return NodeUtils.getUnaryExpressionArgumentNode(unaryExpressionNode.argument);
- }
- return unaryExpressionNode.argument;
- }
- /**
- * @param {T} astTree
- * @returns {T}
- */
- public static parentize <T extends ESTree.Node> (astTree: T): T {
- let isRootNode: boolean = true;
- estraverse.traverse(astTree, {
- enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
- let value: ESTree.Node;
- if (isRootNode) {
- if (node.type === NodeType.Program) {
- value = node;
- } else {
- value = Nodes.getProgramNode(<TStatement[]>[node]);
- value.parentNode = value;
- }
- isRootNode = false;
- } else {
- value = parentNode || node;
- }
- node.parentNode = value;
- node.obfuscatedNode = false;
- }
- });
- return astTree;
- }
- /**
- * @param {Node} astTree
- * @param {string} nodeType
- * @param {visitor} visitor
- */
- public static typedReplace (
- astTree: ESTree.Node,
- nodeType: string,
- visitor: {enter?: (node: ESTree.Node) => void, leave?: (node: ESTree.Node) => void},
- ): void {
- NodeUtils.typedTraverse(astTree, nodeType, visitor, 'replace');
- }
- /**
- * @param {Node} astTree
- * @param {string} nodeType
- * @param {Visitor} visitor
- * @param {string} traverseType
- */
- public static typedTraverse (
- astTree: ESTree.Node,
- nodeType: string,
- visitor: estraverse.Visitor,
- traverseType: string = 'traverse'
- ): void {
- (<any>estraverse)[traverseType](astTree, {
- enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
- if (node.type === nodeType && visitor.enter) {
- return visitor.enter(node, parentNode);
- }
- },
- leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
- if (node.type === nodeType && visitor.leave) {
- return visitor.leave(node, parentNode);
- }
- }
- });
- }
- }
|