Compare commits
31 Commits
harmony-v3
...
v3.0.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3408fc9d32 | ||
|
|
eae26756f1 | ||
|
|
43add9416b | ||
|
|
efcf167e5e | ||
|
|
6ed90913ca | ||
|
|
569c21e952 | ||
|
|
87c3a2c0ce | ||
|
|
baef8bf050 | ||
|
|
0813c5316f | ||
|
|
c88139492d | ||
|
|
cb45886512 | ||
|
|
050474ab44 | ||
|
|
f6c805ae1d | ||
|
|
9464d3c20f | ||
|
|
f18abd1b9c | ||
|
|
3be06ad085 | ||
|
|
265008c948 | ||
|
|
ff526be61d | ||
|
|
e005099fb1 | ||
|
|
504a436e9d | ||
|
|
3ca902258c | ||
|
|
fd0951231c | ||
|
|
9e29b6dad2 | ||
|
|
c391576d52 | ||
|
|
ac73c5d421 | ||
|
|
547f41beba | ||
|
|
daaefc17b9 | ||
|
|
1d407e761e | ||
|
|
2b44f4ae30 | ||
|
|
e51c3541da | ||
|
|
3bf194684b |
@@ -1,5 +1,4 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
before_install: "npm install -g npm"
|
|
||||||
node_js:
|
node_js:
|
||||||
- "0.10"
|
- "0.10"
|
||||||
- "0.12"
|
- "0.12"
|
||||||
|
|||||||
602
README.md
602
README.md
@@ -1,13 +1,12 @@
|
|||||||
UglifyJS 3
|
UglifyJS 3
|
||||||
==========
|
==========
|
||||||
[](https://travis-ci.org/mishoo/UglifyJS2)
|
|
||||||
|
|
||||||
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
|
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
|
||||||
|
|
||||||
#### Note:
|
#### Note:
|
||||||
- **`uglify-js@3.x` has a new API and CLI and is not backwards compatible with [`uglify-js@2.x`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
|
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
|
||||||
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
|
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
|
||||||
- Release versions of `uglify-js` only support ECMAScript 5 (ES5).
|
- `uglify-js` only supports ECMAScript 5 (ES5).
|
||||||
- Those wishing to minify
|
- Those wishing to minify
|
||||||
ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
|
ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
|
||||||
|
|
||||||
@@ -25,8 +24,7 @@ From NPM for programmatic use:
|
|||||||
|
|
||||||
npm install uglify-js
|
npm install uglify-js
|
||||||
|
|
||||||
Usage
|
# Command line usage
|
||||||
-----
|
|
||||||
|
|
||||||
uglifyjs [input files] [options]
|
uglifyjs [input files] [options]
|
||||||
|
|
||||||
@@ -43,7 +41,7 @@ a double dash to prevent input files being used as option arguments:
|
|||||||
|
|
||||||
uglifyjs --compress --mangle -- input.js
|
uglifyjs --compress --mangle -- input.js
|
||||||
|
|
||||||
The available options are:
|
### Command line options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help Print usage information.
|
-h, --help Print usage information.
|
||||||
@@ -142,23 +140,28 @@ The available options are:
|
|||||||
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
||||||
goes to STDOUT.
|
goes to STDOUT.
|
||||||
|
|
||||||
## Source map options
|
## CLI source map options
|
||||||
|
|
||||||
UglifyJS can generate a source map file, which is highly useful for
|
UglifyJS can generate a source map file, which is highly useful for
|
||||||
debugging your compressed JavaScript. To get a source map, pass
|
debugging your compressed JavaScript. To get a source map, pass
|
||||||
`--source-map --output output.js` (source map will be written out to
|
`--source-map --output output.js` (source map will be written out to
|
||||||
`output.js.map`).
|
`output.js.map`).
|
||||||
|
|
||||||
Additionally you might need `--source-map root=<URL>` to pass the URL where
|
Additional options:
|
||||||
the original files can be found. Use `--source-map url=<URL>` to specify
|
|
||||||
the URL where the source map can be found.
|
- `--source-map filename=<NAME>` to specify the name of the source map.
|
||||||
|
|
||||||
|
- `--source-map root=<URL>` to pass the URL where the original files can be found.
|
||||||
|
Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
|
||||||
|
`//# sourceMappingURL=` directive.
|
||||||
|
|
||||||
|
- `--source-map url=<URL>` to specify the URL where the source map can be found.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
uglifyjs /home/doe/work/foo/src/js/file1.js \
|
uglifyjs js/file1.js js/file2.js \
|
||||||
/home/doe/work/foo/src/js/file2.js \
|
|
||||||
-o foo.min.js -c -m \
|
-o foo.min.js -c -m \
|
||||||
--source-map base="/home/doe/work/foo/src",root="http://foo.com/src"
|
--source-map root="http://foo.com/src",url=foo.min.js.map
|
||||||
|
|
||||||
The above will compress and mangle `file1.js` and `file2.js`, will drop the
|
The above will compress and mangle `file1.js` and `file2.js`, will drop the
|
||||||
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
|
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
|
||||||
@@ -177,13 +180,24 @@ CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
|
|||||||
compressed JS by mapping every token in the compiled JS to its original
|
compressed JS by mapping every token in the compiled JS to its original
|
||||||
location.
|
location.
|
||||||
|
|
||||||
To use this feature you need to pass `--in-source-map
|
To use this feature pass `--source-map content="/path/to/input/source.map"`
|
||||||
/path/to/input/source.map` or `--in-source-map inline` if the source map is
|
or `--source-map content=inline` if the source map is included inline with
|
||||||
included inline with the sources. Normally the input source map should also
|
the sources.
|
||||||
point to the file containing the generated JS, so if that's correct you can
|
|
||||||
omit input files from the command line.
|
|
||||||
|
|
||||||
## Mangler options
|
## CLI compress options
|
||||||
|
|
||||||
|
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
|
||||||
|
you can pass a comma-separated list of [compress options](#compress-options).
|
||||||
|
|
||||||
|
Options are in the form `foo=bar`, or just `foo` (the latter implies
|
||||||
|
a boolean option that you want to set `true`; it's effectively a
|
||||||
|
shortcut for `foo=true`).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
uglifyjs file.js -c toplevel,sequences=false
|
||||||
|
|
||||||
|
## CLI mangle options
|
||||||
|
|
||||||
To enable the mangler you need to pass `--mangle` (`-m`). The following
|
To enable the mangler you need to pass `--mangle` (`-m`). The following
|
||||||
(comma-separated) options are supported:
|
(comma-separated) options are supported:
|
||||||
@@ -202,14 +216,14 @@ comma-separated list of names. For example:
|
|||||||
|
|
||||||
to prevent the `require`, `exports` and `$` names from being changed.
|
to prevent the `require`, `exports` and `$` names from being changed.
|
||||||
|
|
||||||
### Mangling property names (`--mangle-props`)
|
### CLI mangling property names (`--mangle-props`)
|
||||||
|
|
||||||
**Note:** this will probably break your code. Mangling property names is a
|
**Note:** this will probably break your code. Mangling property names is a
|
||||||
separate step, different from variable name mangling. Pass
|
separate step, different from variable name mangling. Pass
|
||||||
`--mangle-props`. It will mangle all properties that are seen in some
|
`--mangle-props`. It will mangle all properties that are seen in some
|
||||||
object literal, or that are assigned to. For example:
|
object literal, or that are assigned to. For example:
|
||||||
|
|
||||||
```js
|
```javascript
|
||||||
var x = {
|
var x = {
|
||||||
foo: 1
|
foo: 1
|
||||||
};
|
};
|
||||||
@@ -240,10 +254,10 @@ mangled to the same name in all of them. For this, pass `--name-cache filename.
|
|||||||
and UglifyJS will maintain these mappings in a file which can then be reused.
|
and UglifyJS will maintain these mappings in a file which can then be reused.
|
||||||
It should be initially empty. Example:
|
It should be initially empty. Example:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
rm -f /tmp/cache.json # start fresh
|
$ rm -f /tmp/cache.json # start fresh
|
||||||
uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
|
$ uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
|
||||||
uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
|
$ uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, `part1.js` and `part2.js` will be consistent with each other in terms
|
Now, `part1.js` and `part2.js` will be consistent with each other in terms
|
||||||
@@ -252,18 +266,18 @@ of mangled property names.
|
|||||||
Using the name cache is not necessary if you compress all your files in a
|
Using the name cache is not necessary if you compress all your files in a
|
||||||
single call to UglifyJS.
|
single call to UglifyJS.
|
||||||
|
|
||||||
#### Mangling unquoted names (`--mangle-props keep_quoted`)
|
### Mangling unquoted names (`--mangle-props keep_quoted`)
|
||||||
|
|
||||||
Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
|
Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
|
||||||
so that it is not mangled throughout the entire script even when used in an
|
so that it is not mangled throughout the entire script even when used in an
|
||||||
unquoted style (`o.foo`). Example:
|
unquoted style (`o.foo`). Example:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc
|
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc
|
||||||
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo);
|
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Debugging property name mangling
|
### Debugging property name mangling
|
||||||
|
|
||||||
You can also pass `--mangle-props debug` in order to mangle property names
|
You can also pass `--mangle-props debug` in order to mangle property names
|
||||||
without completely obscuring them. For example the property `o.foo`
|
without completely obscuring them. For example the property `o.foo`
|
||||||
@@ -278,12 +292,144 @@ random number on every compile to simulate mangling changing with different
|
|||||||
inputs (e.g. as you update the input script with new properties), and to help
|
inputs (e.g. as you update the input script with new properties), and to help
|
||||||
identify mistakes like writing mangled keys to storage.
|
identify mistakes like writing mangled keys to storage.
|
||||||
|
|
||||||
## Compressor options
|
|
||||||
|
|
||||||
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
|
# API Reference
|
||||||
you can pass a comma-separated list of options. Options are in the form
|
|
||||||
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
|
Assuming installation via NPM, you can load UglifyJS in your application
|
||||||
to set `true`; it's effectively a shortcut for `foo=true`).
|
like this:
|
||||||
|
```javascript
|
||||||
|
var UglifyJS = require("uglify-js");
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a single high level minification function, `minify(files, options)`, which will
|
||||||
|
performs all the steps in a configurable manner.
|
||||||
|
Example:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify("var b = function() {};");
|
||||||
|
console.log(result.code); // minified output
|
||||||
|
console.log(result.error); // runtime error
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also compress multiple files:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify({
|
||||||
|
"file1.js": "var a = function() {};",
|
||||||
|
"file2.js": "var b = function() {};"
|
||||||
|
});
|
||||||
|
console.log(result.code);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Minify options
|
||||||
|
|
||||||
|
- `warnings` (default `false`) — pass `true` to display compressor warnings.
|
||||||
|
|
||||||
|
- `parse` (default `{}`) — pass an object if you wish to specify some
|
||||||
|
additional [parse options](#parse-options).
|
||||||
|
|
||||||
|
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
||||||
|
Pass an object to specify custom [compress options](#compress-options).
|
||||||
|
|
||||||
|
- `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.
|
||||||
|
Pass an object to specify custom [mangle property options](#mangle-properties-options).
|
||||||
|
|
||||||
|
- `output` (default `null`) — pass an object if you wish to specify
|
||||||
|
additional [output options](#output-options). The defaults are optimized
|
||||||
|
for best compression.
|
||||||
|
|
||||||
|
- `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
|
||||||
|
variable and function name mangling and to drop unused variables and functions.
|
||||||
|
|
||||||
|
- `ie8` (default `false`) - set to `true` to support IE8.
|
||||||
|
|
||||||
|
## Minify option structure
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
warnings: false,
|
||||||
|
parse: {
|
||||||
|
// parse options
|
||||||
|
},
|
||||||
|
compress: {
|
||||||
|
// compress options
|
||||||
|
},
|
||||||
|
mangle: {
|
||||||
|
// mangle options
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
// mangle property options
|
||||||
|
}
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
// output options
|
||||||
|
},
|
||||||
|
sourceMap: {
|
||||||
|
// source map options
|
||||||
|
},
|
||||||
|
toplevel: false,
|
||||||
|
ie8: false,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source map options
|
||||||
|
|
||||||
|
To generate a source map:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
|
||||||
|
sourceMap: {
|
||||||
|
filename: "out.js",
|
||||||
|
url: "out.js.map"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(result.code); // minified output
|
||||||
|
console.log(result.map); // source map
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the source map is not saved in a file, it's just returned in
|
||||||
|
`result.map`. The value passed for `sourceMap.url` is only used to set
|
||||||
|
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
|
||||||
|
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
|
||||||
|
in source map file.
|
||||||
|
|
||||||
|
You can set option `sourceMap.url` to be `"inline"` and source map will
|
||||||
|
be appended to code.
|
||||||
|
|
||||||
|
You can also specify sourceRoot property to be included in source map:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
|
||||||
|
sourceMap: {
|
||||||
|
root: "http://example.com/src",
|
||||||
|
url: "out.js.map"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're compressing compiled JavaScript and have a source map for it, you
|
||||||
|
can use `sourceMap.content`:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
|
||||||
|
sourceMap: {
|
||||||
|
content: "content from compiled.js.map",
|
||||||
|
url: "minified.js.map"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// same as before, it returns `code` and `map`
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
|
||||||
|
|
||||||
|
## Parse options
|
||||||
|
|
||||||
|
- `bare_returns` (default `false`) -- support top level `return` statements
|
||||||
|
- `html5_comments` (default `true`)
|
||||||
|
- `shebang` (default `true`) -- support `#!command` as the first line
|
||||||
|
|
||||||
|
## Compress options
|
||||||
|
|
||||||
- `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
|
comma operator. May be set to a positive integer to specify the maximum number
|
||||||
@@ -316,6 +462,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
|||||||
- `unsafe_proto` (default: false) -- optimize expressions like
|
- `unsafe_proto` (default: false) -- optimize expressions like
|
||||||
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
|
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
|
||||||
|
|
||||||
|
- `unsafe_regexp` (default: false) -- enable substitutions of variables with
|
||||||
|
`RegExp` values the same way as if they are constants.
|
||||||
|
|
||||||
- `conditionals` -- apply optimizations for `if`-s and conditional
|
- `conditionals` -- apply optimizations for `if`-s and conditional
|
||||||
expressions
|
expressions
|
||||||
|
|
||||||
@@ -399,14 +548,146 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
|||||||
compressor from discarding function names. Useful for code relying on
|
compressor from discarding function names. Useful for code relying on
|
||||||
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
|
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
|
||||||
|
|
||||||
- `passes` -- default `1`. Number of times to run compress. Use an
|
- `passes` -- default `1`. Number of times to run compress with a maximum of 3.
|
||||||
integer argument larger than 1 to further reduce code size in some cases.
|
In some cases more than one pass leads to further compressed code. Keep in
|
||||||
Note: raising the number of passes will increase uglify compress time.
|
mind more passes will take more time.
|
||||||
|
|
||||||
- `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.
|
being compressed into `1/0`, which may cause performance issues on Chrome.
|
||||||
|
|
||||||
### The `unsafe` option
|
- `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();`
|
||||||
|
|
||||||
|
## Mangle options
|
||||||
|
|
||||||
|
- `reserved` - pass an array of identifiers that should be excluded from mangling
|
||||||
|
|
||||||
|
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||||
|
default).
|
||||||
|
|
||||||
|
- `eval` — mangle names visible in scopes where eval or with are used
|
||||||
|
(disabled by default).
|
||||||
|
|
||||||
|
- `keep_fnames` -- default `false`. Pass `true` to not mangle
|
||||||
|
function names. Useful for code relying on `Function.prototype.name`.
|
||||||
|
See also: the `keep_fnames` [compress option](#compress-options).
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// test.js
|
||||||
|
var globalVar;
|
||||||
|
function funcName(firstLongName, anotherLongName) {
|
||||||
|
var myVariable = firstLongName + anotherLongName;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```javascript
|
||||||
|
var code = fs.readFileSync("test.js", "utf8");
|
||||||
|
|
||||||
|
UglifyJS.minify(code).code;
|
||||||
|
// 'function funcName(a,n){}var globalVar;'
|
||||||
|
|
||||||
|
UglifyJS.minify(code, { mangle: { reserved: ['firstLongName'] } }).code;
|
||||||
|
// 'function funcName(firstLongName,a){}var globalVar;'
|
||||||
|
|
||||||
|
UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
|
||||||
|
// 'function n(n,a){}var a;'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mangle properties options
|
||||||
|
|
||||||
|
- `regex` — Pass a RegExp to only mangle certain names
|
||||||
|
- `keep_quoted` — Only mangle unquoted property names
|
||||||
|
- `debug` — Mangle names with the original name still present. Defaults to `false`.
|
||||||
|
Pass an empty string to enable, or a non-empty string to set the suffix.
|
||||||
|
|
||||||
|
## Output options
|
||||||
|
|
||||||
|
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
|
||||||
|
regexps (affects directives with non-ascii characters becoming invalid)
|
||||||
|
- `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.
|
||||||
|
- `bracketize` (default `false`) -- always insert brackets 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, `"some"` to preserve some comments, a regular expression string
|
||||||
|
(e.g. `/^!/`) or a function.
|
||||||
|
- `indent_level` (default 4)
|
||||||
|
- `indent_start` (default 0) -- prefix all lines by that many spaces
|
||||||
|
- `inline_script` (default `false`) -- escape the slash in occurrences of
|
||||||
|
`</script` in strings
|
||||||
|
- `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)
|
||||||
|
- `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 preserve lines, but it
|
||||||
|
only works if `beautify` is set to `false`.
|
||||||
|
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
|
||||||
|
objects
|
||||||
|
- `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
|
||||||
|
more double quotes in the string itself.
|
||||||
|
- `1` -- always use single quotes
|
||||||
|
- `2` -- always use double quotes
|
||||||
|
- `3` -- always use the original quotes
|
||||||
|
- `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)
|
||||||
|
- `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
|
||||||
|
function expressions. See
|
||||||
|
[#640](https://github.com/mishoo/UglifyJS2/issues/640) for more details.
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
|
||||||
|
### Keeping copyright notices or other comments
|
||||||
|
|
||||||
|
You can pass `--comments` to retain certain comments in the output. By
|
||||||
|
default it will keep JSDoc-style comments that contain "@preserve",
|
||||||
|
"@license" or "@cc_on" (conditional compilation for IE). You can pass
|
||||||
|
`--comments all` to keep all the comments, or a valid JavaScript regexp to
|
||||||
|
keep only comments that match this regexp. For example `--comments /^!/`
|
||||||
|
will keep comments like `/*! Copyright Notice */`.
|
||||||
|
|
||||||
|
Note, however, that there might be situations where comments are lost. For
|
||||||
|
example:
|
||||||
|
```javascript
|
||||||
|
function f() {
|
||||||
|
/** @preserve Foo Bar */
|
||||||
|
function g() {
|
||||||
|
// this function is never called
|
||||||
|
}
|
||||||
|
return something();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Even though it has "@preserve", the comment will be lost because the inner
|
||||||
|
function `g` (which is the AST node to which the comment is attached to) is
|
||||||
|
discarded by the compressor as not referenced.
|
||||||
|
|
||||||
|
The safest comments where to place copyright information (or other info that
|
||||||
|
needs to be kept in the output) are comments attached to toplevel nodes.
|
||||||
|
|
||||||
|
### The `unsafe` `compress` option
|
||||||
|
|
||||||
It enables some transformations that *might* break code logic in certain
|
It enables some transformations that *might* break code logic in certain
|
||||||
contrived cases, but should be fine for most code. You might want to try it
|
contrived cases, but should be fine for most code. You might want to try it
|
||||||
@@ -459,12 +740,13 @@ code as usual. The build will contain the `const` declarations if you use
|
|||||||
them. If you are targeting < ES6 environments which does not support `const`,
|
them. If you are targeting < ES6 environments which does not support `const`,
|
||||||
using `var` with `reduce_vars` (enabled by default) should suffice.
|
using `var` with `reduce_vars` (enabled by default) should suffice.
|
||||||
|
|
||||||
#### Conditional compilation, API
|
### Conditional compilation API
|
||||||
|
|
||||||
You can also use conditional compilation via the programmatic API. With the difference that the
|
You can also use conditional compilation via the programmatic API. With the difference that the
|
||||||
property name is `global_defs` and is a compressor property:
|
property name is `global_defs` and is a compressor property:
|
||||||
|
|
||||||
```js
|
```javascript
|
||||||
uglifyJS.minify([ "input.js"], {
|
var result = uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
|
||||||
compress: {
|
compress: {
|
||||||
dead_code: true,
|
dead_code: true,
|
||||||
global_defs: {
|
global_defs: {
|
||||||
@@ -474,81 +756,46 @@ uglifyJS.minify([ "input.js"], {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Beautifier options
|
### Using native Uglify AST with `minify()`
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
- `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.
|
|
||||||
- `indent_level` (default 4)
|
|
||||||
- `indent_start` (default 0) -- prefix all lines by that many spaces
|
|
||||||
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
|
|
||||||
objects
|
|
||||||
- `space_colon` (default `true`) -- insert a space after the colon signs
|
|
||||||
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
|
|
||||||
regexps (affects directives with non-ascii characters becoming invalid)
|
|
||||||
- `inline_script` (default `false`) -- escape the slash in occurrences of
|
|
||||||
`</script` in strings
|
|
||||||
- `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.
|
|
||||||
- `max_line_len` (default 32000) -- maximum line length (for uglified code)
|
|
||||||
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
|
||||||
`do`, `while` or `with` statements, even if their body is a single
|
|
||||||
statement.
|
|
||||||
- `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).
|
|
||||||
- `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.
|
|
||||||
- `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
|
|
||||||
more double quotes in the string itself.
|
|
||||||
- `1` -- always use single quotes
|
|
||||||
- `2` -- always use double quotes
|
|
||||||
- `3` -- always use the original quotes
|
|
||||||
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
|
|
||||||
quotes from property names in object literals.
|
|
||||||
|
|
||||||
### Keeping copyright notices or other comments
|
|
||||||
|
|
||||||
You can pass `--comments` to retain certain comments in the output. By
|
|
||||||
default it will keep JSDoc-style comments that contain "@preserve",
|
|
||||||
"@license" or "@cc_on" (conditional compilation for IE). You can pass
|
|
||||||
`--comments all` to keep all the comments, or a valid JavaScript regexp to
|
|
||||||
keep only comments that match this regexp. For example `--comments /^!/`
|
|
||||||
will keep comments like `/*! Copyright Notice */`.
|
|
||||||
|
|
||||||
Note, however, that there might be situations where comments are lost. For
|
|
||||||
example:
|
|
||||||
```javascript
|
```javascript
|
||||||
function f() {
|
// example: parse only, produce native Uglify AST
|
||||||
/** @preserve Foo Bar */
|
|
||||||
function g() {
|
var result = UglifyJS.minify(code, {
|
||||||
// this function is never called
|
parse: {},
|
||||||
|
compress: false,
|
||||||
|
mangle: false,
|
||||||
|
output: {
|
||||||
|
ast: true,
|
||||||
|
code: false // optional - faster if false
|
||||||
}
|
}
|
||||||
return something();
|
});
|
||||||
}
|
|
||||||
|
// result.ast contains native Uglify AST
|
||||||
|
```
|
||||||
|
```javascript
|
||||||
|
// example: accept native Uglify AST input and then compress and mangle
|
||||||
|
// to produce both code and native AST.
|
||||||
|
|
||||||
|
var result = UglifyJS.minify(ast, {
|
||||||
|
compress: {},
|
||||||
|
mangle: {},
|
||||||
|
output: {
|
||||||
|
ast: true,
|
||||||
|
code: true // optional - faster if false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// result.ast contains native Uglify AST
|
||||||
|
// result.code contains the minified code in string form.
|
||||||
```
|
```
|
||||||
|
|
||||||
Even though it has "@preserve", the comment will be lost because the inner
|
### Working with Uglify AST
|
||||||
function `g` (which is the AST node to which the comment is attached to) is
|
|
||||||
discarded by the compressor as not referenced.
|
|
||||||
|
|
||||||
The safest comments where to place copyright information (or other info that
|
Transversal and transformation of the native AST can be performed through
|
||||||
needs to be kept in the output) are comments attached to toplevel nodes.
|
[`TreeWalker`](http://lisperator.net/uglifyjs/walk) and
|
||||||
|
[`TreeTransformer`](http://lisperator.net/uglifyjs/transform) respectively.
|
||||||
|
|
||||||
## Support for the SpiderMonkey AST
|
### ESTree / SpiderMonkey AST
|
||||||
|
|
||||||
UglifyJS has its own abstract syntax tree format; for
|
UglifyJS has its own abstract syntax tree format; for
|
||||||
[practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/)
|
[practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/)
|
||||||
@@ -576,138 +823,5 @@ Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
|
|||||||
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
|
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
|
||||||
in total it's a bit more than just using UglifyJS's own parser.
|
in total it's a bit more than just using UglifyJS's own parser.
|
||||||
|
|
||||||
API Reference
|
[acorn]: https://github.com/ternjs/acorn
|
||||||
-------------
|
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
|
||||||
|
|
||||||
Assuming installation via NPM, you can load UglifyJS in your application
|
|
||||||
like this:
|
|
||||||
```javascript
|
|
||||||
var UglifyJS = require("uglify-js");
|
|
||||||
```
|
|
||||||
|
|
||||||
There is a single toplevel function, `minify(files, options)`, which will
|
|
||||||
performs all the steps in a configurable manner.
|
|
||||||
Example:
|
|
||||||
```javascript
|
|
||||||
var result = UglifyJS.minify("var b = function() {};");
|
|
||||||
console.log(result.code); // minified output
|
|
||||||
console.log(result.error); // runtime error
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also compress multiple files:
|
|
||||||
```javascript
|
|
||||||
var result = UglifyJS.minify({
|
|
||||||
"file1.js": "var a = function() {};",
|
|
||||||
"file2.js": "var b = function() {};"
|
|
||||||
});
|
|
||||||
console.log(result.code);
|
|
||||||
```
|
|
||||||
|
|
||||||
To generate a source map:
|
|
||||||
```javascript
|
|
||||||
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
|
|
||||||
sourceMap: {
|
|
||||||
filename: "out.js",
|
|
||||||
url: "out.js.map"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log(result.code); // minified output
|
|
||||||
console.log(result.map); // source map
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the source map is not saved in a file, it's just returned in
|
|
||||||
`result.map`. The value passed for `sourceMap.url` is only used to set
|
|
||||||
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
|
|
||||||
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
|
|
||||||
in source map file.
|
|
||||||
|
|
||||||
You can set option `sourceMap.url` to be `"inline"` and source map will
|
|
||||||
be appended to code.
|
|
||||||
|
|
||||||
You can also specify sourceRoot property to be included in source map:
|
|
||||||
```javascript
|
|
||||||
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
|
|
||||||
sourceMap: {
|
|
||||||
root: "http://example.com/src",
|
|
||||||
url: "out.js.map"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're compressing compiled JavaScript and have a source map for it, you
|
|
||||||
can use `sourceMap.content`:
|
|
||||||
```javascript
|
|
||||||
var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
|
|
||||||
sourceMap: {
|
|
||||||
content: "content from compiled.js.map",
|
|
||||||
url: "minified.js.map"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// same as before, it returns `code` and `map`
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
|
|
||||||
|
|
||||||
Other options:
|
|
||||||
|
|
||||||
- `warnings` (default `false`) — pass `true` to display compressor warnings.
|
|
||||||
|
|
||||||
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
|
|
||||||
an object to specify mangling options (see below).
|
|
||||||
|
|
||||||
- `mangleProperties` (default `false`) — pass an object to specify custom
|
|
||||||
mangle property options.
|
|
||||||
|
|
||||||
- `output` (default `null`) — pass an object if you wish to specify
|
|
||||||
additional [output options](#beautifier-options). The defaults are optimized
|
|
||||||
for best compression.
|
|
||||||
|
|
||||||
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
|
||||||
Pass an object to specify custom [compressor options](#compressor-options).
|
|
||||||
|
|
||||||
- `parse` (default {}) — pass an object if you wish to specify some
|
|
||||||
additional [parser options](#the-parser).
|
|
||||||
|
|
||||||
##### mangle
|
|
||||||
|
|
||||||
- `reserved` - pass an array of identifiers that should be excluded from mangling
|
|
||||||
|
|
||||||
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
|
||||||
default).
|
|
||||||
|
|
||||||
- `eval` — mangle names visible in scopes where eval or with are used
|
|
||||||
(disabled by default).
|
|
||||||
|
|
||||||
- `keep_fnames` -- default `false`. Pass `true` to not mangle
|
|
||||||
function names. Useful for code relying on `Function.prototype.name`.
|
|
||||||
See also: the `keep_fnames` [compress option](#compressor-options).
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
//tst.js
|
|
||||||
var globalVar;
|
|
||||||
function funcName(firstLongName, anotherLongName)
|
|
||||||
{
|
|
||||||
var myVariable = firstLongName + anotherLongName;
|
|
||||||
}
|
|
||||||
|
|
||||||
UglifyJS.minify("tst.js").code;
|
|
||||||
// 'function funcName(a,n){}var globalVar;'
|
|
||||||
|
|
||||||
UglifyJS.minify("tst.js", { mangle: { reserved: ['firstLongName'] } }).code;
|
|
||||||
// 'function funcName(firstLongName,a){}var globalVar;'
|
|
||||||
|
|
||||||
UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code;
|
|
||||||
// 'function n(n,a){}var a;'
|
|
||||||
```
|
|
||||||
|
|
||||||
##### mangle.properties options
|
|
||||||
|
|
||||||
- `regex` — Pass a RegExp to only mangle certain names
|
|
||||||
- `keep_quoted` — Only mangle unquoted property names
|
|
||||||
- `debug` — Mangle names with the original name still present. Defaults to `false`.
|
|
||||||
Pass an empty string to enable, or a non-empty string to set the suffix.
|
|
||||||
|
|
||||||
[acorn]: https://github.com/ternjs/acorn
|
|
||||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
|
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ var options = {
|
|||||||
compress: false,
|
compress: false,
|
||||||
mangle: false
|
mangle: false
|
||||||
};
|
};
|
||||||
program._name = info.name;
|
program.version(info.name + ' ' + info.version);
|
||||||
program.version(info.version);
|
|
||||||
program.parseArgv = program.parse;
|
program.parseArgv = program.parse;
|
||||||
program.parse = undefined;
|
program.parse = undefined;
|
||||||
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
|
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
|
||||||
|
|||||||
20
lib/ast.js
20
lib/ast.js
@@ -495,10 +495,10 @@ var AST_Finally = DEFNODE("Finally", null, {
|
|||||||
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
||||||
}, AST_Block);
|
}, AST_Block);
|
||||||
|
|
||||||
/* -----[ VAR/CONST ]----- */
|
/* -----[ VAR ]----- */
|
||||||
|
|
||||||
var AST_Definitions = DEFNODE("Definitions", "definitions", {
|
var AST_Definitions = DEFNODE("Definitions", "definitions", {
|
||||||
$documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
|
$documentation: "Base class for `var` nodes (variable declarations/initializations)",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
definitions: "[AST_VarDef*] array of variable definitions"
|
definitions: "[AST_VarDef*] array of variable definitions"
|
||||||
},
|
},
|
||||||
@@ -516,14 +516,10 @@ var AST_Var = DEFNODE("Var", null, {
|
|||||||
$documentation: "A `var` statement"
|
$documentation: "A `var` statement"
|
||||||
}, AST_Definitions);
|
}, AST_Definitions);
|
||||||
|
|
||||||
var AST_Const = DEFNODE("Const", null, {
|
|
||||||
$documentation: "A `const` statement"
|
|
||||||
}, AST_Definitions);
|
|
||||||
|
|
||||||
var AST_VarDef = DEFNODE("VarDef", "name value", {
|
var AST_VarDef = DEFNODE("VarDef", "name value", {
|
||||||
$documentation: "A variable declaration; only appears in a AST_Definitions node",
|
$documentation: "A variable declaration; only appears in a AST_Definitions node",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
|
name: "[AST_SymbolVar] name of the variable",
|
||||||
value: "[AST_Node?] initializer, or null of there's no initializer"
|
value: "[AST_Node?] initializer, or null of there's no initializer"
|
||||||
},
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
@@ -689,8 +685,8 @@ var AST_Object = DEFNODE("Object", "properties", {
|
|||||||
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||||
$documentation: "Base class for literal object properties",
|
$documentation: "Base class for literal object properties",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.",
|
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an AST_SymbolAccessor.",
|
||||||
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
|
value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
|
||||||
},
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
@@ -728,17 +724,13 @@ var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
|||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
||||||
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
$documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolVar = DEFNODE("SymbolVar", null, {
|
var AST_SymbolVar = DEFNODE("SymbolVar", null, {
|
||||||
$documentation: "Symbol defining a variable",
|
$documentation: "Symbol defining a variable",
|
||||||
}, AST_SymbolDeclaration);
|
}, AST_SymbolDeclaration);
|
||||||
|
|
||||||
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
|
|
||||||
$documentation: "A constant declaration"
|
|
||||||
}, AST_SymbolDeclaration);
|
|
||||||
|
|
||||||
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
|
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
|
||||||
$documentation: "Symbol naming a function argument",
|
$documentation: "Symbol naming a function argument",
|
||||||
}, AST_SymbolVar);
|
}, AST_SymbolVar);
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ function Compressor(options, false_by_default) {
|
|||||||
unsafe_comps : false,
|
unsafe_comps : false,
|
||||||
unsafe_math : false,
|
unsafe_math : false,
|
||||||
unsafe_proto : false,
|
unsafe_proto : false,
|
||||||
|
unsafe_regexp : false,
|
||||||
unused : !false_by_default,
|
unused : !false_by_default,
|
||||||
warnings : false,
|
warnings : false,
|
||||||
}, true);
|
}, true);
|
||||||
@@ -354,16 +355,27 @@ merge(Compressor.prototype, {
|
|||||||
// So existing transformation rules can work on them.
|
// So existing transformation rules can work on them.
|
||||||
node.argnames.forEach(function(arg, i) {
|
node.argnames.forEach(function(arg, i) {
|
||||||
var d = arg.definition();
|
var d = arg.definition();
|
||||||
|
if (!node.uses_arguments && d.fixed === undefined) {
|
||||||
d.fixed = function() {
|
d.fixed = function() {
|
||||||
return iife.args[i] || make_node(AST_Undefined, iife);
|
return iife.args[i] || make_node(AST_Undefined, iife);
|
||||||
};
|
};
|
||||||
mark(d, true);
|
mark(d, true);
|
||||||
|
} else {
|
||||||
|
d.fixed = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
descend();
|
descend();
|
||||||
pop();
|
pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Accessor) {
|
||||||
|
var save_ids = safe_ids;
|
||||||
|
safe_ids = Object.create(null);
|
||||||
|
descend();
|
||||||
|
safe_ids = save_ids;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_Binary
|
if (node instanceof AST_Binary
|
||||||
&& (node.operator == "&&" || node.operator == "||")) {
|
&& (node.operator == "&&" || node.operator == "||")) {
|
||||||
node.left.walk(tw);
|
node.left.walk(tw);
|
||||||
@@ -475,8 +487,7 @@ merge(Compressor.prototype, {
|
|||||||
if (def.fixed === false) return false;
|
if (def.fixed === false) return false;
|
||||||
if (def.fixed != null && (!value || def.references.length > 0)) return false;
|
if (def.fixed != null && (!value || def.references.length > 0)) return false;
|
||||||
return !def.orig.some(function(sym) {
|
return !def.orig.some(function(sym) {
|
||||||
return sym instanceof AST_SymbolConst
|
return sym instanceof AST_SymbolDefun
|
||||||
|| sym instanceof AST_SymbolDefun
|
|
||||||
|| sym instanceof AST_SymbolLambda;
|
|| sym instanceof AST_SymbolLambda;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -491,7 +502,9 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function reset_def(def) {
|
function reset_def(def) {
|
||||||
def.escaped = false;
|
def.escaped = false;
|
||||||
if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) {
|
if (def.scope.uses_eval) {
|
||||||
|
def.fixed = false;
|
||||||
|
} else if (!def.global || compressor.toplevel(def)) {
|
||||||
def.fixed = undefined;
|
def.fixed = undefined;
|
||||||
} else {
|
} else {
|
||||||
def.fixed = false;
|
def.fixed = false;
|
||||||
@@ -1215,12 +1228,12 @@ merge(Compressor.prototype, {
|
|||||||
&& !node.expression.has_side_effects(compressor);
|
&& !node.expression.has_side_effects(compressor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// may_eq_null()
|
// may_throw_on_access()
|
||||||
// returns true if this node may evaluate to null or undefined
|
// returns true if this node may be null, undefined or contain `AST_Accessor`
|
||||||
(function(def) {
|
(function(def) {
|
||||||
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
|
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
|
||||||
var pure_getters = compressor.option("pure_getters");
|
var pure_getters = compressor.option("pure_getters");
|
||||||
return !pure_getters || this._eq_null(pure_getters);
|
return !pure_getters || this._throw_on_access(pure_getters);
|
||||||
});
|
});
|
||||||
|
|
||||||
function is_strict(pure_getters) {
|
function is_strict(pure_getters) {
|
||||||
@@ -1232,7 +1245,12 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Undefined, return_true);
|
def(AST_Undefined, return_true);
|
||||||
def(AST_Constant, return_false);
|
def(AST_Constant, return_false);
|
||||||
def(AST_Array, return_false);
|
def(AST_Array, return_false);
|
||||||
def(AST_Object, return_false);
|
def(AST_Object, function(pure_getters) {
|
||||||
|
if (!is_strict(pure_getters)) return false;
|
||||||
|
for (var i = this.properties.length; --i >=0;)
|
||||||
|
if (this.properties[i].value instanceof AST_Accessor) return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
def(AST_Function, return_false);
|
def(AST_Function, return_false);
|
||||||
def(AST_UnaryPostfix, return_false);
|
def(AST_UnaryPostfix, return_false);
|
||||||
def(AST_UnaryPrefix, function() {
|
def(AST_UnaryPrefix, function() {
|
||||||
@@ -1241,33 +1259,33 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Binary, function(pure_getters) {
|
def(AST_Binary, function(pure_getters) {
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
case "&&":
|
case "&&":
|
||||||
return this.left._eq_null(pure_getters);
|
return this.left._throw_on_access(pure_getters);
|
||||||
case "||":
|
case "||":
|
||||||
return this.left._eq_null(pure_getters)
|
return this.left._throw_on_access(pure_getters)
|
||||||
&& this.right._eq_null(pure_getters);
|
&& this.right._throw_on_access(pure_getters);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
def(AST_Assign, function(pure_getters) {
|
def(AST_Assign, function(pure_getters) {
|
||||||
return this.operator == "="
|
return this.operator == "="
|
||||||
&& this.right._eq_null(pure_getters);
|
&& this.right._throw_on_access(pure_getters);
|
||||||
})
|
})
|
||||||
def(AST_Conditional, function(pure_getters) {
|
def(AST_Conditional, function(pure_getters) {
|
||||||
return this.consequent._eq_null(pure_getters)
|
return this.consequent._throw_on_access(pure_getters)
|
||||||
|| this.alternative._eq_null(pure_getters);
|
|| this.alternative._throw_on_access(pure_getters);
|
||||||
})
|
})
|
||||||
def(AST_Sequence, function(pure_getters) {
|
def(AST_Sequence, function(pure_getters) {
|
||||||
return this.expressions[this.expressions.length - 1]._eq_null(pure_getters);
|
return this.expressions[this.expressions.length - 1]._throw_on_access(pure_getters);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(pure_getters) {
|
def(AST_SymbolRef, function(pure_getters) {
|
||||||
if (this.is_undefined) return true;
|
if (this.is_undefined) return true;
|
||||||
if (!is_strict(pure_getters)) return false;
|
if (!is_strict(pure_getters)) return false;
|
||||||
var fixed = this.fixed_value();
|
var fixed = this.fixed_value();
|
||||||
return !fixed || fixed._eq_null(pure_getters);
|
return !fixed || fixed._throw_on_access(pure_getters);
|
||||||
});
|
});
|
||||||
})(function(node, func) {
|
})(function(node, func) {
|
||||||
node.DEFMETHOD("_eq_null", func);
|
node.DEFMETHOD("_throw_on_access", func);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ boolean/negation helpers ]----- */
|
/* -----[ boolean/negation helpers ]----- */
|
||||||
@@ -1808,11 +1826,11 @@ merge(Compressor.prototype, {
|
|||||||
return any(this.elements, compressor);
|
return any(this.elements, compressor);
|
||||||
});
|
});
|
||||||
def(AST_Dot, function(compressor){
|
def(AST_Dot, function(compressor){
|
||||||
return this.expression.may_eq_null(compressor)
|
return this.expression.may_throw_on_access(compressor)
|
||||||
|| this.expression.has_side_effects(compressor);
|
|| this.expression.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Sub, function(compressor){
|
def(AST_Sub, function(compressor){
|
||||||
return this.expression.may_eq_null(compressor)
|
return this.expression.may_throw_on_access(compressor)
|
||||||
|| this.expression.has_side_effects(compressor)
|
|| this.expression.has_side_effects(compressor)
|
||||||
|| this.property.has_side_effects(compressor);
|
|| this.property.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
@@ -2365,6 +2383,7 @@ merge(Compressor.prototype, {
|
|||||||
var args = trim(this.args, compressor, first_in_statement);
|
var args = trim(this.args, compressor, first_in_statement);
|
||||||
return args && make_sequence(this, args);
|
return args && make_sequence(this, args);
|
||||||
});
|
});
|
||||||
|
def(AST_Accessor, return_null);
|
||||||
def(AST_Function, return_null);
|
def(AST_Function, return_null);
|
||||||
def(AST_Binary, function(compressor, first_in_statement){
|
def(AST_Binary, function(compressor, first_in_statement){
|
||||||
var right = this.right.drop_side_effect_free(compressor);
|
var right = this.right.drop_side_effect_free(compressor);
|
||||||
@@ -2432,11 +2451,11 @@ merge(Compressor.prototype, {
|
|||||||
return values && make_sequence(this, values);
|
return values && make_sequence(this, values);
|
||||||
});
|
});
|
||||||
def(AST_Dot, function(compressor, first_in_statement){
|
def(AST_Dot, function(compressor, first_in_statement){
|
||||||
if (this.expression.may_eq_null(compressor)) return this;
|
if (this.expression.may_throw_on_access(compressor)) return this;
|
||||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||||
});
|
});
|
||||||
def(AST_Sub, function(compressor, first_in_statement){
|
def(AST_Sub, function(compressor, first_in_statement){
|
||||||
if (this.expression.may_eq_null(compressor)) return this;
|
if (this.expression.may_throw_on_access(compressor)) return this;
|
||||||
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||||
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
|
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
|
||||||
var property = this.property.drop_side_effect_free(compressor);
|
var property = this.property.drop_side_effect_free(compressor);
|
||||||
@@ -3747,7 +3766,7 @@ merge(Compressor.prototype, {
|
|||||||
if (fixed) {
|
if (fixed) {
|
||||||
if (d.should_replace === undefined) {
|
if (d.should_replace === undefined) {
|
||||||
var init = fixed.evaluate(compressor);
|
var init = fixed.evaluate(compressor);
|
||||||
if (init !== fixed) {
|
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
|
||||||
init = make_node_from_constant(init, fixed);
|
init = make_node_from_constant(init, fixed);
|
||||||
var value_length = init.optimize(compressor).print_to_string().length;
|
var value_length = init.optimize(compressor).print_to_string().length;
|
||||||
var fn;
|
var fn;
|
||||||
|
|||||||
@@ -111,23 +111,19 @@
|
|||||||
},
|
},
|
||||||
Property: function(M) {
|
Property: function(M) {
|
||||||
var key = M.key;
|
var key = M.key;
|
||||||
var name = key.type == "Identifier" ? key.name : key.value;
|
|
||||||
var args = {
|
var args = {
|
||||||
start : my_start_token(key),
|
start : my_start_token(key),
|
||||||
end : my_end_token(M.value),
|
end : my_end_token(M.value),
|
||||||
key : name,
|
key : key.type == "Identifier" ? key.name : key.value,
|
||||||
value : from_moz(M.value)
|
value : from_moz(M.value)
|
||||||
};
|
};
|
||||||
switch (M.kind) {
|
if (M.kind == "init") return new AST_ObjectKeyVal(args);
|
||||||
case "init":
|
args.key = new AST_SymbolAccessor({
|
||||||
return new AST_ObjectKeyVal(args);
|
name: args.key
|
||||||
case "set":
|
});
|
||||||
args.value.name = from_moz(key);
|
args.value = new AST_Accessor(args.value);
|
||||||
return new AST_ObjectSetter(args);
|
if (M.kind == "get") return new AST_ObjectGetter(args);
|
||||||
case "get":
|
if (M.kind == "set") return new AST_ObjectSetter(args);
|
||||||
args.value.name = from_moz(key);
|
|
||||||
return new AST_ObjectGetter(args);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
ArrayExpression: function(M) {
|
ArrayExpression: function(M) {
|
||||||
return new AST_Array({
|
return new AST_Array({
|
||||||
@@ -172,7 +168,7 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
VariableDeclaration: function(M) {
|
VariableDeclaration: function(M) {
|
||||||
return new (M.kind === "const" ? AST_Const : AST_Var)({
|
return new AST_Var({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
definitions : M.declarations.map(from_moz)
|
definitions : M.declarations.map(from_moz)
|
||||||
@@ -208,7 +204,7 @@
|
|||||||
Identifier: function(M) {
|
Identifier: function(M) {
|
||||||
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
|
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
|
||||||
return new ( p.type == "LabeledStatement" ? AST_Label
|
return new ( p.type == "LabeledStatement" ? AST_Label
|
||||||
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
|
: p.type == "VariableDeclarator" && p.id === M ? AST_SymbolVar
|
||||||
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
|
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
|
||||||
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
|
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
|
||||||
: p.type == "CatchClause" ? AST_SymbolCatch
|
: p.type == "CatchClause" ? AST_SymbolCatch
|
||||||
@@ -260,10 +256,7 @@
|
|||||||
map("CallExpression", AST_Call, "callee>expression, arguments@args");
|
map("CallExpression", AST_Call, "callee>expression, arguments@args");
|
||||||
|
|
||||||
def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
|
def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
|
||||||
return {
|
return to_moz_scope("Program", M);
|
||||||
type: "Program",
|
|
||||||
body: M.body.map(to_moz)
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
|
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
|
||||||
@@ -271,7 +264,7 @@
|
|||||||
type: "FunctionDeclaration",
|
type: "FunctionDeclaration",
|
||||||
id: to_moz(M.name),
|
id: to_moz(M.name),
|
||||||
params: M.argnames.map(to_moz),
|
params: M.argnames.map(to_moz),
|
||||||
body: to_moz_block(M)
|
body: to_moz_scope("BlockStatement", M)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -280,7 +273,7 @@
|
|||||||
type: "FunctionExpression",
|
type: "FunctionExpression",
|
||||||
id: to_moz(M.name),
|
id: to_moz(M.name),
|
||||||
params: M.argnames.map(to_moz),
|
params: M.argnames.map(to_moz),
|
||||||
body: to_moz_block(M)
|
body: to_moz_scope("BlockStatement", M)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -331,7 +324,7 @@
|
|||||||
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
|
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
|
||||||
return {
|
return {
|
||||||
type: "VariableDeclaration",
|
type: "VariableDeclaration",
|
||||||
kind: M instanceof AST_Const ? "const" : "var",
|
kind: "var",
|
||||||
declarations: M.definitions.map(to_moz)
|
declarations: M.definitions.map(to_moz)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -386,11 +379,10 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
||||||
var key = (
|
var key = {
|
||||||
is_identifier(M.key)
|
type: "Literal",
|
||||||
? {type: "Identifier", name: M.key}
|
value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
|
||||||
: {type: "Literal", value: M.key}
|
};
|
||||||
);
|
|
||||||
var kind;
|
var kind;
|
||||||
if (M instanceof AST_ObjectKeyVal) {
|
if (M instanceof AST_ObjectKeyVal) {
|
||||||
kind = "init";
|
kind = "init";
|
||||||
@@ -551,8 +543,8 @@
|
|||||||
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
||||||
exports, my_start_token, my_end_token, from_moz
|
exports, my_start_token, my_end_token, from_moz
|
||||||
);
|
);
|
||||||
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
|
me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")(
|
||||||
to_moz, to_moz_block
|
to_moz, to_moz_block, to_moz_scope
|
||||||
);
|
);
|
||||||
MOZ_TO_ME[moztype] = moz_to_me;
|
MOZ_TO_ME[moztype] = moz_to_me;
|
||||||
def_to_moz(mytype, me_to_moz);
|
def_to_moz(mytype, me_to_moz);
|
||||||
@@ -610,4 +602,14 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function to_moz_scope(type, node) {
|
||||||
|
var body = node.body.map(to_moz);
|
||||||
|
if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) {
|
||||||
|
body.unshift(to_moz(new AST_EmptyStatement(node.body[0])));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: type,
|
||||||
|
body: body
|
||||||
|
};
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -70,8 +70,6 @@ function OutputStream(options) {
|
|||||||
semicolons : true,
|
semicolons : true,
|
||||||
shebang : true,
|
shebang : true,
|
||||||
source_map : null,
|
source_map : null,
|
||||||
space_colon : true,
|
|
||||||
unescape_regexps : false,
|
|
||||||
width : 80,
|
width : 80,
|
||||||
wrap_iife : false,
|
wrap_iife : false,
|
||||||
}, true);
|
}, true);
|
||||||
@@ -357,7 +355,7 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
function colon() {
|
function colon() {
|
||||||
print(":");
|
print(":");
|
||||||
if (options.space_colon) space();
|
space();
|
||||||
};
|
};
|
||||||
|
|
||||||
var add_mapping = options.source_map ? function(token, name) {
|
var add_mapping = options.source_map ? function(token, name) {
|
||||||
@@ -1033,9 +1031,6 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Var, function(self, output){
|
DEFPRINT(AST_Var, function(self, output){
|
||||||
self._do_print(output, "var");
|
self._do_print(output, "var");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Const, function(self, output){
|
|
||||||
self._do_print(output, "const");
|
|
||||||
});
|
|
||||||
|
|
||||||
function parenthesize_for_noin(node, output, noin) {
|
function parenthesize_for_noin(node, output, noin) {
|
||||||
if (!noin) node.print(output);
|
if (!noin) node.print(output);
|
||||||
@@ -1264,45 +1259,14 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function regexp_safe_literal(code) {
|
|
||||||
return [
|
|
||||||
0x5c , // \
|
|
||||||
0x2f , // /
|
|
||||||
0x2e , // .
|
|
||||||
0x2b , // +
|
|
||||||
0x2a , // *
|
|
||||||
0x3f , // ?
|
|
||||||
0x28 , // (
|
|
||||||
0x29 , // )
|
|
||||||
0x5b , // [
|
|
||||||
0x5d , // ]
|
|
||||||
0x7b , // {
|
|
||||||
0x7d , // }
|
|
||||||
0x24 , // $
|
|
||||||
0x5e , // ^
|
|
||||||
0x3a , // :
|
|
||||||
0x7c , // |
|
|
||||||
0x21 , // !
|
|
||||||
0x0a , // \n
|
|
||||||
0x0d , // \r
|
|
||||||
0x00 , // \0
|
|
||||||
0xfeff , // Unicode BOM
|
|
||||||
0x2028 , // unicode "line separator"
|
|
||||||
0x2029 , // unicode "paragraph separator"
|
|
||||||
].indexOf(code) < 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFPRINT(AST_RegExp, function(self, output){
|
DEFPRINT(AST_RegExp, function(self, output){
|
||||||
var str = self.getValue().toString();
|
var regexp = self.getValue();
|
||||||
|
var str = regexp.toString();
|
||||||
|
if (regexp.raw_source) {
|
||||||
|
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
|
||||||
|
}
|
||||||
if (output.option("ascii_only")) {
|
if (output.option("ascii_only")) {
|
||||||
str = output.to_ascii(str);
|
str = output.to_ascii(str);
|
||||||
} else if (output.option("unescape_regexps")) {
|
|
||||||
str = str.split("\\\\").map(function(str){
|
|
||||||
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
|
|
||||||
var code = parseInt(s.substr(2), 16);
|
|
||||||
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
|
|
||||||
});
|
|
||||||
}).join("\\\\");
|
|
||||||
}
|
}
|
||||||
output.print(str);
|
output.print(str);
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
|
|||||||
117
lib/parse.js
117
lib/parse.js
@@ -285,7 +285,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
|
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
|
||||||
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
|
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
|
||||||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
|
(type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
|
||||||
prev_was_dot = (type == "punc" && value == ".");
|
if (type == "punc" && value == ".") {
|
||||||
|
prev_was_dot = true;
|
||||||
|
} else if (!is_comment) {
|
||||||
|
prev_was_dot = false;
|
||||||
|
}
|
||||||
var ret = {
|
var ret = {
|
||||||
type : type,
|
type : type,
|
||||||
value : value,
|
value : value,
|
||||||
@@ -473,29 +477,31 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
|
var read_regexp = with_eof_error("Unterminated regular expression", function(source) {
|
||||||
var prev_backslash = false, ch, in_class = false;
|
var prev_backslash = false, ch, in_class = false;
|
||||||
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
|
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
|
||||||
parse_error("Unexpected line terminator");
|
parse_error("Unexpected line terminator");
|
||||||
} else if (prev_backslash) {
|
} else if (prev_backslash) {
|
||||||
regexp += "\\" + ch;
|
source += "\\" + ch;
|
||||||
prev_backslash = false;
|
prev_backslash = false;
|
||||||
} else if (ch == "[") {
|
} else if (ch == "[") {
|
||||||
in_class = true;
|
in_class = true;
|
||||||
regexp += ch;
|
source += ch;
|
||||||
} else if (ch == "]" && in_class) {
|
} else if (ch == "]" && in_class) {
|
||||||
in_class = false;
|
in_class = false;
|
||||||
regexp += ch;
|
source += ch;
|
||||||
} else if (ch == "/" && !in_class) {
|
} else if (ch == "/" && !in_class) {
|
||||||
break;
|
break;
|
||||||
} else if (ch == "\\") {
|
} else if (ch == "\\") {
|
||||||
prev_backslash = true;
|
prev_backslash = true;
|
||||||
} else {
|
} else {
|
||||||
regexp += ch;
|
source += ch;
|
||||||
}
|
}
|
||||||
var mods = read_name();
|
var mods = read_name();
|
||||||
try {
|
try {
|
||||||
return token("regexp", new RegExp(regexp, mods));
|
var regexp = new RegExp(source, mods);
|
||||||
|
regexp.raw_source = source;
|
||||||
|
return token("regexp", regexp);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
parse_error(e.message);
|
parse_error(e.message);
|
||||||
}
|
}
|
||||||
@@ -801,28 +807,23 @@ function parse($TEXT, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var statement = embed_tokens(function() {
|
var statement = embed_tokens(function() {
|
||||||
var tmp;
|
|
||||||
handle_regexp();
|
handle_regexp();
|
||||||
switch (S.token.type) {
|
switch (S.token.type) {
|
||||||
case "string":
|
case "string":
|
||||||
var dir = false;
|
if (S.in_directives) {
|
||||||
if (S.in_directives === true) {
|
var token = peek();
|
||||||
if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) {
|
if (S.token.raw.indexOf("\\") == -1
|
||||||
|
&& (token.nlb
|
||||||
|
|| is_token(token, "eof")
|
||||||
|
|| is_token(token, "punc", ";")
|
||||||
|
|| is_token(token, "punc", "}"))) {
|
||||||
S.input.add_directive(S.token.value);
|
S.input.add_directive(S.token.value);
|
||||||
} else {
|
} else {
|
||||||
S.in_directives = false;
|
S.in_directives = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var dir = S.in_directives, stat = simple_statement();
|
var dir = S.in_directives, stat = simple_statement();
|
||||||
if (dir) {
|
return dir ? new AST_Directive(stat.body) : stat;
|
||||||
return new AST_Directive({
|
|
||||||
start : stat.body.start,
|
|
||||||
end : stat.body.end,
|
|
||||||
quote : stat.body.quote,
|
|
||||||
value : stat.body.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return stat;
|
|
||||||
case "num":
|
case "num":
|
||||||
case "regexp":
|
case "regexp":
|
||||||
case "operator":
|
case "operator":
|
||||||
@@ -854,75 +855,97 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "keyword":
|
case "keyword":
|
||||||
switch (tmp = S.token.value, next(), tmp) {
|
switch (S.token.value) {
|
||||||
case "break":
|
case "break":
|
||||||
|
next();
|
||||||
return break_cont(AST_Break);
|
return break_cont(AST_Break);
|
||||||
|
|
||||||
case "continue":
|
case "continue":
|
||||||
|
next();
|
||||||
return break_cont(AST_Continue);
|
return break_cont(AST_Continue);
|
||||||
|
|
||||||
case "debugger":
|
case "debugger":
|
||||||
|
next();
|
||||||
semicolon();
|
semicolon();
|
||||||
return new AST_Debugger();
|
return new AST_Debugger();
|
||||||
|
|
||||||
case "do":
|
case "do":
|
||||||
|
next();
|
||||||
|
var body = in_loop(statement);
|
||||||
|
expect_token("keyword", "while");
|
||||||
|
var condition = parenthesised();
|
||||||
|
semicolon(true);
|
||||||
return new AST_Do({
|
return new AST_Do({
|
||||||
body : in_loop(statement),
|
body : body,
|
||||||
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
|
condition : condition
|
||||||
});
|
});
|
||||||
|
|
||||||
case "while":
|
case "while":
|
||||||
|
next();
|
||||||
return new AST_While({
|
return new AST_While({
|
||||||
condition : parenthesised(),
|
condition : parenthesised(),
|
||||||
body : in_loop(statement)
|
body : in_loop(statement)
|
||||||
});
|
});
|
||||||
|
|
||||||
case "for":
|
case "for":
|
||||||
|
next();
|
||||||
return for_();
|
return for_();
|
||||||
|
|
||||||
case "function":
|
case "function":
|
||||||
|
next();
|
||||||
return function_(AST_Defun);
|
return function_(AST_Defun);
|
||||||
|
|
||||||
case "if":
|
case "if":
|
||||||
|
next();
|
||||||
return if_();
|
return if_();
|
||||||
|
|
||||||
case "return":
|
case "return":
|
||||||
if (S.in_function == 0 && !options.bare_returns)
|
if (S.in_function == 0 && !options.bare_returns)
|
||||||
croak("'return' outside of function");
|
croak("'return' outside of function");
|
||||||
|
next();
|
||||||
|
var value = null;
|
||||||
|
if (is("punc", ";")) {
|
||||||
|
next();
|
||||||
|
} else if (!can_insert_semicolon()) {
|
||||||
|
value = expression(true);
|
||||||
|
semicolon();
|
||||||
|
}
|
||||||
return new AST_Return({
|
return new AST_Return({
|
||||||
value: ( is("punc", ";")
|
value: value
|
||||||
? (next(), null)
|
|
||||||
: can_insert_semicolon()
|
|
||||||
? null
|
|
||||||
: (tmp = expression(true), semicolon(), tmp) )
|
|
||||||
});
|
});
|
||||||
|
|
||||||
case "switch":
|
case "switch":
|
||||||
|
next();
|
||||||
return new AST_Switch({
|
return new AST_Switch({
|
||||||
expression : parenthesised(),
|
expression : parenthesised(),
|
||||||
body : in_loop(switch_body_)
|
body : in_loop(switch_body_)
|
||||||
});
|
});
|
||||||
|
|
||||||
case "throw":
|
case "throw":
|
||||||
|
next();
|
||||||
if (S.token.nlb)
|
if (S.token.nlb)
|
||||||
croak("Illegal newline after 'throw'");
|
croak("Illegal newline after 'throw'");
|
||||||
|
var value = expression(true);
|
||||||
|
semicolon();
|
||||||
return new AST_Throw({
|
return new AST_Throw({
|
||||||
value: (tmp = expression(true), semicolon(), tmp)
|
value: value
|
||||||
});
|
});
|
||||||
|
|
||||||
case "try":
|
case "try":
|
||||||
|
next();
|
||||||
return try_();
|
return try_();
|
||||||
|
|
||||||
case "var":
|
case "var":
|
||||||
return tmp = var_(), semicolon(), tmp;
|
next();
|
||||||
|
var node = var_();
|
||||||
case "const":
|
semicolon();
|
||||||
return tmp = const_(), semicolon(), tmp;
|
return node;
|
||||||
|
|
||||||
case "with":
|
case "with":
|
||||||
if (S.input.has_directive("use strict")) {
|
if (S.input.has_directive("use strict")) {
|
||||||
croak("Strict mode may not include a with statement");
|
croak("Strict mode may not include a with statement");
|
||||||
}
|
}
|
||||||
|
next();
|
||||||
return new AST_With({
|
return new AST_With({
|
||||||
expression : parenthesised(),
|
expression : parenthesised(),
|
||||||
body : statement()
|
body : statement()
|
||||||
@@ -1153,16 +1176,13 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function vardefs(no_in, in_const) {
|
function vardefs(no_in) {
|
||||||
var a = [];
|
var a = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
a.push(new AST_VarDef({
|
a.push(new AST_VarDef({
|
||||||
start : S.token,
|
start : S.token,
|
||||||
name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
|
name : as_symbol(AST_SymbolVar),
|
||||||
value : is("operator", "=")
|
value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
|
||||||
? (next(), expression(false, no_in))
|
|
||||||
: in_const && S.input.has_directive("use strict")
|
|
||||||
? croak("Missing initializer in const declaration") : null,
|
|
||||||
end : prev()
|
end : prev()
|
||||||
}));
|
}));
|
||||||
if (!is("punc", ","))
|
if (!is("punc", ","))
|
||||||
@@ -1175,15 +1195,7 @@ function parse($TEXT, options) {
|
|||||||
var var_ = function(no_in) {
|
var var_ = function(no_in) {
|
||||||
return new AST_Var({
|
return new AST_Var({
|
||||||
start : prev(),
|
start : prev(),
|
||||||
definitions : vardefs(no_in, false),
|
definitions : vardefs(no_in),
|
||||||
end : prev()
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var const_ = function() {
|
|
||||||
return new AST_Const({
|
|
||||||
start : prev(),
|
|
||||||
definitions : vardefs(false, true),
|
|
||||||
end : prev()
|
end : prev()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1324,10 +1336,15 @@ function parse($TEXT, options) {
|
|||||||
var type = start.type;
|
var type = start.type;
|
||||||
var name = as_property_name();
|
var name = as_property_name();
|
||||||
if (type == "name" && !is("punc", ":")) {
|
if (type == "name" && !is("punc", ":")) {
|
||||||
|
var key = new AST_SymbolAccessor({
|
||||||
|
start: S.token,
|
||||||
|
name: as_property_name(),
|
||||||
|
end: prev()
|
||||||
|
});
|
||||||
if (name == "get") {
|
if (name == "get") {
|
||||||
a.push(new AST_ObjectGetter({
|
a.push(new AST_ObjectGetter({
|
||||||
start : start,
|
start : start,
|
||||||
key : as_atom_node(),
|
key : key,
|
||||||
value : create_accessor(),
|
value : create_accessor(),
|
||||||
end : prev()
|
end : prev()
|
||||||
}));
|
}));
|
||||||
@@ -1336,7 +1353,7 @@ function parse($TEXT, options) {
|
|||||||
if (name == "set") {
|
if (name == "set") {
|
||||||
a.push(new AST_ObjectSetter({
|
a.push(new AST_ObjectSetter({
|
||||||
start : start,
|
start : start,
|
||||||
key : as_atom_node(),
|
key : key,
|
||||||
value : create_accessor(),
|
value : create_accessor(),
|
||||||
end : prev()
|
end : prev()
|
||||||
}));
|
}));
|
||||||
|
|||||||
12
lib/scope.js
12
lib/scope.js
@@ -156,8 +156,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||||||
// later.
|
// later.
|
||||||
(node.scope = defun.parent_scope).def_function(node);
|
(node.scope = defun.parent_scope).def_function(node);
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolVar
|
else if (node instanceof AST_SymbolVar) {
|
||||||
|| node instanceof AST_SymbolConst) {
|
|
||||||
defun.def_variable(node);
|
defun.def_variable(node);
|
||||||
if (defun !== scope) {
|
if (defun !== scope) {
|
||||||
node.mark_enclosed(options);
|
node.mark_enclosed(options);
|
||||||
@@ -268,7 +267,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
|
|||||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
this.def_variable(new AST_SymbolConst({
|
this.def_variable(new AST_SymbolFunarg({
|
||||||
name: "arguments",
|
name: "arguments",
|
||||||
start: this.start,
|
start: this.start,
|
||||||
end: this.end
|
end: this.end
|
||||||
@@ -361,11 +360,6 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
|||||||
return this.definition().unmangleable(options);
|
return this.definition().unmangleable(options);
|
||||||
});
|
});
|
||||||
|
|
||||||
// property accessors are not mangleable
|
|
||||||
AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// labels are always mangleable
|
// labels are always mangleable
|
||||||
AST_Label.DEFMETHOD("unmangleable", function(){
|
AST_Label.DEFMETHOD("unmangleable", function(){
|
||||||
return false;
|
return false;
|
||||||
@@ -491,8 +485,6 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
|||||||
}
|
}
|
||||||
else if (node instanceof AST_Var)
|
else if (node instanceof AST_Var)
|
||||||
base54.consider("var");
|
base54.consider("var");
|
||||||
else if (node instanceof AST_Const)
|
|
||||||
base54.consider("const");
|
|
||||||
else if (node instanceof AST_Lambda)
|
else if (node instanceof AST_Lambda)
|
||||||
base54.consider("function");
|
base54.consider("function");
|
||||||
else if (node instanceof AST_For)
|
else if (node instanceof AST_For)
|
||||||
|
|||||||
18
package.json
18
package.json
@@ -4,7 +4,7 @@
|
|||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"version": "3.0.3",
|
"version": "3.0.9",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
@@ -33,19 +33,9 @@
|
|||||||
"source-map": "~0.5.1"
|
"source-map": "~0.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"acorn": "~0.6.0",
|
"acorn": "~5.0.3",
|
||||||
"escodegen": "~1.3.3",
|
"mocha": "~2.3.4",
|
||||||
"esfuzz": "~0.3.1",
|
"semver": "~5.3.0"
|
||||||
"estraverse": "~1.5.1",
|
|
||||||
"mocha": "~2.3.4"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"uglify-to-browserify": "~1.0.0"
|
|
||||||
},
|
|
||||||
"browserify": {
|
|
||||||
"transform": [
|
|
||||||
"uglify-to-browserify"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node test/run-tests.js"
|
"test": "node test/run-tests.js"
|
||||||
|
|||||||
@@ -583,8 +583,8 @@ collapse_vars_assignment: {
|
|||||||
return a = a;
|
return a = a;
|
||||||
}
|
}
|
||||||
function f1(c) {
|
function f1(c) {
|
||||||
const a = 3 / c;
|
var a = 3 / c;
|
||||||
const b = 1 - a;
|
var b = 1 - a;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
function f2(c) {
|
function f2(c) {
|
||||||
@@ -724,10 +724,10 @@ collapse_vars_misc1: {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
function f1(x) { var y = 5 - x; return y; }
|
function f1(x) { var y = 5 - x; return y; }
|
||||||
function f2(x) { const z = foo(), y = z / (5 - x); return y; }
|
function f2(x) { var z = foo(), y = z / (5 - x); return y; }
|
||||||
function f3(x) { var z = foo(), y = (5 - x) / z; return y; }
|
function f3(x) { var z = foo(), y = (5 - x) / z; return y; }
|
||||||
function f4(x) { var z = foo(), y = (5 - u) / z; return y; }
|
function f4(x) { var z = foo(), y = (5 - u) / z; return y; }
|
||||||
function f5(x) { const z = foo(), y = (5 - window.x) / z; return y; }
|
function f5(x) { var z = foo(), y = (5 - window.x) / z; return y; }
|
||||||
function f6() { var b = window.a * window.z; return b && zap(); }
|
function f6() { var b = window.a * window.z; return b && zap(); }
|
||||||
function f7() { var b = window.a * window.z; return b + b; }
|
function f7() { var b = window.a * window.z; return b + b; }
|
||||||
function f8() { var b = window.a * window.z; var c = b + 5; return b + c; }
|
function f8() { var b = window.a * window.z; var c = b + 5; return b + c; }
|
||||||
@@ -744,7 +744,7 @@ collapse_vars_misc1: {
|
|||||||
function f2(x) { return foo() / (5 - x) }
|
function f2(x) { return foo() / (5 - x) }
|
||||||
function f3(x) { return (5 - x) / foo() }
|
function f3(x) { return (5 - x) / foo() }
|
||||||
function f4(x) { var z = foo(); return (5 - u) / z }
|
function f4(x) { var z = foo(); return (5 - u) / z }
|
||||||
function f5(x) { const z = foo(); return (5 - window.x) / z }
|
function f5(x) { var z = foo(); return (5 - window.x) / z }
|
||||||
function f6() { return window.a * window.z && zap() }
|
function f6() { return window.a * window.z && zap() }
|
||||||
function f7() { var b = window.a * window.z; return b + b }
|
function f7() { var b = window.a * window.z; return b + b }
|
||||||
function f8() { var b = window.a * window.z; return b + (b + 5) }
|
function f8() { var b = window.a * window.z; return b + (b + 5) }
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
issue_1191: {
|
|
||||||
options = {
|
|
||||||
evaluate : true,
|
|
||||||
booleans : true,
|
|
||||||
comparisons : true,
|
|
||||||
dead_code : true,
|
|
||||||
conditionals : true,
|
|
||||||
side_effects : true,
|
|
||||||
unused : true,
|
|
||||||
hoist_funs : true,
|
|
||||||
if_return : true,
|
|
||||||
join_vars : true,
|
|
||||||
sequences : false,
|
|
||||||
collapse_vars : false,
|
|
||||||
reduce_vars : true,
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
function foo(rot) {
|
|
||||||
const rotTol = 5;
|
|
||||||
if (rot < -rotTol || rot > rotTol)
|
|
||||||
bar();
|
|
||||||
baz();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
function foo(rot) {
|
|
||||||
(rot < -5 || rot > 5) && bar();
|
|
||||||
baz();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
issue_1194: {
|
|
||||||
options = {
|
|
||||||
evaluate : true,
|
|
||||||
booleans : true,
|
|
||||||
comparisons : true,
|
|
||||||
dead_code : true,
|
|
||||||
conditionals : true,
|
|
||||||
side_effects : true,
|
|
||||||
unused : true,
|
|
||||||
hoist_funs : true,
|
|
||||||
if_return : true,
|
|
||||||
join_vars : true,
|
|
||||||
sequences : false,
|
|
||||||
collapse_vars : false,
|
|
||||||
reduce_vars : true,
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
function f1() {const a = "X"; return a + a;}
|
|
||||||
function f2() {const aa = "X"; return aa + aa;}
|
|
||||||
function f3() {const aaa = "X"; return aaa + aaa;}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
function f1(){return"XX"}
|
|
||||||
function f2(){return"XX"}
|
|
||||||
function f3(){return"XX"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
issue_1396: {
|
|
||||||
options = {
|
|
||||||
evaluate : true,
|
|
||||||
booleans : true,
|
|
||||||
comparisons : true,
|
|
||||||
dead_code : true,
|
|
||||||
conditionals : true,
|
|
||||||
side_effects : true,
|
|
||||||
unused : true,
|
|
||||||
hoist_funs : true,
|
|
||||||
if_return : true,
|
|
||||||
join_vars : true,
|
|
||||||
sequences : false,
|
|
||||||
collapse_vars : false,
|
|
||||||
reduce_vars : true,
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
function foo(a) {
|
|
||||||
const VALUE = 1;
|
|
||||||
console.log(2 | VALUE);
|
|
||||||
console.log(VALUE + 1);
|
|
||||||
console.log(VALUE);
|
|
||||||
console.log(a & VALUE);
|
|
||||||
}
|
|
||||||
function bar() {
|
|
||||||
const s = "01234567890123456789";
|
|
||||||
console.log(s + s + s + s + s);
|
|
||||||
|
|
||||||
const CONSTANT = "abc";
|
|
||||||
console.log(CONSTANT + CONSTANT + CONSTANT + CONSTANT + CONSTANT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
function foo(a) {
|
|
||||||
console.log(3);
|
|
||||||
console.log(2);
|
|
||||||
console.log(1);
|
|
||||||
console.log(1 & a);
|
|
||||||
}
|
|
||||||
function bar() {
|
|
||||||
const s = "01234567890123456789";
|
|
||||||
console.log(s + s + s + s + s);
|
|
||||||
|
|
||||||
console.log("abcabcabcabcabc");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unused_regexp_literal: {
|
|
||||||
options = {
|
|
||||||
evaluate : true,
|
|
||||||
booleans : true,
|
|
||||||
comparisons : true,
|
|
||||||
dead_code : true,
|
|
||||||
conditionals : true,
|
|
||||||
side_effects : true,
|
|
||||||
unused : true,
|
|
||||||
hoist_funs : true,
|
|
||||||
if_return : true,
|
|
||||||
join_vars : true,
|
|
||||||
sequences : false,
|
|
||||||
collapse_vars : false,
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
function f(){ var a = /b/; }
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
function f(){}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
regexp_literal_not_const: {
|
|
||||||
options = {
|
|
||||||
evaluate : true,
|
|
||||||
booleans : true,
|
|
||||||
comparisons : true,
|
|
||||||
dead_code : true,
|
|
||||||
conditionals : true,
|
|
||||||
side_effects : true,
|
|
||||||
unused : true,
|
|
||||||
hoist_funs : true,
|
|
||||||
if_return : true,
|
|
||||||
join_vars : true,
|
|
||||||
sequences : false,
|
|
||||||
collapse_vars : false,
|
|
||||||
reduce_vars : true,
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
(function(){
|
|
||||||
var result;
|
|
||||||
const s = 'acdabcdeabbb';
|
|
||||||
const REGEXP_LITERAL = /ab*/g;
|
|
||||||
while (result = REGEXP_LITERAL.exec(s)) {
|
|
||||||
console.log(result[0]);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
(function() {
|
|
||||||
var result;
|
|
||||||
const REGEXP_LITERAL = /ab*/g;
|
|
||||||
while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
@@ -90,131 +90,6 @@ dead_code_constant_boolean_should_warn_more: {
|
|||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
dead_code_const_declaration: {
|
|
||||||
options = {
|
|
||||||
dead_code : true,
|
|
||||||
loops : true,
|
|
||||||
booleans : true,
|
|
||||||
conditionals : true,
|
|
||||||
evaluate : true,
|
|
||||||
reduce_vars : true,
|
|
||||||
};
|
|
||||||
input: {
|
|
||||||
var unused;
|
|
||||||
const CONST_FOO = false;
|
|
||||||
if (CONST_FOO) {
|
|
||||||
console.log("unreachable");
|
|
||||||
var moo;
|
|
||||||
function bar() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
var unused;
|
|
||||||
const CONST_FOO = !1;
|
|
||||||
var moo;
|
|
||||||
function bar() {}
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
|
|
||||||
dead_code_const_annotation: {
|
|
||||||
options = {
|
|
||||||
dead_code : true,
|
|
||||||
loops : true,
|
|
||||||
booleans : true,
|
|
||||||
conditionals : true,
|
|
||||||
evaluate : true,
|
|
||||||
reduce_vars : true,
|
|
||||||
toplevel : true,
|
|
||||||
};
|
|
||||||
input: {
|
|
||||||
var unused;
|
|
||||||
/** @const */ var CONST_FOO_ANN = false;
|
|
||||||
if (CONST_FOO_ANN) {
|
|
||||||
console.log("unreachable");
|
|
||||||
var moo;
|
|
||||||
function bar() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
var unused;
|
|
||||||
var CONST_FOO_ANN = !1;
|
|
||||||
var moo;
|
|
||||||
function bar() {}
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
|
|
||||||
dead_code_const_annotation_regex: {
|
|
||||||
options = {
|
|
||||||
dead_code : true,
|
|
||||||
loops : true,
|
|
||||||
booleans : true,
|
|
||||||
conditionals : true,
|
|
||||||
evaluate : true
|
|
||||||
};
|
|
||||||
input: {
|
|
||||||
var unused;
|
|
||||||
// @constraint this shouldn't be a constant
|
|
||||||
var CONST_FOO_ANN = false;
|
|
||||||
if (CONST_FOO_ANN) {
|
|
||||||
console.log("reachable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
var unused;
|
|
||||||
var CONST_FOO_ANN = !1;
|
|
||||||
CONST_FOO_ANN && console.log('reachable');
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
|
|
||||||
dead_code_const_annotation_complex_scope: {
|
|
||||||
options = {
|
|
||||||
dead_code : true,
|
|
||||||
loops : true,
|
|
||||||
booleans : true,
|
|
||||||
conditionals : true,
|
|
||||||
evaluate : true,
|
|
||||||
reduce_vars : true,
|
|
||||||
toplevel : true,
|
|
||||||
};
|
|
||||||
input: {
|
|
||||||
var unused_var;
|
|
||||||
/** @const */ var test = 'test';
|
|
||||||
// @const
|
|
||||||
var CONST_FOO_ANN = false;
|
|
||||||
var unused_var_2;
|
|
||||||
if (CONST_FOO_ANN) {
|
|
||||||
console.log("unreachable");
|
|
||||||
var moo;
|
|
||||||
function bar() {}
|
|
||||||
}
|
|
||||||
if (test === 'test') {
|
|
||||||
var beef = 'good';
|
|
||||||
/** @const */ var meat = 'beef';
|
|
||||||
var pork = 'bad';
|
|
||||||
if (meat === 'pork') {
|
|
||||||
console.log('also unreachable');
|
|
||||||
} else if (pork === 'good') {
|
|
||||||
console.log('reached, not const');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
var unused_var;
|
|
||||||
var test = 'test';
|
|
||||||
var CONST_FOO_ANN = !1;
|
|
||||||
var unused_var_2;
|
|
||||||
var moo;
|
|
||||||
function bar() {}
|
|
||||||
var beef = 'good';
|
|
||||||
var meat = 'beef';
|
|
||||||
var pork = 'bad';
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
|
|
||||||
try_catch_finally: {
|
try_catch_finally: {
|
||||||
options = {
|
options = {
|
||||||
conditionals: true,
|
conditionals: true,
|
||||||
@@ -256,3 +131,19 @@ try_catch_finally: {
|
|||||||
"1",
|
"1",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accessor: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
({
|
||||||
|
get a() {},
|
||||||
|
set a(v){
|
||||||
|
this.b = 2;
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -649,37 +649,6 @@ drop_value: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const_assign: {
|
|
||||||
options = {
|
|
||||||
evaluate: true,
|
|
||||||
reduce_vars: true,
|
|
||||||
unused: true,
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
function f() {
|
|
||||||
const b = 2;
|
|
||||||
return 1 + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function g() {
|
|
||||||
const b = 2;
|
|
||||||
b = 3;
|
|
||||||
return 1 + b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
function f() {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
function g() {
|
|
||||||
const b = 2;
|
|
||||||
b = 3;
|
|
||||||
return 1 + b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
issue_1539: {
|
issue_1539: {
|
||||||
options = {
|
options = {
|
||||||
cascade: true,
|
cascade: true,
|
||||||
@@ -816,10 +785,6 @@ issue_1709: {
|
|||||||
var x = 1;
|
var x = 1;
|
||||||
return x;
|
return x;
|
||||||
}(),
|
}(),
|
||||||
function y() {
|
|
||||||
const y = 2;
|
|
||||||
return y;
|
|
||||||
}(),
|
|
||||||
function z() {
|
function z() {
|
||||||
function z() {}
|
function z() {}
|
||||||
return z;
|
return z;
|
||||||
@@ -832,10 +797,6 @@ issue_1709: {
|
|||||||
var x = 1;
|
var x = 1;
|
||||||
return x;
|
return x;
|
||||||
}(),
|
}(),
|
||||||
function() {
|
|
||||||
const y = 2;
|
|
||||||
return y;
|
|
||||||
}(),
|
|
||||||
function() {
|
function() {
|
||||||
function z() {}
|
function z() {}
|
||||||
return z;
|
return z;
|
||||||
|
|||||||
@@ -645,16 +645,17 @@ call_args: {
|
|||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
const a = 1;
|
var a = 1;
|
||||||
console.log(a);
|
console.log(a);
|
||||||
+function(a) {
|
+function(a) {
|
||||||
return a;
|
return a;
|
||||||
}(a);
|
}(a);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
const a = 1;
|
var a = 1;
|
||||||
console.log(1);
|
console.log(1);
|
||||||
+(1, 1);
|
+(1, 1);
|
||||||
}
|
}
|
||||||
@@ -666,17 +667,17 @@ call_args_drop_param: {
|
|||||||
evaluate: true,
|
evaluate: true,
|
||||||
keep_fargs: false,
|
keep_fargs: false,
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
unused: true,
|
unused: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
const a = 1;
|
var a = 1;
|
||||||
console.log(a);
|
console.log(a);
|
||||||
+function(a) {
|
+function(a) {
|
||||||
return a;
|
return a;
|
||||||
}(a, b);
|
}(a, b);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
const a = 1;
|
|
||||||
console.log(1);
|
console.log(1);
|
||||||
+(b, 1);
|
+(b, 1);
|
||||||
}
|
}
|
||||||
@@ -989,3 +990,50 @@ Infinity_NaN_undefined_LHS: {
|
|||||||
"}",
|
"}",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1964_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unsafe_regexp: false,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
var long_variable_name = /\s/;
|
||||||
|
return "a b c".split(long_variable_name)[1];
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
var long_variable_name = /\s/;
|
||||||
|
return "a b c".split(long_variable_name)[1];
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect_stdout: "b"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1964_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unsafe_regexp: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
var long_variable_name = /\s/;
|
||||||
|
return "a b c".split(long_variable_name)[1];
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
return "a b c".split(/\s/)[1];
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect_stdout: "b"
|
||||||
|
}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ mixed: {
|
|||||||
properties: true,
|
properties: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
const FOO = { BAR: 0 };
|
var FOO = { BAR: 0 };
|
||||||
console.log(FOO.BAR);
|
console.log(FOO.BAR);
|
||||||
console.log(++CONFIG.DEBUG);
|
console.log(++CONFIG.DEBUG);
|
||||||
console.log(++CONFIG.VALUE);
|
console.log(++CONFIG.VALUE);
|
||||||
@@ -130,7 +130,7 @@ mixed: {
|
|||||||
console.log(CONFIG);
|
console.log(CONFIG);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
const FOO = { BAR: 0 };
|
var FOO = { BAR: 0 };
|
||||||
console.log("moo");
|
console.log("moo");
|
||||||
console.log(++CONFIG.DEBUG);
|
console.log(++CONFIG.DEBUG);
|
||||||
console.log(++CONFIG.VALUE);
|
console.log(++CONFIG.VALUE);
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
const_declaration: {
|
|
||||||
options = {
|
|
||||||
evaluate: true
|
|
||||||
};
|
|
||||||
|
|
||||||
input: {
|
|
||||||
const goog = goog || {};
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
const goog = goog || {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const_pragma: {
|
const_pragma: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
|
|||||||
@@ -85,15 +85,3 @@ unsafe_undefined: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime_error: {
|
|
||||||
input: {
|
|
||||||
const a = 1;
|
|
||||||
console.log(a++);
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
const a = 1;
|
|
||||||
console.log(a++);
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
|
|||||||
31
test/compress/issue-1943.js
Normal file
31
test/compress/issue-1943.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
operator: {
|
||||||
|
input: {
|
||||||
|
a. //comment
|
||||||
|
typeof
|
||||||
|
}
|
||||||
|
expect_exact: "a.typeof;"
|
||||||
|
}
|
||||||
|
|
||||||
|
name: {
|
||||||
|
input: {
|
||||||
|
a. //comment
|
||||||
|
b
|
||||||
|
}
|
||||||
|
expect_exact: "a.b;"
|
||||||
|
}
|
||||||
|
|
||||||
|
keyword: {
|
||||||
|
input: {
|
||||||
|
a. //comment
|
||||||
|
default
|
||||||
|
}
|
||||||
|
expect_exact: "a.default;"
|
||||||
|
}
|
||||||
|
|
||||||
|
atom: {
|
||||||
|
input: {
|
||||||
|
a. //comment
|
||||||
|
true
|
||||||
|
}
|
||||||
|
expect_exact: "a.true;"
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ mixed: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
const ENV = 3;
|
var ENV = 3;
|
||||||
var FOO = 4;
|
var FOO = 4;
|
||||||
f(ENV * 10);
|
f(ENV * 10);
|
||||||
--FOO;
|
--FOO;
|
||||||
@@ -49,7 +49,7 @@ mixed: {
|
|||||||
x = DEBUG;
|
x = DEBUG;
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
const ENV = 3;
|
var ENV = 3;
|
||||||
var FOO = 4;
|
var FOO = 4;
|
||||||
f(10);
|
f(10);
|
||||||
--FOO;
|
--FOO;
|
||||||
@@ -60,7 +60,7 @@ mixed: {
|
|||||||
x = 0;
|
x = 0;
|
||||||
}
|
}
|
||||||
expect_warnings: [
|
expect_warnings: [
|
||||||
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,14]',
|
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,12]',
|
||||||
'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]',
|
'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]',
|
||||||
'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]',
|
'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]',
|
||||||
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]',
|
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]',
|
||||||
|
|||||||
@@ -146,50 +146,6 @@ parse_do_while_without_semicolon: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
keep_collapse_const_in_own_block_scope: {
|
|
||||||
options = {
|
|
||||||
join_vars: true,
|
|
||||||
loops: true
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
var i=2;
|
|
||||||
const c=5;
|
|
||||||
while(i--)
|
|
||||||
console.log(i);
|
|
||||||
console.log(c);
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
var i=2;
|
|
||||||
const c=5;
|
|
||||||
for(;i--;)
|
|
||||||
console.log(i);
|
|
||||||
console.log(c);
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
|
|
||||||
keep_collapse_const_in_own_block_scope_2: {
|
|
||||||
options = {
|
|
||||||
join_vars: true,
|
|
||||||
loops: true
|
|
||||||
}
|
|
||||||
input: {
|
|
||||||
const c=5;
|
|
||||||
var i=2; // Moves to loop, while it did not in previous test
|
|
||||||
while(i--)
|
|
||||||
console.log(i);
|
|
||||||
console.log(c);
|
|
||||||
}
|
|
||||||
expect: {
|
|
||||||
const c=5;
|
|
||||||
for(var i=2;i--;)
|
|
||||||
console.log(i);
|
|
||||||
console.log(c);
|
|
||||||
}
|
|
||||||
expect_stdout: true
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluate: {
|
evaluate: {
|
||||||
options = {
|
options = {
|
||||||
loops: true,
|
loops: true,
|
||||||
|
|||||||
12
test/compress/node_version.js
Normal file
12
test/compress/node_version.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
eval_let: {
|
||||||
|
input: {
|
||||||
|
eval("let a;");
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
eval("let a;");
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
expect_stdout: ""
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
@@ -119,3 +119,62 @@ chained: {
|
|||||||
a.b.c;
|
a.b.c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impure_getter_1: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
({
|
||||||
|
get a() {
|
||||||
|
console.log(1);
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}).a;
|
||||||
|
({
|
||||||
|
get a() {
|
||||||
|
console.log(1);
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}).b;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
({
|
||||||
|
get a() {
|
||||||
|
console.log(1);
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}).a;
|
||||||
|
({
|
||||||
|
get a() {
|
||||||
|
console.log(1);
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}).b;
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
impure_getter_2: {
|
||||||
|
options = {
|
||||||
|
pure_getters: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
// will produce incorrect output because getter is not pure
|
||||||
|
({
|
||||||
|
get a() {
|
||||||
|
console.log(1);
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}).a;
|
||||||
|
({
|
||||||
|
get a() {
|
||||||
|
console.log(1);
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}).b;
|
||||||
|
}
|
||||||
|
expect: {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,20 +41,20 @@ reduce_vars: {
|
|||||||
var A = 1;
|
var A = 1;
|
||||||
(function() {
|
(function() {
|
||||||
console.log(-3);
|
console.log(-3);
|
||||||
console.log(-4);
|
console.log(A - 5);
|
||||||
})();
|
})();
|
||||||
(function f1() {
|
(function f1() {
|
||||||
var a = 2;
|
var a = 2;
|
||||||
console.log(-3);
|
console.log(a - 5);
|
||||||
eval("console.log(a);");
|
eval("console.log(a);");
|
||||||
})();
|
})();
|
||||||
(function f2(eval) {
|
(function f2(eval) {
|
||||||
var a = 2;
|
var a = 2;
|
||||||
console.log(-3);
|
console.log(a - 5);
|
||||||
eval("console.log(a);");
|
eval("console.log(a);");
|
||||||
})(eval);
|
})(eval);
|
||||||
"yes";
|
"yes";
|
||||||
console.log(2);
|
console.log(A + 1);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
@@ -1749,7 +1749,10 @@ redefine_arguments_3: {
|
|||||||
console.log(function() {
|
console.log(function() {
|
||||||
var arguments;
|
var arguments;
|
||||||
return typeof arguments;
|
return typeof arguments;
|
||||||
}(), "number", "undefined");
|
}(), "number", function(x) {
|
||||||
|
var arguments = x;
|
||||||
|
return typeof arguments;
|
||||||
|
}());
|
||||||
}
|
}
|
||||||
expect_stdout: "object number undefined"
|
expect_stdout: "object number undefined"
|
||||||
}
|
}
|
||||||
@@ -2187,10 +2190,11 @@ issue_1814_1: {
|
|||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
unused: true,
|
unused: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
const a = 42;
|
var a = 42;
|
||||||
!function() {
|
!function() {
|
||||||
var b = a;
|
var b = a;
|
||||||
!function(a) {
|
!function(a) {
|
||||||
@@ -2199,7 +2203,6 @@ issue_1814_1: {
|
|||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
const a = 42;
|
|
||||||
!function() {
|
!function() {
|
||||||
!function(a) {
|
!function(a) {
|
||||||
console.log(a++, 42);
|
console.log(a++, 42);
|
||||||
@@ -2213,10 +2216,11 @@ issue_1814_2: {
|
|||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
unused: true,
|
unused: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
const a = "32";
|
var a = "32";
|
||||||
!function() {
|
!function() {
|
||||||
var b = a + 1;
|
var b = a + 1;
|
||||||
!function(a) {
|
!function(a) {
|
||||||
@@ -2225,7 +2229,6 @@ issue_1814_2: {
|
|||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
const a = "32";
|
|
||||||
!function() {
|
!function() {
|
||||||
!function(a) {
|
!function(a) {
|
||||||
console.log(a++, "321");
|
console.log(a++, "321");
|
||||||
@@ -2461,3 +2464,76 @@ issue_1865: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1922_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
arguments[0] = 2;
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
arguments[0] = 2;
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect_stdout: "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1922_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
var a;
|
||||||
|
eval("a = 1");
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var a;
|
||||||
|
eval("a = 1");
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
accessor: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
console.log({
|
||||||
|
get a() {
|
||||||
|
a = 2;
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}.b, a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
console.log({
|
||||||
|
get a() {
|
||||||
|
a = 2;
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
b: 1
|
||||||
|
}.b, a);
|
||||||
|
}
|
||||||
|
expect_stdout: "1 1"
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,3 +8,12 @@ octal_escape_sequence: {
|
|||||||
var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30";
|
var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1929: {
|
||||||
|
input: {
|
||||||
|
function f(s) {
|
||||||
|
return s.split(/[\\/]/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "function f(s){return s.split(/[\\\\/]/)}"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
function f() {
|
|
||||||
const a;
|
|
||||||
}
|
|
||||||
|
|
||||||
function g() {
|
|
||||||
"use strict";
|
|
||||||
const a;
|
|
||||||
}
|
|
||||||
1
test/input/invalid/else.js
Normal file
1
test/input/invalid/else.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
if (0) else 1;
|
||||||
1
test/input/invalid/return.js
Normal file
1
test/input/invalid/return.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
return 42;
|
||||||
@@ -16,7 +16,7 @@ describe("Accessor tokens", function() {
|
|||||||
assert.equal(node.start.pos, 12);
|
assert.equal(node.start.pos, 12);
|
||||||
assert.equal(node.end.endpos, 46);
|
assert.equal(node.end.endpos, 46);
|
||||||
|
|
||||||
assert(node.key instanceof UglifyJS.AST_SymbolRef);
|
assert(node.key instanceof UglifyJS.AST_SymbolAccessor);
|
||||||
assert.equal(node.key.start.pos, 16);
|
assert.equal(node.key.start.pos, 16);
|
||||||
assert.equal(node.key.end.endpos, 22);
|
assert.equal(node.key.end.endpos, 22);
|
||||||
|
|
||||||
|
|||||||
@@ -379,21 +379,6 @@ describe("bin/uglifyjs", function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("Should throw syntax error (const a)", function(done) {
|
|
||||||
var command = uglifyjscmd + ' test/input/invalid/const.js';
|
|
||||||
|
|
||||||
exec(command, function (err, stdout, stderr) {
|
|
||||||
assert.ok(err);
|
|
||||||
assert.strictEqual(stdout, "");
|
|
||||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
|
||||||
"Parse error at test/input/invalid/const.js:7,11",
|
|
||||||
" const a;",
|
|
||||||
" ^",
|
|
||||||
"ERROR: Missing initializer in const declaration"
|
|
||||||
].join("\n"));
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it("Should throw syntax error (delete x)", function(done) {
|
it("Should throw syntax error (delete x)", function(done) {
|
||||||
var command = uglifyjscmd + ' test/input/invalid/delete.js';
|
var command = uglifyjscmd + ' test/input/invalid/delete.js';
|
||||||
|
|
||||||
@@ -484,6 +469,36 @@ describe("bin/uglifyjs", function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("Should throw syntax error (else)", function(done) {
|
||||||
|
var command = uglifyjscmd + ' test/input/invalid/else.js';
|
||||||
|
|
||||||
|
exec(command, function (err, stdout, stderr) {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(stdout, "");
|
||||||
|
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||||
|
"Parse error at test/input/invalid/else.js:1,7",
|
||||||
|
"if (0) else 1;",
|
||||||
|
" ^",
|
||||||
|
"ERROR: Unexpected token: keyword (else)"
|
||||||
|
].join("\n"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should throw syntax error (return)", function(done) {
|
||||||
|
var command = uglifyjscmd + ' test/input/invalid/return.js';
|
||||||
|
|
||||||
|
exec(command, function (err, stdout, stderr) {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(stdout, "");
|
||||||
|
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||||
|
"Parse error at test/input/invalid/return.js:1,0",
|
||||||
|
"return 42;",
|
||||||
|
"^",
|
||||||
|
"ERROR: 'return' outside of function"
|
||||||
|
].join("\n"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
it("Should handle literal string as source map input", function(done) {
|
it("Should handle literal string as source map input", function(done) {
|
||||||
var command = [
|
var command = [
|
||||||
uglifyjscmd,
|
uglifyjscmd,
|
||||||
|
|||||||
@@ -351,18 +351,28 @@ describe("Directives", function() {
|
|||||||
var tests = [
|
var tests = [
|
||||||
[
|
[
|
||||||
'"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");',
|
'"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");',
|
||||||
'"use strict";"use foo";doSomething("foo");'
|
'"use strict";"use foo";doSomething("foo");',
|
||||||
|
'function f(){ "use strict" }',
|
||||||
|
'function f(){ "use asm" }',
|
||||||
|
'function f(){ "use nondirective" }',
|
||||||
|
'function f(){ ;"use strict" }',
|
||||||
|
'function f(){ "use \n"; }',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
// Nothing gets optimised in the compressor because "use asm" is the first statement
|
// Nothing gets optimised in the compressor because "use asm" is the first statement
|
||||||
'"use asm";"use\\x20strict";1+1;',
|
'"use asm";"use\\x20strict";1+1;',
|
||||||
'"use asm";;"use strict";1+1;' // Yet, the parser noticed that "use strict" wasn't a directive
|
'"use asm";;"use strict";1+1;', // Yet, the parser noticed that "use strict" wasn't a directive
|
||||||
|
'function f(){"use strict"}',
|
||||||
|
'function f(){"use asm"}',
|
||||||
|
'function f(){"use nondirective"}',
|
||||||
|
'function f(){}',
|
||||||
|
'function f(){}',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
for (var i = 0; i < tests.length; i++) {
|
for (var i = 0; i < tests.length; i++) {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
uglify.minify(tests[i][0], {compress: {collapse_vars: true, side_effects: true}}).code,
|
uglify.minify(tests[i][0]).code,
|
||||||
tests[i][1],
|
tests[i][1],
|
||||||
tests[i][0]
|
tests[i][0]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ describe("Getters and setters", function() {
|
|||||||
var fail = function(data) {
|
var fail = function(data) {
|
||||||
return function (e) {
|
return function (e) {
|
||||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
e.message === "Invalid getter/setter name: " + data.operator;
|
e.message === "Unexpected token: operator (" + data.operator + ")";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,103 +1,73 @@
|
|||||||
// Testing UglifyJS <-> SpiderMonkey AST conversion
|
// Testing UglifyJS <-> SpiderMonkey AST conversion
|
||||||
// through generative testing.
|
"use strict";
|
||||||
|
|
||||||
var UglifyJS = require("./node"),
|
var acorn = require("acorn");
|
||||||
escodegen = require("escodegen"),
|
var ufuzz = require("./ufuzz");
|
||||||
esfuzz = require("esfuzz"),
|
var UglifyJS = require("..");
|
||||||
estraverse = require("estraverse"),
|
|
||||||
prefix = "\r ";
|
|
||||||
|
|
||||||
// Normalizes input AST for UglifyJS in order to get correct comparison.
|
function try_beautify(code) {
|
||||||
|
var beautified = UglifyJS.minify(code, {
|
||||||
function normalizeInput(ast) {
|
compress: false,
|
||||||
return estraverse.replace(ast, {
|
mangle: false,
|
||||||
enter: function(node, parent) {
|
output: {
|
||||||
switch (node.type) {
|
beautify: true,
|
||||||
// Internally mark all the properties with semi-standard type "Property".
|
bracketize: true
|
||||||
case "ObjectExpression":
|
|
||||||
node.properties.forEach(function (property) {
|
|
||||||
property.type = "Property";
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Since UglifyJS doesn"t recognize different types of property keys,
|
|
||||||
// decision on SpiderMonkey node type is based on check whether key
|
|
||||||
// can be valid identifier or not - so we do in input AST.
|
|
||||||
case "Property":
|
|
||||||
var key = node.key;
|
|
||||||
if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) {
|
|
||||||
node.key = {
|
|
||||||
type: "Identifier",
|
|
||||||
name: key.value
|
|
||||||
};
|
|
||||||
} else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) {
|
|
||||||
node.key = {
|
|
||||||
type: "Literal",
|
|
||||||
value: key.name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// UglifyJS internally flattens all the expression sequences - either
|
|
||||||
// to one element (if sequence contains only one element) or flat list.
|
|
||||||
case "SequenceExpression":
|
|
||||||
node.expressions = node.expressions.reduce(function flatten(list, expr) {
|
|
||||||
return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]);
|
|
||||||
}, []);
|
|
||||||
if (node.expressions.length === 1) {
|
|
||||||
return node.expressions[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (beautified.error) {
|
||||||
|
console.log("// !!! beautify failed !!!");
|
||||||
|
console.log(beautified.error.stack);
|
||||||
|
console.log(code);
|
||||||
|
} else {
|
||||||
|
console.log("// (beautified)");
|
||||||
|
console.log(beautified.code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(options) {
|
function test(original, estree, description) {
|
||||||
console.log("--- UglifyJS <-> Mozilla AST conversion");
|
var transformed = UglifyJS.minify(UglifyJS.AST_Node.from_mozilla_ast(estree), {
|
||||||
|
compress: false,
|
||||||
|
mangle: false
|
||||||
|
});
|
||||||
|
if (transformed.error || original !== transformed.code) {
|
||||||
|
console.log("//=============================================================");
|
||||||
|
console.log("// !!!!!! Failed... round", round);
|
||||||
|
console.log("// original code");
|
||||||
|
try_beautify(original);
|
||||||
|
console.log();
|
||||||
|
console.log();
|
||||||
|
console.log("//-------------------------------------------------------------");
|
||||||
|
console.log("//", description);
|
||||||
|
if (transformed.error) {
|
||||||
|
console.log(transformed.error.stack);
|
||||||
|
} else {
|
||||||
|
try_beautify(transformed.code);
|
||||||
|
}
|
||||||
|
console.log("!!!!!! Failed... round", round);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var counter = 0; counter < options.iterations; counter++) {
|
var num_iterations = ufuzz.num_iterations;
|
||||||
process.stdout.write(prefix + counter + "/" + options.iterations);
|
for (var round = 1; round <= num_iterations; round++) {
|
||||||
|
process.stdout.write(round + " of " + num_iterations + "\r");
|
||||||
var ast1 = normalizeInput(esfuzz.generate({
|
var code = ufuzz.createTopLevelCode();
|
||||||
maxDepth: options.maxDepth
|
var uglified = UglifyJS.minify(code, {
|
||||||
}));
|
compress: false,
|
||||||
|
mangle: false,
|
||||||
var ast2 =
|
output: {
|
||||||
UglifyJS
|
ast: true
|
||||||
.AST_Node
|
|
||||||
.from_mozilla_ast(ast1)
|
|
||||||
.to_mozilla_ast();
|
|
||||||
|
|
||||||
var astPair = [
|
|
||||||
{name: 'expected', value: ast1},
|
|
||||||
{name: 'actual', value: ast2}
|
|
||||||
];
|
|
||||||
|
|
||||||
var jsPair = astPair.map(function(item) {
|
|
||||||
return {
|
|
||||||
name: item.name,
|
|
||||||
value: escodegen.generate(item.value)
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
|
||||||
if (jsPair[0].value !== jsPair[1].value) {
|
try {
|
||||||
var fs = require("fs");
|
test(uglified.code, acorn.parse(code), "acorn.parse()");
|
||||||
var acorn = require("acorn");
|
} catch (e) {
|
||||||
|
console.log("//=============================================================");
|
||||||
fs.existsSync("tmp") || fs.mkdirSync("tmp");
|
console.log("// acorn parser failed... round", round);
|
||||||
|
console.log(e);
|
||||||
jsPair.forEach(function (item) {
|
console.log("// original code");
|
||||||
var fileName = "tmp/dump_" + item.name;
|
console.log(code);
|
||||||
var ast = acorn.parse(item.value);
|
|
||||||
fs.writeFileSync(fileName + ".js", item.value);
|
|
||||||
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2));
|
|
||||||
});
|
|
||||||
|
|
||||||
process.stdout.write("\n");
|
|
||||||
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log();
|
||||||
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n");
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ var path = require("path");
|
|||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var assert = require("assert");
|
var assert = require("assert");
|
||||||
var sandbox = require("./sandbox");
|
var sandbox = require("./sandbox");
|
||||||
|
var semver = require("semver");
|
||||||
|
|
||||||
var tests_dir = path.dirname(module.filename);
|
var tests_dir = path.dirname(module.filename);
|
||||||
var failures = 0;
|
var failures = 0;
|
||||||
@@ -23,12 +24,6 @@ mocha_tests();
|
|||||||
var run_sourcemaps_tests = require('./sourcemaps');
|
var run_sourcemaps_tests = require('./sourcemaps');
|
||||||
run_sourcemaps_tests();
|
run_sourcemaps_tests();
|
||||||
|
|
||||||
var run_ast_conversion_tests = require("./mozilla-ast");
|
|
||||||
|
|
||||||
run_ast_conversion_tests({
|
|
||||||
iterations: 1000
|
|
||||||
});
|
|
||||||
|
|
||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
function tmpl() {
|
function tmpl() {
|
||||||
@@ -170,7 +165,8 @@ function run_compress_tests() {
|
|||||||
failed_files[file] = 1;
|
failed_files[file] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (test.expect_stdout) {
|
if (test.expect_stdout
|
||||||
|
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
|
||||||
var stdout = sandbox.run_code(input_code);
|
var stdout = sandbox.run_code(input_code);
|
||||||
if (test.expect_stdout === true) {
|
if (test.expect_stdout === true) {
|
||||||
test.expect_stdout = stdout;
|
test.expect_stdout = stdout;
|
||||||
@@ -280,7 +276,14 @@ function parse_test(file) {
|
|||||||
if (node instanceof U.AST_LabeledStatement) {
|
if (node instanceof U.AST_LabeledStatement) {
|
||||||
var label = node.label;
|
var label = node.label;
|
||||||
assert.ok(
|
assert.ok(
|
||||||
["input", "expect", "expect_exact", "expect_warnings", "expect_stdout"].indexOf(label.name) >= 0,
|
[
|
||||||
|
"input",
|
||||||
|
"expect",
|
||||||
|
"expect_exact",
|
||||||
|
"expect_warnings",
|
||||||
|
"expect_stdout",
|
||||||
|
"node_version",
|
||||||
|
].indexOf(label.name) >= 0,
|
||||||
tmpl("Unsupported label {name} [{line},{col}]", {
|
tmpl("Unsupported label {name} [{line},{col}]", {
|
||||||
name: label.name,
|
name: label.name,
|
||||||
line: label.start.line,
|
line: label.start.line,
|
||||||
@@ -288,7 +291,7 @@ function parse_test(file) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
var stat = node.body;
|
var stat = node.body;
|
||||||
if (label.name == "expect_exact") {
|
if (label.name == "expect_exact" || label.name == "node_version") {
|
||||||
test[label.name] = read_string(stat);
|
test[label.name] = read_string(stat);
|
||||||
} else if (label.name == "expect_stdout") {
|
} else if (label.name == "expect_stdout") {
|
||||||
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
|
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
var semver = require("semver");
|
||||||
var vm = require("vm");
|
var vm = require("vm");
|
||||||
|
|
||||||
function safe_log(arg) {
|
function safe_log(arg, level) {
|
||||||
if (arg) switch (typeof arg) {
|
if (arg) switch (typeof arg) {
|
||||||
case "function":
|
case "function":
|
||||||
return arg.toString();
|
return arg.toString();
|
||||||
case "object":
|
case "object":
|
||||||
if (/Error$/.test(arg.name)) return arg.toString();
|
if (/Error$/.test(arg.name)) return arg.toString();
|
||||||
arg.constructor.toString();
|
arg.constructor.toString();
|
||||||
for (var key in arg) {
|
if (level--) for (var key in arg) {
|
||||||
arg[key] = safe_log(arg[key]);
|
if (!Object.getOwnPropertyDescriptor(arg, key).get) {
|
||||||
|
arg[key] = safe_log(arg[key], level);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arg;
|
return arg;
|
||||||
@@ -48,7 +51,9 @@ exports.run_code = function(code) {
|
|||||||
].join("\n"), {
|
].join("\n"), {
|
||||||
console: {
|
console: {
|
||||||
log: function() {
|
log: function() {
|
||||||
return console.log.apply(console, [].map.call(arguments, safe_log));
|
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||||
|
return safe_log(arg, 3);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { timeout: 5000 });
|
}, { timeout: 5000 });
|
||||||
@@ -59,7 +64,7 @@ exports.run_code = function(code) {
|
|||||||
process.stdout.write = original_write;
|
process.stdout.write = original_write;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
exports.same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) {
|
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
|
||||||
if (typeof expected != typeof actual) return false;
|
if (typeof expected != typeof actual) return false;
|
||||||
if (typeof expected != "string") {
|
if (typeof expected != "string") {
|
||||||
if (expected.name != actual.name) return false;
|
if (expected.name != actual.name) return false;
|
||||||
|
|||||||
212
test/ufuzz.js
212
test/ufuzz.js
@@ -48,8 +48,9 @@ var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest functio
|
|||||||
var num_iterations = +process.argv[2] || 1/0;
|
var num_iterations = +process.argv[2] || 1/0;
|
||||||
var verbose = false; // log every generated test
|
var verbose = false; // log every generated test
|
||||||
var verbose_interval = false; // log every 100 generated tests
|
var verbose_interval = false; // log every 100 generated tests
|
||||||
var verbose_error = false;
|
|
||||||
var use_strict = false;
|
var use_strict = false;
|
||||||
|
var catch_redef = require.main === module;
|
||||||
|
var generate_directive = require.main === module;
|
||||||
for (var i = 2; i < process.argv.length; ++i) {
|
for (var i = 2; i < process.argv.length; ++i) {
|
||||||
switch (process.argv[i]) {
|
switch (process.argv[i]) {
|
||||||
case '-v':
|
case '-v':
|
||||||
@@ -58,9 +59,6 @@ for (var i = 2; i < process.argv.length; ++i) {
|
|||||||
case '-V':
|
case '-V':
|
||||||
verbose_interval = true;
|
verbose_interval = true;
|
||||||
break;
|
break;
|
||||||
case '-E':
|
|
||||||
verbose_error = true;
|
|
||||||
break;
|
|
||||||
case '-t':
|
case '-t':
|
||||||
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
|
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
|
||||||
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
|
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
|
||||||
@@ -79,6 +77,12 @@ for (var i = 2; i < process.argv.length; ++i) {
|
|||||||
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
||||||
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
||||||
break;
|
break;
|
||||||
|
case '--no-catch-redef':
|
||||||
|
catch_redef = false;
|
||||||
|
break;
|
||||||
|
case '--no-directive':
|
||||||
|
generate_directive = false;
|
||||||
|
break;
|
||||||
case '--use-strict':
|
case '--use-strict':
|
||||||
use_strict = true;
|
use_strict = true;
|
||||||
break;
|
break;
|
||||||
@@ -103,11 +107,12 @@ for (var i = 2; i < process.argv.length; ++i) {
|
|||||||
console.log('<number>: generate this many cases (if used must be first arg)');
|
console.log('<number>: generate this many cases (if used must be first arg)');
|
||||||
console.log('-v: print every generated test case');
|
console.log('-v: print every generated test case');
|
||||||
console.log('-V: print every 100th generated test case');
|
console.log('-V: print every 100th generated test case');
|
||||||
console.log('-E: print generated test case with runtime error');
|
|
||||||
console.log('-t <int>: generate this many toplevels per run (more take longer)');
|
console.log('-t <int>: generate this many toplevels per run (more take longer)');
|
||||||
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
||||||
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
||||||
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
||||||
|
console.log('--no-catch-redef: do not redefine catch variables');
|
||||||
|
console.log('--no-directive: do not generate directives');
|
||||||
console.log('--use-strict: generate "use strict"');
|
console.log('--use-strict: generate "use strict"');
|
||||||
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
||||||
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
||||||
@@ -192,12 +197,33 @@ var ASSIGNMENTS = [
|
|||||||
'=',
|
'=',
|
||||||
'=',
|
'=',
|
||||||
'=',
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
'=',
|
||||||
|
|
||||||
'==',
|
|
||||||
'!=',
|
|
||||||
'===',
|
|
||||||
'!==',
|
|
||||||
'+=',
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
'+=',
|
||||||
|
|
||||||
'-=',
|
'-=',
|
||||||
'*=',
|
'*=',
|
||||||
'/=',
|
'/=',
|
||||||
@@ -207,7 +233,8 @@ var ASSIGNMENTS = [
|
|||||||
'<<=',
|
'<<=',
|
||||||
'>>=',
|
'>>=',
|
||||||
'>>>=',
|
'>>>=',
|
||||||
'%=' ];
|
'%=',
|
||||||
|
];
|
||||||
|
|
||||||
var UNARY_SAFE = [
|
var UNARY_SAFE = [
|
||||||
'+',
|
'+',
|
||||||
@@ -276,6 +303,7 @@ var TYPEOF_OUTCOMES = [
|
|||||||
'symbol',
|
'symbol',
|
||||||
'crap' ];
|
'crap' ];
|
||||||
|
|
||||||
|
var unique_vars = [];
|
||||||
var loops = 0;
|
var loops = 0;
|
||||||
var funcs = 0;
|
var funcs = 0;
|
||||||
var labels = 10000;
|
var labels = 10000;
|
||||||
@@ -290,6 +318,10 @@ function strictMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createTopLevelCode() {
|
function createTopLevelCode() {
|
||||||
|
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
|
||||||
|
unique_vars.length = 0;
|
||||||
|
loops = 0;
|
||||||
|
funcs = 0;
|
||||||
return [
|
return [
|
||||||
strictMode(),
|
strictMode(),
|
||||||
'var a = 100, b = 10, c = 0;',
|
'var a = 100, b = 10, c = 0;',
|
||||||
@@ -325,33 +357,36 @@ function createArgs() {
|
|||||||
return args.join(', ');
|
return args.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterDirective(s) {
|
||||||
|
if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ';' + s[2];
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
||||||
if (--recurmax < 0) { return ';'; }
|
if (--recurmax < 0) { return ';'; }
|
||||||
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
||||||
var func = funcs++;
|
var func = funcs++;
|
||||||
var namesLenBefore = VAR_NAMES.length;
|
var namesLenBefore = VAR_NAMES.length;
|
||||||
var name = (inGlobal || rng(5) > 0) ? 'f' + func : createVarName(MANDATORY, noDecl);
|
var name;
|
||||||
if (name === 'a' || name === 'b' || name === 'c') name = 'f' + func; // quick hack to prevent assignment to func names of being called
|
if (inGlobal || rng(5) > 0) name = 'f' + func;
|
||||||
var s = '';
|
else {
|
||||||
|
unique_vars.push('a', 'b', 'c');
|
||||||
|
name = createVarName(MANDATORY, noDecl);
|
||||||
|
unique_vars.length -= 3;
|
||||||
|
}
|
||||||
|
var s = [
|
||||||
|
'function ' + name + '(' + createParams() + '){',
|
||||||
|
strictMode()
|
||||||
|
];
|
||||||
if (rng(5) === 0) {
|
if (rng(5) === 0) {
|
||||||
// functions with functions. lower the recursion to prevent a mess.
|
// functions with functions. lower the recursion to prevent a mess.
|
||||||
s = [
|
s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth));
|
||||||
'function ' + name + '(' + createParams() + '){',
|
|
||||||
strictMode(),
|
|
||||||
createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth),
|
|
||||||
'}',
|
|
||||||
''
|
|
||||||
].join('\n');
|
|
||||||
} else {
|
} else {
|
||||||
// functions with statements
|
// functions with statements
|
||||||
s = [
|
s.push(createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
|
||||||
'function ' + name + '(' + createParams() + '){',
|
|
||||||
strictMode(),
|
|
||||||
createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
|
||||||
'}',
|
|
||||||
''
|
|
||||||
].join('\n');
|
|
||||||
}
|
}
|
||||||
|
s.push('}', '');
|
||||||
|
s = filterDirective(s).join('\n');
|
||||||
|
|
||||||
VAR_NAMES.length = namesLenBefore;
|
VAR_NAMES.length = namesLenBefore;
|
||||||
|
|
||||||
@@ -359,7 +394,6 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
|||||||
// avoid "function statements" (decl inside statements)
|
// avoid "function statements" (decl inside statements)
|
||||||
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
|
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
|
||||||
|
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +440,7 @@ function getLabel(label) {
|
|||||||
return label && " L" + label;
|
return label && " L" + label;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) {
|
||||||
++stmtDepth;
|
++stmtDepth;
|
||||||
var loop = ++loops;
|
var loop = ++loops;
|
||||||
if (--recurmax < 0) {
|
if (--recurmax < 0) {
|
||||||
@@ -414,10 +448,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allow to forcefully generate certain structures at first or second recursion level
|
// allow to forcefully generate certain structures at first or second recursion level
|
||||||
var target = 0;
|
if (target === undefined) {
|
||||||
if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
|
if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
|
||||||
else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
|
else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
|
||||||
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
|
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
|
||||||
|
}
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case STMT_BLOCK:
|
case STMT_BLOCK:
|
||||||
@@ -460,20 +495,22 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
case STMT_VAR:
|
case STMT_VAR:
|
||||||
switch (rng(3)) {
|
switch (rng(3)) {
|
||||||
case 0:
|
case 0:
|
||||||
|
unique_vars.push('c');
|
||||||
var name = createVarName(MANDATORY);
|
var name = createVarName(MANDATORY);
|
||||||
if (name === 'c') name = 'a';
|
unique_vars.pop();
|
||||||
return 'var ' + name + ';';
|
return 'var ' + name + ';';
|
||||||
case 1:
|
case 1:
|
||||||
// initializer can only have one expression
|
// initializer can only have one expression
|
||||||
|
unique_vars.push('c');
|
||||||
var name = createVarName(MANDATORY);
|
var name = createVarName(MANDATORY);
|
||||||
if (name === 'c') name = 'b';
|
unique_vars.pop();
|
||||||
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
default:
|
default:
|
||||||
// initializer can only have one expression
|
// initializer can only have one expression
|
||||||
|
unique_vars.push('c');
|
||||||
var n1 = createVarName(MANDATORY);
|
var n1 = createVarName(MANDATORY);
|
||||||
if (n1 === 'c') n1 = 'b';
|
|
||||||
var n2 = createVarName(MANDATORY);
|
var n2 = createVarName(MANDATORY);
|
||||||
if (n2 === 'c') n2 = 'b';
|
unique_vars.pop();
|
||||||
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
}
|
}
|
||||||
case STMT_RETURN_ETC:
|
case STMT_RETURN_ETC:
|
||||||
@@ -514,8 +551,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
var nameLenBefore = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
var catchName = createVarName(MANDATORY);
|
var catchName = createVarName(MANDATORY);
|
||||||
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
|
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
|
||||||
|
if (!catch_redef) unique_vars.push(catchName);
|
||||||
s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
|
s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
|
||||||
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1); // remove catch name
|
// remove catch name
|
||||||
|
if (!catch_redef) unique_vars.pop();
|
||||||
|
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
|
||||||
}
|
}
|
||||||
if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
|
if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
|
||||||
return s;
|
return s;
|
||||||
@@ -593,8 +633,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
var nameLenBefore = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
|
unique_vars.push('c');
|
||||||
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
|
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
|
||||||
if (name == 'c') name = 'a';
|
unique_vars.pop();
|
||||||
var s = [];
|
var s = [];
|
||||||
switch (rng(5)) {
|
switch (rng(5)) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -636,7 +677,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
strictMode()
|
strictMode()
|
||||||
);
|
);
|
||||||
if (instantiate) for (var i = rng(4); --i >= 0;) {
|
if (instantiate) for (var i = rng(4); --i >= 0;) {
|
||||||
if (rng(2)) s.push('this.' + getDotKey() + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
|
if (rng(2)) s.push('this.' + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
|
||||||
else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
|
else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
|
||||||
}
|
}
|
||||||
s.push(
|
s.push(
|
||||||
@@ -646,7 +687,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
VAR_NAMES.length = nameLenBefore;
|
VAR_NAMES.length = nameLenBefore;
|
||||||
return s.join('\n');
|
return filterDirective(s).join('\n');
|
||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
return createTypeofExpr(recurmax, stmtDepth, canThrow);
|
return createTypeofExpr(recurmax, stmtDepth, canThrow);
|
||||||
@@ -689,19 +730,19 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
") || " + rng(10) + ").toString()[" +
|
") || " + rng(10) + ").toString()[" +
|
||||||
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
|
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
|
||||||
case p++:
|
case p++:
|
||||||
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
|
return createArrayLiteral(recurmax, stmtDepth, canThrow);
|
||||||
case p++:
|
case p++:
|
||||||
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
|
return createObjectLiteral(recurmax, stmtDepth, canThrow);
|
||||||
case p++:
|
case p++:
|
||||||
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
|
return createArrayLiteral(recurmax, stmtDepth, canThrow) + '[' +
|
||||||
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
||||||
case p++:
|
case p++:
|
||||||
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
|
return createObjectLiteral(recurmax, stmtDepth, canThrow) + '[' +
|
||||||
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
||||||
case p++:
|
case p++:
|
||||||
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
|
return createArrayLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
|
||||||
case p++:
|
case p++:
|
||||||
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
|
return createObjectLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
|
||||||
case p++:
|
case p++:
|
||||||
var name = getVarName();
|
var name = getVarName();
|
||||||
return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
||||||
@@ -713,7 +754,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) {
|
function createArrayLiteral(recurmax, stmtDepth, canThrow) {
|
||||||
recurmax--;
|
recurmax--;
|
||||||
var arr = "[";
|
var arr = "[";
|
||||||
for (var i = rng(6); --i >= 0;) {
|
for (var i = rng(6); --i >= 0;) {
|
||||||
@@ -746,18 +787,56 @@ var KEYS = [
|
|||||||
"3",
|
"3",
|
||||||
].concat(SAFE_KEYS);
|
].concat(SAFE_KEYS);
|
||||||
|
|
||||||
function getDotKey() {
|
function getDotKey(assign) {
|
||||||
return SAFE_KEYS[rng(SAFE_KEYS.length)];
|
var key;
|
||||||
|
do {
|
||||||
|
key = SAFE_KEYS[rng(SAFE_KEYS.length)];
|
||||||
|
} while (assign && key == "length");
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) {
|
function createAccessor(recurmax, stmtDepth, canThrow) {
|
||||||
recurmax--;
|
var namesLenBefore = VAR_NAMES.length;
|
||||||
var obj = "({";
|
var s;
|
||||||
for (var i = rng(6); --i >= 0;) {
|
var prop1 = getDotKey();
|
||||||
var key = KEYS[rng(KEYS.length)];
|
if (rng(2) == 0) {
|
||||||
obj += key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "), ";
|
s = [
|
||||||
|
'get ' + prop1 + '(){',
|
||||||
|
strictMode(),
|
||||||
|
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
|
||||||
|
'},'
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
var prop2;
|
||||||
|
do {
|
||||||
|
prop2 = getDotKey();
|
||||||
|
} while (prop1 == prop2);
|
||||||
|
s = [
|
||||||
|
'set ' + prop1 + '(' + createVarName(MANDATORY) + '){',
|
||||||
|
strictMode(),
|
||||||
|
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
'this.' + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ';',
|
||||||
|
'},'
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return obj + "})";
|
VAR_NAMES.length = namesLenBefore;
|
||||||
|
return filterDirective(s).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
|
||||||
|
recurmax--;
|
||||||
|
var obj = ['({'];
|
||||||
|
for (var i = rng(6); --i >= 0;) {
|
||||||
|
if (rng(20) == 0) {
|
||||||
|
obj.push(createAccessor(recurmax, stmtDepth, canThrow));
|
||||||
|
} else {
|
||||||
|
var key = KEYS[rng(KEYS.length)];
|
||||||
|
obj.push(key + ':(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '),');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.push('})');
|
||||||
|
return obj.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
|
function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
@@ -787,7 +866,7 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
|
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
|
||||||
case 4:
|
case 4:
|
||||||
assignee = getVarName();
|
assignee = getVarName();
|
||||||
expr = '(' + assignee + '.' + getDotKey() + createAssignment()
|
expr = '(' + assignee + '.' + getDotKey(true) + createAssignment()
|
||||||
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
|
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
|
||||||
default:
|
default:
|
||||||
@@ -844,17 +923,24 @@ function getVarName() {
|
|||||||
|
|
||||||
function createVarName(maybe, dontStore) {
|
function createVarName(maybe, dontStore) {
|
||||||
if (!maybe || rng(2)) {
|
if (!maybe || rng(2)) {
|
||||||
var name = VAR_NAMES[rng(VAR_NAMES.length)];
|
|
||||||
var suffix = rng(3);
|
var suffix = rng(3);
|
||||||
if (suffix) {
|
var name;
|
||||||
name += '_' + suffix;
|
do {
|
||||||
if (!dontStore) VAR_NAMES.push(name);
|
name = VAR_NAMES[rng(VAR_NAMES.length)];
|
||||||
}
|
if (suffix) name += '_' + suffix;
|
||||||
|
} while (unique_vars.indexOf(name) >= 0);
|
||||||
|
if (suffix && !dontStore) VAR_NAMES.push(name);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (require.main !== module) {
|
||||||
|
exports.createTopLevelCode = createTopLevelCode;
|
||||||
|
exports.num_iterations = num_iterations;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function try_beautify(code, result) {
|
function try_beautify(code, result) {
|
||||||
var beautified = UglifyJS.minify(code, {
|
var beautified = UglifyJS.minify(code, {
|
||||||
compress: false,
|
compress: false,
|
||||||
@@ -973,10 +1059,6 @@ var uglify_code, uglify_result, ok;
|
|||||||
for (var round = 1; round <= num_iterations; round++) {
|
for (var round = 1; round <= num_iterations; round++) {
|
||||||
process.stdout.write(round + " of " + num_iterations + "\r");
|
process.stdout.write(round + " of " + num_iterations + "\r");
|
||||||
|
|
||||||
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
|
|
||||||
loops = 0;
|
|
||||||
funcs = 0;
|
|
||||||
|
|
||||||
original_code = createTopLevelCode();
|
original_code = createTopLevelCode();
|
||||||
original_result = sandbox.run_code(original_code);
|
original_result = sandbox.run_code(original_code);
|
||||||
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
|
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
|
||||||
@@ -992,7 +1074,7 @@ for (var round = 1; round <= num_iterations; round++) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
|
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
|
||||||
else if (verbose_error && typeof original_result != "string") {
|
else if (typeof original_result != "string") {
|
||||||
console.log("//=============================================================");
|
console.log("//=============================================================");
|
||||||
console.log("// original code");
|
console.log("// original code");
|
||||||
try_beautify(original_code, original_result);
|
try_beautify(original_code, original_result);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
exports["Dictionary"] = Dictionary;
|
exports["Dictionary"] = Dictionary;
|
||||||
exports["TreeWalker"] = TreeWalker;
|
exports["TreeWalker"] = TreeWalker;
|
||||||
|
exports["TreeTransformer"] = TreeTransformer;
|
||||||
exports["minify"] = minify;
|
exports["minify"] = minify;
|
||||||
exports["_push_uniq"] = push_uniq;
|
exports["_push_uniq"] = push_uniq;
|
||||||
|
|||||||
Reference in New Issue
Block a user