UNPKG

distinctiomagnam

Version:
295 lines (252 loc) 7.51 kB
import { reservedIdentifiers } from "../constants"; import { ObfuscateOrder } from "../order"; import traverse, { walk } from "../traverse"; import { FunctionDeclaration, Identifier, ReturnStatement, VariableDeclaration, VariableDeclarator, CallExpression, MemberExpression, ThisExpression, ArrayExpression, ExpressionStatement, AssignmentExpression, Node, BlockStatement, ArrayPattern, } from "../util/gen"; import { getIdentifierInfo } from "../util/identifiers"; import { getBlockBody, getVarContext, isFunction, prepend, clone, } from "../util/insert"; import Transform from "./transform"; /** * Brings every function to the global level. * * Functions take parameters, input, have a return value and return modified changes to the scoped variables. * * ```js * function topLevel(ref1, ref2, refN, param1, param2, paramN){ * return [ref1, ref2, refN, returnValue]; * } * ``` */ export default class Flatten extends Transform { definedNames: Map<Node, Set<string>>; constructor(o) { super(o, ObfuscateOrder.Flatten); this.definedNames = new Map(); } apply(tree) { traverse(tree, (o, p) => { if ( o.type == "Identifier" && !reservedIdentifiers.has(o.name) && !this.options.globalVariables.has(o.name) ) { var info = getIdentifierInfo(o, p); if (info.spec.isReferenced) { if (info.spec.isDefined) { var c = getVarContext(o, p); if (c) { if (!this.definedNames.has(c)) { this.definedNames.set(c, new Set([o.name])); } else { this.definedNames.get(c).add(o.name); } } } } } }); super.apply(tree); } match(object: Node, parents: Node[]) { return ( object.type == "FunctionDeclaration" && object.body.type == "BlockStatement" && !object.generator && !object.async && !object.params.find((x) => x.type !== "Identifier") ); } transform(object: Node, parents: Node[]) { return () => { // if ( parents.find( (x) => x.type == "ClassExpression" || x.type == "ClassDeclaration" || x.type == "MethodDefinition" ) ) { return; } var defined = new Set<string>(); var references = new Set<string>(); var modified = new Set<string>(); var illegal = new Set<string>(); var isIllegal = false; var definedAbove = new Set<string>(this.options.globalVariables); parents.forEach((x) => { var set = this.definedNames.get(x); if (set) { set.forEach((name) => definedAbove.add(name)); } }); walk(object, parents, (o, p) => { if (object.id && o === object.id) { return; } if ( o.type == "Identifier" && !this.options.globalVariables.has(o.name) && !reservedIdentifiers.has(o.name) ) { var info = getIdentifierInfo(o, p); if (!info.spec.isReferenced) { return; } if (o.hidden) { illegal.add(o.name); } if (info.spec.isDefined) { defined.add(o.name); } else if (info.spec.isModified) { modified.add(o.name); } else { references.add(o.name); } } if (o.type == "TryStatement") { isIllegal = true; return "EXIT"; } if (o.type == "Identifier") { if (o.name == "arguments") { isIllegal = true; return "EXIT"; } } if (o.type == "ThisExpression") { isIllegal = true; return "EXIT"; } if (o.type == "Super") { isIllegal = true; return "EXIT"; } if (o.type == "MetaProperty") { isIllegal = true; return "EXIT"; } if (o.type == "VariableDeclaration" && o.kind !== "var") { isIllegal = true; return "EXIT"; } }); if (isIllegal) { return; } if (illegal.size) { return; } illegal.forEach((name) => { defined.delete(name); }); defined.forEach((name) => { references.delete(name); modified.delete(name); }); // console.log(object.id.name, illegal, references); var input = Array.from(new Set([...modified, ...references])); if (Array.from(input).find((x) => !definedAbove.has(x))) { return; } var output = Array.from(modified); var newName = "flatten" + this.getPlaceholder() + "_" + ((object.id && object.id.name) || "fn"); getBlockBody(object.body).push(ReturnStatement()); walk(object.body, [object, ...parents], (o, p) => { return () => { if (o.type == "ReturnStatement" && getVarContext(o, p) === object) { var elements = output.map(Identifier); if ( o.argument && !( o.argument.type == "Identifier" && o.argument.name == "undefined" ) ) { elements.unshift(clone(o.argument)); } o.argument = ArrayExpression(elements); } }; }); var newBody = getBlockBody(object.body); if (input.length) { newBody.unshift( VariableDeclaration( VariableDeclarator( ArrayPattern(input.map(Identifier)), ThisExpression() ) ) ); } var newFunctionDeclaration = FunctionDeclaration( newName, clone(object.params), newBody ); newFunctionDeclaration.async = !!object.async; newFunctionDeclaration.generator = !!object.generator; prepend(parents[parents.length - 1], newFunctionDeclaration); var newParamNodes = object.params.map(() => Identifier(this.getPlaceholder()) ); // var result = newFn.call([...refs], ...arguments) var call = VariableDeclaration( VariableDeclarator( "result", CallExpression( MemberExpression(Identifier(newName), Identifier("call"), false), [ArrayExpression(input.map(Identifier)), ...newParamNodes] ) ) ); // result.pop() var pop = CallExpression( MemberExpression(Identifier("result"), Identifier("pop"), false), [] ); // var result = newFn.call([...refs], ...arguments) // modified1 = result.pop(); // modified2 = result.pop(); // ...modifiedN = result.pop();... // // return result.pop() object.body = BlockStatement([ call, ...[...output].reverse().map((name) => { return ExpressionStatement( AssignmentExpression("=", Identifier(name), clone(pop)) ); }), ReturnStatement(clone(pop)), ]); object.params = newParamNodes; }; } }