From d71f7cc5f5abea3ce0bcc41abe32b2876c9b0d69 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sat, 14 Dec 2024 22:21:44 +0200 Subject: [PATCH 1/8] Refactor code to use `estraverse.traverse` only to extract the nodes, and move the rest of the logic out of the `traverse` method. --- src/flast.js | 92 ++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/src/flast.js b/src/flast.js index c743751..abc124e 100644 --- a/src/flast.js +++ b/src/flast.js @@ -19,31 +19,10 @@ function parseCode(inputCode, opts = {}) { } const excludedParentKeys = [ - 'type', 'start', 'end', 'range', 'sourceType', 'comments', 'srcClosure', 'nodeId', - 'childNodes', 'parentNode', 'parentKey', 'scope', 'typeMap', 'lineage', 'allScopes', + 'type', 'start', 'end', 'range', 'sourceType', 'comments', 'srcClosure', 'nodeId', 'leadingComments', 'trailingComments', + 'childNodes', 'parentNode', 'parentKey', 'scope', 'typeMap', 'lineage', 'allScopes', 'tokens', ]; -/** - * Return the key the child node is assigned in the parent node if applicable; null otherwise. - * @param {ASTNode} node - * @returns {string|null} - */ -function getParentKey(node) { - if (node.parentNode) { - const keys = Object.keys(node.parentNode); - for (let i = 0; i < keys.length; i++) { - if (excludedParentKeys.includes(keys[i])) continue; - if (node.parentNode[keys[i]] === node) return keys[i]; - if (Array.isArray(node.parentNode[keys[i]])) { - for (let j = 0; j < node.parentNode[keys[i]]?.length; j++) { - if (node.parentNode[keys[i]][j] === node) return keys[i]; - } - } - } - } - return null; -} - const generateFlatASTDefaultOptions = { // If false, do not include any scope details detailed: true, @@ -126,36 +105,51 @@ function generateRootNode(inputCode, opts = {}) { function extractNodesFromRoot(rootNode, opts) { opts = { ...generateFlatASTDefaultOptions, ...opts }; - const tree = []; let nodeId = 0; const typeMap = {}; - - // noinspection JSUnusedGlobalSymbols - estraverse.traverse(rootNode, { - /** - * @param {ASTNode} node - * @param {ASTNode} parentNode - */ - enter(node, parentNode) { - tree.push(node); - node.nodeId = nodeId++; - if (!typeMap[node.type]) typeMap[node.type] = [node]; - else typeMap[node.type].push(node); - node.childNodes = []; - node.parentNode = parentNode; - node.parentKey = parentNode ? getParentKey(node) : ''; - node.lineage = [...parentNode?.lineage || []]; - if (parentNode) { - node.lineage.push(parentNode.nodeId); - parentNode.childNodes.push(node); + const allNodes = []; + const stack = [rootNode]; + while (stack.length) { + const node = stack.shift(); + node.childNodes = node.childNodes || []; + const childrenLoc = {}; // Store the location of child nodes to sort them by order + if (!node.parentKey) node.parentKey = ''; + const keys = Object.keys(node); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (excludedParentKeys.includes(key)) continue; + const content = node[key]; + if (content && typeof content === 'object') { + if (Array.isArray(content)) { + for (let j = 0; j < content.length; j++) { + const childNode = content[j]; + childNode.parentNode = node; + childNode.parentKey = key; + childrenLoc[childNode.start] = childNode; + } + } else { + content.parentNode = node; + content.parentKey = key; + childrenLoc[content.start] = content; + } } - if (opts.includeSrc && !node.src) Object.defineProperty(node, 'src', { - get() { return rootNode.srcClosure(node.range[0], node.range[1]);}, - }); } - }); - if (tree?.length) tree[0].typeMap = typeMap; - return tree; + stack.unshift(...Object.values(childrenLoc)); + allNodes.push(node); + node.nodeId = nodeId++; + if (!typeMap[node.type]) typeMap[node.type] = [node]; + else typeMap[node.type].push(node); + node.lineage = [...node.parentNode?.lineage || []]; + if (node.parentNode) { + node.lineage.push(node.parentNode.nodeId); + node.parentNode.childNodes.push(node); + } + if (opts.includeSrc && !node.src) Object.defineProperty(node, 'src', { + get() { return rootNode.srcClosure(node.start, node.end);}, + }); + } + if (allNodes?.length) allNodes[0].typeMap = typeMap; + return allNodes; } /** From bbd4766c3a61dfd92c4fcb2d94334138ea4ddf79 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sat, 14 Dec 2024 22:22:13 +0200 Subject: [PATCH 2/8] Fix printing the node instead of the nodeId --- tests/functionality.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functionality.test.js b/tests/functionality.test.js index 369969b..5c0068c 100644 --- a/tests/functionality.test.js +++ b/tests/functionality.test.js @@ -21,7 +21,7 @@ describe('Functionality tests', () => { expectedBreakdown.forEach(node => { const parsedNode = ast[node.nodeId]; for (const [k, v] of Object.entries(node)) { - assert.equal(v, parsedNode[k], `Node #${parsedNode[k]} parsed wrong on key '${k}'`); + assert.equal(v, parsedNode[k], `Node #${node.nodeId} parsed wrong on key '${k}'`); } }); }); From c7b8ac6ff4633676a3518ab14fa91c94aee05fc9 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sat, 14 Dec 2024 22:48:02 +0200 Subject: [PATCH 3/8] Fix scopes are collected before nodeId is given by numbering the scopes according to the start location of the scope node. --- src/flast.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/flast.js b/src/flast.js index abc124e..4aec0e9 100644 --- a/src/flast.js +++ b/src/flast.js @@ -53,16 +53,11 @@ function createSrcClosure(src) { * @return {ASTNode[]} An array of flattened AST */ function generateFlatAST(inputCode, opts = {}) { - opts = { ...generateFlatASTDefaultOptions, ...opts }; + opts = {...generateFlatASTDefaultOptions, ...opts}; let tree = []; const rootNode = generateRootNode(inputCode, opts); if (rootNode) { tree = extractNodesFromRoot(rootNode, opts); - if (opts.detailed) { - const scopes = getAllScopes(rootNode); - for (let i = 0; i < tree.length; i++) injectScopeToNode(tree[i], scopes); - tree[0].allScopes = scopes; - } } return tree; } @@ -90,7 +85,7 @@ function generateCode(rootNode, opts = {}) { } function generateRootNode(inputCode, opts = {}) { - opts = { ...generateFlatASTDefaultOptions, ...opts }; + opts = {...generateFlatASTDefaultOptions, ...opts}; const parseOpts = opts.parseOpts || {}; let rootNode; try { @@ -104,10 +99,11 @@ function generateRootNode(inputCode, opts = {}) { } function extractNodesFromRoot(rootNode, opts) { - opts = { ...generateFlatASTDefaultOptions, ...opts }; + opts = {...generateFlatASTDefaultOptions, ...opts}; let nodeId = 0; const typeMap = {}; const allNodes = []; + const scopes = opts.detailed ? getAllScopes(rootNode) : {}; const stack = [rootNode]; while (stack.length) { const node = stack.shift(); @@ -141,14 +137,16 @@ function extractNodesFromRoot(rootNode, opts) { else typeMap[node.type].push(node); node.lineage = [...node.parentNode?.lineage || []]; if (node.parentNode) { - node.lineage.push(node.parentNode.nodeId); - node.parentNode.childNodes.push(node); + node.lineage.push(node.parentNode.start); } + node.childNodes.push(...Object.values(childrenLoc)); if (opts.includeSrc && !node.src) Object.defineProperty(node, 'src', { get() { return rootNode.srcClosure(node.start, node.end);}, }); + if (opts.detailed) injectScopeToNode(node, scopes); } if (allNodes?.length) allNodes[0].typeMap = typeMap; + if (opts.detailed) allNodes[0].allScopes = scopes; return allNodes; } @@ -238,7 +236,7 @@ function getAllScopes(rootNode) { let scope = stack.pop(); if (seen.includes(scope)) continue; seen.push(scope); - const scopeId = scope.block.nodeId; + const scopeId = scope.block.start; scope.block.isScopeBlock = true; if (!allScopes[scopeId]) { allScopes[scopeId] = scope; From 90ae3d28a15957e0e25a54d669d347a66dd632e1 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sun, 15 Dec 2024 09:49:38 +0200 Subject: [PATCH 4/8] Refactor code to improve performance. Add comments explaining the process. Remove async functions as they are no longer more performant than the sync versions --- src/flast.js | 133 ++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 70 deletions(-) diff --git a/src/flast.js b/src/flast.js index 4aec0e9..b789943 100644 --- a/src/flast.js +++ b/src/flast.js @@ -5,6 +5,7 @@ import {logger} from './utils/logger.js'; import {generate, attachComments} from 'escodegen'; const ecmaVersion = 'latest'; +const currentYear = (new Date()).getFullYear(); const sourceType = 'module'; /** @@ -84,6 +85,11 @@ function generateCode(rootNode, opts = {}) { return generate(rootNode, { ...generateCodeDefaultOptions, ...opts }); } +/** + * @param {string} inputCode + * @param {object} [opts] + * @return {ASTNode} + */ function generateRootNode(inputCode, opts = {}) { opts = {...generateFlatASTDefaultOptions, ...opts}; const parseOpts = opts.parseOpts || {}; @@ -98,24 +104,34 @@ function generateRootNode(inputCode, opts = {}) { return rootNode; } +/** + * @param rootNode + * @param opts + * @return {ASTNode[]} + */ function extractNodesFromRoot(rootNode, opts) { opts = {...generateFlatASTDefaultOptions, ...opts}; let nodeId = 0; const typeMap = {}; const allNodes = []; const scopes = opts.detailed ? getAllScopes(rootNode) : {}; + const stack = [rootNode]; while (stack.length) { const node = stack.shift(); + if (node.nodeId) continue; node.childNodes = node.childNodes || []; const childrenLoc = {}; // Store the location of child nodes to sort them by order - if (!node.parentKey) node.parentKey = ''; + node.parentKey = node.parentKey || ''; // Make sure parentKey exists + // Iterate over all keys of the node to find child nodes const keys = Object.keys(node); for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (excludedParentKeys.includes(key)) continue; const content = node[key]; if (content && typeof content === 'object') { + // Sort each child node by its start position + // and set the parentNode and parentKey attributes if (Array.isArray(content)) { for (let j = 0; j < content.length; j++) { const childNode = content[j]; @@ -130,23 +146,25 @@ function extractNodesFromRoot(rootNode, opts) { } } } + // Add the child nodes to top of the stack and populate the node's childNodes array stack.unshift(...Object.values(childrenLoc)); + node.childNodes.push(...Object.values(childrenLoc)); + allNodes.push(node); node.nodeId = nodeId++; - if (!typeMap[node.type]) typeMap[node.type] = [node]; - else typeMap[node.type].push(node); + typeMap[node.type] = typeMap[node.type] || []; + typeMap[node.type].push(node); node.lineage = [...node.parentNode?.lineage || []]; if (node.parentNode) { node.lineage.push(node.parentNode.start); } - node.childNodes.push(...Object.values(childrenLoc)); + // Add a getter for the node's source code if (opts.includeSrc && !node.src) Object.defineProperty(node, 'src', { - get() { return rootNode.srcClosure(node.start, node.end);}, + get() {return rootNode.srcClosure(node.start, node.end);}, }); if (opts.detailed) injectScopeToNode(node, scopes); } if (allNodes?.length) allNodes[0].typeMap = typeMap; - if (opts.detailed) allNodes[0].allScopes = scopes; return allNodes; } @@ -161,18 +179,30 @@ function injectScopeToNode(node, scopes) { if (node.type === 'Identifier' && !(!parentNode.computed && ['property', 'key'].includes(node.parentKey))) { // Track references and declarations // Prevent assigning declNode to member expression properties or object keys - const variables = node.scope.variables.filter(n => n.name === node.name); - if (node.parentKey === 'id' || (variables?.length && variables[0].identifiers.some(n => n === node))) { + const variables = []; + for (let i = 0; i < node.scope.variables.length; i++) { + if (node.scope.variables[i].name === node.name) variables.push(node.scope.variables[i]); + } + if (node.parentKey === 'id' || variables?.[0]?.identifiers?.includes(node)) { node.references = node.references || []; } else { // Find declaration by finding the closest declaration of the same name. let decls = []; if (variables?.length) { - decls = variables.find(n => n.name === node.name)?.identifiers; + for (let i = 0; i < variables.length; i++) { + if (variables[i].name === node.name) { + decls = variables[i].identifiers || []; + break; + } + } } else { - const scopeReference = node.scope.references.find(n => n.identifier.name === node.name); - if (scopeReference) decls = scopeReference.resolved?.identifiers || []; + for (let i = 0; i < node.scope.references.length; i++) { + if (node.scope.references[i].identifier.name === node.name) { + decls = node.scope.references[i].resolved?.identifiers || []; + break; + } + } } let declNode = decls[0]; if (decls.length > 1) { @@ -209,41 +239,29 @@ function maxSharedLength(targetArr, containedArr) { } /** - * @param {ASTNode} node - * @param {ASTScope[]} scopes - * @return {Promise} + * @param {ASTNode} rootNode + * @return {{number: ASTScope}} */ -async function injectScopeToNodeAsync(node, scopes) { - return new Promise((resolve, reject) => { - try { - injectScopeToNode(node, scopes); - resolve(); - } catch (e) { - reject(e); - } - }); -} - function getAllScopes(rootNode) { + // noinspection JSCheckFunctionSignatures const globalScope = analyze(rootNode, { optimistic: true, - ecmaVersion: (new Date()).getFullYear(), + ecmaVersion: currentYear, sourceType}).acquireAll(rootNode)[0]; const allScopes = {}; const stack = [globalScope]; - const seen = []; while (stack.length) { - let scope = stack.pop(); - if (seen.includes(scope)) continue; - seen.push(scope); + let scope = stack.shift(); const scopeId = scope.block.start; scope.block.isScopeBlock = true; - if (!allScopes[scopeId]) { - allScopes[scopeId] = scope; - } - stack.push(...scope.childScopes); - if (scope.type === 'module' && scope.upper?.type === 'global' && scope.variables?.length) { - for (const v of scope.variables) if (!scope.upper.variables.includes(v)) scope.upper.variables.push(v); + allScopes[scopeId] = allScopes[scopeId] || scope; + stack.unshift(...scope.childScopes); + // A single global scope is enough, so if there are variables in a module scope, add them to the global scope + if (scope.type === 'module' && scope.upper === globalScope && scope.variables?.length) { + for (let i = 0; i < scope.variables.length; i++) { + const v = scope.variables[i]; + if (!globalScope.variables.includes(v)) globalScope.variables.push(v); + } } } rootNode.allScopes = allScopes; @@ -256,39 +274,16 @@ function getAllScopes(rootNode) { * @return {ASTScope} */ function matchScopeToNode(node, allScopes) { - if (node.lineage?.length) { - for (const nid of [...node.lineage].reverse()) { - if (allScopes[nid]) { - let scope = allScopes[nid]; - if (scope.type.includes('-name') && scope?.childScopes?.length === 1) scope = scope.childScopes[0]; - return scope; - } - } - } - return allScopes[0]; // Global scope - this should never be reached -} - -/** - * - * @param {string} inputCode - * @param {object} opts - * @return {Promise} - */ -async function generateFlatASTAsync(inputCode, opts = {}) { - opts = { ...generateFlatASTDefaultOptions, ...opts }; - let tree = []; - const promises = []; - const rootNode = generateRootNode(inputCode, opts); - if (rootNode) { - tree = extractNodesFromRoot(rootNode, opts); - if (opts.detailed) { - const scopes = getAllScopes(rootNode); - for (let i = 0; i < tree.length; i++) { - promises.push(injectScopeToNodeAsync(tree[i], scopes)); - } - } + let scopeBlock = node; + while (scopeBlock && !scopeBlock.isScopeBlock) { + scopeBlock = scopeBlock.parentNode; } - return Promise.all(promises).then(() => tree); + let scope; + if (scopeBlock) { + scope = allScopes[scopeBlock.start]; + if (scope.type.includes('-name') && scope?.childScopes?.length === 1) scope = scope.childScopes[0]; + } else scope = allScopes[0]; // Global scope - this should never be reached + return scope; } export { @@ -296,9 +291,7 @@ export { extractNodesFromRoot, generateCode, generateFlatAST, - generateFlatASTAsync, generateRootNode, injectScopeToNode, - injectScopeToNodeAsync, parseCode, }; From 4d8520c0b2bad1c67acade042eaea0b075f72614 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sun, 15 Dec 2024 11:25:06 +0200 Subject: [PATCH 5/8] Replace external log function with internal logger. Refactor code to improve performance. --- src/arborist.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/arborist.js b/src/arborist.js index f71db97..5ba06fb 100644 --- a/src/arborist.js +++ b/src/arborist.js @@ -1,17 +1,17 @@ +import {logger} from './utils/logger.js'; import {generateCode, generateFlatAST} from './flast.js'; const Arborist = class { /** * @param {string|ASTNode[]} scriptOrFlatAstArr - the target script or a flat AST array - * @param {Function} logFunc - (optional) Logging function */ - constructor(scriptOrFlatAstArr, logFunc = null) { + constructor(scriptOrFlatAstArr) { this.script = ''; this.ast = []; - this.log = logFunc || (() => true); this.markedForDeletion = []; // Array of node ids. this.appliedCounter = 0; // Track the number of times changes were applied. this.replacements = []; + this.logger = logger; if (typeof scriptOrFlatAstArr === 'string') { this.script = scriptOrFlatAstArr; this.ast = generateFlatAST(scriptOrFlatAstArr); @@ -32,7 +32,7 @@ const Arborist = class { while (relevantTypes.includes(currentNode?.parentNode?.type) || (currentNode.parentNode.type === 'VariableDeclaration' && (currentNode.parentNode.declarations.length === 1 || - !currentNode.parentNode.declarations.filter(d => d !== currentNode && !d.isMarked).length) + !currentNode.parentNode.declarations.some(d => d !== currentNode && !d.isMarked)) )) currentNode = currentNode.parentNode; if (relevantClauses.includes(currentNode.parentKey)) currentNode.isEmpty = true; return currentNode; @@ -76,34 +76,33 @@ const Arborist = class { applyChanges() { let changesCounter = 0; try { - const that = this; if (this.getNumberOfChanges() > 0) { let rootNode = this.ast[0]; - const rootNodeReplacement = this.replacements.find(n => n[0].nodeId === 0); - if (rootNodeReplacement) { + if (rootNode.isMarked) { + const rootNodeReplacement = this.replacements.find(n => n[0].nodeId === 0); ++changesCounter; - this.log(`[+] Applying changes to the root node...`); + this.logger.debug(`[+] Applying changes to the root node...`); const leadingComments = rootNode.leadingComments || []; const trailingComments = rootNode.trailingComments || []; rootNode = rootNodeReplacement[1]; if (leadingComments.length && rootNode.leadingComments !== leadingComments) rootNode.leadingComments = (rootNode.leadingComments || []).concat(leadingComments); if (trailingComments.length && rootNode.trailingComments !== trailingComments) rootNode.trailingComments = (rootNode.trailingComments || []).concat(trailingComments); } else { - for (const targetNodeId of this.markedForDeletion) { + for (let i = 0; i < this.markedForDeletion.length; i++) { + const targetNodeId = this.markedForDeletion[i]; try { let targetNode = this.ast[targetNodeId]; targetNode = targetNode.nodeId === targetNodeId ? targetNode : this.ast.find(n => n.nodeId === targetNodeId); if (targetNode) { const parent = targetNode.parentNode; if (parent[targetNode.parentKey] === targetNode) { - parent[targetNode.parentKey] = undefined; + delete parent[targetNode.parentKey]; const comments = (targetNode.leadingComments || []).concat(targetNode.trailingComments || []); if (comments.length) parent.trailingComments = (parent.trailingComments || []).concat(comments); ++changesCounter; } else if (Array.isArray(parent[targetNode.parentKey])) { const idx = parent[targetNode.parentKey].indexOf(targetNode); - parent[targetNode.parentKey][idx] = undefined; - parent[targetNode.parentKey] = parent[targetNode.parentKey].filter(n => n); + parent[targetNode.parentKey].splice(idx, 1); const comments = (targetNode.leadingComments || []).concat(targetNode.trailingComments || []); if (comments.length) { const targetParent = idx > 0 ? parent[targetNode.parentKey][idx - 1] : parent[targetNode.parentKey].length > 1 ? parent[targetNode.parentKey][idx + 1] : parent; @@ -113,10 +112,11 @@ const Arborist = class { } } } catch (e) { - that.log(`[-] Unable to delete node: ${e}`); + this.logger.debug(`[-] Unable to delete node: ${e}`); } } - for (const [targetNode, replacementNode] of this.replacements) { + for (let i = 0; i < this.replacements.length; i++) { + const [targetNode, replacementNode] = this.replacements[i]; try { if (targetNode) { const parent = targetNode.parentNode; @@ -142,7 +142,7 @@ const Arborist = class { } } } catch (e) { - that.log(`[-] Unable to replace node: ${e}`); + this.logger.debug(`[-] Unable to replace node: ${e}`); } } } @@ -158,13 +158,13 @@ const Arborist = class { this.script = script; } else { - this.log(`[-] Modified script is invalid. Reverting ${changesCounter} changes...`); + this.logger.log(`[-] Modified script is invalid. Reverting ${changesCounter} changes...`); changesCounter = 0; } } } } catch (e) { - this.log(`[-] Unable to apply changes to AST: ${e}`); + this.logger.log(`[-] Unable to apply changes to AST: ${e}`); } ++this.appliedCounter; return changesCounter; From d0dbbdd7c40b120be62ae92768451e45abc15222 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sun, 15 Dec 2024 14:09:11 +0200 Subject: [PATCH 6/8] Replace external log function with internal logger. Refactor code to improve performance. --- src/arborist.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/arborist.js b/src/arborist.js index 5ba06fb..52c5bde 100644 --- a/src/arborist.js +++ b/src/arborist.js @@ -39,7 +39,6 @@ const Arborist = class { } /** - * * @returns {number} The number of changes to be applied. */ getNumberOfChanges() { @@ -49,8 +48,8 @@ const Arborist = class { /** * Replace the target node with another node or delete the target node completely, depending on whether a replacement * node is provided. - * @param targetNode The node to replace or remove. - * @param replacementNode If exists, replace the target node with this node. + * @param {ASTNode} targetNode The node to replace or remove. + * @param {object|ASTNode} replacementNode If exists, replace the target node with this node. */ markNode(targetNode, replacementNode) { if (!targetNode.isMarked) { From cee8807368a1630eb60bc25b46955b376be40542 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sun, 15 Dec 2024 14:34:41 +0200 Subject: [PATCH 7/8] Refactor to improve performance: - Set scriptHash only on the root element instead of iterating over the entire tree. - Replace +new Date() with Date.now() to improve performance --- src/utils/applyIteratively.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/utils/applyIteratively.js b/src/utils/applyIteratively.js index e6e16bb..13877f5 100644 --- a/src/utils/applyIteratively.js +++ b/src/utils/applyIteratively.js @@ -23,11 +23,12 @@ function applyIteratively(script, funcs, maxIterations = 500) { while (arborist.ast?.length && scriptSnapshot !== script && currentIteration < maxIterations) { const iterationStartTime = Date.now(); scriptSnapshot = script; - // Mark each node with the script hash to distinguish cache of different scripts. - for (let i = 0; i < arborist.ast.length; i++) arborist.ast[i].scriptHash = scriptHash; + + // Mark the root node with the script hash to distinguish cache of different scripts. + arborist.ast[0].scriptHash = scriptHash; for (let i = 0; i < funcs.length; i++) { const func = funcs[i]; - const funcStartTime = +new Date(); + const funcStartTime = Date.now(); try { logger.debug(`\t[!] Running ${func.name}...`); arborist = func(arborist); @@ -40,13 +41,13 @@ function applyIteratively(script, funcs, maxIterations = 500) { arborist.applyChanges(); script = arborist.script; scriptHash = generateHash(script); - for (let j = 0; j < arborist.ast.length; j++) arborist.ast[j].scriptHash = scriptHash; + arborist.ast[0].scriptHash = scriptHash; } } catch (e) { logger.error(`[-] Error in ${func.name} (iteration #${iterationsCounter}): ${e}\n${e.stack}`); } finally { logger.debug(`\t\t[!] Running ${func.name} completed in ` + - `${((+new Date() - funcStartTime) / 1000).toFixed(3)} seconds`); + `${((Date.now() - funcStartTime) / 1000).toFixed(3)} seconds`); } } ++currentIteration; From 72937d2cc1793829b89a3893a41a43270308a455 Mon Sep 17 00:00:00 2001 From: Ben Baryo Date: Sun, 15 Dec 2024 15:46:59 +0200 Subject: [PATCH 8/8] Remove the redundant estraverse --- package-lock.json | 19 +++++++++---------- package.json | 3 +-- src/flast.js | 2 -- tests/functionality.test.js | 1 - 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index ddb5164..508041f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,7 @@ "dependencies": { "escodegen": "npm:@javascript-obfuscator/escodegen", "eslint-scope": "^8.2.0", - "espree": "^10.3.0", - "estraverse": "^5.3.0" + "espree": "^10.3.0" }, "devDependencies": { "eslint": "^9.16.0", @@ -114,9 +113,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.16.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", - "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "dev": true, "license": "MIT", "engines": { @@ -440,9 +439,9 @@ } }, "node_modules/eslint": { - "version": "9.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", - "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", "dev": true, "license": "MIT", "dependencies": { @@ -451,7 +450,7 @@ "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.9.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.16.0", + "@eslint/js": "9.17.0", "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -460,7 +459,7 @@ "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.5", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", diff --git a/package.json b/package.json index 27206de..28dc306 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,7 @@ "dependencies": { "escodegen": "npm:@javascript-obfuscator/escodegen", "eslint-scope": "^8.2.0", - "espree": "^10.3.0", - "estraverse": "^5.3.0" + "espree": "^10.3.0" }, "devDependencies": { "eslint": "^9.16.0", diff --git a/src/flast.js b/src/flast.js index b789943..cb7edef 100644 --- a/src/flast.js +++ b/src/flast.js @@ -1,5 +1,4 @@ import {parse} from 'espree'; -import estraverse from 'estraverse'; import {analyze} from 'eslint-scope'; import {logger} from './utils/logger.js'; import {generate, attachComments} from 'escodegen'; @@ -287,7 +286,6 @@ function matchScopeToNode(node, allScopes) { } export { - estraverse, extractNodesFromRoot, generateCode, generateFlatAST, diff --git a/tests/functionality.test.js b/tests/functionality.test.js index 5c0068c..2914f7a 100644 --- a/tests/functionality.test.js +++ b/tests/functionality.test.js @@ -30,7 +30,6 @@ describe('Functionality tests', () => { 'Arborist', 'ASTNode', 'ASTScope', - 'estraverse', 'generateCode', 'generateFlatAST', 'parseCode',