Compare commits

...

47 Commits

Author SHA1 Message Date
Alex Lam S.L
6335b5fd8a v3.13.3 2021-03-29 06:58:06 +08:00
Alex Lam S.L
daa8319b8a fix corner cases with logical assignment operators (#4828)
fixes #4827
2021-03-28 03:44:45 +08:00
Alex Lam S.L
d5599604e8 enhance collapse_vars (#4826) 2021-03-27 22:14:37 +08:00
Alex Lam S.L
072933f1d5 diagnose GitHub Actions (#4825) 2021-03-25 18:44:39 +08:00
Alex Lam S.L
39df3a1680 fix corner case in functions (#4824)
fixes #4823
2021-03-25 08:49:01 +08:00
Alex Lam S.L
03c5ecb2e3 fix corner cases with class (#4822)
fixes #4821
2021-03-25 04:36:50 +08:00
Alex Lam S.L
40ef074cb3 fix corner case in comparisons (#4820)
fixes #4819
2021-03-24 10:10:02 +08:00
Alex Lam S.L
78e3936cd4 fix corner case in inline (#4818)
fixes #4817
2021-03-23 22:33:24 +08:00
Alex Lam S.L
e7be38b42a fix corner cases with logical assignment operators (#4816)
fixes #4815
2021-03-23 13:02:45 +08:00
Alex Lam S.L
44394e61c9 workaround toString() quirks on global context (#4814) 2021-03-23 11:15:41 +08:00
Alex Lam S.L
f9055df44d support logical assignment operators (#4813) 2021-03-23 04:59:43 +08:00
Alex Lam S.L
51bdb7281b improve global context enumeration under sandbox (#4812)
fixes #4811
2021-03-22 22:43:33 +08:00
Alex Lam S.L
9c01511f84 v3.13.2 2021-03-22 04:34:26 +08:00
Alex Lam S.L
9faee3b66a fix corner case in arguments (#4810)
fixes #4809
2021-03-21 10:40:32 +08:00
Alex Lam S.L
8ea1ced389 document various v8 bugs (#4808)
closes #4805
2021-03-21 06:33:45 +08:00
Alex Lam S.L
24619daf68 fix corner cases in collapse_vars & unused (#4807)
fixes #4806
2021-03-21 03:51:03 +08:00
Alex Lam S.L
b89cc84c3a fix corner case in pure_getters (#4804)
fixes #4803
2021-03-19 16:16:53 +08:00
Alex Lam S.L
3016a78d85 fix corner case in reduce_vars (#4802)
fixes #4801
2021-03-18 23:58:35 +08:00
Alex Lam S.L
2508481e33 fix corner case in evaluate (#4800)
fixes #4799
2021-03-18 11:31:20 +08:00
Alex Lam S.L
48c46fa9a7 fix corner case in sequences (#4798) 2021-03-18 08:31:55 +08:00
Alex Lam S.L
7da49b5709 fix corner case in collapse_vars (#4797) 2021-03-18 07:50:55 +08:00
Alex Lam S.L
d837a46ebd fix corner case in reduce_vars (#4796) 2021-03-18 05:14:14 +08:00
Alex Lam S.L
d4303b62cc build Bootstrap for verification testing (#4795) 2021-03-17 22:34:17 +08:00
Alex Lam S.L
7d595e2eac improve comment formatting logic (#4794) 2021-03-17 13:51:52 +08:00
Alex Lam S.L
9fc0ff5953 parse extended Unicode literal correctly (#4792) 2021-03-17 10:34:50 +08:00
Alex Lam S.L
997d09bb33 extend keep_fnames to classes (#4793) 2021-03-17 09:28:27 +08:00
Alex Lam S.L
b244b4ec21 enhance "functions" (#4791) 2021-03-17 05:38:33 +08:00
Alex Lam S.L
b872ffee01 fix corner case in hoist_funs (#4790) 2021-03-17 01:48:23 +08:00
Alex Lam S.L
9a9543013c fix corner case in functions (#4789)
fixes #4788
2021-03-16 20:58:51 +08:00
Alex Lam S.L
b98ce6c84f avoid flaky cases in verification testing (#4785) 2021-03-16 20:50:33 +08:00
Alex Lam S.L
7b43b6396f ensure valid generated cases in --reduce-test (#4787)
closes #4786
2021-03-16 18:14:24 +08:00
Alex Lam S.L
67f8fcb103 build web-tooling-benchmark for verification testing (#4776) 2021-03-16 17:34:10 +08:00
Alex Lam S.L
352a944868 fix corner cases with new.target (#4784) 2021-03-16 14:34:36 +08:00
Alex Lam S.L
77c9116c91 fix corner case in unsafe (#4783) 2021-03-16 09:42:28 +08:00
Alex Lam S.L
e821787095 fix corner case in reduce_vars (#4782) 2021-03-16 08:05:01 +08:00
Alex Lam S.L
aa6e33e208 parse out-of-range numerals correctly (#4781) 2021-03-16 06:52:21 +08:00
Alex Lam S.L
176581d732 fix corner cases with template literals (#4780) 2021-03-15 21:54:05 +08:00
Alex Lam S.L
01aa078e9c support **= (#4778) 2021-03-15 21:53:48 +08:00
Alex Lam S.L
149d75c092 fix corner case in unsafe (#4779) 2021-03-15 17:51:32 +08:00
Alex Lam S.L
2619bff3cf fix corner case in conditionals (#4777) 2021-03-15 12:44:41 +08:00
Alex Lam S.L
4fb54b066f improve Node.js setup on GitHub Actions (#4775) 2021-03-15 06:25:33 +08:00
Alex Lam S.L
e124ef57e3 parse parentheses-terminated statements correctly (#4774)
fixes #4772
2021-03-14 14:09:29 +08:00
Alex Lam S.L
73e6b2550b fix corner cases with yield (#4771)
fixes #4769
2021-03-14 04:39:30 +08:00
Alex Lam S.L
241113200e support shorthand key-symbol output (#4768) 2021-03-13 15:37:01 +08:00
Alex Lam S.L
6f3ab09319 fix corner case in unused (#4767)
fixes #4766
2021-03-13 15:09:07 +08:00
Alex Lam S.L
3b5d5014e0 implement annotations (#4763) 2021-03-13 03:40:49 +08:00
Alex Lam S.L
c36c3cb470 fix corner case in side_effects (#4765)
fixes #4764
2021-03-13 02:40:28 +08:00
61 changed files with 2960 additions and 1061 deletions

View File

@@ -8,11 +8,22 @@ jobs:
strategy:
fail-fast: false
matrix:
options: [ '-mb braces', '-mc', '--toplevel -mc passes=10,pure_getters,unsafe' ]
script: [ acorn.sh, buble.sh, butternut.sh, mathjs.sh, rollup-es.sh, rollup-ts.sh, sucrase.sh ]
options: [ '-mb braces', '--ie8 -c', '-mc', '--toplevel -mc passes=3,pure_getters,unsafe' ]
script:
- acorn.sh
- bootstrap.sh
- buble.sh
- butternut.sh
- mathjs.sh
- rollup-es.sh
- rollup-ts.sh
- sucrase.sh
- web-tooling-benchmark.sh
include:
- node: '14'
script: acorn.sh
- node: '14'
script: bootstrap.sh
- node: '14'
script: buble.sh
- node: '14'
@@ -25,6 +36,8 @@ jobs:
script: rollup-ts.sh
- node: '14'
script: sucrase.sh
- node: '14'
script: web-tooling-benchmark.sh
name: ${{ matrix.script }} ${{ matrix.options }}
runs-on: ubuntu-latest
env:
@@ -36,20 +49,5 @@ jobs:
- name: Perform uglify, build & test
shell: bash
run: |
git clone --branch v1.6.0 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version
nvs use $NODE
node --version
npm config set audit false
npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
. ./test/release/install.sh
./test/release/$SCRIPT $OPTIONS

View File

@@ -29,20 +29,5 @@ jobs:
- name: Perform tests
shell: bash
run: |
git clone --branch v1.6.0 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version
nvs use $NODE
node --version
npm config set audit false
npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
. ./test/release/install.sh
node test/$TYPE

View File

@@ -30,33 +30,10 @@ jobs:
NODE: ${{ matrix.node }}
steps:
- uses: actions/checkout@v2
- name: Install GNU Core Utilities
if: ${{ startsWith(matrix.os, 'macos') }}
env:
HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
shell: bash
run: |
while !(brew install coreutils); do echo "'brew install' failed - retrying..."; done
- name: Perform fuzzing
shell: bash
run: |
git clone --branch v1.6.0 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version
nvs use $NODE
node --version
npm config set audit false
npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
. ./test/release/install.sh
if [[ $CAUSE == "schedule" ]]; then
node test/ufuzz/job $BASE_URL $TOKEN $RUN_NUM
else

256
README.md
View File

@@ -92,6 +92,9 @@ a double dash to prevent input files being used as option arguments:
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
`spidermonkey` to write UglifyJS or SpiderMonkey AST
as JSON to STDOUT respectively.
--annotations Process and preserve comment annotations.
(`/*@__PURE__*/` or `/*#__PURE__*/`)
--no-annotations Ignore and discard comment annotations.
--comments [filter] Preserve copyright comments in the output. By
default this works like Google Closure, keeping
JSDoc-style comments that contain "@license" or
@@ -221,10 +224,10 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or
- `eval` (default: `false`) mangle names visible in scopes where `eval` or
`with` are used.
- `reserved` (default: `[]`) -- when mangling is enabled but you want to
- `reserved` (default: `[]`) when mangling is enabled but you want to
prevent certain names from being mangled, you can declare those names with
`--mangle reserved` — pass a comma-separated list of names. For example:
@@ -491,46 +494,51 @@ if (result.error) throw result.error;
## Minify options
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
- `annotations` — pass `false` to ignore all comment annotations and elide them
from output. Useful when, for instance, external tools incorrectly applied
`/*@__PURE__*/` or `/*#__PURE__*/`. Pass `true` to both compress and retain
comment annotations in output to allow for further processing downstream.
- `compress` (default: `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compress options](#compress-options).
- `ie8` (default `false`) -- set to `true` to support IE8.
- `ie8` (default: `false`) set to `true` to support IE8.
- `keep_fnames` (default: `false`) -- pass `true` to prevent discarding or mangling
- `keep_fnames` (default: `false`) pass `true` to prevent discarding or mangling
of function names. Useful for code relying on `Function.prototype.name`.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
- `mangle` (default: `true`) — pass `false` to skip mangling names, or pass
an object to specify [mangle options](#mangle-options) (see below).
- `mangle.properties` (default `false`) — a subcategory of the mangle option.
- `mangle.properties` (default: `false`) — a subcategory of the mangle option.
Pass an object to specify custom [mangle property options](#mangle-properties-options).
- `nameCache` (default `null`) -- pass an empty object `{}` or a previously
- `nameCache` (default: `null`) pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `output` (default `null`) — pass an object if you wish to specify
- `output` (default: `null`) — pass an object if you wish to specify
additional [output options](#output-options). The defaults are optimized
for best compression.
- `parse` (default `{}`) — pass an object if you wish to specify some
- `parse` (default: `{}`) — pass an object if you wish to specify some
additional [parse options](#parse-options).
- `sourceMap` (default `false`) -- pass an object if you wish to specify
- `sourceMap` (default: `false`) pass an object if you wish to specify
[source map options](#source-map-options).
- `toplevel` (default `false`) -- set to `true` if you wish to enable top level
- `toplevel` (default: `false`) set to `true` if you wish to enable top level
variable and function name mangling and to drop unused variables and functions.
- `v8` (default `false`) -- enable workarounds for Chrome & Node.js bugs.
- `v8` (default: `false`) enable workarounds for Chrome & Node.js bugs.
- `warnings` (default `false`) — pass `true` to return compressor warnings
- `warnings` (default: `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
- `webkit` (default `false`) -- enable workarounds for Safari/WebKit bugs.
- `webkit` (default: `false`) enable workarounds for Safari/WebKit bugs.
PhantomJS users should set this option to `true`.
## Minify options structure
@@ -615,116 +623,121 @@ to be `false` and all symbol names will be omitted.
## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements
- `bare_returns` (default: `false`) support top level `return` statements
- `html5_comments` (default `true`)
- `html5_comments` (default: `true`)
- `shebang` (default `true`) -- support `#!command` as the first line
- `shebang` (default: `true`) support `#!command` as the first line
## Compress options
- `arguments` (default: `true`) -- replace `arguments[index]` with function
- `annotations` (default: `true`) — Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `arguments` (default: `true`) — replace `arguments[index]` with function
parameter name whenever possible.
- `arrows` (default: `true`) -- apply optimizations to arrow functions
- `arrows` (default: `true`) apply optimizations to arrow functions
- `assignments` (default: `true`) -- apply optimizations to assignment expressions
- `assignments` (default: `true`) apply optimizations to assignment expressions
- `awaits` (default: `true`) -- apply optimizations to `await` expressions
- `awaits` (default: `true`) apply optimizations to `await` expressions
- `booleans` (default: `true`) -- various optimizations for boolean context,
- `booleans` (default: `true`) various optimizations for boolean context,
for example `!!a ? b : c → a ? b : c`
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables,
- `collapse_vars` (default: `true`) Collapse single-use non-constant variables,
side effects permitting.
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
- `comparisons` (default: `true`) apply certain optimizations to binary nodes,
e.g. `!(a <= b) → a > b`, attempts to negate binary nodes, e.g.
`a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
- `conditionals` (default: `true`) apply optimizations for `if`-s and conditional
expressions
- `dead_code` (default: `true`) -- remove unreachable code
- `dead_code` (default: `true`) remove unreachable code
- `default_values` (default: `true`) -- drop overshadowed default values
- `default_values` (default: `true`) drop overshadowed default values
- `directives` (default: `true`) -- remove redundant or non-standard directives
- `directives` (default: `true`) remove redundant or non-standard directives
- `drop_console` (default: `false`) -- Pass `true` to discard calls to
- `drop_console` (default: `false`) Pass `true` to discard calls to
`console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `drop_debugger` (default: `true`) remove `debugger;` statements
- `evaluate` (default: `true`) -- Evaluate expression for shorter constant
- `evaluate` (default: `true`) Evaluate expression for shorter constant
representation. Pass `"eager"` to always replace function calls whenever
possible, or a positive integer to specify an upper bound for each individual
evaluation in number of characters.
- `expression` (default: `false`) -- Pass `true` to preserve completion values
- `expression` (default: `false`) Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `functions` (default: `true`) -- convert declarations from `var` to `function`
- `functions` (default: `true`) convert declarations from `var` to `function`
whenever possible.
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `global_defs` (default: `{}`) see [conditional compilation](#conditional-compilation)
- `hoist_exports` (default: `true`) -- hoist `export` statements to facilitate
- `hoist_exports` (default: `true`) hoist `export` statements to facilitate
various `compress` and `mangle` optimizations.
- `hoist_funs` (default: `false`) -- hoist function declarations
- `hoist_funs` (default: `false`) hoist function declarations
- `hoist_props` (default: `true`) -- hoist properties from constant object and
- `hoist_props` (default: `true`) hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `toplevel` and `mangle` enabled, alongside with `compress` option
`passes` set to `2` or higher.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
- `hoist_vars` (default: `false`) hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `if_return` (default: `true`) optimizations for if/return and if/continue
- `imports` (default: `true`) -- drop unreferenced import symbols when used with `unused`
- `imports` (default: `true`) drop unreferenced import symbols when used with `unused`
- `inline` (default: `true`) -- inline calls to function with simple/`return` statement:
- `false` -- same as `0`
- `0` -- disabled inlining
- `1` -- inline simple functions
- `2` -- inline functions with arguments
- `3` -- inline functions with arguments and variables
- `true` -- same as `3`
- `inline` (default: `true`) inline calls to function with simple/`return` statement:
- `false` same as `0`
- `0` disabled inlining
- `1` inline simple functions
- `2` inline functions with arguments
- `3` inline functions with arguments and variables
- `true` same as `3`
- `join_vars` (default: `true`) -- join consecutive `var` statements
- `join_vars` (default: `true`) join consecutive `var` statements
- `keep_fargs` (default: `false`) -- discard unused function arguments except
- `keep_fargs` (default: `false`) discard unused function arguments except
when unsafe to do so, e.g. code which relies on `Function.prototype.length`.
Pass `true` to always retain function arguments.
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
- `keep_infinity` (default: `false`) Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
- `loops` (default: `true`) optimizations for `do`, `while` and `for` loops
when we can statically determine the condition.
- `merge_vars` (default: `true`) -- combine and reuse variables.
- `merge_vars` (default: `true`) combine and reuse variables.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
- `negate_iife` (default: `true`) negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.
- `objects` (default: `true`) -- compact duplicate keys in object literals.
- `objects` (default: `true`) compact duplicate keys in object literals.
- `passes` (default: `1`) -- The maximum number of times to run compress.
- `passes` (default: `1`) The maximum number of times to run compress.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `properties` (default: `true`) -- rewrite property access using the dot notation, for
- `properties` (default: `true`) rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `pure_funcs` (default: `null`) -- You can pass an array of names and
- `pure_funcs` (default: `null`) You can pass an array of names and
UglifyJS will assume that those functions do not produce side
effects. DANGER: will not check if the name is redefined in scope.
An example case here, for instance `var q = Math.floor(a/b)`. If
@@ -736,24 +749,24 @@ to be `false` and all symbol names will be omitted.
overhead (compression will be slower). Make sure symbols under `pure_funcs`
are also under `mangle.reserved` to avoid mangling.
- `pure_getters` (default: `"strict"`) -- If you pass `true` for
- `pure_getters` (default: `"strict"`) If you pass `true` for
this, UglifyJS will assume that object property access
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `reduce_funcs` (default: `true`) -- Allows single-use functions to be
- `reduce_funcs` (default: `true`) Allows single-use functions to be
inlined as function expressions when permissible allowing further
optimization. Enabled by default. Option depends on `reduce_vars`
being enabled. Some code runs faster in the Chrome V8 engine if this
option is disabled. Does not negatively impact other major browsers.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
- `reduce_vars` (default: `true`) Improve optimization on variables assigned with and
used as constant values.
- `rests` (default: `true`) -- apply optimizations to rest parameters
- `rests` (default: `true`) apply optimizations to rest parameters
- `sequences` (default: `true`) -- join consecutive simple statements using the
- `sequences` (default: `true`) join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
@@ -762,70 +775,68 @@ to be `false` and all symbol names will be omitted.
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `side_effects` (default: `true`) -- Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `side_effects` (default: `true`) — drop extraneous code which does not affect
outcome of runtime execution.
- `spreads` (default: `true`) -- flatten spread expressions.
- `spreads` (default: `true`) flatten spread expressions.
- `strings` (default: `true`) -- compact string concatenations.
- `strings` (default: `true`) compact string concatenations.
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `switches` (default: `true`) de-duplicate and remove unreachable `switch` branches
- `templates` (default: `true`) -- compact template literals by embedding expressions
- `templates` (default: `true`) compact template literals by embedding expressions
and/or converting to string literals, e.g. `` `foo ${42}` → "foo 42"``
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
- `top_retain` (default: `null`) prevent specific toplevel functions and
variables from `unused` removal (can be array, comma-separated, RegExp or
function. Implies `toplevel`)
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
- `toplevel` (default: `false`) drop unreferenced functions (`"funcs"`) and/or
variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
both unreferenced functions and variables)
- `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
- `typeofs` (default: `true`) Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues.
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe` (default: `false`) apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) -- compress expressions like `a <= b` assuming
- `unsafe_comps` (default: `false`) compress expressions like `a <= b` assuming
none of the operands can be (coerced to) `NaN`.
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
- `unsafe_Function` (default: `false`) compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: `false`) -- optimize numerical expressions like
- `unsafe_math` (default: `false`) optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: `false`) -- optimize expressions like
- `unsafe_proto` (default: `false`) optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
- `unsafe_regexp` (default: `false`) enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `unsafe_undefined` (default: `false`) -- substitute `void 0` if there is a
- `unsafe_undefined` (default: `false`) substitute `void 0` if there is a
variable named `undefined` in scope (variable name will be mangled, typically
reduced to a single character)
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
- `unused` (default: `true`) drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`)
- `varify` (default: `true`) -- convert block-scoped declaractions into `var`
- `varify` (default: `true`) convert block-scoped declaractions into `var`
whenever safe to do so
- `yields` (default: `true`) -- apply optimizations to `yield` expressions
- `yields` (default: `true`) apply optimizations to `yield` expressions
## Mangle options
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
- `eval` (default: `false`) Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
- `reserved` (default `[]`) -- Pass an array of identifiers that should be
- `reserved` (default: `[]`) Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` (default `false`) -- Pass `true` to mangle names declared in the
- `toplevel` (default: `false`) Pass `true` to mangle names declared in the
top level scope.
Examples:
@@ -852,18 +863,18 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
- `builtins` (default: `false`) Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
- `debug` (default: `false`) -— Mangle names with the original name still present.
- `debug` (default: `false`) — Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `keep_quoted` (default: `false`) — Only mangle unquoted property names.
- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
- `regex` (default: `null`) — Pass a RegExp literal to only mangle property
names matching the regular expression.
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
- `reserved` (default: `[]`) Do not mangle property names listed in the
`reserved` array.
## Output options
@@ -872,19 +883,23 @@ The code generator tries to output shortest code possible by default. In
case you want beautified output, pass `--beautify` (`-b`). Optionally you
can pass additional arguments that control the code output:
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
- `annotations` (default: `false`) — pass `true` to retain comment annotations
`/*@__PURE__*/` or `/*#__PURE__*/`, otherwise they will be discarded even if
`comments` is set.
- `ascii_only` (default: `false`) — escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `beautify` (default `true`) -- whether to actually beautify the output.
- `beautify` (default: `true`) whether to actually beautify the output.
Passing `-b` will set this to true, but you might need to pass `-b` even
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `braces` (default `false`) -- always insert braces in `if`, `for`,
- `braces` (default: `false`) always insert braces in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `comments` (default `false`) -- pass `true` or `"all"` to preserve all
- `comments` (default: `false`) pass `true` or `"all"` to preserve all
comments, `"some"` to preserve multi-line comments that contain `@cc_on`,
`@license`, or `@preserve` (case-insensitive), a regular expression string
(e.g. `/^!/`), or a function which returns `boolean`, e.g.
@@ -894,53 +909,53 @@ can pass additional arguments that control the code output:
}
```
- `galio` (default `false`) -- enable workarounds for ANT Galio bugs
- `galio` (default: `false`) enable workarounds for ANT Galio bugs
- `indent_level` (default `4`)
- `indent_level` (default: `4`)
- `indent_start` (default `0`) -- prefix all lines by that many spaces
- `indent_start` (default: `0`) prefix all lines by that many spaces
- `inline_script` (default `true`) -- escape HTML comments and the slash in
- `inline_script` (default: `true`) escape HTML comments and the slash in
occurrences of `</script>` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
- `keep_quoted_props` (default: `false`) when turned on, prevents stripping
quotes from property names in object literals.
- `max_line_len` (default `false`) -- maximum line length (for uglified code)
- `max_line_len` (default: `false`) maximum line length (for uglified code)
- `preamble` (default `null`) -- when passed it must be a string and
- `preamble` (default: `null`) when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `preserve_line` (default `false`) -- pass `true` to retain line numbering on
- `preserve_line` (default: `false`) pass `true` to retain line numbering on
a best effort basis.
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
- `quote_keys` (default: `false`) pass `true` to quote all keys in literal
objects
- `quote_style` (default `0`) -- preferred quote style for strings (affects
- `quote_style` (default: `0`) preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
- `0` prefers double quotes, switches to single quotes when there are
more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `1` always use single quotes
- `2` always use double quotes
- `3` always use the original quotes
- `semicolons` (default `true`) -- separate statements with semicolons. If
- `semicolons` (default: `true`) separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `shebang` (default: `true`) preserve shebang `#!` in preamble (bash scripts)
- `width` (default `80`) -- only takes effect when beautification is on, this
- `width` (default: `80`) only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
- `wrap_iife` (default: `false`) pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS/issues/640) for more details.
@@ -1281,3 +1296,16 @@ To allow for better optimizations, the compiler makes various assumptions:
// SyntaxError: Identifier 'e' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
console.log({
...{
set 42(v) {},
42: "PASS",
},
});
// Expected: { '42': 'PASS' }
// Actual: { '42': undefined }
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -95,6 +95,8 @@ function process_option(name, no_value) {
" -b, --beautify [options] Beautify output/specify output options.",
" -O, --output-opts <options> Output options (beautify disabled).",
" -o, --output <file> Output file (default STDOUT).",
" --annotations Process and preserve comment annotations.",
" --no-annotations Ignore and discard comment annotations.",
" --comments [filter] Preserve copyright comments in the output.",
" --config-file <file> Read minify() options from JSON file.",
" -d, --define <expr>[=value] Global definitions.",
@@ -142,6 +144,7 @@ function process_option(name, no_value) {
case "enclose":
options[name] = read_value();
break;
case "annotations":
case "ie8":
case "timings":
case "toplevel":
@@ -149,6 +152,9 @@ function process_option(name, no_value) {
case "webkit":
options[name] = true;
break;
case "no-annotations":
options.annotations = false;
break;
case "keep-fnames":
options.keep_fnames = true;
break;

View File

@@ -55,9 +55,10 @@ function DEFNODE(type, props, methods, base) {
props.forEach(function(prop) {
code.push("this.", prop, "=props.", prop, ";");
});
code.push("}");
var proto = base && new base;
if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();");
code.push("}}");
code.push("}");
var ctor = new Function(code.join(""))();
if (proto) {
ctor.prototype = proto;
@@ -1818,6 +1819,9 @@ var AST_This = DEFNODE("This", null, {
var AST_NewTarget = DEFNODE("NewTarget", null, {
$documentation: "The `new.target` symbol",
initialize: function() {
this.name = "new.target";
},
_validate: function() {
if (this.name !== "new.target") throw new Error('name must be "new.target": ' + this.name);
},

View File

@@ -48,6 +48,7 @@ function Compressor(options, false_by_default) {
return new Compressor(options, false_by_default);
TreeTransformer.call(this, this.before, this.after);
this.options = defaults(options, {
annotations : !false_by_default,
arguments : !false_by_default,
arrows : !false_by_default,
assignments : !false_by_default,
@@ -462,6 +463,7 @@ merge(Compressor.prototype, {
})) {
tw.defun_ids[def.id] = false;
}
def.reassigned = 0;
def.recursive_refs = 0;
def.references = [];
def.should_replace = undefined;
@@ -623,6 +625,7 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Binary) return lazy_op[parent.operator];
if (parent instanceof AST_Conditional) return parent.condition !== node;
if (parent instanceof AST_Sequence) return parent.tail_node() === node;
if (parent instanceof AST_Spread) return true;
}
function mark_escaped(tw, d, scope, node, value, level, depth) {
@@ -660,7 +663,7 @@ merge(Compressor.prototype, {
var key = node.property;
if (key.is_constant()) key = key.value;
if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
def.reassigned = true;
def.reassigned++;
(key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
});
@@ -811,14 +814,91 @@ merge(Compressor.prototype, {
def(AST_Assign, function(tw, descend, compressor) {
var node = this;
var left = node.left;
if (node.operator == "=" && left.equivalent_to(node.right) && !left.has_side_effects(compressor)) {
node.right.walk(tw);
walk_prop(left);
node.__drop = true;
} else if (!(left instanceof AST_Destructured || left instanceof AST_SymbolRef)) {
var scan = left instanceof AST_Destructured || left instanceof AST_SymbolRef;
switch (node.operator) {
case "=":
if (left.equivalent_to(node.right) && !left.has_side_effects(compressor)) {
node.right.walk(tw);
walk_prop(left);
node.__drop = true;
return true;
}
if (scan) {
walk_assign();
return true;
}
mark_assignment_to_arguments(left);
return;
} else if (node.operator == "=") {
case "&&=":
case "||=":
case "??=":
left.walk(tw);
push(tw);
if (scan) {
walk_assign();
} else {
mark_assignment_to_arguments(left);
node.right.walk(tw);
}
pop(tw);
return true;
default:
if (!scan) {
mark_assignment_to_arguments(left);
return;
}
var d = left.definition();
d.assignments++;
var fixed = d.fixed;
if (is_modified(compressor, tw, node, node, 0)) {
d.fixed = false;
return;
}
var safe = safe_to_read(tw, d);
node.right.walk(tw);
if (safe && !left.in_arg && safe_to_assign(tw, d)) {
push_ref(d, left);
mark(tw, d);
if (d.single_use) d.single_use = false;
left.fixed = d.fixed = function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: make_ref(left, fixed),
right: node.right
});
};
left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
left.fixed.assigns.push(node);
} else {
left.walk(tw);
d.fixed = false;
}
return true;
}
function walk_prop(lhs) {
if (lhs instanceof AST_Dot) {
walk_prop(lhs.expression);
} else if (lhs instanceof AST_Sub) {
walk_prop(lhs.expression);
lhs.property.walk(tw);
} else if (lhs instanceof AST_SymbolRef) {
var d = lhs.definition();
push_ref(d, lhs);
if (d.fixed) {
lhs.fixed = d.fixed;
if (lhs.fixed.assigns) {
lhs.fixed.assigns.push(node);
} else {
lhs.fixed.assigns = [ node ];
}
}
} else {
lhs.walk(tw);
}
}
function walk_assign() {
node.right.walk(tw);
scan_declaration(tw, compressor, left, function() {
return node.right;
@@ -846,56 +926,6 @@ merge(Compressor.prototype, {
d.fixed = false;
}
});
} else {
var d = left.definition();
d.assignments++;
var fixed = d.fixed;
if (is_modified(compressor, tw, node, node, 0)) {
d.fixed = false;
return;
}
var safe = safe_to_read(tw, d);
node.right.walk(tw);
if (safe && !left.in_arg && safe_to_assign(tw, d)) {
push_ref(d, left);
mark(tw, d);
if (d.single_use) d.single_use = false;
left.fixed = d.fixed = function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: make_ref(left, fixed),
right: node.right
});
};
left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
left.fixed.assigns.push(node);
} else {
left.walk(tw);
d.fixed = false;
}
}
return true;
function walk_prop(lhs) {
if (lhs instanceof AST_Dot) {
walk_prop(lhs.expression);
} else if (lhs instanceof AST_Sub) {
walk_prop(lhs.expression);
lhs.property.walk(tw);
} else if (lhs instanceof AST_SymbolRef) {
var d = lhs.definition();
push_ref(d, lhs);
if (d.fixed) {
lhs.fixed = d.fixed;
if (lhs.fixed.assigns) {
lhs.fixed.assigns.push(node);
} else {
lhs.fixed.assigns = [ node ];
}
}
} else {
lhs.walk(tw);
}
}
});
def(AST_Binary, function(tw) {
@@ -915,7 +945,7 @@ merge(Compressor.prototype, {
tw.find_parent(AST_Scope).may_call_this();
var exp = this.expression;
if (exp instanceof AST_LambdaExpression) {
var iife = !exp.name;
var iife = is_iife_single(this);
this.args.forEach(function(arg) {
arg.walk(tw);
if (arg instanceof AST_Spread) iife = false;
@@ -968,7 +998,7 @@ merge(Compressor.prototype, {
if (prop.key instanceof AST_Node) prop.key.walk(tw);
return prop.value;
}).forEach(function(prop) {
if (prop.static) {
if (prop.static && (prop.value instanceof AST_Lambda || !prop.value.contains_this())) {
prop.value.walk(tw);
} else {
push(tw);
@@ -1427,6 +1457,7 @@ merge(Compressor.prototype, {
}
}
var identifier_atom = makePredicate("Infinity NaN undefined");
function is_lhs_read_only(lhs, compressor) {
if (lhs instanceof AST_ObjectIdentity) return true;
if (lhs instanceof AST_PropAccess) {
@@ -1510,6 +1541,8 @@ merge(Compressor.prototype, {
var wrap = false;
if (parent.TYPE == "Call") {
wrap = parent.expression === orig && needs_unbinding(compressor, val);
} else if (parent instanceof AST_Template) {
wrap = parent.tag === orig && needs_unbinding(compressor, val);
} else if (parent instanceof AST_UnaryPrefix) {
wrap = parent.operator == "delete"
|| parent.operator == "typeof" && is_undeclared_ref(val);
@@ -1588,6 +1621,19 @@ merge(Compressor.prototype, {
return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
}
function is_iife_single(call) {
var exp = call.expression;
if (exp.name) return false;
if (!(call instanceof AST_New)) return true;
var found = false;
exp.walk(new TreeWalker(function(node) {
if (found) return true;
if (node instanceof AST_NewTarget) return found = true;
if (node instanceof AST_Scope && node !== exp) return true;
}));
return !found;
}
function is_undeclared_ref(node) {
return node instanceof AST_SymbolRef && node.definition().undeclared;
}
@@ -1599,13 +1645,6 @@ merge(Compressor.prototype, {
|| compressor.option("unsafe") && global_names[this.name];
});
var identifier_atom = makePredicate("Infinity NaN undefined");
function is_identifier_atom(node) {
return node instanceof AST_Infinity
|| node instanceof AST_NaN
|| node instanceof AST_Undefined;
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
@@ -1787,6 +1826,18 @@ merge(Compressor.prototype, {
can_replace = replace;
return signal_abort(node);
}
// Scan but don't replace inside block scope with colliding variable
if (node instanceof AST_BlockScope
&& !(node instanceof AST_Scope)
&& !(node.variables && node.variables.all(function(def) {
return !lvalues.has(def.name);
}))) {
var replace = can_replace;
can_replace = false;
if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
can_replace = replace;
return signal_abort(node);
}
return handle_custom_scan_order(node, scanner);
}, signal_abort);
var multi_replacer = new TreeTransformer(function(node) {
@@ -1925,13 +1976,6 @@ merge(Compressor.prototype, {
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
// Stop upon collision with block-scoped variables
if (!(node.variables && node.variables.all(function(def) {
return !lvalues.has(def.name);
}))) {
abort = true;
return node;
}
// Scan object only in a for-in/of statement
if (node instanceof AST_ForEnumeration) {
node.object = node.object.transform(tt);
@@ -1967,7 +2011,9 @@ merge(Compressor.prototype, {
function should_stop(node, parent) {
if (node === rvalue) return true;
if (parent instanceof AST_For) return node !== parent.init;
if (parent instanceof AST_For) {
if (node !== parent.init) return true;
}
if (node instanceof AST_Assign) {
return node.operator != "=" && lhs.equivalent_to(node.left);
}
@@ -2002,7 +2048,8 @@ merge(Compressor.prototype, {
}
function in_conditional(node, parent) {
if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
if (parent instanceof AST_Case) return parent.expression !== node;
if (parent instanceof AST_Conditional) return parent.condition !== node;
return parent instanceof AST_If && parent.condition !== node;
@@ -2124,11 +2171,11 @@ merge(Compressor.prototype, {
var iife, fn = compressor.self();
if (fn instanceof AST_LambdaExpression
&& !is_generator(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.pinned()
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn
&& is_iife_single(iife)
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Spread);
})) {
@@ -2344,21 +2391,36 @@ merge(Compressor.prototype, {
return null;
}
function find_stop_logical(parent, op, level) {
var node;
do {
node = parent;
parent = scanner.parent(++level);
} while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op
|| parent instanceof AST_Binary && parent.operator == op);
return node;
}
function find_stop_value(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
if (parent instanceof AST_Assign) return may_throw(parent) || parent.left.match_symbol(function(ref) {
return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
}) ? node : find_stop_value(parent, level + 1);
if (parent instanceof AST_Binary) {
if (lazy_op[parent.operator] && parent.left !== node) {
do {
node = parent;
parent = scanner.parent(++level);
} while (parent instanceof AST_Binary && parent.operator == node.operator);
return node;
if (parent instanceof AST_Assign) {
if (may_throw(parent)) return node;
if (parent.left.match_symbol(function(ref) {
return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
})) return node;
var op;
if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) {
return find_stop_value(parent, level + 1);
}
return find_stop_value(parent, level + 1);
return find_stop_logical(parent, op, level);
}
if (parent instanceof AST_Binary) {
var op;
if (parent.left === node || !lazy_op[op = parent.operator]) {
return find_stop_value(parent, level + 1);
}
return find_stop_logical(parent, op, level);
}
if (parent instanceof AST_Call) return parent;
if (parent instanceof AST_Case) {
@@ -2411,6 +2473,7 @@ merge(Compressor.prototype, {
if (parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1);
return null;
}
@@ -2689,8 +2752,8 @@ merge(Compressor.prototype, {
function patch_sequence(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
case 0: return null;
case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]);
}
}
@@ -3589,14 +3652,7 @@ merge(Compressor.prototype, {
return true;
});
def(AST_Binary, function(compressor) {
switch (this.operator) {
case "&&":
return this.left._dot_throw(compressor) || this.right._dot_throw(compressor);
case "||":
return this.right._dot_throw(compressor);
default:
return false;
}
return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
});
def(AST_Class, return_false);
def(AST_Conditional, function(compressor) {
@@ -3653,20 +3709,25 @@ merge(Compressor.prototype, {
(function(def) {
def(AST_Node, return_false);
def(AST_Array, return_true);
def(AST_Assign, function(compressor) {
return this.operator != "=" || this.right.is_defined(compressor);
});
def(AST_Binary, function(compressor) {
switch (this.operator) {
function is_binary_defined(compressor, op, node) {
switch (op) {
case "&&":
return this.left.is_defined(compressor) && this.right.is_defined(compressor);
return node.left.is_defined(compressor) && node.right.is_defined(compressor);
case "||":
return this.left.is_truthy() || this.right.is_defined(compressor);
return node.left.is_truthy() || node.right.is_defined(compressor);
case "??":
return this.left.is_defined(compressor) || this.right.is_defined(compressor);
return node.left.is_defined(compressor) || node.right.is_defined(compressor);
default:
return true;
}
}
def(AST_Assign, function(compressor) {
var op = this.operator;
if (op == "=") return this.right.is_defined(compressor);
return is_binary_defined(compressor, op.slice(0, -1), this);
});
def(AST_Binary, function(compressor) {
return is_binary_defined(compressor, this.operator, this);
});
def(AST_Conditional, function(compressor) {
return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
@@ -4725,7 +4786,7 @@ merge(Compressor.prototype, {
|| expr.expression.name == "Math" && expr.property == "random");
}
}
return this.pure || !compressor.pure_funcs(this);
return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
});
AST_Node.DEFMETHOD("is_call_pure", return_false);
AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
@@ -5279,8 +5340,9 @@ merge(Compressor.prototype, {
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Assign) {
var lhs = node.left;
var rhs = node.right;
if (lhs instanceof AST_Destructured) {
node.right.walk(tw);
rhs.walk(tw);
var marker = new TreeWalker(function(node) {
if (node instanceof AST_Destructured) return;
if (node instanceof AST_DefaultValue) {
@@ -5308,9 +5370,17 @@ merge(Compressor.prototype, {
lhs.walk(marker);
return true;
}
if (lazy_op[node.operator.slice(0, -1)]) {
lhs.walk(tw);
push();
rhs.walk(tw);
if (lhs instanceof AST_SymbolRef) mark(lhs);
pop();
return true;
}
if (lhs instanceof AST_SymbolRef) {
if (node.operator != "=") mark(lhs, true);
node.right.walk(tw);
rhs.walk(tw);
mark(lhs);
return true;
}
@@ -6185,10 +6255,11 @@ merge(Compressor.prototype, {
&& var_defs[sym.id] == 1
&& sym.assignments == 0
&& value instanceof AST_LambdaExpression
&& !is_arguments(sym)
&& !is_arrow(value)
&& assigned_once(value, sym.references)
&& can_declare_defun()
&& can_rename(value, def.name.name)) {
&& can_declare_defun(value)
&& (old_def = rename_def(value, def.name.name)) !== false) {
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
var ctor;
switch (value.CTOR) {
@@ -6215,7 +6286,10 @@ merge(Compressor.prototype, {
});
body.push(defun);
} else {
if (drop_sym && var_defs[sym.id] > 1 && sym.orig.indexOf(def.name) > sym.eliminated) {
if (drop_sym
&& var_defs[sym.id] > 1
&& !(parent instanceof AST_ExportDeclaration)
&& sym.orig.indexOf(def.name) > sym.eliminated) {
var_defs[sym.id]--;
duplicated++;
}
@@ -6250,23 +6324,26 @@ merge(Compressor.prototype, {
});
}
function can_declare_defun() {
if (compressor.has_directive("use strict")) return parent instanceof AST_Scope;
function can_declare_defun(fn) {
if (compressor.has_directive("use strict") || !(fn instanceof AST_Function)) {
return parent instanceof AST_Scope;
}
return parent instanceof AST_Block
|| parent instanceof AST_For && parent.init === node
|| parent instanceof AST_If;
}
function can_rename(fn, name) {
if (!fn.name) return !fn.variables.get(name);
old_def = fn.name.definition();
if (old_def.assignments > 0) return false;
if (old_def.name == name) return true;
function rename_def(fn, name) {
if (!fn.name) return null;
var def = fn.name.definition();
if (def.orig.length > 1) return null;
if (def.assignments > 0) return false;
if (def.name == name) return def;
if (name == "await" && is_async(fn)) return false;
if (name == "yield" && is_generator(fn)) return false;
return all(old_def.references, function(ref) {
return all(def.references, function(ref) {
return ref.scope.find_variable(name) === sym;
});
}) && def;
}
function is_catch(node) {
@@ -6375,14 +6452,13 @@ merge(Compressor.prototype, {
scope = save_scope;
}
}, function(node, in_list) {
if (node instanceof AST_BlockStatement) {
return trim_block(node, tt.parent(), in_list);
} else if (node instanceof AST_For) {
// Certain combination of unused name + side effect leads to invalid AST:
// https://github.com/mishoo/UglifyJS/issues/44
// https://github.com/mishoo/UglifyJS/issues/1838
// https://github.com/mishoo/UglifyJS/issues/3371
// We fix it at this stage by moving the `var` outside the `for`.
if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
// Certain combination of unused name + side effect leads to invalid AST:
// https://github.com/mishoo/UglifyJS/issues/44
// https://github.com/mishoo/UglifyJS/issues/1838
// https://github.com/mishoo/UglifyJS/issues/3371
// We fix it at this stage by moving the `var` outside the `for`.
if (node instanceof AST_For) {
var block;
if (node.init instanceof AST_BlockStatement) {
block = node.init;
@@ -6403,7 +6479,8 @@ merge(Compressor.prototype, {
node.init = null;
}
return !block ? node : in_list ? List.splice(block.body) : block;
} else if (node instanceof AST_ForIn) {
}
if (node instanceof AST_ForIn) {
if (!drop_vars || !compressor.option("loops")) return;
if (!is_empty(node.body)) return;
var sym = get_init_symbol(node);
@@ -6424,8 +6501,10 @@ merge(Compressor.prototype, {
body.push(node.init);
}
return insert_statements(body, node, in_list);
} else if (node instanceof AST_Sequence) {
if (node.expressions.length == 1) return node.expressions[0];
}
if (node instanceof AST_Sequence) {
if (node.expressions.length > 1) return;
return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
}
});
tt.push(compressor.parent());
@@ -6778,21 +6857,25 @@ merge(Compressor.prototype, {
var dirs = [];
var hoisted = [];
var vars = new Dictionary(), vars_found = 0;
var tt = new TreeTransformer(function(node) {
var tt = new TreeTransformer(function(node, descend, in_list) {
if (node === self) return;
if (node instanceof AST_Directive) {
dirs.push(node);
return make_node(AST_EmptyStatement, node);
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
}
if (node instanceof AST_Defun) {
if (node instanceof AST_LambdaDefinition) {
if (!hoist_funs) return node;
if (tt.parent() !== self && compressor.has_directive("use strict")) return node;
var p = tt.parent();
if (p instanceof AST_ExportDeclaration) return node;
if (p instanceof AST_ExportDefault) return node;
if (p !== self && compressor.has_directive("use strict")) return node;
hoisted.push(node);
return make_node(AST_EmptyStatement, node);
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
}
if (node instanceof AST_Var) {
if (!hoist_vars) return node;
if (tt.parent() instanceof AST_ExportDeclaration) return node;
var p = tt.parent();
if (p instanceof AST_ExportDeclaration) return node;
if (!all(node.definitions, function(defn) {
var sym = defn.name;
return sym instanceof AST_SymbolVar
@@ -6804,17 +6887,14 @@ merge(Compressor.prototype, {
++vars_found;
});
var seq = node.to_assignments();
var p = tt.parent();
if (p instanceof AST_ForEnumeration && p.init === node) {
if (seq) return seq;
var def = node.definitions[0].name;
return make_node(AST_SymbolRef, def, def);
}
if (p instanceof AST_For && p.init === node) return seq;
if (!seq) return make_node(AST_EmptyStatement, node);
return make_node(AST_SimpleStatement, node, {
body: seq
});
if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
return make_node(AST_SimpleStatement, node, { body: seq });
}
if (node instanceof AST_Scope) return node;
if (node instanceof AST_SymbolConst) {
@@ -7196,9 +7276,14 @@ merge(Compressor.prototype, {
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
if (left.has_side_effects(compressor)) return this;
this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor);
var right = this.right;
if (lazy_op[this.operator.slice(0, -1)]) {
this.write_only = !right.has_side_effects(compressor);
} else {
this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return right.drop_side_effect_free(compressor);
}
}
return this;
});
@@ -7266,10 +7351,10 @@ merge(Compressor.prototype, {
}
var drop_body = false;
if (compressor.option("arrows") && is_arrow(exp)) {
if (exp.value) {
exp.value = exp.value.drop_side_effect_free(compressor);
} else {
if (!exp.value) {
drop_body = true;
} else if (!is_async(exp) || is_primitive(compressor, exp.value)) {
exp.value = exp.value.drop_side_effect_free(compressor);
}
} else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
if (exp.name) {
@@ -7280,14 +7365,22 @@ merge(Compressor.prototype, {
}
}
if (drop_body) {
var async = is_async(exp);
exp.process_expression(false, function(node) {
var value = node.value && node.value.drop_side_effect_free(compressor, true);
return value ? make_node(AST_SimpleStatement, node, {
body: value
}) : make_node(AST_EmptyStatement, node);
var value = node.value;
if (value) {
if (async && !is_primitive(compressor, value)) return node;
value = value.drop_side_effect_free(compressor, true);
}
if (!value) return make_node(AST_EmptyStatement, node);
return make_node(AST_SimpleStatement, node, { body: value });
});
scan_local_returns(exp, function(node) {
if (node.value) node.value = node.value.drop_side_effect_free(compressor);
var value = node.value;
if (value) {
if (async && !is_primitive(compressor, value)) return;
node.value = value.drop_side_effect_free(compressor);
}
});
// always shallow clone to ensure stripping of negated IIFEs
self = self.clone();
@@ -7317,10 +7410,18 @@ merge(Compressor.prototype, {
});
def(AST_Class, function(compressor, first_in_statement) {
var exprs = [], values = [];
this.properties.forEach(function(prop) {
var props = this.properties;
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (prop.key instanceof AST_Node) exprs.push(prop.key);
if (prop instanceof AST_ClassField && prop.static && prop.value) values.push(prop.value);
});
if (prop instanceof AST_ClassField
&& prop.static
&& prop.value
&& !(prop.value instanceof AST_Lambda)) {
if (prop.value.contains_this()) return this;
values.push(prop.value);
}
}
var base = this.extends;
if (base) {
if (base instanceof AST_SymbolRef) base = base.fixed_value();
@@ -8508,11 +8609,12 @@ merge(Compressor.prototype, {
} else if (exp instanceof AST_Dot) switch (exp.property) {
case "toString":
// x.toString() ---> "" + x
if (self.args.length == 0 && !exp.expression.may_throw_on_access(compressor)) {
var expr = exp.expression;
if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
return make_node(AST_Binary, self, {
operator: "+",
left: make_node(AST_String, self, { value: "" }),
right: exp.expression,
right: expr,
}).optimize(compressor);
}
break;
@@ -8521,20 +8623,25 @@ merge(Compressor.prototype, {
var separator = self.args[0];
// [].join() ---> ""
// [].join(x) ---> (x, "")
if (exp.expression.elements.length == 0) return separator ? make_sequence(self, [
separator,
make_node(AST_String, self, { value: "" }),
]).optimize(compressor) : make_node(AST_String, self, { value: "" });
if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
return separator ? make_sequence(self, [
separator,
make_node(AST_String, self, { value: "" }),
]).optimize(compressor) : make_node(AST_String, self, { value: "" });
}
if (separator) {
separator = separator.evaluate(compressor);
if (separator instanceof AST_Node) break EXIT; // not a constant
}
var elements = [];
var consts = [];
exp.expression.elements.forEach(function(el) {
for (var i = 0; i < exp.expression.elements.length; i++) {
var el = exp.expression.elements[i];
var value = el.evaluate(compressor);
if (value !== el) {
consts.push(value);
} else if (el instanceof AST_Spread) {
break EXIT;
} else {
if (consts.length > 0) {
elements.push(make_node(AST_String, self, {
@@ -8544,7 +8651,7 @@ merge(Compressor.prototype, {
}
elements.push(el);
}
});
}
if (consts.length > 0) elements.push(make_node(AST_String, self, {
value: consts.join(separator),
}));
@@ -8723,7 +8830,7 @@ merge(Compressor.prototype, {
var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
if (can_inline && stat instanceof AST_Return) {
var value = stat.value;
if (exp === fn && (!value || value.is_constant_expression() && safe_from_await_yield(value))) {
if (exp === fn && !fn.name && (!value || value.is_constant_expression()) && safe_from_await_yield(fn)) {
return make_sequence(self, convert_args(value)).optimize(compressor);
}
}
@@ -8791,7 +8898,8 @@ merge(Compressor.prototype, {
&& can_drop
&& all(fn.body, is_empty)
&& (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
&& !(is_arrow(fn) && fn.value)) {
&& !(is_arrow(fn) && fn.value)
&& safe_from_await_yield(fn)) {
return make_sequence(self, convert_args()).optimize(compressor);
}
}
@@ -9398,19 +9506,10 @@ merge(Compressor.prototype, {
OPT(AST_UnaryPrefix, function(self, compressor) {
var op = self.operator;
var exp = self.expression;
if (compressor.option("evaluate")
&& op == "delete"
&& !(exp instanceof AST_SymbolRef
|| exp instanceof AST_PropAccess
|| is_identifier_atom(exp))) {
if (exp instanceof AST_Sequence) {
exp = exp.expressions.slice();
exp.push(make_node(AST_True, self));
return make_sequence(self, exp).optimize(compressor);
}
if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
}
if (compressor.option("sequences") && !(op == "typeof" && is_undeclared_ref(exp.tail_node()))) {
if (compressor.option("sequences") && can_lift()) {
var seq = lift_sequence_in_expression(self, compressor);
if (seq !== self) return seq.optimize(compressor);
}
@@ -9460,6 +9559,26 @@ merge(Compressor.prototype, {
// avoids infinite recursion of numerals
return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
? self : try_evaluate(compressor, self);
function may_not_delete(node) {
return node instanceof AST_Infinity
|| node instanceof AST_NaN
|| node instanceof AST_NewTarget
|| node instanceof AST_PropAccess
|| node instanceof AST_SymbolRef
|| node instanceof AST_Undefined;
}
function can_lift() {
switch (op) {
case "delete":
return !may_not_delete(exp.tail_node());
case "typeof":
return !is_undeclared_ref(exp.tail_node());
default:
return true;
}
}
});
OPT(AST_Await, function(self, compressor) {
@@ -10304,7 +10423,7 @@ merge(Compressor.prototype, {
single_use = false;
} else if (fixed.name && fixed.name.definition() !== def) {
single_use = false;
} else if (fixed.parent_scope !== self.scope.resolve() || is_funarg(def)) {
} else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") {
var scope = self.scope;
@@ -10391,7 +10510,7 @@ merge(Compressor.prototype, {
} else {
value = fixed.optimize(compressor);
}
if (value === fixed) value = value.transform(new TreeTransformer(function(node, descend) {
value = value.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_Scope) return node;
node = node.clone();
descend(node, this);
@@ -11031,6 +11150,7 @@ merge(Compressor.prototype, {
if (len != b.length) return -2;
for (var i = 0; i < len; i++) {
if (!a[i].equivalent_to(b[i])) {
if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
for (var j = i + 1; j < len; j++) {
if (!a[j].equivalent_to(b[j])) return -2;
}
@@ -11194,14 +11314,12 @@ merge(Compressor.prototype, {
|| !all(fn.argnames, function(argname) {
return argname instanceof AST_SymbolFunarg;
})) {
if (!compressor.option("reduce_vars")
|| def.reassigned
|| arg_def.assignments
|| arg_def.orig.length > 1) {
argname = null;
}
if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
}
} else if (index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent) && !fn.rest) {
} else if ((assigned || !has_reassigned())
&& index < fn.argnames.length + 5
&& compressor.drop_fargs(fn, fn_parent)
&& !fn.rest) {
while (index >= fn.argnames.length) {
argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
fn.argnames.push(argname);
@@ -11210,7 +11328,7 @@ merge(Compressor.prototype, {
if (argname && find_if(function(node) {
return node.name === argname.name;
}, fn.argnames) === argname) {
def.reassigned = false;
if (assigned) def.reassigned--;
var sym = make_node(AST_SymbolRef, self, argname);
sym.reference();
delete argname.__unused;
@@ -11289,6 +11407,10 @@ merge(Compressor.prototype, {
}
}
}
function has_reassigned() {
return !compressor.option("reduce_vars") || def.reassigned;
}
});
AST_Arrow.DEFMETHOD("contains_super", return_false);
@@ -11308,7 +11430,7 @@ merge(Compressor.prototype, {
AST_Arrow.DEFMETHOD("contains_this", return_false);
AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
AST_Scope.DEFMETHOD("contains_this", function() {
AST_Node.DEFMETHOD("contains_this", function() {
var result;
var self = this;
self.walk(new TreeWalker(function(node) {

View File

@@ -47,14 +47,12 @@ function parse_source_map(content) {
}
function set_shorthand(name, options, keys) {
if (options[name]) {
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
function init_cache(cache) {
@@ -75,6 +73,7 @@ function to_json(cache) {
function minify(files, options) {
try {
options = defaults(options, {
annotations: undefined,
compress: {},
enclose: false,
ie8: false,
@@ -94,17 +93,14 @@ function minify(files, options) {
wrap: false,
}, true);
if (options.validate) AST_Node.enable_validation();
var timings = options.timings && {
start: Date.now()
};
if (options.rename === undefined) {
options.rename = options.compress && options.mangle;
}
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("v8", options, [ "mangle", "output" ]);
set_shorthand("webkit", options, [ "mangle", "output" ]);
var timings = options.timings && { start: Date.now() };
if (options.rename === undefined) options.rename = options.compress && options.mangle;
if (options.annotations !== undefined) set_shorthand("annotations", options, [ "compress", "output" ]);
if (options.ie8) set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output" ]);
if (options.webkit) set_shorthand("webkit", options, [ "mangle", "output" ]);
var quoted_props;
if (options.mangle) {
options.mangle = defaults(options.mangle, {

View File

@@ -52,6 +52,7 @@ function OutputStream(options) {
var readonly = !options;
options = defaults(options, {
annotations : false,
ascii_only : false,
beautify : false,
braces : false,
@@ -346,6 +347,7 @@ function OutputStream(options) {
};
var indent = options.beautify ? function(half) {
if (need_newline_indented) print("\n");
print(repeat_string(" ", half ? indentation - (options.indent_level >> 1) : indentation));
} : noop;
@@ -449,6 +451,27 @@ function OutputStream(options) {
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function pad_comment(token, force) {
if (need_newline_indented) return;
if (token.nlb && (force || !has_nlb())) {
need_newline_indented = true;
} else if (force) {
need_space = true;
}
}
function print_comment(comment) {
var value = comment.value.replace(/[@#]__PURE__/g, " ");
if (/^\s*$/.test(value) && !/^\s*$/.test(comment.value)) return false;
if (/comment[134]/.test(comment.type)) {
print("//" + value);
need_newline_indented = true;
} else if (comment.type == "comment2") {
print("/*" + value + "*/");
}
return true;
}
function prepend_comments(node) {
var self = this;
var scan = node instanceof AST_Exit && node.value;
@@ -488,35 +511,12 @@ function OutputStream(options) {
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
print("\n");
indent();
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, " ") + "\n");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
last_nlb = false;
}
var printed = false;
comments.forEach(function(comment, index) {
pad_comment(comment, index);
if (print_comment(comment)) printed = true;
});
if (!last_nlb) {
if (node.start.nlb) {
print("\n");
indent();
} else {
space();
}
}
if (printed) pad_comment(node.start, true);
function dump(node) {
var token = node.start;
@@ -546,25 +546,9 @@ function OutputStream(options) {
}))) return;
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
need_space = false;
if (need_newline_indented) {
print("\n");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\n");
indent();
} else if (i > 0 || !tail) {
space();
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, " "));
need_newline_indented = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
need_space = true;
}
comments.filter(comment_filter, node).forEach(function(comment, index) {
pad_comment(comment, index || !tail);
print_comment(comment);
});
if (OUTPUT.length > insert) newline_insert = insert;
}
@@ -741,6 +725,8 @@ function OutputStream(options) {
|| p instanceof AST_PropAccess && p.expression === this
// ...(foo, bar, baz)
|| p instanceof AST_Spread
// (foo, bar)`baz`
|| p instanceof AST_Template && p.tag === this
// !(foo, bar, baz)
|| p instanceof AST_Unary
// var a = (1, 2), b = a + a; ---> b == 4
@@ -1436,6 +1422,17 @@ function OutputStream(options) {
});
/* -----[ other expressions ]----- */
function print_annotation(self, output) {
if (!output.option("annotations")) return;
if (!self.pure) return;
var level = 0, parent = self, node;
do {
node = parent;
parent = output.parent(level++);
if (parent instanceof AST_Call && parent.expression === node) return;
} while (parent instanceof AST_PropAccess && parent.expression === node);
output.print("/*" + self.pure + "*/");
}
function print_call_args(self, output) {
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start);
@@ -1448,11 +1445,14 @@ function OutputStream(options) {
});
}
DEFPRINT(AST_Call, function(output) {
this.expression.print(output);
print_call_args(this, output);
var self = this;
print_annotation(self, output);
self.expression.print(output);
print_call_args(self, output);
});
DEFPRINT(AST_New, function(output) {
var self = this;
print_annotation(self, output);
output.print("new");
output.space();
self.expression.print(output);
@@ -1588,7 +1588,14 @@ function OutputStream(options) {
output.space();
} : noop);
});
DEFPRINT(AST_DestructuredKeyVal, print_key_value);
DEFPRINT(AST_DestructuredKeyVal, function(output) {
var self = this;
var key = print_property_key(self, output);
var value = self.value;
if (key && value instanceof AST_SymbolDeclaration && key == get_symbol_name(value)) return;
output.colon();
value.print(output);
});
DEFPRINT(AST_DestructuredObject, function(output) {
var props = this.properties, len = props.length, rest = this.rest;
if (len || rest) output.with_block(function() {
@@ -1651,20 +1658,19 @@ function OutputStream(options) {
output.print_string(key, quote);
} else {
output.print_name(key);
return key;
}
} else {
output.print_string(key, quote);
}
}
}
function print_key_value(output) {
DEFPRINT(AST_ObjectKeyVal, function(output) {
var self = this;
print_property_key(self, output);
output.colon();
self.value.print(output);
}
DEFPRINT(AST_ObjectKeyVal, print_key_value);
});
DEFPRINT(AST_ObjectMethod, function(output) {
print_method(this, output);
});
@@ -1683,32 +1689,36 @@ function OutputStream(options) {
}
DEFPRINT(AST_ObjectGetter, print_accessor("get"));
DEFPRINT(AST_ObjectSetter, print_accessor("set"));
function print_symbol(self, output) {
var def = self.definition();
output.print_name(def && def.mangled_name || self.name);
function get_symbol_name(sym) {
var def = sym.definition();
return def && def.mangled_name || sym.name;
}
DEFPRINT(AST_Symbol, function(output) {
print_symbol(this, output);
output.print_name(get_symbol_name(this));
});
DEFPRINT(AST_SymbolExport, function(output) {
var self = this;
print_symbol(self, output);
if (self.alias) {
var name = get_symbol_name(self);
output.print_name(name);
var alias = self.alias;
if (alias != name) {
output.space();
output.print("as");
output.space();
output.print_name(self.alias);
output.print_name(alias);
}
});
DEFPRINT(AST_SymbolImport, function(output) {
var self = this;
if (self.key) {
output.print_name(self.key);
var name = get_symbol_name(self);
var key = self.key;
if (key && key != name) {
output.print_name(key);
output.space();
output.print("as");
output.space();
}
print_symbol(self, output);
output.print_name(name);
});
DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_Template, function(output) {

View File

@@ -100,15 +100,19 @@ var OPERATORS = makePredicate([
"/=",
"*=",
"%=",
"**=",
">>=",
"<<=",
">>>=",
"&=",
"|=",
"^=",
"&=",
"&&",
"||",
"??",
"&&=",
"||=",
"??=",
]);
var NEWLINE_CHARS = "\n\r\u2028\u2029";
@@ -160,10 +164,10 @@ function decode_escape_sequence(seq) {
case "t": return "\t";
case "u":
var code;
if (seq.length == 5) {
code = seq.slice(1);
} else if (seq[1] == "{" && seq.slice(-1) == "}") {
if (seq[1] == "{" && seq.slice(-1) == "}") {
code = seq.slice(2, -1);
} else if (seq.length == 5) {
code = seq.slice(1);
} else {
return;
}
@@ -652,7 +656,7 @@ var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");
var UNARY_POSTFIX = makePredicate("-- ++");
var ASSIGNMENT = makePredicate("= += -= /= *= %= >>= <<= >>>= |= ^= &=");
var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= &= |= ^= &&= ||= ??=");
var PRECEDENCE = function(a, ret) {
for (var i = 0; i < a.length;) {
@@ -1736,67 +1740,66 @@ function parse($TEXT, options) {
var new_ = function(allow_calls) {
var start = S.token;
expect_token("operator", "new");
var call;
if (is("punc", ".") && is_token(peek(), "name", "target")) {
next();
next();
return new AST_NewTarget({
name: "new.target",
start: start,
end: prev(),
});
}
var newexp = expr_atom(false), args;
if (is("punc", "(")) {
next();
args = expr_list(")", !options.strict);
call = new AST_NewTarget();
} else {
args = [];
var exp = expr_atom(false), args;
if (is("punc", "(")) {
next();
args = expr_list(")", !options.strict);
} else {
args = [];
}
call = new AST_New({ expression: exp, args: args });
}
var call = new AST_New({
start : start,
expression : newexp,
args : args,
end : prev()
});
mark_pure(call);
call.start = start;
call.end = prev();
return subscripts(call, allow_calls);
};
function as_atom_node() {
var tok = S.token, ret;
var ret, tok = S.token, value = tok.value;
switch (tok.type) {
case "num":
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
break;
case "bigint":
ret = new AST_BigInt({ start: tok, end: tok, value: tok.value });
break;
case "string":
ret = new AST_String({
start : tok,
end : tok,
value : tok.value,
quote : tok.quote
});
break;
case "regexp":
ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
break;
case "atom":
switch (tok.value) {
case "false":
ret = new AST_False({ start: tok, end: tok });
break;
case "true":
ret = new AST_True({ start: tok, end: tok });
break;
case "null":
ret = new AST_Null({ start: tok, end: tok });
break;
if (isFinite(value)) {
ret = new AST_Number({ value: value });
} else {
ret = new AST_Infinity();
if (value < 0) ret = new AST_UnaryPrefix({ operator: "-", expression: ret });
}
break;
case "bigint":
ret = new AST_BigInt({ value: value });
break;
case "string":
ret = new AST_String({ value : value, quote : tok.quote });
break;
case "regexp":
ret = new AST_RegExp({ value: value });
break;
case "atom":
switch (value) {
case "false":
ret = new AST_False();
break;
case "true":
ret = new AST_True();
break;
case "null":
ret = new AST_Null();
break;
default:
unexpected();
}
break;
default:
unexpected();
}
next();
ret.start = ret.end = tok;
return ret;
}
@@ -1836,11 +1839,13 @@ function parse($TEXT, options) {
expect(")");
var end = prev();
end.comments_before = ex.end.comments_before;
[].push.apply(ex.end.comments_after, end.comments_after);
end.comments_after.forEach(function(comment) {
ex.end.comments_after.push(comment);
if (comment.nlb) S.token.nlb = true;
});
end.comments_after.length = 0;
end.comments_after = ex.end.comments_after;
ex.end = end;
if (ex instanceof AST_Call) mark_pure(ex);
if (is("punc", "=>")) return arrow(ex instanceof AST_Sequence ? ex.expressions : [ ex ], start);
return subscripts(ex, allow_calls);
case "[":
@@ -2217,19 +2222,6 @@ function parse($TEXT, options) {
});
}
function mark_pure(call) {
var start = call.start;
var comments = start.comments_before;
var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
while (--i >= 0) {
var comment = comments[i];
if (/[@#]__PURE__/.test(comment.value)) {
call.pure = comment;
break;
}
}
}
function template(tag) {
var read = S.input.context().read_template;
var strings = [];
@@ -2277,7 +2269,6 @@ function parse($TEXT, options) {
args : expr_list(")", !options.strict),
end : prev()
});
mark_pure(call);
return subscripts(call, true);
}
if (is("punc", "`")) {
@@ -2286,6 +2277,18 @@ function parse($TEXT, options) {
tmpl.end = prev();
return subscripts(tmpl, allow_calls);
}
if (expr instanceof AST_Call && !expr.pure) {
var start = expr.start;
var comments = start.comments_before;
var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
while (--i >= 0) {
var match = /[@#]__PURE__/.exec(comments[i].value);
if (match) {
expr.pure = match[0];
break;
}
}
}
return expr;
};

View File

@@ -96,8 +96,10 @@ SymbolDef.prototype = {
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun);
&& (this.orig[0] instanceof AST_SymbolClass
|| this.orig[0] instanceof AST_SymbolDefClass
|| this.orig[0] instanceof AST_SymbolDefun
|| this.orig[0] instanceof AST_SymbolLambda);
},
};

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.13.1",
"version": "3.13.3",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -262,6 +262,7 @@ function test_case(test) {
var input = to_toplevel(test.input, test.mangle);
var input_code = make_code(input);
var input_formatted = make_code(test.input, {
annotations: true,
beautify: true,
comments: "all",
keep_quoted_props: true,

View File

@@ -0,0 +1,473 @@
issue_2629_1: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a();
/*@__PURE__*/ (b());
(/*@__PURE__*/ c)();
(/*@__PURE__*/ d());
}
expect_exact: "c();"
}
issue_2629_2: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a(1)(2)(3);
/*@__PURE__*/ (b(1))(2)(3);
/*@__PURE__*/ (c(1)(2))(3);
/*@__PURE__*/ (d(1)(2)(3));
(/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ f(1))(2)(3);
(/*@__PURE__*/ g(1)(2))(3);
(/*@__PURE__*/ h(1)(2)(3));
}
expect_exact: [
"e(1)(2)(3);",
"f(1)(2)(3);",
"g(1)(2)(3);",
]
}
issue_2629_3: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a.x(1).y(2).z(3);
/*@__PURE__*/ (b.x)(1).y(2).z(3);
/*@__PURE__*/ (c.x(1)).y(2).z(3);
/*@__PURE__*/ (d.x(1).y)(2).z(3);
/*@__PURE__*/ (e.x(1).y(2)).z(3);
/*@__PURE__*/ (f.x(1).y(2).z)(3);
/*@__PURE__*/ (g.x(1).y(2).z(3));
(/*@__PURE__*/ h).x(1).y(2).z(3);
(/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ j.x(1)).y(2).z(3);
(/*@__PURE__*/ k.x(1).y)(2).z(3);
(/*@__PURE__*/ l.x(1).y(2)).z(3);
(/*@__PURE__*/ m.x(1).y(2).z)(3);
(/*@__PURE__*/ n.x(1).y(2).z(3));
}
expect_exact: [
"h.x(1).y(2).z(3);",
"i.x(1).y(2).z(3);",
"j.x(1).y(2).z(3);",
"k.x(1).y(2).z(3);",
"l.x(1).y(2).z(3);",
"m.x(1).y(2).z(3);",
]
}
issue_2629_4: {
options = {
annotations: true,
side_effects: true,
}
input: {
(/*@__PURE__*/ x(), y());
(w(), /*@__PURE__*/ x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2629_5: {
options = {
annotations: true,
side_effects: true,
}
input: {
[ /*@__PURE__*/ x() ];
[ /*@__PURE__*/ x(), y() ];
[ w(), /*@__PURE__*/ x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2638: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/(g() || h())(x(), y());
(/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"x(),y();",
"(a()||b())(c(),d());",
]
}
issue_2705_1: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a();
/*@__PURE__*/ (new b());
new (/*@__PURE__*/ c)();
(/*@__PURE__*/ new d());
}
expect_exact: [
"new c;",
]
}
issue_2705_2: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a(1)(2)(3);
/*@__PURE__*/ new (b(1))(2)(3);
/*@__PURE__*/ new (c(1)(2))(3);
/*@__PURE__*/ new (d(1)(2)(3));
new (/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ new f(1))(2)(3);
(/*@__PURE__*/ new g(1)(2))(3);
(/*@__PURE__*/ new h(1)(2)(3));
}
expect_exact: [
"new e(1)(2)(3);",
"new f(1)(2)(3);",
"new g(1)(2)(3);",
]
}
issue_2705_3: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a.x(1).y(2).z(3);
/*@__PURE__*/ new (b.x)(1).y(2).z(3);
/*@__PURE__*/ new (c.x(1)).y(2).z(3);
/*@__PURE__*/ new (d.x(1).y)(2).z(3);
/*@__PURE__*/ new (e.x(1).y(2)).z(3);
/*@__PURE__*/ new (f.x(1).y(2).z)(3);
/*@__PURE__*/ new (g.x(1).y(2).z(3));
new (/*@__PURE__*/ h).x(1).y(2).z(3);
/* */ new (/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ new j.x(1)).y(2).z(3);
(/*@__PURE__*/ new k.x(1).y)(2).z(3);
(/*@__PURE__*/ new l.x(1).y(2)).z(3);
(/*@__PURE__*/ new m.x(1).y(2).z)(3);
(/*@__PURE__*/ new n.x(1).y(2).z(3));
}
expect_exact: [
"new h.x(1).y(2).z(3);",
"/* */new i.x(1).y(2).z(3);",
"new j.x(1).y(2).z(3);",
"new k.x(1).y(2).z(3);",
"new l.x(1).y(2).z(3);",
"new m.x(1).y(2).z(3);",
]
}
issue_2705_4: {
options = {
annotations: true,
side_effects: true,
}
input: {
(/*@__PURE__*/ new x(), y());
(w(), /*@__PURE__*/ new x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2705_5: {
options = {
annotations: true,
side_effects: true,
}
input: {
[ /*@__PURE__*/ new x() ];
[ /*@__PURE__*/ new x(), y() ];
[ w(), /*@__PURE__*/ new x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2705_6: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/new (g() || h())(x(), y());
/* */ new (/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"x(),y();",
"/* */new(a()||b())(c(),d());",
]
}
issue_3858: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
keep_fargs: false,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {
var f = function(a) {
return function() {
console.log(a);
}();
};
f("PASS");
}
expect_stdout: "PASS"
}
inline_pure_call_1: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {}
}
inline_pure_call_2: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
}
expect: {}
}
inline_pure_call_3: {
options = {
annotations: true,
collapse_vars: true,
evaluate: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
console.log(a);
}
expect: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
inline_pure_call_4: {
options = {
annotations: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = /*@__PURE__*/ function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect: {
var a = function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect_stdout: [
"PASS",
"42",
]
}
compress_and_output_annotations_enabled: {
options = {
annotations: true,
side_effects: true,
}
beautify = {
annotations: true,
beautify: true,
comments: false,
}
input: {
var top = /*@__PURE__*/ foo();
/*@__PURE__*/ a(1)(2)(3);
/*@__PURE__*/ (b(1))(2)(3);
/*@__PURE__*/ (c(1)(2))(3);
/*@__PURE__*/ (d(1)(2)(3));
(/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ f(1))(2)(3);
(/*@__PURE__*/ g(1)(2))(3);
(/*@__PURE__*/ h(1)(2)(3));
/*@__PURE__*/ l(1).p(2);
(/*@__PURE__*/ m(1)).p(2);
(/*@__PURE__*/ n(1).p)(2);
(/*@__PURE__*/ o(1).p(2));
}
expect_exact: [
"var top = /*@__PURE__*/foo();",
"",
"e(1)(2)(3);",
"",
"f(1)(2)(3);",
"",
"g(1)(2)(3);",
"",
"m(1).p(2);",
"",
"n(1).p(2);",
]
}
compress_annotations_disabled_output_annotations_enabled: {
options = {
annotations: false,
evaluate: true,
sequences: true,
side_effects: true,
}
beautify = {
annotations: true,
comments: true,
}
input: {
/*@__PURE__*/ a(1+2);
/*#__PURE__*/ (b(2+3));
(/*@__PURE__*/ c)(side_effect);
(/*#__PURE__*/ d(effect()));
}
expect_exact: [
"/*@__PURE__*/a(3),",
"/*#__PURE__*/b(5),",
"c(side_effect),",
"/*#__PURE__*/d(effect());",
]
}
compress_and_output_annotations_disabled: {
options = {
annotations: false,
evaluate: true,
sequences: true,
side_effects: true,
}
beautify = {
annotations: false,
comments: true,
}
input: {
/*@__PURE__*/ a(1+2);
/*@__PURE__*/ (b(2+3));
(/*@__PURE__*/ c)(side_effect);
(/*@__PURE__*/ d(effect()));
}
expect_exact: [
"a(3),",
"b(5),",
"c(side_effect),",
"d(effect());",
]
}

View File

@@ -84,6 +84,7 @@ replace_index_drop_fargs_1: {
evaluate: true,
keep_fargs: false,
properties: true,
reduce_vars: true,
}
input: {
var arguments = [];
@@ -119,7 +120,7 @@ replace_index_drop_fargs_1: {
console.log(b, b, arguments.foo);
})("bar", 42);
(function(arguments) {
console.log(arguments[1], arguments[1], arguments.foo);
console.log("bar"[1], "bar"[1], "bar".foo);
})("bar", 42);
(function(argument_0, argument_1) {
var arguments;
@@ -649,6 +650,7 @@ issue_3420_1: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -671,6 +673,7 @@ issue_3420_2: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
var foo = function() {
@@ -691,6 +694,7 @@ issue_3420_3: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
"use strict";
@@ -713,6 +717,7 @@ issue_3420_4: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
!function() {
@@ -738,6 +743,7 @@ issue_3420_5: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
"use strict";
@@ -765,6 +771,7 @@ issue_3420_6: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -783,6 +790,7 @@ issue_3420_7: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
"use strict";
@@ -827,6 +835,7 @@ issue_4291_1: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -847,6 +856,7 @@ issue_4291_2: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
var a = function() {
@@ -857,8 +867,8 @@ issue_4291_2: {
console.log(a[1], a[0], a.length);
}
expect: {
var a = function(argument_0) {
if (argument_0)
var a = function() {
if (arguments[0])
arguments[1] = "PASS";
return arguments;
}(42);
@@ -871,6 +881,7 @@ issue_4397: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(typeof function() {
@@ -999,3 +1010,26 @@ issue_4696: {
}
expect_stdout: "PASS"
}
issue_4809: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
A = 0;
(function() {
arguments[A] = "PASS";
console.log(arguments[0]);
})();
}
expect: {
A = 0;
(function() {
arguments[A] = "PASS";
console.log(arguments[0]);
})();
}
expect_stdout: "PASS"
}

View File

@@ -276,6 +276,7 @@ drop_arguments: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
console.log(function() {
@@ -803,3 +804,13 @@ issue_4687_2: {
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4772: {
input: {
var f = a => (a)
/**/ console.log(f("PASS"));
}
expect_exact: 'var f=a=>a;console.log(f("PASS"));'
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -475,3 +475,200 @@ issue_4521: {
}
expect_stdout: "42"
}
logical_assignments: {
input: {
var a = 42, b = null, c;
a &&= "foo";
b ||= "bar";
c ??= "baz";
console.log(a, b, c);
}
expect_exact: 'var a=42,b=null,c;a&&="foo";b||="bar";c??="baz";console.log(a,b,c);'
expect_stdout: "foo bar baz"
node_version: ">=15"
}
logical_collapse_vars: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL", b = false;
a = "PASS";
b ??= a;
console.log(a);
}
expect: {
var a = "FAIL", b = false;
a = "PASS";
b ??= a;
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
logical_reduce_vars: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
logical_side_effects: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4815_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4815_2: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4819: {
options = {
comparisons: true,
}
input: {
console.log(void 0 === ([].p &&= 42));
}
expect: {
console.log(void 0 === ([].p &&= 42));
}
expect_stdout: "true"
node_version: ">=15"
}
issue_4827_1: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
A = "FAIL";
var a = A, b = "PASS", c;
c &&= b = a, console.log(b);
}
expect: {
A = "FAIL";
var a = A, b = "PASS", c;
c &&= b = a, console.log(b);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4827_2: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0, b = "PASS";
function f(c) {
a++,
c &&= b = a;
}
f();
console.log(b);
}
expect: {
var a = 0, b = "PASS";
a++,
c &&= b = a;
var c;
console.log(b);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4827_3: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = 0, b, c;
a++;
c &&= b = a;
console.log(b);
}
expect: {
var a = 0, b, c;
a++;
c &&= b = a;
console.log(b);
}
expect_stdout: "undefined"
node_version: ">=15"
}

View File

@@ -564,7 +564,7 @@ drop_return: {
input: {
(async function(a) {
while (!console);
return console.log(a);
return !console.log(a);
})(42);
}
expect: {
@@ -614,14 +614,14 @@ functions: {
async function b() {
return !!b;
}
var c = async function(c) {
async function c(c) {
return c;
};
}
if (await c(await b(await a()))) {
async function d() {}
async function e() {
return typeof e;
}
var d = async function() {};
var e = async function y() {
return typeof y;
};
var f = async function(f) {
return f;
};
@@ -672,9 +672,9 @@ functions_use_strict: {
async function b() {
return !!b;
}
var c = async function(c) {
async function c(c) {
return c;
};
}
if (await c(await b(await a()))) {
var d = async function() {};
var e = async function y() {
@@ -691,6 +691,54 @@ functions_use_strict: {
node_version: ">=8"
}
functions_anonymous: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var await = async function() {
console.log("PASS");
};
await(await);
}
expect: {
async function await() {
console.log("PASS");
}
await();
}
expect_stdout: "PASS"
node_version: ">=8"
}
functions_inner_var: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var await = function a() {
var a;
console.log(a, a);
};
await(await);
}
expect: {
function await() {
var a;
console.log(a, a);
}
await();
}
expect_stdout: "undefined undefined"
node_version: ">=8"
}
issue_4335_1: {
options = {
inline: true,
@@ -1408,3 +1456,78 @@ issue_4747: {
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4764_1: {
options = {
side_effects: true,
}
input: {
(async function() {
return {
then() {
console.log("PASS");
},
};
})();
}
expect: {
(async function() {
return {
then() {
console.log("PASS");
},
};
})();
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4764_2: {
options = {
arrows: true,
side_effects: true,
}
input: {
(async () => ({
get then() {
console.log("PASS");
},
}))();
}
expect: {
(async () => ({
get then() {
console.log("PASS");
},
}))();
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4764_3: {
options = {
side_effects: true,
}
input: {
(async function(o) {
return o;
})({
then() {
console.log("PASS");
},
});
}
expect: {
(async function(o) {
return o;
})({
then() {
console.log("PASS");
},
});
}
expect_stdout: "PASS"
node_version: ">=8"
}

View File

@@ -60,3 +60,33 @@ issue_4590: {
expect_stdout: "PASS"
node_version: ">=10"
}
issue_4801: {
options = {
booleans: true,
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
try {
(function(a) {
A = 42;
a || A;
})(!(0 == 42 >> 0o644n));
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
(function(a) {
0 != (A = 42) >> 0o644n || A;
})();
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=10"
}

View File

@@ -725,7 +725,7 @@ unused_await: {
(() => console.log(await))();
})();
}
expect_stdout: "PASS"
expect_stdout: true
node_version: ">=12"
}
@@ -779,6 +779,27 @@ computed_key_generator: {
node_version: ">=4"
}
keep_fnames: {
options = {
keep_fnames: true,
toplevel: true,
}
mangle = {
keep_fnames: true,
toplevel: true,
}
input: {
"use strict";
class Foo {}
console.log(Foo.name, class Bar {}.name);
}
expect: {
"use strict";
class Foo {}
console.log(Foo.name, class Bar {}.name);
}
}
issue_805_1: {
options = {
inline: true,
@@ -1275,3 +1296,51 @@ issue_4756: {
]
node_version: ">=12"
}
issue_4821_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
class A {
static p = void (a = this);
}
console.log(typeof a);
}
expect: {
var a;
class A {
static p = void (a = this);
}
console.log(typeof a);
}
expect_stdout: "function"
node_version: ">=12"
}
issue_4821_2: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
class A {
static p = void (a = this);
}
console.log(typeof a);
}
expect: {
var a;
(class {
static p = void (a = this);
});
console.log(typeof a);
}
expect_stdout: "function"
node_version: ">=12"
}

View File

@@ -2868,7 +2868,7 @@ lvalues_def: {
expect_stdout: true
}
compound_assignment: {
compound_assignment_1: {
options = {
collapse_vars: true,
}
@@ -2887,6 +2887,23 @@ compound_assignment: {
expect_stdout: "4"
}
compound_assignment_2: {
options = {
collapse_vars: true,
}
input: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect_stdout: "4"
}
issue_2187_1: {
options = {
collapse_vars: true,
@@ -8857,3 +8874,26 @@ dot_non_local: {
}
expect_stdout: "42"
}
issue_4806: {
options = {
collapse_vars: true,
}
input: {
var a, o = {
f: function() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(a = 42, o.f)(42);
}
expect: {
var a, o = {
f: function() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(0, o.f)(a = 42);
}
expect_stdout: "PASS"
}

View File

@@ -1661,3 +1661,23 @@ issue_4588_2_evaluate: {
expect_stdout: "1"
node_version: ">=6"
}
issue_4817: {
options = {
ie8: true,
inline: true,
unused: true,
}
input: {
(function f(a = console.log(typeof f)) {
return 42;
})();
}
expect: {
(function f(a = console.log(typeof f)) {
return 42;
})();
}
expect_stdout: "function"
node_version: ">=6"
}

View File

@@ -3274,3 +3274,86 @@ issue_4662: {
}
expect_stdout: "1 1"
}
issue_4806_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
var a;
(a = 42, O.f)();
a;
}
expect: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
(0, O.f)();
42;
}
expect_stdout: "PASS"
}
issue_4806_2: {
options = {
sequences: true,
toplevel: true,
unused: true,
}
input: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
var a;
(a = 42, O.f)();
a;
}
expect: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
},
(0, O.f)();
}
expect_stdout: "PASS"
}
issue_4806_3: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
var a;
(a = 42, O.f)();
a;
}
expect: {
O = {
f: function() {
console.log(this === O ? "FAIL" : "PASS");
},
};
(0, O.f)();
}
expect_stdout: "PASS"
}

View File

@@ -43,6 +43,28 @@ await: {
node_version: ">=8"
}
assignment_1: {
input: {
var a = 2;
a **= 5;
console.log(a);
}
expect_exact: "var a=2;a**=5;console.log(a);"
expect_stdout: "32"
node_version: ">=8"
}
assignment_2: {
input: {
var a = 8n;
a **= a;
console.log(a);
}
expect_exact: "var a=8n;a**=a;console.log(a);"
expect_stdout: "16777216n"
node_version: ">=10"
}
evaluate: {
options = {
evaluate: true,

View File

@@ -3,7 +3,7 @@ refs: {
export {};
export { a, b as B, c as case, d as default };
}
expect_exact: "export{};export{a as a,b as B,c as case,d as default};"
expect_exact: "export{};export{a,b as B,c as case,d as default};"
}
var_defs: {
@@ -12,7 +12,7 @@ var_defs: {
export let b = 2, c = 3;
export var { d, e: [] } = f;
}
expect_exact: "export const a=1;export let b=2,c=3;export var{d:d,e:[]}=f;"
expect_exact: "export const a=1;export let b=2,c=3;export var{d,e:[]}=f;"
}
defuns: {
@@ -35,7 +35,7 @@ defaults: {
export default function*(a, b) {};
export default async function f({ c }, ...[ d ]) {};
}
expect_exact: "export default 42;export default async;export default(x,y)=>x*x;export default class{}export default function*(a,b){}export default async function f({c:c},...[d]){}"
expect_exact: "export default 42;export default async;export default(x,y)=>x*x;export default class{}export default function*(a,b){}export default async function f({c},...[d]){}"
}
defaults_parentheses_1: {
@@ -242,15 +242,15 @@ hoist_exports_2: {
}
}
expect: {
let f, { foo: o } = 42;
function c(t, { [f]: a }) {
t(a, c);
let e, { foo: a } = 42;
function f(t, { [e]: o }) {
t(o, f);
}
export default 42;
export default async function e(t, ...{ [o]: a }) {
(await t)(e, a);
export default async function n(t, ...{ [a]: o }) {
(await t)(n, o);
};
export { f as bbb, o as ccc, c as fff };
export { e as bbb, a as ccc, f as fff };
}
}
@@ -399,6 +399,17 @@ single_use_class_default: {
}
}
hoist_funs: {
options = {
hoist_funs: true,
}
input: {
export function f() {}
export default async function* g() {}
}
expect_exact: "export function f(){}export default async function*g(){}"
}
issue_4742_join_vars_1: {
options = {
join_vars: true,
@@ -464,3 +475,17 @@ issue_4761: {
}
expect_exact: 'export default"function"==42;'
}
issue_4766: {
options = {
unused: true,
}
input: {
var a = "foo";
export var a = "bar";
}
expect: {
var a = "foo";
export var a = "bar";
}
}

View File

@@ -2751,17 +2751,17 @@ functions: {
function b() {
return !!b;
}
var c = function(c) {
function c(c) {
return c;
};
}
if (c(b(a()))) {
function d() {}
function e() {
return typeof e;
}
var f = function(f) {
function f(f) {
return f;
};
}
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
@@ -2808,9 +2808,9 @@ functions_use_strict: {
function b() {
return !!b;
}
var c = function(c) {
function c(c) {
return c;
};
}
if (c(b(a()))) {
var d = function() {};
var e = function y() {
@@ -2826,6 +2826,30 @@ functions_use_strict: {
expect_stdout: "a true 42 function function function"
}
functions_inner_var: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function() {
var a;
console.log(a, a);
};
a(a);
}
expect: {
function a() {
var a;
console.log(a, a);
}
a();
}
expect_stdout: "undefined undefined"
}
issue_2437: {
options = {
collapse_vars: true,
@@ -5753,22 +5777,120 @@ issue_4725_2: {
node_version: ">=4"
}
new_target: {
new_target_1: {
input: {
console.log(typeof new function() {
return new.target;
}, function() {
new function f() {
console.log(new.target === f);
}();
console.log(function() {
return new.target;
}());
}
expect: {
console.log(typeof new function() {
return new.target;
}(), function() {
new function f() {
console.log(new.target === f);
}();
console.log(function() {
return new.target;
}());
}
expect_stdout: "function undefined"
expect_stdout: [
"true",
"undefined",
]
node_version: ">=6"
}
new_target_2: {
input: {
new function(a) {
if (!new.target)
console.log("FAIL");
else if (a)
console.log("PASS");
else
new new.target(new.target.length);
}();
}
expect: {
new function(a) {
if (!new.target)
console.log("FAIL");
else if (a)
console.log("PASS");
else
new new.target(new.target.length);
}();
}
expect_stdout: "PASS"
node_version: ">=6"
}
new_target_collapse_vars: {
options = {
collapse_vars: true,
unused: true,
}
input: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect_stdout: "PASS"
node_version: ">=6"
}
new_target_delete: {
options = {
evaluate: true,
}
input: {
new function() {
console.log(delete new.target);
}();
}
expect: {
new function() {
console.log(delete new.target);
}();
}
expect_stdout: true
node_version: ">=6"
}
new_target_reduce_vars: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect: {
new function(a) {
if (a)
console.log("PASS");
else
new new.target(new.target.length);
}(0);
}
expect_stdout: "PASS"
node_version: ">=6"
}
@@ -5832,3 +5954,70 @@ issue_4753_2: {
}
expect_stdout: "PASS"
}
issue_4788: {
options = {
evaluate: true,
functions: true,
keep_fnames: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
var a = function g() {
if (0) {
var g = 42;
f();
}
g || console.log("PASS");
};
a(a);
}
f();
}
expect: {
(function f() {
function a() {
if (0) {
var g = 42;
f();
}
g || console.log("PASS");
}
a();
})();
}
expect_stdout: "PASS"
}
issue_4823: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
{
function f() {}
var arguments = f();
function g() {}
var arguments = g;
}
return f && arguments;
}());
}
expect: {
console.log(typeof function() {
{
function f() {}
arguments = f();
var arguments = function() {};
}
return f && arguments;
}());
}
expect_stdout: "function"
}

View File

@@ -153,7 +153,7 @@ issue_4487: {
}
expect: {
function a() {
var a = console.log(typeof a);
var f = console.log(typeof f);
}
a();
}

View File

@@ -23,7 +23,7 @@ keys_only: {
input: {
import { as as foo, bar, delete as baz } from "moo";
}
expect_exact: 'import{as as foo,bar as bar,delete as baz}from"moo";'
expect_exact: 'import{as as foo,bar,delete as baz}from"moo";'
}
default_all: {
@@ -37,7 +37,7 @@ default_keys: {
input: {
import foo, { bar } from "baz";
}
expect_exact: 'import foo,{bar as bar}from"baz";'
expect_exact: 'import foo,{bar}from"baz";'
}
dynamic: {

View File

@@ -4,7 +4,7 @@ mangle_keep_fnames_false: {
keep_fnames: true,
}
mangle = {
keep_fnames : false,
keep_fnames: false,
}
input: {
"use strict";
@@ -30,7 +30,7 @@ mangle_keep_fnames_true: {
keep_fnames: true,
}
mangle = {
keep_fnames : true,
keep_fnames: true,
}
input: {
"use strict";

View File

@@ -1,5 +1,6 @@
pure_function_calls: {
options = {
annotations: true,
booleans: true,
comparisons: true,
conditionals: true,
@@ -60,6 +61,7 @@ pure_function_calls: {
pure_function_calls_toplevel: {
options = {
annotations: true,
booleans: true,
comparisons: true,
conditionals: true,
@@ -126,6 +128,7 @@ pure_function_calls_toplevel: {
should_warn: {
options = {
annotations: true,
booleans: true,
conditionals: true,
evaluate: true,

View File

@@ -426,6 +426,7 @@ wrap_iife_in_return_call: {
pure_annotation_1: {
options = {
annotations: true,
inline: true,
side_effects: true,
}
@@ -439,6 +440,7 @@ pure_annotation_1: {
pure_annotation_2: {
options = {
annotations: true,
collapse_vars: true,
inline: true,
side_effects: true,

View File

@@ -359,7 +359,7 @@ reduce_block_2_toplevel: {
node_version: ">=4"
}
reduce_vars: {
reduce_vars_1: {
options = {
evaluate: true,
reduce_vars: true,
@@ -381,6 +381,86 @@ reduce_vars: {
node_version: ">=4"
}
reduce_vars_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
(function() {
function f() {
console.log(typeof a);
}
for (let a in [ 42 ])
f();
})();
}
expect: {
"use strict";
(function() {
function f() {
console.log(typeof a);
}
for (let a in [ 42 ])
f();
})();
}
expect_stdout: "undefined"
node_version: ">=4"
}
reduce_vars_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
(function(a) {
let i = 1;
function f() {
i = 0;
}
for (let i = 0, x = 0; i < a.length; i++, x++) {
if (x != i) {
console.log("FAIL");
break;
}
f();
console.log(a[i]);
}
console.log(i);
})([ 4, 2 ]);
}
expect: {
"use strict";
(function(a) {
let i = 1;
function f() {
i = 0;
}
for (let i = 0, x = 0; i < a.length; i++, x++) {
if (x != i) {
console.log("FAIL");
break;
}
f();
console.log(a[i]);
}
console.log(i);
})([ 4, 2 ]);
}
expect_stdout: [
"4",
"2",
"0",
]
node_version: ">=4"
}
hoist_props: {
options = {
hoist_props: true,

View File

@@ -836,10 +836,7 @@ for_of: {
console.log(async);
}
expect_exact: 'var async=["PASS",42];async.p="FAIL";for(async of(null,async))console.log(async);'
expect_stdout: [
"PASS",
"42",
]
expect_stdout: true
node_version: ">=0.12"
}

View File

@@ -1,3 +1,10 @@
literal_infinity: {
input: {
console.log(2e308, -1e2345);
}
expect_exact: "console.log(1/0,-(1/0));"
}
parentheses_for_prototype_functions: {
beautify = {
beautify: true,

View File

@@ -294,248 +294,6 @@ unary: {
}
}
issue_2629_1: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a();
/*@__PURE__*/ (b());
(/*@__PURE__*/ c)();
(/*@__PURE__*/ d());
}
expect_exact: [
"/* */c();",
]
}
issue_2629_2: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a(1)(2)(3);
/*@__PURE__*/ (b(1))(2)(3);
/*@__PURE__*/ (c(1)(2))(3);
/*@__PURE__*/ (d(1)(2)(3));
(/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ f(1))(2)(3);
(/*@__PURE__*/ g(1)(2))(3);
(/*@__PURE__*/ h(1)(2)(3));
}
expect_exact: [
"/* */e(1)(2)(3);",
"/* */f(1)(2)(3);",
"/* */g(1)(2)(3);",
]
}
issue_2629_3: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ a.x(1).y(2).z(3);
/*@__PURE__*/ (b.x)(1).y(2).z(3);
/*@__PURE__*/ (c.x(1)).y(2).z(3);
/*@__PURE__*/ (d.x(1).y)(2).z(3);
/*@__PURE__*/ (e.x(1).y(2)).z(3);
/*@__PURE__*/ (f.x(1).y(2).z)(3);
/*@__PURE__*/ (g.x(1).y(2).z(3));
(/*@__PURE__*/ h).x(1).y(2).z(3);
(/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ j.x(1)).y(2).z(3);
(/*@__PURE__*/ k.x(1).y)(2).z(3);
(/*@__PURE__*/ l.x(1).y(2)).z(3);
(/*@__PURE__*/ m.x(1).y(2).z)(3);
(/*@__PURE__*/ n.x(1).y(2).z(3));
}
expect_exact: [
"/* */h.x(1).y(2).z(3);",
"/* */i.x(1).y(2).z(3);",
"/* */j.x(1).y(2).z(3);",
"/* */k.x(1).y(2).z(3);",
"/* */l.x(1).y(2).z(3);",
"/* */m.x(1).y(2).z(3);",
]
}
issue_2629_4: {
options = {
side_effects: true,
}
input: {
(/*@__PURE__*/ x(), y());
(w(), /*@__PURE__*/ x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2629_5: {
options = {
side_effects: true,
}
input: {
[ /*@__PURE__*/ x() ];
[ /*@__PURE__*/ x(), y() ];
[ w(), /*@__PURE__*/ x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2638: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/(g() || h())(x(), y());
(/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"/* */x(),y();",
"/* */(a()||b())(c(),d());",
]
}
issue_2705_1: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a();
/*@__PURE__*/ (new b());
new (/*@__PURE__*/ c)();
(/*@__PURE__*/ new d());
}
expect_exact: [
"new/* */c;",
]
}
issue_2705_2: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a(1)(2)(3);
/*@__PURE__*/ new (b(1))(2)(3);
/*@__PURE__*/ new (c(1)(2))(3);
/*@__PURE__*/ new (d(1)(2)(3));
new (/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ new f(1))(2)(3);
(/*@__PURE__*/ new g(1)(2))(3);
(/*@__PURE__*/ new h(1)(2)(3));
}
expect_exact: [
"new/* */e(1)(2)(3);",
"/* */new f(1)(2)(3);",
"/* */new g(1)(2)(3);",
]
}
issue_2705_3: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a.x(1).y(2).z(3);
/*@__PURE__*/ new (b.x)(1).y(2).z(3);
/*@__PURE__*/ new (c.x(1)).y(2).z(3);
/*@__PURE__*/ new (d.x(1).y)(2).z(3);
/*@__PURE__*/ new (e.x(1).y(2)).z(3);
/*@__PURE__*/ new (f.x(1).y(2).z)(3);
/*@__PURE__*/ new (g.x(1).y(2).z(3));
new (/*@__PURE__*/ h).x(1).y(2).z(3);
/* */ new (/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ new j.x(1)).y(2).z(3);
(/*@__PURE__*/ new k.x(1).y)(2).z(3);
(/*@__PURE__*/ new l.x(1).y(2)).z(3);
(/*@__PURE__*/ new m.x(1).y(2).z)(3);
(/*@__PURE__*/ new n.x(1).y(2).z(3));
}
expect_exact: [
"new/* */h.x(1).y(2).z(3);",
"/* */new/* */i.x(1).y(2).z(3);",
"/* */new j.x(1).y(2).z(3);",
"/* */new k.x(1).y(2).z(3);",
"/* */new l.x(1).y(2).z(3);",
"/* */new m.x(1).y(2).z(3);",
]
}
issue_2705_4: {
options = {
side_effects: true,
}
input: {
(/*@__PURE__*/ new x(), y());
(w(), /*@__PURE__*/ new x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2705_5: {
options = {
side_effects: true,
}
input: {
[ /*@__PURE__*/ new x() ];
[ /*@__PURE__*/ new x(), y() ];
[ w(), /*@__PURE__*/ new x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2705_6: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/new (g() || h())(x(), y());
/* */ new (/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"/* */x(),y();",
"/* */new(/* */a()||b())(c(),d());",
]
}
issue_3065_1: {
options = {
inline: true,
@@ -680,130 +438,3 @@ issue_3325_2: {
}
expect_stdout: "PASS"
}
issue_3858: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: false,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {
var f = function(a) {
return function() {
console.log(a);
}();
};
f("PASS");
}
expect_stdout: "PASS"
}
inline_pure_call_1: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {}
}
inline_pure_call_2: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
}
expect: {}
}
inline_pure_call_3: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
console.log(a);
}
expect: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
inline_pure_call_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = /*@__PURE__*/ function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect: {
var a = function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect_stdout: [
"PASS",
"42",
]
}

View File

@@ -1320,11 +1320,10 @@ issue_2878: {
issue_3427: {
options = {
assignments: true,
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
@@ -1490,3 +1489,78 @@ issue_4751: {
};
}
}
super_toString: {
options = {
pure_getters: true,
unsafe: true,
}
input: {
console.log({
f() {
return super.toString();
},
}.f());
}
expect: {
console.log({
f() {
return super.toString();
},
}.f());
}
expect_stdout: "[object Object]"
node_version: ">=4"
}
this_toString: {
options = {
pure_getters: true,
unsafe: true,
}
input: {
console.log({
f() {
return this.toString();
},
}.f());
}
expect: {
console.log({
f() {
return "" + this;
},
}.f());
}
expect_stdout: "[object Object]"
node_version: ">=4"
}
issue_4803: {
options = {
hoist_vars: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var o = {
get f() {
console.log("PASS");
},
} || 42;
for (var k in o)
o[k];
}
expect: {
var k, o = {
get f() {
console.log("PASS");
},
} || 42;
for (k in o)
o[k];
}
expect_stdout: "PASS"
}

View File

@@ -172,3 +172,32 @@ issue_4054: {
}
expect_stdout: "{ p: [Setter] }"
}
issue_4811_1: {
input: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect_stdout: "PASS [object global] true"
}
issue_4811_2: {
options = {
side_effects: true,
}
input: {
(async function() {});
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect_stdout: "PASS [object global] true"
node_version: ">=8"
}

View File

@@ -564,6 +564,34 @@ delete_seq_3: {
}
delete_seq_4: {
options = {
booleans: true,
evaluate: false,
sequences: true,
side_effects: true,
}
input: {
function f() {}
console.log(delete (f(), undefined));
console.log(delete (f(), void 0));
console.log(delete (f(), Infinity));
console.log(delete (f(), 1 / 0));
console.log(delete (f(), NaN));
console.log(delete (f(), 0 / 0));
}
expect: {
function f() {}
console.log(delete void f()),
console.log(delete void f()),
console.log((f(), delete (1 / 0))),
console.log((f(), delete (1 / 0))),
console.log(delete (f(), NaN)),
console.log((f(), delete(0 / 0)));
}
expect_stdout: true
}
delete_seq_4_evaluate: {
options = {
booleans: true,
evaluate: true,
@@ -592,6 +620,35 @@ delete_seq_4: {
}
delete_seq_5: {
options = {
booleans: true,
evaluate: false,
keep_infinity: true,
sequences: true,
side_effects: true,
}
input: {
function f() {}
console.log(delete (f(), undefined));
console.log(delete (f(), void 0));
console.log(delete (f(), Infinity));
console.log(delete (f(), 1 / 0));
console.log(delete (f(), NaN));
console.log(delete (f(), 0 / 0));
}
expect: {
function f() {}
console.log(delete void f()),
console.log(delete void f()),
console.log(delete (f(), Infinity)),
console.log((f(), delete (1 / 0))),
console.log(delete (f(), NaN)),
console.log((f(), delete (0 / 0)));
}
expect_stdout: true
}
delete_seq_5_evaluate: {
options = {
booleans: true,
evaluate: true,

View File

@@ -85,7 +85,7 @@ collapse_vars_4: {
node_version: ">=6"
}
conditionals_farg: {
conditionals_farg_1: {
options = {
conditionals: true,
}
@@ -107,6 +107,28 @@ conditionals_farg: {
node_version: ">=6"
}
conditionals_farg_2: {
options = {
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
var log = console.log;
(function(a) {
return a.length ? log(...a) : log("FAIL");
})([ "PASS" ]);
}
expect: {
var log = console.log;
(function(a) {
return a.length ? log(...a) : log("FAIL");
})([ "PASS" ]);
}
expect_stdout: "PASS"
node_version: ">=6"
}
dont_inline: {
options = {
inline: true,
@@ -272,6 +294,31 @@ reduce_vars_2: {
node_version: ">=6"
}
reduce_vars_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {}
function g() {
return (a => a)(...[ f ]);
}
console.log(g() === g() ? "PASS" : "FAIL");
}
expect: {
function f() {}
function g() {
return (a => a)(...[ f ]);
}
console.log(g() === g() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=6"
}
convert_setter: {
options = {
objects: true,
@@ -667,6 +714,57 @@ unused_var_side_effects: {
node_version: ">=8"
}
unsafe_join_1: {
options = {
unsafe: true,
}
input: {
console.log([ ..."foo" ].join());
}
expect: {
console.log([ ..."foo" ].join());
}
expect_stdout: "f,o,o"
node_version: ">=6"
}
unsafe_join_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([ "foo", ..."bar" ].join(""));
}
expect: {
console.log([ "foo", ..."bar" ].join(""));
}
expect_stdout: "foobar"
node_version: ">=6"
}
unsafe_join_3: {
options = {
unsafe: true,
}
input: {
try {
[].join(...console);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
[].join(...console);
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4329: {
options = {
objects: true,

View File

@@ -73,6 +73,20 @@ tag_parentheses_new: {
node_version: ">=4"
}
tag_parentheses_sequence: {
input: {
var o = {
f() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(42, o.f)``;
}
expect_exact: 'var o={f(){console.log(this===o?"FAIL":"PASS")}};(42,o.f)``;'
expect_stdout: "PASS"
node_version: ">=4"
}
malformed_escape: {
input: {
(function(s) {
@@ -211,7 +225,7 @@ unsafe_evaluate: {
node_version: ">=8"
}
side_effects: {
side_effects_1: {
options = {
side_effects: true,
}
@@ -228,6 +242,30 @@ side_effects: {
node_version: ">=4"
}
side_effects_2: {
options = {
side_effects: true,
}
input: {
var o = {
f() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(42, o.f)``;
}
expect: {
var o = {
f() {
console.log(this === o ? "FAIL" : "PASS");
},
};
(0, o.f)``;
}
expect_stdout: "PASS"
node_version: ">=4"
}
unsafe_side_effects: {
options = {
side_effects: true,

View File

@@ -50,7 +50,7 @@ unicode_parse_variables: {
}
}
unicode_escaped_identifier: {
unicode_escaped_identifier_1: {
input: {
var \u0061 = "\ud800\udc00";
console.log(a);
@@ -59,6 +59,17 @@ unicode_escaped_identifier: {
expect_stdout: "\ud800\udc00"
}
unicode_escaped_identifier_2: {
input: {
var \u{61} = "foo";
var \u{10000} = "bar";
console.log(a, \u{10000});
}
expect_exact: 'var a="foo";var \u{10000}="bar";console.log(a,\u{10000});'
expect_stdout: "foo bar"
node_version: ">=4"
}
unicode_identifier_ascii_only: {
beautify = {
ascii_only: true,

View File

@@ -247,6 +247,30 @@ collapse_vars_4: {
node_version: ">=4"
}
collapse_vars_5: {
options = {
collapse_vars: true,
}
input: {
var a = function* f(b, c) {
b = yield c = b;
console.log(c);
}("PASS");
a.next();
a.next("FAIL");
}
expect: {
var a = function* f(b, c) {
b = yield c = b;
console.log(c);
}("PASS");
a.next();
a.next("FAIL");
}
expect_stdout: "PASS"
node_version: ">=4"
}
collapse_property_lambda: {
options = {
collapse_vars: true,
@@ -388,14 +412,14 @@ functions: {
function* b() {
return !!b;
}
var c = function*(c) {
function* c(c) {
return c;
};
}
if (yield* c(yield* b(yield* a()))) {
function* d() {}
function* e() {
return typeof e;
}
var d = function*() {};
var e = function* y() {
return typeof y;
};
var f = function*(f) {
return f;
};
@@ -446,9 +470,9 @@ functions_use_strict: {
function* b() {
return !!b;
}
var c = function*(c) {
function* c(c) {
return c;
};
}
if (yield* c(yield* b(yield* a()))) {
var d = function*() {};
var e = function* y() {
@@ -465,6 +489,54 @@ functions_use_strict: {
node_version: ">=4"
}
functions_anonymous: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var yield = function*() {
return "PASS";
};
console.log(yield().next(yield).value);
}
expect: {
function* yield() {
return "PASS";
}
console.log(yield().next(yield).value);
}
expect_stdout: "PASS"
node_version: ">=4"
}
functions_inner_var: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var yield = function* a() {
var a;
console.log(a, a);
};
yield().next(yield);
}
expect: {
function* yield() {
var a;
console.log(a, a);
}
yield().next(yield);
}
expect_stdout: "undefined undefined"
node_version: ">=4"
}
negate_iife: {
options = {
negate_iife: true,
@@ -950,3 +1022,43 @@ issue_4641_2: {
]
node_version: ">=10"
}
issue_4769_1: {
options = {
side_effects: true,
}
input: {
console.log(function*() {
(function({} = yield => {}) {})();
}().next().done);
}
expect: {
console.log(function*() {
(function({} = yield => {}) {})();
}().next().done);
}
expect_stdout: "true"
node_version: ">=6"
}
issue_4769_2: {
options = {
inline: true,
}
input: {
console.log(function*() {
return function({} = yield => {}) {
return "PASS";
}();
}().next().value);
}
expect: {
console.log(function*() {
return function({} = yield => {}) {
return "PASS";
}();
}().next().value);
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -1,11 +1,11 @@
{
"version": 3,
"sources": [
"input.js"
],
"names": [],
"mappings": ";;;;;;;;;;;;;;eAAc,OAAO,CAAC,KAAD,C;IAAd,G,YAAA,G;;gBACS,OAAO,CAAC,OAAD,C;IAAhB,K,aAAA,K;;AAEP,GAAG,CAAC,CAAJ,OAAA,GAAG,qBAAM,GAAG,CAAC,CAAJ,CAAM,KAAK,CAAC,CAAZ,CAAN,EAAH",
"sourcesContent": [
"const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"
]
}
{
"version": 3,
"sources": [
"input.js"
],
"names": [],
"mappings": ";;;;;;;;;;;;;;eAAc,OAAO,CAAC,KAAD,C;IAAd,G,YAAA,G;;gBACS,OAAO,CAAC,OAAD,C;IAAhB,K,aAAA,K;;AAEP,GAAG,CAAC,CAAJ,OAAA,GAAG,qBAAM,GAAG,CAAC,CAAJ,CAAM,KAAK,CAAC,CAAZ,CAAN,EAAH",
"sourcesContent": [
"const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"
]
}

View File

@@ -1,8 +1,8 @@
{
"compress": false,
"mangle": {
"properties": {
"regex": "/^_/"
}
}
}
{
"compress": false,
"mangle": {
"properties": {
"regex": "/^_/"
}
}
}

View File

@@ -29,7 +29,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n\n");
assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n");
done();
});
});
@@ -37,7 +37,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments /r/';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n// bar\n\n");
assert.strictEqual(stdout, "/*@preserve*/\n// bar\n");
done();
});
});
@@ -45,7 +45,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n\n");
assert.strictEqual(stdout, "/*@preserve*/\n");
done();
});
});

View File

@@ -392,12 +392,12 @@ describe("comments", function() {
describe("comment filters", function() {
it("Should be able to filter comments by passing regexp", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8");
});
it("Should be able to filter comments with the 'all' option", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8");
});
it("Should be able to filter commments with the 'some' option", function() {
@@ -410,13 +410,12 @@ describe("comments", function() {
var f = function(node, comment) {
return comment.value.length === 8;
};
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n");
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.");
});
it("Should be able to filter comments by passing regex in string format", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "/^!/"}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: "/^!/"}), "/*!test1*/\n//!test3\n//!test6\n//!test8");
});
it("Should be able to get the comment and comment type when using a function", function() {
@@ -424,14 +423,12 @@ describe("comments", function() {
var f = function(node, comment) {
return comment.type == "comment1" || comment.type == "comment3";
};
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6\n");
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6");
});
it("Should be able to filter comments by passing a boolean", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8");
assert.strictEqual(ast.print_to_string({comments: false}), "");
});
@@ -439,10 +436,8 @@ describe("comments", function() {
var ast = UglifyJS.parse("#!Random comment\n//test1\n/*test2*/");
var f = function(node, comment) {
assert.strictEqual(comment.type === "comment5", false);
return true;
};
assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/");
});
@@ -453,9 +448,8 @@ describe("comments", function() {
it("Should have no problem on multiple calls", function() {
const options = {
comments: /ok/
comments: /ok/,
};
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
@@ -463,14 +457,14 @@ describe("comments", function() {
it("Should handle shebang and preamble correctly", function() {
var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", {
output: { preamble: "/* Build */" }
output: { preamble: "/* Build */" },
}).code;
assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;");
});
it("Should handle preamble without shebang correctly", function() {
var code = UglifyJS.minify("var x = 10;", {
output: { preamble: "/* Build */" }
output: { preamble: "/* Build */" },
}).code;
assert.strictEqual(code, "/* Build */\nvar x=10;");
});

View File

@@ -235,8 +235,8 @@ describe("minify", function() {
}
});
var code = result.code;
assert.strictEqual(code, "var a=/* */function(){foo()}();");
})
assert.strictEqual(code, "var a=function(){foo()}();");
});
});
describe("JS_Parse_Error", function() {

View File

@@ -361,4 +361,22 @@ describe("test/reduce.js", function() {
if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/destructured_catch.reduced.js"));
});
it("Should not enumerate `toString` over global context", function() {
if (semver.satisfies(process.version, "<8")) return;
var code = [
"(async function() {});",
"for (var k in this);",
"console.log(k, 42 + this);",
].join("\n");
var result = reduce_test(code, {
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure",
"// minify options: {",
'// "mangle": false',
"// }",
].join("\n"));
});
});

View File

@@ -208,12 +208,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
if (node.expression instanceof U.AST_Function) {
// hoist and return expressions from the IIFE function expression
var body = node.expression.body;
node.expression.body = [];
var seq = [];
body.forEach(function(node) {
node.expression.body.forEach(function(node) {
var expr = expr instanceof U.AST_Exit ? node.value : node.body;
if (expr instanceof U.AST_Node && !U.is_statement(expr)) {
if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr)) {
// collect expressions from each statements' body
seq.push(expr);
}
@@ -264,7 +262,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true;
return List.skip;
default:
if (!has_exit(node)) {
if (!has_exit(node) && can_hoist(node)) {
// hoist function declaration body
var body = node.body;
node.body = [];
@@ -381,11 +379,9 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body
node.start._permute++;
if (!has_exit(node.body.expression)) {
var body = node.body.expression.body;
node.body.expression.body = [];
if (!has_exit(node.body.expression) && can_hoist(node.body.expression)) {
CHANGED = true;
return List.splice(body);
return List.splice(node.body.expression.body);
}
}
}
@@ -673,6 +669,20 @@ function has_loopcontrol(body, loop, label) {
return found;
}
function can_hoist(body) {
var found = false;
body.walk(new U.TreeWalker(function(node) {
if (found) return true;
if (node instanceof U.AST_NewTarget) return found = true;
if (node instanceof U.AST_Scope) {
if (node === body) return;
return true;
}
if (node instanceof U.AST_Super) return found = true;
}));
return !found;
}
function is_timed_out(result) {
return sandbox.is_error(result) && /timed out/.test(result.message);
}

View File

@@ -4,10 +4,10 @@ alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --in-situ"
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -name '*.js'`
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done

182
test/release/bootstrap.sh Executable file
View File

@@ -0,0 +1,182 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/bootstrap \
&& git clone --depth 1 --branch v5.0.0-beta2 https://github.com/twbs/bootstrap.git tmp/bootstrap \
&& cd tmp/bootstrap \
&& rm -rf .git/hooks \
&& patch -p1 <<EOF
--- a/.babelrc.js
+++ /dev/null
@@ -1,12 +0,0 @@
-module.exports = {
- presets: [
- [
- '@babel/preset-env',
- {
- loose: true,
- bugfixes: true,
- modules: false
- }
- ]
- ]
-};
--- a/.gitattributes
+++ b/.gitattributes
@@ -5,0 +6 @@
+*.png binary
--- a/build/build-plugins.js
+++ b/build/build-plugins.js
@@ -14 +13,0 @@ const rollup = require('rollup')
-const { babel } = require('@rollup/plugin-babel')
@@ -19,6 +17,0 @@ const plugins = [
- babel({
- // Only transpile our source code
- exclude: 'node_modules/**',
- // Include the helpers in each file, at most one copy of each
- babelHelpers: 'bundled'
- })
--- a/build/rollup.config.js
+++ b/build/rollup.config.js
@@ -4 +3,0 @@ const path = require('path')
-const { babel } = require('@rollup/plugin-babel')
@@ -15,6 +13,0 @@ const plugins = [
- babel({
- // Only transpile our source code
- exclude: 'node_modules/**',
- // Include the helpers in the bundle, at most one copy of each
- babelHelpers: 'bundled'
- })
--- a/js/tests/integration/rollup.bundle.js
+++ b/js/tests/integration/rollup.bundle.js
@@ -3 +2,0 @@
-const { babel } = require('@rollup/plugin-babel')
@@ -18,4 +16,0 @@ module.exports = {
- babel({
- exclude: 'node_modules/**',
- babelHelpers: 'bundled'
- })
--- a/js/tests/karma.conf.js
+++ b/js/tests/karma.conf.js
@@ -7,2 +6,0 @@ const ip = require('ip')
-const { babel } = require('@rollup/plugin-babel')
-const istanbul = require('rollup-plugin-istanbul')
@@ -84,13 +81,0 @@ const conf = {
- istanbul({
- exclude: [
- 'node_modules/**',
- 'js/tests/unit/**/*.spec.js',
- 'js/tests/helpers/**/*.js'
- ]
- }),
- babel({
- // Only transpile our source code
- exclude: 'node_modules/**',
- // Inline the required helpers in each file
- babelHelpers: 'inline'
- }),
@@ -142 +126,0 @@ if (BROWSERSTACK) {
- 'karma-coverage-istanbul-reporter'
@@ -144 +127,0 @@ if (BROWSERSTACK) {
- reporters.push('coverage-istanbul')
--- a/package.json
+++ b/package.json
@@ -23 +23 @@
- "start": "npm-run-all --parallel watch docs-serve",
+ "start": "npm-run-all --parallel watch",
@@ -28,3 +27,0 @@
- "css-lint": "npm-run-all --continue-on-error --parallel css-lint-*",
- "css-lint-stylelint": "stylelint \"**/*.{css,scss}\" --cache --cache-location .cache/.stylelintcache --rd",
- "css-lint-vars": "fusv scss/ site/assets/scss/",
@@ -44 +40,0 @@
- "js-lint": "eslint --cache --cache-location .cache/.eslintcache --report-unused-disable-directives .",
@@ -46,3 +42,3 @@
- "js-minify-standalone": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js",
- "js-minify-standalone-esm": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.esm.js.map,includeSources,url=bootstrap.esm.min.js.map\" --output dist/js/bootstrap.esm.min.js dist/js/bootstrap.esm.js",
- "js-minify-bundle": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js",
+ "js-minify-standalone": "../../bin/uglifyjs --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js",
+ "js-minify-standalone-esm": "../../bin/uglifyjs --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.esm.js.map,includeSources,url=bootstrap.esm.min.js.map\" --output dist/js/bootstrap.esm.min.js dist/js/bootstrap.esm.js",
+ "js-minify-bundle": "../../bin/uglifyjs --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js",
@@ -56,25 +52 @@
- "lint": "npm-run-all --parallel js-lint css-lint lockfile-lint",
- "docs": "npm-run-all docs-build docs-lint",
- "docs-build": "hugo --cleanDestinationDir",
- "docs-compile": "npm run docs-build",
- "docs-linkinator": "linkinator _gh_pages --recurse --silent --skip \"^(?!http://localhost)\"",
- "docs-vnu": "node build/vnu-jar.js",
- "docs-lint": "npm-run-all --parallel docs-vnu docs-linkinator",
- "docs-serve": "hugo server --port 9001 --disableFastRender",
- "docs-serve-only": "npx sirv-cli _gh_pages --port 9001",
- "lockfile-lint": "lockfile-lint --allowed-hosts npm --allowed-schemes https: --empty-hostname false --type npm --path package-lock.json",
- "update-deps": "ncu -u -x karma-browserstack-launcher,terser && npm update && echo Manually update site/assets/js/vendor",
- "release": "npm-run-all dist release-sri docs-build release-zip*",
- "release-sri": "node build/generate-sri.js",
- "release-version": "node build/change-version.js",
- "release-zip": "cross-env-shell \"rm -rf bootstrap-\$npm_package_version-dist && cp -r dist/ bootstrap-\$npm_package_version-dist && zip -r9 bootstrap-\$npm_package_version-dist.zip bootstrap-\$npm_package_version-dist && rm -rf bootstrap-\$npm_package_version-dist\"",
- "release-zip-examples": "node build/zip-examples.js",
- "dist": "npm-run-all --parallel css js",
- "test": "npm-run-all lint dist js-test docs-build docs-lint",
- "netlify": "cross-env-shell HUGO_BASEURL=\$DEPLOY_PRIME_URL npm-run-all dist release-sri docs-build",
- "watch": "npm-run-all --parallel watch-*",
- "watch-css-main": "nodemon --watch scss/ --ext scss --exec \"npm-run-all css-lint css-compile css-prefix\"",
- "watch-css-dist": "nodemon --watch dist/css/ --ext css --ignore \"dist/css/*.rtl.*\" --exec \"npm run css-rtl\"",
- "watch-css-docs": "nodemon --watch site/assets/scss/ --ext scss --exec \"npm run css-lint\"",
- "watch-js-main": "nodemon --watch js/src/ --ext js --exec \"npm-run-all js-lint js-compile\"",
- "watch-js-docs": "nodemon --watch site/assets/js/ --ext js --exec \"npm run js-lint\""
+ "dist": "npm run css && npm run js"
@@ -103,3 +74,0 @@
- "@babel/cli": "^7.12.13",
- "@babel/core": "^7.12.13",
- "@babel/preset-env": "^7.12.13",
@@ -107 +75,0 @@
- "@rollup/plugin-babel": "^5.2.3",
@@ -115,4 +82,0 @@
- "eslint": "^7.19.0",
- "eslint-config-xo": "^0.34.0",
- "eslint-plugin-import": "^2.22.1",
- "eslint-plugin-unicorn": "^27.0.0",
@@ -122 +85,0 @@
- "hugo-bin": "^0.68.0",
@@ -128 +90,0 @@
- "karma-coverage-istanbul-reporter": "^3.0.3",
@@ -134,2 +95,0 @@
- "linkinator": "^2.13.4",
- "lockfile-lint": "^4.3.7",
@@ -141 +100,0 @@
- "rollup-plugin-istanbul": "^3.0.0",
@@ -144,5 +103 @@
- "shelljs": "^0.8.4",
- "stylelint": "^13.9.0",
- "stylelint-config-twbs-bootstrap": "^2.1.0",
- "terser": "5.1.0",
- "vnu-jar": "21.2.5"
+ "shelljs": "^0.8.4"
@@ -155,3 +109,0 @@
- "hugo-bin": {
- "buildTags": "extended"
- },
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
rm -rf node_modules \
&& npm ci \
&& minify_in_situ "node_modules/@popperjs/core" \
&& rm -rf dist/js/* \
&& minify_in_situ "build" \
&& minify_in_situ "js" \
&& minify_in_situ "site" \
&& npm run dist \
&& minify_in_situ "dist" \
&& npm run js-test

View File

@@ -4,10 +4,10 @@ alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --in-situ"
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -name '*.js'`
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done

View File

@@ -4,10 +4,10 @@ alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --in-situ"
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -name '*.js'`
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done

54
test/release/install.sh Executable file
View File

@@ -0,0 +1,54 @@
echo "::group::GitHub Environment Variables"
echo "CI: $CI"
echo "GITHUB_WORKFLOW: $GITHUB_WORKFLOW"
echo "GITHUB_RUN_ID: $GITHUB_RUN_ID"
echo "GITHUB_RUN_NUMBER: $GITHUB_RUN_NUMBER"
echo "GITHUB_ACTION: $GITHUB_ACTION"
echo "GITHUB_ACTIONS: $GITHUB_ACTIONS"
echo "GITHUB_ACTOR: $GITHUB_ACTOR"
echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY"
echo "GITHUB_EVENT_NAME: $GITHUB_EVENT_NAME"
echo "GITHUB_EVENT_PATH: $GITHUB_EVENT_PATH"
echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE"
echo "GITHUB_SHA: $GITHUB_SHA"
echo "GITHUB_REF: $GITHUB_REF"
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
echo "GITHUB_BASE_REF: $GITHUB_BASE_REF"
echo "GITHUB_SERVER_URL: $GITHUB_SERVER_URL"
echo "GITHUB_API_URL: $GITHUB_API_URL"
echo "GITHUB_GRAPHQL_URL: $GITHUB_GRAPHQL_URL"
echo "::endgroup::"
if command -v timeout &> /dev/null; then NATIVE=1; fi
timeout() {
T=$1
shift
shift
shift
expect <<EOF
set timeout $T
spawn -noecho sh -c "$@"
expect timeout { exit 124 } eof
catch wait ret
exit [lindex \$ret 3]
EOF
return $?
}
if [ $NATIVE ]; then unset -f timeout; fi
git clone --branch v1.6.0 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c ". ~/.nvs/nvs.sh add $NODE && nvs use $NODE"; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version
nvs use $NODE
node --version
npm config set audit false
npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done

View File

@@ -1,17 +1,17 @@
#!/bin/sh
alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
UGLIFY_OPTIONS="--annotations $@"
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --in-situ"
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -name '*.js'`
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
for i in `find $DIRS -name '*.mjs'`
for i in `find $DIRS -type f -name '*.mjs'`
do
ARGS="$ARGS $i"
done
@@ -46,11 +46,15 @@ rm -rf tmp/mathjs \
@@ -68 +75 @@ export function format (value, options) {
- return value.toString()
+ return HACK(value).toString()
--- a/test/node-tests/treeShaking/treeShaking.test.js
+++ b/test/node-tests/treeShaking/treeShaking.test.js
@@ -35 +35 @@ describe('tree shaking', function () {
- it('should apply tree-shaking when bundling', function (done) {
+ if (0) it('should apply tree-shaking when bundling', function (done) {
--- a/test/node-tests/cli/cli.test.js
+++ b/test/node-tests/cli/cli.test.js
@@ -36 +35,0 @@ describe('command line interface', function () {
- const path2 = path.join(__dirname, 'script2')
@@ -38,2 +37,2 @@ describe('command line interface', function () {
- run('"' + path1 + '" "' + path2 + '"', function (e, result) {
- assert.strictEqual(result, '2\n8\n')
+ run('"' + path1 + '"', function (e, result) {
+ assert.strictEqual(result, '2\n')
--- a/test/unit-tests/expression/node/Node.test.js
+++ b/test/unit-tests/expression/node/Node.test.js
@@ -157 +157 @@ describe('Node', function () {

View File

@@ -4,10 +4,10 @@ alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --in-situ"
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -name '*.js'`
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done

View File

@@ -4,15 +4,15 @@ alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --in-situ"
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -name '*.js'`
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
for i in `find $DIRS -name '*.ts' | grep -v '\.d\.ts'`
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
node_modules/.bin/esbuild --loader=ts --target=node14 < "$i" \
@@ -32,6 +32,12 @@ rm -rf tmp/rollup \
- "postpublish": "pinst --enable",
- "prepare": "npm run build",
- "prepublishOnly": "pinst --disable && npm ci && npm run lint:nofix && npm run security && npm run build:bootstrap && npm run test:all",
--- a/test/cli/index.js
+++ b/test/cli/index.js
@@ -13,0 +14,3 @@ sander.rimrafSync(__dirname, 'node_modules');
+sander.rimrafSync(__dirname, 'samples', 'watch', 'bundle-error');
+sander.rimrafSync(__dirname, 'samples', 'watch', 'watch-config-error');
+sander.rimrafSync(__dirname, 'samples', 'watch', 'watch-config-initial-error');
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
npm install esbuild-wasm@0.8.56 \

View File

@@ -4,19 +4,19 @@ alias uglify-js=$PWD/bin/uglifyjs
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --in-situ"
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -name '*.js'`
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
for i in `find $DIRS -name '*.mjs'`
for i in `find $DIRS -type f -name '*.mjs'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
for i in `find $DIRS -name '*.ts' | grep -v '\.d\.ts'`
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
node_modules/.bin/esbuild --loader=ts --target=node14 < "$i" \

View File

@@ -0,0 +1,47 @@
#!/bin/sh
alias uglify-js="node --max-old-space-size=4096 $PWD/bin/uglifyjs"
UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
}
rm -rf tmp/web-tooling-benchmark \
&& git clone --depth 1 --branch v0.5.3 https://github.com/v8/web-tooling-benchmark.git tmp/web-tooling-benchmark \
&& cd tmp/web-tooling-benchmark \
&& rm -rf .git/hooks \
&& patch -l -p1 <<EOF
--- a/package.json
+++ b/package.json
@@ -12 +11,0 @@
- "postinstall": "npm run build:terser-bundled && npm run build:uglify-js-bundled && npm run build",
--- a/src/cli-flags-helper.js
+++ b/src/cli-flags-helper.js
@@ -7 +6,0 @@ const targetList = new Set([
- "chai",
--- a/src/cli.js
+++ b/src/cli.js
@@ -18,0 +19 @@ suite.on("error", event => {
+ global.process.exitCode = 42;
EOF
ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "src" \
&& minify_in_situ "third_party" \
&& rm -rf node_modules \
&& npm ci \
&& rm -rf build/* \
&& npm run build:terser-bundled \
&& npm run build:uglify-js-bundled \
&& minify_in_situ "build" \
&& rm -rf dist \
&& npm run build \
&& minify_in_situ "dist" \
&& node dist/cli.js

View File

@@ -200,9 +200,13 @@ function setup(global, builtins, setup_log, setup_tty) {
});
Object.defineProperties(global, props);
// for Node.js v8+
global.toString = function() {
return "[object global]";
};
if (global.toString !== Object.prototype.toString) {
global.__proto__ = Object.defineProperty(Object.create(global.__proto__), "toString", {
value: function() {
return "[object global]";
},
});
}
function self() {
return this;

View File

@@ -149,6 +149,8 @@ var SUPPORT = function(matrix) {
for_of: "for (var a of []);",
generator: "function* f(){}",
let: "let a;",
logical_assignment: "[].p ??= 0;",
new_target: "function f() { new.target; }",
nullish: "0 ?? 0",
rest: "var [...a] = [];",
rest_object: "var {...a} = {};",
@@ -157,6 +159,9 @@ var SUPPORT = function(matrix) {
template: "``",
trailing_comma: "function f(a,) {}",
});
if (SUPPORT.exponentiation && sandbox.run_code("console.log(10 ** 100 === Math.pow(10, 100));") !== "true\n") {
SUPPORT.exponentiation = false;
}
var VALUES = [
'"a"',
@@ -240,51 +245,31 @@ BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS.push(" in ");
var ASSIGNMENTS = [
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"=",
"+=",
"+=",
"+=",
"+=",
"+=",
"+=",
"+=",
"+=",
"+=",
"+=",
var ASSIGNMENTS = [ "=" ];
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
ASSIGNMENTS.push("+=");
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
ASSIGNMENTS = ASSIGNMENTS.concat([
"-=",
"*=",
"/=",
"%=",
"&=",
"|=",
"^=",
"<<=",
">>=",
">>>=",
"%=",
];
]);
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
if (SUPPORT.exponentiation) ASSIGNMENTS.push("**=");
if (SUPPORT.logical_assignment) ASSIGNMENTS = ASSIGNMENTS.concat([
"&&=",
"||=",
"??=",
]);
var UNARY_SAFE = [
"+",
@@ -1400,13 +1385,16 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
s.push(
instantiate + makeFunction(name) + "(" + createParams(save_async, save_generator) + "){",
strictMode(),
defns()
strictMode()
);
var add_new_target = SUPPORT.new_target && VALUES.indexOf("new.target") < 0;
if (add_new_target) VALUES.push("new.target");
s.push(defns());
if (instantiate) for (var i = rng(4); --i >= 0;) {
s.push((in_class ? "if (this) " : "") + createThisAssignment(recurmax, stmtDepth, canThrow));
}
s.push(_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
if (add_new_target) VALUES.splice(VALUES.indexOf("new.target"), 1);
});
generator = save_generator;
async = save_async;
@@ -1492,7 +1480,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
var name = getVarName();
var fn = name + "." + getDotKey();
var s = "typeof " + fn + ' == "function" && --_calls_ >= 0 && ' + fn + createArgs(recurmax, stmtDepth, canThrow);
var s = "typeof " + fn + ' == "function" && --_calls_ >= 0 && ';
s += rng(5) ? fn : "(" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ", " + fn + ")";
s += createArgs(recurmax, stmtDepth, canThrow);
return mayDefer(canThrow && rng(20) == 0 ? s : name + " && " + s);
case p++:
if (SUPPORT.class) {
@@ -1940,7 +1930,11 @@ function createTypeofExpr(recurmax, stmtDepth, canThrow) {
}
function createValue() {
return VALUES[rng(VALUES.length)];
var v;
do {
v = VALUES[rng(VALUES.length)];
} while (v == "new.target" && rng(200));
return v;
}
function createBinaryOp(noComma, canThrow) {
@@ -2212,8 +2206,7 @@ function log(options) {
function sort_globals(code) {
var globals = run_code("throw Object.keys(this).sort(" + function(global) {
return function(m, n) {
return (n == "toString") - (m == "toString")
|| (typeof global[n] == "function") - (typeof global[m] == "function")
return (typeof global[n] == "function") - (typeof global[m] == "function")
|| (m < n ? -1 : m > n ? 1 : 0);
};
} + "(this));" + code);