const fs            = require('fs');
const types         = require("@babel/types");
const parser        = require("@babel/parser");
const traverse      = require("@babel/traverse").default;
const generator     = require("@babel/generator").default;
const template      = require("@babel/template").default;

//js混淆代码读取
process.argv.length > 2 ? encodeFile = process.argv[2]: encodeFile ="./encode.js";
process.argv.length > 3 ? decodeFile = process.argv[3]: decodeFile ="./decodeResult.js";

//将源代码解析为AST
let sourceCode = fs.readFileSync(encodeFile, {encoding: "utf-8"});

let ast    = parser.parse(sourceCode);


console.time("处理完毕，耗时");




let tryNODE =  template(`try{
	A;
}catch(e){}`);
	
function getEvalCode(ast)
{
	
	let  firstNode = ast.program.body[0].expression.callee.body;
	
	let tryNode = tryNODE({"A":firstNode});  //使用try语句，将局部变量暴露出来。
	
	let evalCode = generator(tryNode).code;
	
	return evalCode;

}


let  evalCode = getEvalCode(ast);

window = global;

eval(evalCode);



let funcNames = [];

const getFuncNames = 
{
	VariableDeclarator(path)
	{
		let {scope,node} = path;
		let {id,init} = node;
		
		if (!types.isIdentifier(id) || !types.isCallExpression(init))
		{
			return;
		}
		
		let {callee,arguments} = init;
		

		if (arguments.length != 1 || !types.isMemberExpression(callee) || 
		   !types.isIdentifier(callee.property,{"name":"join"}))
		    
		{
			return;
		}
		
		
		let name = id.name;
		let binding = scope.getBinding(name);
		
		if (!binding || !binding.constant || binding.references  != 1 )
		{
			return;
		}
		
		let referPath = binding.referencePaths[0];
		
		let {parentPath} = referPath;
		
		if (parentPath.isVariableDeclarator({"init":referPath.node}))
		{
			funcNames.push(parentPath.node.id.name); //自动获取函数名。
		}
		
	}
}

traverse(ast,getFuncNames);


const decodeMemberExpression = 
{
	CallExpression(path)
	{
		let {callee,arguments} = path.node;
		if (!types.isMemberExpression (callee) || arguments.length != 2)
		{
			return;
		}
		let {object,property} = callee;
		if (!types.isIdentifier(object) || !types.isIdentifier(property,{"name":"substr"}))
		{
			return;
		}
		if (!funcNames.includes(object.name))
		{
			return;
		}
		let value = eval(path.toString());
		console.log(path.toString(),"--->",value);
		path.replaceWith(types.valueToNode(value));
		
	}
}

traverse(ast,decodeMemberExpression);

console.timeEnd("处理完毕，耗时");


let {code} = generator(ast,opts = {jsescOption:{"minimal":true}});

fs.writeFile(decodeFile, code, (err) => {});