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

expand symbol space to improve compression #2460

Merged
merged 1 commit into from
Nov 19, 2017
Merged

Conversation

alexlamsl
Copy link
Collaborator

Introduce options.rename, default to true if both options.compress and options.mangle are enabled.

$ cat test.js
var a = "PASS";
function f() {
    return a;
}
function g() {
    return f();
}
!function() {
    var a = "FAIL";
    if (a == a) console.log(g());
}();

Before

$ uglifyjs test.js --toplevel -b bracketize -mc evaluate=0,passes=10
function n() {
    return o;
}

var o = "PASS";

!function() {
    var o = "FAIL";
    o == o && console.log(n());
}();

After

$ uglifyjs test.js --toplevel -b bracketize -mc evaluate=0,passes=10
var o = "PASS";

!function() {
    var n = "FAIL";
    n == n && console.log(o);
}();

@alexlamsl
Copy link
Collaborator Author

node test/benchmark.js --toplevel -mc unsafe,keep_fargs=0,hoist_props,passes=1000

1127a2c #2460
https://code.jquery.com/jquery-3.2.1.js
- parse: 0.234s
- compress: 2.672s
- scope: 0.047s
- mangle: 0.109s
- properties: 0.000s
- output: 0.125s
- total: 3.187s

Original: 268039 bytes
Uglified: 86598 bytes
GZipped:  30306 bytes
SHA1 sum: eef21d73fbce238d4a360366fbee620edfb0db4e

https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js
- parse: 0.430s
- compress: 7.054s
- scope: 0.078s
- mangle: 0.172s
- properties: 0.000s
- output: 0.125s
- total: 7.859s

Original: 1249863 bytes
Uglified: 173244 bytes
GZipped:  60269 bytes
SHA1 sum: d0419d19d94408df9fbd81746d770ef2c3b99a90

https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js
- parse: 0.836s
- compress: 13.008s
- scope: 0.140s
- mangle: 0.344s
- properties: 0.000s
- output: 0.422s
- total: 14.750s

Original: 1590107 bytes
Uglified: 465357 bytes
GZipped:  118763 bytes
SHA1 sum: a596f5959eab7e54e50f6697859b0849ee9bbf6f

https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js
- parse: 0.078s
- compress: 0.881s
- scope: 0.014s
- mangle: 0.058s
- properties: 0.000s
- output: 0.049s
- total: 1.080s

Original: 69707 bytes
Uglified: 36802 bytes
GZipped:  9678 bytes
SHA1 sum: 73725863e29246e1355cdec03e51c7de20e40eb9

https://unpkg.com/react@15.3.2/dist/react.js
- parse: 0.343s
- compress: 4.704s
- scope: 0.062s
- mangle: 0.188s
- properties: 0.000s
- output: 0.125s
- total: 5.422s

Original: 701412 bytes
Uglified: 204582 bytes
GZipped:  62101 bytes
SHA1 sum: 87164d0eea5a823db9542f8840abc071f5e98190

http://builds.emberjs.com/tags/v2.11.0/ember.prod.js
- parse: 0.845s
- compress: 9.436s
- scope: 0.157s
- mangle: 0.359s
- properties: 0.000s
- output: 0.312s
- total: 11.109s

Original: 1852178 bytes
Uglified: 525166 bytes
GZipped:  128267 bytes
SHA1 sum: a359b821126ed29f0742177ce8c06e8d008546e0

https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js
- parse: 0.250s
- compress: 4.750s
- scope: 0.047s
- mangle: 0.109s
- properties: 0.000s
- output: 0.078s
- total: 5.234s

Original: 539590 bytes
Uglified: 69867 bytes
GZipped:  24275 bytes
SHA1 sum: 6b25ef5a4823e63121657dc095efcff588a52db4

https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js
- parse: 0.460s
- compress: 6.462s
- scope: 0.125s
- mangle: 0.250s
- properties: 0.000s
- output: 0.172s
- total: 7.469s

Original: 451131 bytes
Uglified: 211064 bytes
GZipped:  70732 bytes
SHA1 sum: ae25557c05c7f7886d71f0c5fa96e992b94c70f6
https://code.jquery.com/jquery-3.2.1.js
- parse: 0.390s
- compress: 2.500s
- scope: 0.031s
- mangle: 0.094s
- properties: 0.000s
- output: 0.110s
- total: 3.125s

Original: 268039 bytes
Uglified: 86598 bytes
GZipped:  30306 bytes
SHA1 sum: eef21d73fbce238d4a360366fbee620edfb0db4e

https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js
- parse: 0.656s
- compress: 6.203s
- scope: 0.063s
- mangle: 0.187s
- properties: 0.000s
- output: 0.156s
- total: 7.265s

Original: 1249863 bytes
Uglified: 173082 bytes
GZipped:  60197 bytes
SHA1 sum: cb5445efe5314113db42c83854bbd426349c1314

https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js
- parse: 1.296s
- compress: 12.204s
- scope: 0.140s
- mangle: 0.344s
- properties: 0.000s
- output: 0.422s
- total: 14.406s

Original: 1590107 bytes
Uglified: 465581 bytes
GZipped:  118750 bytes
SHA1 sum: 4215e13c36dd7fc91ce6824067df2d38afebbacf

https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js
- parse: 0.140s
- compress: 0.750s
- scope: 0.016s
- mangle: 0.062s
- properties: 0.000s
- output: 0.047s
- total: 1.015s

Original: 69707 bytes
Uglified: 36802 bytes
GZipped:  9678 bytes
SHA1 sum: 73725863e29246e1355cdec03e51c7de20e40eb9

https://unpkg.com/react@15.3.2/dist/react.js
- parse: 0.593s
- compress: 4.250s
- scope: 0.047s
- mangle: 0.172s
- properties: 0.000s
- output: 0.109s
- total: 5.171s

Original: 701412 bytes
Uglified: 204497 bytes
GZipped:  62095 bytes
SHA1 sum: 5c4cffa0f9c36f8b3e335fefcd90499afd7f37b7

http://builds.emberjs.com/tags/v2.11.0/ember.prod.js
- parse: 1.250s
- compress: 8.781s
- scope: 0.141s
- mangle: 0.312s
- properties: 0.000s
- output: 0.391s
- total: 10.875s

Original: 1852178 bytes
Uglified: 524992 bytes
GZipped:  128285 bytes
SHA1 sum: e946d6134ff0c843642e8c5f5c5e6914e6d9bd4d

https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js
- parse: 0.421s
- compress: 4.282s
- scope: 0.031s
- mangle: 0.125s
- properties: 0.000s
- output: 0.094s
- total: 4.953s

Original: 539590 bytes
Uglified: 69606 bytes
GZipped:  24280 bytes
SHA1 sum: 0c9dba5b3f16b54f703a298c4cff19d64ae1b04b

https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js
- parse: 0.828s
- compress: 5.937s
- scope: 0.094s
- mangle: 0.234s
- properties: 0.000s
- output: 0.204s
- total: 7.297s

Original: 451131 bytes
Uglified: 211064 bytes
GZipped:  70744 bytes
SHA1 sum: 32b157eae7cc37621f921ddb9c4d586352f7dffe

@kzc
Copy link
Contributor

kzc commented Nov 9, 2017

Introduce options.rename, default to true if both options.compress and options.mangle are enabled.

gzip output appears to be a wash.

Is the motivation for this to improve non-gzip output size as discussed in #2422?

What does expand_names do?

@alexlamsl
Copy link
Collaborator Author

AST_Toplevel.expand_names() assigns globally distinct names to distinct variables - this minimises variable name collisions, which hinders function inlining and substitutions across scope boundaries.

@alexlamsl
Copy link
Collaborator Author

With improved chances of function inlining and/or substitution, we conceivably increase the average number of variables in AST_Scope, which means less repetition of mangled variable names, which in turn could hurt gzip compression ratio.

@kzc
Copy link
Contributor

kzc commented Nov 9, 2017

All things being equal, slightly less CPU use is worth something.

@alexlamsl alexlamsl changed the title [WIP] expand symbol space to improve compression expand symbol space to improve compression Nov 10, 2017
@alexlamsl
Copy link
Collaborator Author

@kzc slightly OT: how comfortable are you with hoist_props?

I'm comtemplating whether to merge this PR now, or wait for 3.2.0 - the latter would mean enabling hoist_props by default.

@kzc
Copy link
Contributor

kzc commented Nov 10, 2017

I think hoist_props works well and could be enabled by default.

But 3.2.0 should definitely have a new option to gate single-use AST_Defun to AST_Function conversion in reduce_vars. PR coming up.

@alexlamsl
Copy link
Collaborator Author

How about I make 3.1.9 first with the current fixes and see if anything else pops up next week, then we cut 3.2.0?

@kzc
Copy link
Contributor

kzc commented Nov 10, 2017

The blanket AST_Defun to AST_Function conversion is a big deal. It slows down a lot of code. I think it should go in the next release. If reduce_funcs is in there at least they can disable it.

@alexlamsl
Copy link
Collaborator Author

It slows down a lot of code.

Oh?

@kzc
Copy link
Contributor

kzc commented Nov 10, 2017

Same scope function inlining is probably fine, but the overhead to repeatedly generate a function expression is quite high - even in latest JS engines.

@alexlamsl
Copy link
Collaborator Author

I think the same scope replacement should present no performance difference, indeed.

I'm just not sure about the a lot of code part, especially whether they are being noticed. Having said that, I'm all for letting people shoot themselves in the foot have higher granularity of choice.

Thinking back though - part of this initiative is to match some of the functionalities of Closure. So wouldn't they suffer from the same performance concern? 🤔

@kzc
Copy link
Contributor

kzc commented Nov 10, 2017

part of this initiative is to match some of the functionalities of Closure. So wouldn't they suffer from the same performance concern?

The reason why Closure is not subject to performance degradation is due to the fact that they will only inline a function declaration when they can inline the function entirely so there is no function expression at all. What we do is replace the function symbol reference to the function declaration with a function expression and hope a subsequent pass will inline it.

@alexlamsl
Copy link
Collaborator Author

Gotcha - will bear that in mind in the next round of hacking.

@kzc
Copy link
Contributor

kzc commented Nov 17, 2017

Will 3.2.0 include the 3.1.x fixes, this PR and enable hoist_props by default?

@alexlamsl
Copy link
Collaborator Author

That's my current plan, yes.

@kzc
Copy link
Contributor

kzc commented Nov 17, 2017

Would it be possible to cut a final 3.1.10 release with just what is present in master without adding this PR and leaving hoist_props as is? The reason being that it's desirable to leave each minor release in a stable state. That final 3.1.x release would not require extensive testing and can immediately be followed with a 3.2.0 release with the features above with normal testing. That way there can be a fallback should any unforeseen issue with 3.2.0 and the new hoist_props: true default arise.

@alexlamsl
Copy link
Collaborator Author

Let's see if I can run test/ufuzz.js twice this weekend, if so I don't mind doing a double release.

@alexlamsl
Copy link
Collaborator Author

Off to a bad start with Node.js 9.2.0

37 of Infinity
<--- Last few GCs --->

[664:000000EC737E2470]    16938 ms: Mark-sweep 3187.2 (3229.7) -> 3186.6 (3227.7) MB, 61.2 / 0.0 ms  allocation failure GC in old space requested
[664:000000EC737E2470]    16955 ms: Mark-sweep 3186.6 (3227.7) -> 3186.6 (3194.7) MB, 16.8 / 0.0 ms  last resort GC in old space requested
[664:000000EC737E2470]    16977 ms: Mark-sweep 3186.6 (3194.7) -> 3186.6 (3194.7) MB, 22.2 / 0.0 ms  last resort GC in old space requested


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 000003AA20425749 <JSObject>
    2: DoJoin(aka DoJoin) [native array.js:1] [bytecode=0000028C9AC0BED1 offset=227](this=00000198735022D1 <undefined>,o=00000340CF187079 <JSArray[5]>,p=5,D=0000019873502371 <true>,z=000003AA2043DAC9 <String[1]: ,>,y=00000198735023E1 <false>)
    3: Join(aka Join) [native array.js:1] [bytecode=0000028C9AC0B859 offset=71](this=00000198735022D1 <undefined>,o=00000340CF187079 <JSArray[5]>,p=5,z=0...

@kzc
Copy link
Contributor

kzc commented Nov 18, 2017

One of these?

var a=[];a[1e10]=1;console.log(a+"");

off-topic:

$ echo 'var a=[];a[9]=1;console.log(a);' | node421
[ , , , , , , , , , 1 ]

$ echo 'var a=[];a[9]=1;console.log(a);' | node690
[ , , , , , , , , , 1 ]

$ echo 'var a=[];a[9]=1;console.log(a);' | node800
[ <9 empty items>, 1 ]

@alexlamsl
Copy link
Collaborator Author

One of these?

That example works just fine - no errors is returned.

off-topic:

I guess that may upset expect_stdout - something to keep in mind 😉

@kzc
Copy link
Contributor

kzc commented Nov 18, 2017

That example works just fine - no errors is returned.

Just an example. This one does:

var a=[];a[1e9]=1;console.log(a+"");

@alexlamsl
Copy link
Collaborator Author

Ah, of course - and I can confirm this is new behaviour in Node.js 9:

$ nvs use node/6
PATH = nvs\node\6.12.0\x64

$ echo 'var a=[];a[1e9]=1;console.log(a+"");' | node
[stdin]:1
var a=[];a[1e9]=1;console.log(a+"");
                               ^

RangeError: Invalid string length
    at Array.toString (native)
    at [stdin]:1:32
    at ContextifyScript.Script.runInThisContext (vm.js:25:33)
    at Object.runInThisContext (vm.js:97:38)
    at Object.<anonymous> ([stdin]-wrapper:6:22)
    at Module._compile (module.js:570:32)
    at evalScript (bootstrap_node.js:347:27)
    at Socket.<anonymous> (bootstrap_node.js:186:13)
    at emitNone (events.js:91:20)
    at Socket.emit (events.js:185:7)
$ nvs use node/8
PATH = nvs\node\8.9.1\x64

$ echo 'var a=[];a[1e9]=1;console.log(a+"");' | node
[stdin]:1
var a=[];a[1e9]=1;console.log(a+"");
                               ^

RangeError: Invalid string length
    at Array.toString (native)
    at [stdin]:1:32
    at ContextifyScript.Script.runInThisContext (vm.js:50:33)
    at Object.runInThisContext (vm.js:139:38)
    at Object.<anonymous> ([stdin]-wrapper:6:22)
    at Module._compile (module.js:635:30)
    at evalScript (bootstrap_node.js:462:27)
    at Socket.<anonymous> (bootstrap_node.js:233:15)
    at emitNone (events.js:111:20)
    at Socket.emit (events.js:208:7)
$ nvs use node/9
PATH = nvs\node\9.2.0\x64

$ echo 'var a=[];a[1e9]=1;console.log(a+"");' | node

<--- Last few GCs --->

[7028:000002604FD76F10]      994 ms: Mark-sweep 957.6 (964.5) -> 957.6 (964.5) MB, 7.8 / 0.0 ms  allocation failure GC in old space requested
[7028:000002604FD76F10]      998 ms: Mark-sweep 957.6 (964.5) -> 957.6 (961.5) MB, 3.5 / 0.0 ms  last resort GC in old space requested
[7028:000002604FD76F10]     1002 ms: Mark-sweep 957.6 (961.5) -> 957.6 (961.5) MB, 4.4 / 0.0 ms  last resort GC in old space requested


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0000000ECF4A5749 <JSObject>
    1: createWriteReq(aka createWriteReq) [net.js:806] [bytecode=00000211CE4F1729 offset=111](this=000003DD20A022D1 <undefined>,req=00000211CE4F2651 <WriteWrap map = 000003D109045229>,handle=000003B615D70051 <TTY map = 000003D109041761>,data=00000211CE4F2369 <Very long string[1000000002]>,encoding=0000000ECF4B4E21 <String[4]: utf8>)
    2: _writeGeneric [net.js:769] [bytecode=00000211CE4F0DE1 ...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

... which is rather undesirable IMHO because it breaks the process. Let's see how bad this gets - we can always go back to Node.js 8 for test/ufuzz.js

@kzc
Copy link
Contributor

kzc commented Nov 18, 2017

The fact that var a=[];a[1e10]=1;console.log(a+""); doesn't produce an error of any kind and returns immediately is a bit curious.

@alexlamsl
Copy link
Collaborator Author

That's because 1e10 is larger than the valid integer range for array index:

$ node
> var a = [];
> a[1e10] = 1;
1
> a.length
0
> var a = [];
> a[1e9] = 1;
1
> a.length
1000000001
> 1e10 > (1 << 31)
true

@alexlamsl
Copy link
Collaborator Author

alexlamsl commented Nov 18, 2017

New stacktrace, but doesn't kill the node test/ufuzz.js process, so we are good:

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

{
    var brake1 = 5;
    while (--b + !b && --brake1 > 0) {
        var c_1 = {
            set NaN(parseInt_1) {
                try {
                    c = c + 1;
                } catch (Infinity) {
                } finally {
                    var parseInt_1_2 = parseInt_1 && parseInt_1.a, bar_1 = bar_1 && bar_1.length;
                }
                this.length += ((parseInt_1_2 && (parseInt_1_2[(c = c + 1) + (b + 1 - .1 - .1 - .1)] = (("object" >= 22) / (4 >> 22) == (-1 == 2 !== 25 + -5) && (bar_1 && (bar_1[--b + [ (c = 1 + c,
                {} > {} == ([ , 0 ].length === 2) << this & (1 > 23..toString()) - (c_1 && (c_1[(c = 1 + c,
                (2 % 25 | null >>> /[a2][^e]+$/) < ("", "object", false >> ([ , 0 ].length === 2)))] = -0 >>> 4))), (c = 1 + c,
                (("bar" ^ [ , 0 ].length === 2) >= undefined / false) - (parseInt_1_2 &= false / "undefined" - (2 !== "bar"))) ].length] = (parseInt_1 && (parseInt_1.foo = "undefined" & false)) <= [] >>> /[a2][^e]+$/) && this / 0 + ~([ , 0 ].length === 2))) > ((24..toString() || -2) > (4 & "")) / ((-2 >= ([ , 0 ].length === 2)) + (22 || 25)) >>> (-5 / 4 >>> ("function",
                4) || "undefined" / "function" !== -1 * this) <= ((parseInt_1 && (parseInt_1[parseInt_1_2] = -(22 % -2 != "bar" >= -4) - (("bar" ^ /[a2][^e]+$/) >= true >>> Infinity | !22 * (null,
                "number")))) & "" * -0 % (4 <= "undefined") != (24..toString() >> -5) + (5 & 24..toString()) != (3 < "undefined" & (25 & undefined)) >> ((23..toString() != false) > -0 + [])))) << (((23..toString() === ([ , 0 ].length === 2)) % (1 + "bar") & 3 << this == ("bar" & /[a2][^e]+$/) & ((true & -5 || "object" ^ this) && 5 - 4 >>> - -1)) <= ((parseInt_1 && (parseInt_1[1 === 1 ? a : b] += 0 === 2 & "object" >> "number")) <= (NaN >> -5 < ("undefined" == []))) >>> ((25 < 38..toString()) % ("foo" == true) << (-4 && 0) + (-5 + 3))) * (((("undefined" == this) <= (24..toString() <= "function")) * ((-3 | -1) ^ (bar_1 && (bar_1.undefined &= [] / null))) ^ (3 << -2 || true / -5 || (22 && -3) >>> (c = c + 1,
                [ , 0 ][1]))) << (!23..toString() != 0 << 1 | (-3 - [ , 0 ][1]) % (-0 & this)) - (({} != "function" || "foo" ^ "number") && (c_1 && (c_1[(c = 1 + c,
                c_1 && (c_1[(a++ + parseInt_1 || a || 3).toString()] += ("foo" || 22) <= (-0 | {}) !== (-3,
                [ , 0 ][1]) >= ("function" || "object")))] = [ , 0 ].length === 2 | -4)) <= {} << "object"))) + ((bar_1 && (bar_1.Infinity += (parseInt_1_2 && (parseInt_1_2.undefined = ((Infinity === 23..toString()) <= this * "") - ((-3 && Infinity) | (38..toString() && 5)) <= (c_1 += true > "function" != (-0 | -3) | (parseInt_1 += -2 >>> NaN) - "bar" % null))) & (1 - Infinity + ([ , 0 ].length === 2 & -0) ^ -(-5 != "object")) % (("foo" != "function") - +24..toString() ^ ("bar",
                23..toString()) + (2 >= false)))) >> ((c = c + 1, -4) & undefined <= NaN || (parseInt_1 = ~2 <= void "foo") || ((false && 22) << ("number" ^ 4)) - (24..toString() >> 24..toString() && false <= 3)) - (c = c + 1,
                c_1 && (c_1[(c = c + 1) + ((-1 ^ -3 ^ (1 || -1)) == 23..toString() + ([ , 0 ].length === 2) >> (c = c + 1,
                38..toString()))] += ((22 & 38..toString()) - (22 < true)) / (!"bar", 22 & 1))) ^ delete (((25 - "foo" ^ (0 && -3)) > (this - 25 >= (parseInt_1 && (parseInt_1[(c = 1 + c,
                (0 > 3 | 3 / Infinity) - ((3 != false) << ([ , 0 ][1] > -1)))] = 38..toString() ^ -4)))) * (((bar_1 += "foo" ^ "bar") & "undefined" > 23..toString()) / (parseInt_1 && (parseInt_1[!function bar_1() {
                }()] = -([ , 0 ].length === 2) && [ , 0 ].length === 2 === [ , 0 ][1]))) & ((("foo" ^ null) >>> (22 === {})) - (parseInt_1_2 += (parseInt_1 && (parseInt_1[(c = 1 + c,
                -4 + 4 | "number" * null, (23..toString() || -1) & -3 <= [])] = this - 1)) - (-3 | "foo")) ^ ((2 ^ "function") << ("object" >>> 38..toString())) - (null / 24..toString() ^ (c_1 && (c_1[(c = 1 + c,
                (c = c + 1, [] | /[a2][^e]+$/) << ({} >> true) + (-4 >= 23..toString()))] = {} >> []))))));
            },
            "": [ !function() {
                c = c + 1;
                return --b + typeof [][(c = 1 + c, (Infinity >>> -4) + (c_1 && (c_1.NaN <<= 25 != -4)) || (-1 >> true) - (0 >> 38..toString()))];
            }() ].b,
            Infinity: a++ + (c_1 && c_1.in),
            "\t": c_1 && c_1.undefined,
            c: --b + +(("undefined" && true) < (false !== NaN) & ((c_1 && (c_1.foo += 2 << NaN)) | -4 != 24..toString()))
        };
    }
}

var Math_1;

console.log(null, a, b, c);
original result:
evalmachine.<anonymous>:30
this.length+=(((parseInt_1_2 && (parseInt_1_2[((c = c + 1) + (b + 1-0.1-0.1-0.1))]=(((((("object">=22)/(4>>22))==(((-1)==2)!==(25.  + (-5))))&&((bar_1 && (bar_1[((--b) + ([(c = 1 + c, (((({})>({}))==(([,0].length === 2)<<this))&((1>23..toString()) - (c_1 && (c_1[(c = 1 + c, (((2%25. )|(null>>> /[a2][^e]+$/ ))<(("","object"),(false>>([,0].length === 2)))))]=(-0>>>4)))))), (c = 1 + c, ((("bar"^([,0].length === 2))>=(undefined/false)) - (parseInt_1_2&=((false/"undefined") - (2!=="bar"))))), ].length))]=((parseInt_1 && (parseInt_1.foo=("undefined"&false)))<=([]>>> /[a2][^e]+$/ ))))&&((this/0) + (~(([,0].length === 2))))))>((((24 .toString()||(-2))>(4&""))/(((-2)>=([,0].length === 2)) + (22||25. )))>>>((((-5)/4)>>>("function",4))||(("undefined"/"function")!==((-1)*this)))))<=((parseInt_1 && (parseInt_1[(parseInt_1_2)]=((-(((22%(-2))!=("bar">=(-4))))) - ((("bar"^ /[a2][^e]+$/ )>=(true>>>Infinity))|((!(22))*(null,"number"))))))&((((""*-0)%(4<="undefined"))!=((24 .toString()>>(-5)) + (5&2

RangeError: Maximum call stack size exceeded
    at RegExp.toString (<anonymous>)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1169)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)
    at Object.set NaN [as NaN] (evalmachine.<anonymous>:30:1878)

@kzc
Copy link
Contributor

kzc commented Nov 18, 2017

That's because 1e10 is larger than the valid integer range for array index:

Is that an official ECMAScript thing or a V8 array optimization? I thought it should have converted the integer index into a string as per the negative key below:

$ node
> var a = [9, 2];
undefined
> a.prop = 5;
5
> a
[ 9, 2, prop: 5 ]
> a[-23] = 4
4
> a
[ 9, 2, prop: 5, '-23': 4 ]

@alexlamsl
Copy link
Collaborator Author

Is that an official ECMAScript thing or a V8 array optimization?

Not sure, but IE11 behaves the same way with regards to a[1e9]=1 versus a[1e10]=1

@alexlamsl
Copy link
Collaborator Author

Also 1e10 does seem to act like -23 in your case above:

$ node
> var a = []
> a[1e10] = 1
1
> a
[ '10000000000': 1 ]

@kzc
Copy link
Contributor

kzc commented Nov 19, 2017

Not sure, but IE11 behaves the same way with regards to a[1e9]=1 versus a[1e10]=1

It's a pseudo standard. I think browsers copied the implementation of the original netscape engine and this array behavior has never been officially standardized.

$ node690
> var a = []
undefined
> a[1e10] = 1
1
> a.length
0
> a
[  ]
> a[1e9] = 1
1
> a.length
1000000001
> a + ""
RangeError: Invalid string length
    at Array.toString (native)

- give globally distinct names to distinct variables
- improve ability to compress cross-scoped
- introduce `options.rename` to `minify()`
- default `true` if both `compress` & `mangle`
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