aboutsummaryrefslogtreecommitdiff
path: root/test/sandbox.js
blob: 13cfe0883773ffebfe0a74a5c35d5847fe474683 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
var semver = require("semver");
var vm = require("vm");

function createContext() {
    var context = Object.create(null);
    Object.defineProperty(context, "console", {
        value: function() {
            var con = Object.create(null);
            Object.defineProperty(con, "log", {
                value: function(msg) {
                    if (arguments.length == 1 && typeof msg == "string") {
                        return console.log("%s", msg);
                    }
                    return console.log.apply(console, [].map.call(arguments, function(arg) {
                        return safe_log(arg, 3);
                    }));
                }
            });
            return con;
        }()
    });
    return vm.createContext(context);
}

function safe_log(arg, level) {
    if (arg) switch (typeof arg) {
      case "function":
        return arg.toString();
      case "object":
        if (/Error$/.test(arg.name)) return arg.toString();
        arg.constructor.toString();
        if (level--) for (var key in arg) {
            var desc = Object.getOwnPropertyDescriptor(arg, key);
            if (!desc || !desc.get) {
                arg[key] = safe_log(arg[key], level);
            }
        }
    }
    return arg;
}

function strip_func_ids(text) {
    return text.toString().replace(/F[0-9]{6}N/g, "<F<>N>");
}

var context;
var FUNC_TOSTRING = [
    "[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
    "    f.toString = Function.prototype.toString;",
    "});",
    "Function.prototype.toString = function() {",
    "    var id = 100000;",
    "    return function() {",
    "        var n = this.name;",
    "        if (!/^F[0-9]{6}N$/.test(n)) {",
    '            n = "F" + ++id + "N";',
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
    '            Object.defineProperty(this, "name", {',
    "                get: function() {",
    "                    return n;",
    "                }",
    "            });",
] : [], [
    "        }",
    '        return "function(){}";',
    "    };",
    "}();",
]).join("\n");
exports.run_code = function(code, reuse) {
    var stdout = "";
    var original_write = process.stdout.write;
    process.stdout.write = function(chunk) {
        stdout += chunk;
    };
    try {
        if (!reuse || !context) context = createContext();
        vm.runInContext([
            FUNC_TOSTRING,
            "!function() {",
            code,
            "}();",
        ].join("\n"), context, { timeout: 5000 });
        return stdout;
    } catch (ex) {
        return ex;
    } finally {
        process.stdout.write = original_write;
        if (!reuse || /prototype/.test(code)) {
            context = null;
        } else for (var key in context) {
            delete context[key];
        }
    }
};
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
    if (typeof expected != typeof actual) return false;
    if (typeof expected != "string") {
        if (expected.name != actual.name) return false;
        expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
        actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
    }
    return strip_func_ids(expected) == strip_func_ids(actual);
} : function(expected, actual) {
    return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
};