From cb62bd98d3397d9eb3d738cc0c7f53886d3a213b Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 28 Dec 2017 02:53:14 +0800 Subject: fix function inlining within loops (#2675) fixes #2663 --- lib/compress.js | 71 ++++++++++++--------- test/compress/functions.js | 150 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 30 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index bd61b87f..ac5cd235 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3982,23 +3982,30 @@ merge(Compressor.prototype, { return self; function can_flatten_args(fn) { - var catches = Object.create(null); + var catches = Object.create(null), defs; do { scope = compressor.parent(++level); - if (scope instanceof AST_SymbolRef) { - if (scope.fixed_value() instanceof AST_Scope) return false; - } else if (scope instanceof AST_Catch) { + if (scope instanceof AST_Catch) { catches[scope.argname.name] = true; + } else if (scope instanceof AST_IterationStatement) { + defs = []; + } else if (scope instanceof AST_SymbolRef) { + if (scope.fixed_value() instanceof AST_Scope) return false; } } while (!(scope instanceof AST_Scope)); var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel); - return all(fn.argnames, function(arg) { - return arg.__unused - || safe_to_inject - && !catches[arg.name] - && !identifier_atom(arg.name) - && !scope.var_names()[arg.name]; - }); + for (var i = 0, len = fn.argnames.length; i < len; i++) { + var arg = fn.argnames[i]; + if (arg.__unused) continue; + if (!safe_to_inject + || catches[arg.name] + || identifier_atom(arg.name) + || scope.var_names()[arg.name]) { + return false; + } + if (defs) defs.push(arg.definition()); + } + return !defs || defs.length == 0 || !is_reachable(fn.body[0], defs); } function flatten_args(fn) { @@ -4838,6 +4845,28 @@ merge(Compressor.prototype, { return self; }); + function is_reachable(node, defs) { + var reachable = false; + var find_ref = new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_SymbolRef && member(node.definition(), defs)) { + return reachable = true; + } + }); + var scan_scope = new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_Scope) { + var parent = scan_scope.parent(); + if (!(parent instanceof AST_Call && parent.expression === node)) { + node.walk(find_ref); + } + return true; + } + }); + node.walk(scan_scope); + return reachable; + } + var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ]; var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ]; OPT(AST_Assign, function(self, compressor){ @@ -4851,7 +4880,7 @@ merge(Compressor.prototype, { parent = compressor.parent(level++); if (parent instanceof AST_Exit) { if (in_try(level, parent instanceof AST_Throw)) break; - if (is_reachable(def)) break; + if (is_reachable(self, [ def ])) break; if (self.operator == "=") return self.right; return make_node(AST_Binary, self, { operator: self.operator.slice(0, -1), @@ -4893,24 +4922,6 @@ merge(Compressor.prototype, { } } } - - function is_reachable(def) { - var reachable = false; - var find_ref = new TreeWalker(function(node) { - if (reachable) return true; - if (node instanceof AST_SymbolRef && node.definition() === def) { - return reachable = true; - } - }); - self.right.walk(new TreeWalker(function(node) { - if (reachable) return true; - if (node instanceof AST_Scope) { - node.walk(find_ref); - return true; - } - })); - return reachable; - } }); OPT(AST_Conditional, function(self, compressor){ diff --git a/test/compress/functions.js b/test/compress/functions.js index 83a27a06..888c6e3c 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -1496,3 +1496,153 @@ issue_2657: { } expect_stdout: "42" } + +issue_2663_1: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var i, o = {}; + function createFn(j) { + return function() { + console.log(j); + }; + } + for (i in { a: 1, b: 2, c: 3 }) + o[i] = createFn(i); + for (i in o) + o[i](); + })(); + } + expect: { + (function() { + var i, o = {}; + function createFn(j) { + return function() { + console.log(j); + }; + } + for (i in { a: 1, b: 2, c: 3 }) + o[i] = createFn(i); + for (i in o) + o[i](); + })(); + } + expect_stdout: [ + "a", + "b", + "c", + ] +} + +issue_2663_2: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var i; + function fn(j) { + return function() { + console.log(j); + }(); + } + for (i in { a: 1, b: 2, c: 3 }) + fn(i); + })(); + } + expect: { + (function() { + var i; + for (i in { a: 1, b: 2, c: 3 }) + j = i, console.log(j); + var j; + })(); + } + expect_stdout: [ + "a", + "b", + "c", + ] +} + +issue_2663_3: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function () { + var outputs = [ + { type: 0, target: null, eventName: "ngSubmit", propName: null }, + { type: 0, target: null, eventName: "submit", propName: null }, + { type: 0, target: null, eventName: "reset", propName: null }, + ]; + function listenToElementOutputs(outputs) { + var handlers = []; + for (var i = 0; i < outputs.length; i++) { + var output = outputs[i]; + var handleEventClosure = renderEventHandlerClosure(output.eventName); + handlers.push(handleEventClosure) + } + var target, name; + return handlers; + } + function renderEventHandlerClosure(eventName) { + return function () { + return console.log(eventName); + }; + } + listenToElementOutputs(outputs).forEach(function (handler) { + return handler() + }); + })(); + } + expect: { + (function() { + function renderEventHandlerClosure(eventName) { + return function() { + return console.log(eventName); + }; + } + (function(outputs) { + var handlers = []; + for (var i = 0; i < outputs.length; i++) { + var output = outputs[i]; + var handleEventClosure = renderEventHandlerClosure(output.eventName); + handlers.push(handleEventClosure); + } + return handlers; + })([ { + type: 0, + target: null, + eventName: "ngSubmit", + propName: null + }, { + type: 0, + target: null, + eventName: "submit", + propName: null + }, { + type: 0, + target: null, + eventName: "reset", + propName: null + } ]).forEach(function(handler) { + return handler(); + }); + })(); + } + expect_stdout: [ + "ngSubmit", + "submit", + "reset", + ] +} -- cgit v1.2.3 cce04a48aa1350229442a9f'>ssh: Speed up RPCs by using #:nodelay....Partly fixes <https://bugs.gnu.org/41702>. * guix/ssh.scm (open-ssh-session): Enable #:nodelay. * m4/guix.m4 (GUIX_CHECK_GUILE_SSH): Add feature check for this new parameter. * doc/guix.texi (Requirements): Adjust. Co-authored-by: Ludovic Courtès <ludo@gnu.org> Lars-Dominik Braun 2020-05-28doc: Remove explicit support for mips64el-linux....It's been a good run, but no one is maintaining the architecture. So long, and thanks for all the fish. * doc/guix.texi (GNU Distribution): Change text for mips64el-linux to denote it is deprecated. (Daemon Offload Setup): Change occurrences of mips64el-linux to aarch64-linux and adjust local code snippets. (Guix Environment)[cross-compilation]: Change mips64el-linux-gnu to aarch64-linux-gnu. (GNU Build System)(package-cross-derivation]: Same. (G-Expressions)[cross compilation]: Same. (Additional Build Options)[cross-compilation, build logs]: Same. (qemu-binfmt-service-type): Remove mips64el. * doc/contributing.texi (Submitting Patches): Same. * m4/guix.m4: (GUIX_ASSERT_SUPPORTED_SYSTEM): Remove mips64el-linux. Efraim Flashner 2020-05-28maint: Check whether Guile-Gcrypt is recent enough....Suggested by Danny Milosavljevic <dannym@scratchpost.org> in <https://bugs.gnu.org/41494>. * m4/guix.m4 (GUIX_CHECK_GUILE_GCRYPT): New macro. * configure.ac: Use it. Ludovic Courtès 2020-02-22build: Depend on guile-ssh 0.12.0...This is a followup to 35f35111678e6622301b414f3d464acb71e106bb. * m4/guix.m4 (GUIX_CHECK_GUILE_SSH): Check for userauth-gssapi! * doc/guix.texi: Document version requirement Signed-off-by: Ludovic Courtès <ludo@gnu.org> Lars-Dominik Braun 2019-08-17build: 'GUIX_CHECK_GUILE_JSON' really checks for Guile-JSON 3.x....Until now the 'guile' process would always exit with 0, as long as Guile-JSON is installed, whether it's version 1 or version 3. * m4/guix.m4 (GUIX_CHECK_GUILE_JSON): Fix array syntax and remove catch-all 'match' clause. Ludovic Courtès 2019-07-25maint: Switch to Guile-JSON 3.x....Guile-JSON 3.x is incompatible with Guile-JSON 1.x, which we relied on until now: it maps JSON dictionaries to alists (instead of hash tables), and JSON arrays to vectors (instead of lists). This commit is about adjusting all the existing code to this new mapping. * m4/guix.m4 (GUIX_CHECK_GUILE_JSON): New macro. * configure.ac: Use it. * doc/guix.texi (Requirements): Mention the Guile-JSON version. * guix/git-download.scm (git-fetch)[guile-json]: Use GUILE-JSON-3. * guix/import/cpan.scm (string->license): Expect vectors instead of lists. (module->dist-name): Use 'json-fetch' instead of 'json-fetch-alist'. (cpan-fetch): Likewise. * guix/import/crate.scm (crate-fetch): Likewise, and call 'vector->list' for DEPS. * guix/import/gem.scm (rubygems-fetch): Likewise. * guix/import/json.scm (json-fetch-alist): Remove. * guix/import/pypi.scm (pypi-fetch): Use 'json-fetch' instead of 'json-fetch-alist'. (latest-source-release, latest-wheel-release): Call 'vector->list' on RELEASES. * guix/import/stackage.scm (stackage-lts-info-fetch): Use 'json-fetch' instead of 'json-fetch-alist'. (lts-package-version): Use 'vector->list'. * guix/import/utils.scm (hash-table->alist): Remove. (alist->package): Pass 'vector->list' on the inputs fields, and default to the empty vector. * guix/scripts/import/json.scm (guix-import-json): Remove call to 'hash-table->alist'. * guix/swh.scm (define-json-reader): Expect pair? or null? instead of hash-table?. [extract-field]: Use 'assoc-ref' instead of 'hash-ref'. (json->branches): Use 'map' instead of 'hash-map->list'. (json->checksums): Likewise. (json->directory-entries, origin-visits): Call 'vector->list' on the result of 'json->scm'. * tests/import-utils.scm ("alist->package with dependencies"): New test. * gnu/installer.scm (build-compiled-file)[builder]: Use GUILE-JSON-3. * gnu/installer.scm (installer-program)[installer-builder]: Likewise. * gnu/installer/locale.scm (iso639->iso639-languages): Use 'assoc-ref' instead of 'hash-ref', and pass vectors through 'vector->list'. (iso3166->iso3166-territories): Likewise. * gnu/system/vm.scm (system-docker-image)[build]: Use GUILE-JSON-3. * guix/docker.scm (manifest, config): Adjust for Guile-JSON 3. * guix/scripts/pack.scm (docker-image)[build]: Use GUILE-JSON-3. * guix/import/github.scm (fetch-releases-or-tags): Update docstring. (latest-released-version): Use 'assoc-ref' instead of 'hash-ref'. Pass the result of 'fetch-releases-or-tags' to 'vector->list'. * guix/import/launchpad.scm (latest-released-version): Likewise. Ludovic Courtès 2019-05-06Add (guix lzlib)....* guix/lzlib.scm, tests/lzlib.scm: New files. * Makefile.am (MODULES): Add guix/lzlib.scm. (SCM_TESTS): Add tests/lzlib.scm. * m4/guix.m4 (GUIX_LIBLZ_LIBDIR): New macro. * configure.ac (LIBLZ_LIBDIR): Use it. Define and substitute 'LIBLZ'. * guix/config.scm.in (%liblz): New variable. * guix/self.scm (make-config.scm): Add TODO comment. Co-authored-by: Ludovic Courtès <ludo@gnu.org> Pierre Neidhardt 2018-11-23Update Guile-SQLite3 URL everywhere....* README: Update Guile-SQLite3 URL. * doc/guix.texi (Requirements): Likewise. * guix/store/database.scm (sqlite-exec): Likewise. * m4/guix.m4 (GUIX_CHECK_GUILE_SQLITE3): Likewise. Ludovic Courtès 2018-09-04Switch to Guile-Gcrypt....This removes (guix hash) and (guix pk-crypto), which now live as part of Guile-Gcrypt (version 0.1.0.) * guix/gcrypt.scm, guix/hash.scm, guix/pk-crypto.scm, tests/hash.scm, tests/pk-crypto.scm: Remove. * configure.ac: Test for Guile-Gcrypt. Remove LIBGCRYPT and LIBGCRYPT_LIBDIR assignments. * m4/guix.m4 (GUIX_ASSERT_LIBGCRYPT_USABLE): Remove. * README: Add Guile-Gcrypt to the dependencies; move libgcrypt as "required unless --disable-daemon". * doc/guix.texi (Requirements): Likewise. * gnu/packages/bash.scm, guix/derivations.scm, guix/docker.scm, guix/git.scm, guix/http-client.scm, guix/import/cpan.scm, guix/import/cran.scm, guix/import/crate.scm, guix/import/elpa.scm, guix/import/gnu.scm, guix/import/hackage.scm, guix/import/texlive.scm, guix/import/utils.scm, guix/nar.scm, guix/pki.scm, guix/scripts/archive.scm, guix/scripts/authenticate.scm, guix/scripts/download.scm, guix/scripts/hash.scm, guix/scripts/pack.scm, guix/scripts/publish.scm, guix/scripts/refresh.scm, guix/scripts/substitute.scm, guix/store.scm, guix/store/deduplication.scm, guix/tests.scm, tests/base32.scm, tests/builders.scm, tests/challenge.scm, tests/cpan.scm, tests/crate.scm, tests/derivations.scm, tests/gem.scm, tests/nar.scm, tests/opam.scm, tests/pki.scm, tests/publish.scm, tests/pypi.scm, tests/store-deduplication.scm, tests/store.scm, tests/substitute.scm: Adjust imports. * gnu/system/vm.scm: Likewise. (guile-sqlite3&co): Rename to... (gcrypt-sqlite3&co): ... this. Add GUILE-GCRYPT. (expression->derivation-in-linux-vm)[config]: Remove. (iso9660-image)[config]: Remove. (qemu-image)[config]: Remove. (system-docker-image)[config]: Remove. * guix/scripts/pack.scm: Adjust imports. (guile-sqlite3&co): Rename to... (gcrypt-sqlite3&co): ... this. Add GUILE-GCRYPT. (self-contained-tarball)[build]: Call 'make-config.scm' without #:libgcrypt argument. (squashfs-image)[libgcrypt]: Remove. [build]: Call 'make-config.scm' without #:libgcrypt. (docker-image)[config, json]: Remove. [build]: Add GUILE-GCRYPT to the extensions Remove (guix config) from the imported modules. * guix/self.scm (specification->package): Remove "libgcrypt", add "guile-gcrypt". (compiled-guix): Remove #:libgcrypt. [guile-gcrypt]: New variable. [dependencies]: Add it. [*core-modules*]: Remove #:libgcrypt from 'make-config.scm' call. Add #:extensions. [*config*]: Remove #:libgcrypt from 'make-config.scm' call. (%dependency-variables): Remove %libgcrypt. (make-config.scm): Remove #:libgcrypt. * build-aux/build-self.scm (guile-gcrypt): New variable. (make-config.scm): Remove #:libgcrypt. (build-program)[fake-gcrypt-hash]: New variable. Add (gcrypt hash) to the imported modules. Adjust load path assignments. * gnu/packages/package-management.scm (guix)[propagated-inputs]: Add GUILE-GCRYPT. [arguments]: In 'wrap-program' phase, add GUILE-GCRYPT to the search path. Ludovic Courtès 2018-06-01build: Check for Guile-SQLite3....* m4/guix.m4 (GUIX_CHECK_GUILE_SQLITE3): New macro. * configure.ac: Use it and define 'HAVE_GUILE_SQLITE3'. * guix/self.scm (specification->package): Add "guile-sqlite3". (compiled-guix)[guile-sqlite3]: New variable. [dependencies]: Add it. Ludovic Courtès 2018-02-26build: Require Guile >= 2.0.13....* README, configure.ac, doc/guix.texi (Requirements): Increase minimum Guile version from 2.0.9 to 2.0.13. * config-daemon.ac: Remove use of 'GUIX_CHECK_UNBUFFERED_CBIP'. * m4/guix.m4 (GUIX_CHECK_UNBUFFERED_CBIP): Remove. * guix/build/download.scm (current-http-proxy): Remove. * guix/build/syscalls.scm (%libc-errno-pointer, errno): Remove. (syscall->procedure): Use #:return-errno unconditionally. * guix/hash.scm (open-sha256-input-port)[unbuffered]: Remove outdated comment. * guix/http-client.scm (when-guile<=2.0.5-or-otherwise-broken): Remove. <top level>: Remove 'when-guile<=2.0.5-or-otherwise-broken' block. * guix/scripts/substitute.scm (fetch): Remove 'guile-version>?' conditional. * tests/hash.scm (supports-unbuffered-cbip?): Remove. <top level>: Remove 'test-skip' call. Ludovic Courtès 2018-01-06build: Detect broken 'equal?' in Guile 2.2.1....Fixes <https://bugs.gnu.org/29903>. Reported by Mathieu Lirzin <mthl@gnu.org>. * m4/guix.m4 (GUIX_ASSERT_SYNTAX_OBJECT_EQUAL): New macro. * configure.ac: Use it. Ludovic Courtès