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 = "./init_ok_ok.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 ShowCallExpression = {
    CallExpression(path) {
        let {callee,arguments} = path.node;
        if (!types.isIdentifier(callee) || arguments.length != 2)
        {
            return;
        }
        console.log(path.toString());

    },
}

traverse(ast, ShowCallExpression);


function isExpressionConstant(PathOrNode)
{

    let node = PathOrNode.node || PathOrNode;

    let BrowList = ['window', 'document', 'navigator', 'location', 'history', 'screen',];

    if (types.isLiteral(node) && node.value != null)
    {
        return true;
    }

    if (types.isIdentifier(node) && BrowList.includes(node.name))
    {
        return true;
    }

    if (types.isIdentifier(node) && typeof globalThis[node.name] != "undefined") {
        return true;
    }

    if (types.isMemberExpression(node))
    {
        let {object,property} = node;

        if (types.isIdentifier(object) && typeof globalThis[object.name] != "undefined")
        {
            let properName = types.isIdentifier(property) ? property.name : property.value;
            if (typeof globalThis[object.name][properName] != "undefined") {
                return true;
            }
        }

        if (types.isMemberExpression(object))
        {
            return isExpressionConstant(object);
        }

    }

    if (types.isUnaryExpression(node) && ["+", "-", "!","typeof","~"].includes(node.operator)) {
        return isExpressionConstant(node.argument);
    }

    return false;
}

const restoreVarDeclarator =
{
    VariableDeclarator(path) {
        let scope = path.scope;
        let { id, init } = path.node;

        if (!types.isIdentifier(id) || init == null || !isExpressionConstant(init)) {
            return;
        }

        const binding = scope.getBinding(id.name);

        if (!binding) return;

        let { constant, referencePaths, constantViolations } = binding;  

        if (constantViolations.length > 1) {
            return;
        }

        if (constant || constantViolations[0] == path) {
            for (let referPath of referencePaths) {
                referPath.replaceWith(init);
            }
        }
    },
}

traverse(ast, restoreVarDeclarator);


const restoreAssignConstant = 
{//常量还原插件
    AssignmentExpression(path)
    {
        let {scope,node,parentPath} = path;

        let {left,operator,right} = node;

        if (!types.isIdentifier(left) || operator != "=" || !isExpressionConstant(right))
        {
            return;
        }

        let binding = scope.getBinding(left.name);

        if (!binding || binding.constantViolations.length > 1)
        {//如果没有binding,或者赋值语句本身改变了它，因此这里判断只有一处改变。
            return;
        }

        let {start} = binding.constantViolations[0].node;

        let referStart = start;

        for (let referPath of binding.referencePaths)
        {
            if (referPath.node.start < referStart)
            {
                referStart = referPath.node.start;
            }
        }

        if (start > referStart)
        {//防止在更改前被引用
            return;
        }

        for (let referPath of binding.referencePaths)
        {
            referPath.replaceWith(right);
        }

        if(parentPath.isExpressionStatement() || parentPath.isSequenceExpression())
        {
            path.remove();
        }
    },

}

// traverse(ast, restoreAssignConstant);

ast = parser.parse(generator(ast).code);  //去除多余的 ()，可以将其屏蔽，看看效果。

const constantFold = {
    "BinaryExpression|UnaryExpression|MemberExpression|CallExpression"(path) {
        if (path.isUnaryExpression({ operator: "-" }) ||
            path.isUnaryExpression({ operator: "void" })) {
            return;
        }
        const { confident, value } = path.evaluate();

        if (!confident)
            return;

        if (typeof value == 'number' && (!Number.isFinite(value))) {
            return;
        }

        path.replaceWith(types.valueToNode(value));
    },
}

traverse(ast, constantFold);


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

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