简体   繁体   中英

How can you recover the source code from a pure JavaScript function?

By Pure, I mean in the sense of the λ-calculus, ie, a single-argument function containing nothing on its body other than single-argument functions and single argument function calls. By recovering the source code, I mean up to variable renaming. So, for example,

n2 = function(v0){return function(v1){return v0(v0(v1))}}
console.log(source(n2));
console.log(source(n2(n2)));

Should print:

function(v0){return function(v0){return v0(v0(v1))}}
function(v0){return function(v0){return v0(v0(v0(v0(v1))))}}

That is, the first line shows the original source of the function n2 , and the second one shows the source of the function that is returned by the evaluation of n2(n2) .

I've managed to implement it as follows:

function source(f){
    var nextVarId = 0;
    return (function recur(f){
        if (typeof f === "function"){
            if (f.isVarFunc) return f(null);
            else {
                var varName = "v"+(nextVarId++);
                var varFunc = function rec(res){
                    var varFunc = function(arg){
                        return arg === null
                            ? "("+res.join(")(")+")"
                            : rec(res.concat(recur(arg)));
                    };
                    varFunc.isVarFunc = true;
                    return varFunc;
                };
                varFunc.isVarFunc = true;
                var body = f(varFunc([varName]));
                body     = body.isVarFunc ? body(null) : recur(body);
                return "(function("+varName+"){return "+body+"})";
            };
        } else return f;
    })(f);
};

The issue is that I'm using some rather ugly method of tagging functions by setting their names to a specific value, and that it won't work in functions that are applied more than once (such as a(b)(b) ). Is there any better principled way to solve this problem?

Edit: I managed to design a version that seems to be correct in all cases, but it is still an ugly unreadable unprincipled mess.

Finally, this is a considerably cleaned up version of the mess above.

// source :: PureFunction -> String
// Evaluates a pure JavaScript function to normal form and returns the 
// source code of the resulting function as a string.
function source(fn){
    var nextVarId = 0;
    return (function normalize(fn){
        // This is responsible for collecting the argument list of a bound
        // variable. For example, in `function(x){return x(a)(b)(c)}`, it
        // collects `a`, `b`, `c` as the arguments of `x`.  For that, it
        // creates a variadic argumented function that is applied to many
        // arguments, collecting them in a closure, until it is applied to
        // `null`. When it is, it returns the JS source string for the
        // application of the collected argument list.
        function application(argList){
            var app = function(arg){
                return arg === null
                    ? "("+argList.join(")(")+")"
                    : application(argList.concat(normalize(arg)));
            };
            app.isApplication = true;
            return app;
        };
        // If we try to normalize an application, we apply
        // it to `null` to stop the argument-collecting.
        if (fn.isApplication) 
            return fn(null);
        // Otherwise, it is a JavaScript function. We need to create an
        // application for its variable, and call the function on it.
        // We then normalize the resulting body and return the JS
        // source for the function.
        else {
            var varName = "v"+(nextVarId++);
            var body    = normalize(fn(application([varName])));
            return "(function("+varName+"){return "+body+"})";
        };
    })(fn);
};

It is still not perfect but looks much better nether less. It works as expected:

console.log(source(function(a){return function(b){return a(b)}}))

Outputs:

(function(v0){return (function(v1){return (v0)((v1))})})

I wonder how inefficient that is, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM