-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add utils: - applyIteratively - treeModifier - logger * Add missing allScopes object * Add utils tests * Change imports to relative without __dirname
- Loading branch information
Showing
12 changed files
with
182 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
module.exports = { | ||
...require(__dirname + '/flast'), | ||
...require(__dirname + '/arborist'), | ||
...require(__dirname + '/types'), | ||
...require('./flast'), | ||
...require('./arborist'), | ||
...require('./types'), | ||
utils: require('./utils'), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
const {Arborist} = require('../arborist'); | ||
const logger = require('./logger'); | ||
const {createHash} = require('node:crypto'); | ||
|
||
const generateHash = str => createHash('sha256').update(str).digest('hex'); | ||
|
||
|
||
/** | ||
* Apply functions to modify the script repeatedly until they are no long effective or the max number of iterations is reached. | ||
* @param {string} script The target script to run the functions on. | ||
* @param {function[]} funcs | ||
* @param {number?} maxIterations (optional) Stop the loop after this many iterations at most. | ||
* @return {string} The possibly modified script. | ||
*/ | ||
function runLoop(script, funcs, maxIterations = 500) { | ||
let scriptSnapshot = ''; | ||
let currentIteration = 0; | ||
let changesCounter = 0; | ||
let iterationsCounter = 0; | ||
try { | ||
let scriptHash = generateHash(script); | ||
let arborist = new Arborist(script); | ||
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; | ||
for (let i = 0; i < funcs.length; i++) { | ||
const func = funcs[i]; | ||
const funcStartTime = +new Date(); | ||
try { | ||
logger.debug(`\t[!] Running ${func.name}...`); | ||
arborist = func(arborist); | ||
if (!arborist.ast?.length) break; | ||
// If the hash doesn't exist it means the Arborist was replaced | ||
const numberOfNewChanges = arborist.getNumberOfChanges() + +!arborist.ast[0].scriptHash; | ||
if (numberOfNewChanges) { | ||
changesCounter += numberOfNewChanges; | ||
logger.log(`\t[+] ${func.name} applying ${numberOfNewChanges} new changes!`); | ||
arborist.applyChanges(); | ||
script = arborist.script; | ||
scriptHash = generateHash(script); | ||
for (let j = 0; j < arborist.ast.length; j++) arborist.ast[j].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`); | ||
} | ||
} | ||
++currentIteration; | ||
++iterationsCounter; | ||
logger.log(`[+] ==> Iteartion #${iterationsCounter} completed in ${(Date.now() - iterationStartTime) / 1000} seconds` + | ||
` with ${changesCounter ? changesCounter : 'no'} changes (${arborist.ast?.length || '???'} nodes)`); | ||
changesCounter = 0; | ||
} | ||
if (changesCounter) script = arborist.script; | ||
} catch (e) { | ||
logger.error(`[-] Error on iteration #${iterationsCounter}: ${e}\n${e.stack}`); | ||
} | ||
return script; | ||
} | ||
|
||
module.exports = runLoop; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module.exports = { | ||
applyIteratively: require('./applyIteratively'), | ||
logger: require('./logger'), | ||
treeModifier: require('./treeModifier'), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
const logLevels = { | ||
DEBUG: 1, | ||
LOG: 2, | ||
ERROR: 3, | ||
NONE: 9e10, | ||
}; | ||
|
||
/** | ||
* @param {number} logLevel | ||
* @returns {function(*): void|undefined} | ||
*/ | ||
function createLoggerForLevel(logLevel) { | ||
if (!Object.values(logLevels).includes(logLevel)) throw new Error(`Unknown log level ${logLevel}.`); | ||
return msg => logLevel >= logger.currentLogLevel ? logger.logFunc(msg) : undefined; | ||
} | ||
|
||
const logger = { | ||
logLevels, | ||
logFunc: console.log, | ||
debug: createLoggerForLevel(logLevels.DEBUG), | ||
log: createLoggerForLevel(logLevels.LOG), | ||
error: createLoggerForLevel(logLevels.ERROR), | ||
currentLogLevel: logLevels.NONE, | ||
|
||
/** | ||
* Set the current log level | ||
* @param {number} newLogLevel | ||
*/ | ||
setLogLevel(newLogLevel) { | ||
if (!Object.values(this.logLevels).includes(newLogLevel)) throw new Error(`Unknown log level ${newLogLevel}.`); | ||
this.currentLogLevel = newLogLevel; | ||
}, | ||
|
||
setLogLeveNone() {this.setLogLevel(this.logLevels.NONE);}, | ||
setLogLeveDebug() {this.setLogLevel(this.logLevels.DEBUG);}, | ||
setLogLeveLog() {this.setLogLevel(this.logLevels.LOG);}, | ||
setLogLeveError() {this.setLogLevel(this.logLevels.ERROR);}, | ||
|
||
setLogFunc(newLogfunc) { | ||
this.logFunc = newLogfunc; | ||
}, | ||
}; | ||
|
||
module.exports = logger; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* Boilerplate for filter functions that identify the desired structure and a modifier function that modifies the tree. | ||
* An optional name for the function can be provided for better logging. | ||
* @param {Function} filterFunc | ||
* @param {Function} modFunc | ||
* @param {string} [funcName] | ||
* @returns {function(Arborist): Arborist} | ||
*/ | ||
function treeModifier(filterFunc, modFunc, funcName) { | ||
const func = function(arb) { | ||
for (let i = 0; i < arb.ast.length; i++) { | ||
const n = arb.ast[i]; | ||
if (filterFunc(n, arb)) { | ||
modFunc(n, arb); | ||
} | ||
} | ||
return arb; | ||
}; | ||
if (funcName) Object.defineProperty(func, 'name', {value: funcName}); | ||
return func; | ||
} | ||
|
||
module.exports = treeModifier; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const {utils} = require(__dirname + '/../src/index'); | ||
const assert = require('node:assert'); | ||
module.exports = [ | ||
{ | ||
enabled: true, | ||
name: 'treeModifier + applyIteratively', | ||
description: '', | ||
run() { | ||
const code = `console.log('Hello' + ' ' + 'there');`; | ||
const expectedOutput = `console.log('General' + ' ' + 'Kenobi');`; | ||
const expectedFuncName = 'StarWarsDialog'; | ||
const replacements = { | ||
Hello: 'General', | ||
there: 'Kenobi', | ||
}; | ||
let result = code; | ||
const f = n => n.type === 'Literal' && replacements[n.value]; | ||
const m = (n, arb) => arb.markNode(n, { | ||
type: 'Literal', | ||
value: replacements[n.value], | ||
}); | ||
const generatedFunc = utils.treeModifier(f, m, expectedFuncName); | ||
result = utils.applyIteratively(result, [generatedFunc]); | ||
|
||
assert.equal(result, expectedOutput, | ||
`Result does not match expected output.`); | ||
assert.equal(generatedFunc.name, expectedFuncName, | ||
`The name of the generated function does not match.`); | ||
return true; | ||
}, | ||
}, | ||
]; |