quamvoluptatem
Version:
JavaScript Obfuscation Tool.
137 lines (122 loc) • 3.28 kB
text/typescript
import { Node } from "./util/gen";
import { validateChain } from "./util/identifiers";
/**
* Returns all the scopes given parents array.
* - `[object, ...parents]` is recommended.
*
* @param parents
*/
export function getBlocks(parents: any[]): any[] {
return parents.filter((x) => isBlock(x));
}
/**
* A block refers to any object that has a **`.body`** property where code is nested.
*
* Types: `BlockStatement`, `Program`
*
* @param object
* @param parents
*/
export function getBlock(object: any, parents: any[]) {
if (!Array.isArray(parents)) {
throw new Error("parents must be an array");
}
return getBlocks([object, ...parents])[0];
}
/**
* Must have a **`.body`** property and be an array.
*
* - "BlockStatement"
* - "Program"
*
* @param object
*/
export function isBlock(object: any) {
return object && !!object.body && Array.isArray(object.body);
}
/**
* Returns a numerical representation of the depth.
* - Depth is how many blocks nested.
* - Program = 1 depth
* - First Fn = 2 depth
* - Nested Fn = 3 depth
* - Second Fn = 2 depth
* - etc...
* @param object
* @param parents
*/
export function getDepth(object: any, parents: any[]) {
if (!Array.isArray(parents)) {
throw new Error("parents should be an array");
}
var scopes = getBlocks([object, ...parents].filter((x) => x));
return scopes.length;
}
export type EnterCallback = (
object: Node,
parents: Node[]
) => ExitCallback | "EXIT" | void;
export type ExitCallback = () => void;
export function walk(
object: Node | Node[],
parents: Node[],
onEnter: EnterCallback,
seen = new Set<Node>()
): "EXIT" | void {
if (typeof object === "object" && object) {
if (seen.has(object as any)) {
console.log(object);
throw new Error("Already seen: " + (object as any).type);
}
seen.add(object as any);
var newParents: Node[] = [object as Node, ...parents];
if (!Array.isArray(object)) {
validateChain(object, parents);
}
// 1. Call `onEnter` function and remember any onExit callback returned
var onExit = onEnter(object as Node, parents);
// 2. Traverse children
if (Array.isArray(object)) {
var copy = [...object];
for (var element of copy) {
if (walk(element, newParents, onEnter) === "EXIT") {
return "EXIT";
}
}
copy.forEach((x) => {});
} else {
var keys = Object.keys(object);
for (var key of keys) {
if (!key.startsWith("$")) {
if (walk(object[key], newParents, onEnter) === "EXIT") {
return "EXIT";
}
}
}
}
if (onExit === "EXIT") {
return "EXIT";
}
// 3. Done with children, call `onExit` callback
if (onExit) {
onExit();
}
}
}
/**
* The bare-bones walker.
*
* - Recursively traverse an AST object.
* - Calls the `onEnter` function with:
* - - `object` - The current node
* - - `parents` - Array of ancestors `[closest, ..., root]`
* - The `onEnter` callback can return an `onExit` callback for that node.
*
* - *Note*: Does not validate the property names.
*
* @param tree
* @param onEnter
*/
export default function traverse(tree, onEnter: EnterCallback) {
walk(tree, [], onEnter);
}