Use ESTree-based optimizer for core JS passes, to support ES6+ inputs (#7973)

Fixes #6000

The key change here is to rewrite the JS optimizer passes that run in a normal `-O3` etc. build from the Uglify1 AST to ESTree. With ESTree we can use modern parsers etc. so that we support ES6+ inputs to js libraries, pre-jses, EM_ASM, etc.

Aside from that rewrite, the other changes are less critical and can be altered later. Specifically, this uses acorn for parsing and terser for outputting, but we could switch to anything using ESTree very easily. Acorn is nice for parsing since it's small and standalone. For outputting I experimented with astring, which is small and nice, and escodegen, which looks very robust, but neither could output compact-enough JS to not regress our JS code sizes. This is not truly critical since for minimal code size people should use closure anyhow, however, it's nice for default builds to be small (and we don't run closure by default), and I didn't want to regress anything. Using the terser outputter achieves that. (Since it uses the Uglify2 AST internally, this means using their tool to convert ESTree to Uglify2.) They may be some minor code size changes with this PR, just because we use a different outputter now, but nothing major in either direction. Most changes seem positive actually. Sizes after closure are unchanged.

This uses almost unmodified versions of acorn and terser, but they are stripped down to what we need, and I had to make two modifications, see these PRs: [acornjs/acorn#793](https://github.com/acornjs/acorn/pull/793) (quote the error on parse exceptions) and [mishoo/UglifyJS2#3323](https://github.com/mishoo/UglifyJS2/pull/3323) (preserve quoted properties).

This may very slightly regress compile times when using those passes, as Uglify1 was just very fast. However, the change should be very small.

This does _not_ rewrite every single JS optimizer pass. In particular the asm.js passes don't need to support ES6, and so don't need to be rewritten. There are also optional passes that do not run by default, that we can convert later depending on priority.
This commit is contained in:
Alon Zakai 2019-02-08 13:59:52 -08:00 коммит произвёл GitHub
Родитель c3e6c600e0
Коммит 59f08fb3ab
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
25 изменённых файлов: 17452 добавлений и 612 удалений

Просмотреть файл

@ -1,15 +1,20 @@
var z = fleefl();
var zz = fleefl();
function g(a) {
return a + 1;
}
Module["g"] = g;
function h(a) {
return a + 1;
}
print(h(123));
((function() {
(function() {
var z = fleefl();
var zz = fleefl();
function g(a) {
@ -20,8 +25,9 @@ print(h(123));
return a + 1;
}
print(hh(123));
}))();
})();
function glue() {
}
glue();
glue();

Просмотреть файл

@ -0,0 +1,3 @@
for (var i in x) {}
for (var j = 0; ;) {}

Просмотреть файл

@ -0,0 +1,4 @@
for (var i in x) {}
for (var j = 0;;) {}

Просмотреть файл

@ -1,6 +1,7 @@
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
if (hasOwnProperty({}, "prop_name")) {
console.log("yeah");
}

Просмотреть файл

@ -1,19 +1,26 @@
var z = fleefl();
var zz = fleefl();
var keeperObj = {
x: fleefl()
};
var keeperArray = [ 1, 2, "3", four() ];
function g(a) {
return a + 1;
}
Module["g"] = g;
function h(a) {
return a + 1;
}
print(h(123));
((function() {
(function() {
var z = fleefl();
var zz = fleefl();
function g(a) {
@ -24,31 +31,38 @@ print(h(123));
return a + 1;
}
print(hh(123));
}))();
})();
function glue() {
function lookup() {
throw 1;
}
}
glue();
function _glCreateShader() {
return 1;
}
function emulate() {
_glCreateShader = function _glCreateShader(shaderType) {
return glCreateShader();
};
}
emulate();
___cxa_find_matching_catch_before();
function ___cxa_find_matching_catch_before() {
if (!___cxa_find_matching_catch_before.buffer) ___cxa_find_matching_catch_before.buffer = {};
}
function ___cxa_find_matching_catch_after() {
if (!___cxa_find_matching_catch_after.buffer) ___cxa_find_matching_catch_after.buffer = {};
}
___cxa_find_matching_catch_after();
var dotOther = Side.effect;

Просмотреть файл

@ -1,54 +0,0 @@
var defun = (function() {
})();
var name = (function() {
})();
var object = (function() {
})();
var non_reserved = (function() {
})();
function func_1() {
}
function func_2() {
}
function func_3() {
}
function func_4() {
}
function func_5() {
}
function func_6() {
}
function func_7() {
}
function func_8() {
}
function func_9() {
}
function func_10() {
}
var quotedObject = {
"var": func_1,
"defun": func_2,
"function": func_3,
"name": func_4,
"non_reserved": func_5
};
var unquotedObject = {
"var": func_6,
defun: func_7,
"function": func_8,
name: func_9,
non_reserved: func_10
};
var recursiveObject = {
object: {
func: (function() {
}),
object: {
func: (function() {
})
}
}
};
quotedObject || unquotedObject || recursiveObject;

Просмотреть файл

@ -1,47 +0,0 @@
var defun = (function () { var a = 1; })();
var name = (function () { var a = 1; })();
var object = (function () { var a = 1; })();
var non_reserved = (function () { var a = 1; })();
function func_1() { var a = 1; }
function func_2() { var a = 1; }
function func_3() { var a = 1; }
function func_4() { var a = 1; }
function func_5() { var a = 1; }
function func_6() { var a = 1; }
function func_7() { var a = 1; }
function func_8() { var a = 1; }
function func_9() { var a = 1; }
function func_10() { var a = 1; }
function func_deleted() { var a = 1; }
var quotedObject = {
"var": func_1,
"defun": func_2,
"function": func_3,
"name": func_4,
"non_reserved": func_5
};
var unquotedObject = {
var: func_6,
defun: func_7,
function: func_8,
name: func_9,
non_reserved: func_10
};
var recursiveObject = {
object: {
func: function () {
var a = 1;
},
object: {
func: function () {
var b = 1;
}
}
}
};
quotedObject || unquotedObject || recursiveObject; // fake uses

Просмотреть файл

@ -1,26 +1,40 @@
var name;
var asmLibraryArg = {
"save1": 1,
"save2": 2
};
var expD1 = Module["expD1"] = asm["expD1"];
var expD2 = Module["expD2"] = asm["expD2"];
var expD3 = Module["expD3"] = asm["expD3"];
var expD4 = undefined;
var expI1 = Module["expI1"] = (function() {
return Module["asm"]["expI1"].apply(null, arguments);
});
var expI2 = Module["expI2"] = (function() {
return Module["asm"]["expI2"].apply(null, arguments);
});
var expI3 = Module["expI3"] = (function() {
return Module["asm"]["expI3"].apply(null, arguments);
});
var expI4 = undefined;
expD1;
Module["expD2"];
asm["expD3"];
expI1;
Module["expI2"];
asm["expI3"];
var expD1 = Module["expD1"] = asm["expD1"];
var expD2 = Module["expD2"] = asm["expD2"];
var expD3 = Module["expD3"] = asm["expD3"];
var expD4;
var expI1 = Module["expI1"] = function() {
return Module["asm"]["expI1"].apply(null, arguments);
};
var expI2 = Module["expI2"] = function() {
return Module["asm"]["expI2"].apply(null, arguments);
};
var expI3 = Module["expI3"] = function() {
return Module["asm"]["expI3"].apply(null, arguments);
};
var expI4;
expD1;
Module["expD2"];
asm["expD3"];
expI1;
Module["expI2"];
asm["expI3"];

Просмотреть файл

@ -1,4 +1,5 @@
var name;
var asmLibraryArg = {
"a": 1,
"A": 33,
@ -12,10 +13,9 @@ var asmLibraryArg = {
"d": ___syscall140,
"q": ___syscall146
};
var expD1 = Module["expD1"] = asm["c"];
var expI1 = Module["expI1"] = (function() {
var expI1 = Module["expI1"] = function() {
return Module["asm"]["d"].apply(null, arguments);
});
};

Просмотреть файл

@ -0,0 +1,237 @@
var Module;
if (!Module) Module = "__EMSCRIPTEN_PRIVATE_MODULE_EXPORT_NAME_SUBSTITUTION__";
var ENVIRONMENT_IS_NODE = typeof process === "object";
if (ENVIRONMENT_IS_NODE) {
var fs = require("fs");
Module["wasm"] = fs.readFileSync(__dirname + "/a.wasm");
}
function out(text) {
console.log(text);
}
function err(text) {
console.error(text);
}
function ready() {
run();
}
function abort(what) {
throw what;
}
var UTF8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined;
function UTF8ArrayToString(u8Array, idx, maxBytesToRead) {
var endIdx = idx + maxBytesToRead;
var endPtr = idx;
while (u8Array[endPtr] && !(endPtr >= endIdx)) ++endPtr;
if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) {
return UTF8Decoder.decode(u8Array.subarray(idx, endPtr));
} else {
var str = "";
while (idx < endPtr) {
var u0 = u8Array[idx++];
if (!(u0 & 128)) {
str += String.fromCharCode(u0);
continue;
}
var u1 = u8Array[idx++] & 63;
if ((u0 & 224) == 192) {
str += String.fromCharCode((u0 & 31) << 6 | u1);
continue;
}
var u2 = u8Array[idx++] & 63;
if ((u0 & 240) == 224) {
u0 = (u0 & 15) << 12 | u1 << 6 | u2;
} else {
u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | u8Array[idx++] & 63;
}
if (u0 < 65536) {
str += String.fromCharCode(u0);
} else {
var ch = u0 - 65536;
str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);
}
}
}
return str;
}
function UTF8ToString(ptr, maxBytesToRead) {
return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : "";
}
var TOTAL_MEMORY = 16777216, STATIC_BASE = 1024, DYNAMICTOP_PTR = 6016;
var wasmMaximumMemory = TOTAL_MEMORY;
var wasmMemory = new WebAssembly.Memory({
"initial": TOTAL_MEMORY >> 16,
"maximum": wasmMaximumMemory >> 16
});
var buffer = wasmMemory.buffer;
var HEAP8 = new Int8Array(buffer);
var HEAP16 = new Int16Array(buffer);
var HEAP32 = new Int32Array(buffer);
var HEAPU8 = new Uint8Array(buffer);
var HEAPU16 = new Uint16Array(buffer);
var HEAPU32 = new Uint32Array(buffer);
var HEAPF32 = new Float32Array(buffer);
var HEAPF64 = new Float64Array(buffer);
HEAP32[DYNAMICTOP_PTR >> 2] = 5249152;
var SYSCALLS = {
buffers: [ null, [], [] ],
printChar: function(stream, curr) {
var buffer = SYSCALLS.buffers[stream];
if (curr === 0 || curr === 10) {
(stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0));
buffer.length = 0;
} else {
buffer.push(curr);
}
},
varargs: 0,
get: function(varargs) {
SYSCALLS.varargs += 4;
var ret = HEAP32[SYSCALLS.varargs - 4 >> 2];
return ret;
},
getStr: function() {
var ret = UTF8ToString(SYSCALLS.get());
return ret;
},
get64: function() {
var low = SYSCALLS.get(), high = SYSCALLS.get();
return low;
},
getZero: function() {
SYSCALLS.get();
}
};
function ___syscall140(which, varargs) {
SYSCALLS.varargs = varargs;
try {
var stream = SYSCALLS.getStreamFromFD(), offset_high = SYSCALLS.get(), offset_low = SYSCALLS.get(), result = SYSCALLS.get(), whence = SYSCALLS.get();
var offset = offset_low;
FS.llseek(stream, offset, whence);
HEAP32[result >> 2] = stream.position;
if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null;
return 0;
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno;
}
}
function ___syscall146(which, varargs) {
SYSCALLS.varargs = varargs;
try {
var stream = SYSCALLS.get(), iov = SYSCALLS.get(), iovcnt = SYSCALLS.get();
var ret = 0;
for (var i = 0; i < iovcnt; i++) {
var ptr = HEAP32[iov + i * 8 >> 2];
var len = HEAP32[iov + (i * 8 + 4) >> 2];
for (var j = 0; j < len; j++) {
SYSCALLS.printChar(stream, HEAPU8[ptr + j]);
}
ret += len;
}
return ret;
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno;
}
}
function ___syscall54(which, varargs) {
SYSCALLS.varargs = varargs;
try {
return 0;
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno;
}
}
function ___syscall6(which, varargs) {
SYSCALLS.varargs = varargs;
try {
var stream = SYSCALLS.getStreamFromFD();
FS.close(stream);
return 0;
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno;
}
}
function _emscripten_get_now() {
abort();
}
function _emscripten_random() {
return Math.random();
}
function _emscripten_memcpy_big(dest, src, num) {
HEAPU8.set(HEAPU8.subarray(src, src + num), dest);
}
if (ENVIRONMENT_IS_NODE) {
_emscripten_get_now = function _emscripten_get_now_actual() {
var t = process["hrtime"]();
return t[0] * 1e3 + t[1] / 1e6;
};
} else if (typeof dateNow !== "undefined") {
_emscripten_get_now = dateNow;
} else if (typeof self === "object" && self["performance"] && typeof self["performance"]["now"] === "function") {
_emscripten_get_now = function() {
return self["performance"]["now"]();
};
} else if (typeof performance === "object" && typeof performance["now"] === "function") {
_emscripten_get_now = function() {
return performance["now"]();
};
} else {
_emscripten_get_now = Date.now;
}
var asmLibraryArg = {
"b": abort,
"h": ___syscall140,
"a": ___syscall146,
"g": ___syscall54,
"f": ___syscall6,
"e": _emscripten_get_now,
"d": _emscripten_memcpy_big,
"c": _emscripten_random
};
function run() {
var ret = _main();
}
function initRuntime(asm) {
asm["i"]();
}
var env = asmLibraryArg;
env["memory"] = wasmMemory;
env["table"] = new WebAssembly.Table({
"initial": 6,
"maximum": 6,
"element": "anyfunc"
});
env["__memory_base"] = STATIC_BASE;
env["__table_base"] = 0;
var imports = {
"env": env,
"global": {
"NaN": NaN,
Infinity: Infinity
},
"global.Math": Math,
"asm2wasm": {
"f64-rem": function(x, y) {
return x % y;
},
"debugger": function() {
debugger;
}
}
};
var ___errno_location, _llvm_bswap_i32, _main, _memcpy, _memset, dynCall_ii, dynCall_iiii;
WebAssembly.instantiate(Module["wasm"], imports).then(function(output) {
var asm = output.instance.exports;
___errno_location = asm["j"];
_llvm_bswap_i32 = asm["k"];
_main = asm["l"];
_memcpy = asm["m"];
_memset = asm["n"];
dynCall_ii = asm["o"];
dynCall_iiii = asm["p"];
initRuntime(asm);
ready();
});

Просмотреть файл

@ -0,0 +1,267 @@
var Module;
if (!Module) Module = "__EMSCRIPTEN_PRIVATE_MODULE_EXPORT_NAME_SUBSTITUTION__";
var ENVIRONMENT_IS_NODE = typeof process === "object";
if (ENVIRONMENT_IS_NODE) {
var fs = require("fs");
Module["wasm"] = fs.readFileSync(__dirname + "/a.wasm")
}
function out(text) {
console.log(text)
}
function err(text) {
console.error(text)
}
function ready() {
run()
}
function abort(what) {
throw what
}
var UTF8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined;
function UTF8ArrayToString(u8Array, idx, maxBytesToRead) {
var endIdx = idx + maxBytesToRead;
var endPtr = idx;
while (u8Array[endPtr] && !(endPtr >= endIdx)) ++endPtr;
if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) {
return UTF8Decoder.decode(u8Array.subarray(idx, endPtr))
} else {
var str = "";
while (idx < endPtr) {
var u0 = u8Array[idx++];
if (!(u0 & 128)) {
str += String.fromCharCode(u0);
continue
}
var u1 = u8Array[idx++] & 63;
if ((u0 & 224) == 192) {
str += String.fromCharCode((u0 & 31) << 6 | u1);
continue
}
var u2 = u8Array[idx++] & 63;
if ((u0 & 240) == 224) {
u0 = (u0 & 15) << 12 | u1 << 6 | u2
} else {
u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | u8Array[idx++] & 63
}
if (u0 < 65536) {
str += String.fromCharCode(u0)
} else {
var ch = u0 - 65536;
str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023)
}
}
}
return str
}
function UTF8ToString(ptr, maxBytesToRead) {
return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""
}
var TOTAL_MEMORY = 16777216,
STATIC_BASE = 1024,
DYNAMICTOP_PTR = 6016;
var wasmMaximumMemory = TOTAL_MEMORY;
var wasmMemory = new WebAssembly.Memory({
"initial": TOTAL_MEMORY >> 16,
"maximum": wasmMaximumMemory >> 16
});
var buffer = wasmMemory.buffer;
var HEAP8 = new Int8Array(buffer);
var HEAP16 = new Int16Array(buffer);
var HEAP32 = new Int32Array(buffer);
var HEAPU8 = new Uint8Array(buffer);
var HEAPU16 = new Uint16Array(buffer);
var HEAPU32 = new Uint32Array(buffer);
var HEAPF32 = new Float32Array(buffer);
var HEAPF64 = new Float64Array(buffer);
HEAP32[DYNAMICTOP_PTR >> 2] = 5249152;
var SYSCALLS = {
buffers: [null, [],
[]
],
printChar: (function(stream, curr) {
var buffer = SYSCALLS.buffers[stream];
if (curr === 0 || curr === 10) {
(stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0));
buffer.length = 0
} else {
buffer.push(curr)
}
}),
varargs: 0,
get: (function(varargs) {
SYSCALLS.varargs += 4;
var ret = HEAP32[SYSCALLS.varargs - 4 >> 2];
return ret
}),
getStr: (function() {
var ret = UTF8ToString(SYSCALLS.get());
return ret
}),
get64: (function() {
var low = SYSCALLS.get(),
high = SYSCALLS.get();
return low
}),
getZero: (function() {
SYSCALLS.get()
})
};
function ___syscall140(which, varargs) {
SYSCALLS.varargs = varargs;
try {
var stream = SYSCALLS.getStreamFromFD(),
offset_high = SYSCALLS.get(),
offset_low = SYSCALLS.get(),
result = SYSCALLS.get(),
whence = SYSCALLS.get();
var offset = offset_low;
FS.llseek(stream, offset, whence);
HEAP32[result >> 2] = stream.position;
if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null;
return 0
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno
}
}
function ___syscall146(which, varargs) {
SYSCALLS.varargs = varargs;
try {
var stream = SYSCALLS.get(),
iov = SYSCALLS.get(),
iovcnt = SYSCALLS.get();
var ret = 0;
for (var i = 0; i < iovcnt; i++) {
var ptr = HEAP32[iov + i * 8 >> 2];
var len = HEAP32[iov + (i * 8 + 4) >> 2];
for (var j = 0; j < len; j++) {
SYSCALLS.printChar(stream, HEAPU8[ptr + j])
}
ret += len
}
return ret
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno
}
}
function ___syscall54(which, varargs) {
SYSCALLS.varargs = varargs;
try {
return 0
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno
}
}
function ___syscall6(which, varargs) {
SYSCALLS.varargs = varargs;
try {
var stream = SYSCALLS.getStreamFromFD();
FS.close(stream);
return 0
} catch (e) {
if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);
return -e.errno
}
}
function _emscripten_get_now() {
abort()
}
function _emscripten_random() {
return Math.random()
}
function _emscripten_memcpy_big(dest, src, num) {
HEAPU8.set(HEAPU8.subarray(src, src + num), dest)
}
if (ENVIRONMENT_IS_NODE) {
_emscripten_get_now = function _emscripten_get_now_actual() {
var t = process["hrtime"]();
return t[0] * 1e3 + t[1] / 1e6
}
} else if (typeof dateNow !== "undefined") {
_emscripten_get_now = dateNow
} else if (typeof self === "object" && self["performance"] && typeof self["performance"]["now"] === "function") {
_emscripten_get_now = (function() {
return self["performance"]["now"]()
})
} else if (typeof performance === "object" && typeof performance["now"] === "function") {
_emscripten_get_now = (function() {
return performance["now"]()
})
} else {
_emscripten_get_now = Date.now
}
var asmLibraryArg = {
"abort": abort,
"___syscall140": ___syscall140,
"___syscall146": ___syscall146,
"___syscall54": ___syscall54,
"___syscall6": ___syscall6,
"_emscripten_get_now": _emscripten_get_now,
"_emscripten_memcpy_big": _emscripten_memcpy_big,
"_emscripten_random": _emscripten_random
};
function run() {
var ret = _main()
}
function initRuntime(asm) {
asm["__GLOBAL__sub_I_test_global_initializer_cpp"]()
}
var env = asmLibraryArg;
env["memory"] = wasmMemory;
env["table"] = new WebAssembly.Table({
"initial": 6,
"maximum": 6,
"element": "anyfunc"
});
env["__memory_base"] = STATIC_BASE;
env["__table_base"] = 0;
var imports = {
"env": env,
"global": {
"NaN": NaN,
"Infinity": Infinity
},
"global.Math": Math,
"asm2wasm": {
"f64-rem": (function(x, y) {
return x % y
}),
"debugger": (function() {
debugger
})
}
};
var ___errno_location, _llvm_bswap_i32, _main, _memcpy, _memset, dynCall_ii, dynCall_iiii;
WebAssembly.instantiate(Module["wasm"], imports).then((function(output) {
var asm = output.instance.exports;
___errno_location = asm["___errno_location"];
_llvm_bswap_i32 = asm["_llvm_bswap_i32"];
_main = asm["_main"];
_memcpy = asm["_memcpy"];
_memset = asm["_memset"];
dynCall_ii = asm["dynCall_ii"];
dynCall_iiii = asm["dynCall_iiii"];
initRuntime(asm);
ready()
}))
// EXTRA_INFO: {"mapping": {"_llvm_bswap_i32": "k", "_emscripten_random": "c", "dynCall_ii": "o", "__GLOBAL__sub_I_test_global_initializer_cpp": "i", "___errno_location": "j", "dynCall_iiii": "p", "___syscall6": "f", "_memset": "n", "_memcpy": "m", "abort": "b", "___syscall146": "a", "_emscripten_memcpy_big": "d", "___syscall54": "g", "___syscall140": "h", "_emscripten_get_now": "e", "_main": "l"}}

59
tests/test_other.py поставляемый
Просмотреть файл

@ -2106,7 +2106,9 @@ int f() {
assert 'hello, world!' in run_js('two.js')
def test_js_optimizer(self):
ACORN_PASSES = ['JSDCE', 'AJSDCE', 'applyImportAndExportNameChanges', 'emitDCEGraph', 'applyDCEGraphRemovals']
for input, expected, passes in [
(path_from_root('tests', 'optimizer', 'eliminateDeadGlobals.js'), open(path_from_root('tests', 'optimizer', 'eliminateDeadGlobals-output.js')).read(),
['eliminateDeadGlobals']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-output.js')).read(),
@ -2179,30 +2181,32 @@ int f() {
['splitMemory']),
(path_from_root('tests', 'optimizer', 'JSDCE.js'), open(path_from_root('tests', 'optimizer', 'JSDCE-output.js')).read(),
['JSDCE']),
(path_from_root('tests', 'optimizer', 'JSDCE-uglifyjsNodeTypes.js'), open(path_from_root('tests', 'optimizer', 'JSDCE-uglifyjsNodeTypes-output.js')).read(),
['JSDCE']),
(path_from_root('tests', 'optimizer', 'JSDCE-hasOwnProperty.js'), open(path_from_root('tests', 'optimizer', 'JSDCE-hasOwnProperty-output.js')).read(),
['JSDCE']),
(path_from_root('tests', 'optimizer', 'JSDCE-fors.js'), open(path_from_root('tests', 'optimizer', 'JSDCE-fors-output.js')).read(),
['JSDCE']),
(path_from_root('tests', 'optimizer', 'AJSDCE.js'), open(path_from_root('tests', 'optimizer', 'AJSDCE-output.js')).read(),
['AJSDCE']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph-output.js')).read(),
['emitDCEGraph', 'noEmitAst']),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph2.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph2-output.js')).read(),
['emitDCEGraph', 'noEmitAst']),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph3.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph3-output.js')).read(),
['emitDCEGraph', 'noEmitAst']),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph4.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph4-output.js')).read(),
['emitDCEGraph', 'noEmitAst']),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph5.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph5-output.js')).read(),
['emitDCEGraph', 'noEmitAst']),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'applyDCEGraphRemovals.js'), open(path_from_root('tests', 'optimizer', 'applyDCEGraphRemovals-output.js')).read(),
['applyDCEGraphRemovals']),
(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges.js'), open(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges-output.js')).read(),
['applyImportAndExportNameChanges']),
(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges2.js'), open(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges2-output.js')).read(),
['applyImportAndExportNameChanges']),
(path_from_root('tests', 'optimizer', 'detectSign-modulus-emterpretify.js'), open(path_from_root('tests', 'optimizer', 'detectSign-modulus-emterpretify-output.js')).read(),
['noPrintMetadata', 'emterpretify', 'noEmitAst']),
(path_from_root('tests', 'optimizer', 'minimal-runtime-emitDCEGraph.js'), open(path_from_root('tests', 'optimizer', 'minimal-runtime-emitDCEGraph-output.js')).read(),
['emitDCEGraph', 'noEmitAst']),
['emitDCEGraph', 'noPrint']),
]:
print(input, passes)
@ -2210,9 +2214,15 @@ int f() {
expected = [expected]
expected = [out.replace('\n\n', '\n').replace('\n\n', '\n') for out in expected]
# test calling js optimizer
print(' js')
output = run_process(NODE_JS + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).stdout
acorn = any([p for p in passes if p in ACORN_PASSES])
# test calling optimizer
if not acorn:
print(' js')
output = run_process(NODE_JS + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).stdout
else:
print(' acorn')
output = run_process(NODE_JS + [path_from_root('tools', 'acorn-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).stdout
def check_js(js, expected):
# print >> sys.stderr, 'chak\n==========================\n', js, '\n===========================\n'
@ -2345,11 +2355,10 @@ int f() {
else:
self.assertNotIn(' -g ', finalize)
else:
opts = '\n'.join([l for l in lines if os.path.sep + 'opt' in l])
if expect_debug:
self.assertNotIn('strip-debug', opts)
self.assertNotIn('strip-debug', err)
else:
self.assertIn('strip-debug', opts)
self.assertIn('strip-debug', err)
@unittest.skipIf(not scons_path, 'scons not found in PATH')
@with_env_modify({'EMSCRIPTEN_ROOT': path_from_root()})
@ -8345,10 +8354,28 @@ int main() {
}
''')
output = run_process([PYTHON, EMCC, 'src.cpp', '-O2'], stdout=PIPE, stderr=PIPE, check=False)
self.assertContained('''
# wasm backend output doesn't have spaces in the EM_ASM function bodies
self.assertContained(('''
var ASM_CONSTS = [function() { var x = !<->5.; }];
^
''', output.stderr)
''', '''
var ASM_CONSTS = [function() {var x = !<->5.;}];
^
'''), output.stderr)
def test_EM_ASM_ES6(self):
# check we show a proper understandable error for JS parse problems
create_test_file('src.cpp', r'''
#include <emscripten.h>
int main() {
EM_ASM({
var x = (a, b) => 5; // valid ES6!
out('hello!');
});
}
''')
run_process([PYTHON, EMCC, 'src.cpp', '-O2'])
self.assertContained('hello!', run_js('a.out.js'))
def test_check_sourcemapurl(self):
if not self.is_wasm():

800
tools/acorn-optimizer.js Normal file
Просмотреть файл

@ -0,0 +1,800 @@
var acorn = require('acorn');
var terser = require("terser");
var fs = require('fs');
var path = require('path');
// Setup
var print = function(x) {
process.stdout.write(x + '\n');
};
var printErr = function(x) {
process.stderr.write(x + '\n');
};
var read = function(x) {
return fs.readFileSync(x).toString();
}
// Utilities
function assert(condition, text) {
if (!condition) throw text + ' : ' + new Error().stack;
}
function set(args) {
var ret = {};
for (var i = 0; i < args.length; i++) {
ret[args[i]] = 0;
}
return ret;
}
// Visits and walks
// (We don't use acorn-walk because it ignores x in 'x = y'.)
function visitChildren(node, c) {
// emptyOut() and temporary ignoring may mark nodes as empty,
// while they have properties with children we should ignore.
if (node.type === 'EmptyStatement') {
return;
}
function maybeChild(child) {
if (child && typeof child === 'object' && typeof child.type === 'string') {
c(child);
return true;
}
return false;
}
for (var key in node) {
var child = node[key];
// Check for a child.
if (!maybeChild(child)) {
// Check for an array of children.
if (Array.isArray(child)) {
child.forEach(maybeChild);
}
}
}
}
// Simple post-order walk, calling properties on an object by node type,
// if the type exists.
function simpleWalk(node, cs) {
visitChildren(node, function(child) {
simpleWalk(child, cs);
});
if (node.type in cs) {
cs[node.type](node);
}
}
// Full post-order walk, calling a single function for all types.
function fullWalk(node, c) {
visitChildren(node, function(child) {
fullWalk(child, c);
});
c(node);
}
// Recursive post-order walk, calling properties on an object by node type,
// if the type exists, and if so leaving recursion to that function.
function recursiveWalk(node, cs) {
//print('recw1 ' + JSON.stringify(node));
(function c(node) {
if (!(node.type in cs)) {
visitChildren(node, function(child) {
recursiveWalk(child, cs);
});
} else {
cs[node.type](node, c);
}
})(node);
}
// AST Utilities
function emptyOut(node) {
node.type = 'EmptyStatement';
}
function nullify(node) {
node.type = 'Literal';
node.value = null;
node.raw = 'null';
}
function undefinedify(node) {
node.type = 'Literal';
node.value = undefined;
node.raw = 'undefined';
}
function setLiteralValue(item, value) {
item.value = value;
item.raw = "'" + value + "'";
}
function isLiteralString(node) {
return node.type === 'Literal' &&
(node.raw[0] === '"' || node.raw[0] === "'");
}
function dump(node, text) {
if (text) print(text);
print(JSON.stringify(node, null, ' '));
}
// Mark inner scopes temporarily as empty statements. Returns
// a special object that must be used to restore them.
function ignoreInnerScopes(node) {
var map = new WeakMap();
function ignore(node) {
map.set(node, node.type);
node.type = 'EmptyStatement';
}
simpleWalk(node, {
FunctionDeclaration(node) {
ignore(node);
},
FunctionExpression(node) {
ignore(node);
},
ArrowFunctionExpression(node) {
ignore(node);
},
// TODO: arrow etc.
});
return map;
}
// Mark inner scopes temporarily as empty statements.
function restoreInnerScopes(node, map) {
fullWalk(node, function(node) {
if (map.has(node)) {
node.type = map.get(node);
map.delete(node);
restoreInnerScopes(node, map);
}
});
}
// If we empty out a var from
// for (var i in x) {}
// for (var j = 0;;) {}
// then it will be invalid. We saved it on the side;
// restore it here.
function restoreForVars(node) {
var restored = 0;
function fix(init) {
if (init && init.type === 'EmptyStatement') {
assert(init.oldDeclarations);
init.type = 'VariableDeclaration';
init.declarations = init.oldDeclarations;
restored++;
}
}
simpleWalk(node, {
ForStatement(node) { fix(node.init) },
ForInStatement(node) { fix(node.left) },
});
return restored;
}
function hasSideEffects(node) {
// Conservative analysis.
var map = ignoreInnerScopes(node);
var has = false;
fullWalk(node, function(node) {
switch (node.type) {
// TODO: go through all the ESTree spec
case 'Literal':
case 'Identifier':
case 'UnaryExpression':
case 'BinaryExpresson':
case 'ExpressionStatement':
case 'UpdateOperator':
case 'ConditionalExpression':
case 'FunctionDeclaration':
case 'FunctionExpression':
case 'ArrowFunctionExpression':
case 'VariableDeclaration':
case 'VariableDeclarator':
case 'ObjectExpression':
case 'Property':
case 'BlockStatement':
case 'ArrayExpression':
case 'EmptyStatement': {
break; // safe
}
case 'MemberExpression': {
// safe if on Math (or other familiar objects, TODO)
if (node.object.type !== 'Identifier' ||
node.object.name !== 'Math') {
//console.error('because member on ' + node.object.name);
has = true;
}
break;
}
default: {
has = true;
//console.error('because ' + node.type);
}
}
});
restoreInnerScopes(node, map);
return has;
}
// Passes
// Removes obviously-unused code. Similar to closure compiler in its rules -
// export e.g. by Module['..'] = theThing; , or use it somewhere, otherwise
// it goes away.
//
// Note that this is somewhat conservative, since the ESTree AST does not
// have a simple separation between definitions and uses, e.g.
// Identifier is used both for the x in function foo(x) {
// and for y = x + 1 . That means we need to consider new ES6+ constructs
// as they appear (like ArrowFunctionExpression). Instead, we do a conservative
// analysis here.
function JSDCE(ast, multipleIterations) {
function iteration() {
var removed = 0;
var scopes = [{}]; // begin with empty toplevel scope
function DUMP() {
printErr('vvvvvvvvvvvvvv');
for (var i = 0; i < scopes.length; i++) {
printErr(i + ' : ' + JSON.stringify(scopes[i]));
}
printErr('^^^^^^^^^^^^^^');
}
function ensureData(scope, name) {
if (Object.prototype.hasOwnProperty.call(scope, name)) return scope[name];
scope[name] = {
def: 0,
use: 0,
param: 0 // true for function params, which cannot be eliminated
};
return scope[name];
}
function cleanUp(ast, names) {
recursiveWalk(ast, {
VariableDeclaration(node, c) {
var old = node.declarations;
var removedHere = 0;
node.declarations = node.declarations.filter(function(node) {
var curr = node.id.name
var value = node.init;
var keep = !(curr in names) || (value && hasSideEffects(value));
if (!keep) removedHere = 1;
return keep;
});
removed += removedHere;
if (node.declarations.length === 0) {
emptyOut(node);
// If this is in a for, we may need to restore it.
node.oldDeclarations = old;
}
},
FunctionDeclaration(node, c) {
if (Object.prototype.hasOwnProperty.call(names, node.id.name)) {
removed++;
emptyOut(node);
return;
}
// do not recurse into other scopes
},
// do not recurse into other scopes
FunctionExpression() {},
ArrowFunctionExpression() {},
});
removed -= restoreForVars(ast);
}
function handleFunction(node, c, defun) {
// defun names matter - function names (the y in var x = function y() {..}) are just for stack traces.
if (defun) {
ensureData(scopes[scopes.length-1], node.id.name).def = 1;
}
var scope = {};
node.params.forEach(function(param) {
var name = param.name;
ensureData(scope, name).def = 1;
scope[name].param = 1;
});
scopes.push(scope);
c(node.body);
// we can ignore self-references, i.e., references to ourselves inside
// ourselves, for named defined (defun) functions
var ownName = defun ? node.id.name : '';
var scope = scopes.pop();
var names = {};
for (var name in scope) {
if (name === ownName) continue;
var data = scope[name];
if (data.use && !data.def) {
// this is used from a higher scope, propagate the use down
ensureData(scopes[scopes.length-1], name).use = 1;
continue;
}
if (data.def && !data.use && !data.param) {
// this is eliminateable!
names[name] = 0;
}
}
cleanUp(node.body, names);
}
recursiveWalk(ast, {
VariableDeclarator(node, c) {
var name = node.id.name;
ensureData(scopes[scopes.length-1], name).def = 1;
if (node.init) c(node.init);
},
ObjectExpression(node, c) {
// ignore the property identifiers
node.properties.forEach(function(node) {
c(node.value);
});
},
FunctionDeclaration(node, c) {
handleFunction(node, c, true /* defun */);
},
FunctionExpression(node, c) {
handleFunction(node, c);
},
ArrowFunctionExpression(node, c) {
handleFunction(node, c);
},
Identifier(node, c) {
var name = node.name;
ensureData(scopes[scopes.length-1], name).use = 1;
},
});
// toplevel
var scope = scopes.pop();
assert(scopes.length === 0);
var names = {};
for (var name in scope) {
var data = scope[name];
if (data.def && !data.use) {
assert(!data.param); // can't be
// this is eliminateable!
names[name] = 0;
}
}
cleanUp(ast, names);
return removed;
}
while (iteration() && multipleIterations) { }
}
// Aggressive JSDCE - multiple iterations
function AJSDCE(ast) {
JSDCE(ast, /* multipleIterations= */ true);
}
function isAsmLibraryArgAssign(node) { // var asmLibraryArg = ..
return node.type === 'VariableDeclaration' &&
node.declarations.length === 1 &&
node.declarations[0].id.name === 'asmLibraryArg' &&
node.declarations[0].init &&
node.declarations[0].init.type === 'ObjectExpression';
}
function getAsmLibraryArgValue(node) {
return node.declarations[0].init;
}
function isAsmUse(node) {
return node.type === 'MemberExpression' &&
((node.object.type === 'Identifier' && // asm['X']
node.object.name === 'asm' &&
node.property.type === 'Literal') ||
(node.object.type === 'MemberExpression' && // Module['asm']['X']
node.object.object.type === 'Identifier' &&
node.object.object.name === 'Module' &&
node.object.property.type === 'Literal' &&
node.object.property.value === 'asm' &&
isLiteralString(node.property)));
}
function getAsmOrModuleUseName(node) {
return node.property.value;
}
function isModuleUse(node) {
return node.type === 'MemberExpression' && // Module['X']
node.object.type === 'Identifier' &&
node.object.name === 'Module' &&
isLiteralString(node.property);
}
function isModuleAsmUse(node) { // Module['asm'][..string..]
return node.type === 'MemberExpression' &&
node.object.type === 'MemberExpression' &&
node.object.object.type === 'Identifier' &&
node.object.object.name === 'Module' &&
node.object.property.type === 'Literal' &&
node.object.property.value === 'asm' &&
isLiteralString(node.property);
}
// Apply import/export name changes (after minifying them)
function applyImportAndExportNameChanges(ast) {
var mapping = extraInfo.mapping;
fullWalk(ast, function(node) {
if (isAsmLibraryArgAssign(node)) {
var assignedObject = getAsmLibraryArgValue(node);
assignedObject.properties.forEach(function(item) {
if (mapping[item.key.value]) {
setLiteralValue(item.key, mapping[item.key.value]);
}
});
} else if (node.type === 'AssignmentExpression') {
var target = node.left;
var value = node.right;
if (isAsmUse(value)) {
var name = value.property.value;
if (mapping[name]) {
setLiteralValue(value.property, mapping[name]);
}
}
} else if (isModuleAsmUse(node)) {
var prop = node.property;
var name = prop.value;
if (mapping[name]) {
setLiteralValue(prop, mapping[name]);
}
} else if (isAsmUse(node)) {
var prop = node.property;
var name = prop.value;
if (mapping[name]) {
setLiteralValue(prop, mapping[name]);
}
}
});
}
// A static dyncall is dynCall('vii', ..), which is actually static even
// though we call dynCall() - we see the string signature statically.
function isStaticDynCall(node) {
return node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
node.callee.name === 'dynCall' &&
isLiteralString(node.arguments[0])
}
function getStaticDynCallName(node) {
return 'dynCall_' + node.arguments[0].value;
}
// a dynamic dyncall is one in which all we know is *some* dynCall may
// be called, but not who. This can be either
// dynCall(*not a string*, ..)
// or, to be conservative,
// "dynCall_"
// as that prefix means we may be constructing a dynamic dyncall name
// (dynCall and embind's requireFunction do this internally).
function isDynamicDynCall(node) {
return (node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
node.callee.name === 'dynCall' &&
!isLiteralString(node.arguments[0])) ||
(isLiteralString(node) &&
node.value === 'dynCall_');
}
//
// Emit the DCE graph, to help optimize the combined JS+wasm.
// This finds where JS depends on wasm, and where wasm depends
// on JS, and prints that out.
//
// The analysis here is simplified, and not completely general. It
// is enough to optimize the common case of JS library and runtime
// functions involved in loops with wasm, but not more complicated
// things like JS objects and sub-functions. Specifically we
// analyze as follows:
//
// * We consider (1) the toplevel scope, and (2) the scopes of toplevel defined
// functions (defun, not function; i.e., function X() {} where
// X can be called later, and not y = function Z() {} where Z is
// just a name for stack traces). We also consider the wasm, which
// we can see things going to and arriving from.
// * Anything used in a defun creates a link in the DCE graph, either
// to another defun, or the wasm.
// * Anything used in the toplevel scope is rooted, as it is code
// we assume will execute. The exceptions are
// * when we receive something from wasm; those are "free" and
// do not cause rooting. (They will become roots if they are
// exported, the metadce logic will handle that.)
// * when we send something to wasm; sending a defun causes a
// link in the DCE graph.
// * Anything not in the toplevel or not in a toplevel defun is
// considering rooted. We don't optimize those cases.
//
// Special handling:
//
// * dynCall('vii', ..) are dynamic dynCalls, but we analyze them
// statically, to preserve the dynCall_vii etc. method they depend on.
// Truly dynamic dynCalls (not to a string constant) will not work,
// and require the user to export them.
// * Truly dynamic dynCalls are assumed to reach any dynCall_*.
//
// XXX this modifies the input AST. if you want to keep using it,
// that should be fixed. Currently the main use case here does
// not require that. TODO FIXME
//
function emitDCEGraph(ast) {
// First pass: find the wasm imports and exports, and the toplevel
// defuns, and save them on the side, removing them from the AST,
// which makes the second pass simpler.
//
// The imports that wasm receives look like this:
//
// Module.asmLibraryArg = { "abort": abort, "assert": assert, [..] };
//
// The exports are trickier, as they have a different form whether or not
// async compilation is enabled. It can be either:
//
// var _malloc = Module["_malloc"] = asm["_malloc"];
//
// or
//
// var _malloc = Module["_malloc"] = (function() {
// return Module["asm"]["_malloc"].apply(null, arguments);
// });
//
var imports = [];
var defuns = [];
var dynCallNames = [];
var nameToGraphName = {};
var modulePropertyToGraphName = {};
var exportNameToGraphName = {}; // identical to asm['..'] nameToGraphName
var foundAsmLibraryArgAssign = false;
var graph = [];
function saveAsmExport(name, asmName) {
// the asmName is what the wasm provides directly; the outside JS
// name may be slightly different (extra "_" in wasm backend)
var graphName = getGraphName(name, 'export');
nameToGraphName[name] = graphName;
modulePropertyToGraphName[name] = graphName;
exportNameToGraphName[asmName] = graphName;
if (/^dynCall_/.test(name)) {
dynCallNames.push(graphName);
}
}
fullWalk(ast, function(node) {
if (isAsmLibraryArgAssign(node)) {
var assignedObject = getAsmLibraryArgValue(node);
assignedObject.properties.forEach(function(item) {
var value = item.value;
assert(value.type === 'Identifier');
imports.push(value.name); // the name doesn't matter, only the value which is that actual thing we are importing
});
foundAsmLibraryArgAssign = true;
emptyOut(node); // ignore this in the second pass; this does not root
} else if (node.type === 'VariableDeclaration') {
if (node.declarations.length === 1) {
var item = node.declarations[0];
var name = item.id.name;
var value = item.init;
if (value && value.type === 'AssignmentExpression') {
var assigned = value.left;
if (isModuleUse(assigned) && getAsmOrModuleUseName(assigned) === name) {
// this is
// var x = Module['x'] = ?
// which looks like a wasm export being received. confirm with the asm use
var found = 0;
var asmName;
fullWalk(value.right, function(node) {
if (isAsmUse(node)) {
found++;
asmName = getAsmOrModuleUseName(node);
}
});
// in the wasm backend, the asm name may have one fewer "_" prefixed
if (found === 1) {
// this is indeed an export
// the asmName is what the wasm provides directly; the outside JS
// name may be slightly different (extra "_" in wasm backend)
saveAsmExport(name, asmName);
emptyOut(node); // ignore this in the second pass; this does not root
}
}
}
}
} else if (node.type === 'FunctionDeclaration') {
defuns.push(node);
var name = node.id.name;
nameToGraphName[name] = getGraphName(name, 'defun');
emptyOut(node); // ignore this in the second pass; we scan defuns separately
}
});
// must find the info we need
assert(foundAsmLibraryArgAssign, 'could not find the assigment to "asmLibraryArg". perhaps --pre-js or --post-js code moved it out of the global scope? (things like that should be done after emcc runs, as they do not need to be run through the optimizer which is the special thing about --pre-js/--post-js code)');
// Read exports that were declared in extraInfo
if (extraInfo) {
for (var e in extraInfo.exports) {
var exp = extraInfo.exports[e];
saveAsmExport(exp[0], exp[1]);
}
}
// Second pass: everything used in the toplevel scope is rooted;
// things used in defun scopes create links
function getGraphName(name, what) {
return 'emcc$' + what + '$' + name;
}
var infos = {}; // the graph name of the item => info for it
imports.forEach(function(import_) {
var name = getGraphName(import_, 'import');
var info = infos[name] = {
name: name,
import: ['env', import_],
reaches: {}
};
if (nameToGraphName.hasOwnProperty(import_)) {
info.reaches[nameToGraphName[import_]] = 1;
} // otherwise, it's a number, ignore
});
for (var e in exportNameToGraphName) {
var name = exportNameToGraphName[e];
infos[name] = {
name: name,
export: e,
reaches: {}
};
}
// a function that handles a node we visit, in either a defun or
// the toplevel scope (in which case the second param is not provided)
function visitNode(node, defunInfo) {
// TODO: scope awareness here. for now we just assume all uses are
// from the top scope, which might create more uses than needed
var reached;
if (node.type === 'Identifier') {
var name = node.name;
if (nameToGraphName.hasOwnProperty(name)) {
reached = nameToGraphName[name];
}
} else if (isModuleUse(node)) {
var name = getAsmOrModuleUseName(node);
if (modulePropertyToGraphName.hasOwnProperty(name)) {
reached = modulePropertyToGraphName[name];
}
} else if (isStaticDynCall(node)) {
reached = getGraphName(getStaticDynCallName(node), 'export');
} else if (isDynamicDynCall(node)) {
// this can reach *all* dynCall_* targets, we can't narrow it down
reached = dynCallNames;
} else if (isAsmUse(node)) {
// any remaining asm uses are always rooted in any case
var name = getAsmOrModuleUseName(node);
if (exportNameToGraphName.hasOwnProperty(name)) {
infos[exportNameToGraphName[name]].root = true;
}
return;
}
if (reached) {
function addReach(reached) {
if (defunInfo) {
defunInfo.reaches[reached] = 1; // defun reaches it
} else {
infos[reached].root = true; // in global scope, root it
}
}
if (typeof reached === 'string') {
addReach(reached);
} else {
reached.forEach(addReach);
}
}
}
defuns.forEach(function(defun) {
var name = getGraphName(defun.id.name, 'defun');
var info = infos[name] = {
name: name,
reaches: {}
};
fullWalk(defun.body, function(node) {
visitNode(node, info);
});
});
fullWalk(ast, function(node) {
visitNode(node, null);
});
// Final work: print out the graph
// sort for determinism
function sortedNamesFromMap(map) {
var names = [];
for (var name in map) {
names.push(name);
}
names.sort();
return names;
}
sortedNamesFromMap(infos).forEach(function(name) {
var info = infos[name];
info.reaches = sortedNamesFromMap(info.reaches);
graph.push(info);
});
print(JSON.stringify(graph, null, ' '));
}
// Apply graph removals from running wasm-metadce
function applyDCEGraphRemovals(ast) {
var unused = set(extraInfo.unused);
fullWalk(ast, function(node) {
if (isAsmLibraryArgAssign(node)) {
var assignedObject = getAsmLibraryArgValue(node);
assignedObject.properties = assignedObject.properties.filter(function(item) {
var name = item.key.value;
var value = item.value;
var full = 'emcc$import$' + name;
return !((full in unused) && !hasSideEffects(value));
});
} else if (node.type === 'AssignmentExpression') {
// when we assign to a thing we don't need, we can just remove the assign
var target = node.left;
if (isAsmUse(target) || isModuleUse(target)) {
var name = getAsmOrModuleUseName(target);
var full = 'emcc$export$' + name;
var value = node.right;
if ((full in unused) &&
(isAsmUse(value) || !hasSideEffects(value))) {
undefinedify(node);
}
}
}
});
}
// Main
var arguments = process['argv'].slice(2);;
var infile = arguments[0];
var passes = arguments.slice(1);
var input = read(infile);
var extraInfoStart = input.lastIndexOf('// EXTRA_INFO:')
var extraInfo = null;
if (extraInfoStart > 0) {
extraInfo = JSON.parse(input.substr(extraInfoStart + 14));
}
var ast = acorn.parse(input, { ecmaVersion: 6 });
var minifyWhitespace = false;
var noPrint = false;
var registry = {
JSDCE: JSDCE,
AJSDCE: AJSDCE,
applyImportAndExportNameChanges: applyImportAndExportNameChanges,
emitDCEGraph: emitDCEGraph,
applyDCEGraphRemovals: applyDCEGraphRemovals,
minifyWhitespace: function() { minifyWhitespace = true },
noPrint: function() { noPrint = true },
dump: function() { dump(ast) },
};
passes.forEach(function(pass) {
registry[pass](ast);
});
if (!noPrint) {
var terserAst = terser.AST_Node.from_mozilla_ast(ast);
var output = terserAst.print_to_string({
beautify: !minifyWhitespace,
indent_level: minifyWhitespace ? 0 : 1,
keep_quoted_props: true, // for closure
});
print(output);
}

Просмотреть файл

@ -7914,121 +7914,6 @@ function eliminateDeadGlobals(ast) {
});
}
// Removes obviously-unused code. Similar to closure compiler in its rules -
// export e.g. by Module['..'] = theThing; , or use it somewhere, otherwise
// it goes away.
function JSDCE(ast, multipleIterations) {
function iteration() {
var removed = false;
var scopes = [{}]; // begin with empty toplevel scope
function DUMP() {
printErr('vvvvvvvvvvvvvv');
for (var i = 0; i < scopes.length; i++) {
printErr(i + ' : ' + JSON.stringify(scopes[i]));
}
printErr('^^^^^^^^^^^^^^');
}
function ensureData(scope, name) {
if (Object.prototype.hasOwnProperty.call(scope, name)) return scope[name];
scope[name] = {
def: 0,
use: 0,
param: 0 // true for function params, which cannot be eliminated
};
return scope[name];
}
function cleanUp(ast, names) {
traverse(ast, function(node, type) {
if (type === 'defun' && Object.prototype.hasOwnProperty.call(names, node[1])) {
removed = true;
return emptyNode();
}
if (type === 'defun' || type === 'function') return null; // do not enter other scopes
if (type === 'var') {
node[1] = node[1].filter(function(varItem, j) {
var curr = varItem[0];
var value = varItem[1];
var keep = !(curr in names) || (value && hasSideEffects(value));
if (!keep) removed = true;
return keep;
});
if (node[1].length === 0) return emptyNode();
}
});
return ast;
}
traverse(ast, function(node, type) {
if (type === 'var') {
node[1].forEach(function(varItem, j) {
var name = varItem[0];
ensureData(scopes[scopes.length-1], name).def = 1;
});
return;
}
if (type === 'object') {
return;
}
if (type === 'defun' || type === 'function') {
// defun names matter - function names (the y in var x = function y() {..}) are just for stack traces.
if (type === 'defun') ensureData(scopes[scopes.length-1], node[1]).def = 1;
var scope = {};
node[2].forEach(function(param) {
ensureData(scope, param).def = 1;
scope[param].param = 1;
});
scopes.push(scope);
return;
}
if (type === 'name') {
ensureData(scopes[scopes.length-1], node[1]).use = 1;
}
}, function(node, type) {
if (type === 'defun' || type === 'function') {
// we can ignore self-references, i.e., references to ourselves inside
// ourselves, for named defined (defun) functions
var ownName = type === 'defun' ? node[1] : '';
var scope = scopes.pop();
var names = set();
for (name in scope) {
if (name === ownName) continue;
var data = scope[name];
if (data.use && !data.def) {
// this is used from a higher scope, propagate the use down
ensureData(scopes[scopes.length-1], name).use = 1;
continue;
}
if (data.def && !data.use && !data.param) {
// this is eliminateable!
names[name] = 0;
}
}
cleanUp(node[3], names);
}
});
// toplevel
var scope = scopes.pop();
assert(scopes.length === 0);
var names = set();
for (var name in scope) {
var data = scope[name];
if (data.def && !data.use) {
assert(!data.param); // can't be
// this is eliminateable!
names[name] = 0;
}
}
cleanUp(ast, names);
return removed;
}
while (iteration() && multipleIterations) { }
}
// Aggressive JSDCE - multiple iterations
function AJSDCE(ast) {
JSDCE(ast, /* multipleIterations= */ true);
}
function isAsmLibraryArgAssign(node) {
return node[0] === 'var' && node[1][0] && node[1][0][0] == 'asmLibraryArg';
}
@ -8086,330 +7971,6 @@ function isDynamicDynCall(node) {
(node[0] === 'string' && node[1] === 'dynCall_');
}
//
// Emit the DCE graph, to help optimize the combined JS+wasm.
// This finds where JS depends on wasm, and where wasm depends
// on JS, and prints that out.
//
// The analysis here is simplified, and not completely general. It
// is enough to optimize the common case of JS library and runtime
// functions involved in loops with wasm, but not more complicated
// things like JS objects and sub-functions. Specifically we
// analyze as follows:
//
// * We consider (1) the toplevel scope, and (2) the scopes of toplevel defined
// functions (defun, not function; i.e., function X() {} where
// X can be called later, and not y = function Z() {} where Z is
// just a name for stack traces). We also consider the wasm, which
// we can see things going to and arriving from.
// * Anything used in a defun creates a link in the DCE graph, either
// to another defun, or the wasm.
// * Anything used in the toplevel scope is rooted, as it is code
// we assume will execute. The exceptions are
// * when we receive something from wasm; those are "free" and
// do not cause rooting. (They will become roots if they are
// exported, the metadce logic will handle that.)
// * when we send something to wasm; sending a defun causes a
// link in the DCE graph.
// * Anything not in the toplevel or not in a toplevel defun is
// considering rooted. We don't optimize those cases.
//
// Special handling:
//
// * dynCall('vii', ..) are dynamic dynCalls, but we analyze them
// statically, to preserve the dynCall_vii etc. method they depend on.
// Truly dynamic dynCalls (not to a string constant) will not work,
// and require the user to export them.
// * Truly dynamic dynCalls are assumed to reach any dynCall_*.
//
// XXX this modifies the input AST. if you want to keep using it,
// that should be fixed. Currently the main use case here does
// not require that. TODO FIXME
//
function emitDCEGraph(ast) {
// First pass: find the wasm imports and exports, and the toplevel
// defuns, and save them on the side, removing them from the AST,
// which makes the second pass simpler.
//
// The imports that wasm receives look like this:
//
// var asmLibraryArg = { "abort": abort, "assert": assert, [..] };
//
// The exports are trickier, as they have a different form whether or not
// async compilation is enabled. It can be either:
//
// var _malloc = Module["_malloc"] = asm["_malloc"];
//
// or
//
// var _malloc = Module["_malloc"] = (function() {
// return Module["asm"]["_malloc"].apply(null, arguments);
// });
//
var imports = [];
var defuns = [];
var dynCallNames = [];
var nameToGraphName = {};
var modulePropertyToGraphName = {};
var exportNameToGraphName = {}; // identical to asm['..'] nameToGraphName
var foundAsmLibraryArgAssign = false;
var graph = [];
function saveAsmExport(name, asmName) {
var graphName = getGraphName(name, 'export');
nameToGraphName[name] = graphName;
modulePropertyToGraphName[name] = graphName;
exportNameToGraphName[asmName] = graphName;
if (/^dynCall_/.test(name) && dynCallNames.indexOf(name) < 0) {
dynCallNames.push(graphName);
}
}
traverse(ast, function(node, type) {
if (isAsmLibraryArgAssign(node)) {
var items = asmLibraryArgs(node)[1];
items.forEach(function(item) {
imports.push(item[1][1]); // the name doesn't matter, only the value which is that actual thing we are importing
});
foundAsmLibraryArgAssign = true;
return emptyNode(); // ignore this in the second pass; this does not root
} else if (type === 'var') {
if (node[1] && node[1].length === 1) {
var item = node[1][0];
var name = item[0];
var value = item[1];
if (Array.isArray(value) && value[0] === 'assign') {
var assigned = value[2];
if (isModuleUse(assigned) && getModuleUseName(assigned) === name) {
// this is
// var x = Module['x'] = ?
// which looks like a wasm export being received. confirm with the asm use
var found = 0;
var asmName;
traverse(value[3], function(node, type) {
if (isAsmUse(node)) {
found++;
asmName = getAsmUseName(node);
}
});
// in the wasm backend, the asm name may have one fewer "_" prefixed
if (found === 1) {
// this is indeed an export
// the asmName is what the wasm provides directly; the outside JS
// name may be slightly different (extra "_" in wasm backend)
saveAsmExport(name, asmName);
return emptyNode(); // ignore this in the second pass; this does not root
}
}
}
}
} else if (type === 'defun') {
defuns.push(node);
var name = node[1];
nameToGraphName[name] = getGraphName(name, 'defun');
return emptyNode(); // ignore this in the second pass; we scan defuns separately
} else if (type === 'function') {
return null; // don't look inside
}
});
// must find the info we need
assert(foundAsmLibraryArgAssign, 'could not find the assigment to "asmLibraryArg". perhaps --pre-js or --post-js code moved it out of the global scope? (things like that should be done after emcc runs, as they do not need to be run through the optimizer which is the special thing about --pre-js/--post-js code)');
// Read exports that were declared in extraInfo:
if (extraInfo) {
for(var e in extraInfo.exports) {
var exp = extraInfo.exports[e];
saveAsmExport(exp[0], exp[1]);
}
}
// Second pass: everything used in the toplevel scope is rooted;
// things used in defun scopes create links
function getGraphName(name, what) {
return 'emcc$' + what + '$' + name;
}
var infos = {}; // the graph name of the item => info for it
imports.forEach(function(import_) {
var name = getGraphName(import_, 'import');
var info = infos[name] = {
name: name,
import: ['env', import_],
reaches: {}
};
if (nameToGraphName.hasOwnProperty(import_)) {
info.reaches[nameToGraphName[import_]] = 1;
} // otherwise, it's a number, ignore
});
for (var e in exportNameToGraphName) {
var name = exportNameToGraphName[e];
infos[name] = {
name: name,
export: e,
reaches: {}
};
}
// a function that handles a node we visit, in either a defun or
// the toplevel scope (in which case the second param is not provided)
function visitNode(node, defunInfo) {
// TODO: scope awareness here. for now we just assume all uses are
// from the top scope, which might create more uses than needed
var reached;
if (node[0] === 'name') {
var name = node[1];
if (nameToGraphName.hasOwnProperty(name)) {
reached = nameToGraphName[name];
}
} else if (isModuleUse(node)) {
var name = getModuleUseName(node);
if (modulePropertyToGraphName.hasOwnProperty(name)) {
reached = modulePropertyToGraphName[name];
}
} else if (isStaticDynCall(node)) {
reached = getGraphName(getStaticDynCallName(node), 'export');
} else if (isDynamicDynCall(node)) {
// this can reach *all* dynCall_* targets, we can't narrow it down
reached = dynCallNames;
} else if (isAsmUse(node)) {
// any remaining asm uses are always rooted in any case
var name = getAsmUseName(node);
if (exportNameToGraphName.hasOwnProperty(name)) {
infos[exportNameToGraphName[name]].root = true;
}
return;
}
if (reached) {
function addReach(reached) {
if (defunInfo) {
defunInfo.reaches[reached] = 1; // defun reaches it
} else {
infos[reached].root = true; // in global scope, root it
}
}
if (typeof reached === 'string') {
addReach(reached);
} else {
reached.forEach(addReach);
}
}
}
defuns.forEach(function(defun) {
var name = getGraphName(defun[1], 'defun');
var info = infos[name] = {
name: name,
reaches: {}
};
traverse(defun[3], function(node, type) {
visitNode(node, info);
});
});
traverse(ast, function(node, type) {
visitNode(node, null);
});
// Final work: print out the graph
// sort for determinism
function sortedNamesFromMap(map) {
var names = [];
for (var name in map) {
names.push(name);
}
names.sort();
return names;
}
sortedNamesFromMap(infos).forEach(function(name) {
var info = infos[name];
info.reaches = sortedNamesFromMap(info.reaches);
graph.push(info);
});
print(JSON.stringify(graph, null, ' '));
}
// Apply graph removals from running wasm-metadce
function applyDCEGraphRemovals(ast) {
var unused = set(extraInfo.unused);
traverse(ast, function(node, type) {
if (isAsmLibraryArgAssign(node)) {
var args = asmLibraryArgs(node);
args[1] = args[1].filter(function(item) {
var name = item[0];
var value = item[1];
var full = 'emcc$import$' + name;
return !((full in unused) && !hasSideEffects(value));
});
} else if (type === 'assign') {
// when we assign to a thing we don't need, we can just remove the assign
var target = node[2];
if (isAsmUse(target) || isModuleUse(target)) {
var name = target[2][1];
var full = 'emcc$export$' + name;
var value = node[3];
if ((full in unused) && !hasSideEffects(value)) {
return ['name', 'undefined'];
}
}
}
});
}
// Apply import/export name changes (after minifying them)
function applyImportAndExportNameChanges(ast) {
var mapping = extraInfo.mapping;
traverse(ast, function(node, type) {
if (isAsmLibraryArgAssign(node)) {
var args = asmLibraryArgs(node);
args[1] = args[1].map(function(item) {
var name = item[0];
var value = item[1];
if (mapping[name]) {
var ret = [mapping[name], value];
// Uglify uses this property to tell it to emit
// { "quotedname": .. }
// as opposed to
// { quotedname: }
// We need quoting for closure compiler to work
// TODO: disable otherwise
ret.quoted = true;
return ret;
}
return item;
});
} else if (type === 'var') { // var foo = asm["foo"];
var children = node[1];
var target = children[0][0];
var value = children[0][1];
if (value && isAsmUse(value)) {
var name = value[2][1];
if (mapping[name]) {
value[2][1] = mapping[name];
}
}
} else if (type === 'assign') { // foo = asm["foo"];
var target = node[2];
var value = node[3];
if (isAsmUse(value)) {
var name = value[2][1];
if (mapping[name]) {
value[2][1] = mapping[name];
}
}
} else if (type === 'call') { // asm["foo"]();
var target = node[1];
if (isAsmUse(target)) {
var name = target[2][1];
if (mapping[name]) {
target[2][1] = mapping[name];
}
}
} else if (isModuleAsmUse(node)) { // Module["asm"]["foo"]
var prop = node[2];
var name = prop[1];
if (mapping[name]) {
prop[1] = mapping[name];
}
}
});
}
function removeFuncs(ast) {
assert(ast[0] === 'toplevel');
var keep = set(extraInfo.keep);
@ -8457,11 +8018,6 @@ var passes = {
findReachable: findReachable,
dumpCallGraph: dumpCallGraph,
asmLastOpts: asmLastOpts,
JSDCE: JSDCE,
AJSDCE: AJSDCE,
emitDCEGraph: emitDCEGraph,
applyDCEGraphRemovals: applyDCEGraphRemovals,
applyImportAndExportNameChanges: applyImportAndExportNameChanges,
removeFuncs: removeFuncs,
noop: function() {},

Просмотреть файл

@ -499,12 +499,11 @@ EMSCRIPTEN_FUNCS();
temp_files.note(cld)
elif cleanup:
if DEBUG: print('running cleanup on shell code', file=sys.stderr)
next = cld + '.cl.js'
temp_files.note(next)
proc = subprocess.Popen(js_engine + [JS_OPTIMIZER, cld, 'noPrintMetadata', 'JSDCE'] + (['minifyWhitespace'] if 'minifyWhitespace' in passes else []), stdout=open(next, 'w'))
proc.communicate()
assert proc.returncode == 0
cld = next
passes = ['JSDCE']
if 'minifyWhitespace' in passes:
passes.append('minifyWhitespace')
cld = shared.Building.acorn_optimizer(cld, passes)
temp_files.note(cld)
coutput = open(cld).read()
coutput = coutput.replace('wakaUnknownBefore();', start_asm)

462
tools/node_modules/acorn/CHANGELOG.md сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,462 @@
## 6.0.5 (2019-01-02)
### Bug fixes
Fix TypeScript type for `Parser.extend` and add `allowAwaitOutsideFunction` to options type.
Don't treat `let` as a keyword when the next token is `{` on the next line.
Fix bug that broke checking for parentheses around an object pattern in a destructuring assignment when `preserveParens` was on.
## 6.0.4 (2018-11-05)
### Bug fixes
Further improvements to tokenizing regular expressions in corner cases.
## 6.0.3 (2018-11-04)
### Bug fixes
Fix bug in tokenizing an expression-less return followed by a function followed by a regular expression.
Remove stray symlink in the package tarball.
## 6.0.2 (2018-09-26)
### Bug fixes
Fix bug where default expressions could fail to parse inside an object destructuring assignment expression.
## 6.0.1 (2018-09-14)
### Bug fixes
Fix wrong value in `version` export.
## 6.0.0 (2018-09-14)
### Bug fixes
Better handle variable-redefinition checks for catch bindings and functions directly under if statements.
Forbid `new.target` in top-level arrow functions.
Fix issue with parsing a regexp after `yield` in some contexts.
### New features
The package now comes with TypeScript definitions.
### Breaking changes
The default value of the `ecmaVersion` option is now 9 (2018).
Plugins work differently, and will have to be rewritten to work with this version.
The loose parser and walker have been moved into separate packages (`acorn-loose` and `acorn-walk`).
## 5.7.3 (2018-09-10)
### Bug fixes
Fix failure to tokenize regexps after expressions like `x.of`.
Better error message for unterminated template literals.
## 5.7.2 (2018-08-24)
### Bug fixes
Properly handle `allowAwaitOutsideFunction` in for statements.
Treat function declarations at the top level of modules like let bindings.
Don't allow async function declarations as the only statement under a label.
## 5.7.0 (2018-06-15)
### New features
Upgraded to Unicode 11.
## 5.6.0 (2018-05-31)
### New features
Allow U+2028 and U+2029 in string when ECMAVersion >= 10.
Allow binding-less catch statements when ECMAVersion >= 10.
Add `allowAwaitOutsideFunction` option for parsing top-level `await`.
## 5.5.3 (2018-03-08)
### Bug fixes
A _second_ republish of the code in 5.5.1, this time with yarn, to hopefully get valid timestamps.
## 5.5.2 (2018-03-08)
### Bug fixes
A republish of the code in 5.5.1 in an attempt to solve an issue with the file timestamps in the npm package being 0.
## 5.5.1 (2018-03-06)
### Bug fixes
Fix misleading error message for octal escapes in template strings.
## 5.5.0 (2018-02-27)
### New features
The identifier character categorization is now based on Unicode version 10.
Acorn will now validate the content of regular expressions, including new ES9 features.
## 5.4.0 (2018-02-01)
### Bug fixes
Disallow duplicate or escaped flags on regular expressions.
Disallow octal escapes in strings in strict mode.
### New features
Add support for async iteration.
Add support for object spread and rest.
## 5.3.0 (2017-12-28)
### Bug fixes
Fix parsing of floating point literals with leading zeroes in loose mode.
Allow duplicate property names in object patterns.
Don't allow static class methods named `prototype`.
Disallow async functions directly under `if` or `else`.
Parse right-hand-side of `for`/`of` as an assignment expression.
Stricter parsing of `for`/`in`.
Don't allow unicode escapes in contextual keywords.
### New features
Parsing class members was factored into smaller methods to allow plugins to hook into it.
## 5.2.1 (2017-10-30)
### Bug fixes
Fix a token context corruption bug.
## 5.2.0 (2017-10-30)
### Bug fixes
Fix token context tracking for `class` and `function` in property-name position.
Make sure `%*` isn't parsed as a valid operator.
Allow shorthand properties `get` and `set` to be followed by default values.
Disallow `super` when not in callee or object position.
### New features
Support [`directive` property](https://github.com/estree/estree/compare/b3de58c9997504d6fba04b72f76e6dd1619ee4eb...1da8e603237144f44710360f8feb7a9977e905e0) on directive expression statements.
## 5.1.2 (2017-09-04)
### Bug fixes
Disable parsing of legacy HTML-style comments in modules.
Fix parsing of async methods whose names are keywords.
## 5.1.1 (2017-07-06)
### Bug fixes
Fix problem with disambiguating regexp and division after a class.
## 5.1.0 (2017-07-05)
### Bug fixes
Fix tokenizing of regexps in an object-desctructuring `for`/`of` loop and after `yield`.
Parse zero-prefixed numbers with non-octal digits as decimal.
Allow object/array patterns in rest parameters.
Don't error when `yield` is used as a property name.
Allow `async` as a shorthand object property.
### New features
Implement the [template literal revision proposal](https://github.com/tc39/proposal-template-literal-revision) for ES9.
## 5.0.3 (2017-04-01)
### Bug fixes
Fix spurious duplicate variable definition errors for named functions.
## 5.0.2 (2017-03-30)
### Bug fixes
A binary operator after a parenthesized arrow expression is no longer incorrectly treated as an error.
## 5.0.0 (2017-03-28)
### Bug fixes
Raise an error for duplicated lexical bindings.
Fix spurious error when an assignement expression occurred after a spread expression.
Accept regular expressions after `of` (in `for`/`of`), `yield` (in a generator), and braced arrow functions.
Allow labels in front or `var` declarations, even in strict mode.
### Breaking changes
Parse declarations following `export default` as declaration nodes, not expressions. This means that class and function declarations nodes can now have `null` as their `id`.
## 4.0.11 (2017-02-07)
### Bug fixes
Allow all forms of member expressions to be parenthesized as lvalue.
## 4.0.10 (2017-02-07)
### Bug fixes
Don't expect semicolons after default-exported functions or classes, even when they are expressions.
Check for use of `'use strict'` directives in non-simple parameter functions, even when already in strict mode.
## 4.0.9 (2017-02-06)
### Bug fixes
Fix incorrect error raised for parenthesized simple assignment targets, so that `(x) = 1` parses again.
## 4.0.8 (2017-02-03)
### Bug fixes
Solve spurious parenthesized pattern errors by temporarily erring on the side of accepting programs that our delayed errors don't handle correctly yet.
## 4.0.7 (2017-02-02)
### Bug fixes
Accept invalidly rejected code like `(x).y = 2` again.
Don't raise an error when a function _inside_ strict code has a non-simple parameter list.
## 4.0.6 (2017-02-02)
### Bug fixes
Fix exponential behavior (manifesting itself as a complete hang for even relatively small source files) introduced by the new 'use strict' check.
## 4.0.5 (2017-02-02)
### Bug fixes
Disallow parenthesized pattern expressions.
Allow keywords as export names.
Don't allow the `async` keyword to be parenthesized.
Properly raise an error when a keyword contains a character escape.
Allow `"use strict"` to appear after other string literal expressions.
Disallow labeled declarations.
## 4.0.4 (2016-12-19)
### Bug fixes
Fix crash when `export` was followed by a keyword that can't be
exported.
## 4.0.3 (2016-08-16)
### Bug fixes
Allow regular function declarations inside single-statement `if` branches in loose mode. Forbid them entirely in strict mode.
Properly parse properties named `async` in ES2017 mode.
Fix bug where reserved words were broken in ES2017 mode.
## 4.0.2 (2016-08-11)
### Bug fixes
Don't ignore period or 'e' characters after octal numbers.
Fix broken parsing for call expressions in default parameter values of arrow functions.
## 4.0.1 (2016-08-08)
### Bug fixes
Fix false positives in duplicated export name errors.
## 4.0.0 (2016-08-07)
### Breaking changes
The default `ecmaVersion` option value is now 7.
A number of internal method signatures changed, so plugins might need to be updated.
### Bug fixes
The parser now raises errors on duplicated export names.
`arguments` and `eval` can now be used in shorthand properties.
Duplicate parameter names in non-simple argument lists now always produce an error.
### New features
The `ecmaVersion` option now also accepts year-style version numbers
(2015, etc).
Support for `async`/`await` syntax when `ecmaVersion` is >= 8.
Support for trailing commas in call expressions when `ecmaVersion` is >= 8.
## 3.3.0 (2016-07-25)
### Bug fixes
Fix bug in tokenizing of regexp operator after a function declaration.
Fix parser crash when parsing an array pattern with a hole.
### New features
Implement check against complex argument lists in functions that enable strict mode in ES7.
## 3.2.0 (2016-06-07)
### Bug fixes
Improve handling of lack of unicode regexp support in host
environment.
Properly reject shorthand properties whose name is a keyword.
### New features
Visitors created with `visit.make` now have their base as _prototype_, rather than copying properties into a fresh object.
## 3.1.0 (2016-04-18)
### Bug fixes
Properly tokenize the division operator directly after a function expression.
Allow trailing comma in destructuring arrays.
## 3.0.4 (2016-02-25)
### Fixes
Allow update expressions as left-hand-side of the ES7 exponential operator.
## 3.0.2 (2016-02-10)
### Fixes
Fix bug that accidentally made `undefined` a reserved word when parsing ES7.
## 3.0.0 (2016-02-10)
### Breaking changes
The default value of the `ecmaVersion` option is now 6 (used to be 5).
Support for comprehension syntax (which was dropped from the draft spec) has been removed.
### Fixes
`let` and `yield` are now “contextual keywords”, meaning you can mostly use them as identifiers in ES5 non-strict code.
A parenthesized class or function expression after `export default` is now parsed correctly.
### New features
When `ecmaVersion` is set to 7, Acorn will parse the exponentiation operator (`**`).
The identifier character ranges are now based on Unicode 8.0.0.
Plugins can now override the `raiseRecoverable` method to override the way non-critical errors are handled.
## 2.7.0 (2016-01-04)
### Fixes
Stop allowing rest parameters in setters.
Disallow `y` rexexp flag in ES5.
Disallow `\00` and `\000` escapes in strict mode.
Raise an error when an import name is a reserved word.
## 2.6.2 (2015-11-10)
### Fixes
Don't crash when no options object is passed.
## 2.6.0 (2015-11-09)
### Fixes
Add `await` as a reserved word in module sources.
Disallow `yield` in a parameter default value for a generator.
Forbid using a comma after a rest pattern in an array destructuring.
### New features
Support parsing stdin in command-line tool.
## 2.5.0 (2015-10-27)
### Fixes
Fix tokenizer support in the command-line tool.
Stop allowing `new.target` outside of functions.
Remove legacy `guard` and `guardedHandler` properties from try nodes.
Stop allowing multiple `__proto__` properties on an object literal in strict mode.
Don't allow rest parameters to be non-identifier patterns.
Check for duplicate paramter names in arrow functions.

19
tools/node_modules/acorn/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,19 @@
Copyright (C) 2012-2018 by various contributors (see AUTHORS)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

274
tools/node_modules/acorn/README.md сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,274 @@
# Acorn
A tiny, fast JavaScript parser written in JavaScript.
## Community
Acorn is open source software released under an
[MIT license](https://github.com/acornjs/acorn/blob/master/LICENSE).
You are welcome to
[report bugs](https://github.com/acornjs/acorn/issues) or create pull
requests on [github](https://github.com/acornjs/acorn). For questions
and discussion, please use the
[Tern discussion forum](https://discuss.ternjs.net).
## Installation
The easiest way to install acorn is from [`npm`](https://www.npmjs.com/):
```sh
npm install acorn
```
Alternately, you can download the source and build acorn yourself:
```sh
git clone https://github.com/acornjs/acorn.git
cd acorn
npm install
```
## Interface
**parse**`(input, options)` is the main interface to the library. The
`input` parameter is a string, `options` can be undefined or an object
setting some of the options listed below. The return value will be an
abstract syntax tree object as specified by the [ESTree
spec](https://github.com/estree/estree).
```javascript
let acorn = require("acorn");
console.log(acorn.parse("1 + 1"));
```
When encountering a syntax error, the parser will raise a
`SyntaxError` object with a meaningful message. The error object will
have a `pos` property that indicates the string offset at which the
error occurred, and a `loc` object that contains a `{line, column}`
object referring to that same position.
Options can be provided by passing a second argument, which should be
an object containing any of these fields:
- **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
either 3, 5, 6 (2015), 7 (2016), 8 (2017), 9 (2018) or 10 (2019, partial
support). This influences support for strict mode, the set of
reserved words, and support for new syntax features. Default is 7.
**NOTE**: Only 'stage 4' (finalized) ECMAScript features are being
implemented by Acorn. Other proposed new features can be implemented
through plugins.
- **sourceType**: Indicate the mode the code should be parsed in. Can be
either `"script"` or `"module"`. This influences global strict mode
and parsing of `import` and `export` declarations.
- **onInsertedSemicolon**: If given a callback, that callback will be
called whenever a missing semicolon is inserted by the parser. The
callback will be given the character offset of the point where the
semicolon is inserted as argument, and if `locations` is on, also a
`{line, column}` object representing this position.
- **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing
commas.
- **allowReserved**: If `false`, using a reserved word will generate
an error. Defaults to `true` for `ecmaVersion` 3, `false` for higher
versions. When given the value `"never"`, reserved words and
keywords can also not be used as property names (as in Internet
Explorer's old parser).
- **allowReturnOutsideFunction**: By default, a return statement at
the top level raises an error. Set this to `true` to accept such
code.
- **allowImportExportEverywhere**: By default, `import` and `export`
declarations can only appear at a program's top level. Setting this
option to `true` allows them anywhere where a statement is allowed.
- **allowAwaitOutsideFunction**: By default, `await` expressions can
only appear inside `async` functions. Setting this option to
`true` allows to have top-level `await` expressions. They are
still not allowed in non-`async` functions, though.
- **allowHashBang**: When this is enabled (off by default), if the
code starts with the characters `#!` (as in a shellscript), the
first line will be treated as a comment.
- **locations**: When `true`, each node has a `loc` object attached
with `start` and `end` subobjects, each of which contains the
one-based line and zero-based column numbers in `{line, column}`
form. Default is `false`.
- **onToken**: If a function is passed for this option, each found
token will be passed in same format as tokens returned from
`tokenizer().getToken()`.
If array is passed, each found token is pushed to it.
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **onComment**: If a function is passed for this option, whenever a
comment is encountered the function will be called with the
following parameters:
- `block`: `true` if the comment is a block comment, false if it
is a line comment.
- `text`: The content of the comment.
- `start`: Character offset of the start of the comment.
- `end`: Character offset of the end of the comment.
When the `locations` options is on, the `{line, column}` locations
of the comments start and end are passed as two additional
parameters.
If array is passed for this option, each found comment is pushed
to it as object in Esprima format:
```javascript
{
"type": "Line" | "Block",
"value": "comment text",
"start": Number,
"end": Number,
// If `locations` option is on:
"loc": {
"start": {line: Number, column: Number}
"end": {line: Number, column: Number}
},
// If `ranges` option is on:
"range": [Number, Number]
}
```
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **ranges**: Nodes have their start and end characters offsets
recorded in `start` and `end` properties (directly on the node,
rather than the `loc` object, which holds line/column data. To also
add a
[semi-standardized](https://bugzilla.mozilla.org/show_bug.cgi?id=745678)
`range` property holding a `[start, end]` array with the same
numbers, set the `ranges` option to `true`.
- **program**: It is possible to parse multiple files into a single
AST by passing the tree produced by parsing the first file as the
`program` option in subsequent parses. This will add the toplevel
forms of the parsed file to the "Program" (top) node of an existing
parse tree.
- **sourceFile**: When the `locations` option is `true`, you can pass
this option to add a `source` attribute in every nodes `loc`
object. Note that the contents of this option are not examined or
processed in any way; you are free to use whatever format you
choose.
- **directSourceFile**: Like `sourceFile`, but a `sourceFile` property
will be added (regardless of the `location` option) directly to the
nodes, rather than the `loc` object.
- **preserveParens**: If this option is `true`, parenthesized expressions
are represented by (non-standard) `ParenthesizedExpression` nodes
that have a single `expression` property containing the expression
inside parentheses.
**parseExpressionAt**`(input, offset, options)` will parse a single
expression in a string, and return its AST. It will not complain if
there is more of the string left after the expression.
**tokenizer**`(input, options)` returns an object with a `getToken`
method that can be called repeatedly to get the next token, a `{start,
end, type, value}` object (with added `loc` property when the
`locations` option is enabled and `range` property when the `ranges`
option is enabled). When the token's type is `tokTypes.eof`, you
should stop calling the method, since it will keep returning that same
token forever.
In ES6 environment, returned result can be used as any other
protocol-compliant iterable:
```javascript
for (let token of acorn.tokenizer(str)) {
// iterate over the tokens
}
// transform code to array of tokens:
var tokens = [...acorn.tokenizer(str)];
```
**tokTypes** holds an object mapping names to the token type objects
that end up in the `type` properties of tokens.
**getLineInfo**`(input, offset)` can be used to get a `{line,
column}` object for a given program string and offset.
### The `Parser` class
Instances of the **`Parser`** class contain all the state and logic
that drives a parse. It has static methods `parse`,
`parseExpressionAt`, and `tokenizer` that match the top-level
functions by the same name.
When extending the parser with plugins, you need to call these methods
on the extended version of the class. To extend a parser with plugins,
you can use its static `extend` method.
```javascript
var acorn = require("acorn");
var jsx = require("acorn-jsx");
var JSXParser = acorn.Parser.extend(jsx());
JSXParser.parse("foo(<bar/>)");
```
The `extend` method takes any number of plugin values, and returns a
new `Parser` class that includes the extra parser logic provided by
the plugins.
## Command line interface
The `bin/acorn` utility can be used to parse a file from the command
line. It accepts as arguments its input file and the following
options:
- `--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|--ecma10`: Sets the ECMAScript version
to parse. Default is version 9.
- `--module`: Sets the parsing mode to `"module"`. Is set to `"script"` otherwise.
- `--locations`: Attaches a "loc" object to each node with "start" and
"end" subobjects, each of which contains the one-based line and
zero-based column numbers in `{line, column}` form.
- `--allow-hash-bang`: If the code starts with the characters #! (as
in a shellscript), the first line will be treated as a comment.
- `--compact`: No whitespace is used in the AST output.
- `--silent`: Do not output the AST, just return the exit status.
- `--help`: Print the usage information and quit.
The utility spits out the syntax tree as JSON data.
## Existing plugins
- [`acorn-jsx`](https://github.com/RReverser/acorn-jsx): Parse [Facebook JSX syntax extensions](https://github.com/facebook/jsx)
Plugins for ECMAScript proposals:
- [`acorn-stage3`](https://github.com/acornjs/acorn-stage3): Parse most stage 3 proposals, bundling:
- [`acorn-async-iteration`](https://github.com/acornjs/acorn-async-iteration): Parse [async iteration proposal](https://github.com/tc39/proposal-async-iteration)
- [`acorn-bigint`](https://github.com/acornjs/acorn-bigint): Parse [BigInt proposal](https://github.com/tc39/proposal-bigint)
- [`acorn-class-fields`](https://github.com/acornjs/acorn-class-fields): Parse [class fields proposal](https://github.com/tc39/proposal-class-fields)
- [`acorn-dynamic-import`](https://github.com/kesne/acorn-dynamic-import): Parse [import() proposal](https://github.com/tc39/proposal-dynamic-import)
- [`acorn-import-meta`](https://github.com/acornjs/acorn-import-meta): Parse [import.meta proposal](https://github.com/tc39/proposal-import-meta)
- [`acorn-numeric-separator`](https://github.com/acornjs/acorn-numeric-separator): Parse [numeric separator proposal](https://github.com/tc39/proposal-numeric-separator)
- [`acorn-private-methods`](https://github.com/acornjs/acorn-private-methods): parse [private methods, getters and setters proposal](https://github.com/tc39/proposal-private-methods)n
# @LOCALMOD XXX EMSCRIPTEN
Add a quote of the erroring text on parse errors, and point to where it is, search for `XXX EMSCRIPTEN` in `acorn.js`, https://github.com/acornjs/acorn/pull/793

5341
tools/node_modules/acorn/dist/acorn.js сгенерированный поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

91
tools/node_modules/acorn/package.json сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,91 @@
{
"_args": [
[
"acorn",
"/home/alon/Dev/emscripten/tools"
]
],
"_from": "acorn@latest",
"_hasShrinkwrap": false,
"_id": "acorn@6.0.5",
"_inCache": true,
"_installable": true,
"_location": "/acorn",
"_nodeVersion": "10.11.0",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/acorn_6.0.5_1546429664768_0.2904693881325544"
},
"_npmUser": {
"email": "marijnh@gmail.com",
"name": "marijn"
},
"_npmVersion": "6.5.0",
"_phantomChildren": {},
"_requested": {
"name": "acorn",
"raw": "acorn",
"rawSpec": "",
"scope": null,
"spec": "latest",
"type": "tag"
},
"_requiredBy": [
"#USER"
],
"_resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz",
"_shasum": "81730c0815f3f3b34d8efa95cb7430965f4d887a",
"_shrinkwrap": null,
"_spec": "acorn",
"_where": "/home/alon/Dev/emscripten/tools",
"bin": {
"acorn": "./bin/acorn"
},
"bugs": {
"url": "https://github.com/acornjs/acorn/issues"
},
"dependencies": {},
"description": "ECMAScript parser",
"devDependencies": {},
"directories": {},
"dist": {
"fileCount": 11,
"integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==",
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcLKThCRA9TVsSAnZWagAAPdMQAJRvN+u8N+Brr9vEHhxL\nhhCFbe+WC8MA0OzCC7eYj9H8cP+HbmL9gXXteyl/OvKT8TL19JiHgrE2wREV\nte054iG3K7xmciPuJw9KYHTrxcQMYRBJdbFgRspikJqfcZTXI0bXSh7K7d6S\nYG4rQEt38J2Gxsz53acstm8/6P/qU2clUODmsJdUMmVJwW4UdZ6yDVOmrxfV\ncs8rQTUkamCsBaNcDfQNqnqoc9aigXuSCFNR6650XOAZMnYvEhX+vOgNBD49\ny1WNHmswvETnei2+aWENYbsjVBmKLSdKiCpmgB5S5VLhcHzB2/3dAxP+5mCA\n+fYp3PGdnilWKB/TYsgwe+odI8k2P5UTXqCVeQsHApS1Gz8xmRv7f2olh24D\n14YBwwZpZLOoC8DDyelcK1uJ9eHpv7wRD2961GSVN9Zm64tYXWm24472PFBk\nBFRwNgTG3qrKsEB4WbfY63wkHkomJtQby7EVMtRJaDVU561pYmKOXFhuNeXO\nvbKrfsxM1cOcdWpZDf4+Tl20ud6D+/YmxnwVBwsqVb+qHRZC52Sfse4McBUg\nkKS48IquB/U95vXqYhKnmB+PmV2ONR7X4z8h8GhQXQwnWvm2NMH2OIe+VN55\ne4j9F0sYYQ3zMhCbiz1BXT+2U0aAqCPbDXfn7xnEWHiLhXRVkQ4I89PisOQM\n72uj\r\n=PbvZ\r\n-----END PGP SIGNATURE-----\r\n",
"shasum": "81730c0815f3f3b34d8efa95cb7430965f4d887a",
"tarball": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz",
"unpackedSize": 1084980
},
"engines": {
"node": ">=0.4.0"
},
"homepage": "https://github.com/acornjs/acorn",
"license": "MIT",
"main": "dist/acorn.js",
"maintainers": [
{
"name": "adrianheine",
"email": "mail@adrianheine.de"
},
{
"name": "marijn",
"email": "marijnh@gmail.com"
},
{
"name": "rreverser",
"email": "me@rreverser.com"
}
],
"module": "dist/acorn.mjs",
"name": "acorn",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/acornjs/acorn.git"
},
"scripts": {
"prepare": "cd ..; npm run build:main && npm run build:bin"
},
"version": "6.0.5"
}

29
tools/node_modules/terser/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
UglifyJS is released under the BSD license:
Copyright 2012-2018 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

1281
tools/node_modules/terser/README.md сгенерированный поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

156
tools/node_modules/terser/package.json сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,156 @@
{
"_from": "terser",
"_id": "terser@3.14.1",
"_inBundle": false,
"_integrity": "sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw==",
"_location": "/terser",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "terser",
"name": "terser",
"escapedName": "terser",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/terser/-/terser-3.14.1.tgz",
"_shasum": "cc4764014af570bc79c79742358bd46926018a32",
"_spec": "terser",
"_where": "/usr/local/google/home/azakai/Dev/emscripten/tools",
"author": {
"name": "Mihai Bazon",
"email": "mihai.bazon@gmail.com",
"url": "http://lisperator.net/"
},
"bin": {
"terser": "bin/uglifyjs"
},
"bugs": {
"url": "https://github.com/fabiosantoscode/terser/issues"
},
"bundleDependencies": false,
"dependencies": {
"commander": "~2.17.1",
"source-map": "~0.6.1",
"source-map-support": "~0.5.6"
},
"deprecated": false,
"description": "JavaScript parser, mangler/compressor and beautifier toolkit for ES6+",
"devDependencies": {
"acorn": "^6.0.4",
"coveralls": "^3.0.2",
"csv": "^5.0.0",
"es6-promise": "^4.2.5",
"escodegen": "^1.9.1",
"eslint": "^4.19.1",
"eslump": "^2.0.0",
"istanbul": "^0.4.5",
"mocha": "^3.0.0",
"mochallel": "^1.8.2",
"pre-commit": "^1.2.2",
"semver": "~5.5.0"
},
"engines": {
"node": ">=4.0.0"
},
"eslintConfig": {
"rules": {
"brace-style": [
"error",
"1tbs",
{
"allowSingleLine": true
}
],
"quotes": [
"error",
"double",
"avoid-escape"
],
"no-debugger": "error",
"semi": [
"error",
"always"
],
"no-extra-semi": "error",
"no-irregular-whitespace": "error",
"space-before-blocks": [
"error",
"always"
]
}
},
"files": [
"bin",
"lib",
"dist",
"!dist/bundle.instrumented.js",
"tools",
"LICENSE"
],
"homepage": "https://github.com/fabiosantoscode/terser",
"keywords": [
"uglify",
"terser",
"uglify-es",
"uglify-js",
"minify",
"minifier",
"javascript",
"ecmascript",
"es5",
"es6",
"es7",
"es8",
"es2015",
"es2016",
"es2017",
"async",
"await"
],
"license": "BSD-2-Clause",
"main": "terser.js",
"zmain": "dist/bundle.js",
"maintainers": [
{
"name": "Fábio Santos",
"email": "fabiosantosart@gmail.com"
},
{
"name": "Alex Lam",
"email": "alexlamsl@gmail.com"
},
{
"name": "Mihai Bazon",
"email": "mihai.bazon@gmail.com",
"url": "http://lisperator.net/"
}
],
"name": "terser",
"pre-commit": [
"lint-fix",
"test"
],
"repository": {
"type": "git",
"url": "git+https://github.com/fabiosantoscode/terser.git"
},
"scripts": {
"coverage": "istanbul cover test/run-tests.js",
"coveralls": "coveralls < coverage/lcov.info",
"lint": "eslint lib",
"lint-fix": "eslint --fix lib",
"prepare": "cd dist && TERSER_NO_BUNDLE=1 ../bin/uglifyjs ../tools/domprops.js ../lib/utils.js ../lib/ast.js ../lib/parse.js ../lib/transform.js ../lib/scope.js ../lib/output.js ../lib/compress.js ../lib/sourcemap.js ../lib/mozilla-ast.js ../lib/propmangle.js ../lib/minify.js ../tools/exports.js -mc -d \"MOZ_SourceMap=require('source-map')\" --source-map \"includeSources=true,url='bundle.js.map'\" -e \"exports:(typeof module != 'undefined' ? module.exports : Terser = {})\" -b beautify=false,ascii_only --comments /license/ -o ../dist/bundle.js",
"test": "rm -f dist/* && npm run prepare && istanbul instrument dist/bundle.js > dist/bundle.instrumented.js && node test/run-tests.js"
},
"types": "tools/terser.d.ts",
"version": "3.14.1"
}

8352
tools/node_modules/terser/terser.js сгенерированный поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -2346,8 +2346,11 @@ class Building(object):
# run JS optimizer on some JS, ignoring asm.js contents if any - just run on it all
@staticmethod
def js_optimizer_no_asmjs(filename, passes, return_output=False, extra_info=None):
from . import js_optimizer
def js_optimizer_no_asmjs(filename, passes, return_output=False, extra_info=None, acorn=False):
if not acorn:
optimizer = path_from_root('tools', 'js-optimizer.js')
else:
optimizer = path_from_root('tools', 'acorn-optimizer.js')
original_filename = filename
if extra_info is not None:
temp_files = configuration.get_temp_files()
@ -2356,13 +2359,18 @@ class Building(object):
with open(temp, 'a') as f:
f.write('// EXTRA_INFO: ' + extra_info)
filename = temp
cmd = NODE_JS + [optimizer, filename] + passes
if not return_output:
next = original_filename + '.jso.js'
configuration.get_temp_files().note(next)
check_call(NODE_JS + [js_optimizer.JS_OPTIMIZER, filename] + passes, stdout=open(next, 'w'))
run_process(cmd, stdout=open(next, 'w'))
return next
else:
return run_process(NODE_JS + [js_optimizer.JS_OPTIMIZER, filename] + passes, stdout=PIPE).stdout
return run_process(cmd, stdout=PIPE).stdout
@staticmethod
def acorn_optimizer(filename, passes, extra_info=None, return_output=False):
return Building.js_optimizer_no_asmjs(filename, passes, extra_info=extra_info, return_output=return_output, acorn=True)
# evals ctors. if binaryen_bin is provided, it is the dir of the binaryen tool for this, and we are in wasm mode
@staticmethod
@ -2531,7 +2539,7 @@ class Building(object):
passes.append('minifyWhitespace')
if passes:
logger.debug('running cleanup on shell code: ' + ' '.join(passes))
js_file = Building.js_optimizer_no_asmjs(js_file, ['noPrintMetadata'] + passes)
js_file = Building.acorn_optimizer(js_file, passes)
# if we can optimize this js+wasm combination under the assumption no one else
# will see the internals, do so
if not Settings.LINKABLE:
@ -2541,11 +2549,11 @@ class Building(object):
js_file = Building.metadce(js_file, wasm_file, minify_whitespace=minify_whitespace, debug_info=debug_info)
# now that we removed unneeded communication between js and wasm, we can clean up
# the js some more.
passes = ['noPrintMetadata', 'AJSDCE']
passes = ['AJSDCE']
if minify_whitespace:
passes.append('minifyWhitespace')
logger.debug('running post-meta-DCE cleanup on shell code: ' + ' '.join(passes))
js_file = Building.js_optimizer_no_asmjs(js_file, passes)
js_file = Building.acorn_optimizer(js_file, passes)
# also minify the names used between js and wasm, if we emitting JS (then the JS knows how to load the minified names)
# If we are building with DECLARE_ASM_MODULE_EXPORTS=0, we must *not* minify the exports from the wasm module, since in DECLARE_ASM_MODULE_EXPORTS=0 mode, the code that
# reads out the exports is compacted by design that it does not have a chance to unminify the functions. If we are building with DECLARE_ASM_MODULE_EXPORTS=1, we might
@ -2565,7 +2573,7 @@ class Building(object):
temp_files = configuration.get_temp_files()
# first, get the JS part of the graph
extra_info = '{ "exports": [' + ','.join(map(lambda x: '["' + x + '","' + x + '"]', Settings.MODULE_EXPORTS)) + ']}'
txt = Building.js_optimizer_no_asmjs(js_file, ['emitDCEGraph', 'noEmitAst'], return_output=True, extra_info=extra_info)
txt = Building.acorn_optimizer(js_file, ['emitDCEGraph', 'noPrint'], return_output=True, extra_info=extra_info)
graph = json.loads(txt)
# add exports based on the backend output, that are not present in the JS
if not Settings.DECLARE_ASM_MODULE_EXPORTS:
@ -2623,7 +2631,7 @@ class Building(object):
if minify_whitespace:
passes.append('minifyWhitespace')
extra_info = {'unused': unused}
return Building.js_optimizer_no_asmjs(js_file, passes, extra_info=json.dumps(extra_info))
return Building.acorn_optimizer(js_file, passes, extra_info=json.dumps(extra_info))
@staticmethod
def minify_wasm_imports_and_exports(js_file, wasm_file, minify_whitespace, minify_exports, debug_info):
@ -2646,7 +2654,7 @@ class Building(object):
if minify_whitespace:
passes.append('minifyWhitespace')
extra_info = {'mapping': mapping}
return Building.js_optimizer_no_asmjs(js_file, passes, extra_info=json.dumps(extra_info))
return Building.acorn_optimizer(js_file, passes, extra_info=json.dumps(extra_info))
# the exports the user requested
user_requested_exports = []