var readFileSync = require("fs").readFileSync; var semver = require("semver"); var spawnSync = require("child_process").spawnSync; var vm = require("vm"); setup_log(); var setup_code = "(" + setup + ")(" + [ "this", find_builtins(), setup_log, "function(process) {" + readFileSync(require.resolve("../tools/tty", "utf8")) + "}", ].join(",\n") + ");\n"; exports.has_toplevel = function(options) { return options.toplevel || options.mangle && options.mangle.toplevel || options.compress && options.compress.toplevel; }; exports.is_error = is_error; exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) { var stdout = run_code_vm(code, toplevel, timeout); if (typeof stdout != "string" || !/arguments/.test(code)) return stdout; do { var prev = stdout; stdout = run_code_vm(code, toplevel, timeout); } while (prev !== stdout); return stdout; } : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) { if (/\basync([ \t]+[^\s()[\]{},.&|!~=*%/+-]+|[ \t]*\([\s\S]*?\))[ \t]*=>|\b(async[ \t]+function|setInterval|setTimeout)\b/.test(code)) { return run_code_exec(code, toplevel, timeout); } else { return run_code_vm(code, toplevel, timeout); } }; exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { if (typeof expected != typeof actual) return false; if (is_error(expected)) { if (expected.name !== actual.name) return false; if (typeof actual.message != "string") 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); }; function is_error(result) { return result && typeof result.name == "string" && typeof result.message == "string"; } function strip_color_codes(value) { return value.replace(/\u001b\[\d+m/g, ""); } function strip_func_ids(text) { return ("" + text).replace(/F[0-9]{6}N/g, "N>"); } function setup_log() { var inspect = require("util").inspect; if (inspect.defaultOptions) { var log_options = { breakLength: Infinity, colors: false, compact: true, customInspect: false, depth: Infinity, maxArrayLength: Infinity, maxStringLength: Infinity, showHidden: false, }; for (var name in log_options) { if (name in inspect.defaultOptions) inspect.defaultOptions[name] = log_options[name]; } } return inspect; } function find_builtins() { setup_code = "console.log(Object.keys(this));"; var builtins = run_code_vm(""); if (semver.satisfies(process.version, ">=0.12")) builtins += ".concat(" + run_code_exec("") + ")"; return builtins; } function setup(global, builtins, setup_log, setup_tty) { [ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) { f.toString = Function.prototype.toString; }); Function.prototype.toString = function() { var configurable = Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable; var id = 100000; return function() { var n = this.name; if (!/^F[0-9]{6}N$/.test(n)) { n = "F" + ++id + "N"; if (configurable) Object.defineProperty(this, "name", { get: function() { return n; } }); } return "function(){}"; }; }(); var process = global.process; if (process) { setup_tty(process); var inspect = setup_log(); process.on("uncaughtException", function(ex) { var value = ex; if (value instanceof Error) { value = {}; for (var name in ex) { value[name] = ex[name]; delete ex[name]; } } process.stderr.write(inspect(value) + "\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n"); throw ex; }).on("unhandledRejection", function() {}); } var log = console.log; var safe_console = { log: function(msg) { if (arguments.length == 1 && typeof msg == "string") return log("%s", msg); return log.apply(null, [].map.call(arguments, function(arg) { return safe_log(arg, { level: 5, original: [], replaced: [], }); })); }, }; var props = { // for Node.js v8 console: { get: function() { return safe_console; }, }, global: { get: self }, self: { get: self }, window: { get: self }, }; [ // for Node.js v0.12 "Buffer", "clearInterval", "clearTimeout", // for Node.js v0.12 "DTRACE_NET_STREAM_END", // for Node.js v8 "process", "setInterval", "setTimeout", ].forEach(function(name) { var value = global[name]; props[name] = { get: function() { return value; }, }; }); builtins.forEach(function(name) { try { delete global[name]; } catch (e) {} }); Object.defineProperties(global, props); // for Node.js v8+ global.toString = function() { return "[object global]"; }; function self() { return this; } function safe_log(arg, cache) { if (arg) switch (typeof arg) { case "function": return arg.toString(); case "object": if (arg === global) return "[object global]"; if (/Error$/.test(arg.name)) return arg.toString(); if (typeof arg.then == "function") return "[object Promise]"; arg.constructor.toString(); var index = cache.original.indexOf(arg); if (index >= 0) return cache.replaced[index]; if (--cache.level < 0) return "[object Object]"; var value = {}; cache.original.push(arg); cache.replaced.push(value); for (var key in arg) { var desc = Object.getOwnPropertyDescriptor(arg, key); if (desc && (desc.get || desc.set)) { Object.defineProperty(value, key, desc); } else { value[key] = safe_log(arg[key], cache); } } return value; } return arg; } } function run_code_vm(code, toplevel, timeout) { timeout = timeout || 5000; var stdout = ""; var original_write = process.stdout.write; process.stdout.write = function(chunk) { stdout += chunk; }; try { var ctx = vm.createContext({ console: console }); // for Node.js v6 vm.runInContext(setup_code, ctx); vm.runInContext(toplevel ? "(function(){" + code + "})();" : code, ctx, { timeout: timeout }); return strip_color_codes(stdout); } catch (ex) { return ex; } finally { process.stdout.write = original_write; } } function run_code_exec(code, toplevel, timeout) { if (toplevel) { code = setup_code + "(function(){" + code + "})();"; } else { code = code.replace(/^((["'])[^"']*\2(;|$))?/, function(directive) { return directive + setup_code; }); } var result = spawnSync(process.argv[0], [ '--max-old-space-size=2048' ], { encoding: "utf8", input: code, stdio: "pipe", timeout: timeout || 5000, }); if (result.status === 0) return result.stdout; if (result.error && result.error.code == "ETIMEDOUT" || /FATAL ERROR:/.test(msg)) { return new Error("Script execution timed out."); } if (result.error) return result.error; var msg = result.stderr.replace(/\r\n/g, "\n"); var end = msg.indexOf("\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n"); var details; if (end >= 0) { details = msg.slice(0, end).replace(/<([1-9][0-9]*) empty items?>/g, function(match, count) { return new Array(+count).join(); }); try { details = vm.runInNewContext("(" + details + ")"); } catch (e) {} } var match = /\n([^:\s]*Error)(?:: ([\s\S]+?))?\n( at [\s\S]+)\n$/.exec(msg); if (!match) return details; var ex = new global[match[1]](match[2]); ex.stack = ex.stack.slice(0, ex.stack.indexOf(" at ")) + match[3]; if (typeof details == "object") { for (var name in details) ex[name] = details[name]; } else if (end >= 0) { ex.details = details; } return ex; }