Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extend test/ufuzz.js to inline & reduce_funcs #2620

Merged
merged 1 commit into from Dec 20, 2017

Conversation

alexlamsl
Copy link
Collaborator

#2533 (comment)

@kzc I've also adjusted test/ufuzz.json, the highlight being unsafe as rename should now circumvent any corner cases regarding Infinity, NaN or undefined.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

This PR will not forward call fN() functions. It will only call variables.

@alexlamsl
Copy link
Collaborator Author

Oops - I forgot about those 😅

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

Okay, the patch looks familiar now. ;-)

Let's see what interesting things happen with it.

@alexlamsl
Copy link
Collaborator Author

I've backed out the unsafe bits, and also record forward calls that have been generated so that when createFunction() comes around, it can choose to omit AST_Call, making the resulting function single-use.

@alexlamsl
Copy link
Collaborator Author

// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

if (a++ + [ (c = c + 1) + {}.length, b-- ]) {
    var brake2 = 5;
    do {
        {
            var brake3 = 5;
            while ((~a ? (c = c + 1) + (a && (a[typeof f0 == "function" && --_calls_ >= 0 && f0((c = 1 + c,
            (-4 - Infinity) * (-5 / -4) != (a && (a[(c = c + 1) + ((c = 1 + c, (-5 - 24..toString()) / delete 23..toString() <= (3 || -2) + (NaN !== 22)) ? (c = 1 + c,
            a && (a[1 === 1 ? a : b] = NaN >>> false >= (24..toString() || 38..toString()) == ([ , 0 ].length === 2 & 24..toString()) < (a && (a[(c = 1 + c,
            (c = c + 1, 25 + "function") >>> (-4 && {}) * (22 == /[a2][^e]+$/))] %= "undefined" == "number")))) : (c = 1 + c,
            (c = c + 1, [] && -4) == (a && (a[(c = 1 + c, ([] && 38..toString() || [ , 0 ][1] == 2) << (24..toString() < 22 || undefined >> -3))] >>>= 2 == false)) >= ([] >= [])))] /= "" >> {} !== (c = c + 1,
            this)))), (c = 1 + c, (a && (a.var += (a && (a.c += "undefined" / null)) >= 38..toString() - 23..toString())) > (c = c + 1,
            c = c + 1, 38..toString())))] *= ((1 ^ 22) == (a && (a[(c = 1 + c, 1 >>> [ , 0 ][1] <= null << -0 >= (25 && "") << ("undefined" !== 3))] += "function" >> "undefined"))) > ("function" << 25 !== this % ([ , 0 ].length === 2)))) : --b + (a && a[{
                undefined: (c = c + 1) + (this - null >= (22 || "undefined") >= (0 != []) >>> ([ , 0 ].length === 2) * []),
                length: a++ + typeof c_1,
                "\t": [ a++ + {
                    c: (c = 1 + c, (undefined & "number") * (4 | -3) === 3 + "" <= (Infinity, 24..toString())),
                    3: (c = 1 + c, -5 >= {} ^ 22 >= -1 ^ (24..toString() + ([ , 0 ].length === 2) ^ -4 + -5)),
                    0: (c = 1 + c, (25 << ([ , 0 ].length === 2) ^ (0 | -1)) % (("bar", {}) >> (0 >> 24..toString())))
                }[(c = 1 + c, "function" % 0 - (true != undefined) & 38..toString() > 4 !== "number" * Infinity)], a++ + [ (c = 1 + c,
                (a && (a.Infinity += NaN != "object" ^ /[a2][^e]+$/ === "function")) > ((-0 && undefined) !== {} + -1)), (c = 1 + c,
                ([ , 0 ].length === 2 != [ , 0 ][1] & (22 ^ -2)) << ((/[a2][^e]+$/ ^ "bar") < (Infinity < 2))), (c = 1 + c,
                +(/[a2][^e]+$/ ^ 23..toString()) >> (-1 * this || (c = c + 1, false))) ].c ]
            }.b]) ? --b + (b-- ? typeof (a++ + [ 0 === 1 ? a : b, a++ + 3, a++ + {
                3: (c = 1 + c, -("" % "undefined" <= (25 & 24..toString()))),
                1.5: (c = 1 + c, !(("function" & -3) <= (a && (a[(c = 1 + c, (-4 >> {} != (a = "number" & "undefined")) * (("object" !== /[a2][^e]+$/) << ("object" << -5)))] += "foo" >> null)))),
                1.5: (c = 1 + c, -1 ^ -3 ^ 2 != -1 ^ (a = [] % [ , 0 ][1] && -0 + 24..toString())),
                undefined: (c = 1 + c, (23..toString() === "function") << "foo" % 1 || (3 && -4) >>> (/[a2][^e]+$/ && 22)),
                "\t": (c = 1 + c, a = ({} <= 1) << (undefined || "function") ^ (-2 == "") * (true & {}))
            } ][++a]) : --b + {
                in: (c = c + 1) + {
                    Infinity: a--
                }.length,
                1.5: a++ + ~b,
                0: (c = c + 1) + function() {
                    var bar = (c = 1 + c, ((23..toString() !== 22) >= (false ^ 0)) + ((-0 & 4) !== "bar" <= NaN)), Infinity_2 = (c = 1 + c,
                    c = c + 1, (38..toString() < null) * (Infinity_2 && (Infinity_2[(c = 1 + c, Infinity_2 |= (-0 == 22 != (c = c + 1,
                    0)) % (5 >> 0 == "object" * "number"))] += 22 * 23..toString())));
                }()
            }[--b + {
                set undefined(arguments_2) {
                    c = c + 1;
                    this.b >>= arguments_2 && (arguments_2.undefined = (-3 ^ -2) !== (-4 & -1)) || arguments_2 && (arguments_2[a++ + a++] = "bar" - Infinity < (NaN ^ true));
                },
                in: --b + (1 === 1 ? a : b),
                b: (c = c + 1) + a--,
                0: (a || 7).toString()[~(((5, this) ^ "function" != -5) >= (("undefined", this) != 22 % 25))],
                var: --b + (a && a.foo)
            }]) : a++) && --brake3 > 0) {
                var brake6 = 5;
                L10038: do {
                    for (var brake7 = 5; --b + {
                        1.5: ((--b + [ (c = 1 + c, (a && (a.a += 5 >= undefined)) + ("" + 0) | ((a && (a[(c = 1 + c,
                        (this % "undefined" === "object" % 38..toString()) > (([ , 0 ][1] ^ "foo") >= this / -5))] += -1 ^ "undefined")) | -3 > "")), (c = 1 + c,
                        (-4 >= -5 || (a = "foo" % 2)) * ((a = "function" | 0) + (a && (a.a = this * 24..toString())))), (c = 1 + c,
                        ((a && (a.b = "undefined" - -0)) ^ (a += [] ^ null)) / (~-4 & NaN >>> 5)) ] || a || 3).toString() || 8).toString()[--b + (--b + (a++ + [ , (c = 1 + c,
                        ("object" ^ -1) >>> (a += (null, 5)) === ((5, -4) !== ("undefined" & 22))), (c = 1 + c,
                        (null !== 22 ^ [ , 0 ][1] % 0) & (a *= -3 > {}) << (c = c + 1, 23..toString())), (c = 1 + c,
                        c = c + 1, (a && (a[(c = 1 + c, ((/[a2][^e]+$/ < "undefined") >> (24..toString() <= -2)) - (a && (a.NaN = (a && (a[(c = 1 + c,
                        (-1 != -4) * ("object" <= "number") % (([] >> 3) / (5 | -4)))] = -0 << 3)) !== (-3 ^ [ , 0 ][1]))))] = "object" == -2)) < ([ , 0 ][1] ^ 25)), (c = 1 + c,
                        ("" <= -1 ^ -0 < 4) * ({} >> 0, "bar" & 5)) ][(c = 1 + c, (a = (-3 === 38..toString()) >> (null >= "bar")) <= ((4 && 0) == 1 - false))] || 4).toString()[({} / 25 !== (NaN || 1)) > ((a += 22 || {}) >= (38..toString() > "object"))] ? 0 === 1 ? a : b : --b + ((1 === 1 ? a : b) || 9).toString()[(c = c + 1,
                        true >> 2) || (-2 <= null) / (23..toString() < "bar")])],
                        0: (c = c + 1) + /[abc4]/.test((a++ + (typeof Math_1 != "object") || b || 5).toString())
                    } && brake7 > 0; --brake7) {
                        var a_1;
                        if (b++) {} else {
                            function f0(arguments_1, Infinity_1, a_2) {
                                c = 1 + c, 2 <= 25 != [ , 0 ][1] * "foo" | (c = c + 1, delete -2);
                                c = 1 + c, ({} <= 22 && 2 >>> 25) === (0 <= 2, 4 % 23..toString());
                            }
                        }
                    }
                } while ((c = c + 1) + (typeof a_1 == "function" && --_calls_ >= 0 && a_1(-4)) && --brake6 > 0);
            }
        }
    } while ((b = a) && --brake2 > 0);
} else {
    var a_2;
}

if (-0 / ("bar" && 1) / ((NaN, 5) + (NaN !== undefined))) {
    c = c + 1;
} else {
    var brake19 = 5;
    L10039: do {
        {
            var brake20 = 5;
            L10040: do {
                for (var brake21 = 5; +(("" && "function") / ({} % "number") - (c = c + 1, 1 == "number")) && brake21 > 0; --brake21) {
                    (c = c + 1) + void (c = c + 1, 22 >= 3 > (a_1 && (a_1.b = "foo" >> 3)));
                }
            } while (a_2 && a_2.c && --brake20 > 0);
        }
    } while (!b && --brake19 > 0);
}

console.log(null, a, b, c);
// uglified code
// (beautified)
function f0(arguments_1, Infinity_1, a_2) {
    c = 1 + c, c = 1 + (c += 1), 23..toString();
}

var _calls_ = 10, a = 100, b = 10, c = 0;

if (a++ + [ (c += 1) + {}.length, b-- ]) {
    var brake2 = 5;
    do {
        for (var brake3 = 5; (~a ? (c += 1) + (a && (a["function" == typeof f0 && --_calls_ >= 0 && f0((c = 1 + c,
        -1 / 0 != (a && (a[(c += 1) + (c = 1 + c, (-5 - 24..toString()) / (23..toString(),
        !0) <= 4 ? (c = 1 + c, a && (a[a] = 0 >= (24..toString() || 38..toString()) == (2 === [ , 0 ].length & 24..toString()) < (a && (a[(c = 1 + c,
        c += 1, "25function" >>> !1 * {})] %= !1)))) : (c = 1 + c, c += 1, ([] && -4) == (a && (a[(c = 1 + c,
        ([] && 38..toString() || !1) << (24..toString() < 22 || 0))] >>>= !1)) >= ([] >= [])))] /= "" >> {} !== (c += 1,
        this)))), (c = 1 + c, (a && (a.var += (a && (a.c += NaN)) >= 38..toString() - 23..toString())) > (c += 1,
        c += 1, 38..toString())))] *= (23 == (a && (a[(c = 1 + c, !0)] += 0))) > (0 != this % (2 === [ , 0 ].length)))) : --b + (a && a[{
            undefined: (c += 1) + (this - null >= 22 >= (0 != []) >>> (2 === [ , 0 ].length) * []),
            length: a++ + typeof c_1,
            "\t": [ a++ + {
                c: (c = 1 + c, -0 === "3" <= 24..toString()),
                3: (c = 1 + c, -5 >= {} ^ !0 ^ 24..toString() + (2 === [ , 0 ].length) ^ -9),
                0: (c = 1 + c, (25 << (2 === [ , 0 ].length) ^ -1) % ({} >> (0 >> 24..toString())))
            }[(c = 1 + c, NaN & 38..toString() > 4 !== NaN)], a++ + [ (c = 1 + c, (a && (a.Infinity += 1)) > (-0 !== {} + -1)), (c = 1 + c,
            (2 === [ , 0 ].length != 0 & -24) << !1), (c = 1 + c, +(/[a2][^e]+$/ ^ 23..toString()) >> (-1 * this || (c += 1,
            !1))) ].c ]
        }.b]) ? --b + (b-- ? typeof (a++ + [ b, 3 + a++, a++ + {
            3: (c = 1 + c, -(NaN <= (25 & 24..toString()))),
            1.5: (c = 1 + c, !(0 <= (a && (a[(c = 1 + c, 1 * (-4 >> {} != (a = 0)))] += 0)))),
            1.5: (c = 1 + c, 3 ^ (a = [] % 0 && -0 + 24..toString())),
            undefined: (c = 1 + c, ("function" === 23..toString()) << NaN || 1023),
            "\t": (c = 1 + c, a = ({} <= 1) << "function" ^ !1 * (!0 & {}))
        } ][++a]) : --b + {
            in: (c += 1) + {
                Infinity: a--
            }.length,
            1.5: a++ + ~b,
            0: (c += 1) + function() {
                c = 1 + c, 23..toString();
                var Infinity_2 = (c = 1 + c, c += 1, (38..toString() < null) * (Infinity_2 && (Infinity_2[(c = 1 + c,
                Infinity_2 |= (0 != (c += 1, 0)) % !1)] += 22 * 23..toString())));
            }()
        }[--b + {
            set undefined(arguments_2) {
                c += 1, this.b >>= arguments_2 && (arguments_2.undefined = !0) || arguments_2 && (arguments_2[a++ + a++] = !1);
            },
            in: --b + a,
            b: (c += 1) + a--,
            0: (a || 7).toString()[~((!0 ^ this) >= (22 != this))],
            var: --b + (a && a.foo)
        }]) : a++) && --brake3 > 0; ) {
            var brake6 = 5;
            do {
                for (var brake7 = 5; --b + {
                    1.5: ((--b + [ (c = 1 + c, (a && (a.a += !1)) + "0" | !1 | (a && (a[(c = 1 + c,
                    (this % "undefined" == "object" % 38..toString()) > (0 >= this / -5))] += -1))), (c = 1 + c,
                    !0 * ((a = 0) + (a && (a.a = this * 24..toString())))), (c = 1 + c, ((a && (a.b = NaN)) ^ (a += null ^ [])) / 0) ] || a || 3).toString() || 8).toString()[--b + (--b + (a++ + [ , (c = 1 + c,
                    -1 >>> (a += 5) === !0), (c = 1 + c, 1 & (a *= -3 > {}) << (c += 1, 23..toString())), (c = 1 + c,
                    c += 1, (a && (a[(c = 1 + c, (!0 >> (24..toString() <= -2)) - (a && (a.NaN = -3 !== (a && (a[(c = 1 + c,
                    0 % (([] >> 3) / -3))] = 0)))))] = !1)) < 25), (c = 1 + c, 0) ][(c = 1 + c, (a = (-3 === 38..toString()) >> !1) <= !1)] || 4).toString()[({} / 25 != 1) > ((a += 22) >= (38..toString() > "object"))] ? b : --b + (a || 9).toString()[(c += 1,
                    !0 / (23..toString() < "bar"))])],
                    0: (c += 1) + /[abc4]/.test((a++ + ("object" != typeof Math_1) || b || 5).toString())
                } && brake7 > 0; --brake7) {
                    var a_1;
                    b++;
                }
            } while ((c += 1) + ("function" == typeof a_1 && --_calls_ >= 0 && a_1(-4)) && --brake6 > 0);
        }
    } while ((b = a) && --brake2 > 0);
} else {
    var a_2;
}

var brake19 = 5;

do {
    var brake20 = 5;
    do {
        for (var brake21 = 5; +("" / ({} % "number") - (c += 1, !1)) && brake21 > 0; --brake21) {
            c += 1, c += 1, a_1 && (a_1.b = 0);
        }
    } while (a_2 && a_2.c && --brake20 > 0);
} while (!b && --brake19 > 0);

console.log(null, a, b, c);
original result:
null 101 101 12

uglified result:
null 101 101 77

minify(options):
{
  "mangle": false
}

Suspicious compress options:
  hoist_funs

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

Is it a legitimate fuzz failure?

@alexlamsl
Copy link
Collaborator Author

I thnk so - f0 is toplevel from ES5's point of view, but I guess being wrapped within do, while and else makes it not so under ES6+?

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

That's a good question. I suspect if a function is declared within a block in ES6 then it's limited to that block, but I'm not sure.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

$ echo 'if (1) { f(); } else { function f(){console.log(2)} }' | node800
[stdin]:1
if (1) { f(); } else { function f(){console.log(2)} }
         ^

TypeError: f is not a function
$ echo 'if (1) { f(); } else { function f(){console.log(2)} }' | bin/uglifyjs -c | node
2

I think we ran into this problem when the fuzzer was first created: #1666

The thing is that no browser implements ES5 scoping rules any longer - it's all ES6 now.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

fyi...

$ echo 'if (1) { f(); } else { function f(){console.log(2)} }' | node0_12_9 
2
$ echo '"use strict"; if (1) { f(); } else { function f(){console.log(2)} }' | node0_12_9 
[stdin]:1
"use strict"; if (1) { f(); } else { function f(){console.log(2)} }
                                     ^^^^^^^^
SyntaxError: In strict mode code, functions can only be declared at top level or immediately within another function.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

Looks like ES6 adopted the ES5 strict mode behavior by default.

@alexlamsl
Copy link
Collaborator Author

So var remains hoisting while function got this weird block scope thing - great work with backward compatibility, ECMAScript.

@alexlamsl
Copy link
Collaborator Author

I guess one way around this is to make hoist_funs less aggressive, or false by default - otherwise uglify-js will in theory produce code that breaks ES6+ platforms.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

I think even uglify-js has to adopt ES6 rules for function declarations at this point. No sense writing a transpiler for an environment that no longer exists.

make hoist_funs less aggressive, or false by default

false by default might be the answer. How does it affect code size in test/benchmark.js?

Perhaps function declarations could still be hoisted so long as every symbol reference could see it in ES6 scope rules. That way all the existing tests will still work.

@alexlamsl
Copy link
Collaborator Author

All the existing test/compress tests will work even if we set hoist_funs to false by default.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

It doesn't work anyway with all the other optimizations in place...

$ echo 'if (1) { f(); } else { function f(){console.log(2)} }' | bin/uglifyjs -c hoist_funs=0 -b
function f() {
    console.log(2);
}

f();

@alexlamsl
Copy link
Collaborator Author

That's dead_code extracting declarations (which again, works for var but not function under ES6+ just for giggles)

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

No one has ever really complained about this hoisting behavior in the wild. No one writes code like that. But it certainly impacts fuzzing.

@alexlamsl
Copy link
Collaborator Author

On the topic of hoist_funs=0, looks like we do get better uglified and/or gzip sizes without that feature for most inputs under test/benchmark.js

@alexlamsl
Copy link
Collaborator Author

No one writes code like that.

Not even for job interview questions? 😈

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

No one would get the job. :-)

@alexlamsl
Copy link
Collaborator Author

node test/benchmark.js

-mc -mc hoist_funs=0
https://code.jquery.com/jquery-3.2.1.js
- parse: 0.203s
- rename: 0.172s
- compress: 1.250s
- scope: 0.078s
- mangle: 0.125s
- properties: 0.000s
- output: 0.109s
- total: 1.937s

Original: 268039 bytes
Uglified: 86661 bytes
GZipped:  30334 bytes
SHA1 sum: 4337b99a8b99335d0ca727cded3cf2e4bcb9fd50

https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js
- parse: 0.406s
- rename: 0.250s
- compress: 2.281s
- scope: 0.141s
- mangle: 0.187s
- properties: 0.000s
- output: 0.110s
- total: 3.375s

Original: 1249863 bytes
Uglified: 173555 bytes
GZipped:  60403 bytes
SHA1 sum: c3791168b68199c6c7f79de6182c22b2ec280b9a

https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js
- parse: 0.734s
- rename: 0.391s
- compress: 4.843s
- scope: 0.157s
- mangle: 0.343s
- properties: 0.000s
- output: 0.266s
- total: 6.734s

Original: 1590107 bytes
Uglified: 467570 bytes
GZipped:  118908 bytes
SHA1 sum: 59a152ed85ef4a7c65bf59caf147cfbd35ecd5d3

https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js
- parse: 0.078s
- rename: 0.063s
- compress: 0.406s
- scope: 0.031s
- mangle: 0.063s
- properties: 0.000s
- output: 0.047s
- total: 0.703s

Original: 69707 bytes
Uglified: 36837 bytes
GZipped:  9676 bytes
SHA1 sum: a1ce1632c1c8da13a9702c63f18fd1f7f387230f

https://unpkg.com/react@15.3.2/dist/react.js
- parse: 0.406s
- rename: 0.265s
- compress: 1.891s
- scope: 0.109s
- mangle: 0.188s
- properties: 0.000s
- output: 0.156s
- total: 3.015s

Original: 701412 bytes
Uglified: 205046 bytes
GZipped:  62250 bytes
SHA1 sum: 46489b55c0e0adddcc53bb8157e99fdefd9b38b3

http://builds.emberjs.com/tags/v2.11.0/ember.prod.js
- parse: 0.687s
- rename: 0.453s
- compress: 3.563s
- scope: 0.172s
- mangle: 0.421s
- properties: 0.000s
- output: 0.266s
- total: 5.562s

Original: 1852178 bytes
Uglified: 525811 bytes
GZipped:  128673 bytes
SHA1 sum: 7536db9a8817057b1f02555de7372697877c407c

https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js
- parse: 0.218s
- rename: 0.172s
- compress: 2.360s
- scope: 0.046s
- mangle: 0.188s
- properties: 0.000s
- output: 0.078s
- total: 3.062s

Original: 539590 bytes
Uglified: 69607 bytes
GZipped:  24418 bytes
SHA1 sum: 87aa00f789138e5ceef545f515f17d7bd52bddad

https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js
- parse: 0.484s
- rename: 0.328s
- compress: 2.672s
- scope: 0.109s
- mangle: 0.266s
- properties: 0.000s
- output: 0.156s
- total: 4.015s

Original: 451131 bytes
Uglified: 211465 bytes
GZipped:  71072 bytes
SHA1 sum: 50cf23b4948e1abcfbf5eaf7ab1f5ee080cb6c52
https://code.jquery.com/jquery-3.2.1.js
- parse: 0.250s
- rename: 0.125s
- compress: 1.125s
- scope: 0.046s
- mangle: 0.125s
- properties: 0.000s
- output: 0.110s
- total: 1.781s

Original: 268039 bytes
Uglified: 86105 bytes
GZipped:  29986 bytes
SHA1 sum: 57a2ca8d4724a803d7f3339ba19932fa687df722

https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js
- parse: 0.468s
- rename: 0.266s
- compress: 2.172s
- scope: 0.125s
- mangle: 0.219s
- properties: 0.000s
- output: 0.109s
- total: 3.359s

Original: 1249863 bytes
Uglified: 173712 bytes
GZipped:  60011 bytes
SHA1 sum: c8895d50b4d25178e7c790045803bf89ae1980f8

https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js
- parse: 0.781s
- rename: 0.500s
- compress: 4.265s
- scope: 0.250s
- mangle: 0.344s
- properties: 0.000s
- output: 0.250s
- total: 6.390s

Original: 1590107 bytes
Uglified: 467202 bytes
GZipped:  118738 bytes
SHA1 sum: d27c25c417d280c169078b45e4643d9de516da45

https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js
- parse: 0.078s
- rename: 0.078s
- compress: 0.390s
- scope: 0.016s
- mangle: 0.078s
- properties: 0.000s
- output: 0.047s
- total: 0.687s

Original: 69707 bytes
Uglified: 36838 bytes
GZipped:  9576 bytes
SHA1 sum: d81bc1985fe993421b2b2344d2dba69aeb6118c6

https://unpkg.com/react@15.3.2/dist/react.js
- parse: 0.437s
- rename: 0.234s
- compress: 1.954s
- scope: 0.093s
- mangle: 0.188s
- properties: 0.000s
- output: 0.109s
- total: 3.015s

Original: 701412 bytes
Uglified: 205488 bytes
GZipped:  62265 bytes
SHA1 sum: a5fd90a7eb574af16a4b0fc6d71be972e7e43a0a

http://builds.emberjs.com/tags/v2.11.0/ember.prod.js
- parse: 0.734s
- rename: 0.562s
- compress: 3.250s
- scope: 0.235s
- mangle: 0.328s
- properties: 0.000s
- output: 0.234s
- total: 5.343s

Original: 1852178 bytes
Uglified: 526684 bytes
GZipped:  128951 bytes
SHA1 sum: f83a9d5ff7a41ee3963728e1ee28faa053e18ea2

https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js
- parse: 0.218s
- rename: 0.141s
- compress: 2.422s
- scope: 0.047s
- mangle: 0.125s
- properties: 0.000s
- output: 0.078s
- total: 3.031s

Original: 539590 bytes
Uglified: 69846 bytes
GZipped:  24769 bytes
SHA1 sum: 377ca712c421d96a3e00efb25687a28e1f47654d

https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js
- parse: 0.515s
- rename: 0.313s
- compress: 2.922s
- scope: 0.125s
- mangle: 0.218s
- properties: 0.000s
- output: 0.157s
- total: 4.250s

Original: 451131 bytes
Uglified: 211792 bytes
GZipped:  70742 bytes
SHA1 sum: ff33e5df14fc024479cc453ea6f649101b5a0df8

@alexlamsl
Copy link
Collaborator Author

I thnk the best course of action is for test/ufuzz.js not to generate any AST_Defun except at the top level.

OTOH compression ratio seems to be better with hoist_funs:false

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

Judging by the numbers it's fine to disable hoist_funs by default.

I thnk the best course of action is for test/ufuzz.js not to generate any AST_Defun except at the top level.

Declaring and calling functions between scopes is a legitimate test scenario. What if we only generate AST_Defuns at top level or top of functions - i.e. never in blocks?

@alexlamsl
Copy link
Collaborator Author

Declaring and call functions between scopes is a legitimate test scenario. What if we only generate AST_Defuns at top level or top of functions - i.e. never in blocks?

Fair point - let me try and do it that way.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

The old node 0.12 error says as much:

SyntaxError: In strict mode code, functions can only be declared at top level or immediately within another function.

@alexlamsl
Copy link
Collaborator Author

Right, let's see if 0da91e5 does what it says on the tin...

@alexlamsl
Copy link
Collaborator Author

50kFuzz

// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(foo_2, Math_1, undefined_2) {
    function f1() {
        c = c + 1;
        {
            var expr2 = (typeof Math || 7).toString()[foo_2 && foo_2[(c = 1 + c, (Math_1 && (Math_1.b >>>= 2 === ([ , 0 ].length === 2))) * ({} >>> "object") ^ (([ , 0 ][1] | undefined) ^ 23..toString() * 5))]] ? foo_2 && foo_2.c : --b + (1 === 1 ? a : b);
            for (var key2 in expr2) {
            }
        }
    }
    var foo_2 = f1((c = c + 1) + [ a++ + [ (c = 1 + c, (undefined_2 && (undefined_2.in ^= (24..toString() === 24..toString()) >> (undefined == 23..toString()))) !== ((Math_1 && (Math_1[(c = 1 + c, 
    (Math_1 && (Math_1[(c = 1 + c, (0 + -5 ^ -1 > 1) !== (foo_2 = [] + -3) >> (false == [ , 0 ][1]))] += true || 1)) != (undefined_2 && (undefined_2.in = 22 + 38..toString())) | 5 << /[a2][^e]+$/ ^ "" - 38..toString())] += [ , 0 ][1] ^ 3)) & {} << "bar")), (c = 1 + c, 
    (Math_1 && (Math_1.b = (c = c + 1, 2) * (null && NaN))) / ({} - true < ([ , 0 ][1] === [ , 0 ][1]))), (c = 1 + c, 
    Math_1 = (Math_1 && (Math_1[a++ + -((foo_2 = 2 === "") / (-2 + -2) % ((false <= true) / (22 <= 23..toString())))] >>>= "" < -2 | 1 <= this)) >> ([ , 0 ][1] > 23..toString() < 4 << -4)) ].in, 24..toString() ][(c = c + 1) + (a++ + (typeof f1 == "function" && --_calls_ >= 0 && f1()))], undefined, {}[a++ + (b *= a)]);
    function f2(a_2, a_2, Math_2) {
        {
            var NaN = function NaN_2(a_2) {
                {
                }
                c = 1 + c, (-3 >> "function") - (-0 !== NaN) | (a_2 && (a_2.Infinity ^= "number" && 22)) ^ 23..toString() !== [];
            }(a_2 && a_2.b);
        }
        if (--b + ((a_2 || 2).toString()[typeof undefined_2 == "function" && --_calls_ >= 0 && undefined_2(3, (c = 1 + c, 
        "object" / -1 / (5 < "function") & (a_2 = 25 << -0) - (-4 ^ [])), (c = 1 + c, ((Math_2 && (Math_2.a = Infinity > -5)) & (undefined_2 && (undefined_2[(c = 1 + c, 
        foo_2 && (foo_2.in /= !(25 || 1) == (23..toString() == 25 !== (22 ^ [ , 0 ][1]))))] += "" && -1))) >> (a_2 = (2 || -0) <= (23..toString() <= /[a2][^e]+$/))))] ? [ (c = 1 + c, 
        (null | -4) + (foo_2 && (foo_2.c = 22 - 3)) & (a_2 && (a_2.null *= (2 && this) != (undefined == 3)))), (c = 1 + c, 
        (-5 != {}) / (-4 << 5) / (0 * 5 - (3 <= 1))), (c = 1 + c, (1 == "") << (-3 || -3) === ("" - 0 ^ {} > "")), (c = 1 + c, 
        undefined !== false == 22 % undefined, (Math_2 = (5, 5)) !== (24..toString() | 22)), (c = 1 + c, 
        foo_2 && (foo_2[--b] = (-0 - -5 | (22 && 0)) === (-2 ^ 38..toString()) << ([ , 0 ][1] === "foo"))) ][a++ + (undefined_2 && undefined_2[(c = 1 + c, 
        (-1 + 5) % (22 | 2), (a_2 = {} || 22) != (25 === -5))])] : (c = c + 1) + a_2)) {
            try {
                L15638: {
                    c = 1 + c, ("" * -5 !== 2 % "number") % ((a_2.var = 25 << "number") ^ false === -1);
                    c = 1 + c, (delete 4 | void true) & (-5 && "bar") >= (-4 | Infinity);
                }
            } catch (Infinity) {
                c = c + 1;
            } finally {
                L15639: {
                }
                {
                    var expr15 = (c = 1 + c, false > Infinity <= -5 % -5 === 1 <= 24..toString() < "bar" * []);
                    for (var key15 in expr15) {
                        c = 1 + c, -3 * "object" > 5 + "function", -0 == ([ , 0 ].length === 2) || "" >>> -5;
                    }
                }
            }
        }
    }
    var a_1 = f2(/[abc4]/.test(("undefined" || b || 5).toString()), 22, --b + a++);
}

var NaN = f0(1);

console.log(null, a, b, c);
// uglified code
// (beautified)
function f0(foo_2, Math_1, undefined_2) {
    function f1() {
        c += 1;
        var expr2 = (typeof Math || 7).toString()[foo_2 && foo_2[(c = 1 + c, (Math_1 && (Math_1.b >>>= 2 === (2 === [ , 0 ].length))) * ({} >>> "object") ^ 0 ^ 5 * 23..toString())]] ? foo_2 && foo_2.c : --b + a;
        for (var key2 in expr2) {}
    }
    var a_2;
    foo_2 = f1((c += 1, a++, c = 1 + c, undefined_2 && (undefined_2.in ^= (24..toString() == 24..toString()) >> (void 0 == 23..toString())), 
    Math_1 && (Math_1[(c = 1 + c, (Math_1 && (Math_1[(c = 1 + c, -5 != (foo_2 = [] + -3) >> !0)] += !0)) != (undefined_2 && (undefined_2.in = 22 + 38..toString())) | 5 ^ "" - 38..toString())] += 3), 
    c = 1 + c, Math_1 && (Math_1.b = null * (c += 1, 2)), c = 1 + c, Math_1 = (Math_1 && (Math_1[a++ + -(foo_2 = !1) / -4 % (!0 / (22 <= 23..toString()))] >>>= !1 | 1 <= this)) >> (0 > 23..toString() < 4 << -4), 
    24..toString(), c += 1, a++, --_calls_ >= 0 && f1()), (a++, b *= a)), function(a_2, a_2, Math_2) {
        if (a_2 = a_2 && a_2.b, c = 1 + c, a_2 && (a_2.Infinity ^= 22), 23..toString(), 
        --b + ((a_2 || 2).toString()["function" == typeof undefined_2 && --_calls_ >= 0 && undefined_2(3, (c = 1 + c, 
        0 / 0 & (a_2 = 25) - (-4 ^ [])), (c = 1 + c, ((Math_2 && (Math_2.a = !0)) & (undefined_2 && (undefined_2[(c = 1 + c, 
        foo_2 && (foo_2.in /= 0 == (25 == 23..toString() !== 22)))] += ""))) >> (a_2 = 2 <= (23..toString() <= /[a2][^e]+$/))))] ? [ (c = 1 + c, 
        -4 + (foo_2 && (foo_2.c = 19)) & (a_2 && (a_2.null *= 0 != this))), (c = 1 + c, 
        (-5 != {}) / -128 / 0), (c = 1 + c, 0 == (0 ^ {} > "")), (c = 1 + c, (Math_2 = 5) != (22 | 24..toString())), (c = 1 + c, 
        foo_2 && (foo_2[--b] = 5 == (-2 ^ 38..toString()) << !1)) ][a++ + (undefined_2 && undefined_2[(c = 1 + c, 
        0 != (a_2 = {} || 22))])] : (c += 1) + a_2)) {
            try {
                c = 1 + c, a_2.var = 25, c = 1 + c;
            } catch (Infinity) {
                c += 1;
            } finally {
                var expr15 = (c = 1 + c, 1 == 1 <= 24..toString() < "bar" * []);
                for (var key15 in expr15) {
                    c = 1 + c;
                }
            }
        }
    }(/[abc4]/.test("undefined".toString()), 22, --b + a++);
}

var _calls_ = 10, a = 100, b = 10, c = 0, NaN = f0(1);

console.log(null, a, b, c);
original result:
null 104 924 14

uglified result:
null 104 924 11

minify(options):
{
  "mangle": false
}

Suspicious compress options:
  inline
  reduce_vars
  sequences
  side_effects
  unused

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

Fuzz failure above wasn't a cross-scope call. Were nested AST_Defuns ever called before?

@alexlamsl
Copy link
Collaborator Author

I notice the 0/0 part right away (it was +function(){} in the original test case), it's the switch(a) { case a: break; ... } part that I was puzzled with.

But then of course, NaN !== NaN 😏

@alexlamsl
Copy link
Collaborator Author

So the problem here is OPT(AST_NaN) being run before inline introduces the local variable NaN.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

The problem is that the variable NaN depends on local context. print_to_string() is unaware of the local context and always emits NaN for AST_NaN.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

Perhaps if a function redefines one of the triad of evil (NaN, Infinity, undefined) don't attempt to inline. Actually, undefined may be okay since a yet to be initialized undefined local variable is void 0.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

But then of course, NaN !== NaN

Unless it's overridden.

$ echo 'console.log(NaN===NaN); !function(NaN){console.log(NaN===NaN);}();' | node
false
true

@alexlamsl
Copy link
Collaborator Author

We aren't using equivalent_to() in this case, so print_to_string() is off the hook - it's evaluate which turns 0/0 into AST_NaN.

The parser doesn't ever generates AST_NaN and friends - they are always parsed as AST_SymbolRef. It's OPT(AST_SymbolRef) which turns them into those node types.

Your suggestion of skipping any functions with argument names of Infinity, NaN or undefined would have been limiting back in the days - but we now have rename, so we can safely apply this rule of yours without worrying about loss of compression ratio in the default case 😉

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

skipping any functions with argument names of Infinity, NaN or undefined

Checking the function args alone are insufficient. Even local variables of the same name would present a problem.

undefined may be okay, but it's not worth the risk - exclude all three.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

It's OPT(AST_SymbolRef) which turns them into those node types.

I know. And that's what the compressor did with 0/0 - it transformed it into AST_NaN.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

...which is why print_to_string() is not off the hook. AST_NaN has to be emitted as output eventually, and when it is it will be incorrect since it will be in the wrong context.

@alexlamsl
Copy link
Collaborator Author

Ah, I see where we differ - I treat OutputStream as a dumb pipe, so I only focus on the invalid AST being generated.

@alexlamsl
Copy link
Collaborator Author

Rebased and restarted test/ufuzz.js - I think if we can merge this PR once we reached 1MFuzz without incident.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

Regarding the fuzzer changes in this PR... are all AST_Defuns guaranteed to be called or just called with high probability? Do you see generated fuzzer output where an AST_Defun is not called?

@alexlamsl
Copy link
Collaborator Author

AFAICT, only createFunction() can (but not always) generate AST_Defuns, and it was always immediately called in the subsequent AST_VarDef.

With this PR, the only time the call gets omitted is when there exists a prior typeof f0 == "function" && f0(...).

In both previous and current cases, an AST_Defun is not guaranteed to be called, as the section of code might not be reachable. Previously the only times when AST_Defun is "visible" but not called is when an exception is thrown, whereas now we also get that if typeof ... is somehow unreachable.

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

My real question with this PR is whether it is possible to potentially generate pairs of AST_Defuns (either sibling or nested) that can call each other (directly or indirectly).

@alexlamsl
Copy link
Collaborator Author

In theory the following can be generated:

var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    function f1() {
        typeof f2 == "function" && --_calls_ >= 0 && f2();
    }
    var a_1 = f1();
    function f2() {
        typeof f1 == "function" && --_calls_ >= 0 && f1();
    }
}
var a_2 = f0();

console.log(null, a, b, c);

@kzc
Copy link
Contributor

kzc commented Dec 19, 2017

In theory the following can be generated:

Perfect. Will bump up odds of calls to see if we can generate such a case.

@kzc
Copy link
Contributor

kzc commented Dec 20, 2017

I've seen fuzzed examples of successful recursion and forward calls. Although I haven't seen backward calls, there's no reason why they can't be generated.

@@ -754,6 +758,16 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
var name = getVarName();
return name + ' && ' + name + '.' + getDotKey();
case p++:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the odds of calls being generated should be bumped up by repeating case p++: a few times.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 1200833

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think 1200833 is what we want. I meant to merely repeat the case p++: statement many times.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait - I now see you doubled the odds. I'd quadruple it at least.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind 99% of call attempts never execute, so you need a lot of them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doubled again in 19db723

@alexlamsl
Copy link
Collaborator Author

Something I use to hunt interesting test cases:

--- a/test/ufuzz.js
+++ b/test/ufuzz.js
@@ -329,6 +329,7 @@ function createTopLevelCode() {
         rng(2) == 0
         ? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)
         : createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0),
+'if (_calls_ < 10 && _calls_ >= 0) throw new Error(_calls_);',
         'console.log(null, a, b, c);' // preceding `null` makes for a cleaner output (empty string still shows up etc)
     ].join('\n');
 }

@kzc
Copy link
Contributor

kzc commented Dec 20, 2017

When the odds of generating calls was increased by 8 times the following (console.log annotated) code with a backward call was generated:

$ cat fuzz.js 
// original code
// (beautified and annotated)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    console.log("*** function f0, _calls_ =", _calls_);
    function f1(c_1, parseInt_1, b_2) {
        console.log("*** function f1, _calls_ =", _calls_);
        c = c + 1;
        c = c + 1;
    }
    var c = f1(5);
    function f2(bar_1, b_1) {
        console.log("*** function f2, _calls_ =", _calls_);
        function f3(b, Infinity) {
            console.log("*** function f3, _calls_ =", _calls_);
            var Infinity;
            try {
                c = 1 + c, b_1 && (b_1.Infinity = ("" != Infinity) >> ("undefined" != -2) & (3 ^ -1) <= [ , 0 ][1] >>> "undefined");
            } catch (Infinity_2) {
            }
        }
        var a_1 = f3(22);
        function f4(a, undefined_1) {
            console.log("*** function f4, _calls_ =", _calls_);
            function f5(arguments) {
                console.log("*** function f5, _calls_ =", _calls_);
            }
            var bar = f5((c = 1 + c, +(0 + this && 3 & "function")), (c = 1 + c, (-5 == /[a2][^e]+$/ ^ 25 == 3) < ("foo" != "function" && -0 !== 22)), (c = 1 + c, 
            (bar_1 && (bar_1[typeof b_1 == "function" && --_calls_ >= 0 && (console.log("*** calling b_1, _calls_ =", _calls_), b_1())] = -4 >>> -3 >> (false !== 22))) <= (22 % [ , 0 ][1] !== (22 && [ , 0 ].length === 2))));
        }
        var b_2 = f4(b -= a, void function() {
        }());
        function f6(b_2, bar_1, bar_2) {
            console.log("*** function f6, _calls_ =", _calls_);
            if (c = 1 + c, (-1 === -4) * (0 / "object") == (([ , 0 ].length === 2) >>> null | 3 + "foo")) {
                c = 1 + c, (false != {}, 2 && []) < (2 >> -4 ^ 5 >> "bar");
            } else {
                c = 1 + c, (bar_1 && (bar_1.null = NaN !== 2)) == (/[a2][^e]+$/ | 1) == "function" << "function" << ("undefined" > 4);
            }
            {
            }
        }
        var undefined_2 = f6();
    }
    var bar = f2();
    function f7() {
        console.log("*** function f7, _calls_ =", _calls_);
        return a++ + -((a && (a.foo &= (/[a2][^e]+$/ == NaN) - undefined / /[a2][^e]+$/)) <= (a && (a[typeof f0 == "function" && --_calls_ >= 0 && (console.log("*** calling f0, _calls_ =", _calls_), f0((c = 1 + c, 
        delete (true !== "undefined") + (a && (a[(c = c + 1) + (typeof a == "function" && --_calls_ >= 0 && (console.log("*** calling a, _calls_ =", _calls_), a([ , 0 ].length === 2)))] += (a += 5 | undefined) > (1 ^ [])))), -4, (c = 1 + c, 
        (c = c + 1, 1) << ("" != 1), a && (a.null += ([ , 0 ][1] ^ "object") & 3 == 4))))] += (0 > 25) + (c = c + 1, 
        22))));
    }
    var arguments_2 = f7((c = c + 1) + typeof bar_1, --b + ((NaN < -1) >> undefined / undefined | [ , 0 ][1] >> [] << (24..toString(), 
    23..toString())));
    function f8(a_1, foo_1) {
        console.log("*** function f8, _calls_ =", _calls_);
        {
            var expr12 = (c = c + 1) + b++;
            for (var key12 in expr12) {
                c = 1 + c;
                var parseInt_1 = expr12[key12];
                {
                    var brake13 = 5;
                    do {
                        for (var brake14 = 5; --b + parseInt_1 && brake14 > 0; --brake14) {
                            var brake15 = 5;
                            while ((c = 1 + c, (Infinity - -0) / -true | (a_1 && (a_1[(c = 1 + c, (-0 ^ 4) * (0 ^ [ , 0 ][1]) === ([ , 0 ][1] & -1) << (a_1 && (a_1[(c = 1 + c, 
                            24..toString() % "foo" / delete "bar" == ([ , 0 ][1] % -4 | ([ , 0 ].length === 2 && -0)))] = 38..toString() <= "bar")))] = (-5, 
                            [ , 0 ].length === 2))) + +"function") && --brake15 > 0) {
                                c = 1 + c, c = c + 1, (Infinity, "undefined") << (NaN, -3);
                            }
                        }
                    } while ((0 === 1 ? a : b) && --brake13 > 0);
                }
            }
        }
        return [ , a++ + Infinity, typeof parseInt_1 == "function" && --_calls_ >= 0 && (console.log("*** calling parseInt_1, _calls_ =", _calls_), parseInt_1("number", (c = 1 + c, 
        (foo_1 && (foo_1.a = ("function" && "") << (this < -1))) & (parseInt_1 && (parseInt_1[typeof f10 == "function" && --_calls_ >= 0 && (console.log("*** calling f10, _calls_ =", _calls_), f10())] = NaN * this + ("foo" !== "foo")))), (c = 1 + c, 
        (24..toString() >>> "function" || 23..toString() != "undefined") >> (38..toString() - 25 << (Infinity || -2))))), a++ + void function b_1() {
        }() ].NaN;
    }
    var arguments = f8();
}

var Infinity_1 = f0((c = c + 1) + /[abc4]/.test((typeof a_1 == "function" || b || 5).toString()), 38..toString());

console.log(null, a, b, c);
$ node fuzz.js 
*** function f0, _calls_ = 10
*** function f1, _calls_ = 10
*** function f2, _calls_ = 10
*** function f3, _calls_ = 10
*** function f4, _calls_ = 10
*** function f5, _calls_ = 10
*** function f6, _calls_ = 10
*** function f7, _calls_ = 10
*** calling f0, _calls_ = 9
*** function f0, _calls_ = 9
*** function f1, _calls_ = 9
*** function f2, _calls_ = 9
*** function f3, _calls_ = 9
*** function f4, _calls_ = 9
*** function f5, _calls_ = 9
*** function f6, _calls_ = 9
*** function f7, _calls_ = 9
*** calling f0, _calls_ = 8
*** function f0, _calls_ = 8
*** function f1, _calls_ = 8
*** function f2, _calls_ = 8
*** function f3, _calls_ = 8
*** function f4, _calls_ = 8
*** function f5, _calls_ = 8
*** function f6, _calls_ = 8
*** function f7, _calls_ = 8
*** calling f0, _calls_ = 7
*** function f0, _calls_ = 7
*** function f1, _calls_ = 7
*** function f2, _calls_ = 7
*** function f3, _calls_ = 7
*** function f4, _calls_ = 7
*** function f5, _calls_ = 7
*** function f6, _calls_ = 7
*** function f7, _calls_ = 7
*** calling f0, _calls_ = 6
*** function f0, _calls_ = 6
*** function f1, _calls_ = 6
*** function f2, _calls_ = 6
*** function f3, _calls_ = 6
*** function f4, _calls_ = 6
*** function f5, _calls_ = 6
*** function f6, _calls_ = 6
*** function f7, _calls_ = 6
*** calling f0, _calls_ = 5
*** function f0, _calls_ = 5
*** function f1, _calls_ = 5
*** function f2, _calls_ = 5
*** function f3, _calls_ = 5
*** function f4, _calls_ = 5
*** function f5, _calls_ = 5
*** function f6, _calls_ = 5
*** function f7, _calls_ = 5
*** calling f0, _calls_ = 4
*** function f0, _calls_ = 4
*** function f1, _calls_ = 4
*** function f2, _calls_ = 4
*** function f3, _calls_ = 4
*** function f4, _calls_ = 4
*** function f5, _calls_ = 4
*** function f6, _calls_ = 4
*** function f7, _calls_ = 4
*** calling f0, _calls_ = 3
*** function f0, _calls_ = 3
*** function f1, _calls_ = 3
*** function f2, _calls_ = 3
*** function f3, _calls_ = 3
*** function f4, _calls_ = 3
*** function f5, _calls_ = 3
*** function f6, _calls_ = 3
*** function f7, _calls_ = 3
*** calling f0, _calls_ = 2
*** function f0, _calls_ = 2
*** function f1, _calls_ = 2
*** function f2, _calls_ = 2
*** function f3, _calls_ = 2
*** function f4, _calls_ = 2
*** function f5, _calls_ = 2
*** function f6, _calls_ = 2
*** function f7, _calls_ = 2
*** calling f0, _calls_ = 1
*** function f0, _calls_ = 1
*** function f1, _calls_ = 1
*** function f2, _calls_ = 1
*** function f3, _calls_ = 1
*** function f4, _calls_ = 1
*** function f5, _calls_ = 1
*** function f6, _calls_ = 1
*** function f7, _calls_ = 1
*** calling f0, _calls_ = 0
*** function f0, _calls_ = 0
*** function f1, _calls_ = 0
*** function f2, _calls_ = 0
*** function f3, _calls_ = 0
*** function f4, _calls_ = 0
*** function f5, _calls_ = 0
*** function f6, _calls_ = 0
*** function f7, _calls_ = 0
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
*** function f8, _calls_ = -1
null 183 -1420 1

@kzc
Copy link
Contributor

kzc commented Dec 20, 2017

LGTM

- forward call `fN()`
- allow forward call functions to be single-use
- avoid generating `AST_Defun` within blocks
@alexlamsl
Copy link
Collaborator Author

Commits squashed - pending 1MFuzz before merge... 🤖

@kzc thanks for the help, as usual 💯

@kzc
Copy link
Contributor

kzc commented Dec 20, 2017

Do you still plan to disable hoist_funs by default?

@alexlamsl
Copy link
Collaborator Author

Do you still plan to disable hoist_funs by default?

Thanks for the reminder - yes, I think it makes sense judging from the statisitics.

I'll scan through the actual diffs to make sure nothing funky is happening, but otherwise it'll be off by default from v3.3.0

Hopefully I'll get some hints for your hypothesis on mangle name assignment as well.

@alexlamsl alexlamsl merged commit 4113609 into mishoo:master Dec 20, 2017
@alexlamsl alexlamsl deleted the ufuzz-func branch December 20, 2017 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants