Skip to content

Commit e7b9b4a

Browse files
authored
workaround function declaration quirks in ES6+ (#5833)
fixes #1666
1 parent 3dfb379 commit e7b9b4a

File tree

4 files changed

+317
-2
lines changed

4 files changed

+317
-2
lines changed

lib/compress.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,11 @@ Compressor.prototype.compress = function(node) {
13751375
var d = this.definition();
13761376
if (!d.first_decl && d.references.length == 0) d.first_decl = this;
13771377
});
1378+
def(AST_SymbolDefun, function() {
1379+
var d = this.definition();
1380+
if (!d.first_decl && d.references.length == 0) d.first_decl = this;
1381+
if (d.orig.length > 1 && d.scope.resolve() !== this.scope) d.fixed = false;
1382+
});
13781383
def(AST_SymbolImport, function() {
13791384
var d = this.definition();
13801385
d.first_decl = this;
@@ -1893,6 +1898,7 @@ Compressor.prototype.compress = function(node) {
18931898
if (stat instanceof AST_LambdaDefinition) {
18941899
var def = stat.name.definition();
18951900
var scope = stat.name.scope;
1901+
if (def.orig.length > 1 && def.scope.resolve() !== scope) return false;
18961902
return def.scope === scope || all(def.references, function(ref) {
18971903
var s = ref.scope;
18981904
do {
@@ -2405,6 +2411,7 @@ Compressor.prototype.compress = function(node) {
24052411
}
24062412
var read_toplevel = false;
24072413
var modify_toplevel = false;
2414+
var defun_scopes = get_defun_scopes(lhs);
24082415
// Locate symbols which may execute code outside of scanning range
24092416
var enclosed = new Dictionary();
24102417
var well_defined = true;
@@ -2535,8 +2542,11 @@ Compressor.prototype.compress = function(node) {
25352542
if (parent instanceof AST_For) {
25362543
if (node !== parent.init) return true;
25372544
}
2538-
if (node instanceof AST_Assign) {
2539-
return node.operator != "=" && lhs.equals(node.left);
2545+
if (node instanceof AST_Assign) return node.operator != "=" && lhs.equals(node.left);
2546+
if (node instanceof AST_BlockStatement) {
2547+
return defun_scopes && !all(defun_scopes, function(scope) {
2548+
return node !== scope;
2549+
});
25402550
}
25412551
if (node instanceof AST_Call) {
25422552
if (!(lhs instanceof AST_PropAccess)) return false;
@@ -3437,6 +3447,33 @@ Compressor.prototype.compress = function(node) {
34373447
}
34383448
}
34393449

3450+
function get_defun_scopes(lhs) {
3451+
if (!(lhs instanceof AST_SymbolDeclaration
3452+
|| lhs instanceof AST_SymbolRef
3453+
|| lhs instanceof AST_Destructured)) return;
3454+
var scopes = [];
3455+
lhs.mark_symbol(function(node) {
3456+
if (node instanceof AST_Symbol) {
3457+
var def = node.definition();
3458+
var scope = def.scope.resolve();
3459+
var found = false;
3460+
var avoid = def.orig.reduce(function(scopes, sym) {
3461+
if (sym instanceof AST_SymbolDefun) {
3462+
if (sym.scope !== scope) push_uniq(scopes, sym.scope);
3463+
} else {
3464+
found = true;
3465+
}
3466+
return scopes;
3467+
}, []);
3468+
if (found) avoid.forEach(function(scope) {
3469+
push_uniq(scopes, scope);
3470+
});
3471+
}
3472+
});
3473+
if (scopes.length == 0) return;
3474+
return scopes;
3475+
}
3476+
34403477
function is_lhs_local(lhs) {
34413478
var sym = root_expr(lhs);
34423479
if (!(sym instanceof AST_SymbolRef)) return false;

test/compress/blocks.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,45 @@ keep_some_blocks: {
4747
} else stuff();
4848
}
4949
}
50+
51+
issue_1666: {
52+
input: {
53+
var a = 42;
54+
{
55+
function a() {}
56+
a();
57+
}
58+
console.log("PASS");
59+
}
60+
expect: {
61+
var a = 42;
62+
{
63+
function a() {}
64+
a();
65+
}
66+
console.log("PASS");
67+
}
68+
expect_stdout: true
69+
}
70+
71+
issue_1666_strict: {
72+
input: {
73+
"use strict";
74+
var a = 42;
75+
{
76+
function a() {}
77+
a();
78+
}
79+
console.log("PASS");
80+
}
81+
expect: {
82+
"use strict";
83+
var a = 42;
84+
{
85+
function a() {}
86+
a();
87+
}
88+
console.log("PASS");
89+
}
90+
expect_stdout: true
91+
}

test/compress/collapse_vars.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10172,3 +10172,115 @@ issue_5779: {
1017210172
}
1017310173
expect_stdout: "PASS"
1017410174
}
10175+
10176+
issue_1666: {
10177+
options = {
10178+
collapse_vars: true,
10179+
}
10180+
input: {
10181+
var x = 42;
10182+
{
10183+
x();
10184+
function x() {
10185+
console.log("foo");
10186+
}
10187+
}
10188+
console.log(typeof x);
10189+
}
10190+
expect: {
10191+
var x = 42;
10192+
{
10193+
x();
10194+
function x() {
10195+
console.log("foo");
10196+
}
10197+
}
10198+
console.log(typeof x);
10199+
}
10200+
expect_stdout: true
10201+
}
10202+
10203+
issue_1666_strict: {
10204+
options = {
10205+
collapse_vars: true,
10206+
}
10207+
input: {
10208+
"use strict";
10209+
var x = 42;
10210+
{
10211+
x();
10212+
function x() {
10213+
console.log("foo");
10214+
}
10215+
}
10216+
console.log(typeof x);
10217+
}
10218+
expect: {
10219+
"use strict";
10220+
var x = 42;
10221+
{
10222+
x();
10223+
function x() {
10224+
console.log("foo");
10225+
}
10226+
}
10227+
console.log(typeof x);
10228+
}
10229+
expect_stdout: true
10230+
}
10231+
10232+
issue_1666_undefined: {
10233+
options = {
10234+
collapse_vars: true,
10235+
}
10236+
input: {
10237+
var undefined = 42;
10238+
{
10239+
undefined();
10240+
function undefined() {
10241+
console.log("foo");
10242+
}
10243+
}
10244+
console.log(typeof undefined);
10245+
}
10246+
expect: {
10247+
var undefined = 42;
10248+
{
10249+
undefined();
10250+
function undefined() {
10251+
console.log("foo");
10252+
}
10253+
}
10254+
console.log(typeof undefined);
10255+
}
10256+
expect_stdout: true
10257+
}
10258+
10259+
issue_1666_undefined_strict: {
10260+
options = {
10261+
collapse_vars: true,
10262+
}
10263+
input: {
10264+
"use strict";
10265+
var undefined = 42;
10266+
{
10267+
undefined();
10268+
function undefined() {
10269+
console.log("foo");
10270+
}
10271+
}
10272+
console.log(typeof undefined);
10273+
}
10274+
expect: {
10275+
"use strict";
10276+
var undefined = 42;
10277+
{
10278+
undefined();
10279+
function undefined() {
10280+
console.log("foo");
10281+
}
10282+
}
10283+
console.log(typeof undefined);
10284+
}
10285+
expect_stdout: true
10286+
}

test/compress/reduce_vars.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8222,3 +8222,127 @@ issue_5777_2: {
82228222
}
82238223
expect_stdout: "PASS"
82248224
}
8225+
8226+
issue_1666: {
8227+
options = {
8228+
evaluate: true,
8229+
reduce_vars: true,
8230+
toplevel: true,
8231+
unused: true,
8232+
}
8233+
input: {
8234+
var x = 42;
8235+
{
8236+
x();
8237+
function x() {
8238+
console.log("foo");
8239+
}
8240+
}
8241+
console.log(typeof x);
8242+
}
8243+
expect: {
8244+
var x = 42;
8245+
{
8246+
x();
8247+
function x() {
8248+
console.log("foo");
8249+
}
8250+
}
8251+
console.log(typeof x);
8252+
}
8253+
expect_stdout: true
8254+
}
8255+
8256+
issue_1666_strict: {
8257+
options = {
8258+
evaluate: true,
8259+
reduce_vars: true,
8260+
toplevel: true,
8261+
unused: true,
8262+
}
8263+
input: {
8264+
"use strict";
8265+
var x = 42;
8266+
{
8267+
x();
8268+
function x() {
8269+
console.log("foo");
8270+
}
8271+
}
8272+
console.log(typeof x);
8273+
}
8274+
expect: {
8275+
"use strict";
8276+
var x = 42;
8277+
{
8278+
x();
8279+
function x() {
8280+
console.log("foo");
8281+
}
8282+
}
8283+
console.log(typeof x);
8284+
}
8285+
expect_stdout: true
8286+
}
8287+
8288+
issue_1666_undefined: {
8289+
options = {
8290+
evaluate: true,
8291+
reduce_vars: true,
8292+
toplevel: true,
8293+
unused: true,
8294+
}
8295+
input: {
8296+
var undefined = 42;
8297+
{
8298+
undefined();
8299+
function undefined() {
8300+
console.log("foo");
8301+
}
8302+
}
8303+
console.log(typeof undefined);
8304+
}
8305+
expect: {
8306+
var undefined = 42;
8307+
{
8308+
undefined();
8309+
function undefined() {
8310+
console.log("foo");
8311+
}
8312+
}
8313+
console.log(typeof undefined);
8314+
}
8315+
expect_stdout: true
8316+
}
8317+
8318+
issue_1666_undefined_strict: {
8319+
options = {
8320+
evaluate: true,
8321+
reduce_vars: true,
8322+
toplevel: true,
8323+
unused: true,
8324+
}
8325+
input: {
8326+
"use strict";
8327+
var undefined = 42;
8328+
{
8329+
undefined();
8330+
function undefined() {
8331+
console.log("foo");
8332+
}
8333+
}
8334+
console.log(typeof undefined);
8335+
}
8336+
expect: {
8337+
"use strict";
8338+
var undefined = 42;
8339+
{
8340+
undefined();
8341+
function undefined() {
8342+
console.log("foo");
8343+
}
8344+
}
8345+
console.log(typeof undefined);
8346+
}
8347+
expect_stdout: true
8348+
}

0 commit comments

Comments
 (0)