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


//js混淆代码读取
process.argv.length > 2 ? encodeFile = process.argv[2] : encodeFile = "./main.src.js";  //默认的js文件
process.argv.length > 3 ? decodeFile = process.argv[3] : decodeFile = encodeFile.slice(0, encodeFile.length - 3) + "_ok.js";

//将源代码解析为AST
let sourceCode = fs.readFileSync(encodeFile, { encoding: "utf-8" });
let ast = parser.parse(sourceCode);
console.time("处理完毕，耗时");



const callToConditionalExpression =
{
	CallExpression: {
		exit(path) {
			let { callee, arguments } = path.node;
			if (arguments.length != 1 || !types.isConditionalExpression(arguments[0])) {
				return;
			}

			let { test, consequent, alternate } = arguments[0];

			let consequentCallNode = types.CallExpression(callee, [consequent]);
			let alternateCallNode = types.CallExpression(callee, [alternate]);

			let ConditionalNode = types.ConditionalExpression(test, consequentCallNode, alternateCallNode);

			path.replaceWith(ConditionalNode);
		}

	},
}

traverse(ast, callToConditionalExpression);


let window = globalThis;
let decodeCode = "";
let funcName = "";

const getAtobSourceCode =
{
	FunctionDeclaration(path) {
		let name = path.node.id.name;
		let sourceCode = path.toString();
		if (!sourceCode.includes("fromCharCode") || !sourceCode.includes("charCodeAt")) {
			return;
		}

		let allPrevSiblings = path.getAllPrevSiblings();
		if (allPrevSiblings.length < 1) {
			return;
		}

		if (!allPrevSiblings[0].isVariableDeclaration()) {
			return;
		}


		for (let prevSibling of allPrevSiblings.reverse())  //这里的reverse确保代码的执行流程一致。
		{
			decodeCode += prevSibling.toString();
		}

		decodeCode += sourceCode;
		eval(decodeCode);

		funcName = name;
		globalThis[funcName] = eval(funcName);



		if (true) {//使用块级作用域分离代码

			function calcCallExpression(name, path) {

				let { scope, node } = path;

				if (name != funcName) {
					return;
				}

				let binding = undefined;
				if (path.isVariableDeclarator()) {
					binding = scope.getBinding(node.id.name);
					if (!binding || binding.constantViolations.length > 1) {

						return;
					}

					if (binding.constantViolations.length == 1 && binding.constantViolations[0] != path) {

						return;
					}
				}
				else if (path.isAssignmentExpression() && path.get('left').isIdentifier()) {
					binding = scope.getBinding(node.left.name);
					if (!binding || binding.constantViolations.length != 1) {

						return;
					}
				}

				if (!binding) return;

				for (let referPath of binding.referencePaths) {

					let { parentPath, node } = referPath;

					if (parentPath.isVariableDeclarator({ "init": node }) || parentPath.isAssignmentExpression({ "right": node })) {
						calcCallExpression(name, parentPath);
					}

					if (!parentPath.isCallExpression({ "callee": node })) {

						continue;
					}

					let { arguments } = parentPath.node;

					if (arguments.length != 1 || !types.isStringLiteral(arguments[0])) {

						continue;
					}
					let value = globalThis[funcName](arguments[0].value);
					console.log(parentPath.toString(), "--->", value);
					parentPath.replaceWith(types.valueToNode(value));
				}

			}

			let scope = path.parentPath.scope;
			let binding = scope.getBinding(name);

			for (let referPath of binding.referencePaths) {

				let { parentPath, node } = referPath;

				if (parentPath.isVariableDeclarator({ "init": node }) || parentPath.isAssignmentExpression({ "right": node })) {
					calcCallExpression(name, parentPath);
				}

				if (!parentPath.isCallExpression({ "callee": node })) {

					continue;
				}

				let { arguments } = parentPath.node;

				if (arguments.length != 1 || !types.isStringLiteral(arguments[0])) {

					continue;
				}
				let value = globalThis[funcName](arguments[0].value);
				console.log(parentPath.toString(), "--->", value);
				parentPath.replaceWith(types.valueToNode(value));
			}

		}

		path.stop(); //遍历一次就停止，防止遍历到错误的函数

	},

}


traverse(ast, getAtobSourceCode);

console.timeEnd("处理完毕，耗时");
let { code } = generator(ast, opts = {
	"compact": false,  // 是否压缩代码
	"comments": false,  // 是否保留注释
	"jsescOption": { "minimal": true },  //Unicode转义
});

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