You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
34156 lines
1.0 MiB
34156 lines
1.0 MiB
(function (global, factory) { |
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jridgewell/source-map')) : |
|
typeof define === 'function' && define.amd ? define(['exports', '@jridgewell/source-map'], factory) : |
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Terser = {}, global.sourceMap)); |
|
})(this, (function (exports, sourceMap) { 'use strict'; |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
function characters(str) { |
|
return str.split(""); |
|
} |
|
|
|
function member(name, array) { |
|
return array.includes(name); |
|
} |
|
|
|
class DefaultsError extends Error { |
|
constructor(msg, defs) { |
|
super(); |
|
|
|
this.name = "DefaultsError"; |
|
this.message = msg; |
|
this.defs = defs; |
|
} |
|
} |
|
|
|
function defaults(args, defs, croak) { |
|
if (args === true) { |
|
args = {}; |
|
} else if (args != null && typeof args === "object") { |
|
args = {...args}; |
|
} |
|
|
|
const ret = args || {}; |
|
|
|
if (croak) for (const i in ret) if (HOP(ret, i) && !HOP(defs, i)) { |
|
throw new DefaultsError("`" + i + "` is not a supported option", defs); |
|
} |
|
|
|
for (const i in defs) if (HOP(defs, i)) { |
|
if (!args || !HOP(args, i)) { |
|
ret[i] = defs[i]; |
|
} else if (i === "ecma" || i === "builtins_ecma") { |
|
let ecma = args[i] | 0; |
|
if (ecma > 5 && ecma < 2015) ecma += 2009; |
|
ret[i] = ecma; |
|
} else { |
|
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; |
|
} |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
function noop() {} |
|
function return_false() { return false; } |
|
function return_true() { return true; } |
|
function return_this() { return this; } |
|
function return_null() { return null; } |
|
|
|
var MAP = (function() { |
|
function MAP(a, tw, allow_splicing = true) { |
|
const new_a = []; |
|
|
|
for (let i = 0; i < a.length; ++i) { |
|
let item = a[i]; |
|
let ret = item.transform(tw, allow_splicing); |
|
|
|
if (ret instanceof AST_Node) { |
|
new_a.push(ret); |
|
} else if (ret instanceof Splice) { |
|
new_a.push(...ret.v); |
|
} |
|
} |
|
|
|
return new_a; |
|
} |
|
|
|
MAP.splice = function(val) { return new Splice(val); }; |
|
MAP.skip = {}; |
|
function Splice(val) { this.v = val; } |
|
return MAP; |
|
})(); |
|
|
|
function make_node(ctor, orig, props) { |
|
if (!props) props = {}; |
|
if (orig) { |
|
if (!props.start) props.start = orig.start; |
|
if (!props.end) props.end = orig.end; |
|
} |
|
return new ctor(props); |
|
} |
|
|
|
/** Makes a `void 0` expression. Use instead of AST_Undefined which may conflict |
|
* with an existing variable called `undefined` */ |
|
function make_void_0(orig) { |
|
return make_node(AST_UnaryPrefix, orig, { |
|
operator: "void", |
|
expression: make_node(AST_Number, orig, { value: 0 }) |
|
}); |
|
} |
|
|
|
function push_uniq(array, el) { |
|
if (!array.includes(el)) |
|
array.push(el); |
|
} |
|
|
|
function string_template(text, props) { |
|
return text.replace(/{(.+?)}/g, function(str, p) { |
|
return props && props[p]; |
|
}); |
|
} |
|
|
|
function remove(array, el) { |
|
for (var i = array.length; --i >= 0;) { |
|
if (array[i] === el) array.splice(i, 1); |
|
} |
|
} |
|
|
|
function mergeSort(array, cmp) { |
|
if (array.length < 2) return array.slice(); |
|
function merge(a, b) { |
|
var r = [], ai = 0, bi = 0, i = 0; |
|
while (ai < a.length && bi < b.length) { |
|
cmp(a[ai], b[bi]) <= 0 |
|
? r[i++] = a[ai++] |
|
: r[i++] = b[bi++]; |
|
} |
|
if (ai < a.length) r.push.apply(r, a.slice(ai)); |
|
if (bi < b.length) r.push.apply(r, b.slice(bi)); |
|
return r; |
|
} |
|
function _ms(a) { |
|
if (a.length <= 1) |
|
return a; |
|
var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m); |
|
left = _ms(left); |
|
right = _ms(right); |
|
return merge(left, right); |
|
} |
|
return _ms(array); |
|
} |
|
|
|
function makePredicate(words) { |
|
if (!Array.isArray(words)) words = words.split(" "); |
|
|
|
return new Set(words.sort()); |
|
} |
|
|
|
function map_add(map, key, value) { |
|
if (map.has(key)) { |
|
map.get(key).push(value); |
|
} else { |
|
map.set(key, [ value ]); |
|
} |
|
} |
|
|
|
function map_from_object(obj) { |
|
var map = new Map(); |
|
for (var key in obj) { |
|
if (HOP(obj, key) && key.charAt(0) === "$") { |
|
map.set(key.substr(1), obj[key]); |
|
} |
|
} |
|
return map; |
|
} |
|
|
|
function map_to_object(map) { |
|
var obj = Object.create(null); |
|
map.forEach(function (value, key) { |
|
obj["$" + key] = value; |
|
}); |
|
return obj; |
|
} |
|
|
|
function HOP(obj, prop) { |
|
return Object.prototype.hasOwnProperty.call(obj, prop); |
|
} |
|
|
|
function keep_name(keep_setting, name) { |
|
return keep_setting === true |
|
|| (keep_setting instanceof RegExp && keep_setting.test(name)); |
|
} |
|
|
|
var lineTerminatorEscape = { |
|
"\0": "0", |
|
"\n": "n", |
|
"\r": "r", |
|
"\u2028": "u2028", |
|
"\u2029": "u2029", |
|
}; |
|
function regexp_source_fix(source) { |
|
// V8 does not escape line terminators in regexp patterns in node 12 |
|
// We'll also remove literal \0 |
|
return source.replace(/[\0\n\r\u2028\u2029]/g, function (match, offset) { |
|
var escaped = source[offset - 1] == "\\" |
|
&& (source[offset - 2] != "\\" |
|
|| /(?:^|[^\\])(?:\\{2})*$/.test(source.slice(0, offset - 1))); |
|
return (escaped ? "" : "\\") + lineTerminatorEscape[match]; |
|
}); |
|
} |
|
|
|
// Subset of regexps that is not going to cause regexp based DDOS |
|
// https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS |
|
const re_safe_regexp = /^[\\/|\0\s\w^$.[\]()]*$/; |
|
|
|
/** Check if the regexp is safe for Terser to create without risking a RegExp DOS */ |
|
const regexp_is_safe = (source) => re_safe_regexp.test(source); |
|
|
|
const all_flags = "dgimsuyv"; |
|
function sort_regexp_flags(flags) { |
|
const existing_flags = new Set(flags.split("")); |
|
let out = ""; |
|
for (const flag of all_flags) { |
|
if (existing_flags.has(flag)) { |
|
out += flag; |
|
existing_flags.delete(flag); |
|
} |
|
} |
|
if (existing_flags.size) { |
|
// Flags Terser doesn't know about |
|
existing_flags.forEach(flag => { out += flag; }); |
|
} |
|
return out; |
|
} |
|
|
|
function has_annotation(node, annotation) { |
|
return node._annotations & annotation; |
|
} |
|
|
|
function set_annotation(node, annotation) { |
|
node._annotations |= annotation; |
|
} |
|
|
|
function clear_annotation(node, annotation) { |
|
node._annotations &= ~annotation; |
|
} |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/). |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
var LATEST_RAW = ""; // Only used for numbers and template strings |
|
var TEMPLATE_RAWS = new Map(); // Raw template strings |
|
|
|
var KEYWORDS = "break case catch class const continue debugger default delete do else export extends finally for function if in instanceof let new return switch throw try typeof var void while with"; |
|
var KEYWORDS_ATOM = "false null true"; |
|
var RESERVED_WORDS = "enum import super this " + KEYWORDS_ATOM + " " + KEYWORDS; |
|
var ALL_RESERVED_WORDS = "implements interface package private protected public static " + RESERVED_WORDS; |
|
var KEYWORDS_BEFORE_EXPRESSION = "return new delete throw else case yield await"; |
|
|
|
KEYWORDS = makePredicate(KEYWORDS); |
|
RESERVED_WORDS = makePredicate(RESERVED_WORDS); |
|
KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION); |
|
KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM); |
|
ALL_RESERVED_WORDS = makePredicate(ALL_RESERVED_WORDS); |
|
|
|
var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); |
|
|
|
var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; |
|
var RE_OCT_NUMBER = /^0[0-7]+$/; |
|
var RE_ES6_OCT_NUMBER = /^0o[0-7]+$/i; |
|
var RE_BIN_NUMBER = /^0b[01]+$/i; |
|
var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i; |
|
var RE_BIG_INT = /^(0[xob])?[0-9a-f]+n$/i; |
|
|
|
var RE_KEYWORD_RELATIONAL_OPERATORS = /in(?:stanceof)?/y; |
|
|
|
var OPERATORS = makePredicate([ |
|
"in", |
|
"instanceof", |
|
"typeof", |
|
"new", |
|
"void", |
|
"delete", |
|
"++", |
|
"--", |
|
"+", |
|
"-", |
|
"!", |
|
"~", |
|
"&", |
|
"|", |
|
"^", |
|
"*", |
|
"**", |
|
"/", |
|
"%", |
|
">>", |
|
"<<", |
|
">>>", |
|
"<", |
|
">", |
|
"<=", |
|
">=", |
|
"==", |
|
"===", |
|
"!=", |
|
"!==", |
|
"?", |
|
"=", |
|
"+=", |
|
"-=", |
|
"||=", |
|
"&&=", |
|
"??=", |
|
"/=", |
|
"*=", |
|
"**=", |
|
"%=", |
|
">>=", |
|
"<<=", |
|
">>>=", |
|
"|=", |
|
"^=", |
|
"&=", |
|
"&&", |
|
"??", |
|
"||", |
|
]); |
|
|
|
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\uFEFF")); |
|
|
|
var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029")); |
|
|
|
var PUNC_AFTER_EXPRESSION = makePredicate(characters(";]),:")); |
|
|
|
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:")); |
|
|
|
var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); |
|
|
|
/* -----[ Tokenizer ]----- */ |
|
|
|
// surrogate safe regexps adapted from https://github.com/mathiasbynens/unicode-8.0.0/tree/89b412d8a71ecca9ed593d9e9fa073ab64acfebe/Binary_Property |
|
var UNICODE = { |
|
ID_Start: /[$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/, |
|
ID_Continue: /(?:[$0-9A-Z_a-z\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF])+/, |
|
}; |
|
|
|
function get_full_char(str, pos) { |
|
if (is_surrogate_pair_head(str.charCodeAt(pos))) { |
|
if (is_surrogate_pair_tail(str.charCodeAt(pos + 1))) { |
|
return str.charAt(pos) + str.charAt(pos + 1); |
|
} |
|
} else if (is_surrogate_pair_tail(str.charCodeAt(pos))) { |
|
if (is_surrogate_pair_head(str.charCodeAt(pos - 1))) { |
|
return str.charAt(pos - 1) + str.charAt(pos); |
|
} |
|
} |
|
return str.charAt(pos); |
|
} |
|
|
|
function get_full_char_code(str, pos) { |
|
// https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates |
|
if (is_surrogate_pair_head(str.charCodeAt(pos))) { |
|
return 0x10000 + (str.charCodeAt(pos) - 0xd800 << 10) + str.charCodeAt(pos + 1) - 0xdc00; |
|
} |
|
return str.charCodeAt(pos); |
|
} |
|
|
|
function get_full_char_length(str) { |
|
var surrogates = 0; |
|
|
|
for (var i = 0; i < str.length; i++) { |
|
if (is_surrogate_pair_head(str.charCodeAt(i)) && is_surrogate_pair_tail(str.charCodeAt(i + 1))) { |
|
surrogates++; |
|
i++; |
|
} |
|
} |
|
|
|
return str.length - surrogates; |
|
} |
|
|
|
function from_char_code(code) { |
|
// Based on https://github.com/mathiasbynens/String.fromCodePoint/blob/master/fromcodepoint.js |
|
if (code > 0xFFFF) { |
|
code -= 0x10000; |
|
return (String.fromCharCode((code >> 10) + 0xD800) + |
|
String.fromCharCode((code % 0x400) + 0xDC00)); |
|
} |
|
return String.fromCharCode(code); |
|
} |
|
|
|
function is_surrogate_pair_head(code) { |
|
return code >= 0xd800 && code <= 0xdbff; |
|
} |
|
|
|
function is_surrogate_pair_tail(code) { |
|
return code >= 0xdc00 && code <= 0xdfff; |
|
} |
|
|
|
function is_digit(code) { |
|
return code >= 48 && code <= 57; |
|
} |
|
|
|
function is_identifier_start(ch) { |
|
return UNICODE.ID_Start.test(ch); |
|
} |
|
|
|
function is_identifier_char(ch) { |
|
return UNICODE.ID_Continue.test(ch); |
|
} |
|
|
|
const BASIC_IDENT = /^[a-z_$][a-z0-9_$]*$/i; |
|
|
|
function is_basic_identifier_string(str) { |
|
return BASIC_IDENT.test(str); |
|
} |
|
|
|
function is_identifier_string(str, allow_surrogates) { |
|
if (BASIC_IDENT.test(str)) { |
|
return true; |
|
} |
|
if (!allow_surrogates && /[\ud800-\udfff]/.test(str)) { |
|
return false; |
|
} |
|
var match = UNICODE.ID_Start.exec(str); |
|
if (!match || match.index !== 0) { |
|
return false; |
|
} |
|
|
|
str = str.slice(match[0].length); |
|
if (!str) { |
|
return true; |
|
} |
|
|
|
match = UNICODE.ID_Continue.exec(str); |
|
return !!match && match[0].length === str.length; |
|
} |
|
|
|
function parse_js_number(num, allow_e = true) { |
|
if (!allow_e && num.includes("e")) { |
|
return NaN; |
|
} |
|
if (RE_HEX_NUMBER.test(num)) { |
|
return parseInt(num.substr(2), 16); |
|
} else if (RE_OCT_NUMBER.test(num)) { |
|
return parseInt(num.substr(1), 8); |
|
} else if (RE_ES6_OCT_NUMBER.test(num)) { |
|
return parseInt(num.substr(2), 8); |
|
} else if (RE_BIN_NUMBER.test(num)) { |
|
return parseInt(num.substr(2), 2); |
|
} else if (RE_DEC_NUMBER.test(num)) { |
|
return parseFloat(num); |
|
} else { |
|
var val = parseFloat(num); |
|
if (val == num) return val; |
|
} |
|
} |
|
|
|
class JS_Parse_Error extends Error { |
|
constructor(message, filename, line, col, pos) { |
|
super(); |
|
|
|
this.name = "SyntaxError"; |
|
this.message = message; |
|
this.filename = filename; |
|
this.line = line; |
|
this.col = col; |
|
this.pos = pos; |
|
} |
|
} |
|
|
|
function js_error(message, filename, line, col, pos) { |
|
throw new JS_Parse_Error(message, filename, line, col, pos); |
|
} |
|
|
|
function is_token(token, type, val) { |
|
return token.type == type && (val == null || token.value == val); |
|
} |
|
|
|
var EX_EOF = {}; |
|
|
|
function tokenizer($TEXT, filename, html5_comments, shebang) { |
|
var S = { |
|
text : $TEXT, |
|
filename : filename, |
|
pos : 0, |
|
tokpos : 0, |
|
line : 1, |
|
tokline : 0, |
|
col : 0, |
|
tokcol : 0, |
|
newline_before : false, |
|
regex_allowed : false, |
|
brace_counter : 0, |
|
template_braces : [], |
|
comments_before : [], |
|
directives : {}, |
|
directive_stack : [] |
|
}; |
|
|
|
function peek() { return get_full_char(S.text, S.pos); } |
|
|
|
// Used because parsing ?. involves a lookahead for a digit |
|
function is_option_chain_op() { |
|
const must_be_dot = S.text.charCodeAt(S.pos + 1) === 46; |
|
if (!must_be_dot) return false; |
|
|
|
const cannot_be_digit = S.text.charCodeAt(S.pos + 2); |
|
return cannot_be_digit < 48 || cannot_be_digit > 57; |
|
} |
|
|
|
function next(signal_eof, in_string) { |
|
var ch = get_full_char(S.text, S.pos++); |
|
if (signal_eof && !ch) |
|
throw EX_EOF; |
|
if (NEWLINE_CHARS.has(ch)) { |
|
S.newline_before = S.newline_before || !in_string; |
|
++S.line; |
|
S.col = 0; |
|
if (ch == "\r" && peek() == "\n") { |
|
// treat a \r\n sequence as a single \n |
|
++S.pos; |
|
ch = "\n"; |
|
} |
|
} else { |
|
if (ch.length > 1) { |
|
++S.pos; |
|
++S.col; |
|
} |
|
++S.col; |
|
} |
|
return ch; |
|
} |
|
|
|
function forward(i) { |
|
while (i--) next(); |
|
} |
|
|
|
function looking_at(str) { |
|
return S.text.substr(S.pos, str.length) == str; |
|
} |
|
|
|
function find_eol() { |
|
var text = S.text; |
|
for (var i = S.pos, n = S.text.length; i < n; ++i) { |
|
var ch = text[i]; |
|
if (NEWLINE_CHARS.has(ch)) |
|
return i; |
|
} |
|
return -1; |
|
} |
|
|
|
function find(what, signal_eof) { |
|
var pos = S.text.indexOf(what, S.pos); |
|
if (signal_eof && pos == -1) throw EX_EOF; |
|
return pos; |
|
} |
|
|
|
function start_token() { |
|
S.tokline = S.line; |
|
S.tokcol = S.col; |
|
S.tokpos = S.pos; |
|
} |
|
|
|
var prev_was_dot = false; |
|
var previous_token = null; |
|
function token(type, value, is_comment) { |
|
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX.has(value)) || |
|
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION.has(value)) || |
|
(type == "punc" && PUNC_BEFORE_EXPRESSION.has(value))) || |
|
(type == "arrow"); |
|
if (type == "punc" && (value == "." || value == "?.")) { |
|
prev_was_dot = true; |
|
} else if (!is_comment) { |
|
prev_was_dot = false; |
|
} |
|
const line = S.tokline; |
|
const col = S.tokcol; |
|
const pos = S.tokpos; |
|
const nlb = S.newline_before; |
|
const file = filename; |
|
let comments_before = []; |
|
let comments_after = []; |
|
|
|
if (!is_comment) { |
|
comments_before = S.comments_before; |
|
comments_after = S.comments_before = []; |
|
} |
|
S.newline_before = false; |
|
const tok = new AST_Token(type, value, line, col, pos, nlb, comments_before, comments_after, file); |
|
|
|
if (!is_comment) previous_token = tok; |
|
return tok; |
|
} |
|
|
|
function skip_whitespace() { |
|
while (WHITESPACE_CHARS.has(peek())) |
|
next(); |
|
} |
|
|
|
function peek_next_token_start_or_newline() { |
|
var pos = S.pos; |
|
for (var in_multiline_comment = false; pos < S.text.length; ) { |
|
var ch = get_full_char(S.text, pos); |
|
if (NEWLINE_CHARS.has(ch)) { |
|
return { char: ch, pos: pos }; |
|
} else if (in_multiline_comment) { |
|
if (ch == "*" && get_full_char(S.text, pos + 1) == "/") { |
|
pos += 2; |
|
in_multiline_comment = false; |
|
} else { |
|
pos++; |
|
} |
|
} else if (!WHITESPACE_CHARS.has(ch)) { |
|
if (ch == "/") { |
|
var next_ch = get_full_char(S.text, pos + 1); |
|
if (next_ch == "/") { |
|
pos = find_eol(); |
|
return { char: get_full_char(S.text, pos), pos: pos }; |
|
} else if (next_ch == "*") { |
|
in_multiline_comment = true; |
|
pos += 2; |
|
continue; |
|
} |
|
} |
|
return { char: ch, pos: pos }; |
|
} else { |
|
pos++; |
|
} |
|
} |
|
return { char: null, pos: pos }; |
|
} |
|
|
|
function ch_starts_binding_identifier(ch, pos) { |
|
if (ch == "\\") { |
|
return true; |
|
} else if (is_identifier_start(ch)) { |
|
RE_KEYWORD_RELATIONAL_OPERATORS.lastIndex = pos; |
|
if (RE_KEYWORD_RELATIONAL_OPERATORS.test(S.text)) { |
|
var after = get_full_char(S.text, RE_KEYWORD_RELATIONAL_OPERATORS.lastIndex); |
|
if (!is_identifier_char(after) && after != "\\") { |
|
// "in" or "instanceof" are keywords, not binding identifiers |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
function read_while(pred) { |
|
var ret = "", ch, i = 0; |
|
while ((ch = peek()) && pred(ch, i++)) |
|
ret += next(); |
|
return ret; |
|
} |
|
|
|
function parse_error(err) { |
|
js_error(err, filename, S.tokline, S.tokcol, S.tokpos); |
|
} |
|
|
|
function read_num(prefix) { |
|
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false, numeric_separator = false; |
|
var num = read_while(function(ch, i) { |
|
if (is_big_int) return false; |
|
|
|
var code = ch.charCodeAt(0); |
|
switch (code) { |
|
case 95: // _ |
|
return (numeric_separator = true); |
|
case 98: case 66: // bB |
|
return (has_x = true); // Can occur in hex sequence, don't return false yet |
|
case 111: case 79: // oO |
|
case 120: case 88: // xX |
|
return has_x ? false : (has_x = true); |
|
case 101: case 69: // eE |
|
return has_x ? true : has_e ? false : (has_e = after_e = true); |
|
case 45: // - |
|
return after_e || (i == 0 && !prefix); |
|
case 43: // + |
|
return after_e; |
|
case (after_e = false, 46): // . |
|
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; |
|
case 110: // n |
|
is_big_int = true; |
|
return true; |
|
} |
|
|
|
return ( |
|
code >= 48 && code <= 57 // 0-9 |
|
|| code >= 97 && code <= 102 // a-f |
|
|| code >= 65 && code <= 70 // A-F |
|
); |
|
}); |
|
if (prefix) num = prefix + num; |
|
|
|
LATEST_RAW = num; |
|
|
|
if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) { |
|
parse_error("Legacy octal literals are not allowed in strict mode"); |
|
} |
|
if (numeric_separator) { |
|
if (num.endsWith("_")) { |
|
parse_error("Numeric separators are not allowed at the end of numeric literals"); |
|
} else if (num.includes("__")) { |
|
parse_error("Only one underscore is allowed as numeric separator"); |
|
} |
|
num = num.replace(/_/g, ""); |
|
} |
|
if (is_big_int) { |
|
const without_n = num.slice(0, -1); |
|
const allow_e = RE_HEX_NUMBER.test(without_n); |
|
const valid = parse_js_number(without_n, allow_e); |
|
if (!has_dot && RE_BIG_INT.test(num) && !isNaN(valid)) |
|
return token("big_int", without_n); |
|
parse_error("Invalid or unexpected token"); |
|
} |
|
var valid = parse_js_number(num); |
|
if (!isNaN(valid)) { |
|
return token("num", valid); |
|
} else { |
|
parse_error("Invalid syntax: " + num); |
|
} |
|
} |
|
|
|
function is_octal(ch) { |
|
return ch >= "0" && ch <= "7"; |
|
} |
|
|
|
function read_escaped_char(in_string, strict_hex, template_string) { |
|
var ch = next(true, in_string); |
|
switch (ch.charCodeAt(0)) { |
|
case 110 : return "\n"; |
|
case 114 : return "\r"; |
|
case 116 : return "\t"; |
|
case 98 : return "\b"; |
|
case 118 : return "\u000b"; // \v |
|
case 102 : return "\f"; |
|
case 120 : return String.fromCharCode(hex_bytes(2, strict_hex)); // \x |
|
case 117 : // \u |
|
if (peek() == "{") { |
|
next(true); |
|
if (peek() === "}") |
|
parse_error("Expecting hex-character between {}"); |
|
while (peek() == "0") next(true); // No significance |
|
var result, length = find("}", true) - S.pos; |
|
// Avoid 32 bit integer overflow (1 << 32 === 1) |
|
// We know first character isn't 0 and thus out of range anyway |
|
if (length > 6 || (result = hex_bytes(length, strict_hex)) > 0x10FFFF) { |
|
parse_error("Unicode reference out of bounds"); |
|
} |
|
next(true); |
|
return from_char_code(result); |
|
} |
|
return String.fromCharCode(hex_bytes(4, strict_hex)); |
|
case 10 : return ""; // newline |
|
case 13 : // \r |
|
if (peek() == "\n") { // DOS newline |
|
next(true, in_string); |
|
return ""; |
|
} |
|
} |
|
if (is_octal(ch)) { |
|
if (template_string && strict_hex) { |
|
const represents_null_character = ch === "0" && !is_octal(peek()); |
|
if (!represents_null_character) { |
|
parse_error("Octal escape sequences are not allowed in template strings"); |
|
} |
|
} |
|
return read_octal_escape_sequence(ch, strict_hex); |
|
} |
|
return ch; |
|
} |
|
|
|
function read_octal_escape_sequence(ch, strict_octal) { |
|
// Read |
|
var p = peek(); |
|
if (p >= "0" && p <= "7") { |
|
ch += next(true); |
|
if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7") |
|
ch += next(true); |
|
} |
|
|
|
// Parse |
|
if (ch === "0") return "\0"; |
|
if (ch.length > 0 && next_token.has_directive("use strict") && strict_octal) |
|
parse_error("Legacy octal escape sequences are not allowed in strict mode"); |
|
return String.fromCharCode(parseInt(ch, 8)); |
|
} |
|
|
|
function hex_bytes(n, strict_hex) { |
|
var num = 0; |
|
for (; n > 0; --n) { |
|
if (!strict_hex && isNaN(parseInt(peek(), 16))) { |
|
return parseInt(num, 16) || ""; |
|
} |
|
var digit = next(true); |
|
if (isNaN(parseInt(digit, 16))) |
|
parse_error("Invalid hex-character pattern in string"); |
|
num += digit; |
|
} |
|
return parseInt(num, 16); |
|
} |
|
|
|
var read_string = with_eof_error("Unterminated string constant", function() { |
|
const start_pos = S.pos; |
|
var quote = next(), ret = []; |
|
for (;;) { |
|
var ch = next(true, true); |
|
if (ch == "\\") ch = read_escaped_char(true, true); |
|
else if (ch == "\r" || ch == "\n") parse_error("Unterminated string constant"); |
|
else if (ch == quote) break; |
|
ret.push(ch); |
|
} |
|
var tok = token("string", ret.join("")); |
|
LATEST_RAW = S.text.slice(start_pos, S.pos); |
|
tok.quote = quote; |
|
return tok; |
|
}); |
|
|
|
var read_template_characters = with_eof_error("Unterminated template", function(begin) { |
|
if (begin) { |
|
S.template_braces.push(S.brace_counter); |
|
} |
|
var content = "", raw = "", ch, tok; |
|
next(true, true); |
|
while ((ch = next(true, true)) != "`") { |
|
if (ch == "\r") { |
|
if (peek() == "\n") ++S.pos; |
|
ch = "\n"; |
|
} else if (ch == "$" && peek() == "{") { |
|
next(true, true); |
|
S.brace_counter++; |
|
tok = token(begin ? "template_head" : "template_cont", content); |
|
TEMPLATE_RAWS.set(tok, raw); |
|
tok.template_end = false; |
|
return tok; |
|
} |
|
|
|
raw += ch; |
|
if (ch == "\\") { |
|
var tmp = S.pos; |
|
var prev_is_tag = previous_token && (previous_token.type === "name" || previous_token.type === "punc" && (previous_token.value === ")" || previous_token.value === "]")); |
|
ch = read_escaped_char(true, !prev_is_tag, true); |
|
raw += S.text.substr(tmp, S.pos - tmp); |
|
} |
|
|
|
content += ch; |
|
} |
|
S.template_braces.pop(); |
|
tok = token(begin ? "template_head" : "template_cont", content); |
|
TEMPLATE_RAWS.set(tok, raw); |
|
tok.template_end = true; |
|
return tok; |
|
}); |
|
|
|
function skip_line_comment(type) { |
|
var regex_allowed = S.regex_allowed; |
|
var i = find_eol(), ret; |
|
if (i == -1) { |
|
ret = S.text.substr(S.pos); |
|
S.pos = S.text.length; |
|
} else { |
|
ret = S.text.substring(S.pos, i); |
|
S.pos = i; |
|
} |
|
S.col = S.tokcol + (S.pos - S.tokpos); |
|
S.comments_before.push(token(type, ret, true)); |
|
S.regex_allowed = regex_allowed; |
|
return next_token; |
|
} |
|
|
|
var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function() { |
|
var regex_allowed = S.regex_allowed; |
|
var i = find("*/", true); |
|
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, "\n"); |
|
// update stream position |
|
forward(get_full_char_length(text) /* text length doesn't count \r\n as 2 char while S.pos - i does */ + 2); |
|
S.comments_before.push(token("comment2", text, true)); |
|
S.newline_before = S.newline_before || text.includes("\n"); |
|
S.regex_allowed = regex_allowed; |
|
return next_token; |
|
}); |
|
|
|
var read_name = function () { |
|
let start = S.pos, end = start - 1, ch = "c"; |
|
|
|
while ( |
|
(ch = S.text.charAt(++end)) |
|
&& (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z") |
|
); |
|
|
|
// 0x7F is very rare in actual code, so we compare it to "~" (0x7E) |
|
if (end > start + 1 && ch && ch !== "\\" && !is_identifier_char(ch) && ch <= "~") { |
|
S.pos += end - start; |
|
S.col += end - start; |
|
return S.text.slice(start, S.pos); |
|
} |
|
|
|
return read_name_hard(); |
|
}; |
|
|
|
var read_name_hard = with_eof_error("Unterminated identifier name", function() { |
|
var name = [], ch, escaped = false; |
|
var read_escaped_identifier_char = function() { |
|
escaped = true; |
|
next(); |
|
if (peek() !== "u") { |
|
parse_error("Expecting UnicodeEscapeSequence -- uXXXX or u{XXXX}"); |
|
} |
|
return read_escaped_char(false, true); |
|
}; |
|
|
|
// Read first character (ID_Start) |
|
if ((ch = peek()) === "\\") { |
|
ch = read_escaped_identifier_char(); |
|
if (!is_identifier_start(ch)) { |
|
parse_error("First identifier char is an invalid identifier char"); |
|
} |
|
} else if (is_identifier_start(ch)) { |
|
next(); |
|
} else { |
|
return ""; |
|
} |
|
|
|
name.push(ch); |
|
|
|
// Read ID_Continue |
|
while ((ch = peek()) != null) { |
|
if ((ch = peek()) === "\\") { |
|
ch = read_escaped_identifier_char(); |
|
if (!is_identifier_char(ch)) { |
|
parse_error("Invalid escaped identifier char"); |
|
} |
|
} else { |
|
if (!is_identifier_char(ch)) { |
|
break; |
|
} |
|
next(); |
|
} |
|
name.push(ch); |
|
} |
|
const name_str = name.join(""); |
|
if (RESERVED_WORDS.has(name_str) && escaped) { |
|
parse_error("Escaped characters are not allowed in keywords"); |
|
} |
|
return name_str; |
|
}); |
|
|
|
var read_regexp = with_eof_error("Unterminated regular expression", function(source) { |
|
var prev_backslash = false, ch, in_class = false; |
|
while ((ch = next(true))) if (NEWLINE_CHARS.has(ch)) { |
|
parse_error("Unexpected line terminator"); |
|
} else if (prev_backslash) { |
|
if (/^[\u0000-\u007F]$/.test(ch)) { |
|
source += "\\" + ch; |
|
} else { |
|
// Remove the useless slash before the escape, but only for characters that won't be added to regexp syntax |
|
source += ch; |
|
} |
|
prev_backslash = false; |
|
} else if (ch == "[") { |
|
in_class = true; |
|
source += ch; |
|
} else if (ch == "]" && in_class) { |
|
in_class = false; |
|
source += ch; |
|
} else if (ch == "/" && !in_class) { |
|
break; |
|
} else if (ch == "\\") { |
|
prev_backslash = true; |
|
} else { |
|
source += ch; |
|
} |
|
const flags = read_name(); |
|
return token("regexp", "/" + source + "/" + flags); |
|
}); |
|
|
|
function read_operator(prefix) { |
|
function grow(op) { |
|
if (!peek()) return op; |
|
var bigger = op + peek(); |
|
if (OPERATORS.has(bigger)) { |
|
next(); |
|
return grow(bigger); |
|
} else { |
|
return op; |
|
} |
|
} |
|
return token("operator", grow(prefix || next())); |
|
} |
|
|
|
function handle_slash() { |
|
next(); |
|
switch (peek()) { |
|
case "/": |
|
next(); |
|
return skip_line_comment("comment1"); |
|
case "*": |
|
next(); |
|
return skip_multiline_comment(); |
|
} |
|
return S.regex_allowed ? read_regexp("") : read_operator("/"); |
|
} |
|
|
|
function handle_eq_sign() { |
|
next(); |
|
if (peek() === ">") { |
|
next(); |
|
return token("arrow", "=>"); |
|
} else { |
|
return read_operator("="); |
|
} |
|
} |
|
|
|
function handle_dot() { |
|
next(); |
|
if (is_digit(peek().charCodeAt(0))) { |
|
return read_num("."); |
|
} |
|
if (peek() === ".") { |
|
next(); // Consume second dot |
|
next(); // Consume third dot |
|
return token("expand", "..."); |
|
} |
|
|
|
return token("punc", "."); |
|
} |
|
|
|
function read_word() { |
|
var word = read_name(); |
|
if (prev_was_dot) return token("name", word); |
|
return KEYWORDS_ATOM.has(word) ? token("atom", word) |
|
: !KEYWORDS.has(word) ? token("name", word) |
|
: OPERATORS.has(word) ? token("operator", word) |
|
: token("keyword", word); |
|
} |
|
|
|
function read_private_word() { |
|
next(); |
|
return token("privatename", read_name()); |
|
} |
|
|
|
function with_eof_error(eof_error, cont) { |
|
return function(x) { |
|
try { |
|
return cont(x); |
|
} catch(ex) { |
|
if (ex === EX_EOF) parse_error(eof_error); |
|
else throw ex; |
|
} |
|
}; |
|
} |
|
|
|
function next_token(force_regexp) { |
|
if (force_regexp != null) |
|
return read_regexp(force_regexp); |
|
if (shebang && S.pos == 0 && looking_at("#!")) { |
|
start_token(); |
|
forward(2); |
|
skip_line_comment("comment5"); |
|
} |
|
for (;;) { |
|
skip_whitespace(); |
|
start_token(); |
|
if (html5_comments) { |
|
if (looking_at("<!--")) { |
|
forward(4); |
|
skip_line_comment("comment3"); |
|
continue; |
|
} |
|
if (looking_at("-->") && S.newline_before) { |
|
forward(3); |
|
skip_line_comment("comment4"); |
|
continue; |
|
} |
|
} |
|
var ch = peek(); |
|
if (!ch) return token("eof"); |
|
var code = ch.charCodeAt(0); |
|
switch (code) { |
|
case 34: case 39: return read_string(); |
|
case 46: return handle_dot(); |
|
case 47: { |
|
var tok = handle_slash(); |
|
if (tok === next_token) continue; |
|
return tok; |
|
} |
|
case 61: return handle_eq_sign(); |
|
case 63: { |
|
if (!is_option_chain_op()) break; // Handled below |
|
|
|
next(); // ? |
|
next(); // . |
|
|
|
return token("punc", "?."); |
|
} |
|
case 96: return read_template_characters(true); |
|
case 123: |
|
S.brace_counter++; |
|
break; |
|
case 125: |
|
S.brace_counter--; |
|
if (S.template_braces.length > 0 |
|
&& S.template_braces[S.template_braces.length - 1] === S.brace_counter) |
|
return read_template_characters(false); |
|
break; |
|
} |
|
if (is_digit(code)) return read_num(); |
|
if (PUNC_CHARS.has(ch)) return token("punc", next()); |
|
if (OPERATOR_CHARS.has(ch)) return read_operator(); |
|
if (code == 92 || is_identifier_start(ch)) return read_word(); |
|
if (code == 35) return read_private_word(); |
|
break; |
|
} |
|
parse_error("Unexpected character '" + ch + "'"); |
|
} |
|
|
|
next_token.next = next; |
|
next_token.peek = peek; |
|
|
|
next_token.context = function(nc) { |
|
if (nc) S = nc; |
|
return S; |
|
}; |
|
|
|
next_token.add_directive = function(directive) { |
|
S.directive_stack[S.directive_stack.length - 1].push(directive); |
|
|
|
if (S.directives[directive] === undefined) { |
|
S.directives[directive] = 1; |
|
} else { |
|
S.directives[directive]++; |
|
} |
|
}; |
|
|
|
next_token.push_directives_stack = function() { |
|
S.directive_stack.push([]); |
|
}; |
|
|
|
next_token.pop_directives_stack = function() { |
|
var directives = S.directive_stack[S.directive_stack.length - 1]; |
|
|
|
for (var i = 0; i < directives.length; i++) { |
|
S.directives[directives[i]]--; |
|
} |
|
|
|
S.directive_stack.pop(); |
|
}; |
|
|
|
next_token.has_directive = function(directive) { |
|
return S.directives[directive] > 0; |
|
}; |
|
|
|
next_token.peek_next_token_start_or_newline = peek_next_token_start_or_newline; |
|
next_token.ch_starts_binding_identifier = ch_starts_binding_identifier; |
|
|
|
return next_token; |
|
|
|
} |
|
|
|
/* -----[ Parser (constants) ]----- */ |
|
|
|
var UNARY_PREFIX = makePredicate([ |
|
"typeof", |
|
"void", |
|
"delete", |
|
"--", |
|
"++", |
|
"!", |
|
"~", |
|
"-", |
|
"+" |
|
]); |
|
|
|
var UNARY_POSTFIX = makePredicate([ "--", "++" ]); |
|
|
|
var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "??=", "&&=", "||=", "/=", "*=", "**=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); |
|
|
|
var LOGICAL_ASSIGNMENT = makePredicate([ "??=", "&&=", "||=" ]); |
|
|
|
var PRECEDENCE = (function(a, ret) { |
|
for (var i = 0; i < a.length; ++i) { |
|
for (const op of a[i]) { |
|
ret[op] = i + 1; |
|
} |
|
} |
|
return ret; |
|
})( |
|
[ |
|
["||"], |
|
["??"], |
|
["&&"], |
|
["|"], |
|
["^"], |
|
["&"], |
|
["==", "===", "!=", "!=="], |
|
["<", ">", "<=", ">=", "in", "instanceof"], |
|
[">>", "<<", ">>>"], |
|
["+", "-"], |
|
["*", "/", "%"], |
|
["**"] |
|
], |
|
{} |
|
); |
|
|
|
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "big_int", "string", "regexp", "name"]); |
|
|
|
/* -----[ Parser ]----- */ |
|
|
|
function parse($TEXT, options) { |
|
// maps start tokens to count of comments found outside of their parens |
|
// Example: /* I count */ ( /* I don't */ foo() ) |
|
// Useful because comments_before property of call with parens outside |
|
// contains both comments inside and outside these parens. Used to find the |
|
|
|
const outer_comments_before_counts = new WeakMap(); |
|
|
|
options = defaults(options, { |
|
bare_returns : false, |
|
ecma : null, // Legacy |
|
expression : false, |
|
filename : null, |
|
html5_comments : true, |
|
module : false, |
|
shebang : true, |
|
strict : false, |
|
toplevel : null, |
|
}, true); |
|
|
|
var S = { |
|
input : (typeof $TEXT == "string" |
|
? tokenizer($TEXT, options.filename, |
|
options.html5_comments, options.shebang) |
|
: $TEXT), |
|
token : null, |
|
prev : null, |
|
peeked : null, |
|
in_function : 0, |
|
in_async : -1, |
|
in_generator : -1, |
|
in_directives : true, |
|
in_loop : 0, |
|
labels : [] |
|
}; |
|
|
|
S.token = next(); |
|
|
|
function is(type, value) { |
|
return is_token(S.token, type, value); |
|
} |
|
|
|
function peek() { return S.peeked || (S.peeked = S.input()); } |
|
|
|
function next() { |
|
S.prev = S.token; |
|
|
|
if (!S.peeked) peek(); |
|
S.token = S.peeked; |
|
S.peeked = null; |
|
S.in_directives = S.in_directives && ( |
|
S.token.type == "string" || is("punc", ";") |
|
); |
|
return S.token; |
|
} |
|
|
|
function prev() { |
|
return S.prev; |
|
} |
|
|
|
function croak(msg, line, col, pos) { |
|
var ctx = S.input.context(); |
|
js_error(msg, |
|
ctx.filename, |
|
line != null ? line : ctx.tokline, |
|
col != null ? col : ctx.tokcol, |
|
pos != null ? pos : ctx.tokpos); |
|
} |
|
|
|
function token_error(token, msg) { |
|
croak(msg, token.line, token.col); |
|
} |
|
|
|
function unexpected(token) { |
|
if (token == null) |
|
token = S.token; |
|
token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); |
|
} |
|
|
|
function expect_token(type, val) { |
|
if (is(type, val)) { |
|
return next(); |
|
} |
|
token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); |
|
} |
|
|
|
function expect(punc) { return expect_token("punc", punc); } |
|
|
|
function has_newline_before(token) { |
|
return token.nlb || !token.comments_before.every((comment) => !comment.nlb); |
|
} |
|
|
|
function can_insert_semicolon() { |
|
return !options.strict |
|
&& (is("eof") || is("punc", "}") || has_newline_before(S.token)); |
|
} |
|
|
|
function is_in_generator() { |
|
return S.in_generator === S.in_function; |
|
} |
|
|
|
function is_in_async() { |
|
return S.in_async === S.in_function; |
|
} |
|
|
|
function can_await() { |
|
return ( |
|
S.in_async === S.in_function |
|
|| S.in_function === 0 && S.input.has_directive("use strict") |
|
); |
|
} |
|
|
|
function semicolon(optional) { |
|
if (is("punc", ";")) next(); |
|
else if (!optional && !can_insert_semicolon()) unexpected(); |
|
} |
|
|
|
function parenthesised() { |
|
expect("("); |
|
var exp = expression(true); |
|
expect(")"); |
|
return exp; |
|
} |
|
|
|
function embed_tokens(parser) { |
|
return function _embed_tokens_wrapper(...args) { |
|
const start = S.token; |
|
const expr = parser(...args); |
|
expr.start = start; |
|
expr.end = prev(); |
|
return expr; |
|
}; |
|
} |
|
|
|
function handle_regexp() { |
|
if (is("operator", "/") || is("operator", "/=")) { |
|
S.peeked = null; |
|
S.token = S.input(S.token.value.substr(1)); // force regexp |
|
} |
|
} |
|
|
|
var statement = embed_tokens(function statement(is_export_default, is_for_body, is_if_body) { |
|
handle_regexp(); |
|
switch (S.token.type) { |
|
case "string": |
|
if (S.in_directives) { |
|
var token = peek(); |
|
if (!LATEST_RAW.includes("\\") |
|
&& (is_token(token, "punc", ";") |
|
|| is_token(token, "punc", "}") |
|
|| has_newline_before(token) |
|
|| is_token(token, "eof"))) { |
|
S.input.add_directive(S.token.value); |
|
} else { |
|
S.in_directives = false; |
|
} |
|
} |
|
var dir = S.in_directives, stat = simple_statement(); |
|
return dir && stat.body instanceof AST_String ? new AST_Directive(stat.body) : stat; |
|
case "template_head": |
|
case "num": |
|
case "big_int": |
|
case "regexp": |
|
case "operator": |
|
case "atom": |
|
return simple_statement(); |
|
|
|
case "name": |
|
if (S.token.value == "async" && is_token(peek(), "keyword", "function")) { |
|
next(); |
|
next(); |
|
if (is_for_body) { |
|
croak("functions are not allowed as the body of a loop"); |
|
} |
|
return function_(AST_Defun, false, true, is_export_default); |
|
} |
|
if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) { |
|
next(); |
|
var node = import_statement(); |
|
semicolon(); |
|
return node; |
|
} |
|
if (S.token.value == "using" && is_token(peek(), "name") && !has_newline_before(peek())) { |
|
next(); |
|
var node = using_(); |
|
semicolon(); |
|
return node; |
|
} |
|
if (S.token.value == "await" && can_await() && is_token(peek(), "name", "using") && !has_newline_before(peek())) { |
|
var next_next = S.input.peek_next_token_start_or_newline(); |
|
if (S.input.ch_starts_binding_identifier(next_next.char, next_next.pos)) { |
|
next(); |
|
// The "using" token will be consumed by the await_using_ function. |
|
var node = await_using_(); |
|
semicolon(); |
|
return node; |
|
} |
|
} |
|
return is_token(peek(), "punc", ":") |
|
? labeled_statement() |
|
: simple_statement(); |
|
|
|
case "privatename": |
|
if(!S.in_class) |
|
croak("Private field must be used in an enclosing class"); |
|
return simple_statement(); |
|
|
|
case "punc": |
|
switch (S.token.value) { |
|
case "{": |
|
return new AST_BlockStatement({ |
|
start : S.token, |
|
body : block_(), |
|
end : prev() |
|
}); |
|
case "[": |
|
case "(": |
|
return simple_statement(); |
|
case ";": |
|
S.in_directives = false; |
|
next(); |
|
return new AST_EmptyStatement(); |
|
default: |
|
unexpected(); |
|
} |
|
|
|
case "keyword": |
|
switch (S.token.value) { |
|
case "break": |
|
next(); |
|
return break_cont(AST_Break); |
|
|
|
case "continue": |
|
next(); |
|
return break_cont(AST_Continue); |
|
|
|
case "debugger": |
|
next(); |
|
semicolon(); |
|
return new AST_Debugger(); |
|
|
|
case "do": |
|
next(); |
|
var body = in_loop(statement); |
|
expect_token("keyword", "while"); |
|
var condition = parenthesised(); |
|
semicolon(true); |
|
return new AST_Do({ |
|
body : body, |
|
condition : condition |
|
}); |
|
|
|
case "while": |
|
next(); |
|
return new AST_While({ |
|
condition : parenthesised(), |
|
body : in_loop(function() { return statement(false, true); }) |
|
}); |
|
|
|
case "for": |
|
next(); |
|
return for_(); |
|
|
|
case "class": |
|
next(); |
|
if (is_for_body) { |
|
croak("classes are not allowed as the body of a loop"); |
|
} |
|
if (is_if_body) { |
|
croak("classes are not allowed as the body of an if"); |
|
} |
|
return class_(AST_DefClass, is_export_default); |
|
|
|
case "function": |
|
next(); |
|
if (is_for_body) { |
|
croak("functions are not allowed as the body of a loop"); |
|
} |
|
return function_(AST_Defun, false, false, is_export_default); |
|
|
|
case "if": |
|
next(); |
|
return if_(); |
|
|
|
case "return": |
|
if (S.in_function == 0 && !options.bare_returns) |
|
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({ |
|
value: value |
|
}); |
|
|
|
case "switch": |
|
next(); |
|
return new AST_Switch({ |
|
expression : parenthesised(), |
|
body : in_loop(switch_body_) |
|
}); |
|
|
|
case "throw": |
|
next(); |
|
if (has_newline_before(S.token)) |
|
croak("Illegal newline after 'throw'"); |
|
var value = expression(true); |
|
semicolon(); |
|
return new AST_Throw({ |
|
value: value |
|
}); |
|
|
|
case "try": |
|
next(); |
|
return try_(); |
|
|
|
case "var": |
|
next(); |
|
var node = var_(); |
|
semicolon(); |
|
return node; |
|
|
|
case "let": |
|
next(); |
|
var node = let_(); |
|
semicolon(); |
|
return node; |
|
|
|
case "const": |
|
next(); |
|
var node = const_(); |
|
semicolon(); |
|
return node; |
|
|
|
case "with": |
|
if (S.input.has_directive("use strict")) { |
|
croak("Strict mode may not include a with statement"); |
|
} |
|
next(); |
|
return new AST_With({ |
|
expression : parenthesised(), |
|
body : statement() |
|
}); |
|
|
|
case "export": |
|
if (!is_token(peek(), "punc", "(")) { |
|
next(); |
|
var node = export_statement(); |
|
if (is("punc", ";")) semicolon(); |
|
return node; |
|
} |
|
} |
|
} |
|
unexpected(); |
|
}); |
|
|
|
function labeled_statement() { |
|
var label = as_symbol(AST_Label); |
|
if (label.name === "await" && is_in_async()) { |
|
token_error(S.prev, "await cannot be used as label inside async function"); |
|
} |
|
if (S.labels.some((l) => l.name === label.name)) { |
|
// ECMA-262, 12.12: An ECMAScript program is considered |
|
// syntactically incorrect if it contains a |
|
// LabelledStatement that is enclosed by a |
|
// LabelledStatement with the same Identifier as label. |
|
croak("Label " + label.name + " defined twice"); |
|
} |
|
expect(":"); |
|
S.labels.push(label); |
|
var stat = statement(); |
|
S.labels.pop(); |
|
if (!(stat instanceof AST_IterationStatement)) { |
|
// check for `continue` that refers to this label. |
|
// those should be reported as syntax errors. |
|
// https://github.com/mishoo/UglifyJS2/issues/287 |
|
label.references.forEach(function(ref) { |
|
if (ref instanceof AST_Continue) { |
|
ref = ref.label.start; |
|
croak("Continue label `" + label.name + "` refers to non-IterationStatement.", |
|
ref.line, ref.col, ref.pos); |
|
} |
|
}); |
|
} |
|
return new AST_LabeledStatement({ body: stat, label: label }); |
|
} |
|
|
|
function simple_statement(tmp) { |
|
return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) }); |
|
} |
|
|
|
function break_cont(type) { |
|
var label = null, ldef; |
|
if (!can_insert_semicolon()) { |
|
label = as_symbol(AST_LabelRef, true); |
|
} |
|
if (label != null) { |
|
ldef = S.labels.find((l) => l.name === label.name); |
|
if (!ldef) |
|
croak("Undefined label " + label.name); |
|
label.thedef = ldef; |
|
} else if (S.in_loop == 0) |
|
croak(type.TYPE + " not inside a loop or switch"); |
|
semicolon(); |
|
var stat = new type({ label: label }); |
|
if (ldef) ldef.references.push(stat); |
|
return stat; |
|
} |
|
|
|
function for_() { |
|
var for_await_error = "`for await` invalid in this context"; |
|
var await_tok = S.token; |
|
if (await_tok.type == "name" && await_tok.value == "await") { |
|
if (!can_await()) { |
|
token_error(await_tok, for_await_error); |
|
} |
|
next(); |
|
} else { |
|
await_tok = false; |
|
} |
|
expect("("); |
|
var init = null; |
|
if (!is("punc", ";")) { |
|
init = |
|
is("keyword", "var") ? (next(), var_(true)) : |
|
is("keyword", "let") ? (next(), let_(true)) : |
|
is("keyword", "const") ? (next(), const_(true)) : |
|
is("name", "using") && is_token(peek(), "name") && (peek().value != "of" || S.input.peek_next_token_start_or_newline().char == "=") ? (next(), using_(true)) : |
|
is("name", "await") && can_await() && is_token(peek(), "name", "using") ? (next(), await_using_(true)) : |
|
expression(true, true); |
|
var is_in = is("operator", "in"); |
|
var is_of = is("name", "of"); |
|
if (await_tok && !is_of) { |
|
token_error(await_tok, for_await_error); |
|
} |
|
if (is_in || is_of) { |
|
if (init instanceof AST_DefinitionsLike) { |
|
if (init.definitions.length > 1) |
|
token_error(init.start, "Only one variable declaration allowed in for..in loop"); |
|
if (is_in && init instanceof AST_Using) { |
|
token_error(init.start, "Invalid using declaration in for..in loop"); |
|
} |
|
} else if (!(is_assignable(init) || (init = to_destructuring(init)) instanceof AST_Destructuring)) { |
|
token_error(init.start, "Invalid left-hand side in for..in loop"); |
|
} |
|
next(); |
|
if (is_in) { |
|
return for_in(init); |
|
} else { |
|
return for_of(init, !!await_tok); |
|
} |
|
} |
|
} else if (await_tok) { |
|
token_error(await_tok, for_await_error); |
|
} |
|
return regular_for(init); |
|
} |
|
|
|
function regular_for(init) { |
|
expect(";"); |
|
var test = is("punc", ";") ? null : expression(true); |
|
expect(";"); |
|
var step = is("punc", ")") ? null : expression(true); |
|
expect(")"); |
|
return new AST_For({ |
|
init : init, |
|
condition : test, |
|
step : step, |
|
body : in_loop(function() { return statement(false, true); }) |
|
}); |
|
} |
|
|
|
function for_of(init, is_await) { |
|
var lhs = init instanceof AST_DefinitionsLike ? init.definitions[0].name : null; |
|
var obj = expression(true); |
|
expect(")"); |
|
return new AST_ForOf({ |
|
await : is_await, |
|
init : init, |
|
name : lhs, |
|
object : obj, |
|
body : in_loop(function() { return statement(false, true); }) |
|
}); |
|
} |
|
|
|
function for_in(init) { |
|
var obj = expression(true); |
|
expect(")"); |
|
return new AST_ForIn({ |
|
init : init, |
|
object : obj, |
|
body : in_loop(function() { return statement(false, true); }) |
|
}); |
|
} |
|
|
|
var arrow_function = function(start, argnames, is_async) { |
|
if (has_newline_before(S.token)) { |
|
croak("Unexpected newline before arrow (=>)"); |
|
} |
|
|
|
expect_token("arrow", "=>"); |
|
|
|
var body = _function_body(is("punc", "{"), false, is_async); |
|
|
|
return new AST_Arrow({ |
|
start : start, |
|
end : body.end, |
|
async : is_async, |
|
argnames : argnames, |
|
body : body |
|
}); |
|
}; |
|
|
|
var function_ = function(ctor, is_generator, is_async, is_export_default) { |
|
var in_statement = ctor === AST_Defun; |
|
if (is("operator", "*")) { |
|
is_generator = true; |
|
next(); |
|
} |
|
|
|
var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; |
|
if (in_statement && !name) { |
|
if (is_export_default) { |
|
ctor = AST_Function; |
|
} else { |
|
unexpected(); |
|
} |
|
} |
|
|
|
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration)) |
|
unexpected(prev()); |
|
|
|
var args = []; |
|
var body = _function_body(true, is_generator, is_async, name, args); |
|
return new ctor({ |
|
start : args.start, |
|
end : body.end, |
|
is_generator: is_generator, |
|
async : is_async, |
|
name : name, |
|
argnames: args, |
|
body : body |
|
}); |
|
}; |
|
|
|
class UsedParametersTracker { |
|
constructor(is_parameter, strict, duplicates_ok = false) { |
|
this.is_parameter = is_parameter; |
|
this.duplicates_ok = duplicates_ok; |
|
this.parameters = new Set(); |
|
this.duplicate = null; |
|
this.default_assignment = false; |
|
this.spread = false; |
|
this.strict_mode = !!strict; |
|
} |
|
add_parameter(token) { |
|
if (this.parameters.has(token.value)) { |
|
if (this.duplicate === null) { |
|
this.duplicate = token; |
|
} |
|
this.check_strict(); |
|
} else { |
|
this.parameters.add(token.value); |
|
if (this.is_parameter) { |
|
switch (token.value) { |
|
case "arguments": |
|
case "eval": |
|
case "yield": |
|
if (this.strict_mode) { |
|
token_error(token, "Unexpected " + token.value + " identifier as parameter inside strict mode"); |
|
} |
|
break; |
|
default: |
|
if (RESERVED_WORDS.has(token.value)) { |
|
unexpected(); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
mark_default_assignment(token) { |
|
if (this.default_assignment === false) { |
|
this.default_assignment = token; |
|
} |
|
} |
|
mark_spread(token) { |
|
if (this.spread === false) { |
|
this.spread = token; |
|
} |
|
} |
|
mark_strict_mode() { |
|
this.strict_mode = true; |
|
} |
|
is_strict() { |
|
return this.default_assignment !== false || this.spread !== false || this.strict_mode; |
|
} |
|
check_strict() { |
|
if (this.is_strict() && this.duplicate !== null && !this.duplicates_ok) { |
|
token_error(this.duplicate, "Parameter " + this.duplicate.value + " was used already"); |
|
} |
|
} |
|
} |
|
|
|
function parameters(params) { |
|
var used_parameters = new UsedParametersTracker(true, S.input.has_directive("use strict")); |
|
|
|
expect("("); |
|
|
|
while (!is("punc", ")")) { |
|
var param = parameter(used_parameters); |
|
params.push(param); |
|
|
|
if (!is("punc", ")")) { |
|
expect(","); |
|
} |
|
|
|
if (param instanceof AST_Expansion) { |
|
break; |
|
} |
|
} |
|
|
|
next(); |
|
} |
|
|
|
function parameter(used_parameters, symbol_type) { |
|
var param; |
|
var expand = false; |
|
if (used_parameters === undefined) { |
|
used_parameters = new UsedParametersTracker(true, S.input.has_directive("use strict")); |
|
} |
|
if (is("expand", "...")) { |
|
expand = S.token; |
|
used_parameters.mark_spread(S.token); |
|
next(); |
|
} |
|
param = binding_element(used_parameters, symbol_type); |
|
|
|
if (is("operator", "=") && expand === false) { |
|
used_parameters.mark_default_assignment(S.token); |
|
next(); |
|
param = new AST_DefaultAssign({ |
|
start: param.start, |
|
left: param, |
|
operator: "=", |
|
right: expression(false), |
|
end: S.token |
|
}); |
|
} |
|
|
|
if (expand !== false) { |
|
if (!is("punc", ")")) { |
|
unexpected(); |
|
} |
|
param = new AST_Expansion({ |
|
start: expand, |
|
expression: param, |
|
end: expand |
|
}); |
|
} |
|
used_parameters.check_strict(); |
|
|
|
return param; |
|
} |
|
|
|
function binding_element(used_parameters, symbol_type) { |
|
var elements = []; |
|
var first = true; |
|
var is_expand = false; |
|
var expand_token; |
|
var first_token = S.token; |
|
if (used_parameters === undefined) { |
|
const strict = S.input.has_directive("use strict"); |
|
const duplicates_ok = symbol_type === AST_SymbolVar; |
|
used_parameters = new UsedParametersTracker(false, strict, duplicates_ok); |
|
} |
|
symbol_type = symbol_type === undefined ? AST_SymbolFunarg : symbol_type; |
|
if (is("punc", "[")) { |
|
next(); |
|
while (!is("punc", "]")) { |
|
if (first) { |
|
first = false; |
|
} else { |
|
expect(","); |
|
} |
|
|
|
if (is("expand", "...")) { |
|
is_expand = true; |
|
expand_token = S.token; |
|
used_parameters.mark_spread(S.token); |
|
next(); |
|
} |
|
if (is("punc")) { |
|
switch (S.token.value) { |
|
case ",": |
|
elements.push(new AST_Hole({ |
|
start: S.token, |
|
end: S.token |
|
})); |
|
continue; |
|
case "]": // Trailing comma after last element |
|
break; |
|
case "[": |
|
case "{": |
|
elements.push(binding_element(used_parameters, symbol_type)); |
|
break; |
|
default: |
|
unexpected(); |
|
} |
|
} else if (is("name")) { |
|
used_parameters.add_parameter(S.token); |
|
elements.push(as_symbol(symbol_type)); |
|
} else { |
|
croak("Invalid function parameter"); |
|
} |
|
if (is("operator", "=") && is_expand === false) { |
|
used_parameters.mark_default_assignment(S.token); |
|
next(); |
|
elements[elements.length - 1] = new AST_DefaultAssign({ |
|
start: elements[elements.length - 1].start, |
|
left: elements[elements.length - 1], |
|
operator: "=", |
|
right: expression(false), |
|
end: S.token |
|
}); |
|
} |
|
if (is_expand) { |
|
if (!is("punc", "]")) { |
|
croak("Rest element must be last element"); |
|
} |
|
elements[elements.length - 1] = new AST_Expansion({ |
|
start: expand_token, |
|
expression: elements[elements.length - 1], |
|
end: expand_token |
|
}); |
|
} |
|
} |
|
expect("]"); |
|
used_parameters.check_strict(); |
|
return new AST_Destructuring({ |
|
start: first_token, |
|
names: elements, |
|
is_array: true, |
|
end: prev() |
|
}); |
|
} else if (is("punc", "{")) { |
|
next(); |
|
while (!is("punc", "}")) { |
|
if (first) { |
|
first = false; |
|
} else { |
|
expect(","); |
|
} |
|
if (is("expand", "...")) { |
|
is_expand = true; |
|
expand_token = S.token; |
|
used_parameters.mark_spread(S.token); |
|
next(); |
|
} |
|
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].includes(peek().value)) { |
|
used_parameters.add_parameter(S.token); |
|
var start = prev(); |
|
var value = as_symbol(symbol_type); |
|
if (is_expand) { |
|
elements.push(new AST_Expansion({ |
|
start: expand_token, |
|
expression: value, |
|
end: value.end, |
|
})); |
|
} else { |
|
elements.push(new AST_ObjectKeyVal({ |
|
start: start, |
|
key: value.name, |
|
value: value, |
|
end: value.end, |
|
})); |
|
} |
|
} else if (is("punc", "}")) { |
|
continue; // Allow trailing hole |
|
} else { |
|
var property_token = S.token; |
|
var property = as_property_name(); |
|
if (property === null) { |
|
unexpected(prev()); |
|
} else if (prev().type === "name" && !is("punc", ":")) { |
|
elements.push(new AST_ObjectKeyVal({ |
|
start: prev(), |
|
key: property, |
|
value: new symbol_type({ |
|
start: prev(), |
|
name: property, |
|
end: prev() |
|
}), |
|
end: prev() |
|
})); |
|
} else { |
|
expect(":"); |
|
elements.push(new AST_ObjectKeyVal({ |
|
start: property_token, |
|
quote: property_token.quote, |
|
key: property, |
|
value: binding_element(used_parameters, symbol_type), |
|
end: prev() |
|
})); |
|
} |
|
} |
|
if (is_expand) { |
|
if (!is("punc", "}")) { |
|
croak("Rest element must be last element"); |
|
} |
|
} else if (is("operator", "=")) { |
|
used_parameters.mark_default_assignment(S.token); |
|
next(); |
|
elements[elements.length - 1].value = new AST_DefaultAssign({ |
|
start: elements[elements.length - 1].value.start, |
|
left: elements[elements.length - 1].value, |
|
operator: "=", |
|
right: expression(false), |
|
end: S.token |
|
}); |
|
} |
|
} |
|
expect("}"); |
|
used_parameters.check_strict(); |
|
return new AST_Destructuring({ |
|
start: first_token, |
|
names: elements, |
|
is_array: false, |
|
end: prev() |
|
}); |
|
} else if (is("name")) { |
|
used_parameters.add_parameter(S.token); |
|
return as_symbol(symbol_type); |
|
} else { |
|
croak("Invalid function parameter"); |
|
} |
|
} |
|
|
|
function params_or_seq_(allow_arrows, maybe_sequence) { |
|
var spread_token; |
|
var invalid_sequence; |
|
var trailing_comma; |
|
var a = []; |
|
expect("("); |
|
while (!is("punc", ")")) { |
|
if (spread_token) unexpected(spread_token); |
|
if (is("expand", "...")) { |
|
spread_token = S.token; |
|
if (maybe_sequence) invalid_sequence = S.token; |
|
next(); |
|
a.push(new AST_Expansion({ |
|
start: prev(), |
|
expression: expression(), |
|
end: S.token, |
|
})); |
|
} else { |
|
a.push(expression()); |
|
} |
|
if (!is("punc", ")")) { |
|
expect(","); |
|
if (is("punc", ")")) { |
|
trailing_comma = prev(); |
|
if (maybe_sequence) invalid_sequence = trailing_comma; |
|
} |
|
} |
|
} |
|
expect(")"); |
|
if (allow_arrows && is("arrow", "=>")) { |
|
if (spread_token && trailing_comma) unexpected(trailing_comma); |
|
} else if (invalid_sequence) { |
|
unexpected(invalid_sequence); |
|
} |
|
return a; |
|
} |
|
|
|
function _function_body(block, generator, is_async, name, args) { |
|
var loop = S.in_loop; |
|
var labels = S.labels; |
|
var current_generator = S.in_generator; |
|
var current_async = S.in_async; |
|
++S.in_function; |
|
if (generator) |
|
S.in_generator = S.in_function; |
|
if (is_async) |
|
S.in_async = S.in_function; |
|
if (args) parameters(args); |
|
if (block) |
|
S.in_directives = true; |
|
S.in_loop = 0; |
|
S.labels = []; |
|
if (block) { |
|
S.input.push_directives_stack(); |
|
var a = block_(); |
|
if (name) _verify_symbol(name); |
|
if (args) args.forEach(_verify_symbol); |
|
S.input.pop_directives_stack(); |
|
} else { |
|
var a = [new AST_Return({ |
|
start: S.token, |
|
value: expression(false), |
|
end: S.token |
|
})]; |
|
} |
|
--S.in_function; |
|
S.in_loop = loop; |
|
S.labels = labels; |
|
S.in_generator = current_generator; |
|
S.in_async = current_async; |
|
return a; |
|
} |
|
|
|
function _await_expression() { |
|
// Previous token must be "await" and not be interpreted as an identifier |
|
if (!can_await()) { |
|
croak("Unexpected await expression outside async function", |
|
S.prev.line, S.prev.col, S.prev.pos); |
|
} |
|
// the await expression is parsed as a unary expression in Babel |
|
return new AST_Await({ |
|
start: prev(), |
|
end: S.token, |
|
expression : maybe_unary(true), |
|
}); |
|
} |
|
|
|
function _yield_expression() { |
|
var start = S.token; |
|
var star = false; |
|
var has_expression = true; |
|
|
|
// Attempt to get expression or star (and then the mandatory expression) |
|
// behind yield on the same line. |
|
// |
|
// If nothing follows on the same line of the yieldExpression, |
|
// it should default to the value `undefined` for yield to return. |
|
// In that case, the `undefined` stored as `null` in ast. |
|
// |
|
// Note 1: It isn't allowed for yield* to close without an expression |
|
// Note 2: If there is a nlb between yield and star, it is interpret as |
|
// yield <explicit undefined> <inserted automatic semicolon> * |
|
if ( |
|
can_insert_semicolon() |
|
|| is("punc") && PUNC_AFTER_EXPRESSION.has(S.token.value) |
|
|| is("template_cont") |
|
) { |
|
has_expression = false; |
|
} else if (is("operator", "*")) { |
|
star = true; |
|
next(); |
|
} |
|
|
|
return new AST_Yield({ |
|
start : start, |
|
is_star : star, |
|
expression : has_expression ? expression() : null, |
|
end : prev() |
|
}); |
|
} |
|
|
|
function if_() { |
|
var cond = parenthesised(), body = statement(false, false, true), belse = null; |
|
if (is("keyword", "else")) { |
|
next(); |
|
belse = statement(false, false, true); |
|
} |
|
return new AST_If({ |
|
condition : cond, |
|
body : body, |
|
alternative : belse |
|
}); |
|
} |
|
|
|
function block_() { |
|
expect("{"); |
|
var a = []; |
|
while (!is("punc", "}")) { |
|
if (is("eof")) unexpected(); |
|
a.push(statement()); |
|
} |
|
next(); |
|
return a; |
|
} |
|
|
|
function switch_body_() { |
|
expect("{"); |
|
var a = [], cur = null, branch = null, tmp; |
|
while (!is("punc", "}")) { |
|
if (is("eof")) unexpected(); |
|
if (is("keyword", "case")) { |
|
if (branch) branch.end = prev(); |
|
cur = []; |
|
branch = new AST_Case({ |
|
start : (tmp = S.token, next(), tmp), |
|
expression : expression(true), |
|
body : cur |
|
}); |
|
a.push(branch); |
|
expect(":"); |
|
} else if (is("keyword", "default")) { |
|
if (branch) branch.end = prev(); |
|
cur = []; |
|
branch = new AST_Default({ |
|
start : (tmp = S.token, next(), expect(":"), tmp), |
|
body : cur |
|
}); |
|
a.push(branch); |
|
} else { |
|
if (!cur) unexpected(); |
|
cur.push(statement()); |
|
} |
|
} |
|
if (branch) branch.end = prev(); |
|
next(); |
|
return a; |
|
} |
|
|
|
function try_() { |
|
var body, bcatch = null, bfinally = null; |
|
body = new AST_TryBlock({ |
|
start : S.token, |
|
body : block_(), |
|
end : prev(), |
|
}); |
|
if (is("keyword", "catch")) { |
|
var start = S.token; |
|
next(); |
|
if (is("punc", "{")) { |
|
var name = null; |
|
} else { |
|
expect("("); |
|
var name = parameter(undefined, AST_SymbolCatch); |
|
expect(")"); |
|
} |
|
bcatch = new AST_Catch({ |
|
start : start, |
|
argname : name, |
|
body : block_(), |
|
end : prev() |
|
}); |
|
} |
|
if (is("keyword", "finally")) { |
|
var start = S.token; |
|
next(); |
|
bfinally = new AST_Finally({ |
|
start : start, |
|
body : block_(), |
|
end : prev() |
|
}); |
|
} |
|
if (!bcatch && !bfinally) |
|
croak("Missing catch/finally blocks"); |
|
return new AST_Try({ |
|
body : body, |
|
bcatch : bcatch, |
|
bfinally : bfinally |
|
}); |
|
} |
|
|
|
/** |
|
* var |
|
* vardef1 = 2, |
|
* vardef2 = 3; |
|
*/ |
|
function vardefs(no_in, kind) { |
|
var var_defs = []; |
|
var def; |
|
for (;;) { |
|
var sym_type = |
|
kind === "var" ? AST_SymbolVar : |
|
kind === "const" ? AST_SymbolConst : |
|
kind === "let" ? AST_SymbolLet : |
|
kind === "using" ? AST_SymbolUsing : |
|
kind === "await using" ? AST_SymbolUsing : null; |
|
var def_type = kind === "using" || kind === "await using" ? AST_UsingDef : AST_VarDef; |
|
// var { a } = b |
|
if (is("punc", "{") || is("punc", "[")) { |
|
def = new def_type({ |
|
start: S.token, |
|
name: binding_element(undefined, sym_type), |
|
value: is("operator", "=") ? (expect_token("operator", "="), expression(false, no_in)) : null, |
|
end: prev() |
|
}); |
|
} else { |
|
def = new def_type({ |
|
start : S.token, |
|
name : as_symbol(sym_type), |
|
value : is("operator", "=") |
|
? (next(), expression(false, no_in)) |
|
: !no_in && (kind === "const" || kind === "using" || kind === "await using") |
|
? croak("Missing initializer in " + kind + " declaration") : null, |
|
end : prev() |
|
}); |
|
if (def.name.name == "import") croak("Unexpected token: import"); |
|
} |
|
var_defs.push(def); |
|
if (!is("punc", ",")) |
|
break; |
|
next(); |
|
} |
|
return var_defs; |
|
} |
|
|
|
var var_ = function(no_in) { |
|
return new AST_Var({ |
|
start : prev(), |
|
definitions : vardefs(no_in, "var"), |
|
end : prev() |
|
}); |
|
}; |
|
|
|
var let_ = function(no_in) { |
|
return new AST_Let({ |
|
start : prev(), |
|
definitions : vardefs(no_in, "let"), |
|
end : prev() |
|
}); |
|
}; |
|
|
|
var const_ = function(no_in) { |
|
return new AST_Const({ |
|
start : prev(), |
|
definitions : vardefs(no_in, "const"), |
|
end : prev() |
|
}); |
|
}; |
|
|
|
var using_ = function(no_in) { |
|
return new AST_Using({ |
|
start : prev(), |
|
await : false, |
|
definitions : vardefs(no_in, "using"), |
|
end : prev() |
|
}); |
|
}; |
|
|
|
var await_using_ = function(no_in) { |
|
// Assumption: When await_using_ is called, only the `await` token has been consumed. |
|
return new AST_Using({ |
|
start : prev(), |
|
await : true, |
|
definitions : (next(), vardefs(no_in, "await using")), |
|
end : prev() |
|
}); |
|
}; |
|
|
|
var new_ = function(allow_calls) { |
|
var start = S.token; |
|
expect_token("operator", "new"); |
|
if (is("punc", ".")) { |
|
next(); |
|
expect_token("name", "target"); |
|
return subscripts(new AST_NewTarget({ |
|
start : start, |
|
end : prev() |
|
}), allow_calls); |
|
} |
|
var newexp = expr_atom(false), args; |
|
if (is("punc", "(")) { |
|
next(); |
|
args = expr_list(")", true); |
|
} else { |
|
args = []; |
|
} |
|
var call = new AST_New({ |
|
start : start, |
|
expression : newexp, |
|
args : args, |
|
end : prev() |
|
}); |
|
annotate(call); |
|
return subscripts(call, allow_calls); |
|
}; |
|
|
|
function as_atom_node() { |
|
var tok = S.token, ret; |
|
switch (tok.type) { |
|
case "name": |
|
ret = _make_symbol(AST_SymbolRef); |
|
break; |
|
case "num": |
|
if (tok.value === Infinity) { |
|
// very large float values are parsed as Infinity |
|
ret = new AST_Infinity({ |
|
start: tok, |
|
end: tok, |
|
}); |
|
} else { |
|
ret = new AST_Number({ |
|
start: tok, |
|
end: tok, |
|
value: tok.value, |
|
raw: LATEST_RAW |
|
}); |
|
} |
|
break; |
|
case "big_int": |
|
ret = new AST_BigInt({ |
|
start: tok, |
|
end: tok, |
|
value: tok.value, |
|
raw: LATEST_RAW, |
|
}); |
|
break; |
|
case "string": |
|
ret = new AST_String({ |
|
start : tok, |
|
end : tok, |
|
value : tok.value, |
|
quote : tok.quote |
|
}); |
|
annotate(ret); |
|
break; |
|
case "regexp": |
|
const [_, source, flags] = tok.value.match(/^\/(.*)\/(\w*)$/); |
|
|
|
ret = new AST_RegExp({ start: tok, end: tok, value: { source, flags } }); |
|
break; |
|
case "atom": |
|
switch (tok.value) { |
|
case "false": |
|
ret = new AST_False({ start: tok, end: tok }); |
|
break; |
|
case "true": |
|
ret = new AST_True({ start: tok, end: tok }); |
|
break; |
|
case "null": |
|
ret = new AST_Null({ start: tok, end: tok }); |
|
break; |
|
} |
|
break; |
|
} |
|
next(); |
|
return ret; |
|
} |
|
|
|
function to_fun_args(ex, default_seen_above) { |
|
var insert_default = function(ex, default_value) { |
|
if (default_value) { |
|
return new AST_DefaultAssign({ |
|
start: ex.start, |
|
left: ex, |
|
operator: "=", |
|
right: default_value, |
|
end: default_value.end |
|
}); |
|
} |
|
return ex; |
|
}; |
|
if (ex instanceof AST_Object) { |
|
return insert_default(new AST_Destructuring({ |
|
start: ex.start, |
|
end: ex.end, |
|
is_array: false, |
|
names: ex.properties.map(prop => to_fun_args(prop)) |
|
}), default_seen_above); |
|
} else if (ex instanceof AST_ObjectKeyVal) { |
|
ex.value = to_fun_args(ex.value); |
|
return insert_default(ex, default_seen_above); |
|
} else if (ex instanceof AST_Hole) { |
|
return ex; |
|
} else if (ex instanceof AST_Destructuring) { |
|
ex.names = ex.names.map(name => to_fun_args(name)); |
|
return insert_default(ex, default_seen_above); |
|
} else if (ex instanceof AST_SymbolRef) { |
|
return insert_default(new AST_SymbolFunarg({ |
|
name: ex.name, |
|
start: ex.start, |
|
end: ex.end |
|
}), default_seen_above); |
|
} else if (ex instanceof AST_Expansion) { |
|
ex.expression = to_fun_args(ex.expression); |
|
return insert_default(ex, default_seen_above); |
|
} else if (ex instanceof AST_Array) { |
|
return insert_default(new AST_Destructuring({ |
|
start: ex.start, |
|
end: ex.end, |
|
is_array: true, |
|
names: ex.elements.map(elm => to_fun_args(elm)) |
|
}), default_seen_above); |
|
} else if (ex instanceof AST_Assign) { |
|
return insert_default(to_fun_args(ex.left, ex.right), default_seen_above); |
|
} else if (ex instanceof AST_DefaultAssign) { |
|
ex.left = to_fun_args(ex.left); |
|
return ex; |
|
} else { |
|
croak("Invalid function parameter", ex.start.line, ex.start.col); |
|
} |
|
} |
|
|
|
var expr_atom = function(allow_calls, allow_arrows) { |
|
if (is("operator", "new")) { |
|
return new_(allow_calls); |
|
} |
|
if (is("name", "import") && is_token(peek(), "punc", ".")) { |
|
return import_meta(allow_calls); |
|
} |
|
var start = S.token; |
|
var peeked; |
|
var async = is("name", "async") |
|
&& (peeked = peek()).value != "[" |
|
&& peeked.type != "arrow" |
|
&& as_atom_node(); |
|
if (is("punc")) { |
|
switch (S.token.value) { |
|
case "(": |
|
if (async && !allow_calls) break; |
|
var exprs = params_or_seq_(allow_arrows, !async); |
|
if (allow_arrows && is("arrow", "=>")) { |
|
return arrow_function(start, exprs.map(e => to_fun_args(e)), !!async); |
|
} |
|
var ex = async ? new AST_Call({ |
|
expression: async, |
|
args: exprs |
|
}) : to_expr_or_sequence(start, exprs); |
|
if (ex.start) { |
|
const outer_comments_before = start.comments_before.length; |
|
outer_comments_before_counts.set(start, outer_comments_before); |
|
ex.start.comments_before.unshift(...start.comments_before); |
|
start.comments_before = ex.start.comments_before; |
|
if (outer_comments_before == 0 && start.comments_before.length > 0) { |
|
var comment = start.comments_before[0]; |
|
if (!comment.nlb) { |
|
comment.nlb = start.nlb; |
|
start.nlb = false; |
|
} |
|
} |
|
start.comments_after = ex.start.comments_after; |
|
} |
|
ex.start = start; |
|
var end = prev(); |
|
if (ex.end) { |
|
end.comments_before = ex.end.comments_before; |
|
ex.end.comments_after.push(...end.comments_after); |
|
end.comments_after = ex.end.comments_after; |
|
} |
|
ex.end = end; |
|
if (ex instanceof AST_Call) annotate(ex); |
|
return subscripts(ex, allow_calls); |
|
case "[": |
|
return subscripts(array_(), allow_calls); |
|
case "{": |
|
return subscripts(object_or_destructuring_(), allow_calls); |
|
} |
|
if (!async) unexpected(); |
|
} |
|
if (allow_arrows && is("name") && is_token(peek(), "arrow")) { |
|
var param = new AST_SymbolFunarg({ |
|
name: S.token.value, |
|
start: start, |
|
end: start, |
|
}); |
|
next(); |
|
return arrow_function(start, [param], !!async); |
|
} |
|
if (is("keyword", "function")) { |
|
next(); |
|
var func = function_(AST_Function, false, !!async); |
|
func.start = start; |
|
func.end = prev(); |
|
return subscripts(func, allow_calls); |
|
} |
|
if (async) return subscripts(async, allow_calls); |
|
if (is("keyword", "class")) { |
|
next(); |
|
var cls = class_(AST_ClassExpression); |
|
cls.start = start; |
|
cls.end = prev(); |
|
return subscripts(cls, allow_calls); |
|
} |
|
if (is("template_head")) { |
|
return subscripts(template_string(), allow_calls); |
|
} |
|
if (ATOMIC_START_TOKEN.has(S.token.type)) { |
|
return subscripts(as_atom_node(), allow_calls); |
|
} |
|
unexpected(); |
|
}; |
|
|
|
function template_string() { |
|
var segments = [], start = S.token; |
|
|
|
segments.push(new AST_TemplateSegment({ |
|
start: S.token, |
|
raw: TEMPLATE_RAWS.get(S.token), |
|
value: S.token.value, |
|
end: S.token |
|
})); |
|
|
|
while (!S.token.template_end) { |
|
next(); |
|
handle_regexp(); |
|
segments.push(expression(true)); |
|
|
|
segments.push(new AST_TemplateSegment({ |
|
start: S.token, |
|
raw: TEMPLATE_RAWS.get(S.token), |
|
value: S.token.value, |
|
end: S.token |
|
})); |
|
} |
|
next(); |
|
|
|
return new AST_TemplateString({ |
|
start: start, |
|
segments: segments, |
|
end: S.token |
|
}); |
|
} |
|
|
|
function expr_list(closing, allow_trailing_comma, allow_empty) { |
|
var first = true, a = []; |
|
while (!is("punc", closing)) { |
|
if (first) first = false; else expect(","); |
|
if (allow_trailing_comma && is("punc", closing)) break; |
|
if (is("punc", ",") && allow_empty) { |
|
a.push(new AST_Hole({ start: S.token, end: S.token })); |
|
} else if (is("expand", "...")) { |
|
next(); |
|
a.push(new AST_Expansion({start: prev(), expression: expression(),end: S.token})); |
|
} else { |
|
a.push(expression(false)); |
|
} |
|
} |
|
next(); |
|
return a; |
|
} |
|
|
|
var array_ = embed_tokens(function() { |
|
expect("["); |
|
return new AST_Array({ |
|
elements: expr_list("]", !options.strict, true) |
|
}); |
|
}); |
|
|
|
var create_accessor = embed_tokens((is_generator, is_async) => { |
|
return function_(AST_Accessor, is_generator, is_async); |
|
}); |
|
|
|
var object_or_destructuring_ = embed_tokens(function object_or_destructuring_() { |
|
var start = S.token, first = true, a = []; |
|
expect("{"); |
|
while (!is("punc", "}")) { |
|
if (first) first = false; else expect(","); |
|
if (!options.strict && is("punc", "}")) |
|
// allow trailing comma |
|
break; |
|
|
|
start = S.token; |
|
if (start.type == "expand") { |
|
next(); |
|
a.push(new AST_Expansion({ |
|
start: start, |
|
expression: expression(false), |
|
end: prev(), |
|
})); |
|
continue; |
|
} |
|
if(is("privatename")) { |
|
croak("private fields are not allowed in an object"); |
|
} |
|
var name = as_property_name(); |
|
var value; |
|
|
|
// Check property and fetch value |
|
if (!is("punc", ":")) { |
|
var concise = object_or_class_property(name, start); |
|
if (concise) { |
|
a.push(concise); |
|
continue; |
|
} |
|
|
|
value = new AST_SymbolRef({ |
|
start: prev(), |
|
name: name, |
|
end: prev() |
|
}); |
|
} else if (name === null) { |
|
unexpected(prev()); |
|
} else { |
|
next(); // `:` - see first condition |
|
value = expression(false); |
|
} |
|
|
|
// Check for default value and alter value accordingly if necessary |
|
if (is("operator", "=")) { |
|
next(); |
|
value = new AST_Assign({ |
|
start: start, |
|
left: value, |
|
operator: "=", |
|
right: expression(false), |
|
logical: false, |
|
end: prev() |
|
}); |
|
} |
|
|
|
// Create property |
|
const kv = new AST_ObjectKeyVal({ |
|
start: start, |
|
quote: start.quote, |
|
key: name, |
|
value: value, |
|
end: prev() |
|
}); |
|
a.push(annotate(kv)); |
|
} |
|
next(); |
|
return new AST_Object({ properties: a }); |
|
}); |
|
|
|
function class_(KindOfClass, is_export_default) { |
|
var start, method, class_name, extends_, properties = []; |
|
|
|
S.input.push_directives_stack(); // Push directive stack, but not scope stack |
|
S.input.add_directive("use strict"); |
|
|
|
if (S.token.type == "name" && S.token.value != "extends") { |
|
class_name = as_symbol(KindOfClass === AST_DefClass ? AST_SymbolDefClass : AST_SymbolClass); |
|
} |
|
|
|
if (KindOfClass === AST_DefClass && !class_name) { |
|
if (is_export_default) { |
|
KindOfClass = AST_ClassExpression; |
|
} else { |
|
unexpected(); |
|
} |
|
} |
|
|
|
if (S.token.value == "extends") { |
|
next(); |
|
extends_ = expression(true); |
|
} |
|
|
|
expect("{"); |
|
// mark in class feild, |
|
const save_in_class = S.in_class; |
|
S.in_class = true; |
|
while (is("punc", ";")) { next(); } // Leading semicolons are okay in class bodies. |
|
while (!is("punc", "}")) { |
|
start = S.token; |
|
method = object_or_class_property(as_property_name(), start, true); |
|
if (!method) { unexpected(); } |
|
properties.push(method); |
|
while (is("punc", ";")) { next(); } |
|
} |
|
// mark in class feild, |
|
S.in_class = save_in_class; |
|
|
|
S.input.pop_directives_stack(); |
|
|
|
next(); |
|
|
|
return new KindOfClass({ |
|
start: start, |
|
name: class_name, |
|
extends: extends_, |
|
properties: properties, |
|
end: prev(), |
|
}); |
|
} |
|
|
|
function object_or_class_property(name, start, is_class) { |
|
const get_symbol_ast = (name, SymbolClass) => { |
|
if (typeof name === "string") { |
|
return new SymbolClass({ start, name, end: prev() }); |
|
} else if (name === null) { |
|
unexpected(); |
|
} |
|
return name; |
|
}; |
|
|
|
var is_private = prev().type === "privatename"; |
|
const is_not_method_start = () => |
|
!is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("punc", ";") && !is("operator", "=") && !is_private; |
|
|
|
var is_async = false; |
|
var is_static = false; |
|
var is_generator = false; |
|
var accessor_type = null; |
|
|
|
if (is_class && name === "static" && is_not_method_start()) { |
|
const static_block = class_static_block(); |
|
if (static_block != null) { |
|
return static_block; |
|
} |
|
is_static = true; |
|
name = as_property_name(); |
|
} |
|
if (name === "async" && is_not_method_start()) { |
|
is_async = true; |
|
name = as_property_name(); |
|
} |
|
if (prev().type === "operator" && prev().value === "*") { |
|
is_generator = true; |
|
name = as_property_name(); |
|
} |
|
if ((name === "get" || name === "set") && is_not_method_start()) { |
|
accessor_type = name; |
|
name = as_property_name(); |
|
} |
|
if (!is_private && prev().type === "privatename") { |
|
is_private = true; |
|
} |
|
|
|
const property_token = prev(); |
|
|
|
if (accessor_type != null) { |
|
if (!is_private) { |
|
const AccessorClass = accessor_type === "get" |
|
? AST_ObjectGetter |
|
: AST_ObjectSetter; |
|
|
|
name = get_symbol_ast(name, AST_SymbolMethod); |
|
return annotate(new AccessorClass({ |
|
start, |
|
static: is_static, |
|
key: name, |
|
quote: name instanceof AST_SymbolMethod ? property_token.quote : undefined, |
|
value: create_accessor(), |
|
end: prev() |
|
})); |
|
} else { |
|
const AccessorClass = accessor_type === "get" |
|
? AST_PrivateGetter |
|
: AST_PrivateSetter; |
|
|
|
return annotate(new AccessorClass({ |
|
start, |
|
static: is_static, |
|
key: get_symbol_ast(name, AST_SymbolMethod), |
|
value: create_accessor(), |
|
end: prev(), |
|
})); |
|
} |
|
} |
|
|
|
if (is("punc", "(")) { |
|
name = get_symbol_ast(name, AST_SymbolMethod); |
|
const AST_MethodVariant = is_private |
|
? AST_PrivateMethod |
|
: AST_ConciseMethod; |
|
var node = new AST_MethodVariant({ |
|
start : start, |
|
static : is_static, |
|
key : name, |
|
quote : name instanceof AST_SymbolMethod ? |
|
property_token.quote : undefined, |
|
value : create_accessor(is_generator, is_async), |
|
end : prev() |
|
}); |
|
return annotate(node); |
|
} |
|
|
|
if (is_class) { |
|
const AST_SymbolVariant = is_private |
|
? AST_SymbolPrivateProperty |
|
: AST_SymbolClassProperty; |
|
const AST_ClassPropertyVariant = is_private |
|
? AST_ClassPrivateProperty |
|
: AST_ClassProperty; |
|
|
|
const key = get_symbol_ast(name, AST_SymbolVariant); |
|
const quote = key instanceof AST_SymbolClassProperty |
|
? property_token.quote |
|
: undefined; |
|
if (is("operator", "=")) { |
|
next(); |
|
return annotate( |
|
new AST_ClassPropertyVariant({ |
|
start, |
|
static: is_static, |
|
quote, |
|
key, |
|
value: expression(false), |
|
end: prev() |
|
}) |
|
); |
|
} else if ( |
|
is("name") |
|
|| is("privatename") |
|
|| is("punc", "[") |
|
|| is("operator", "*") |
|
|| is("punc", ";") |
|
|| is("punc", "}") |
|
|| is("string") |
|
|| is("num") |
|
|| is("big_int") |
|
) { |
|
return annotate( |
|
new AST_ClassPropertyVariant({ |
|
start, |
|
static: is_static, |
|
quote, |
|
key, |
|
end: prev() |
|
}) |
|
); |
|
} |
|
} |
|
} |
|
|
|
function class_static_block() { |
|
if (!is("punc", "{")) { |
|
return null; |
|
} |
|
|
|
const start = S.token; |
|
const body = []; |
|
|
|
next(); |
|
|
|
while (!is("punc", "}")) { |
|
body.push(statement()); |
|
} |
|
|
|
next(); |
|
|
|
return new AST_ClassStaticBlock({ start, body, end: prev() }); |
|
} |
|
|
|
function maybe_import_attributes() { |
|
if ( |
|
(is("keyword", "with") || is("name", "assert")) |
|
&& !has_newline_before(S.token) |
|
) { |
|
next(); |
|
return object_or_destructuring_(); |
|
} |
|
return null; |
|
} |
|
|
|
function import_statement() { |
|
var start = prev(); |
|
|
|
var imported_name; |
|
var imported_names; |
|
if (is("name")) { |
|
imported_name = as_symbol(AST_SymbolImport); |
|
} |
|
|
|
if (is("punc", ",")) { |
|
next(); |
|
} |
|
|
|
imported_names = map_names(true); |
|
|
|
if (imported_names || imported_name) { |
|
expect_token("name", "from"); |
|
} |
|
var mod_str = S.token; |
|
if (mod_str.type !== "string") { |
|
unexpected(); |
|
} |
|
next(); |
|
|
|
const attributes = maybe_import_attributes(); |
|
|
|
return new AST_Import({ |
|
start, |
|
imported_name, |
|
imported_names, |
|
module_name: new AST_String({ |
|
start: mod_str, |
|
value: mod_str.value, |
|
quote: mod_str.quote, |
|
end: mod_str, |
|
}), |
|
attributes, |
|
end: S.token, |
|
}); |
|
} |
|
|
|
function import_meta(allow_calls) { |
|
var start = S.token; |
|
expect_token("name", "import"); |
|
expect_token("punc", "."); |
|
expect_token("name", "meta"); |
|
return subscripts(new AST_ImportMeta({ |
|
start: start, |
|
end: prev() |
|
}), allow_calls); |
|
} |
|
|
|
function map_name(is_import) { |
|
function make_symbol(type, quote) { |
|
return new type({ |
|
name: as_property_name(), |
|
quote: quote || undefined, |
|
start: prev(), |
|
end: prev() |
|
}); |
|
} |
|
|
|
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign; |
|
var type = is_import ? AST_SymbolImport : AST_SymbolExport; |
|
var start = S.token; |
|
var foreign_name; |
|
var name; |
|
|
|
if (is_import) { |
|
foreign_name = make_symbol(foreign_type, start.quote); |
|
} else { |
|
name = make_symbol(type, start.quote); |
|
} |
|
if (is("name", "as")) { |
|
next(); // The "as" word |
|
if (is_import) { |
|
name = make_symbol(type); |
|
} else { |
|
foreign_name = make_symbol(foreign_type, S.token.quote); |
|
} |
|
} else { |
|
if (is_import) { |
|
name = new type(foreign_name); |
|
} else { |
|
foreign_name = new foreign_type(name); |
|
} |
|
} |
|
|
|
return new AST_NameMapping({ |
|
start: start, |
|
foreign_name: foreign_name, |
|
name: name, |
|
end: prev(), |
|
}); |
|
} |
|
|
|
function map_nameAsterisk(is_import, import_or_export_foreign_name) { |
|
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign; |
|
var type = is_import ? AST_SymbolImport : AST_SymbolExport; |
|
var start = S.token; |
|
var name, foreign_name; |
|
var end = prev(); |
|
|
|
if (is_import) { |
|
name = import_or_export_foreign_name; |
|
} else { |
|
foreign_name = import_or_export_foreign_name; |
|
} |
|
|
|
name = name || new type({ |
|
start: start, |
|
name: "*", |
|
end: end, |
|
}); |
|
|
|
foreign_name = foreign_name || new foreign_type({ |
|
start: start, |
|
name: "*", |
|
end: end, |
|
}); |
|
|
|
return new AST_NameMapping({ |
|
start: start, |
|
foreign_name: foreign_name, |
|
name: name, |
|
end: end, |
|
}); |
|
} |
|
|
|
function map_names(is_import) { |
|
var names; |
|
if (is("punc", "{")) { |
|
next(); |
|
names = []; |
|
while (!is("punc", "}")) { |
|
names.push(map_name(is_import)); |
|
if (is("punc", ",")) { |
|
next(); |
|
} |
|
} |
|
next(); |
|
} else if (is("operator", "*")) { |
|
var name; |
|
next(); |
|
if (is("name", "as")) { |
|
next(); // The "as" word |
|
name = is_import ? as_symbol(AST_SymbolImport) : as_symbol_or_string(AST_SymbolExportForeign); |
|
} |
|
names = [map_nameAsterisk(is_import, name)]; |
|
} |
|
return names; |
|
} |
|
|
|
function export_statement() { |
|
var start = S.token; |
|
var is_default; |
|
var exported_names; |
|
|
|
if (is("keyword", "default")) { |
|
is_default = true; |
|
next(); |
|
} else if (exported_names = map_names(false)) { |
|
if (is("name", "from")) { |
|
next(); |
|
|
|
var mod_str = S.token; |
|
if (mod_str.type !== "string") { |
|
unexpected(); |
|
} |
|
next(); |
|
|
|
const attributes = maybe_import_attributes(); |
|
|
|
return new AST_Export({ |
|
start: start, |
|
is_default: is_default, |
|
exported_names: exported_names, |
|
module_name: new AST_String({ |
|
start: mod_str, |
|
value: mod_str.value, |
|
quote: mod_str.quote, |
|
end: mod_str, |
|
}), |
|
end: prev(), |
|
attributes |
|
}); |
|
} else { |
|
return new AST_Export({ |
|
start: start, |
|
is_default: is_default, |
|
exported_names: exported_names, |
|
end: prev(), |
|
}); |
|
} |
|
} |
|
|
|
var node; |
|
var exported_value; |
|
var exported_definition; |
|
if (is("punc", "{") |
|
|| is_default |
|
&& (is("keyword", "class") || is("keyword", "function")) |
|
&& is_token(peek(), "punc")) { |
|
exported_value = expression(false); |
|
semicolon(); |
|
} else if ((node = statement(is_default)) instanceof AST_Definitions && is_default) { |
|
unexpected(node.start); |
|
} else if ( |
|
node instanceof AST_Definitions |
|
|| node instanceof AST_Defun |
|
|| node instanceof AST_DefClass |
|
) { |
|
exported_definition = node; |
|
} else if ( |
|
node instanceof AST_ClassExpression |
|
|| node instanceof AST_Function |
|
) { |
|
exported_value = node; |
|
} else if (node instanceof AST_SimpleStatement) { |
|
exported_value = node.body; |
|
} else { |
|
unexpected(node.start); |
|
} |
|
|
|
return new AST_Export({ |
|
start: start, |
|
is_default: is_default, |
|
exported_value: exported_value, |
|
exported_definition: exported_definition, |
|
end: prev(), |
|
attributes: null |
|
}); |
|
} |
|
|
|
function as_property_name() { |
|
var tmp = S.token; |
|
switch (tmp.type) { |
|
case "punc": |
|
if (tmp.value === "[") { |
|
next(); |
|
var ex = expression(false); |
|
expect("]"); |
|
return ex; |
|
} else unexpected(tmp); |
|
case "operator": |
|
if (tmp.value === "*") { |
|
next(); |
|
return null; |
|
} |
|
if (!["delete", "in", "instanceof", "new", "typeof", "void"].includes(tmp.value)) { |
|
unexpected(tmp); |
|
} |
|
/* falls through */ |
|
case "name": |
|
case "privatename": |
|
case "string": |
|
case "keyword": |
|
case "atom": |
|
next(); |
|
return tmp.value; |
|
case "num": |
|
case "big_int": |
|
next(); |
|
return "" + tmp.value; |
|
default: |
|
unexpected(tmp); |
|
} |
|
} |
|
|
|
function as_name() { |
|
var tmp = S.token; |
|
if (tmp.type != "name" && tmp.type != "privatename") unexpected(); |
|
next(); |
|
return tmp.value; |
|
} |
|
|
|
function _make_symbol(type) { |
|
var name = S.token.value; |
|
return new (name == "this" ? AST_This : |
|
name == "super" ? AST_Super : |
|
type)({ |
|
name : String(name), |
|
start : S.token, |
|
end : S.token |
|
}); |
|
} |
|
|
|
function _verify_symbol(sym) { |
|
var name = sym.name; |
|
if (is_in_generator() && name == "yield") { |
|
token_error(sym.start, "Yield cannot be used as identifier inside generators"); |
|
} |
|
if (S.input.has_directive("use strict")) { |
|
if (name == "yield") { |
|
token_error(sym.start, "Unexpected yield identifier inside strict mode"); |
|
} |
|
if (sym instanceof AST_SymbolDeclaration && (name == "arguments" || name == "eval")) { |
|
token_error(sym.start, "Unexpected " + name + " in strict mode"); |
|
} |
|
} |
|
} |
|
|
|
function as_symbol(type, noerror) { |
|
if (!is("name")) { |
|
if (!noerror) croak("Name expected"); |
|
return null; |
|
} |
|
var sym = _make_symbol(type); |
|
_verify_symbol(sym); |
|
next(); |
|
return sym; |
|
} |
|
|
|
function as_symbol_or_string(type) { |
|
if (!is("name")) { |
|
if (!is("string")) { |
|
croak("Name or string expected"); |
|
} |
|
var tok = S.token; |
|
var ret = new type({ |
|
start : tok, |
|
end : tok, |
|
name : tok.value, |
|
quote : tok.quote |
|
}); |
|
next(); |
|
return ret; |
|
} |
|
var sym = _make_symbol(type); |
|
_verify_symbol(sym); |
|
next(); |
|
return sym; |
|
} |
|
|
|
// Annotate AST_Call, AST_Lambda or AST_New with the special comments |
|
function annotate(node, before_token = node.start) { |
|
var comments = before_token.comments_before; |
|
const comments_outside_parens = outer_comments_before_counts.get(before_token); |
|
var i = comments_outside_parens != null ? comments_outside_parens : comments.length; |
|
while (--i >= 0) { |
|
var comment = comments[i]; |
|
if (/[@#]__/.test(comment.value)) { |
|
if (/[@#]__PURE__/.test(comment.value)) { |
|
set_annotation(node, _PURE); |
|
break; |
|
} |
|
if (/[@#]__INLINE__/.test(comment.value)) { |
|
set_annotation(node, _INLINE); |
|
break; |
|
} |
|
if (/[@#]__NOINLINE__/.test(comment.value)) { |
|
set_annotation(node, _NOINLINE); |
|
break; |
|
} |
|
if (/[@#]__KEY__/.test(comment.value)) { |
|
set_annotation(node, _KEY); |
|
break; |
|
} |
|
if (/[@#]__MANGLE_PROP__/.test(comment.value)) { |
|
set_annotation(node, _MANGLEPROP); |
|
break; |
|
} |
|
} |
|
} |
|
return node; |
|
} |
|
|
|
var subscripts = function(expr, allow_calls, is_chain) { |
|
var start = expr.start; |
|
if (is("punc", ".")) { |
|
next(); |
|
if(is("privatename") && !S.in_class) |
|
croak("Private field must be used in an enclosing class"); |
|
const AST_DotVariant = is("privatename") ? AST_DotHash : AST_Dot; |
|
return annotate(subscripts(new AST_DotVariant({ |
|
start : start, |
|
expression : expr, |
|
optional : false, |
|
property : as_name(), |
|
end : prev() |
|
}), allow_calls, is_chain)); |
|
} |
|
if (is("punc", "[")) { |
|
next(); |
|
var prop = expression(true); |
|
expect("]"); |
|
return annotate(subscripts(new AST_Sub({ |
|
start : start, |
|
expression : expr, |
|
optional : false, |
|
property : prop, |
|
end : prev() |
|
}), allow_calls, is_chain)); |
|
} |
|
if (allow_calls && is("punc", "(")) { |
|
next(); |
|
var call = new AST_Call({ |
|
start : start, |
|
expression : expr, |
|
optional : false, |
|
args : call_args(), |
|
end : prev() |
|
}); |
|
annotate(call); |
|
return subscripts(call, true, is_chain); |
|
} |
|
|
|
// Optional chain |
|
if (is("punc", "?.")) { |
|
next(); |
|
|
|
let chain_contents; |
|
|
|
if (allow_calls && is("punc", "(")) { |
|
next(); |
|
|
|
const call = new AST_Call({ |
|
start, |
|
optional: true, |
|
expression: expr, |
|
args: call_args(), |
|
end: prev() |
|
}); |
|
annotate(call); |
|
|
|
chain_contents = subscripts(call, true, true); |
|
} else if (is("name") || is("privatename")) { |
|
if(is("privatename") && !S.in_class) |
|
croak("Private field must be used in an enclosing class"); |
|
const AST_DotVariant = is("privatename") ? AST_DotHash : AST_Dot; |
|
chain_contents = annotate(subscripts(new AST_DotVariant({ |
|
start, |
|
expression: expr, |
|
optional: true, |
|
property: as_name(), |
|
end: prev() |
|
}), allow_calls, true)); |
|
} else if (is("punc", "[")) { |
|
next(); |
|
const property = expression(true); |
|
expect("]"); |
|
chain_contents = annotate(subscripts(new AST_Sub({ |
|
start, |
|
expression: expr, |
|
optional: true, |
|
property, |
|
end: prev() |
|
}), allow_calls, true)); |
|
} |
|
|
|
if (!chain_contents) unexpected(); |
|
|
|
if (chain_contents instanceof AST_Chain) return chain_contents; |
|
|
|
return new AST_Chain({ |
|
start, |
|
expression: chain_contents, |
|
end: prev() |
|
}); |
|
} |
|
|
|
if (is("template_head")) { |
|
if (is_chain) { |
|
// a?.b`c` is a syntax error |
|
unexpected(); |
|
} |
|
|
|
return subscripts(new AST_PrefixedTemplateString({ |
|
start: start, |
|
prefix: expr, |
|
template_string: template_string(), |
|
end: prev() |
|
}), allow_calls); |
|
} |
|
return expr; |
|
}; |
|
|
|
function call_args() { |
|
var args = []; |
|
while (!is("punc", ")")) { |
|
if (is("expand", "...")) { |
|
next(); |
|
args.push(new AST_Expansion({ |
|
start: prev(), |
|
expression: expression(false), |
|
end: prev() |
|
})); |
|
} else { |
|
args.push(expression(false)); |
|
} |
|
if (!is("punc", ")")) { |
|
expect(","); |
|
} |
|
} |
|
next(); |
|
return args; |
|
} |
|
|
|
var maybe_unary = function(allow_calls, allow_arrows) { |
|
var start = S.token; |
|
if (start.type == "name" && start.value == "await" && can_await()) { |
|
next(); |
|
return _await_expression(); |
|
} |
|
if (is("operator") && UNARY_PREFIX.has(start.value)) { |
|
next(); |
|
handle_regexp(); |
|
var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls)); |
|
ex.start = start; |
|
ex.end = prev(); |
|
return ex; |
|
} |
|
var val = expr_atom(allow_calls, allow_arrows); |
|
while (is("operator") && UNARY_POSTFIX.has(S.token.value) && !has_newline_before(S.token)) { |
|
if (val instanceof AST_Arrow) unexpected(); |
|
val = make_unary(AST_UnaryPostfix, S.token, val); |
|
val.start = start; |
|
val.end = S.token; |
|
next(); |
|
} |
|
return val; |
|
}; |
|
|
|
function make_unary(ctor, token, expr) { |
|
var op = token.value; |
|
switch (op) { |
|
case "++": |
|
case "--": |
|
if (!is_assignable(expr)) |
|
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos); |
|
break; |
|
case "delete": |
|
if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict")) |
|
croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos); |
|
break; |
|
} |
|
return new ctor({ operator: op, expression: expr }); |
|
} |
|
|
|
var expr_op = function(left, min_prec, no_in) { |
|
var op = is("operator") ? S.token.value : null; |
|
if (op == "in" && no_in) op = null; |
|
if (op == "**" && left instanceof AST_UnaryPrefix |
|
/* unary token in front not allowed - parenthesis required */ |
|
&& !is_token(left.start, "punc", "(") |
|
&& left.operator !== "--" && left.operator !== "++") |
|
unexpected(left.start); |
|
var prec = op != null ? PRECEDENCE[op] : null; |
|
if (prec != null && (prec > min_prec || (op === "**" && min_prec === prec))) { |
|
next(); |
|
var right = expr_ops(no_in, prec, true); |
|
return expr_op(new AST_Binary({ |
|
start : left.start, |
|
left : left, |
|
operator : op, |
|
right : right, |
|
end : right.end |
|
}), min_prec, no_in); |
|
} |
|
return left; |
|
}; |
|
|
|
function expr_ops(no_in, min_prec, allow_calls, allow_arrows) { |
|
// maybe_unary won't return us a AST_SymbolPrivateProperty |
|
if (!no_in && min_prec < PRECEDENCE["in"] && is("privatename")) { |
|
if(!S.in_class) { |
|
croak("Private field must be used in an enclosing class"); |
|
} |
|
|
|
const start = S.token; |
|
const key = new AST_SymbolPrivateProperty({ |
|
start, |
|
name: start.value, |
|
end: start |
|
}); |
|
next(); |
|
expect_token("operator", "in"); |
|
|
|
const private_in = new AST_PrivateIn({ |
|
start, |
|
key, |
|
value: expr_ops(no_in, PRECEDENCE["in"], true), |
|
end: prev() |
|
}); |
|
|
|
return expr_op(private_in, 0, no_in); |
|
} else { |
|
return expr_op(maybe_unary(allow_calls, allow_arrows), min_prec, no_in); |
|
} |
|
} |
|
|
|
var maybe_conditional = function(no_in) { |
|
var start = S.token; |
|
var expr = expr_ops(no_in, 0, true, true); |
|
if (is("operator", "?")) { |
|
next(); |
|
var yes = expression(false); |
|
expect(":"); |
|
return new AST_Conditional({ |
|
start : start, |
|
condition : expr, |
|
consequent : yes, |
|
alternative : expression(false, no_in), |
|
end : prev() |
|
}); |
|
} |
|
return expr; |
|
}; |
|
|
|
function is_assignable(expr) { |
|
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef; |
|
} |
|
|
|
function to_destructuring(node) { |
|
if (node instanceof AST_Object) { |
|
node = new AST_Destructuring({ |
|
start: node.start, |
|
names: node.properties.map(to_destructuring), |
|
is_array: false, |
|
end: node.end |
|
}); |
|
} else if (node instanceof AST_Array) { |
|
var names = []; |
|
|
|
for (var i = 0; i < node.elements.length; i++) { |
|
// Only allow expansion as last element |
|
if (node.elements[i] instanceof AST_Expansion) { |
|
if (i + 1 !== node.elements.length) { |
|
token_error(node.elements[i].start, "Spread must the be last element in destructuring array"); |
|
} |
|
node.elements[i].expression = to_destructuring(node.elements[i].expression); |
|
} |
|
|
|
names.push(to_destructuring(node.elements[i])); |
|
} |
|
|
|
node = new AST_Destructuring({ |
|
start: node.start, |
|
names: names, |
|
is_array: true, |
|
end: node.end |
|
}); |
|
} else if (node instanceof AST_ObjectProperty) { |
|
node.value = to_destructuring(node.value); |
|
} else if (node instanceof AST_Assign) { |
|
node = new AST_DefaultAssign({ |
|
start: node.start, |
|
left: node.left, |
|
operator: "=", |
|
right: node.right, |
|
end: node.end |
|
}); |
|
} |
|
return node; |
|
} |
|
|
|
// In ES6, AssignmentExpression can also be an ArrowFunction |
|
var maybe_assign = function(no_in) { |
|
handle_regexp(); |
|
var start = S.token; |
|
|
|
if (start.type == "name" && start.value == "yield") { |
|
if (is_in_generator()) { |
|
next(); |
|
return _yield_expression(); |
|
} else if (S.input.has_directive("use strict")) { |
|
token_error(S.token, "Unexpected yield identifier inside strict mode"); |
|
} |
|
} |
|
|
|
var left = maybe_conditional(no_in); |
|
var val = S.token.value; |
|
|
|
if (is("operator") && ASSIGNMENT.has(val)) { |
|
if (is_assignable(left) || (left = to_destructuring(left)) instanceof AST_Destructuring) { |
|
next(); |
|
|
|
return new AST_Assign({ |
|
start : start, |
|
left : left, |
|
operator : val, |
|
right : maybe_assign(no_in), |
|
logical : LOGICAL_ASSIGNMENT.has(val), |
|
end : prev() |
|
}); |
|
} |
|
croak("Invalid assignment"); |
|
} |
|
return left; |
|
}; |
|
|
|
var to_expr_or_sequence = function(start, exprs) { |
|
if (exprs.length === 1) { |
|
return exprs[0]; |
|
} else if (exprs.length > 1) { |
|
return new AST_Sequence({ start, expressions: exprs, end: peek() }); |
|
} else { |
|
croak("Invalid parenthesized expression"); |
|
} |
|
}; |
|
|
|
var expression = function(commas, no_in) { |
|
var start = S.token; |
|
var exprs = []; |
|
while (true) { |
|
exprs.push(maybe_assign(no_in)); |
|
if (!commas || !is("punc", ",")) break; |
|
next(); |
|
commas = true; |
|
} |
|
return to_expr_or_sequence(start, exprs); |
|
}; |
|
|
|
function in_loop(cont) { |
|
++S.in_loop; |
|
var ret = cont(); |
|
--S.in_loop; |
|
return ret; |
|
} |
|
|
|
if (options.expression) { |
|
return expression(true); |
|
} |
|
|
|
return (function parse_toplevel() { |
|
var start = S.token; |
|
var body = []; |
|
S.input.push_directives_stack(); |
|
if (options.module) S.input.add_directive("use strict"); |
|
while (!is("eof")) { |
|
body.push(statement()); |
|
} |
|
S.input.pop_directives_stack(); |
|
var end = prev(); |
|
var toplevel = options.toplevel; |
|
if (toplevel) { |
|
toplevel.body = toplevel.body.concat(body); |
|
toplevel.end = end; |
|
} else { |
|
toplevel = new AST_Toplevel({ start: start, body: body, end: end }); |
|
} |
|
TEMPLATE_RAWS = new Map(); |
|
return toplevel; |
|
})(); |
|
|
|
} |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
function DEFNODE(type, props, ctor, methods, base = AST_Node) { |
|
if (!props) props = []; |
|
else props = props.split(/\s+/); |
|
var self_props = props; |
|
if (base && base.PROPS) |
|
props = props.concat(base.PROPS); |
|
const proto = base && Object.create(base.prototype); |
|
if (proto) { |
|
ctor.prototype = proto; |
|
ctor.BASE = base; |
|
} |
|
if (base) base.SUBCLASSES.push(ctor); |
|
ctor.prototype.CTOR = ctor; |
|
ctor.prototype.constructor = ctor; |
|
ctor.PROPS = props || null; |
|
ctor.SELF_PROPS = self_props; |
|
ctor.SUBCLASSES = []; |
|
if (type) { |
|
ctor.prototype.TYPE = ctor.TYPE = type; |
|
} |
|
if (methods) for (let i in methods) if (HOP(methods, i)) { |
|
if (i[0] === "$") { |
|
ctor[i.substr(1)] = methods[i]; |
|
} else { |
|
ctor.prototype[i] = methods[i]; |
|
} |
|
} |
|
ctor.DEFMETHOD = function(name, method) { |
|
this.prototype[name] = method; |
|
}; |
|
return ctor; |
|
} |
|
|
|
const has_tok_flag = (tok, flag) => Boolean(tok.flags & flag); |
|
const set_tok_flag = (tok, flag, truth) => { |
|
if (truth) { |
|
tok.flags |= flag; |
|
} else { |
|
tok.flags &= ~flag; |
|
} |
|
}; |
|
|
|
const TOK_FLAG_NLB = 0b0001; |
|
const TOK_FLAG_QUOTE_SINGLE = 0b0010; |
|
const TOK_FLAG_QUOTE_EXISTS = 0b0100; |
|
const TOK_FLAG_TEMPLATE_END = 0b1000; |
|
|
|
class AST_Token { |
|
constructor(type, value, line, col, pos, nlb, comments_before, comments_after, file) { |
|
this.flags = (nlb ? 1 : 0); |
|
|
|
this.type = type; |
|
this.value = value; |
|
this.line = line; |
|
this.col = col; |
|
this.pos = pos; |
|
this.comments_before = comments_before; |
|
this.comments_after = comments_after; |
|
this.file = file; |
|
|
|
Object.seal(this); |
|
} |
|
|
|
// Return a string summary of the token for node.js console.log |
|
[Symbol.for("nodejs.util.inspect.custom")](_depth, options) { |
|
const special = str => options.stylize(str, "special"); |
|
const quote = typeof this.value === "string" && this.value.includes("`") ? "'" : "`"; |
|
const value = `${quote}${this.value}${quote}`; |
|
return `${special("[AST_Token")} ${value} at ${this.line}:${this.col}${special("]")}`; |
|
} |
|
|
|
get nlb() { |
|
return has_tok_flag(this, TOK_FLAG_NLB); |
|
} |
|
|
|
set nlb(new_nlb) { |
|
set_tok_flag(this, TOK_FLAG_NLB, new_nlb); |
|
} |
|
|
|
get quote() { |
|
return !has_tok_flag(this, TOK_FLAG_QUOTE_EXISTS) |
|
? "" |
|
: (has_tok_flag(this, TOK_FLAG_QUOTE_SINGLE) ? "'" : '"'); |
|
} |
|
|
|
set quote(quote_type) { |
|
set_tok_flag(this, TOK_FLAG_QUOTE_SINGLE, quote_type === "'"); |
|
set_tok_flag(this, TOK_FLAG_QUOTE_EXISTS, !!quote_type); |
|
} |
|
|
|
get template_end() { |
|
return has_tok_flag(this, TOK_FLAG_TEMPLATE_END); |
|
} |
|
|
|
set template_end(new_template_end) { |
|
set_tok_flag(this, TOK_FLAG_TEMPLATE_END, new_template_end); |
|
} |
|
} |
|
|
|
var AST_Node = DEFNODE("Node", "start end", function AST_Node(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
_clone: function(deep) { |
|
if (deep) { |
|
var self = this.clone(); |
|
return self.transform(new TreeTransformer(function(node) { |
|
if (node !== self) { |
|
return node.clone(true); |
|
} |
|
})); |
|
} |
|
return new this.CTOR(this); |
|
}, |
|
clone: function(deep) { |
|
return this._clone(deep); |
|
}, |
|
$documentation: "Base class of all AST nodes", |
|
$propdoc: { |
|
start: "[AST_Token] The first token of this node", |
|
end: "[AST_Token] The last token of this node" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this); |
|
}, |
|
walk: function(visitor) { |
|
return this._walk(visitor); // not sure the indirection will be any help |
|
}, |
|
_children_backwards: () => {} |
|
}, null); |
|
|
|
/* -----[ statements ]----- */ |
|
|
|
var AST_Statement = DEFNODE("Statement", null, function AST_Statement(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class of all statements", |
|
}); |
|
|
|
var AST_Debugger = DEFNODE("Debugger", null, function AST_Debugger(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Represents a debugger statement", |
|
}, AST_Statement); |
|
|
|
var AST_Directive = DEFNODE("Directive", "value quote", function AST_Directive(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.quote = props.quote; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Represents a directive, like \"use strict\";", |
|
$propdoc: { |
|
value: "[string] The value of this directive as a plain string (it's not an AST_String!)", |
|
quote: "[string] the original quote character" |
|
}, |
|
}, AST_Statement); |
|
|
|
var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", function AST_SimpleStatement(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A statement consisting of an expression, i.e. a = 1 + 2", |
|
$propdoc: { |
|
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.body._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.body); |
|
} |
|
}, AST_Statement); |
|
|
|
function walk_body(node, visitor) { |
|
const body = node.body; |
|
for (var i = 0, len = body.length; i < len; i++) { |
|
body[i]._walk(visitor); |
|
} |
|
} |
|
|
|
function clone_block_scope(deep) { |
|
var clone = this._clone(deep); |
|
if (this.block_scope) { |
|
clone.block_scope = this.block_scope.clone(); |
|
} |
|
return clone; |
|
} |
|
|
|
var AST_Block = DEFNODE("Block", "body block_scope", function AST_Block(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A body of statements (usually braced)", |
|
$propdoc: { |
|
body: "[AST_Statement*] an array of statements", |
|
block_scope: "[AST_Scope] the block scope" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
walk_body(this, visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.body.length; |
|
while (i--) push(this.body[i]); |
|
}, |
|
clone: clone_block_scope |
|
}, AST_Statement); |
|
|
|
var AST_BlockStatement = DEFNODE("BlockStatement", null, function AST_BlockStatement(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A block statement", |
|
}, AST_Block); |
|
|
|
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, function AST_EmptyStatement(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The empty statement (empty block or simply a semicolon)" |
|
}, AST_Statement); |
|
|
|
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", function AST_StatementWithBody(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", |
|
$propdoc: { |
|
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" |
|
} |
|
}, AST_Statement); |
|
|
|
var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", function AST_LabeledStatement(props) { |
|
if (props) { |
|
this.label = props.label; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Statement with a label", |
|
$propdoc: { |
|
label: "[AST_Label] a label definition" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.label._walk(visitor); |
|
this.body._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.body); |
|
push(this.label); |
|
}, |
|
clone: function(deep) { |
|
var node = this._clone(deep); |
|
if (deep) { |
|
var label = node.label; |
|
var def = this.label; |
|
node.walk(new TreeWalker(function(node) { |
|
if (node instanceof AST_LoopControl |
|
&& node.label && node.label.thedef === def) { |
|
node.label.thedef = label; |
|
label.references.push(node); |
|
} |
|
})); |
|
} |
|
return node; |
|
} |
|
}, AST_StatementWithBody); |
|
|
|
var AST_IterationStatement = DEFNODE( |
|
"IterationStatement", |
|
"block_scope", |
|
function AST_IterationStatement(props) { |
|
if (props) { |
|
this.block_scope = props.block_scope; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "Internal class. All loops inherit from it.", |
|
$propdoc: { |
|
block_scope: "[AST_Scope] the block scope for this iteration statement." |
|
}, |
|
clone: clone_block_scope |
|
}, |
|
AST_StatementWithBody |
|
); |
|
|
|
var AST_DWLoop = DEFNODE("DWLoop", "condition", function AST_DWLoop(props) { |
|
if (props) { |
|
this.condition = props.condition; |
|
this.block_scope = props.block_scope; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for do/while statements", |
|
$propdoc: { |
|
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" |
|
} |
|
}, AST_IterationStatement); |
|
|
|
var AST_Do = DEFNODE("Do", null, function AST_Do(props) { |
|
if (props) { |
|
this.condition = props.condition; |
|
this.block_scope = props.block_scope; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `do` statement", |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.body._walk(visitor); |
|
this.condition._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.condition); |
|
push(this.body); |
|
} |
|
}, AST_DWLoop); |
|
|
|
var AST_While = DEFNODE("While", null, function AST_While(props) { |
|
if (props) { |
|
this.condition = props.condition; |
|
this.block_scope = props.block_scope; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `while` statement", |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.condition._walk(visitor); |
|
this.body._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.body); |
|
push(this.condition); |
|
}, |
|
}, AST_DWLoop); |
|
|
|
var AST_For = DEFNODE("For", "init condition step", function AST_For(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.condition = props.condition; |
|
this.step = props.step; |
|
this.block_scope = props.block_scope; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `for` statement", |
|
$propdoc: { |
|
init: "[AST_Node?] the `for` initialization code, or null if empty", |
|
condition: "[AST_Node?] the `for` termination clause, or null if empty", |
|
step: "[AST_Node?] the `for` update clause, or null if empty" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.init) this.init._walk(visitor); |
|
if (this.condition) this.condition._walk(visitor); |
|
if (this.step) this.step._walk(visitor); |
|
this.body._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.body); |
|
if (this.step) push(this.step); |
|
if (this.condition) push(this.condition); |
|
if (this.init) push(this.init); |
|
}, |
|
}, AST_IterationStatement); |
|
|
|
var AST_ForIn = DEFNODE("ForIn", "init object", function AST_ForIn(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.object = props.object; |
|
this.block_scope = props.block_scope; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `for ... in` statement", |
|
$propdoc: { |
|
init: "[AST_Node] the `for/in` initialization code", |
|
object: "[AST_Node] the object that we're looping through" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.init._walk(visitor); |
|
this.object._walk(visitor); |
|
this.body._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.body); |
|
if (this.object) push(this.object); |
|
if (this.init) push(this.init); |
|
}, |
|
}, AST_IterationStatement); |
|
|
|
var AST_ForOf = DEFNODE("ForOf", "await", function AST_ForOf(props) { |
|
if (props) { |
|
this.await = props.await; |
|
this.init = props.init; |
|
this.object = props.object; |
|
this.block_scope = props.block_scope; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `for ... of` statement", |
|
}, AST_ForIn); |
|
|
|
var AST_With = DEFNODE("With", "expression", function AST_With(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `with` statement", |
|
$propdoc: { |
|
expression: "[AST_Node] the `with` expression" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
this.body._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.body); |
|
push(this.expression); |
|
}, |
|
}, AST_StatementWithBody); |
|
|
|
/* -----[ scope and functions ]----- */ |
|
|
|
var AST_Scope = DEFNODE( |
|
"Scope", |
|
"variables uses_with uses_eval parent_scope enclosed cname", |
|
function AST_Scope(props) { |
|
if (props) { |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "Base class for all statements introducing a lexical scope", |
|
$propdoc: { |
|
variables: "[Map/S] a map of name -> SymbolDef for all variables/functions defined in this scope", |
|
uses_with: "[boolean/S] tells whether this scope uses the `with` statement", |
|
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", |
|
parent_scope: "[AST_Scope?/S] link to the parent scope", |
|
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", |
|
cname: "[integer/S] current index for mangling variables (used internally by the mangler)", |
|
}, |
|
get_defun_scope: function() { |
|
var self = this; |
|
while (self.is_block_scope()) { |
|
self = self.parent_scope; |
|
} |
|
return self; |
|
}, |
|
clone: function(deep, toplevel) { |
|
var node = this._clone(deep); |
|
if (deep && this.variables && toplevel && !this._block_scope) { |
|
node.figure_out_scope({}, { |
|
toplevel: toplevel, |
|
parent_scope: this.parent_scope |
|
}); |
|
} else { |
|
if (this.variables) node.variables = new Map(this.variables); |
|
if (this.enclosed) node.enclosed = this.enclosed.slice(); |
|
if (this._block_scope) node._block_scope = this._block_scope; |
|
} |
|
return node; |
|
}, |
|
pinned: function() { |
|
return this.uses_eval || this.uses_with; |
|
} |
|
}, |
|
AST_Block |
|
); |
|
|
|
var AST_Toplevel = DEFNODE("Toplevel", "globals", function AST_Toplevel(props) { |
|
if (props) { |
|
this.globals = props.globals; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The toplevel scope", |
|
$propdoc: { |
|
globals: "[Map/S] a map of name -> SymbolDef for all undeclared names", |
|
}, |
|
wrap_commonjs: function(name) { |
|
var body = this.body; |
|
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");"; |
|
wrapped_tl = parse(wrapped_tl); |
|
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function(node) { |
|
if (node instanceof AST_Directive && node.value == "$ORIG") { |
|
return MAP.splice(body); |
|
} |
|
})); |
|
return wrapped_tl; |
|
}, |
|
wrap_enclose: function(args_values) { |
|
if (typeof args_values != "string") args_values = ""; |
|
var index = args_values.indexOf(":"); |
|
if (index < 0) index = args_values.length; |
|
var body = this.body; |
|
return parse([ |
|
"(function(", |
|
args_values.slice(0, index), |
|
'){"$ORIG"})(', |
|
args_values.slice(index + 1), |
|
")" |
|
].join("")).transform(new TreeTransformer(function(node) { |
|
if (node instanceof AST_Directive && node.value == "$ORIG") { |
|
return MAP.splice(body); |
|
} |
|
})); |
|
} |
|
}, AST_Scope); |
|
|
|
var AST_Expansion = DEFNODE("Expansion", "expression", function AST_Expansion(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list", |
|
$propdoc: { |
|
expression: "[AST_Node] the thing to be expanded" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression.walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.expression); |
|
}, |
|
}); |
|
|
|
var AST_Lambda = DEFNODE( |
|
"Lambda", |
|
"name argnames uses_arguments is_generator async", |
|
function AST_Lambda(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.argnames = props.argnames; |
|
this.uses_arguments = props.uses_arguments; |
|
this.is_generator = props.is_generator; |
|
this.async = props.async; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "Base class for functions", |
|
$propdoc: { |
|
name: "[AST_SymbolDeclaration?] the name of this function", |
|
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments", |
|
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array", |
|
is_generator: "[boolean] is this a generator method", |
|
async: "[boolean] is this method async", |
|
}, |
|
args_as_names: function () { |
|
var out = []; |
|
for (var i = 0; i < this.argnames.length; i++) { |
|
if (this.argnames[i] instanceof AST_Destructuring) { |
|
out.push(...this.argnames[i].all_symbols()); |
|
} else { |
|
out.push(this.argnames[i]); |
|
} |
|
} |
|
return out; |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.name) this.name._walk(visitor); |
|
var argnames = this.argnames; |
|
for (var i = 0, len = argnames.length; i < len; i++) { |
|
argnames[i]._walk(visitor); |
|
} |
|
walk_body(this, visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.body.length; |
|
while (i--) push(this.body[i]); |
|
|
|
i = this.argnames.length; |
|
while (i--) push(this.argnames[i]); |
|
|
|
if (this.name) push(this.name); |
|
}, |
|
is_braceless() { |
|
return this.body[0] instanceof AST_Return && this.body[0].value; |
|
}, |
|
// Default args and expansion don't count, so .argnames.length doesn't cut it |
|
length_property() { |
|
let length = 0; |
|
|
|
for (const arg of this.argnames) { |
|
if (arg instanceof AST_SymbolFunarg || arg instanceof AST_Destructuring) { |
|
length++; |
|
} |
|
} |
|
|
|
return length; |
|
} |
|
}, |
|
AST_Scope |
|
); |
|
|
|
var AST_Accessor = DEFNODE("Accessor", null, function AST_Accessor(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.argnames = props.argnames; |
|
this.uses_arguments = props.uses_arguments; |
|
this.is_generator = props.is_generator; |
|
this.async = props.async; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A setter/getter function. The `name` property is always null." |
|
}, AST_Lambda); |
|
|
|
var AST_Function = DEFNODE("Function", null, function AST_Function(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.argnames = props.argnames; |
|
this.uses_arguments = props.uses_arguments; |
|
this.is_generator = props.is_generator; |
|
this.async = props.async; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A function expression" |
|
}, AST_Lambda); |
|
|
|
var AST_Arrow = DEFNODE("Arrow", null, function AST_Arrow(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.argnames = props.argnames; |
|
this.uses_arguments = props.uses_arguments; |
|
this.is_generator = props.is_generator; |
|
this.async = props.async; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An ES6 Arrow function ((a) => b)" |
|
}, AST_Lambda); |
|
|
|
var AST_Defun = DEFNODE("Defun", null, function AST_Defun(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.argnames = props.argnames; |
|
this.uses_arguments = props.uses_arguments; |
|
this.is_generator = props.is_generator; |
|
this.async = props.async; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A function definition" |
|
}, AST_Lambda); |
|
|
|
/* -----[ DESTRUCTURING ]----- */ |
|
var AST_Destructuring = DEFNODE("Destructuring", "names is_array", function AST_Destructuring(props) { |
|
if (props) { |
|
this.names = props.names; |
|
this.is_array = props.is_array; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names", |
|
$propdoc: { |
|
"names": "[AST_Node*] Array of properties or elements", |
|
"is_array": "[Boolean] Whether the destructuring represents an object or array" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.names.forEach(function(name) { |
|
name._walk(visitor); |
|
}); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.names.length; |
|
while (i--) push(this.names[i]); |
|
}, |
|
all_symbols: function() { |
|
var out = []; |
|
walk(this, node => { |
|
if (node instanceof AST_SymbolDeclaration) { |
|
out.push(node); |
|
} |
|
if (node instanceof AST_Lambda) { |
|
return true; |
|
} |
|
}); |
|
return out; |
|
} |
|
}); |
|
|
|
var AST_PrefixedTemplateString = DEFNODE( |
|
"PrefixedTemplateString", |
|
"template_string prefix", |
|
function AST_PrefixedTemplateString(props) { |
|
if (props) { |
|
this.template_string = props.template_string; |
|
this.prefix = props.prefix; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`", |
|
$propdoc: { |
|
template_string: "[AST_TemplateString] The template string", |
|
prefix: "[AST_Node] The prefix, which will get called." |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function () { |
|
this.prefix._walk(visitor); |
|
this.template_string._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.template_string); |
|
push(this.prefix); |
|
}, |
|
} |
|
); |
|
|
|
var AST_TemplateString = DEFNODE("TemplateString", "segments", function AST_TemplateString(props) { |
|
if (props) { |
|
this.segments = props.segments; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A template string literal", |
|
$propdoc: { |
|
segments: "[AST_Node*] One or more segments, starting with AST_TemplateSegment. AST_Node may follow AST_TemplateSegment, but each AST_Node must be followed by AST_TemplateSegment." |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.segments.forEach(function(seg) { |
|
seg._walk(visitor); |
|
}); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.segments.length; |
|
while (i--) push(this.segments[i]); |
|
} |
|
}); |
|
|
|
var AST_TemplateSegment = DEFNODE("TemplateSegment", "value raw", function AST_TemplateSegment(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.raw = props.raw; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A segment of a template string literal", |
|
$propdoc: { |
|
value: "Content of the segment", |
|
raw: "Raw source of the segment", |
|
} |
|
}); |
|
|
|
/* -----[ JUMPS ]----- */ |
|
|
|
var AST_Jump = DEFNODE("Jump", null, function AST_Jump(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)" |
|
}, AST_Statement); |
|
|
|
/** Base class for “exits” (`return` and `throw`) */ |
|
var AST_Exit = DEFNODE("Exit", "value", function AST_Exit(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for “exits” (`return` and `throw`)", |
|
$propdoc: { |
|
value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, this.value && function() { |
|
this.value._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.value) push(this.value); |
|
}, |
|
}, AST_Jump); |
|
|
|
var AST_Return = DEFNODE("Return", null, function AST_Return(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `return` statement" |
|
}, AST_Exit); |
|
|
|
var AST_Throw = DEFNODE("Throw", null, function AST_Throw(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `throw` statement" |
|
}, AST_Exit); |
|
|
|
var AST_LoopControl = DEFNODE("LoopControl", "label", function AST_LoopControl(props) { |
|
if (props) { |
|
this.label = props.label; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for loop control statements (`break` and `continue`)", |
|
$propdoc: { |
|
label: "[AST_LabelRef?] the label, or null if none", |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, this.label && function() { |
|
this.label._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.label) push(this.label); |
|
}, |
|
}, AST_Jump); |
|
|
|
var AST_Break = DEFNODE("Break", null, function AST_Break(props) { |
|
if (props) { |
|
this.label = props.label; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `break` statement" |
|
}, AST_LoopControl); |
|
|
|
var AST_Continue = DEFNODE("Continue", null, function AST_Continue(props) { |
|
if (props) { |
|
this.label = props.label; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `continue` statement" |
|
}, AST_LoopControl); |
|
|
|
var AST_Await = DEFNODE("Await", "expression", function AST_Await(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An `await` statement", |
|
$propdoc: { |
|
expression: "[AST_Node] the mandatory expression being awaited", |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.expression); |
|
}, |
|
}); |
|
|
|
var AST_Yield = DEFNODE("Yield", "expression is_star", function AST_Yield(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.is_star = props.is_star; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `yield` statement", |
|
$propdoc: { |
|
expression: "[AST_Node?] the value returned or thrown by this statement; could be null (representing undefined) but only when is_star is set to false", |
|
is_star: "[Boolean] Whether this is a yield or yield* statement" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, this.expression && function() { |
|
this.expression._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.expression) push(this.expression); |
|
} |
|
}); |
|
|
|
/* -----[ IF ]----- */ |
|
|
|
var AST_If = DEFNODE("If", "condition alternative", function AST_If(props) { |
|
if (props) { |
|
this.condition = props.condition; |
|
this.alternative = props.alternative; |
|
this.body = props.body; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `if` statement", |
|
$propdoc: { |
|
condition: "[AST_Node] the `if` condition", |
|
alternative: "[AST_Statement?] the `else` part, or null if not present" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.condition._walk(visitor); |
|
this.body._walk(visitor); |
|
if (this.alternative) this.alternative._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.alternative) { |
|
push(this.alternative); |
|
} |
|
push(this.body); |
|
push(this.condition); |
|
} |
|
}, AST_StatementWithBody); |
|
|
|
/* -----[ SWITCH ]----- */ |
|
|
|
var AST_Switch = DEFNODE("Switch", "expression", function AST_Switch(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `switch` statement", |
|
$propdoc: { |
|
expression: "[AST_Node] the `switch` “discriminant”" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
walk_body(this, visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.body.length; |
|
while (i--) push(this.body[i]); |
|
push(this.expression); |
|
} |
|
}, AST_Block); |
|
|
|
var AST_SwitchBranch = DEFNODE("SwitchBranch", null, function AST_SwitchBranch(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for `switch` branches", |
|
}, AST_Block); |
|
|
|
var AST_Default = DEFNODE("Default", null, function AST_Default(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `default` switch branch", |
|
}, AST_SwitchBranch); |
|
|
|
var AST_Case = DEFNODE("Case", "expression", function AST_Case(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `case` switch branch", |
|
$propdoc: { |
|
expression: "[AST_Node] the `case` expression" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
walk_body(this, visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.body.length; |
|
while (i--) push(this.body[i]); |
|
push(this.expression); |
|
}, |
|
}, AST_SwitchBranch); |
|
|
|
/* -----[ EXCEPTIONS ]----- */ |
|
|
|
var AST_Try = DEFNODE("Try", "body bcatch bfinally", function AST_Try(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.bcatch = props.bcatch; |
|
this.bfinally = props.bfinally; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `try` statement", |
|
$propdoc: { |
|
body: "[AST_TryBlock] the try block", |
|
bcatch: "[AST_Catch?] the catch block, or null if not present", |
|
bfinally: "[AST_Finally?] the finally block, or null if not present" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.body._walk(visitor); |
|
if (this.bcatch) this.bcatch._walk(visitor); |
|
if (this.bfinally) this.bfinally._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.bfinally) push(this.bfinally); |
|
if (this.bcatch) push(this.bcatch); |
|
push(this.body); |
|
}, |
|
}, AST_Statement); |
|
|
|
var AST_TryBlock = DEFNODE("TryBlock", null, function AST_TryBlock(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `try` block of a try statement" |
|
}, AST_Block); |
|
|
|
var AST_Catch = DEFNODE("Catch", "argname", function AST_Catch(props) { |
|
if (props) { |
|
this.argname = props.argname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `catch` node; only makes sense as part of a `try` statement", |
|
$propdoc: { |
|
argname: "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.argname) this.argname._walk(visitor); |
|
walk_body(this, visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.body.length; |
|
while (i--) push(this.body[i]); |
|
if (this.argname) push(this.argname); |
|
}, |
|
}, AST_Block); |
|
|
|
var AST_Finally = DEFNODE("Finally", null, function AST_Finally(props) { |
|
if (props) { |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `finally` node; only makes sense as part of a `try` statement" |
|
}, AST_Block); |
|
|
|
/* -----[ VAR/CONST ]----- */ |
|
|
|
var AST_DefinitionsLike = DEFNODE("DefinitionsLike", "definitions", function AST_DefinitionsLike(props) { |
|
if (props) { |
|
this.definitions = props.definitions; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for variable definitions and `using`", |
|
$propdoc: { |
|
definitions: "[AST_VarDef*|AST_UsingDef*] array of variable definitions" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
var definitions = this.definitions; |
|
for (var i = 0, len = definitions.length; i < len; i++) { |
|
definitions[i]._walk(visitor); |
|
} |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.definitions.length; |
|
while (i--) push(this.definitions[i]); |
|
}, |
|
}, AST_Statement); |
|
|
|
var AST_Definitions = DEFNODE("Definitions", null, function AST_Definitions(props) { |
|
if (props) { |
|
this.definitions = props.definitions; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)", |
|
}, AST_DefinitionsLike); |
|
|
|
var AST_Var = DEFNODE("Var", null, function AST_Var(props) { |
|
if (props) { |
|
this.definitions = props.definitions; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `var` statement" |
|
}, AST_Definitions); |
|
|
|
var AST_Let = DEFNODE("Let", null, function AST_Let(props) { |
|
if (props) { |
|
this.definitions = props.definitions; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `let` statement" |
|
}, AST_Definitions); |
|
|
|
var AST_Const = DEFNODE("Const", null, function AST_Const(props) { |
|
if (props) { |
|
this.definitions = props.definitions; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `const` statement" |
|
}, AST_Definitions); |
|
|
|
var AST_Using = DEFNODE("Using", "await", function AST_Using(props) { |
|
if (props) { |
|
this.await = props.await; |
|
this.definitions = props.definitions; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `using` statement", |
|
$propdoc: { |
|
await: "[boolean] Whether it's `await using`" |
|
}, |
|
}, AST_DefinitionsLike); |
|
|
|
var AST_VarDefLike = DEFNODE("VarDefLike", "name value", function AST_VarDefLike(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A name=value pair in a variable definition statement or `using`", |
|
$propdoc: { |
|
name: "[AST_Destructuring|AST_SymbolDeclaration] name of the variable", |
|
value: "[AST_Node?] initializer, or null of there's no initializer" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.name._walk(visitor); |
|
if (this.value) this.value._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.value) push(this.value); |
|
push(this.name); |
|
}, |
|
declarations_as_names() { |
|
if (this.name instanceof AST_SymbolDeclaration) { |
|
return [this.name]; |
|
} else { |
|
return this.name.all_symbols(); |
|
} |
|
} |
|
}); |
|
|
|
var AST_VarDef = DEFNODE("VarDef", null, function AST_VarDef(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A variable declaration; only appears in a AST_Definitions node", |
|
}, AST_VarDefLike); |
|
|
|
var AST_UsingDef = DEFNODE("UsingDef", null, function AST_UsingDef(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Like VarDef but specific to AST_Using", |
|
}, AST_VarDefLike); |
|
|
|
var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", function AST_NameMapping(props) { |
|
if (props) { |
|
this.foreign_name = props.foreign_name; |
|
this.name = props.name; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The part of the export/import statement that declare names from a module.", |
|
$propdoc: { |
|
foreign_name: "[AST_SymbolExportForeign|AST_SymbolImportForeign] The name being exported/imported (as specified in the module)", |
|
name: "[AST_SymbolExport|AST_SymbolImport] The name as it is visible to this module." |
|
}, |
|
_walk: function (visitor) { |
|
return visitor._visit(this, function() { |
|
this.foreign_name._walk(visitor); |
|
this.name._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.name); |
|
push(this.foreign_name); |
|
}, |
|
}); |
|
|
|
var AST_Import = DEFNODE( |
|
"Import", |
|
"imported_name imported_names module_name attributes", |
|
function AST_Import(props) { |
|
if (props) { |
|
this.imported_name = props.imported_name; |
|
this.imported_names = props.imported_names; |
|
this.module_name = props.module_name; |
|
this.attributes = props.attributes; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "An `import` statement", |
|
$propdoc: { |
|
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.", |
|
imported_names: "[AST_NameMapping*] The names of non-default imported variables", |
|
module_name: "[AST_String] String literal describing where this module came from", |
|
attributes: "[AST_Object?] The import attributes (with {...})" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.imported_name) { |
|
this.imported_name._walk(visitor); |
|
} |
|
if (this.imported_names) { |
|
this.imported_names.forEach(function(name_import) { |
|
name_import._walk(visitor); |
|
}); |
|
} |
|
this.module_name._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.module_name); |
|
if (this.imported_names) { |
|
let i = this.imported_names.length; |
|
while (i--) push(this.imported_names[i]); |
|
} |
|
if (this.imported_name) push(this.imported_name); |
|
}, |
|
} |
|
); |
|
|
|
var AST_ImportMeta = DEFNODE("ImportMeta", null, function AST_ImportMeta(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A reference to import.meta", |
|
}); |
|
|
|
var AST_Export = DEFNODE( |
|
"Export", |
|
"exported_definition exported_value is_default exported_names module_name attributes", |
|
function AST_Export(props) { |
|
if (props) { |
|
this.exported_definition = props.exported_definition; |
|
this.exported_value = props.exported_value; |
|
this.is_default = props.is_default; |
|
this.exported_names = props.exported_names; |
|
this.module_name = props.module_name; |
|
this.attributes = props.attributes; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "An `export` statement", |
|
$propdoc: { |
|
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition", |
|
exported_value: "[AST_Node?] An exported value", |
|
exported_names: "[AST_NameMapping*?] List of exported names", |
|
module_name: "[AST_String?] Name of the file to load exports from", |
|
is_default: "[Boolean] Whether this is the default exported value of this module", |
|
attributes: "[AST_Object?] The import attributes" |
|
}, |
|
_walk: function (visitor) { |
|
return visitor._visit(this, function () { |
|
if (this.exported_definition) { |
|
this.exported_definition._walk(visitor); |
|
} |
|
if (this.exported_value) { |
|
this.exported_value._walk(visitor); |
|
} |
|
if (this.exported_names) { |
|
this.exported_names.forEach(function(name_export) { |
|
name_export._walk(visitor); |
|
}); |
|
} |
|
if (this.module_name) { |
|
this.module_name._walk(visitor); |
|
} |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.module_name) push(this.module_name); |
|
if (this.exported_names) { |
|
let i = this.exported_names.length; |
|
while (i--) push(this.exported_names[i]); |
|
} |
|
if (this.exported_value) push(this.exported_value); |
|
if (this.exported_definition) push(this.exported_definition); |
|
} |
|
}, |
|
AST_Statement |
|
); |
|
|
|
/* -----[ OTHER ]----- */ |
|
|
|
var AST_Call = DEFNODE( |
|
"Call", |
|
"expression args optional _annotations", |
|
function AST_Call(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.args = props.args; |
|
this.optional = props.optional; |
|
this._annotations = props._annotations; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this.initialize(); |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "A function call expression", |
|
$propdoc: { |
|
expression: "[AST_Node] expression to invoke as function", |
|
args: "[AST_Node*] array of arguments", |
|
optional: "[boolean] whether this is an optional call (IE ?.() )", |
|
_annotations: "[number] bitfield containing information about the call" |
|
}, |
|
initialize() { |
|
if (this._annotations == null) this._annotations = 0; |
|
}, |
|
_walk(visitor) { |
|
return visitor._visit(this, function() { |
|
var args = this.args; |
|
for (var i = 0, len = args.length; i < len; i++) { |
|
args[i]._walk(visitor); |
|
} |
|
this.expression._walk(visitor); // TODO why do we need to crawl this last? |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.args.length; |
|
while (i--) push(this.args[i]); |
|
push(this.expression); |
|
}, |
|
} |
|
); |
|
|
|
var AST_New = DEFNODE("New", null, function AST_New(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.args = props.args; |
|
this.optional = props.optional; |
|
this._annotations = props._annotations; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this.initialize(); |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" |
|
}, AST_Call); |
|
|
|
var AST_Sequence = DEFNODE("Sequence", "expressions", function AST_Sequence(props) { |
|
if (props) { |
|
this.expressions = props.expressions; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A sequence expression (comma-separated expressions)", |
|
$propdoc: { |
|
expressions: "[AST_Node*] array of expressions (at least two)" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expressions.forEach(function(node) { |
|
node._walk(visitor); |
|
}); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.expressions.length; |
|
while (i--) push(this.expressions[i]); |
|
}, |
|
}); |
|
|
|
var AST_PropAccess = DEFNODE( |
|
"PropAccess", |
|
"expression property optional", |
|
function AST_PropAccess(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.property = props.property; |
|
this.optional = props.optional; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`", |
|
$propdoc: { |
|
expression: "[AST_Node] the “container” expression", |
|
property: "[AST_Node|string] the property to access. For AST_Dot & AST_DotHash this is always a plain string, while for AST_Sub it's an arbitrary AST_Node", |
|
|
|
optional: "[boolean] whether this is an optional property access (IE ?.)" |
|
} |
|
} |
|
); |
|
|
|
var AST_Dot = DEFNODE("Dot", "quote", function AST_Dot(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.expression = props.expression; |
|
this.property = props.property; |
|
this.optional = props.optional; |
|
this._annotations = props._annotations; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A dotted property access expression", |
|
$propdoc: { |
|
quote: "[string] the original quote character when transformed from AST_Sub", |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.expression); |
|
}, |
|
}, AST_PropAccess); |
|
|
|
var AST_DotHash = DEFNODE("DotHash", "", function AST_DotHash(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.property = props.property; |
|
this.optional = props.optional; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A dotted property access to a private property", |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.expression); |
|
}, |
|
}, AST_PropAccess); |
|
|
|
var AST_Sub = DEFNODE("Sub", null, function AST_Sub(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.property = props.property; |
|
this.optional = props.optional; |
|
this._annotations = props._annotations; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Index-style property access, i.e. `a[\"foo\"]`", |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
this.property._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.property); |
|
push(this.expression); |
|
}, |
|
}, AST_PropAccess); |
|
|
|
var AST_Chain = DEFNODE("Chain", "expression", function AST_Chain(props) { |
|
if (props) { |
|
this.expression = props.expression; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A chain expression like a?.b?.(c)?.[d]", |
|
$propdoc: { |
|
expression: "[AST_Call|AST_Dot|AST_DotHash|AST_Sub] chain element." |
|
}, |
|
_walk: function (visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.expression); |
|
}, |
|
}); |
|
|
|
var AST_Unary = DEFNODE("Unary", "operator expression", function AST_Unary(props) { |
|
if (props) { |
|
this.operator = props.operator; |
|
this.expression = props.expression; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for unary expressions", |
|
$propdoc: { |
|
operator: "[string] the operator", |
|
expression: "[AST_Node] expression that this unary operator applies to" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.expression._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.expression); |
|
}, |
|
}); |
|
|
|
var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, function AST_UnaryPrefix(props) { |
|
if (props) { |
|
this.operator = props.operator; |
|
this.expression = props.expression; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Unary prefix expression, i.e. `typeof i` or `++i`" |
|
}, AST_Unary); |
|
|
|
var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, function AST_UnaryPostfix(props) { |
|
if (props) { |
|
this.operator = props.operator; |
|
this.expression = props.expression; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Unary postfix expression, i.e. `i++`" |
|
}, AST_Unary); |
|
|
|
var AST_Binary = DEFNODE("Binary", "operator left right", function AST_Binary(props) { |
|
if (props) { |
|
this.operator = props.operator; |
|
this.left = props.left; |
|
this.right = props.right; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Binary expression, i.e. `a + b`", |
|
$propdoc: { |
|
left: "[AST_Node] left-hand side expression", |
|
operator: "[string] the operator", |
|
right: "[AST_Node] right-hand side expression" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.left._walk(visitor); |
|
this.right._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.right); |
|
push(this.left); |
|
}, |
|
}); |
|
|
|
var AST_Conditional = DEFNODE( |
|
"Conditional", |
|
"condition consequent alternative", |
|
function AST_Conditional(props) { |
|
if (props) { |
|
this.condition = props.condition; |
|
this.consequent = props.consequent; |
|
this.alternative = props.alternative; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`", |
|
$propdoc: { |
|
condition: "[AST_Node]", |
|
consequent: "[AST_Node]", |
|
alternative: "[AST_Node]" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.condition._walk(visitor); |
|
this.consequent._walk(visitor); |
|
this.alternative._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.alternative); |
|
push(this.consequent); |
|
push(this.condition); |
|
}, |
|
} |
|
); |
|
|
|
var AST_Assign = DEFNODE("Assign", "logical", function AST_Assign(props) { |
|
if (props) { |
|
this.logical = props.logical; |
|
this.operator = props.operator; |
|
this.left = props.left; |
|
this.right = props.right; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An assignment expression — `a = b + 5`", |
|
$propdoc: { |
|
logical: "Whether it's a logical assignment" |
|
} |
|
}, AST_Binary); |
|
|
|
var AST_DefaultAssign = DEFNODE("DefaultAssign", null, function AST_DefaultAssign(props) { |
|
if (props) { |
|
this.operator = props.operator; |
|
this.left = props.left; |
|
this.right = props.right; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A default assignment expression like in `(a = 3) => a`" |
|
}, AST_Binary); |
|
|
|
/* -----[ LITERALS ]----- */ |
|
|
|
var AST_Array = DEFNODE("Array", "elements", function AST_Array(props) { |
|
if (props) { |
|
this.elements = props.elements; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An array literal", |
|
$propdoc: { |
|
elements: "[AST_Node*] array of elements" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
var elements = this.elements; |
|
for (var i = 0, len = elements.length; i < len; i++) { |
|
elements[i]._walk(visitor); |
|
} |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.elements.length; |
|
while (i--) push(this.elements[i]); |
|
}, |
|
}); |
|
|
|
var AST_Object = DEFNODE("Object", "properties", function AST_Object(props) { |
|
if (props) { |
|
this.properties = props.properties; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An object literal", |
|
$propdoc: { |
|
properties: "[AST_ObjectProperty*] array of properties" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
var properties = this.properties; |
|
for (var i = 0, len = properties.length; i < len; i++) { |
|
properties[i]._walk(visitor); |
|
} |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.properties.length; |
|
while (i--) push(this.properties[i]); |
|
}, |
|
}); |
|
|
|
/* -----[ OBJECT/CLASS PROPERTIES ]----- */ |
|
|
|
/** |
|
* Everything inside the curly braces of an object/class is a subclass of AST_ObjectProperty, except for AST_ClassStaticBlock. |
|
**/ |
|
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", function AST_ObjectProperty(props) { |
|
if (props) { |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this._annotations = props._annotations; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for literal object properties", |
|
$propdoc: { |
|
key: "[string|AST_Node] property name. For ObjectKeyVal this is a string. For getters, setters and computed property this is an AST_Node.", |
|
value: "[AST_Node] property value. For getters, setters and methods this is an AST_Accessor." |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.key instanceof AST_Node) |
|
this.key._walk(visitor); |
|
this.value._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.value); |
|
if (this.key instanceof AST_Node) push(this.key); |
|
}, |
|
}); |
|
|
|
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", function AST_ObjectKeyVal(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this._annotations = props._annotations; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A key: value object property", |
|
$propdoc: { |
|
quote: "[string] the original quote character" |
|
}, |
|
computed_key() { |
|
return this.key instanceof AST_Node; |
|
} |
|
}, AST_ObjectProperty); |
|
|
|
var AST_PrivateSetter = DEFNODE("PrivateSetter", "static", function AST_PrivateSetter(props) { |
|
if (props) { |
|
this.static = props.static; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$propdoc: { |
|
static: "[boolean] whether this is a static private setter" |
|
}, |
|
$documentation: "A private setter property", |
|
computed_key() { |
|
return false; |
|
} |
|
}, AST_ObjectProperty); |
|
|
|
var AST_PrivateGetter = DEFNODE("PrivateGetter", "static", function AST_PrivateGetter(props) { |
|
if (props) { |
|
this.static = props.static; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$propdoc: { |
|
static: "[boolean] whether this is a static private getter" |
|
}, |
|
$documentation: "A private getter property", |
|
computed_key() { |
|
return false; |
|
} |
|
}, AST_ObjectProperty); |
|
|
|
var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", function AST_ObjectSetter(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.static = props.static; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this._annotations = props._annotations; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$propdoc: { |
|
quote: "[string|undefined] the original quote character, if any", |
|
static: "[boolean] whether this is a static setter (classes only)" |
|
}, |
|
$documentation: "An object setter property", |
|
computed_key() { |
|
return !(this.key instanceof AST_SymbolMethod); |
|
} |
|
}, AST_ObjectProperty); |
|
|
|
var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", function AST_ObjectGetter(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.static = props.static; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this._annotations = props._annotations; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$propdoc: { |
|
quote: "[string|undefined] the original quote character, if any", |
|
static: "[boolean] whether this is a static getter (classes only)" |
|
}, |
|
$documentation: "An object getter property", |
|
computed_key() { |
|
return !(this.key instanceof AST_SymbolMethod); |
|
} |
|
}, AST_ObjectProperty); |
|
|
|
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static", function AST_ConciseMethod(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.static = props.static; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this._annotations = props._annotations; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$propdoc: { |
|
quote: "[string|undefined] the original quote character, if any", |
|
static: "[boolean] is this method static (classes only)", |
|
}, |
|
$documentation: "An ES6 concise method inside an object or class", |
|
computed_key() { |
|
return !(this.key instanceof AST_SymbolMethod); |
|
} |
|
}, AST_ObjectProperty); |
|
|
|
var AST_PrivateMethod = DEFNODE("PrivateMethod", "static", function AST_PrivateMethod(props) { |
|
if (props) { |
|
this.static = props.static; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A private class method inside a class", |
|
$propdoc: { |
|
static: "[boolean] is this a static private method", |
|
}, |
|
computed_key() { |
|
return false; |
|
}, |
|
}, AST_ObjectProperty); |
|
|
|
var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.extends = props.extends; |
|
this.properties = props.properties; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$propdoc: { |
|
name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.", |
|
extends: "[AST_Node]? optional parent class", |
|
properties: "[AST_ObjectProperty|AST_ClassStaticBlock]* array of properties or static blocks" |
|
}, |
|
$documentation: "An ES6 class", |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.name) { |
|
this.name._walk(visitor); |
|
} |
|
if (this.extends) { |
|
this.extends._walk(visitor); |
|
} |
|
this.properties.forEach((prop) => prop._walk(visitor)); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.properties.length; |
|
while (i--) push(this.properties[i]); |
|
if (this.extends) push(this.extends); |
|
if (this.name) push(this.name); |
|
}, |
|
/** go through the bits that are executed instantly, not when the class is `new`'d. Doesn't walk the name. */ |
|
visit_nondeferred_class_parts(visitor) { |
|
if (this.extends) { |
|
this.extends._walk(visitor); |
|
} |
|
this.properties.forEach((prop) => { |
|
if (prop instanceof AST_ClassStaticBlock) { |
|
prop._walk(visitor); |
|
return; |
|
} |
|
if (prop.computed_key()) { |
|
visitor.push(prop); |
|
prop.key._walk(visitor); |
|
visitor.pop(); |
|
} |
|
if ( |
|
prop instanceof AST_ClassPrivateProperty && prop.static && prop.value |
|
|| prop instanceof AST_ClassProperty && prop.static && prop.value |
|
) { |
|
visitor.push(prop); |
|
prop.value._walk(visitor); |
|
visitor.pop(); |
|
} |
|
}); |
|
}, |
|
/** go through the bits that are executed later, when the class is `new`'d or a static method is called */ |
|
visit_deferred_class_parts(visitor) { |
|
this.properties.forEach((prop) => { |
|
if ( |
|
prop instanceof AST_ConciseMethod |
|
|| prop instanceof AST_PrivateMethod |
|
) { |
|
prop.walk(visitor); |
|
} else if ( |
|
prop instanceof AST_ClassProperty && !prop.static && prop.value |
|
|| prop instanceof AST_ClassPrivateProperty && !prop.static && prop.value |
|
) { |
|
visitor.push(prop); |
|
prop.value._walk(visitor); |
|
visitor.pop(); |
|
} |
|
}); |
|
}, |
|
is_self_referential: function() { |
|
const this_id = this.name && this.name.definition().id; |
|
let found = false; |
|
let class_this = true; |
|
this.visit_nondeferred_class_parts(new TreeWalker((node, descend) => { |
|
if (found) return true; |
|
if (node instanceof AST_This) return (found = class_this); |
|
if (node instanceof AST_SymbolRef) return (found = node.definition().id === this_id); |
|
if (node instanceof AST_Lambda && !(node instanceof AST_Arrow)) { |
|
const class_this_save = class_this; |
|
class_this = false; |
|
descend(); |
|
class_this = class_this_save; |
|
return true; |
|
} |
|
})); |
|
return found; |
|
}, |
|
}, AST_Scope /* TODO a class might have a scope but it's not a scope */); |
|
|
|
var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_ClassProperty(props) { |
|
if (props) { |
|
this.static = props.static; |
|
this.quote = props.quote; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this._annotations = props._annotations; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A class property", |
|
$propdoc: { |
|
static: "[boolean] whether this is a static key", |
|
quote: "[string] which quote is being used" |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.key instanceof AST_Node) |
|
this.key._walk(visitor); |
|
if (this.value instanceof AST_Node) |
|
this.value._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.value instanceof AST_Node) push(this.value); |
|
if (this.key instanceof AST_Node) push(this.key); |
|
}, |
|
computed_key() { |
|
return !(this.key instanceof AST_SymbolClassProperty); |
|
} |
|
}, AST_ObjectProperty); |
|
|
|
var AST_ClassPrivateProperty = DEFNODE("ClassPrivateProperty", "", function AST_ClassPrivateProperty(props) { |
|
if (props) { |
|
this.static = props.static; |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A class property for a private property", |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
if (this.value instanceof AST_Node) |
|
this.value._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
if (this.value instanceof AST_Node) push(this.value); |
|
}, |
|
computed_key() { |
|
return false; |
|
}, |
|
}, AST_ObjectProperty); |
|
|
|
var AST_PrivateIn = DEFNODE("PrivateIn", "key value", function AST_PrivateIn(props) { |
|
if (props) { |
|
this.key = props.key; |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "An `in` binop when the key is private, eg #x in this", |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
this.key._walk(visitor); |
|
this.value._walk(visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
push(this.value); |
|
push(this.key); |
|
}, |
|
}); |
|
|
|
var AST_DefClass = DEFNODE("DefClass", null, function AST_DefClass(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.extends = props.extends; |
|
this.properties = props.properties; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A class definition", |
|
}, AST_Class); |
|
|
|
var AST_ClassStaticBlock = DEFNODE("ClassStaticBlock", "body block_scope", function AST_ClassStaticBlock (props) { |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
}, { |
|
$documentation: "A block containing statements to be executed in the context of the class", |
|
$propdoc: { |
|
body: "[AST_Statement*] an array of statements", |
|
}, |
|
_walk: function(visitor) { |
|
return visitor._visit(this, function() { |
|
walk_body(this, visitor); |
|
}); |
|
}, |
|
_children_backwards(push) { |
|
let i = this.body.length; |
|
while (i--) push(this.body[i]); |
|
}, |
|
clone: clone_block_scope, |
|
computed_key() { |
|
return false; |
|
}, |
|
}, AST_Scope); |
|
|
|
var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExpression(props) { |
|
if (props) { |
|
this.name = props.name; |
|
this.extends = props.extends; |
|
this.properties = props.properties; |
|
this.variables = props.variables; |
|
this.uses_with = props.uses_with; |
|
this.uses_eval = props.uses_eval; |
|
this.parent_scope = props.parent_scope; |
|
this.enclosed = props.enclosed; |
|
this.cname = props.cname; |
|
this.body = props.body; |
|
this.block_scope = props.block_scope; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A class expression." |
|
}, AST_Class); |
|
|
|
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", function AST_Symbol(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$propdoc: { |
|
name: "[string] name of this symbol", |
|
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", |
|
thedef: "[SymbolDef/S] the definition of this symbol" |
|
}, |
|
$documentation: "Base class for all symbols" |
|
}); |
|
|
|
var AST_NewTarget = DEFNODE("NewTarget", null, function AST_NewTarget(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A reference to new.target" |
|
}); |
|
|
|
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", function AST_SymbolDeclaration(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", |
|
}, AST_Symbol); |
|
|
|
var AST_SymbolVar = DEFNODE("SymbolVar", null, function AST_SymbolVar(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol defining a variable", |
|
}, AST_SymbolDeclaration); |
|
|
|
var AST_SymbolBlockDeclaration = DEFNODE( |
|
"SymbolBlockDeclaration", |
|
null, |
|
function AST_SymbolBlockDeclaration(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, |
|
{ |
|
$documentation: "Base class for block-scoped declaration symbols" |
|
}, |
|
AST_SymbolDeclaration |
|
); |
|
|
|
var AST_SymbolConst = DEFNODE("SymbolConst", null, function AST_SymbolConst(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A constant declaration" |
|
}, AST_SymbolBlockDeclaration); |
|
|
|
var AST_SymbolUsing = DEFNODE("SymbolUsing", null, function AST_SymbolUsing(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A `using` declaration" |
|
}, AST_SymbolBlockDeclaration); |
|
|
|
var AST_SymbolLet = DEFNODE("SymbolLet", null, function AST_SymbolLet(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A block-scoped `let` declaration" |
|
}, AST_SymbolBlockDeclaration); |
|
|
|
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, function AST_SymbolFunarg(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol naming a function argument", |
|
}, AST_SymbolVar); |
|
|
|
var AST_SymbolDefun = DEFNODE("SymbolDefun", null, function AST_SymbolDefun(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol defining a function", |
|
}, AST_SymbolDeclaration); |
|
|
|
var AST_SymbolMethod = DEFNODE("SymbolMethod", null, function AST_SymbolMethod(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol in an object defining a method", |
|
}, AST_Symbol); |
|
|
|
var AST_SymbolClassProperty = DEFNODE("SymbolClassProperty", null, function AST_SymbolClassProperty(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol for a class property", |
|
}, AST_Symbol); |
|
|
|
var AST_SymbolLambda = DEFNODE("SymbolLambda", null, function AST_SymbolLambda(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol naming a function expression", |
|
}, AST_SymbolDeclaration); |
|
|
|
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, function AST_SymbolDefClass(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class." |
|
}, AST_SymbolBlockDeclaration); |
|
|
|
var AST_SymbolClass = DEFNODE("SymbolClass", null, function AST_SymbolClass(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol naming a class's name. Lexically scoped to the class." |
|
}, AST_SymbolDeclaration); |
|
|
|
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, function AST_SymbolCatch(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol naming the exception in catch", |
|
}, AST_SymbolBlockDeclaration); |
|
|
|
var AST_SymbolImport = DEFNODE("SymbolImport", null, function AST_SymbolImport(props) { |
|
if (props) { |
|
this.init = props.init; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol referring to an imported name", |
|
}, AST_SymbolBlockDeclaration); |
|
|
|
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", "quote", function AST_SymbolImportForeign(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes", |
|
}, AST_Symbol); |
|
|
|
var AST_Label = DEFNODE("Label", "references", function AST_Label(props) { |
|
if (props) { |
|
this.references = props.references; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this.initialize(); |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol naming a label (declaration)", |
|
$propdoc: { |
|
references: "[AST_LoopControl*] a list of nodes referring to this label" |
|
}, |
|
initialize: function() { |
|
this.references = []; |
|
this.thedef = this; |
|
} |
|
}, AST_Symbol); |
|
|
|
var AST_SymbolRef = DEFNODE("SymbolRef", null, function AST_SymbolRef(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Reference to some symbol (not definition/declaration)", |
|
}, AST_Symbol); |
|
|
|
var AST_SymbolExport = DEFNODE("SymbolExport", "quote", function AST_SymbolExport(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Symbol referring to a name to export", |
|
}, AST_SymbolRef); |
|
|
|
var AST_SymbolExportForeign = DEFNODE("SymbolExportForeign", "quote", function AST_SymbolExportForeign(props) { |
|
if (props) { |
|
this.quote = props.quote; |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A symbol exported from this module, but it is used in the other module, and its real name is irrelevant for this module's purposes", |
|
}, AST_Symbol); |
|
|
|
var AST_LabelRef = DEFNODE("LabelRef", null, function AST_LabelRef(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Reference to a label symbol", |
|
}, AST_Symbol); |
|
|
|
var AST_SymbolPrivateProperty = DEFNODE("SymbolPrivateProperty", null, function AST_SymbolPrivateProperty(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A symbol that refers to a private property", |
|
}, AST_Symbol); |
|
|
|
var AST_This = DEFNODE("This", null, function AST_This(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `this` symbol", |
|
}, AST_Symbol); |
|
|
|
var AST_Super = DEFNODE("Super", null, function AST_Super(props) { |
|
if (props) { |
|
this.scope = props.scope; |
|
this.name = props.name; |
|
this.thedef = props.thedef; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `super` symbol", |
|
}, AST_This); |
|
|
|
var AST_Constant = DEFNODE("Constant", null, function AST_Constant(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for all constants", |
|
getValue: function() { |
|
return this.value; |
|
} |
|
}); |
|
|
|
var AST_String = DEFNODE("String", "value quote", function AST_String(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.quote = props.quote; |
|
this.start = props.start; |
|
this.end = props.end; |
|
this._annotations = props._annotations; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A string literal", |
|
$propdoc: { |
|
value: "[string] the contents of this string", |
|
quote: "[string] the original quote character" |
|
} |
|
}, AST_Constant); |
|
|
|
var AST_Number = DEFNODE("Number", "value raw", function AST_Number(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.raw = props.raw; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A number literal", |
|
$propdoc: { |
|
value: "[number] the numeric value", |
|
raw: "[string] numeric value as string" |
|
} |
|
}, AST_Constant); |
|
|
|
var AST_BigInt = DEFNODE("BigInt", "value raw", function AST_BigInt(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.raw = props.raw; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A big int literal", |
|
$propdoc: { |
|
value: "[string] big int value, represented as a string", |
|
raw: "[string] the original format preserved" |
|
} |
|
}, AST_Constant); |
|
|
|
var AST_RegExp = DEFNODE("RegExp", "value", function AST_RegExp(props) { |
|
if (props) { |
|
this.value = props.value; |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A regexp literal", |
|
$propdoc: { |
|
value: "[RegExp] the actual regexp", |
|
} |
|
}, AST_Constant); |
|
|
|
var AST_Atom = DEFNODE("Atom", null, function AST_Atom(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for atoms", |
|
}, AST_Constant); |
|
|
|
var AST_Null = DEFNODE("Null", null, function AST_Null(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `null` atom", |
|
value: null |
|
}, AST_Atom); |
|
|
|
var AST_NaN = DEFNODE("NaN", null, function AST_NaN(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The impossible value", |
|
value: 0/0 |
|
}, AST_Atom); |
|
|
|
var AST_Undefined = DEFNODE("Undefined", null, function AST_Undefined(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `undefined` value", |
|
value: (function() {}()) |
|
}, AST_Atom); |
|
|
|
var AST_Hole = DEFNODE("Hole", null, function AST_Hole(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "A hole in an array", |
|
value: (function() {}()) |
|
}, AST_Atom); |
|
|
|
var AST_Infinity = DEFNODE("Infinity", null, function AST_Infinity(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `Infinity` value", |
|
value: 1/0 |
|
}, AST_Atom); |
|
|
|
var AST_Boolean = DEFNODE("Boolean", null, function AST_Boolean(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "Base class for booleans", |
|
}, AST_Atom); |
|
|
|
var AST_False = DEFNODE("False", null, function AST_False(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `false` atom", |
|
value: false |
|
}, AST_Boolean); |
|
|
|
var AST_True = DEFNODE("True", null, function AST_True(props) { |
|
if (props) { |
|
this.start = props.start; |
|
this.end = props.end; |
|
} |
|
|
|
this.flags = 0; |
|
}, { |
|
$documentation: "The `true` atom", |
|
value: true |
|
}, AST_Boolean); |
|
|
|
/* -----[ Walk function ]---- */ |
|
|
|
/** |
|
* Walk nodes in depth-first search fashion. |
|
* Callback can return `walk_abort` symbol to stop iteration. |
|
* It can also return `true` to stop iteration just for child nodes. |
|
* Iteration can be stopped and continued by passing the `to_visit` argument, |
|
* which is given to the callback in the second argument. |
|
**/ |
|
function walk(node, cb, to_visit = [node]) { |
|
const push = to_visit.push.bind(to_visit); |
|
while (to_visit.length) { |
|
const node = to_visit.pop(); |
|
const ret = cb(node, to_visit); |
|
|
|
if (ret) { |
|
if (ret === walk_abort) return true; |
|
continue; |
|
} |
|
|
|
node._children_backwards(push); |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Walks an AST node and its children. |
|
* |
|
* {cb} can return `walk_abort` to interrupt the walk. |
|
* |
|
* @param node |
|
* @param cb {(node, info: { parent: (nth) => any }) => (boolean | undefined)} |
|
* |
|
* @returns {boolean} whether the walk was aborted |
|
* |
|
* @example |
|
* const found_some_cond = walk_parent(my_ast_node, (node, { parent }) => { |
|
* if (some_cond(node, parent())) return walk_abort |
|
* }); |
|
*/ |
|
function walk_parent(node, cb, initial_stack) { |
|
const to_visit = [node]; |
|
const push = to_visit.push.bind(to_visit); |
|
const stack = initial_stack ? initial_stack.slice() : []; |
|
const parent_pop_indices = []; |
|
|
|
let current; |
|
|
|
const info = { |
|
parent: (n = 0) => { |
|
if (n === -1) { |
|
return current; |
|
} |
|
|
|
// [ p1 p0 ] [ 1 0 ] |
|
if (initial_stack && n >= stack.length) { |
|
n -= stack.length; |
|
return initial_stack[ |
|
initial_stack.length - (n + 1) |
|
]; |
|
} |
|
|
|
return stack[stack.length - (1 + n)]; |
|
}, |
|
}; |
|
|
|
while (to_visit.length) { |
|
current = to_visit.pop(); |
|
|
|
while ( |
|
parent_pop_indices.length && |
|
to_visit.length == parent_pop_indices[parent_pop_indices.length - 1] |
|
) { |
|
stack.pop(); |
|
parent_pop_indices.pop(); |
|
} |
|
|
|
const ret = cb(current, info); |
|
|
|
if (ret) { |
|
if (ret === walk_abort) return true; |
|
continue; |
|
} |
|
|
|
const visit_length = to_visit.length; |
|
|
|
current._children_backwards(push); |
|
|
|
// Push only if we're going to traverse the children |
|
if (to_visit.length > visit_length) { |
|
stack.push(current); |
|
parent_pop_indices.push(visit_length - 1); |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
const walk_abort = Symbol("abort walk"); |
|
|
|
/* -----[ TreeWalker ]----- */ |
|
|
|
class TreeWalker { |
|
constructor(callback) { |
|
this.visit = callback; |
|
this.stack = []; |
|
this.directives = Object.create(null); |
|
} |
|
|
|
_visit(node, descend) { |
|
this.push(node); |
|
var ret = this.visit(node, descend ? function() { |
|
descend.call(node); |
|
} : noop); |
|
if (!ret && descend) { |
|
descend.call(node); |
|
} |
|
this.pop(); |
|
return ret; |
|
} |
|
|
|
parent(n) { |
|
return this.stack[this.stack.length - 2 - (n || 0)]; |
|
} |
|
|
|
push(node) { |
|
if (node instanceof AST_Lambda) { |
|
this.directives = Object.create(this.directives); |
|
} else if (node instanceof AST_Directive && !this.directives[node.value]) { |
|
this.directives[node.value] = node; |
|
} else if (node instanceof AST_Class) { |
|
this.directives = Object.create(this.directives); |
|
if (!this.directives["use strict"]) { |
|
this.directives["use strict"] = node; |
|
} |
|
} |
|
this.stack.push(node); |
|
} |
|
|
|
pop() { |
|
var node = this.stack.pop(); |
|
if (node instanceof AST_Lambda || node instanceof AST_Class) { |
|
this.directives = Object.getPrototypeOf(this.directives); |
|
} |
|
} |
|
|
|
self() { |
|
return this.stack[this.stack.length - 1]; |
|
} |
|
|
|
find_parent(type) { |
|
var stack = this.stack; |
|
for (var i = stack.length; --i >= 0;) { |
|
var x = stack[i]; |
|
if (x instanceof type) return x; |
|
} |
|
} |
|
|
|
is_within_loop() { |
|
let i = this.stack.length - 1; |
|
let child = this.stack[i]; |
|
while (i--) { |
|
const node = this.stack[i]; |
|
|
|
if (node instanceof AST_Lambda) return false; |
|
if ( |
|
node instanceof AST_IterationStatement |
|
// exclude for-loop bits that only run once |
|
&& !((node instanceof AST_For) && child === node.init) |
|
&& !((node instanceof AST_ForIn || node instanceof AST_ForOf) && child === node.object) |
|
) { |
|
return true; |
|
} |
|
|
|
child = node; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
find_scope() { |
|
var stack = this.stack; |
|
for (var i = stack.length; --i >= 0;) { |
|
const p = stack[i]; |
|
if (p instanceof AST_Toplevel) return p; |
|
if (p instanceof AST_Lambda) return p; |
|
if (p.block_scope) return p.block_scope; |
|
} |
|
} |
|
|
|
has_directive(type) { |
|
var dir = this.directives[type]; |
|
if (dir) return dir; |
|
var node = this.stack[this.stack.length - 1]; |
|
if (node instanceof AST_Scope && node.body) { |
|
for (var i = 0; i < node.body.length; ++i) { |
|
var st = node.body[i]; |
|
if (!(st instanceof AST_Directive)) break; |
|
if (st.value == type) return st; |
|
} |
|
} |
|
} |
|
|
|
loopcontrol_target(node) { |
|
var stack = this.stack; |
|
if (node.label) for (var i = stack.length; --i >= 0;) { |
|
var x = stack[i]; |
|
if (x instanceof AST_LabeledStatement && x.label.name == node.label.name) |
|
return x.body; |
|
} else for (var i = stack.length; --i >= 0;) { |
|
var x = stack[i]; |
|
if (x instanceof AST_IterationStatement |
|
|| node instanceof AST_Break && x instanceof AST_Switch) |
|
return x; |
|
} |
|
} |
|
} |
|
|
|
// Tree transformer helpers. |
|
class TreeTransformer extends TreeWalker { |
|
constructor(before, after) { |
|
super(); |
|
this.before = before; |
|
this.after = after; |
|
} |
|
} |
|
|
|
const _PURE = 0b00000001; |
|
const _INLINE = 0b00000010; |
|
const _NOINLINE = 0b00000100; |
|
const _KEY = 0b00001000; |
|
const _MANGLEPROP = 0b00010000; |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
function def_transform(node, descend) { |
|
node.DEFMETHOD("transform", function(tw, in_list) { |
|
let transformed = undefined; |
|
tw.push(this); |
|
if (tw.before) transformed = tw.before(this, descend, in_list); |
|
if (transformed === undefined) { |
|
transformed = this; |
|
descend(transformed, tw); |
|
if (tw.after) { |
|
const after_ret = tw.after(transformed, in_list); |
|
if (after_ret !== undefined) transformed = after_ret; |
|
} |
|
} |
|
tw.pop(); |
|
return transformed; |
|
}); |
|
} |
|
|
|
def_transform(AST_Node, noop); |
|
|
|
def_transform(AST_LabeledStatement, function(self, tw) { |
|
self.label = self.label.transform(tw); |
|
self.body = self.body.transform(tw); |
|
}); |
|
|
|
def_transform(AST_SimpleStatement, function(self, tw) { |
|
self.body = self.body.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Block, function(self, tw) { |
|
self.body = MAP(self.body, tw); |
|
}); |
|
|
|
def_transform(AST_Do, function(self, tw) { |
|
self.body = self.body.transform(tw); |
|
self.condition = self.condition.transform(tw); |
|
}); |
|
|
|
def_transform(AST_While, function(self, tw) { |
|
self.condition = self.condition.transform(tw); |
|
self.body = self.body.transform(tw); |
|
}); |
|
|
|
def_transform(AST_For, function(self, tw) { |
|
if (self.init) self.init = self.init.transform(tw); |
|
if (self.condition) self.condition = self.condition.transform(tw); |
|
if (self.step) self.step = self.step.transform(tw); |
|
self.body = self.body.transform(tw); |
|
}); |
|
|
|
def_transform(AST_ForIn, function(self, tw) { |
|
self.init = self.init.transform(tw); |
|
self.object = self.object.transform(tw); |
|
self.body = self.body.transform(tw); |
|
}); |
|
|
|
def_transform(AST_With, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
self.body = self.body.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Exit, function(self, tw) { |
|
if (self.value) self.value = self.value.transform(tw); |
|
}); |
|
|
|
def_transform(AST_LoopControl, function(self, tw) { |
|
if (self.label) self.label = self.label.transform(tw); |
|
}); |
|
|
|
def_transform(AST_If, function(self, tw) { |
|
self.condition = self.condition.transform(tw); |
|
self.body = self.body.transform(tw); |
|
if (self.alternative) self.alternative = self.alternative.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Switch, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
self.body = MAP(self.body, tw); |
|
}); |
|
|
|
def_transform(AST_Case, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
self.body = MAP(self.body, tw); |
|
}); |
|
|
|
def_transform(AST_Try, function(self, tw) { |
|
self.body = self.body.transform(tw); |
|
if (self.bcatch) self.bcatch = self.bcatch.transform(tw); |
|
if (self.bfinally) self.bfinally = self.bfinally.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Catch, function(self, tw) { |
|
if (self.argname) self.argname = self.argname.transform(tw); |
|
self.body = MAP(self.body, tw); |
|
}); |
|
|
|
def_transform(AST_DefinitionsLike, function(self, tw) { |
|
self.definitions = MAP(self.definitions, tw); |
|
}); |
|
|
|
def_transform(AST_VarDefLike, function(self, tw) { |
|
self.name = self.name.transform(tw); |
|
if (self.value) self.value = self.value.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Destructuring, function(self, tw) { |
|
self.names = MAP(self.names, tw); |
|
}); |
|
|
|
def_transform(AST_Lambda, function(self, tw) { |
|
if (self.name) self.name = self.name.transform(tw); |
|
self.argnames = MAP(self.argnames, tw, /* allow_splicing */ false); |
|
if (self.body instanceof AST_Node) { |
|
self.body = self.body.transform(tw); |
|
} else { |
|
self.body = MAP(self.body, tw); |
|
} |
|
}); |
|
|
|
def_transform(AST_Call, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
self.args = MAP(self.args, tw, /* allow_splicing */ false); |
|
}); |
|
|
|
def_transform(AST_Sequence, function(self, tw) { |
|
const result = MAP(self.expressions, tw); |
|
self.expressions = result.length |
|
? result |
|
: [new AST_Number({ value: 0 })]; |
|
}); |
|
|
|
def_transform(AST_PropAccess, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Sub, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
self.property = self.property.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Chain, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Yield, function(self, tw) { |
|
if (self.expression) self.expression = self.expression.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Await, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Unary, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Binary, function(self, tw) { |
|
self.left = self.left.transform(tw); |
|
self.right = self.right.transform(tw); |
|
}); |
|
|
|
def_transform(AST_PrivateIn, function(self, tw) { |
|
self.key = self.key.transform(tw); |
|
self.value = self.value.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Conditional, function(self, tw) { |
|
self.condition = self.condition.transform(tw); |
|
self.consequent = self.consequent.transform(tw); |
|
self.alternative = self.alternative.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Array, function(self, tw) { |
|
self.elements = MAP(self.elements, tw); |
|
}); |
|
|
|
def_transform(AST_Object, function(self, tw) { |
|
self.properties = MAP(self.properties, tw); |
|
}); |
|
|
|
def_transform(AST_ObjectProperty, function(self, tw) { |
|
if (self.key instanceof AST_Node) { |
|
self.key = self.key.transform(tw); |
|
} |
|
if (self.value) self.value = self.value.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Class, function(self, tw) { |
|
if (self.name) self.name = self.name.transform(tw); |
|
if (self.extends) self.extends = self.extends.transform(tw); |
|
self.properties = MAP(self.properties, tw); |
|
}); |
|
|
|
def_transform(AST_ClassStaticBlock, function(self, tw) { |
|
self.body = MAP(self.body, tw); |
|
}); |
|
|
|
def_transform(AST_Expansion, function(self, tw) { |
|
self.expression = self.expression.transform(tw); |
|
}); |
|
|
|
def_transform(AST_NameMapping, function(self, tw) { |
|
self.foreign_name = self.foreign_name.transform(tw); |
|
self.name = self.name.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Import, function(self, tw) { |
|
if (self.imported_name) self.imported_name = self.imported_name.transform(tw); |
|
if (self.imported_names) MAP(self.imported_names, tw); |
|
self.module_name = self.module_name.transform(tw); |
|
}); |
|
|
|
def_transform(AST_Export, function(self, tw) { |
|
if (self.exported_definition) self.exported_definition = self.exported_definition.transform(tw); |
|
if (self.exported_value) self.exported_value = self.exported_value.transform(tw); |
|
if (self.exported_names) MAP(self.exported_names, tw); |
|
if (self.module_name) self.module_name = self.module_name.transform(tw); |
|
}); |
|
|
|
def_transform(AST_TemplateString, function(self, tw) { |
|
self.segments = MAP(self.segments, tw); |
|
}); |
|
|
|
def_transform(AST_PrefixedTemplateString, function(self, tw) { |
|
self.prefix = self.prefix.transform(tw); |
|
self.template_string = self.template_string.transform(tw); |
|
}); |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
(function() { |
|
|
|
var normalize_directives = function(body) { |
|
for (var i = 0; i < body.length; i++) { |
|
if (body[i] instanceof AST_Statement && body[i].body instanceof AST_String) { |
|
body[i] = new AST_Directive({ |
|
start: body[i].start, |
|
end: body[i].end, |
|
quote: '"', |
|
value: body[i].body.value |
|
}); |
|
} else { |
|
return body; |
|
} |
|
} |
|
|
|
return body; |
|
}; |
|
|
|
function import_attributes_from_moz(attributes) { |
|
if (attributes && attributes.length > 0) { |
|
return new AST_Object({ |
|
start: my_start_token(attributes), |
|
end: my_end_token(attributes), |
|
properties: attributes.map((attr) => |
|
new AST_ObjectKeyVal({ |
|
start: my_start_token(attr), |
|
end: my_end_token(attr), |
|
key: attr.key.name || attr.key.value, |
|
value: from_moz(attr.value) |
|
}) |
|
) |
|
}); |
|
} |
|
return null; |
|
} |
|
|
|
var MOZ_TO_ME = { |
|
Program: function(M) { |
|
return new AST_Toplevel({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
body: normalize_directives(M.body.map(from_moz)) |
|
}); |
|
}, |
|
|
|
ArrayPattern: function(M) { |
|
return new AST_Destructuring({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
names: M.elements.map(function(elm) { |
|
if (elm === null) { |
|
return new AST_Hole(); |
|
} |
|
return from_moz(elm); |
|
}), |
|
is_array: true |
|
}); |
|
}, |
|
|
|
ObjectPattern: function(M) { |
|
return new AST_Destructuring({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
names: M.properties.map(from_moz), |
|
is_array: false |
|
}); |
|
}, |
|
|
|
AssignmentPattern: function(M) { |
|
return new AST_DefaultAssign({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
left: from_moz(M.left), |
|
operator: "=", |
|
right: from_moz(M.right) |
|
}); |
|
}, |
|
|
|
SpreadElement: function(M) { |
|
return new AST_Expansion({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.argument) |
|
}); |
|
}, |
|
|
|
RestElement: function(M) { |
|
return new AST_Expansion({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.argument) |
|
}); |
|
}, |
|
|
|
TemplateElement: function(M) { |
|
return new AST_TemplateSegment({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
value: M.value.cooked, |
|
raw: M.value.raw |
|
}); |
|
}, |
|
|
|
TemplateLiteral: function(M) { |
|
var segments = []; |
|
for (var i = 0; i < M.quasis.length; i++) { |
|
segments.push(from_moz(M.quasis[i])); |
|
if (M.expressions[i]) { |
|
segments.push(from_moz(M.expressions[i])); |
|
} |
|
} |
|
return new AST_TemplateString({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
segments: segments |
|
}); |
|
}, |
|
|
|
TaggedTemplateExpression: function(M) { |
|
return new AST_PrefixedTemplateString({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
template_string: from_moz(M.quasi), |
|
prefix: from_moz(M.tag) |
|
}); |
|
}, |
|
|
|
FunctionDeclaration: function(M) { |
|
return new AST_Defun({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
name: M.id && from_moz_symbol(AST_SymbolDefun, M.id), |
|
argnames: M.params.map(M => from_moz_pattern(M, AST_SymbolFunarg)), |
|
is_generator: M.generator, |
|
async: M.async, |
|
body: normalize_directives(from_moz(M.body).body) |
|
}); |
|
}, |
|
|
|
FunctionExpression: function(M) { |
|
return from_moz_lambda(M, /*is_method=*/false); |
|
}, |
|
|
|
ArrowFunctionExpression: function(M) { |
|
const body = M.body.type === "BlockStatement" |
|
? from_moz(M.body).body |
|
: [make_node(AST_Return, {}, { value: from_moz(M.body) })]; |
|
return new AST_Arrow({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
argnames: M.params.map(p => from_moz_pattern(p, AST_SymbolFunarg)), |
|
body, |
|
async: M.async, |
|
}); |
|
}, |
|
|
|
ExpressionStatement: function(M) { |
|
return new AST_SimpleStatement({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
body: from_moz(M.expression) |
|
}); |
|
}, |
|
|
|
TryStatement: function(M) { |
|
var handlers = M.handlers || [M.handler]; |
|
if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) { |
|
throw new Error("Multiple catch clauses are not supported."); |
|
} |
|
return new AST_Try({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
body : new AST_TryBlock(from_moz(M.block)), |
|
bcatch : from_moz(handlers[0]), |
|
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null |
|
}); |
|
}, |
|
|
|
Property: function(M) { |
|
if (M.kind == "init" && !M.method) { |
|
var args = { |
|
start : my_start_token(M.key || M.value), |
|
end : my_end_token(M.value), |
|
key : M.computed |
|
? from_moz(M.key) |
|
: M.key.name || String(M.key.value), |
|
quote : from_moz_quote(M.key, M.computed), |
|
static : false, // always an object |
|
value : from_moz(M.value) |
|
}; |
|
|
|
return new AST_ObjectKeyVal(args); |
|
} else { |
|
var value = from_moz_lambda(M.value, /*is_method=*/true); |
|
var args = { |
|
start : my_start_token(M.key || M.value), |
|
end : my_end_token(M.value), |
|
key : M.computed |
|
? from_moz(M.key) |
|
: from_moz_symbol(AST_SymbolMethod, M.key), |
|
quote : from_moz_quote(M.key, M.computed), |
|
static : false, // always an object |
|
value, |
|
}; |
|
|
|
if (M.kind == "get") return new AST_ObjectGetter(args); |
|
if (M.kind == "set") return new AST_ObjectSetter(args); |
|
if (M.method) return new AST_ConciseMethod(args); |
|
} |
|
}, |
|
|
|
MethodDefinition: function(M) { |
|
const is_private = M.key.type === "PrivateIdentifier"; |
|
const key = M.computed ? from_moz(M.key) : new AST_SymbolMethod({ name: M.key.name || String(M.key.value) }); |
|
|
|
var args = { |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
key, |
|
quote : from_moz_quote(M.key, M.computed), |
|
value : from_moz_lambda(M.value, /*is_method=*/true), |
|
static : M.static, |
|
}; |
|
if (M.kind == "get") { |
|
return new (is_private ? AST_PrivateGetter : AST_ObjectGetter)(args); |
|
} |
|
if (M.kind == "set") { |
|
return new (is_private ? AST_PrivateSetter : AST_ObjectSetter)(args); |
|
} |
|
return new (is_private ? AST_PrivateMethod : AST_ConciseMethod)(args); |
|
}, |
|
|
|
FieldDefinition: function(M) { |
|
let key; |
|
if (M.computed) { |
|
key = from_moz(M.key); |
|
} else { |
|
if (M.key.type !== "Identifier") throw new Error("Non-Identifier key in FieldDefinition"); |
|
key = from_moz(M.key); |
|
} |
|
return new AST_ClassProperty({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
quote : from_moz_quote(M.key, M.computed), |
|
key, |
|
value : from_moz(M.value), |
|
static : M.static, |
|
}); |
|
}, |
|
|
|
PropertyDefinition: function(M) { |
|
let key; |
|
if (M.computed) { |
|
key = from_moz(M.key); |
|
} else if (M.key.type === "PrivateIdentifier") { |
|
return new AST_ClassPrivateProperty({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
key : from_moz(M.key), |
|
value : from_moz(M.value), |
|
static : M.static, |
|
}); |
|
} else { |
|
key = from_moz_symbol(AST_SymbolClassProperty, M.key); |
|
} |
|
|
|
return new AST_ClassProperty({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
quote : from_moz_quote(M.key, M.computed), |
|
key, |
|
value : from_moz(M.value), |
|
static : M.static, |
|
}); |
|
}, |
|
|
|
PrivateIdentifier: function (M) { |
|
return new AST_SymbolPrivateProperty({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
name: M.name |
|
}); |
|
}, |
|
|
|
StaticBlock: function(M) { |
|
return new AST_ClassStaticBlock({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
body : M.body.map(from_moz), |
|
}); |
|
}, |
|
|
|
ArrayExpression: function(M) { |
|
return new AST_Array({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
elements : M.elements.map(function(elem) { |
|
return elem === null ? new AST_Hole() : from_moz(elem); |
|
}) |
|
}); |
|
}, |
|
|
|
ObjectExpression: function(M) { |
|
return new AST_Object({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
properties : M.properties.map(function(prop) { |
|
if (prop.type === "SpreadElement") { |
|
return from_moz(prop); |
|
} |
|
prop.type = "Property"; |
|
return from_moz(prop); |
|
}) |
|
}); |
|
}, |
|
|
|
SequenceExpression: function(M) { |
|
return new AST_Sequence({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
expressions: M.expressions.map(from_moz) |
|
}); |
|
}, |
|
|
|
MemberExpression: function(M) { |
|
if (M.property.type === "PrivateIdentifier") { |
|
return new AST_DotHash({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
property : M.property.name, |
|
expression : from_moz(M.object), |
|
optional : M.optional || false |
|
}); |
|
} |
|
return new (M.computed ? AST_Sub : AST_Dot)({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
property : M.computed ? from_moz(M.property) : M.property.name, |
|
expression : from_moz(M.object), |
|
optional : M.optional || false |
|
}); |
|
}, |
|
|
|
ChainExpression: function(M) { |
|
return new AST_Chain({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
expression : from_moz(M.expression) |
|
}); |
|
}, |
|
|
|
SwitchCase: function(M) { |
|
return new (M.test ? AST_Case : AST_Default)({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
expression : from_moz(M.test), |
|
body : M.consequent.map(from_moz) |
|
}); |
|
}, |
|
|
|
VariableDeclaration: function(M) { |
|
let decl_type; |
|
let defs_type = AST_VarDef; |
|
let sym_type; |
|
let await_using = false; |
|
if (M.kind === "const") { |
|
decl_type = AST_Const; |
|
sym_type = AST_SymbolConst; |
|
} else if (M.kind === "let") { |
|
decl_type = AST_Let; |
|
sym_type = AST_SymbolLet; |
|
} else if (M.kind === "using") { |
|
decl_type = AST_Using; |
|
defs_type = AST_UsingDef; |
|
sym_type = AST_SymbolUsing; |
|
} else if (M.kind === "await using") { |
|
decl_type = AST_Using; |
|
defs_type = AST_UsingDef; |
|
sym_type = AST_SymbolUsing; |
|
await_using = true; |
|
} else { |
|
decl_type = AST_Var; |
|
sym_type = AST_SymbolVar; |
|
} |
|
const definitions = M.declarations.map(M => { |
|
return new defs_type({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
name: from_moz_pattern(M.id, sym_type), |
|
value: from_moz(M.init), |
|
}); |
|
}); |
|
return new decl_type({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
definitions : definitions, |
|
await : await_using, |
|
}); |
|
}, |
|
|
|
ImportDeclaration: function(M) { |
|
var imported_name = null; |
|
var imported_names = null; |
|
M.specifiers.forEach(function (specifier) { |
|
if (specifier.type === "ImportSpecifier" || specifier.type === "ImportNamespaceSpecifier") { |
|
if (!imported_names) { imported_names = []; } |
|
imported_names.push(from_moz(specifier)); |
|
} else if (specifier.type === "ImportDefaultSpecifier") { |
|
imported_name = from_moz(specifier); |
|
} |
|
}); |
|
return new AST_Import({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
imported_name: imported_name, |
|
imported_names : imported_names, |
|
module_name : from_moz(M.source), |
|
attributes: import_attributes_from_moz(M.attributes || M.assertions) |
|
}); |
|
}, |
|
|
|
ImportSpecifier: function(M) { |
|
return new AST_NameMapping({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
foreign_name: from_moz_symbol(AST_SymbolImportForeign, M.imported, M.imported.type === "Literal"), |
|
name: from_moz_symbol(AST_SymbolImport, M.local) |
|
}); |
|
}, |
|
|
|
ImportDefaultSpecifier: function(M) { |
|
return from_moz_symbol(AST_SymbolImport, M.local); |
|
}, |
|
|
|
ImportNamespaceSpecifier: function(M) { |
|
return new AST_NameMapping({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
foreign_name: new AST_SymbolImportForeign({ name: "*" }), |
|
name: from_moz_symbol(AST_SymbolImport, M.local) |
|
}); |
|
}, |
|
|
|
ImportExpression: function(M) { |
|
const args = [from_moz(M.source)]; |
|
if (M.options) { |
|
args.push(from_moz(M.options)); |
|
} |
|
return new AST_Call({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz({ |
|
type: "Identifier", |
|
name: "import" |
|
}), |
|
optional: false, |
|
args |
|
}); |
|
}, |
|
|
|
ExportAllDeclaration: function(M) { |
|
var foreign_name = M.exported == null ? |
|
new AST_SymbolExportForeign({ name: "*" }) : |
|
from_moz_symbol(AST_SymbolExportForeign, M.exported, M.exported.type === "Literal"); |
|
return new AST_Export({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
exported_names: [ |
|
new AST_NameMapping({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
name: new AST_SymbolExport({ name: "*" }), |
|
foreign_name: foreign_name |
|
}) |
|
], |
|
module_name: from_moz(M.source), |
|
attributes: import_attributes_from_moz(M.attributes || M.assertions) |
|
}); |
|
}, |
|
|
|
ExportNamedDeclaration: function(M) { |
|
if (M.declaration) { |
|
// export const, export function, ... |
|
return new AST_Export({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
exported_definition: from_moz(M.declaration), |
|
exported_names: null, |
|
module_name: null, |
|
attributes: null, |
|
}); |
|
} else { |
|
return new AST_Export({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
exported_definition: null, |
|
exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(from_moz) : [], |
|
module_name: from_moz(M.source), |
|
attributes: import_attributes_from_moz(M.attributes || M.assertions), |
|
}); |
|
} |
|
}, |
|
|
|
ExportDefaultDeclaration: function(M) { |
|
return new AST_Export({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
exported_value: from_moz(M.declaration), |
|
is_default: true |
|
}); |
|
}, |
|
|
|
ExportSpecifier: function(M) { |
|
return new AST_NameMapping({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
foreign_name: from_moz_symbol(AST_SymbolExportForeign, M.exported, M.exported.type === "Literal"), |
|
name: from_moz_symbol(AST_SymbolExport, M.local, M.local.type === "Literal"), |
|
}); |
|
}, |
|
|
|
Literal: function(M) { |
|
var val = M.value, args = { |
|
start : my_start_token(M), |
|
end : my_end_token(M) |
|
}; |
|
var rx = M.regex; |
|
if (rx && rx.pattern) { |
|
// RegExpLiteral as per ESTree AST spec |
|
args.value = { |
|
source: rx.pattern, |
|
flags: rx.flags |
|
}; |
|
return new AST_RegExp(args); |
|
} else if (rx) { |
|
// support legacy RegExp |
|
const rx_source = M.raw || val; |
|
const match = rx_source.match(/^\/(.*)\/(\w*)$/); |
|
if (!match) throw new Error("Invalid regex source " + rx_source); |
|
const [_, source, flags] = match; |
|
args.value = { source, flags }; |
|
return new AST_RegExp(args); |
|
} |
|
const bi = typeof M.value === "bigint" ? M.value.toString() : M.bigint; |
|
if (typeof bi === "string") { |
|
args.value = bi; |
|
args.raw = M.raw; |
|
return new AST_BigInt(args); |
|
} |
|
if (val === null) return new AST_Null(args); |
|
switch (typeof val) { |
|
case "string": |
|
args.quote = "\""; |
|
args.value = val; |
|
return new AST_String(args); |
|
case "number": |
|
args.value = val; |
|
args.raw = M.raw || val.toString(); |
|
return new AST_Number(args); |
|
case "boolean": |
|
return new (val ? AST_True : AST_False)(args); |
|
} |
|
}, |
|
|
|
MetaProperty: function(M) { |
|
if (M.meta.name === "new" && M.property.name === "target") { |
|
return new AST_NewTarget({ |
|
start: my_start_token(M), |
|
end: my_end_token(M) |
|
}); |
|
} else if (M.meta.name === "import" && M.property.name === "meta") { |
|
return new AST_ImportMeta({ |
|
start: my_start_token(M), |
|
end: my_end_token(M) |
|
}); |
|
} |
|
}, |
|
|
|
Identifier: function(M) { |
|
return new AST_SymbolRef({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
name : M.name |
|
}); |
|
}, |
|
|
|
EmptyStatement: function(M) { |
|
return new AST_EmptyStatement({ |
|
start: my_start_token(M), |
|
end: my_end_token(M) |
|
}); |
|
}, |
|
|
|
BlockStatement: function(M) { |
|
return new AST_BlockStatement({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
body: M.body.map(from_moz) |
|
}); |
|
}, |
|
|
|
IfStatement: function(M) { |
|
return new AST_If({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
condition: from_moz(M.test), |
|
body: from_moz(M.consequent), |
|
alternative: from_moz(M.alternate) |
|
}); |
|
}, |
|
|
|
LabeledStatement: function(M) { |
|
try { |
|
const label = from_moz_symbol(AST_Label, M.label); |
|
FROM_MOZ_LABELS.push(label); |
|
|
|
const stat = new AST_LabeledStatement({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
label, |
|
body: from_moz(M.body) |
|
}); |
|
|
|
return stat; |
|
} finally { |
|
FROM_MOZ_LABELS.pop(); |
|
} |
|
}, |
|
|
|
BreakStatement: function(M) { |
|
return new AST_Break({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
label: from_moz_label_ref(M.label), |
|
}); |
|
}, |
|
|
|
ContinueStatement: function(M) { |
|
return new AST_Continue({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
label: from_moz_label_ref(M.label), |
|
}); |
|
}, |
|
|
|
WithStatement: function(M) { |
|
return new AST_With({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.object), |
|
body: from_moz(M.body) |
|
}); |
|
}, |
|
|
|
SwitchStatement: function(M) { |
|
return new AST_Switch({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.discriminant), |
|
body: M.cases.map(from_moz) |
|
}); |
|
}, |
|
|
|
ReturnStatement: function(M) { |
|
return new AST_Return({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
value: from_moz(M.argument) |
|
}); |
|
}, |
|
|
|
ThrowStatement: function(M) { |
|
return new AST_Throw({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
value: from_moz(M.argument) |
|
}); |
|
}, |
|
|
|
WhileStatement: function(M) { |
|
return new AST_While({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
condition: from_moz(M.test), |
|
body: from_moz(M.body) |
|
}); |
|
}, |
|
|
|
DoWhileStatement: function(M) { |
|
return new AST_Do({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
condition: from_moz(M.test), |
|
body: from_moz(M.body) |
|
}); |
|
}, |
|
|
|
ForStatement: function(M) { |
|
return new AST_For({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
init: from_moz(M.init), |
|
condition: from_moz(M.test), |
|
step: from_moz(M.update), |
|
body: from_moz(M.body) |
|
}); |
|
}, |
|
|
|
ForInStatement: function(M) { |
|
return new AST_ForIn({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
init: from_moz(M.left), |
|
object: from_moz(M.right), |
|
body: from_moz(M.body) |
|
}); |
|
}, |
|
|
|
ForOfStatement: function(M) { |
|
return new AST_ForOf({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
init: from_moz(M.left), |
|
object: from_moz(M.right), |
|
body: from_moz(M.body), |
|
await: M.await |
|
}); |
|
}, |
|
|
|
AwaitExpression: function(M) { |
|
return new AST_Await({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.argument) |
|
}); |
|
}, |
|
|
|
YieldExpression: function(M) { |
|
return new AST_Yield({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.argument), |
|
is_star: M.delegate |
|
}); |
|
}, |
|
|
|
DebuggerStatement: function(M) { |
|
return new AST_Debugger({ |
|
start: my_start_token(M), |
|
end: my_end_token(M) |
|
}); |
|
}, |
|
|
|
CatchClause: function(M) { |
|
return new AST_Catch({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
argname: M.param ? from_moz_pattern(M.param, AST_SymbolCatch) : null, |
|
body: from_moz(M.body).body |
|
}); |
|
}, |
|
|
|
ThisExpression: function(M) { |
|
return new AST_This({ |
|
start: my_start_token(M), |
|
name: "this", |
|
end: my_end_token(M) |
|
}); |
|
}, |
|
|
|
Super: function(M) { |
|
return new AST_Super({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
name: "super", |
|
}); |
|
}, |
|
|
|
BinaryExpression: function(M) { |
|
if (M.left.type === "PrivateIdentifier") { |
|
return new AST_PrivateIn({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
key: new AST_SymbolPrivateProperty({ |
|
start: my_start_token(M.left), |
|
end: my_end_token(M.left), |
|
name: M.left.name |
|
}), |
|
value: from_moz(M.right), |
|
}); |
|
} |
|
return new AST_Binary({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
operator: M.operator, |
|
left: from_moz(M.left), |
|
right: from_moz(M.right) |
|
}); |
|
}, |
|
|
|
LogicalExpression: function(M) { |
|
return new AST_Binary({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
operator: M.operator, |
|
left: from_moz(M.left), |
|
right: from_moz(M.right) |
|
}); |
|
}, |
|
|
|
AssignmentExpression: function(M) { |
|
return new AST_Assign({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
operator: M.operator, |
|
logical: M.operator === "??=" || M.operator === "&&=" || M.operator === "||=", |
|
left: from_moz(M.left), |
|
right: from_moz(M.right) |
|
}); |
|
}, |
|
|
|
ConditionalExpression: function(M) { |
|
return new AST_Conditional({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
condition: from_moz(M.test), |
|
consequent: from_moz(M.consequent), |
|
alternative: from_moz(M.alternate) |
|
}); |
|
}, |
|
|
|
NewExpression: function(M) { |
|
return new AST_New({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.callee), |
|
args: M.arguments.map(from_moz) |
|
}); |
|
}, |
|
|
|
CallExpression: function(M) { |
|
return new AST_Call({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz(M.callee), |
|
optional: M.optional, |
|
args: M.arguments.map(from_moz) |
|
}); |
|
} |
|
}; |
|
|
|
MOZ_TO_ME.UpdateExpression = |
|
MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) { |
|
var prefix = "prefix" in M ? M.prefix |
|
: M.type == "UnaryExpression" ? true : false; |
|
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
operator : M.operator, |
|
expression : from_moz(M.argument) |
|
}); |
|
}; |
|
|
|
MOZ_TO_ME.ClassDeclaration = |
|
MOZ_TO_ME.ClassExpression = function From_Moz_Class(M) { |
|
return new (M.type === "ClassDeclaration" ? AST_DefClass : AST_ClassExpression)({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
name : M.id && from_moz_symbol(M.type === "ClassDeclaration" ? AST_SymbolDefClass : AST_SymbolClass, M.id), |
|
extends : from_moz(M.superClass), |
|
properties: M.body.body.map(from_moz) |
|
}); |
|
}; |
|
|
|
def_to_moz(AST_EmptyStatement, function To_Moz_EmptyStatement() { |
|
return { |
|
type: "EmptyStatement" |
|
}; |
|
}); |
|
def_to_moz(AST_BlockStatement, function To_Moz_BlockStatement(M) { |
|
return { |
|
type: "BlockStatement", |
|
body: M.body.map(to_moz) |
|
}; |
|
}); |
|
def_to_moz(AST_If, function To_Moz_IfStatement(M) { |
|
return { |
|
type: "IfStatement", |
|
test: to_moz(M.condition), |
|
consequent: to_moz(M.body), |
|
alternate: to_moz(M.alternative) |
|
}; |
|
}); |
|
def_to_moz(AST_LabeledStatement, function To_Moz_LabeledStatement(M) { |
|
return { |
|
type: "LabeledStatement", |
|
label: to_moz(M.label), |
|
body: to_moz(M.body) |
|
}; |
|
}); |
|
def_to_moz(AST_Break, function To_Moz_BreakStatement(M) { |
|
return { |
|
type: "BreakStatement", |
|
label: to_moz(M.label) |
|
}; |
|
}); |
|
def_to_moz(AST_Continue, function To_Moz_ContinueStatement(M) { |
|
return { |
|
type: "ContinueStatement", |
|
label: to_moz(M.label) |
|
}; |
|
}); |
|
def_to_moz(AST_With, function To_Moz_WithStatement(M) { |
|
return { |
|
type: "WithStatement", |
|
object: to_moz(M.expression), |
|
body: to_moz(M.body) |
|
}; |
|
}); |
|
def_to_moz(AST_Switch, function To_Moz_SwitchStatement(M) { |
|
return { |
|
type: "SwitchStatement", |
|
discriminant: to_moz(M.expression), |
|
cases: M.body.map(to_moz) |
|
}; |
|
}); |
|
def_to_moz(AST_Return, function To_Moz_ReturnStatement(M) { |
|
return { |
|
type: "ReturnStatement", |
|
argument: to_moz(M.value) |
|
}; |
|
}); |
|
def_to_moz(AST_Throw, function To_Moz_ThrowStatement(M) { |
|
return { |
|
type: "ThrowStatement", |
|
argument: to_moz(M.value) |
|
}; |
|
}); |
|
def_to_moz(AST_While, function To_Moz_WhileStatement(M) { |
|
return { |
|
type: "WhileStatement", |
|
test: to_moz(M.condition), |
|
body: to_moz(M.body) |
|
}; |
|
}); |
|
def_to_moz(AST_Do, function To_Moz_DoWhileStatement(M) { |
|
return { |
|
type: "DoWhileStatement", |
|
test: to_moz(M.condition), |
|
body: to_moz(M.body) |
|
}; |
|
}); |
|
def_to_moz(AST_For, function To_Moz_ForStatement(M) { |
|
return { |
|
type: "ForStatement", |
|
init: to_moz(M.init), |
|
test: to_moz(M.condition), |
|
update: to_moz(M.step), |
|
body: to_moz(M.body) |
|
}; |
|
}); |
|
def_to_moz(AST_ForIn, function To_Moz_ForInStatement(M) { |
|
return { |
|
type: "ForInStatement", |
|
left: to_moz(M.init), |
|
right: to_moz(M.object), |
|
body: to_moz(M.body) |
|
}; |
|
}); |
|
def_to_moz(AST_ForOf, function To_Moz_ForOfStatement(M) { |
|
return { |
|
type: "ForOfStatement", |
|
left: to_moz(M.init), |
|
right: to_moz(M.object), |
|
body: to_moz(M.body), |
|
await: M.await |
|
}; |
|
}); |
|
def_to_moz(AST_Await, function To_Moz_AwaitExpression(M) { |
|
return { |
|
type: "AwaitExpression", |
|
argument: to_moz(M.expression) |
|
}; |
|
}); |
|
def_to_moz(AST_Yield, function To_Moz_YieldExpression(M) { |
|
return { |
|
type: "YieldExpression", |
|
argument: to_moz(M.expression), |
|
delegate: M.is_star |
|
}; |
|
}); |
|
def_to_moz(AST_Debugger, function To_Moz_DebuggerStatement() { |
|
return { |
|
type: "DebuggerStatement" |
|
}; |
|
}); |
|
def_to_moz(AST_VarDefLike, function To_Moz_VariableDeclarator(M) { |
|
return { |
|
type: "VariableDeclarator", |
|
id: to_moz(M.name), |
|
init: to_moz(M.value) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_This, function To_Moz_ThisExpression() { |
|
return { |
|
type: "ThisExpression" |
|
}; |
|
}); |
|
def_to_moz(AST_Super, function To_Moz_Super() { |
|
return { |
|
type: "Super" |
|
}; |
|
}); |
|
def_to_moz(AST_Conditional, function To_Moz_ConditionalExpression(M) { |
|
return { |
|
type: "ConditionalExpression", |
|
test: to_moz(M.condition), |
|
consequent: to_moz(M.consequent), |
|
alternate: to_moz(M.alternative) |
|
}; |
|
}); |
|
def_to_moz(AST_New, function To_Moz_NewExpression(M) { |
|
return { |
|
type: "NewExpression", |
|
callee: to_moz(M.expression), |
|
arguments: M.args.map(to_moz) |
|
}; |
|
}); |
|
def_to_moz(AST_Call, function To_Moz_CallExpression(M) { |
|
if (M.expression instanceof AST_SymbolRef && M.expression.name === "import") { |
|
const [source, options] = M.args.map(to_moz); |
|
return { |
|
type: "ImportExpression", |
|
source, |
|
options: options || null |
|
}; |
|
} |
|
|
|
return { |
|
type: "CallExpression", |
|
callee: to_moz(M.expression), |
|
optional: M.optional, |
|
arguments: M.args.map(to_moz) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Toplevel, function To_Moz_Program(M) { |
|
return to_moz_scope("Program", M); |
|
}); |
|
|
|
def_to_moz(AST_Expansion, function To_Moz_Spread(M) { |
|
return { |
|
type: to_moz_in_destructuring() ? "RestElement" : "SpreadElement", |
|
argument: to_moz(M.expression) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_PrefixedTemplateString, function To_Moz_TaggedTemplateExpression(M) { |
|
return { |
|
type: "TaggedTemplateExpression", |
|
tag: to_moz(M.prefix), |
|
quasi: to_moz(M.template_string) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_TemplateString, function To_Moz_TemplateLiteral(M) { |
|
var quasis = []; |
|
var expressions = []; |
|
for (var i = 0; i < M.segments.length; i++) { |
|
if (i % 2 !== 0) { |
|
expressions.push(to_moz(M.segments[i])); |
|
} else { |
|
quasis.push({ |
|
type: "TemplateElement", |
|
value: { |
|
raw: M.segments[i].raw, |
|
cooked: M.segments[i].value |
|
}, |
|
tail: i === M.segments.length - 1 |
|
}); |
|
} |
|
} |
|
return { |
|
type: "TemplateLiteral", |
|
quasis: quasis, |
|
expressions: expressions |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { |
|
return { |
|
type: "FunctionDeclaration", |
|
id: to_moz(M.name), |
|
params: M.argnames.map(to_moz_pattern), |
|
generator: M.is_generator, |
|
async: M.async, |
|
body: to_moz_scope("BlockStatement", M) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) { |
|
return { |
|
type: "FunctionExpression", |
|
id: to_moz(M.name), |
|
params: M.argnames.map(to_moz_pattern), |
|
generator: M.is_generator || false, |
|
async: M.async || false, |
|
body: to_moz_scope("BlockStatement", M) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Arrow, function To_Moz_ArrowFunctionExpression(M) { |
|
var body = M.body.length === 1 && M.body[0] instanceof AST_Return && M.body[0].value |
|
? to_moz(M.body[0].value) |
|
: { |
|
type: "BlockStatement", |
|
body: M.body.map(to_moz) |
|
}; |
|
return { |
|
type: "ArrowFunctionExpression", |
|
params: M.argnames.map(to_moz_pattern), |
|
async: M.async, |
|
body: body, |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Destructuring, function To_Moz_ObjectPattern(M) { |
|
if (M.is_array) { |
|
return { |
|
type: "ArrayPattern", |
|
elements: M.names.map( |
|
M => M instanceof AST_Hole ? null : to_moz_pattern(M) |
|
), |
|
}; |
|
} |
|
return { |
|
type: "ObjectPattern", |
|
properties: M.names.map(M => { |
|
if (M instanceof AST_ObjectKeyVal) { |
|
var computed = M.computed_key(); |
|
const [shorthand, key] = to_moz_property_key(M.key, computed, M.quote, M.value); |
|
|
|
return { |
|
type: "Property", |
|
computed, |
|
kind: "init", |
|
key: key, |
|
method: false, |
|
shorthand, |
|
value: to_moz_pattern(M.value) |
|
}; |
|
} else { |
|
return to_moz_pattern(M); |
|
} |
|
}), |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_DefaultAssign, function To_Moz_AssignmentExpression(M) { |
|
return { |
|
type: "AssignmentPattern", |
|
left: to_moz_pattern(M.left), |
|
right: to_moz(M.right), |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Directive, function To_Moz_Directive(M) { |
|
return { |
|
type: "ExpressionStatement", |
|
expression: { |
|
type: "Literal", |
|
value: M.value, |
|
raw: M.print_to_string() |
|
}, |
|
directive: M.value |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) { |
|
return { |
|
type: "ExpressionStatement", |
|
expression: to_moz(M.body) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) { |
|
return { |
|
type: "SwitchCase", |
|
test: to_moz(M.expression), |
|
consequent: M.body.map(to_moz) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Try, function To_Moz_TryStatement(M) { |
|
return { |
|
type: "TryStatement", |
|
block: to_moz_block(M.body), |
|
handler: to_moz(M.bcatch), |
|
guardedHandlers: [], |
|
finalizer: to_moz(M.bfinally) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Catch, function To_Moz_CatchClause(M) { |
|
return { |
|
type: "CatchClause", |
|
param: M.argname != null ? to_moz_pattern(M.argname) : null, |
|
body: to_moz_block(M) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_DefinitionsLike, function To_Moz_VariableDeclaration(M) { |
|
return { |
|
type: "VariableDeclaration", |
|
kind: |
|
M instanceof AST_Const ? "const" : |
|
M instanceof AST_Let ? "let" : |
|
M instanceof AST_Using ? (M.await ? "await using" : "using") : |
|
"var", |
|
declarations: M.definitions.map(to_moz) |
|
}; |
|
}); |
|
|
|
function import_attributes_to_moz(attribute) { |
|
const import_attributes = []; |
|
if (attribute) { |
|
for (const { key, value } of attribute.properties) { |
|
const key_moz = is_basic_identifier_string(key) |
|
? { type: "Identifier", name: key } |
|
: { type: "Literal", value: key, raw: JSON.stringify(key) }; |
|
import_attributes.push({ |
|
type: "ImportAttribute", |
|
key: key_moz, |
|
value: to_moz(value) |
|
}); |
|
} |
|
} |
|
return import_attributes; |
|
} |
|
|
|
def_to_moz(AST_Export, function To_Moz_ExportDeclaration(M) { |
|
if (M.exported_names) { |
|
var first_exported = M.exported_names[0]; |
|
if (first_exported && first_exported.name.name === "*" && !first_exported.name.quote) { |
|
var foreign_name = first_exported.foreign_name; |
|
var exported = foreign_name.name === "*" && !foreign_name.quote |
|
? null |
|
: to_moz(foreign_name); |
|
return { |
|
type: "ExportAllDeclaration", |
|
source: to_moz(M.module_name), |
|
exported: exported, |
|
attributes: import_attributes_to_moz(M.attributes) |
|
}; |
|
} |
|
return { |
|
type: "ExportNamedDeclaration", |
|
specifiers: M.exported_names.map(function (name_mapping) { |
|
return { |
|
type: "ExportSpecifier", |
|
exported: to_moz(name_mapping.foreign_name), |
|
local: to_moz(name_mapping.name) |
|
}; |
|
}), |
|
declaration: to_moz(M.exported_definition), |
|
source: to_moz(M.module_name), |
|
attributes: import_attributes_to_moz(M.attributes) |
|
}; |
|
} |
|
|
|
if (M.is_default) { |
|
return { |
|
type: "ExportDefaultDeclaration", |
|
declaration: to_moz(M.exported_value || M.exported_definition), |
|
}; |
|
} else { |
|
return { |
|
type: "ExportNamedDeclaration", |
|
declaration: to_moz(M.exported_value || M.exported_definition), |
|
specifiers: [], |
|
source: null, |
|
}; |
|
} |
|
}); |
|
|
|
def_to_moz(AST_Import, function To_Moz_ImportDeclaration(M) { |
|
var specifiers = []; |
|
if (M.imported_name) { |
|
specifiers.push({ |
|
type: "ImportDefaultSpecifier", |
|
local: to_moz(M.imported_name) |
|
}); |
|
} |
|
if (M.imported_names) { |
|
var first_imported_foreign_name = M.imported_names[0].foreign_name; |
|
if (first_imported_foreign_name.name === "*" && !first_imported_foreign_name.quote) { |
|
specifiers.push({ |
|
type: "ImportNamespaceSpecifier", |
|
local: to_moz(M.imported_names[0].name) |
|
}); |
|
} else { |
|
M.imported_names.forEach(function(name_mapping) { |
|
specifiers.push({ |
|
type: "ImportSpecifier", |
|
local: to_moz(name_mapping.name), |
|
imported: to_moz(name_mapping.foreign_name) |
|
}); |
|
}); |
|
} |
|
} |
|
return { |
|
type: "ImportDeclaration", |
|
specifiers: specifiers, |
|
source: to_moz(M.module_name), |
|
attributes: import_attributes_to_moz(M.attributes) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_ImportMeta, function To_Moz_MetaProperty() { |
|
return { |
|
type: "MetaProperty", |
|
meta: { |
|
type: "Identifier", |
|
name: "import" |
|
}, |
|
property: { |
|
type: "Identifier", |
|
name: "meta" |
|
} |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) { |
|
return { |
|
type: "SequenceExpression", |
|
expressions: M.expressions.map(to_moz) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_DotHash, function To_Moz_PrivateMemberExpression(M) { |
|
return { |
|
type: "MemberExpression", |
|
object: to_moz(M.expression), |
|
computed: false, |
|
property: { |
|
type: "PrivateIdentifier", |
|
name: M.property |
|
}, |
|
optional: M.optional |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) { |
|
var isComputed = M instanceof AST_Sub; |
|
return { |
|
type: "MemberExpression", |
|
object: to_moz(M.expression), |
|
computed: isComputed, |
|
property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}, |
|
optional: M.optional |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Chain, function To_Moz_ChainExpression(M) { |
|
return { |
|
type: "ChainExpression", |
|
expression: to_moz(M.expression) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Unary, function To_Moz_Unary(M) { |
|
return { |
|
type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression", |
|
operator: M.operator, |
|
prefix: M instanceof AST_UnaryPrefix, |
|
argument: to_moz(M.expression) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) { |
|
if (M.operator == "=" && to_moz_in_destructuring()) { |
|
return { |
|
type: "AssignmentPattern", |
|
left: to_moz(M.left), |
|
right: to_moz(M.right) |
|
}; |
|
} |
|
|
|
const type = M.operator == "&&" || M.operator == "||" || M.operator === "??" |
|
? "LogicalExpression" |
|
: "BinaryExpression"; |
|
|
|
return { |
|
type, |
|
left: to_moz(M.left), |
|
operator: M.operator, |
|
right: to_moz(M.right) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Assign, function To_Moz_AssignmentExpression(M) { |
|
return { |
|
type: "AssignmentExpression", |
|
operator: M.operator, |
|
left: to_moz(M.left), |
|
right: to_moz(M.right) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_PrivateIn, function To_Moz_BinaryExpression_PrivateIn(M) { |
|
return { |
|
type: "BinaryExpression", |
|
left: { type: "PrivateIdentifier", name: M.key.name }, |
|
operator: "in", |
|
right: to_moz(M.value), |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) { |
|
return { |
|
type: "ArrayExpression", |
|
elements: M.elements.map(to_moz) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) { |
|
return { |
|
type: "ObjectExpression", |
|
properties: M.properties.map(to_moz) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M, parent) { |
|
var computed = M.computed_key(); |
|
const [shorthand, key] = to_moz_property_key(M.key, computed, M.quote, M.value); |
|
|
|
var kind; |
|
if (M instanceof AST_ObjectGetter) { |
|
kind = "get"; |
|
} else |
|
if (M instanceof AST_ObjectSetter) { |
|
kind = "set"; |
|
} |
|
if (M instanceof AST_PrivateGetter || M instanceof AST_PrivateSetter) { |
|
const kind = M instanceof AST_PrivateGetter ? "get" : "set"; |
|
return { |
|
type: "MethodDefinition", |
|
computed: false, |
|
kind: kind, |
|
static: M.static, |
|
key: { |
|
type: "PrivateIdentifier", |
|
name: M.key.name |
|
}, |
|
value: to_moz(M.value) |
|
}; |
|
} |
|
if (M instanceof AST_ClassPrivateProperty) { |
|
return { |
|
type: "PropertyDefinition", |
|
key: { |
|
type: "PrivateIdentifier", |
|
name: M.key.name |
|
}, |
|
value: to_moz(M.value), |
|
computed: false, |
|
static: M.static |
|
}; |
|
} |
|
if (M instanceof AST_ClassProperty) { |
|
return { |
|
type: "PropertyDefinition", |
|
key, |
|
value: to_moz(M.value), |
|
computed, |
|
static: M.static |
|
}; |
|
} |
|
if (parent instanceof AST_Class) { |
|
return { |
|
type: "MethodDefinition", |
|
computed: computed, |
|
kind: kind, |
|
static: M.static, |
|
key: to_moz(M.key), |
|
value: to_moz(M.value) |
|
}; |
|
} |
|
return { |
|
type: "Property", |
|
computed: computed, |
|
method: false, |
|
shorthand, |
|
kind: kind, |
|
key: key, |
|
value: to_moz(M.value) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_ObjectKeyVal, function To_Moz_Property(M) { |
|
var computed = M.computed_key(); |
|
const [shorthand, key] = to_moz_property_key(M.key, computed, M.quote, M.value); |
|
|
|
return { |
|
type: "Property", |
|
computed: computed, |
|
shorthand: shorthand, |
|
method: false, |
|
kind: "init", |
|
key: key, |
|
value: to_moz(M.value) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_ConciseMethod, function To_Moz_MethodDefinition(M, parent) { |
|
const computed = M.computed_key(); |
|
const [_always_false, key] = to_moz_property_key(M.key, computed, M.quote, M.value); |
|
|
|
if (parent instanceof AST_Object) { |
|
return { |
|
type: "Property", |
|
kind: "init", |
|
computed, |
|
method: true, |
|
shorthand: false, |
|
key, |
|
value: to_moz(M.value), |
|
}; |
|
} |
|
|
|
return { |
|
type: "MethodDefinition", |
|
kind: !computed && M.key.name === "constructor" ? "constructor" : "method", |
|
computed, |
|
key, |
|
value: to_moz(M.value), |
|
static: M.static, |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_PrivateMethod, function To_Moz_MethodDefinition(M) { |
|
return { |
|
type: "MethodDefinition", |
|
kind: "method", |
|
key: { type: "PrivateIdentifier", name: M.key.name }, |
|
value: to_moz(M.value), |
|
computed: false, |
|
static: M.static, |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Class, function To_Moz_Class(M) { |
|
var type = M instanceof AST_ClassExpression ? "ClassExpression" : "ClassDeclaration"; |
|
return { |
|
type: type, |
|
superClass: to_moz(M.extends), |
|
id: M.name ? to_moz(M.name) : null, |
|
body: { |
|
type: "ClassBody", |
|
body: M.properties.map(to_moz) |
|
} |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_ClassStaticBlock, function To_Moz_StaticBlock(M) { |
|
return { |
|
type: "StaticBlock", |
|
body: M.body.map(to_moz), |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_NewTarget, function To_Moz_MetaProperty() { |
|
return { |
|
type: "MetaProperty", |
|
meta: { |
|
type: "Identifier", |
|
name: "new" |
|
}, |
|
property: { |
|
type: "Identifier", |
|
name: "target" |
|
} |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Symbol, function To_Moz_Identifier(M, parent) { |
|
if ( |
|
(M instanceof AST_SymbolMethod && parent.quote) || |
|
(( |
|
M instanceof AST_SymbolImportForeign || |
|
M instanceof AST_SymbolExportForeign || |
|
M instanceof AST_SymbolExport |
|
) && M.quote) |
|
) { |
|
return { |
|
type: "Literal", |
|
value: M.name |
|
}; |
|
} |
|
var def = M.definition(); |
|
return { |
|
type: "Identifier", |
|
name: def ? def.mangled_name || def.name : M.name |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { |
|
const pattern = M.value.source; |
|
const flags = M.value.flags; |
|
return { |
|
type: "Literal", |
|
value: null, |
|
raw: M.print_to_string(), |
|
regex: { pattern, flags } |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Constant, function To_Moz_Literal(M) { |
|
var value = M.value; |
|
return { |
|
type: "Literal", |
|
value: value, |
|
raw: M.raw || M.print_to_string() |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_Atom, function To_Moz_Atom(M) { |
|
return { |
|
type: "Identifier", |
|
name: String(M.value) |
|
}; |
|
}); |
|
|
|
def_to_moz(AST_BigInt, M => ({ |
|
type: "Literal", |
|
// value cannot be represented natively |
|
// see: https://github.com/estree/estree/blob/master/es2020.md#bigintliteral |
|
value: null, |
|
// `M.value` is a string that may be a hex number representation. |
|
// but "bigint" property should have only decimal digits |
|
bigint: typeof BigInt === "function" ? BigInt(M.value).toString() : M.value, |
|
raw: M.raw, |
|
})); |
|
|
|
AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); |
|
AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); |
|
AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null; }); |
|
|
|
AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast); |
|
AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast); |
|
|
|
/* -----[ tools ]----- */ |
|
|
|
function my_start_token(moznode) { |
|
var loc = moznode.loc, start = loc && loc.start; |
|
var range = moznode.range; |
|
return new AST_Token( |
|
"", |
|
"", |
|
start && start.line || 0, |
|
start && start.column || 0, |
|
range ? range [0] : moznode.start, |
|
false, |
|
[], |
|
[], |
|
loc && loc.source, |
|
); |
|
} |
|
|
|
function my_end_token(moznode) { |
|
var loc = moznode.loc, end = loc && loc.end; |
|
var range = moznode.range; |
|
return new AST_Token( |
|
"", |
|
"", |
|
end && end.line || 0, |
|
end && end.column || 0, |
|
range ? range [0] : moznode.end, |
|
false, |
|
[], |
|
[], |
|
loc && loc.source, |
|
); |
|
} |
|
|
|
var FROM_MOZ_LABELS = null; |
|
|
|
function from_moz(node) { |
|
if (node == null) return null; |
|
return MOZ_TO_ME[node.type](node); |
|
} |
|
|
|
function from_moz_quote(moz_key, computed) { |
|
if (!computed && moz_key.type === "Literal" && typeof moz_key.value === "string") { |
|
return '"'; |
|
} else { |
|
return ""; |
|
} |
|
} |
|
|
|
function from_moz_symbol(symbol_type, M, has_quote) { |
|
return new symbol_type({ |
|
start: my_start_token(M), |
|
quote: has_quote ? '"' : undefined, |
|
name: M.type === "Identifier" ? M.name : String(M.value), |
|
end: my_end_token(M), |
|
}); |
|
} |
|
|
|
function from_moz_lambda(M, is_method) { |
|
return new (is_method ? AST_Accessor : AST_Function)({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
name: M.id && from_moz_symbol(is_method ? AST_SymbolMethod : AST_SymbolLambda, M.id), |
|
argnames: M.params.map(M => from_moz_pattern(M, AST_SymbolFunarg)), |
|
is_generator: M.generator, |
|
async: M.async, |
|
body: normalize_directives(from_moz(M.body).body) |
|
}); |
|
} |
|
|
|
function from_moz_pattern(M, sym_type) { |
|
switch (M.type) { |
|
case "ObjectPattern": |
|
return new AST_Destructuring({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
names: M.properties.map(p => from_moz_pattern(p, sym_type)), |
|
is_array: false |
|
}); |
|
|
|
case "Property": |
|
var key = M.key; |
|
var args = { |
|
start : my_start_token(key || M.value), |
|
end : my_end_token(M.value), |
|
key : key.type == "Identifier" ? key.name : String(key.value), |
|
quote : !M.computed && key.type === "Literal" && typeof key.value === "string" |
|
? '"' |
|
: "", |
|
value : from_moz_pattern(M.value, sym_type) |
|
}; |
|
if (M.computed) { |
|
args.key = from_moz(M.key); |
|
} |
|
return new AST_ObjectKeyVal(args); |
|
|
|
case "ArrayPattern": |
|
return new AST_Destructuring({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
names: M.elements.map(function(elm) { |
|
if (elm === null) { |
|
return new AST_Hole(); |
|
} |
|
return from_moz_pattern(elm, sym_type); |
|
}), |
|
is_array: true |
|
}); |
|
|
|
case "SpreadElement": |
|
case "RestElement": |
|
return new AST_Expansion({ |
|
start: my_start_token(M), |
|
end: my_end_token(M), |
|
expression: from_moz_pattern(M.argument, sym_type), |
|
}); |
|
|
|
case "AssignmentPattern": |
|
return new AST_DefaultAssign({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
left : from_moz_pattern(M.left, sym_type), |
|
operator: "=", |
|
right : from_moz(M.right), |
|
}); |
|
|
|
case "Identifier": |
|
return new sym_type({ |
|
start : my_start_token(M), |
|
end : my_end_token(M), |
|
name : M.name, |
|
}); |
|
|
|
default: |
|
throw new Error("Invalid node type for destructuring: " + M.type); |
|
} |
|
} |
|
|
|
function from_moz_label_ref(m_label) { |
|
if (!m_label) return null; |
|
|
|
const label = from_moz_symbol(AST_LabelRef, m_label); |
|
|
|
let i = FROM_MOZ_LABELS.length; |
|
while (i--) { |
|
const label_origin = FROM_MOZ_LABELS[i]; |
|
|
|
if (label.name === label_origin.name) { |
|
label.thedef = label_origin; |
|
break; |
|
} |
|
} |
|
|
|
return label; |
|
} |
|
|
|
AST_Node.from_mozilla_ast = function(node) { |
|
var save_labels = FROM_MOZ_LABELS; |
|
FROM_MOZ_LABELS = []; |
|
var ast = from_moz(node); |
|
FROM_MOZ_LABELS = save_labels; |
|
return ast; |
|
}; |
|
|
|
function set_moz_loc(mynode, moznode) { |
|
var start = mynode.start; |
|
var end = mynode.end; |
|
if (!(start && end)) { |
|
return moznode; |
|
} |
|
if (start.pos != null && end.endpos != null) { |
|
moznode.range = [start.pos, end.endpos]; |
|
} |
|
if (start.line) { |
|
moznode.loc = { |
|
start: {line: start.line, column: start.col}, |
|
end: end.endline ? {line: end.endline, column: end.endcol} : null |
|
}; |
|
if (start.file) { |
|
moznode.loc.source = start.file; |
|
} |
|
} |
|
return moznode; |
|
} |
|
|
|
function def_to_moz(mytype, handler) { |
|
mytype.DEFMETHOD("to_mozilla_ast", function(parent) { |
|
return set_moz_loc(this, handler(this, parent)); |
|
}); |
|
} |
|
|
|
var TO_MOZ_STACK = null; |
|
|
|
function to_moz(node) { |
|
if (TO_MOZ_STACK === null) { TO_MOZ_STACK = []; } |
|
TO_MOZ_STACK.push(node); |
|
var ast = node != null ? node.to_mozilla_ast(TO_MOZ_STACK[TO_MOZ_STACK.length - 2]) : null; |
|
TO_MOZ_STACK.pop(); |
|
if (TO_MOZ_STACK.length === 0) { TO_MOZ_STACK = null; } |
|
return ast; |
|
} |
|
|
|
/** Object property keys can be number literals, string literals, or raw names. Additionally they can be shorthand. We decide that here. */ |
|
function to_moz_property_key(key, computed = false, quote = false, value = null) { |
|
if (computed) { |
|
return [false, to_moz(key)]; |
|
} |
|
|
|
const key_name = typeof key === "string" ? key : key.name; |
|
let moz_key; |
|
if (quote) { |
|
moz_key = { type: "Literal", value: key_name, raw: JSON.stringify(key_name) }; |
|
} else if ("" + +key_name === key_name && +key_name >= 0) { |
|
// representable as a number |
|
moz_key = { type: "Literal", value: +key_name, raw: JSON.stringify(+key_name) }; |
|
} else { |
|
moz_key = { type: "Identifier", name: key_name }; |
|
} |
|
|
|
const shorthand = |
|
moz_key.type === "Identifier" |
|
&& moz_key.name === key_name |
|
&& (value instanceof AST_Symbol && value.name === key_name |
|
|| value instanceof AST_DefaultAssign && value.left.name === key_name); |
|
return [shorthand, moz_key]; |
|
} |
|
|
|
function to_moz_pattern(node) { |
|
if (node instanceof AST_Expansion) { |
|
return { |
|
type: "RestElement", |
|
argument: to_moz_pattern(node.expression), |
|
}; |
|
} |
|
|
|
if (( |
|
node instanceof AST_Symbol |
|
|| node instanceof AST_Destructuring |
|
|| node instanceof AST_DefaultAssign |
|
|| node instanceof AST_PropAccess |
|
)) { |
|
// Plain translation |
|
return to_moz(node); |
|
} |
|
|
|
throw new Error(node.TYPE); |
|
} |
|
|
|
function to_moz_in_destructuring() { |
|
var i = TO_MOZ_STACK.length; |
|
while (i--) { |
|
if (TO_MOZ_STACK[i] instanceof AST_Destructuring) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
function to_moz_block(node) { |
|
return { |
|
type: "BlockStatement", |
|
body: node.body.map(to_moz) |
|
}; |
|
} |
|
|
|
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 |
|
}; |
|
} |
|
})(); |
|
|
|
// return true if the node at the top of the stack (that means the |
|
// innermost node in the current output) is lexically the first in |
|
// a statement. |
|
function first_in_statement(stack) { |
|
let node = stack.parent(-1); |
|
for (let i = 0, p; p = stack.parent(i); i++) { |
|
if (p instanceof AST_Statement && p.body === node) |
|
return true; |
|
if ((p instanceof AST_Sequence && p.expressions[0] === node) || |
|
(p.TYPE === "Call" && p.expression === node) || |
|
(p instanceof AST_PrefixedTemplateString && p.prefix === node) || |
|
(p instanceof AST_Dot && p.expression === node) || |
|
(p instanceof AST_Sub && p.expression === node) || |
|
(p instanceof AST_Chain && p.expression === node) || |
|
(p instanceof AST_Conditional && p.condition === node) || |
|
(p instanceof AST_Binary && p.left === node) || |
|
(p instanceof AST_UnaryPostfix && p.expression === node) |
|
) { |
|
node = p; |
|
} else { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
// Returns whether the leftmost item in the expression is an object |
|
function left_is_object(node) { |
|
if (node instanceof AST_Object) return true; |
|
if (node instanceof AST_Sequence) return left_is_object(node.expressions[0]); |
|
if (node.TYPE === "Call") return left_is_object(node.expression); |
|
if (node instanceof AST_PrefixedTemplateString) return left_is_object(node.prefix); |
|
if (node instanceof AST_Dot || node instanceof AST_Sub) return left_is_object(node.expression); |
|
if (node instanceof AST_Chain) return left_is_object(node.expression); |
|
if (node instanceof AST_Conditional) return left_is_object(node.condition); |
|
if (node instanceof AST_Binary) return left_is_object(node.left); |
|
if (node instanceof AST_UnaryPostfix) return left_is_object(node.expression); |
|
return false; |
|
} |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
const CODE_LINE_BREAK = 10; |
|
const CODE_SPACE = 32; |
|
|
|
const r_annotation = /[@#]__(PURE|INLINE|NOINLINE)__/; |
|
|
|
function is_some_comments(comment) { |
|
// multiline comment |
|
return ( |
|
(comment.type === "comment2" || comment.type === "comment1") |
|
&& /@preserve|@copyright|@lic|@cc_on|^\**!/i.test(comment.value) |
|
); |
|
} |
|
|
|
const ROPE_COMMIT_WHEN = 8 * 1000; |
|
class Rope { |
|
constructor() { |
|
this.committed = ""; |
|
this.current = ""; |
|
} |
|
|
|
append(str) { |
|
/** When `this.current` is too long, commit it. */ |
|
if (this.current.length > ROPE_COMMIT_WHEN) { |
|
this.committed += this.current + str; |
|
this.current = ""; |
|
} else { |
|
this.current += str; |
|
} |
|
} |
|
|
|
insertAt(char, index) { |
|
const { committed, current } = this; |
|
if (index < committed.length) { |
|
this.committed = committed.slice(0, index) + char + committed.slice(index); |
|
} else if (index === committed.length) { |
|
this.committed += char; |
|
} else { |
|
index -= committed.length; |
|
this.committed += current.slice(0, index) + char; |
|
this.current = current.slice(index); |
|
} |
|
} |
|
|
|
charAt(index) { |
|
const { committed } = this; |
|
if (index < committed.length) return committed[index]; |
|
return this.current[index - committed.length]; |
|
} |
|
|
|
charCodeAt(index) { |
|
const { committed } = this; |
|
if (index < committed.length) return committed.charCodeAt(index); |
|
return this.current.charCodeAt(index - committed.length); |
|
} |
|
|
|
length() { |
|
return this.committed.length + this.current.length; |
|
} |
|
|
|
expectDirective() { |
|
// /^$|[;{][\s\n]*$/ |
|
|
|
let ch, n = this.length(); |
|
|
|
if (n <= 0) return true; |
|
|
|
// Skip N whitespace from the end |
|
while ( |
|
(ch = this.charCodeAt(--n)) |
|
&& (ch == CODE_SPACE || ch == CODE_LINE_BREAK) |
|
); |
|
|
|
// either ";", or "{", or the string ended |
|
return !ch || ch === 59 || ch === 123; |
|
} |
|
|
|
hasNLB() { |
|
let n = this.length() - 1; |
|
while (n >= 0) { |
|
const code = this.charCodeAt(n--); |
|
|
|
if (code === CODE_LINE_BREAK) return true; |
|
if (code !== CODE_SPACE) return false; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
toString() { |
|
return this.committed + this.current; |
|
} |
|
} |
|
|
|
function OutputStream(options) { |
|
|
|
var readonly = !options; |
|
options = defaults(options, { |
|
ascii_only : false, |
|
beautify : false, |
|
braces : false, |
|
comments : "some", |
|
ecma : 5, |
|
ie8 : false, |
|
indent_level : 4, |
|
indent_start : 0, |
|
inline_script : true, |
|
keep_numbers : false, |
|
keep_quoted_props : false, |
|
max_line_len : false, |
|
preamble : null, |
|
preserve_annotations : false, |
|
quote_keys : false, |
|
quote_style : 0, |
|
safari10 : false, |
|
semicolons : true, |
|
shebang : true, |
|
shorthand : undefined, |
|
source_map : null, |
|
webkit : false, |
|
width : 80, |
|
wrap_iife : false, |
|
wrap_func_args : false, |
|
|
|
_destroy_ast : false |
|
}, true); |
|
|
|
if (options.shorthand === undefined) |
|
options.shorthand = options.ecma > 5; |
|
|
|
// Convert comment option to RegExp if necessary and set up comments filter |
|
var comment_filter = return_false; // Default case, throw all comments away |
|
if (options.comments) { |
|
let comments = options.comments; |
|
if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) { |
|
var regex_pos = options.comments.lastIndexOf("/"); |
|
comments = new RegExp( |
|
options.comments.substr(1, regex_pos - 1), |
|
options.comments.substr(regex_pos + 1) |
|
); |
|
} |
|
if (comments instanceof RegExp) { |
|
comment_filter = function(comment) { |
|
return comment.type != "comment5" && comments.test(comment.value); |
|
}; |
|
} else if (typeof comments === "function") { |
|
comment_filter = function(comment) { |
|
return comment.type != "comment5" && comments(this, comment); |
|
}; |
|
} else if (comments === "some") { |
|
comment_filter = is_some_comments; |
|
} else { // NOTE includes "all" option |
|
comment_filter = return_true; |
|
} |
|
} |
|
|
|
if (options.preserve_annotations) { |
|
let prev_comment_filter = comment_filter; |
|
comment_filter = function (comment) { |
|
return r_annotation.test(comment.value) || prev_comment_filter.apply(this, arguments); |
|
}; |
|
} |
|
|
|
var indentation = 0; |
|
var current_col = 0; |
|
var current_line = 1; |
|
var current_pos = 0; |
|
var OUTPUT = new Rope(); |
|
let printed_comments = new Set(); |
|
|
|
var to_utf8 = options.ascii_only ? function(str, identifier = false, regexp = false) { |
|
if (options.ecma >= 2015 && !options.safari10 && !regexp) { |
|
str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) { |
|
var code = get_full_char_code(ch, 0).toString(16); |
|
return "\\u{" + code + "}"; |
|
}); |
|
} |
|
return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) { |
|
var code = ch.charCodeAt(0).toString(16); |
|
if (code.length <= 2 && !identifier) { |
|
while (code.length < 2) code = "0" + code; |
|
return "\\x" + code; |
|
} else { |
|
while (code.length < 4) code = "0" + code; |
|
return "\\u" + code; |
|
} |
|
}); |
|
} : function(str) { |
|
return str.replace(/[\ud800-\udbff][\udc00-\udfff]|([\ud800-\udbff]|[\udc00-\udfff])/g, function(match, lone) { |
|
if (lone) { |
|
return "\\u" + lone.charCodeAt(0).toString(16); |
|
} |
|
return match; |
|
}); |
|
}; |
|
|
|
function make_string(str, quote) { |
|
var dq = 0, sq = 0; |
|
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, |
|
function(s, i) { |
|
switch (s) { |
|
case '"': ++dq; return '"'; |
|
case "'": ++sq; return "'"; |
|
case "\\": return "\\\\"; |
|
case "\n": return "\\n"; |
|
case "\r": return "\\r"; |
|
case "\t": return "\\t"; |
|
case "\b": return "\\b"; |
|
case "\f": return "\\f"; |
|
case "\x0B": return options.ie8 ? "\\x0B" : "\\v"; |
|
case "\u2028": return "\\u2028"; |
|
case "\u2029": return "\\u2029"; |
|
case "\ufeff": return "\\ufeff"; |
|
case "\0": |
|
return /[0-9]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0"; |
|
} |
|
return s; |
|
}); |
|
function quote_single() { |
|
return "'" + str.replace(/\x27/g, "\\'") + "'"; |
|
} |
|
function quote_double() { |
|
return '"' + str.replace(/\x22/g, '\\"') + '"'; |
|
} |
|
function quote_template() { |
|
return "`" + str.replace(/`/g, "\\`") + "`"; |
|
} |
|
str = to_utf8(str); |
|
if (quote === "`") return quote_template(); |
|
switch (options.quote_style) { |
|
case 1: |
|
return quote_single(); |
|
case 2: |
|
return quote_double(); |
|
case 3: |
|
return quote == "'" ? quote_single() : quote_double(); |
|
default: |
|
return dq > sq ? quote_single() : quote_double(); |
|
} |
|
} |
|
|
|
function encode_string(str, quote) { |
|
var ret = make_string(str, quote); |
|
if (options.inline_script) { |
|
ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2"); |
|
ret = ret.replace(/\x3c!--/g, "\\x3c!--"); |
|
ret = ret.replace(/--\x3e/g, "--\\x3e"); |
|
} |
|
return ret; |
|
} |
|
|
|
function make_name(name) { |
|
name = name.toString(); |
|
name = to_utf8(name, true); |
|
return name; |
|
} |
|
|
|
function make_indent(back) { |
|
return " ".repeat(options.indent_start + indentation - back * options.indent_level); |
|
} |
|
|
|
/* -----[ beautification/minification ]----- */ |
|
|
|
var has_parens = false; |
|
var might_need_space = false; |
|
var might_need_semicolon = false; |
|
var might_add_newline = 0; |
|
var need_newline_indented = false; |
|
var need_space = false; |
|
var newline_insert = -1; |
|
var last = ""; |
|
var mapping_token, mapping_name, mappings = options.source_map && []; |
|
|
|
var do_add_mapping = mappings ? function() { |
|
mappings.forEach(function(mapping) { |
|
try { |
|
let { name, token } = mapping; |
|
if (name !== false) { |
|
if (token.type == "name" || token.type === "privatename") { |
|
name = token.value; |
|
} else if (name instanceof AST_Symbol) { |
|
name = token.type === "string" ? token.value : name.name; |
|
} |
|
} |
|
options.source_map.add( |
|
mapping.token.file, |
|
mapping.line, mapping.col, |
|
mapping.token.line, mapping.token.col, |
|
is_basic_identifier_string(name) ? name : undefined |
|
); |
|
} catch(ex) { |
|
// Ignore bad mapping |
|
} |
|
}); |
|
mappings = []; |
|
} : noop; |
|
|
|
var ensure_line_len = options.max_line_len ? function() { |
|
if (current_col > options.max_line_len) { |
|
if (might_add_newline) { |
|
OUTPUT.insertAt("\n", might_add_newline); |
|
const len_after_newline = OUTPUT.length() - might_add_newline - 1; |
|
if (mappings) { |
|
var delta = len_after_newline - current_col; |
|
mappings.forEach(function(mapping) { |
|
mapping.line++; |
|
mapping.col += delta; |
|
}); |
|
} |
|
current_line++; |
|
current_pos++; |
|
current_col = len_after_newline; |
|
} |
|
} |
|
if (might_add_newline) { |
|
might_add_newline = 0; |
|
do_add_mapping(); |
|
} |
|
} : noop; |
|
|
|
var requireSemicolonChars = makePredicate("( [ + * / - , . `"); |
|
|
|
function print(str) { |
|
str = String(str); |
|
var ch = get_full_char(str, 0); |
|
if (need_newline_indented && ch) { |
|
need_newline_indented = false; |
|
if (ch !== "\n") { |
|
print("\n"); |
|
indent(); |
|
} |
|
} |
|
if (need_space && ch) { |
|
need_space = false; |
|
if (!/[\s;})]/.test(ch)) { |
|
space(); |
|
} |
|
} |
|
newline_insert = -1; |
|
var prev = last.charAt(last.length - 1); |
|
if (might_need_semicolon) { |
|
might_need_semicolon = false; |
|
|
|
if (prev === ":" && ch === "}" || (!ch || !";}".includes(ch)) && prev !== ";") { |
|
if (options.semicolons || requireSemicolonChars.has(ch)) { |
|
OUTPUT.append(";"); |
|
current_col++; |
|
current_pos++; |
|
} else { |
|
ensure_line_len(); |
|
if (current_col > 0) { |
|
OUTPUT.append("\n"); |
|
current_pos++; |
|
current_line++; |
|
current_col = 0; |
|
} |
|
|
|
if (/^\s+$/.test(str)) { |
|
// reset the semicolon flag, since we didn't print one |
|
// now and might still have to later |
|
might_need_semicolon = true; |
|
} |
|
} |
|
|
|
if (!options.beautify) |
|
might_need_space = false; |
|
} |
|
} |
|
|
|
if (might_need_space) { |
|
if ((is_identifier_char(prev) |
|
&& (is_identifier_char(ch) || ch == "\\")) |
|
|| (ch == "/" && ch == prev) |
|
|| ((ch == "+" || ch == "-") && ch == last) |
|
) { |
|
OUTPUT.append(" "); |
|
current_col++; |
|
current_pos++; |
|
} |
|
might_need_space = false; |
|
} |
|
|
|
if (mapping_token) { |
|
mappings.push({ |
|
token: mapping_token, |
|
name: mapping_name, |
|
line: current_line, |
|
col: current_col |
|
}); |
|
mapping_token = false; |
|
if (!might_add_newline) do_add_mapping(); |
|
} |
|
|
|
OUTPUT.append(str); |
|
has_parens = str[str.length - 1] == "("; |
|
current_pos += str.length; |
|
var a = str.split(/\r?\n/), n = a.length - 1; |
|
current_line += n; |
|
current_col += a[0].length; |
|
if (n > 0) { |
|
ensure_line_len(); |
|
current_col = a[n].length; |
|
} |
|
last = str; |
|
} |
|
|
|
var star = function() { |
|
print("*"); |
|
}; |
|
|
|
var space = options.beautify ? function() { |
|
print(" "); |
|
} : function() { |
|
might_need_space = true; |
|
}; |
|
|
|
var indent = options.beautify ? function(half) { |
|
if (options.beautify) { |
|
print(make_indent(half ? 0.5 : 0)); |
|
} |
|
} : noop; |
|
|
|
var with_indent = options.beautify ? function(col, cont) { |
|
if (col === true) col = next_indent(); |
|
var save_indentation = indentation; |
|
indentation = col; |
|
var ret = cont(); |
|
indentation = save_indentation; |
|
return ret; |
|
} : function(col, cont) { return cont(); }; |
|
|
|
var newline = options.beautify ? function() { |
|
if (newline_insert < 0) return print("\n"); |
|
if (OUTPUT.charAt(newline_insert) != "\n") { |
|
OUTPUT.insertAt("\n", newline_insert); |
|
current_pos++; |
|
current_line++; |
|
} |
|
newline_insert++; |
|
} : options.max_line_len ? function() { |
|
ensure_line_len(); |
|
might_add_newline = OUTPUT.length(); |
|
} : noop; |
|
|
|
var semicolon = options.beautify ? function() { |
|
print(";"); |
|
} : function() { |
|
might_need_semicolon = true; |
|
}; |
|
|
|
function force_semicolon() { |
|
might_need_semicolon = false; |
|
print(";"); |
|
} |
|
|
|
function next_indent() { |
|
return indentation + options.indent_level; |
|
} |
|
|
|
function with_block(cont) { |
|
var ret; |
|
print("{"); |
|
newline(); |
|
with_indent(next_indent(), function() { |
|
ret = cont(); |
|
}); |
|
indent(); |
|
print("}"); |
|
return ret; |
|
} |
|
|
|
function with_parens(cont) { |
|
print("("); |
|
//XXX: still nice to have that for argument lists |
|
//var ret = with_indent(current_col, cont); |
|
var ret = cont(); |
|
print(")"); |
|
return ret; |
|
} |
|
|
|
function with_square(cont) { |
|
print("["); |
|
//var ret = with_indent(current_col, cont); |
|
var ret = cont(); |
|
print("]"); |
|
return ret; |
|
} |
|
|
|
function comma() { |
|
print(","); |
|
space(); |
|
} |
|
|
|
function colon() { |
|
print(":"); |
|
space(); |
|
} |
|
|
|
var add_mapping = mappings ? function(token, name) { |
|
mapping_token = token; |
|
mapping_name = name; |
|
} : noop; |
|
|
|
function get() { |
|
if (might_add_newline) { |
|
ensure_line_len(); |
|
} |
|
return OUTPUT.toString(); |
|
} |
|
|
|
function filter_comment(comment) { |
|
if (!options.preserve_annotations) { |
|
comment = comment.replace(r_annotation, " "); |
|
} |
|
if (/^\s*$/.test(comment)) { |
|
return ""; |
|
} |
|
return comment.replace(/(<\s*\/\s*)(script)/i, "<\\/$2"); |
|
} |
|
|
|
function prepend_comments(node) { |
|
var self = this; |
|
var start = node.start; |
|
if (!start) return; |
|
var printed_comments = self.printed_comments; |
|
|
|
// There cannot be a newline between return/yield and its value. |
|
const keyword_with_value = |
|
node instanceof AST_Exit && node.value |
|
|| (node instanceof AST_Await || node instanceof AST_Yield) |
|
&& node.expression; |
|
|
|
if ( |
|
start.comments_before |
|
&& printed_comments.has(start.comments_before) |
|
) { |
|
if (keyword_with_value) { |
|
start.comments_before = []; |
|
} else { |
|
return; |
|
} |
|
} |
|
|
|
var comments = start.comments_before; |
|
if (!comments) { |
|
comments = start.comments_before = []; |
|
} |
|
printed_comments.add(comments); |
|
|
|
if (keyword_with_value) { |
|
var tw = new TreeWalker(function(node) { |
|
var parent = tw.parent(); |
|
if (parent instanceof AST_Exit |
|
|| parent instanceof AST_Await |
|
|| parent instanceof AST_Yield |
|
|| parent instanceof AST_Binary && parent.left === node |
|
|| parent.TYPE == "Call" && parent.expression === node |
|
|| parent instanceof AST_Conditional && parent.condition === node |
|
|| parent instanceof AST_Dot && parent.expression === node |
|
|| parent instanceof AST_Sequence && parent.expressions[0] === node |
|
|| parent instanceof AST_Sub && parent.expression === node |
|
|| parent instanceof AST_UnaryPostfix) { |
|
if (!node.start) return; |
|
var text = node.start.comments_before; |
|
if (text && !printed_comments.has(text)) { |
|
printed_comments.add(text); |
|
comments = comments.concat(text); |
|
} |
|
} else { |
|
return true; |
|
} |
|
}); |
|
tw.push(node); |
|
keyword_with_value.walk(tw); |
|
} |
|
|
|
if (current_pos == 0) { |
|
if (comments.length > 0 && options.shebang && comments[0].type === "comment5" |
|
&& !printed_comments.has(comments[0])) { |
|
print("#!" + comments.shift().value + "\n"); |
|
indent(); |
|
} |
|
var preamble = options.preamble; |
|
if (preamble) { |
|
print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); |
|
} |
|
} |
|
|
|
comments = comments.filter(comment_filter, node).filter(c => !printed_comments.has(c)); |
|
if (comments.length == 0) return; |
|
var last_nlb = OUTPUT.hasNLB(); |
|
comments.forEach(function(c, i) { |
|
printed_comments.add(c); |
|
if (!last_nlb) { |
|
if (c.nlb) { |
|
print("\n"); |
|
indent(); |
|
last_nlb = true; |
|
} else if (i > 0) { |
|
space(); |
|
} |
|
} |
|
|
|
if (/comment[134]/.test(c.type)) { |
|
var value = filter_comment(c.value); |
|
if (value) { |
|
print("//" + value + "\n"); |
|
indent(); |
|
} |
|
last_nlb = true; |
|
} else if (c.type == "comment2") { |
|
var value = filter_comment(c.value); |
|
if (value) { |
|
print("/*" + value + "*/"); |
|
} |
|
last_nlb = false; |
|
} |
|
}); |
|
if (!last_nlb) { |
|
if (start.nlb) { |
|
print("\n"); |
|
indent(); |
|
} else { |
|
space(); |
|
} |
|
} |
|
} |
|
|
|
function append_comments(node, tail) { |
|
var self = this; |
|
var token = node.end; |
|
if (!token) return; |
|
var printed_comments = self.printed_comments; |
|
var comments = token[tail ? "comments_before" : "comments_after"]; |
|
if (!comments || printed_comments.has(comments)) return; |
|
if (!(node instanceof AST_Statement || comments.every((c) => |
|
!/comment[134]/.test(c.type) |
|
))) return; |
|
printed_comments.add(comments); |
|
var insert = OUTPUT.length(); |
|
comments.filter(comment_filter, node).forEach(function(c, i) { |
|
if (printed_comments.has(c)) return; |
|
printed_comments.add(c); |
|
need_space = false; |
|
if (need_newline_indented) { |
|
print("\n"); |
|
indent(); |
|
need_newline_indented = false; |
|
} else if (c.nlb && (i > 0 || !OUTPUT.hasNLB())) { |
|
print("\n"); |
|
indent(); |
|
} else if (i > 0 || !tail) { |
|
space(); |
|
} |
|
if (/comment[134]/.test(c.type)) { |
|
const value = filter_comment(c.value); |
|
if (value) { |
|
print("//" + value); |
|
} |
|
need_newline_indented = true; |
|
} else if (c.type == "comment2") { |
|
const value = filter_comment(c.value); |
|
if (value) { |
|
print("/*" + value + "*/"); |
|
} |
|
need_space = true; |
|
} |
|
}); |
|
if (OUTPUT.length() > insert) newline_insert = insert; |
|
} |
|
|
|
/** |
|
* When output.option("_destroy_ast") is enabled, destroy the function. |
|
* Call this after printing it. |
|
*/ |
|
const gc_scope = |
|
options["_destroy_ast"] |
|
? function gc_scope(scope) { |
|
scope.body.length = 0; |
|
scope.argnames.length = 0; |
|
} |
|
: noop; |
|
|
|
var stack = []; |
|
return { |
|
get : get, |
|
toString : get, |
|
indent : indent, |
|
in_directive : false, |
|
use_asm : null, |
|
active_scope : null, |
|
indentation : function() { return indentation; }, |
|
current_width : function() { return current_col - indentation; }, |
|
should_break : function() { return options.width && this.current_width() >= options.width; }, |
|
has_parens : function() { return has_parens; }, |
|
newline : newline, |
|
print : print, |
|
star : star, |
|
space : space, |
|
comma : comma, |
|
colon : colon, |
|
last : function() { return last; }, |
|
semicolon : semicolon, |
|
force_semicolon : force_semicolon, |
|
to_utf8 : to_utf8, |
|
print_name : function(name) { print(make_name(name)); }, |
|
print_string : function(str, quote, escape_directive) { |
|
var encoded = encode_string(str, quote); |
|
if (escape_directive === true && !encoded.includes("\\")) { |
|
// Insert semicolons to break directive prologue |
|
if (!OUTPUT.expectDirective()) { |
|
force_semicolon(); |
|
} |
|
force_semicolon(); |
|
} |
|
print(encoded); |
|
}, |
|
print_template_string_chars: function(str) { |
|
var encoded = encode_string(str, "`").replace(/\${/g, "\\${"); |
|
return print(encoded.substr(1, encoded.length - 2)); |
|
}, |
|
encode_string : encode_string, |
|
next_indent : next_indent, |
|
with_indent : with_indent, |
|
with_block : with_block, |
|
with_parens : with_parens, |
|
with_square : with_square, |
|
add_mapping : add_mapping, |
|
option : function(opt) { return options[opt]; }, |
|
gc_scope, |
|
printed_comments: printed_comments, |
|
prepend_comments: readonly ? noop : prepend_comments, |
|
append_comments : readonly || comment_filter === return_false ? noop : append_comments, |
|
line : function() { return current_line; }, |
|
col : function() { return current_col; }, |
|
pos : function() { return current_pos; }, |
|
push_node : function(node) { stack.push(node); }, |
|
pop_node : function() { return stack.pop(); }, |
|
parent : function(n) { |
|
return stack[stack.length - 2 - (n || 0)]; |
|
} |
|
}; |
|
|
|
} |
|
|
|
/* -----[ code generators ]----- */ |
|
|
|
(function() { |
|
|
|
/* -----[ utils ]----- */ |
|
|
|
function DEFPRINT(nodetype, generator) { |
|
nodetype.DEFMETHOD("_codegen", generator); |
|
} |
|
|
|
AST_Node.DEFMETHOD("print", function(output, force_parens) { |
|
var self = this, generator = self._codegen; |
|
if (self instanceof AST_Scope) { |
|
output.active_scope = self; |
|
} else if (!output.use_asm && self instanceof AST_Directive && self.value == "use asm") { |
|
output.use_asm = output.active_scope; |
|
} |
|
function doit() { |
|
output.prepend_comments(self); |
|
self.add_source_map(output); |
|
generator(self, output); |
|
output.append_comments(self); |
|
} |
|
output.push_node(self); |
|
if (force_parens || self.needs_parens(output)) { |
|
output.with_parens(doit); |
|
} else { |
|
doit(); |
|
} |
|
output.pop_node(); |
|
if (self === output.use_asm) { |
|
output.use_asm = null; |
|
} |
|
}); |
|
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print); |
|
|
|
AST_Node.DEFMETHOD("print_to_string", function(options) { |
|
var output = OutputStream(options); |
|
this.print(output); |
|
return output.get(); |
|
}); |
|
|
|
/* -----[ PARENTHESES ]----- */ |
|
|
|
function PARENS(nodetype, func) { |
|
if (Array.isArray(nodetype)) { |
|
nodetype.forEach(function(nodetype) { |
|
PARENS(nodetype, func); |
|
}); |
|
} else { |
|
nodetype.DEFMETHOD("needs_parens", func); |
|
} |
|
} |
|
|
|
PARENS(AST_Node, return_false); |
|
|
|
// a function expression needs parens around it when it's provably |
|
// the first token to appear in a statement. |
|
PARENS(AST_Function, function(output) { |
|
if (!output.has_parens() && first_in_statement(output)) { |
|
return true; |
|
} |
|
|
|
if (output.option("webkit")) { |
|
var p = output.parent(); |
|
if (p instanceof AST_PropAccess && p.expression === this) { |
|
return true; |
|
} |
|
} |
|
|
|
if (output.option("wrap_iife")) { |
|
var p = output.parent(); |
|
if (p instanceof AST_Call && p.expression === this) { |
|
return true; |
|
} |
|
} |
|
|
|
if (output.option("wrap_func_args")) { |
|
var p = output.parent(); |
|
if (p instanceof AST_Call && p.args.includes(this)) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
}); |
|
|
|
PARENS(AST_Arrow, function(output) { |
|
var p = output.parent(); |
|
|
|
if ( |
|
output.option("wrap_func_args") |
|
&& p instanceof AST_Call |
|
&& p.args.includes(this) |
|
) { |
|
return true; |
|
} |
|
return p instanceof AST_PropAccess && p.expression === this |
|
|| p instanceof AST_Conditional && p.condition === this; |
|
}); |
|
|
|
// same goes for an object literal (as in AST_Function), because |
|
// otherwise {...} would be interpreted as a block of code. |
|
PARENS(AST_Object, function(output) { |
|
return !output.has_parens() && first_in_statement(output); |
|
}); |
|
|
|
PARENS(AST_ClassExpression, first_in_statement); |
|
|
|
PARENS(AST_Unary, function(output) { |
|
var p = output.parent(); |
|
return p instanceof AST_PropAccess && p.expression === this |
|
|| p instanceof AST_Call && p.expression === this |
|
|| p instanceof AST_Binary |
|
&& p.operator === "**" |
|
&& this instanceof AST_UnaryPrefix |
|
&& p.left === this |
|
&& this.operator !== "++" |
|
&& this.operator !== "--"; |
|
}); |
|
|
|
PARENS(AST_Await, function(output) { |
|
var p = output.parent(); |
|
return p instanceof AST_PropAccess && p.expression === this |
|
|| p instanceof AST_Call && p.expression === this |
|
|| p instanceof AST_Binary && p.operator === "**" && p.left === this |
|
|| output.option("safari10") && p instanceof AST_UnaryPrefix; |
|
}); |
|
|
|
PARENS(AST_Sequence, function(output) { |
|
var p = output.parent(); |
|
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) |
|
|| p instanceof AST_Unary // !(foo, bar, baz) |
|
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8 |
|
|| p instanceof AST_VarDefLike // var a = (1, 2), b = a + a; ==> b == 4 |
|
|| p instanceof AST_PropAccess && this !== p.property // (1, {foo:2}).foo, (1, {foo:2})["foo"], not foo[1, 2] |
|
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] |
|
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2 |
|
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30) |
|
* ==> 20 (side effect, set a := 10 and b := 20) */ |
|
|| p instanceof AST_Arrow // x => (x, x) |
|
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){})) |
|
|| p instanceof AST_Expansion // [...(a, b)] |
|
|| p instanceof AST_ForOf && this === p.object // for (e of (foo, bar)) {} |
|
|| p instanceof AST_Yield // yield (foo, bar) |
|
|| p instanceof AST_Export // export default (foo, bar) |
|
; |
|
}); |
|
|
|
PARENS(AST_Binary, function(output) { |
|
var p = output.parent(); |
|
// (foo && bar)() |
|
if (p instanceof AST_Call && p.expression === this) |
|
return true; |
|
// typeof (foo && bar) |
|
if (p instanceof AST_Unary) |
|
return true; |
|
// (foo && bar)["prop"], (foo && bar).prop |
|
if (p instanceof AST_PropAccess && p.expression === this) |
|
return true; |
|
// this deals with precedence: 3 * (2 + 1) |
|
if (p instanceof AST_Binary) { |
|
const parent_op = p.operator; |
|
const op = this.operator; |
|
|
|
// It is forbidden for ?? to be used with || or && without parens. |
|
if (op === "??" && (parent_op === "||" || parent_op === "&&")) { |
|
return true; |
|
} |
|
if (parent_op === "??" && (op === "||" || op === "&&")) { |
|
return true; |
|
} |
|
|
|
const pp = PRECEDENCE[parent_op]; |
|
const sp = PRECEDENCE[op]; |
|
if (pp > sp |
|
|| (pp == sp |
|
&& (this === p.right || parent_op == "**"))) { |
|
return true; |
|
} |
|
} |
|
if (p instanceof AST_PrivateIn) { |
|
const op = this.operator; |
|
|
|
const pp = PRECEDENCE["in"]; |
|
const sp = PRECEDENCE[op]; |
|
if (pp > sp || (pp == sp && this === p.value)) { |
|
return true; |
|
} |
|
} |
|
}); |
|
|
|
PARENS(AST_PrivateIn, function(output) { |
|
var p = output.parent(); |
|
// (#x in this)() |
|
if (p instanceof AST_Call && p.expression === this) { |
|
return true; |
|
} |
|
// typeof (#x in this) |
|
if (p instanceof AST_Unary) { |
|
return true; |
|
} |
|
// (#x in this)["prop"], (#x in this).prop |
|
if (p instanceof AST_PropAccess && p.expression === this) { |
|
return true; |
|
} |
|
// same precedence as regular in operator |
|
if (p instanceof AST_Binary) { |
|
const parent_op = p.operator; |
|
|
|
const pp = PRECEDENCE[parent_op]; |
|
const sp = PRECEDENCE["in"]; |
|
if (pp > sp |
|
|| (pp == sp |
|
&& (this === p.right || parent_op == "**"))) { |
|
return true; |
|
} |
|
} |
|
// rules are the same as binary in, but the class differs |
|
if (p instanceof AST_PrivateIn && this === p.value) { |
|
return true; |
|
} |
|
}); |
|
|
|
PARENS(AST_Yield, function(output) { |
|
var p = output.parent(); |
|
// (yield 1) + (yield 2) |
|
// a = yield 3 |
|
if (p instanceof AST_Binary && p.operator !== "=") |
|
return true; |
|
// (yield 1)() |
|
// new (yield 1)() |
|
if (p instanceof AST_Call && p.expression === this) |
|
return true; |
|
// (yield 1) ? yield 2 : yield 3 |
|
if (p instanceof AST_Conditional && p.condition === this) |
|
return true; |
|
// -(yield 4) |
|
if (p instanceof AST_Unary) |
|
return true; |
|
// (yield x).foo |
|
// (yield x)['foo'] |
|
if (p instanceof AST_PropAccess && p.expression === this) |
|
return true; |
|
}); |
|
|
|
PARENS(AST_Chain, function(output) { |
|
var p = output.parent(); |
|
if (!(p instanceof AST_Call || p instanceof AST_PropAccess)) return false; |
|
return p.expression === this; |
|
}); |
|
|
|
PARENS(AST_PropAccess, function(output) { |
|
var p = output.parent(); |
|
if (p instanceof AST_New && p.expression === this) { |
|
// i.e. new (foo.bar().baz) |
|
// |
|
// if there's one call into this subtree, then we need |
|
// parens around it too, otherwise the call will be |
|
// interpreted as passing the arguments to the upper New |
|
// expression. |
|
return walk(this, node => { |
|
if (node instanceof AST_Scope) return true; |
|
if (node instanceof AST_Call) { |
|
return walk_abort; // makes walk() return true. |
|
} |
|
}); |
|
} |
|
}); |
|
|
|
PARENS(AST_Call, function(output) { |
|
var p = output.parent(), p1; |
|
if (p instanceof AST_New && p.expression === this |
|
|| p instanceof AST_Export && p.is_default && this.expression instanceof AST_Function) |
|
return true; |
|
|
|
// workaround for Safari bug. |
|
// https://bugs.webkit.org/show_bug.cgi?id=123506 |
|
return this.expression instanceof AST_Function |
|
&& p instanceof AST_PropAccess |
|
&& p.expression === this |
|
&& (p1 = output.parent(1)) instanceof AST_Assign |
|
&& p1.left === p; |
|
}); |
|
|
|
PARENS(AST_New, function(output) { |
|
var p = output.parent(); |
|
if (this.args.length === 0 |
|
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() |
|
|| p instanceof AST_Call && p.expression === this |
|
|| p instanceof AST_PrefixedTemplateString && p.prefix === this)) // (new foo)(bar) |
|
return true; |
|
}); |
|
|
|
PARENS(AST_Number, function(output) { |
|
var p = output.parent(); |
|
if (p instanceof AST_PropAccess && p.expression === this) { |
|
var value = this.getValue(); |
|
if (value < 0 || /^0/.test(make_num(value))) { |
|
return true; |
|
} |
|
} |
|
}); |
|
|
|
PARENS(AST_BigInt, function(output) { |
|
var p = output.parent(); |
|
if (p instanceof AST_PropAccess && p.expression === this) { |
|
var value = this.getValue(); |
|
if (value.startsWith("-")) { |
|
return true; |
|
} |
|
} |
|
}); |
|
|
|
PARENS([ AST_Assign, AST_Conditional ], function(output) { |
|
var p = output.parent(); |
|
// !(a = false) → true |
|
if (p instanceof AST_Unary) |
|
return true; |
|
// 1 + (a = 2) + 3 → 6, side effect setting a = 2 |
|
if (p instanceof AST_Binary && !(p instanceof AST_Assign)) |
|
return true; |
|
// (a = func)() —or— new (a = Object)() |
|
if (p instanceof AST_Call && p.expression === this) |
|
return true; |
|
// (a = foo) ? bar : baz |
|
if (p instanceof AST_Conditional && p.condition === this) |
|
return true; |
|
// (a = foo)["prop"] —or— (a = foo).prop |
|
if (p instanceof AST_PropAccess && p.expression === this) |
|
return true; |
|
// ({a, b} = {a: 1, b: 2}), a destructuring assignment |
|
if (this instanceof AST_Assign && this.left instanceof AST_Destructuring && this.left.is_array === false) |
|
return true; |
|
}); |
|
|
|
/* -----[ PRINTERS ]----- */ |
|
|
|
DEFPRINT(AST_Directive, function(self, output) { |
|
output.print_string(self.value, self.quote); |
|
output.semicolon(); |
|
}); |
|
|
|
DEFPRINT(AST_Expansion, function (self, output) { |
|
output.print("..."); |
|
self.expression.print(output); |
|
}); |
|
|
|
DEFPRINT(AST_Destructuring, function (self, output) { |
|
output.print(self.is_array ? "[" : "{"); |
|
var len = self.names.length; |
|
self.names.forEach(function (name, i) { |
|
if (i > 0) output.comma(); |
|
name.print(output); |
|
// If the final element is a hole, we need to make sure it |
|
// doesn't look like a trailing comma, by inserting an actual |
|
// trailing comma. |
|
if (i == len - 1 && name instanceof AST_Hole) output.comma(); |
|
}); |
|
output.print(self.is_array ? "]" : "}"); |
|
}); |
|
|
|
DEFPRINT(AST_Debugger, function(self, output) { |
|
output.print("debugger"); |
|
output.semicolon(); |
|
}); |
|
|
|
/* -----[ statements ]----- */ |
|
|
|
function display_body(body, is_toplevel, output, allow_directives) { |
|
var last = body.length - 1; |
|
output.in_directive = allow_directives; |
|
body.forEach(function(stmt, i) { |
|
if (output.in_directive === true && !(stmt instanceof AST_Directive || |
|
stmt instanceof AST_EmptyStatement || |
|
(stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) |
|
)) { |
|
output.in_directive = false; |
|
} |
|
if (!(stmt instanceof AST_EmptyStatement)) { |
|
output.indent(); |
|
stmt.print(output); |
|
if (!(i == last && is_toplevel)) { |
|
output.newline(); |
|
if (is_toplevel) output.newline(); |
|
} |
|
} |
|
if (output.in_directive === true && |
|
stmt instanceof AST_SimpleStatement && |
|
stmt.body instanceof AST_String |
|
) { |
|
output.in_directive = false; |
|
} |
|
}); |
|
output.in_directive = false; |
|
} |
|
|
|
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) { |
|
print_maybe_braced_body(this.body, output); |
|
}); |
|
|
|
DEFPRINT(AST_Statement, function(self, output) { |
|
self.body.print(output); |
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_Toplevel, function(self, output) { |
|
display_body(self.body, true, output, true); |
|
output.print(""); |
|
}); |
|
DEFPRINT(AST_LabeledStatement, function(self, output) { |
|
self.label.print(output); |
|
output.colon(); |
|
self.body.print(output); |
|
}); |
|
DEFPRINT(AST_SimpleStatement, function(self, output) { |
|
self.body.print(output); |
|
output.semicolon(); |
|
}); |
|
function print_braced_empty(self, output) { |
|
output.print("{"); |
|
output.with_indent(output.next_indent(), function() { |
|
output.append_comments(self, true); |
|
}); |
|
output.add_mapping(self.end); |
|
output.print("}"); |
|
} |
|
function print_braced(self, output, allow_directives) { |
|
if (self.body.length > 0) { |
|
output.with_block(function() { |
|
display_body(self.body, false, output, allow_directives); |
|
output.add_mapping(self.end); |
|
}); |
|
} else print_braced_empty(self, output); |
|
} |
|
DEFPRINT(AST_BlockStatement, function(self, output) { |
|
print_braced(self, output); |
|
}); |
|
DEFPRINT(AST_EmptyStatement, function(self, output) { |
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_Do, function(self, output) { |
|
output.print("do"); |
|
output.space(); |
|
make_block(self.body, output); |
|
output.space(); |
|
output.print("while"); |
|
output.space(); |
|
output.with_parens(function() { |
|
self.condition.print(output); |
|
}); |
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_While, function(self, output) { |
|
output.print("while"); |
|
output.space(); |
|
output.with_parens(function() { |
|
self.condition.print(output); |
|
}); |
|
output.space(); |
|
self._do_print_body(output); |
|
}); |
|
DEFPRINT(AST_For, function(self, output) { |
|
output.print("for"); |
|
output.space(); |
|
output.with_parens(function() { |
|
if (self.init) { |
|
if (self.init instanceof AST_DefinitionsLike) { |
|
self.init.print(output); |
|
} else { |
|
parenthesize_for_noin(self.init, output, true); |
|
} |
|
output.print(";"); |
|
output.space(); |
|
} else { |
|
output.print(";"); |
|
} |
|
if (self.condition) { |
|
self.condition.print(output); |
|
output.print(";"); |
|
output.space(); |
|
} else { |
|
output.print(";"); |
|
} |
|
if (self.step) { |
|
self.step.print(output); |
|
} |
|
}); |
|
output.space(); |
|
self._do_print_body(output); |
|
}); |
|
DEFPRINT(AST_ForIn, function(self, output) { |
|
output.print("for"); |
|
if (self.await) { |
|
output.space(); |
|
output.print("await"); |
|
} |
|
output.space(); |
|
output.with_parens(function() { |
|
self.init.print(output); |
|
output.space(); |
|
output.print(self instanceof AST_ForOf ? "of" : "in"); |
|
output.space(); |
|
self.object.print(output); |
|
}); |
|
output.space(); |
|
self._do_print_body(output); |
|
}); |
|
DEFPRINT(AST_With, function(self, output) { |
|
output.print("with"); |
|
output.space(); |
|
output.with_parens(function() { |
|
self.expression.print(output); |
|
}); |
|
output.space(); |
|
self._do_print_body(output); |
|
}); |
|
|
|
/* -----[ functions ]----- */ |
|
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) { |
|
var self = this; |
|
if (!nokeyword) { |
|
if (self.async) { |
|
output.print("async"); |
|
output.space(); |
|
} |
|
output.print("function"); |
|
if (self.is_generator) { |
|
output.star(); |
|
} |
|
if (self.name) { |
|
output.space(); |
|
} |
|
} |
|
if (self.name instanceof AST_Symbol) { |
|
self.name.print(output); |
|
} else if (nokeyword && self.name instanceof AST_Node) { |
|
output.with_square(function() { |
|
self.name.print(output); // Computed method name |
|
}); |
|
} |
|
output.with_parens(function() { |
|
self.argnames.forEach(function(arg, i) { |
|
if (i) output.comma(); |
|
arg.print(output); |
|
}); |
|
}); |
|
output.space(); |
|
print_braced(self, output, true); |
|
}); |
|
DEFPRINT(AST_Lambda, function(self, output) { |
|
self._do_print(output); |
|
output.gc_scope(self); |
|
}); |
|
|
|
DEFPRINT(AST_PrefixedTemplateString, function(self, output) { |
|
var tag = self.prefix; |
|
var parenthesize_tag = tag instanceof AST_Lambda |
|
|| tag instanceof AST_Binary |
|
|| tag instanceof AST_Conditional |
|
|| tag instanceof AST_Sequence |
|
|| tag instanceof AST_Unary |
|
|| tag instanceof AST_Dot && tag.expression instanceof AST_Object; |
|
if (parenthesize_tag) output.print("("); |
|
self.prefix.print(output); |
|
if (parenthesize_tag) output.print(")"); |
|
self.template_string.print(output); |
|
}); |
|
DEFPRINT(AST_TemplateString, function(self, output) { |
|
var is_tagged = output.parent() instanceof AST_PrefixedTemplateString; |
|
|
|
output.print("`"); |
|
for (var i = 0; i < self.segments.length; i++) { |
|
if (!(self.segments[i] instanceof AST_TemplateSegment)) { |
|
output.print("${"); |
|
self.segments[i].print(output); |
|
output.print("}"); |
|
} else if (is_tagged) { |
|
output.print(self.segments[i].raw); |
|
} else { |
|
output.print_template_string_chars(self.segments[i].value); |
|
} |
|
} |
|
output.print("`"); |
|
}); |
|
DEFPRINT(AST_TemplateSegment, function(self, output) { |
|
output.print_template_string_chars(self.value); |
|
}); |
|
|
|
AST_Arrow.DEFMETHOD("_do_print", function(output) { |
|
var self = this; |
|
var parent = output.parent(); |
|
var needs_parens = (parent instanceof AST_Binary && |
|
!(parent instanceof AST_Assign) && |
|
!(parent instanceof AST_DefaultAssign)) || |
|
parent instanceof AST_Unary || |
|
(parent instanceof AST_Call && self === parent.expression); |
|
if (needs_parens) { output.print("("); } |
|
if (self.async) { |
|
output.print("async"); |
|
output.space(); |
|
} |
|
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) { |
|
self.argnames[0].print(output); |
|
} else { |
|
output.with_parens(function() { |
|
self.argnames.forEach(function(arg, i) { |
|
if (i) output.comma(); |
|
arg.print(output); |
|
}); |
|
}); |
|
} |
|
output.space(); |
|
output.print("=>"); |
|
output.space(); |
|
const first_statement = self.body[0]; |
|
if ( |
|
self.body.length === 1 |
|
&& first_statement instanceof AST_Return |
|
) { |
|
const returned = first_statement.value; |
|
if (!returned) { |
|
output.print("{}"); |
|
} else if (left_is_object(returned)) { |
|
output.print("("); |
|
returned.print(output); |
|
output.print(")"); |
|
} else { |
|
returned.print(output); |
|
} |
|
} else { |
|
print_braced(self, output); |
|
} |
|
if (needs_parens) { output.print(")"); } |
|
output.gc_scope(self); |
|
}); |
|
|
|
/* -----[ exits ]----- */ |
|
AST_Exit.DEFMETHOD("_do_print", function(output, kind) { |
|
output.print(kind); |
|
if (this.value) { |
|
output.space(); |
|
const comments = this.value.start.comments_before; |
|
if (comments && comments.length && !output.printed_comments.has(comments)) { |
|
output.print("("); |
|
this.value.print(output); |
|
output.print(")"); |
|
} else { |
|
this.value.print(output); |
|
} |
|
} |
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_Return, function(self, output) { |
|
self._do_print(output, "return"); |
|
}); |
|
DEFPRINT(AST_Throw, function(self, output) { |
|
self._do_print(output, "throw"); |
|
}); |
|
|
|
/* -----[ yield ]----- */ |
|
|
|
DEFPRINT(AST_Yield, function(self, output) { |
|
var star = self.is_star ? "*" : ""; |
|
output.print("yield" + star); |
|
if (self.expression) { |
|
output.space(); |
|
self.expression.print(output); |
|
} |
|
}); |
|
|
|
DEFPRINT(AST_Await, function(self, output) { |
|
output.print("await"); |
|
output.space(); |
|
var e = self.expression; |
|
var parens = !( |
|
e instanceof AST_Call |
|
|| e instanceof AST_SymbolRef |
|
|| e instanceof AST_PropAccess |
|
|| e instanceof AST_Unary |
|
|| e instanceof AST_Constant |
|
|| e instanceof AST_Await |
|
|| e instanceof AST_Object |
|
); |
|
if (parens) output.print("("); |
|
self.expression.print(output); |
|
if (parens) output.print(")"); |
|
}); |
|
|
|
/* -----[ loop control ]----- */ |
|
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind) { |
|
output.print(kind); |
|
if (this.label) { |
|
output.space(); |
|
this.label.print(output); |
|
} |
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_Break, function(self, output) { |
|
self._do_print(output, "break"); |
|
}); |
|
DEFPRINT(AST_Continue, function(self, output) { |
|
self._do_print(output, "continue"); |
|
}); |
|
|
|
/* -----[ if ]----- */ |
|
function make_then(self, output) { |
|
var b = self.body; |
|
if (output.option("braces") |
|
|| output.option("ie8") && b instanceof AST_Do) |
|
return make_block(b, output); |
|
// The squeezer replaces "block"-s that contain only a single |
|
// statement with the statement itself; technically, the AST |
|
// is correct, but this can create problems when we output an |
|
// IF having an ELSE clause where the THEN clause ends in an |
|
// IF *without* an ELSE block (then the outer ELSE would refer |
|
// to the inner IF). This function checks for this case and |
|
// adds the block braces if needed. |
|
if (!b) return output.force_semicolon(); |
|
while (true) { |
|
if (b instanceof AST_If) { |
|
if (!b.alternative) { |
|
make_block(self.body, output); |
|
return; |
|
} |
|
b = b.alternative; |
|
} else if (b instanceof AST_StatementWithBody) { |
|
b = b.body; |
|
} else break; |
|
} |
|
print_maybe_braced_body(self.body, output); |
|
} |
|
DEFPRINT(AST_If, function(self, output) { |
|
output.print("if"); |
|
output.space(); |
|
output.with_parens(function() { |
|
self.condition.print(output); |
|
}); |
|
output.space(); |
|
if (self.alternative) { |
|
make_then(self, output); |
|
output.space(); |
|
output.print("else"); |
|
output.space(); |
|
if (self.alternative instanceof AST_If) |
|
self.alternative.print(output); |
|
else |
|
print_maybe_braced_body(self.alternative, output); |
|
} else { |
|
self._do_print_body(output); |
|
} |
|
}); |
|
|
|
/* -----[ switch ]----- */ |
|
DEFPRINT(AST_Switch, function(self, output) { |
|
output.print("switch"); |
|
output.space(); |
|
output.with_parens(function() { |
|
self.expression.print(output); |
|
}); |
|
output.space(); |
|
var last = self.body.length - 1; |
|
if (last < 0) print_braced_empty(self, output); |
|
else output.with_block(function() { |
|
self.body.forEach(function(branch, i) { |
|
output.indent(true); |
|
branch.print(output); |
|
if (i < last && branch.body.length > 0) |
|
output.newline(); |
|
}); |
|
}); |
|
}); |
|
AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) { |
|
output.newline(); |
|
this.body.forEach(function(stmt) { |
|
output.indent(); |
|
stmt.print(output); |
|
output.newline(); |
|
}); |
|
}); |
|
DEFPRINT(AST_Default, function(self, output) { |
|
output.print("default:"); |
|
self._do_print_body(output); |
|
}); |
|
DEFPRINT(AST_Case, function(self, output) { |
|
output.print("case"); |
|
output.space(); |
|
self.expression.print(output); |
|
output.print(":"); |
|
self._do_print_body(output); |
|
}); |
|
|
|
/* -----[ exceptions ]----- */ |
|
DEFPRINT(AST_Try, function(self, output) { |
|
output.print("try"); |
|
output.space(); |
|
self.body.print(output); |
|
if (self.bcatch) { |
|
output.space(); |
|
self.bcatch.print(output); |
|
} |
|
if (self.bfinally) { |
|
output.space(); |
|
self.bfinally.print(output); |
|
} |
|
}); |
|
DEFPRINT(AST_TryBlock, function(self, output) { |
|
print_braced(self, output); |
|
}); |
|
DEFPRINT(AST_Catch, function(self, output) { |
|
output.print("catch"); |
|
if (self.argname) { |
|
output.space(); |
|
output.with_parens(function() { |
|
self.argname.print(output); |
|
}); |
|
} |
|
output.space(); |
|
print_braced(self, output); |
|
}); |
|
DEFPRINT(AST_Finally, function(self, output) { |
|
output.print("finally"); |
|
output.space(); |
|
print_braced(self, output); |
|
}); |
|
|
|
/* -----[ var/const ]----- */ |
|
AST_DefinitionsLike.DEFMETHOD("_do_print", function(output, kind) { |
|
output.print(kind); |
|
output.space(); |
|
this.definitions.forEach(function(def, i) { |
|
if (i) output.comma(); |
|
def.print(output); |
|
}); |
|
var p = output.parent(); |
|
var in_for = p instanceof AST_For || p instanceof AST_ForIn; |
|
var output_semicolon = !in_for || p && p.init !== this; |
|
if (output_semicolon) |
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_Let, function(self, output) { |
|
self._do_print(output, "let"); |
|
}); |
|
DEFPRINT(AST_Var, function(self, output) { |
|
self._do_print(output, "var"); |
|
}); |
|
DEFPRINT(AST_Const, function(self, output) { |
|
self._do_print(output, "const"); |
|
}); |
|
DEFPRINT(AST_Using, function(self, output) { |
|
self._do_print(output, self.await ? "await using" : "using"); |
|
}); |
|
DEFPRINT(AST_Import, function(self, output) { |
|
output.print("import"); |
|
output.space(); |
|
if (self.imported_name) { |
|
self.imported_name.print(output); |
|
} |
|
if (self.imported_name && self.imported_names) { |
|
output.print(","); |
|
output.space(); |
|
} |
|
if (self.imported_names) { |
|
if (self.imported_names.length === 1 && |
|
self.imported_names[0].foreign_name.name === "*" && |
|
!self.imported_names[0].foreign_name.quote) { |
|
self.imported_names[0].print(output); |
|
} else { |
|
output.print("{"); |
|
self.imported_names.forEach(function (name_import, i) { |
|
output.space(); |
|
name_import.print(output); |
|
if (i < self.imported_names.length - 1) { |
|
output.print(","); |
|
} |
|
}); |
|
output.space(); |
|
output.print("}"); |
|
} |
|
} |
|
if (self.imported_name || self.imported_names) { |
|
output.space(); |
|
output.print("from"); |
|
output.space(); |
|
} |
|
self.module_name.print(output); |
|
if (self.attributes) { |
|
output.print("with"); |
|
self.attributes.print(output); |
|
} |
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_ImportMeta, function(self, output) { |
|
output.print("import.meta"); |
|
}); |
|
|
|
DEFPRINT(AST_NameMapping, function(self, output) { |
|
var is_import = output.parent() instanceof AST_Import; |
|
var definition = self.name.definition(); |
|
var foreign_name = self.foreign_name; |
|
var names_are_different = |
|
(definition && definition.mangled_name || self.name.name) !== |
|
foreign_name.name; |
|
if (!names_are_different && |
|
foreign_name.name === "*" && |
|
!!foreign_name.quote != !!self.name.quote) { |
|
// export * as "*" |
|
names_are_different = true; |
|
} |
|
var foreign_name_is_name = !foreign_name.quote; |
|
if (names_are_different) { |
|
if (is_import) { |
|
if (foreign_name_is_name) { |
|
output.print(foreign_name.name); |
|
} else { |
|
output.print_string(foreign_name.name, foreign_name.quote); |
|
} |
|
} else { |
|
if (!self.name.quote) { |
|
self.name.print(output); |
|
} else { |
|
output.print_string(self.name.name, self.name.quote); |
|
} |
|
|
|
} |
|
output.space(); |
|
output.print("as"); |
|
output.space(); |
|
if (is_import) { |
|
self.name.print(output); |
|
} else { |
|
if (foreign_name_is_name) { |
|
output.print(foreign_name.name); |
|
} else { |
|
output.print_string(foreign_name.name, foreign_name.quote); |
|
} |
|
} |
|
} else { |
|
if (!self.name.quote) { |
|
self.name.print(output); |
|
} else { |
|
output.print_string(self.name.name, self.name.quote); |
|
} |
|
} |
|
}); |
|
|
|
DEFPRINT(AST_Export, function(self, output) { |
|
output.print("export"); |
|
output.space(); |
|
if (self.is_default) { |
|
output.print("default"); |
|
output.space(); |
|
} |
|
if (self.exported_names) { |
|
if (self.exported_names.length === 1 && |
|
self.exported_names[0].name.name === "*" && |
|
!self.exported_names[0].name.quote) { |
|
self.exported_names[0].print(output); |
|
} else { |
|
output.print("{"); |
|
self.exported_names.forEach(function(name_export, i) { |
|
output.space(); |
|
name_export.print(output); |
|
if (i < self.exported_names.length - 1) { |
|
output.print(","); |
|
} |
|
}); |
|
output.space(); |
|
output.print("}"); |
|
} |
|
} else if (self.exported_value) { |
|
self.exported_value.print(output); |
|
} else if (self.exported_definition) { |
|
self.exported_definition.print(output); |
|
if (self.exported_definition instanceof AST_Definitions) return; |
|
} |
|
if (self.module_name) { |
|
output.space(); |
|
output.print("from"); |
|
output.space(); |
|
self.module_name.print(output); |
|
} |
|
if (self.attributes) { |
|
output.print("with"); |
|
self.attributes.print(output); |
|
} |
|
if (self.exported_value |
|
&& !(self.exported_value instanceof AST_Defun || |
|
self.exported_value instanceof AST_Function || |
|
self.exported_value instanceof AST_Class) |
|
|| self.module_name |
|
|| self.exported_names |
|
) { |
|
output.semicolon(); |
|
} |
|
}); |
|
|
|
function parenthesize_for_noin(node, output, noin) { |
|
var parens = false; |
|
// need to take some precautions here: |
|
// https://github.com/mishoo/UglifyJS2/issues/60 |
|
if (noin) { |
|
parens = walk(node, node => { |
|
// Don't go into scopes -- except arrow functions: |
|
// https://github.com/terser/terser/issues/1019#issuecomment-877642607 |
|
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) { |
|
return true; |
|
} |
|
if ( |
|
node instanceof AST_Binary && node.operator == "in" |
|
|| node instanceof AST_PrivateIn |
|
) { |
|
return walk_abort; // makes walk() return true |
|
} |
|
}); |
|
} |
|
node.print(output, parens); |
|
} |
|
|
|
DEFPRINT(AST_VarDefLike, function(self, output) { |
|
self.name.print(output); |
|
if (self.value) { |
|
output.space(); |
|
output.print("="); |
|
output.space(); |
|
var p = output.parent(1); |
|
var noin = p instanceof AST_For || p instanceof AST_ForIn; |
|
parenthesize_for_noin(self.value, output, noin); |
|
} |
|
}); |
|
|
|
/* -----[ other expressions ]----- */ |
|
DEFPRINT(AST_Call, function(self, output) { |
|
self.expression.print(output); |
|
if (self instanceof AST_New && self.args.length === 0) |
|
return; |
|
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) { |
|
output.add_mapping(self.start); |
|
} |
|
if (self.optional) output.print("?."); |
|
output.with_parens(function() { |
|
self.args.forEach(function(expr, i) { |
|
if (i) output.comma(); |
|
expr.print(output); |
|
}); |
|
}); |
|
}); |
|
DEFPRINT(AST_New, function(self, output) { |
|
output.print("new"); |
|
output.space(); |
|
AST_Call.prototype._codegen(self, output); |
|
}); |
|
|
|
AST_Sequence.DEFMETHOD("_do_print", function(output) { |
|
this.expressions.forEach(function(node, index) { |
|
if (index > 0) { |
|
output.comma(); |
|
if (output.should_break()) { |
|
output.newline(); |
|
output.indent(); |
|
} |
|
} |
|
node.print(output); |
|
}); |
|
}); |
|
DEFPRINT(AST_Sequence, function(self, output) { |
|
self._do_print(output); |
|
// var p = output.parent(); |
|
// if (p instanceof AST_Statement) { |
|
// output.with_indent(output.next_indent(), function(){ |
|
// self._do_print(output); |
|
// }); |
|
// } else { |
|
// self._do_print(output); |
|
// } |
|
}); |
|
DEFPRINT(AST_Dot, function(self, output) { |
|
var expr = self.expression; |
|
expr.print(output); |
|
var prop = self.property; |
|
var print_computed = ALL_RESERVED_WORDS.has(prop) |
|
? output.option("ie8") |
|
: !is_identifier_string( |
|
prop, |
|
output.option("ecma") >= 2015 && !output.option("safari10") |
|
); |
|
|
|
if (self.optional) output.print("?."); |
|
|
|
if (print_computed) { |
|
output.print("["); |
|
output.add_mapping(self.end); |
|
output.print_string(prop); |
|
output.print("]"); |
|
} else { |
|
if (expr instanceof AST_Number && expr.getValue() >= 0) { |
|
if (!/[xa-f.)]/i.test(output.last())) { |
|
output.print("."); |
|
} |
|
} |
|
if (!self.optional) output.print("."); |
|
// the name after dot would be mapped about here. |
|
output.add_mapping(self.end); |
|
output.print_name(prop); |
|
} |
|
}); |
|
DEFPRINT(AST_DotHash, function(self, output) { |
|
var expr = self.expression; |
|
expr.print(output); |
|
var prop = self.property; |
|
|
|
if (self.optional) output.print("?"); |
|
output.print(".#"); |
|
output.add_mapping(self.end); |
|
output.print_name(prop); |
|
}); |
|
DEFPRINT(AST_Sub, function(self, output) { |
|
self.expression.print(output); |
|
if (self.optional) output.print("?."); |
|
output.print("["); |
|
self.property.print(output); |
|
output.print("]"); |
|
}); |
|
DEFPRINT(AST_Chain, function(self, output) { |
|
self.expression.print(output); |
|
}); |
|
DEFPRINT(AST_UnaryPrefix, function(self, output) { |
|
var op = self.operator; |
|
if (op === "--" && output.last().endsWith("!")) { |
|
// avoid printing "<!--" |
|
output.print(" "); |
|
} |
|
output.print(op); |
|
if (/^[a-z]/i.test(op) |
|
|| (/[+-]$/.test(op) |
|
&& self.expression instanceof AST_UnaryPrefix |
|
&& /^[+-]/.test(self.expression.operator))) { |
|
output.space(); |
|
} |
|
self.expression.print(output); |
|
}); |
|
DEFPRINT(AST_UnaryPostfix, function(self, output) { |
|
self.expression.print(output); |
|
output.print(self.operator); |
|
}); |
|
DEFPRINT(AST_Binary, function(self, output) { |
|
var op = self.operator; |
|
self.left.print(output); |
|
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */ |
|
&& output.last().endsWith("--")) { |
|
// space is mandatory to avoid outputting --> |
|
output.print(" "); |
|
} else { |
|
// the space is optional depending on "beautify" |
|
output.space(); |
|
} |
|
output.print(op); |
|
output.space(); |
|
self.right.print(output); |
|
}); |
|
DEFPRINT(AST_Conditional, function(self, output) { |
|
self.condition.print(output); |
|
output.space(); |
|
output.print("?"); |
|
output.space(); |
|
self.consequent.print(output); |
|
output.space(); |
|
output.colon(); |
|
self.alternative.print(output); |
|
}); |
|
|
|
/* -----[ literals ]----- */ |
|
DEFPRINT(AST_Array, function(self, output) { |
|
output.with_square(function() { |
|
var a = self.elements, len = a.length; |
|
if (len > 0) output.space(); |
|
a.forEach(function(exp, i) { |
|
if (i) output.comma(); |
|
exp.print(output); |
|
// If the final element is a hole, we need to make sure it |
|
// doesn't look like a trailing comma, by inserting an actual |
|
// trailing comma. |
|
if (i === len - 1 && exp instanceof AST_Hole) |
|
output.comma(); |
|
}); |
|
if (len > 0) output.space(); |
|
}); |
|
}); |
|
DEFPRINT(AST_Object, function(self, output) { |
|
if (self.properties.length > 0) output.with_block(function() { |
|
self.properties.forEach(function(prop, i) { |
|
if (i) { |
|
output.print(","); |
|
output.newline(); |
|
} |
|
output.indent(); |
|
prop.print(output); |
|
}); |
|
output.newline(); |
|
}); |
|
else print_braced_empty(self, output); |
|
}); |
|
DEFPRINT(AST_Class, function(self, output) { |
|
output.print("class"); |
|
output.space(); |
|
if (self.name) { |
|
self.name.print(output); |
|
output.space(); |
|
} |
|
if (self.extends) { |
|
var parens = ( |
|
!(self.extends instanceof AST_SymbolRef) |
|
&& !(self.extends instanceof AST_PropAccess) |
|
&& !(self.extends instanceof AST_ClassExpression) |
|
&& !(self.extends instanceof AST_Function) |
|
); |
|
output.print("extends"); |
|
if (parens) { |
|
output.print("("); |
|
} else { |
|
output.space(); |
|
} |
|
self.extends.print(output); |
|
if (parens) { |
|
output.print(")"); |
|
} else { |
|
output.space(); |
|
} |
|
} |
|
if (self.properties.length > 0) output.with_block(function() { |
|
self.properties.forEach(function(prop, i) { |
|
if (i) { |
|
output.newline(); |
|
} |
|
output.indent(); |
|
prop.print(output); |
|
}); |
|
output.newline(); |
|
}); |
|
else output.print("{}"); |
|
}); |
|
DEFPRINT(AST_NewTarget, function(self, output) { |
|
output.print("new.target"); |
|
}); |
|
|
|
/** Prints a prop name. Returns whether it can be used as a shorthand. */ |
|
function print_property_name(key, quote, output) { |
|
if (output.option("quote_keys")) { |
|
output.print_string(key); |
|
return false; |
|
} |
|
if ("" + +key == key && key >= 0) { |
|
if (output.option("keep_numbers")) { |
|
output.print(key); |
|
return false; |
|
} |
|
output.print(make_num(key)); |
|
return false; |
|
} |
|
var print_string = ALL_RESERVED_WORDS.has(key) |
|
? output.option("ie8") |
|
: ( |
|
output.option("ecma") < 2015 || output.option("safari10") |
|
? !is_basic_identifier_string(key) |
|
: !is_identifier_string(key, true) |
|
); |
|
if (print_string || (quote && output.option("keep_quoted_props"))) { |
|
output.print_string(key, quote); |
|
return false; |
|
} |
|
output.print_name(key); |
|
return true; |
|
} |
|
|
|
DEFPRINT(AST_ObjectKeyVal, function(self, output) { |
|
function get_name(self) { |
|
var def = self.definition(); |
|
return def ? def.mangled_name || def.name : self.name; |
|
} |
|
|
|
const try_shorthand = output.option("shorthand") && !(self.key instanceof AST_Node); |
|
if ( |
|
try_shorthand |
|
&& self.value instanceof AST_Symbol |
|
&& get_name(self.value) === self.key |
|
&& !ALL_RESERVED_WORDS.has(self.key) |
|
) { |
|
const was_shorthand = print_property_name(self.key, self.quote, output); |
|
if (!was_shorthand) { |
|
output.colon(); |
|
self.value.print(output); |
|
} |
|
} else if ( |
|
try_shorthand |
|
&& self.value instanceof AST_DefaultAssign |
|
&& self.value.left instanceof AST_Symbol |
|
&& get_name(self.value.left) === self.key |
|
) { |
|
const was_shorthand = print_property_name(self.key, self.quote, output); |
|
if (!was_shorthand) { |
|
output.colon(); |
|
self.value.left.print(output); |
|
} |
|
output.space(); |
|
output.print("="); |
|
output.space(); |
|
self.value.right.print(output); |
|
} else { |
|
if (!(self.key instanceof AST_Node)) { |
|
print_property_name(self.key, self.quote, output); |
|
} else { |
|
output.with_square(function() { |
|
self.key.print(output); |
|
}); |
|
} |
|
output.colon(); |
|
self.value.print(output); |
|
} |
|
}); |
|
DEFPRINT(AST_ClassPrivateProperty, (self, output) => { |
|
if (self.static) { |
|
output.print("static"); |
|
output.space(); |
|
} |
|
|
|
output.print("#"); |
|
|
|
print_property_name(self.key.name, undefined, output); |
|
|
|
if (self.value) { |
|
output.print("="); |
|
self.value.print(output); |
|
} |
|
|
|
output.semicolon(); |
|
}); |
|
DEFPRINT(AST_ClassProperty, (self, output) => { |
|
if (self.static) { |
|
output.print("static"); |
|
output.space(); |
|
} |
|
|
|
if (self.key instanceof AST_SymbolClassProperty) { |
|
print_property_name(self.key.name, self.quote, output); |
|
} else { |
|
output.print("["); |
|
self.key.print(output); |
|
output.print("]"); |
|
} |
|
|
|
if (self.value) { |
|
output.print("="); |
|
self.value.print(output); |
|
} |
|
|
|
output.semicolon(); |
|
}); |
|
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, is_private, output) { |
|
var self = this; |
|
if (self.static) { |
|
output.print("static"); |
|
output.space(); |
|
} |
|
if (type) { |
|
output.print(type); |
|
output.space(); |
|
} |
|
if (self.key instanceof AST_SymbolMethod) { |
|
if (is_private) output.print("#"); |
|
print_property_name(self.key.name, self.quote, output); |
|
self.key.add_source_map(output); |
|
} else { |
|
output.with_square(function() { |
|
self.key.print(output); |
|
}); |
|
} |
|
self.value._do_print(output, true); |
|
}); |
|
DEFPRINT(AST_ObjectSetter, function(self, output) { |
|
self._print_getter_setter("set", false, output); |
|
}); |
|
DEFPRINT(AST_ObjectGetter, function(self, output) { |
|
self._print_getter_setter("get", false, output); |
|
}); |
|
DEFPRINT(AST_PrivateSetter, function(self, output) { |
|
self._print_getter_setter("set", true, output); |
|
}); |
|
DEFPRINT(AST_PrivateGetter, function(self, output) { |
|
self._print_getter_setter("get", true, output); |
|
}); |
|
DEFPRINT(AST_ConciseMethod, function(self, output) { |
|
var type; |
|
if (self.value.is_generator && self.value.async) { |
|
type = "async*"; |
|
} else if (self.value.is_generator) { |
|
type = "*"; |
|
} else if (self.value.async) { |
|
type = "async"; |
|
} |
|
self._print_getter_setter(type, false, output); |
|
}); |
|
DEFPRINT(AST_PrivateMethod, function(self, output) { |
|
var type; |
|
if (self.value.is_generator && self.value.async) { |
|
type = "async*"; |
|
} else if (self.value.is_generator) { |
|
type = "*"; |
|
} else if (self.value.async) { |
|
type = "async"; |
|
} |
|
self._print_getter_setter(type, true, output); |
|
}); |
|
DEFPRINT(AST_PrivateIn, function(self, output) { |
|
self.key.print(output); |
|
output.space(); |
|
output.print("in"); |
|
output.space(); |
|
self.value.print(output); |
|
}); |
|
DEFPRINT(AST_SymbolPrivateProperty, function(self, output) { |
|
output.print("#" + self.name); |
|
}); |
|
DEFPRINT(AST_ClassStaticBlock, function (self, output) { |
|
output.print("static"); |
|
output.space(); |
|
print_braced(self, output); |
|
}); |
|
AST_Symbol.DEFMETHOD("_do_print", function(output) { |
|
var def = this.definition(); |
|
output.print_name(def ? def.mangled_name || def.name : this.name); |
|
}); |
|
DEFPRINT(AST_Symbol, function (self, output) { |
|
self._do_print(output); |
|
}); |
|
DEFPRINT(AST_Hole, noop); |
|
DEFPRINT(AST_This, function(self, output) { |
|
output.print("this"); |
|
}); |
|
DEFPRINT(AST_Super, function(self, output) { |
|
output.print("super"); |
|
}); |
|
DEFPRINT(AST_Constant, function(self, output) { |
|
output.print(self.getValue()); |
|
}); |
|
DEFPRINT(AST_String, function(self, output) { |
|
output.print_string(self.getValue(), self.quote, output.in_directive); |
|
}); |
|
DEFPRINT(AST_Number, function(self, output) { |
|
if ((output.option("keep_numbers") || output.use_asm) && self.raw) { |
|
output.print(self.raw); |
|
} else { |
|
output.print(make_num(self.getValue())); |
|
} |
|
}); |
|
DEFPRINT(AST_BigInt, function(self, output) { |
|
if (output.option("keep_numbers") && self.raw) { |
|
output.print(self.raw); |
|
} else { |
|
output.print(self.getValue() + "n"); |
|
} |
|
}); |
|
|
|
const r_slash_script = /(<\s*\/\s*script)/i; |
|
const r_starts_with_script = /^\s*script/i; |
|
const slash_script_replace = (_, $1) => $1.replace("/", "\\/"); |
|
DEFPRINT(AST_RegExp, function(self, output) { |
|
let { source, flags } = self.getValue(); |
|
source = regexp_source_fix(source); |
|
flags = flags ? sort_regexp_flags(flags) : ""; |
|
|
|
// Avoid outputting end of script tag |
|
source = source.replace(r_slash_script, slash_script_replace); |
|
if (r_starts_with_script.test(source) && output.last().endsWith("<")) { |
|
output.print(" "); |
|
} |
|
|
|
output.print(output.to_utf8(`/${source}/${flags}`, false, true)); |
|
|
|
const parent = output.parent(); |
|
if ( |
|
parent instanceof AST_Binary |
|
&& /^\w/.test(parent.operator) |
|
&& parent.left === self |
|
) { |
|
output.print(" "); |
|
} |
|
}); |
|
|
|
/** if, for, while, may or may not have braces surrounding its body */ |
|
function print_maybe_braced_body(stat, output) { |
|
if (output.option("braces")) { |
|
make_block(stat, output); |
|
} else { |
|
if (!stat || stat instanceof AST_EmptyStatement) |
|
output.force_semicolon(); |
|
else if ((stat instanceof AST_DefinitionsLike && !(stat instanceof AST_Var)) || stat instanceof AST_Class) |
|
make_block(stat, output); |
|
else |
|
stat.print(output); |
|
} |
|
} |
|
|
|
function best_of(a) { |
|
var best = a[0], len = best.length; |
|
for (var i = 1; i < a.length; ++i) { |
|
if (a[i].length < len) { |
|
best = a[i]; |
|
len = best.length; |
|
} |
|
} |
|
return best; |
|
} |
|
|
|
function make_num(num) { |
|
var str = num.toString(10).replace(/^0\./, ".").replace("e+", "e"); |
|
var candidates = [ str ]; |
|
if (Math.floor(num) === num) { |
|
if (num < 0) { |
|
candidates.push("-0x" + (-num).toString(16).toLowerCase()); |
|
} else { |
|
candidates.push("0x" + num.toString(16).toLowerCase()); |
|
} |
|
} |
|
var match, len, digits; |
|
if (match = /^\.0+/.exec(str)) { |
|
len = match[0].length; |
|
digits = str.slice(len); |
|
candidates.push(digits + "e-" + (digits.length + len - 1)); |
|
} else if (match = /0+$/.exec(str)) { |
|
len = match[0].length; |
|
candidates.push(str.slice(0, -len) + "e" + len); |
|
} else if (match = /^(\d)\.(\d+)e(-?\d+)$/.exec(str)) { |
|
candidates.push(match[1] + match[2] + "e" + (match[3] - match[2].length)); |
|
} |
|
return best_of(candidates); |
|
} |
|
|
|
function make_block(stmt, output) { |
|
if (!stmt || stmt instanceof AST_EmptyStatement) |
|
output.print("{}"); |
|
else if (stmt instanceof AST_BlockStatement) |
|
stmt.print(output); |
|
else output.with_block(function() { |
|
output.indent(); |
|
stmt.print(output); |
|
output.newline(); |
|
}); |
|
} |
|
|
|
/* -----[ source map generators ]----- */ |
|
|
|
function DEFMAP(nodetype, generator) { |
|
nodetype.forEach(function(nodetype) { |
|
nodetype.DEFMETHOD("add_source_map", generator); |
|
}); |
|
} |
|
|
|
DEFMAP([ |
|
// We could easily add info for ALL nodes, but it seems to me that |
|
// would be quite wasteful, hence this noop in the base class. |
|
AST_Node, |
|
// since the label symbol will mark it |
|
AST_LabeledStatement, |
|
AST_Toplevel, |
|
], noop); |
|
|
|
// XXX: I'm not exactly sure if we need it for all of these nodes, |
|
// or if we should add even more. |
|
DEFMAP([ |
|
AST_Array, |
|
AST_BlockStatement, |
|
AST_Catch, |
|
AST_Class, |
|
AST_Constant, |
|
AST_Debugger, |
|
AST_DefinitionsLike, |
|
AST_Directive, |
|
AST_Finally, |
|
AST_Jump, |
|
AST_Lambda, |
|
AST_New, |
|
AST_Object, |
|
AST_StatementWithBody, |
|
AST_Symbol, |
|
AST_Switch, |
|
AST_SwitchBranch, |
|
AST_TemplateString, |
|
AST_TemplateSegment, |
|
AST_Try, |
|
], function(output) { |
|
output.add_mapping(this.start); |
|
}); |
|
|
|
DEFMAP([ |
|
AST_ObjectGetter, |
|
AST_ObjectSetter, |
|
AST_PrivateGetter, |
|
AST_PrivateSetter, |
|
AST_ConciseMethod, |
|
AST_PrivateMethod, |
|
], function(output) { |
|
output.add_mapping(this.start, false /*name handled below*/); |
|
}); |
|
|
|
DEFMAP([ |
|
AST_SymbolMethod, |
|
AST_SymbolPrivateProperty |
|
], function(output) { |
|
const tok_type = this.end && this.end.type; |
|
if (tok_type === "name" || tok_type === "privatename") { |
|
output.add_mapping(this.end, this.name); |
|
} else { |
|
output.add_mapping(this.end); |
|
} |
|
}); |
|
|
|
DEFMAP([ AST_ObjectProperty ], function(output) { |
|
output.add_mapping(this.start, this.key); |
|
}); |
|
})(); |
|
|
|
const shallow_cmp = (node1, node2) => { |
|
return ( |
|
node1 === null && node2 === null |
|
|| node1.TYPE === node2.TYPE && node1.shallow_cmp(node2) |
|
); |
|
}; |
|
|
|
const equivalent_to = (tree1, tree2) => { |
|
if (!shallow_cmp(tree1, tree2)) return false; |
|
const walk_1_state = [tree1]; |
|
const walk_2_state = [tree2]; |
|
|
|
const walk_1_push = walk_1_state.push.bind(walk_1_state); |
|
const walk_2_push = walk_2_state.push.bind(walk_2_state); |
|
|
|
while (walk_1_state.length && walk_2_state.length) { |
|
const node_1 = walk_1_state.pop(); |
|
const node_2 = walk_2_state.pop(); |
|
|
|
if (!shallow_cmp(node_1, node_2)) return false; |
|
|
|
node_1._children_backwards(walk_1_push); |
|
node_2._children_backwards(walk_2_push); |
|
|
|
if (walk_1_state.length !== walk_2_state.length) { |
|
// Different number of children |
|
return false; |
|
} |
|
} |
|
|
|
return walk_1_state.length == 0 && walk_2_state.length == 0; |
|
}; |
|
|
|
const pass_through = () => true; |
|
|
|
AST_Node.prototype.shallow_cmp = function () { |
|
throw new Error("did not find a shallow_cmp function for " + this.constructor.name); |
|
}; |
|
|
|
AST_Debugger.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Directive.prototype.shallow_cmp = function(other) { |
|
return this.value === other.value; |
|
}; |
|
|
|
AST_SimpleStatement.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Block.prototype.shallow_cmp = pass_through; |
|
|
|
AST_EmptyStatement.prototype.shallow_cmp = pass_through; |
|
|
|
AST_LabeledStatement.prototype.shallow_cmp = function(other) { |
|
return this.label.name === other.label.name; |
|
}; |
|
|
|
AST_Do.prototype.shallow_cmp = pass_through; |
|
|
|
AST_While.prototype.shallow_cmp = pass_through; |
|
|
|
AST_For.prototype.shallow_cmp = function(other) { |
|
return (this.init == null ? other.init == null : this.init === other.init) && (this.condition == null ? other.condition == null : this.condition === other.condition) && (this.step == null ? other.step == null : this.step === other.step); |
|
}; |
|
|
|
AST_ForIn.prototype.shallow_cmp = pass_through; |
|
|
|
AST_ForOf.prototype.shallow_cmp = pass_through; |
|
|
|
AST_With.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Toplevel.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Expansion.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Lambda.prototype.shallow_cmp = function(other) { |
|
return this.is_generator === other.is_generator && this.async === other.async; |
|
}; |
|
|
|
AST_Destructuring.prototype.shallow_cmp = function(other) { |
|
return this.is_array === other.is_array; |
|
}; |
|
|
|
AST_PrefixedTemplateString.prototype.shallow_cmp = pass_through; |
|
|
|
AST_TemplateString.prototype.shallow_cmp = pass_through; |
|
|
|
AST_TemplateSegment.prototype.shallow_cmp = function(other) { |
|
return this.value === other.value; |
|
}; |
|
|
|
AST_Jump.prototype.shallow_cmp = pass_through; |
|
|
|
AST_LoopControl.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Await.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Yield.prototype.shallow_cmp = function(other) { |
|
return this.is_star === other.is_star; |
|
}; |
|
|
|
AST_If.prototype.shallow_cmp = function(other) { |
|
return this.alternative == null ? other.alternative == null : this.alternative === other.alternative; |
|
}; |
|
|
|
AST_Switch.prototype.shallow_cmp = pass_through; |
|
|
|
AST_SwitchBranch.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Try.prototype.shallow_cmp = function(other) { |
|
return (this.body === other.body) && (this.bcatch == null ? other.bcatch == null : this.bcatch === other.bcatch) && (this.bfinally == null ? other.bfinally == null : this.bfinally === other.bfinally); |
|
}; |
|
|
|
AST_Catch.prototype.shallow_cmp = function(other) { |
|
return this.argname == null ? other.argname == null : this.argname === other.argname; |
|
}; |
|
|
|
AST_Finally.prototype.shallow_cmp = pass_through; |
|
|
|
AST_DefinitionsLike.prototype.shallow_cmp = pass_through; |
|
|
|
AST_VarDefLike.prototype.shallow_cmp = function(other) { |
|
return this.value == null ? other.value == null : this.value === other.value; |
|
}; |
|
|
|
AST_NameMapping.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Import.prototype.shallow_cmp = function(other) { |
|
return (this.imported_name == null ? other.imported_name == null : this.imported_name === other.imported_name) && (this.imported_names == null ? other.imported_names == null : this.imported_names === other.imported_names) && (this.attributes == null ? other.attributes == null : this.attributes === other.attributes); |
|
}; |
|
|
|
AST_ImportMeta.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Export.prototype.shallow_cmp = function(other) { |
|
return (this.exported_definition == null ? other.exported_definition == null : this.exported_definition === other.exported_definition) && (this.exported_value == null ? other.exported_value == null : this.exported_value === other.exported_value) && (this.exported_names == null ? other.exported_names == null : this.exported_names === other.exported_names) && (this.attributes == null ? other.attributes == null : this.attributes === other.attributes) && this.module_name === other.module_name && this.is_default === other.is_default; |
|
}; |
|
|
|
AST_Call.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Sequence.prototype.shallow_cmp = pass_through; |
|
|
|
AST_PropAccess.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Chain.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Dot.prototype.shallow_cmp = function(other) { |
|
return ( |
|
this.property === other.property |
|
&& !!this.quote === !!other.quote |
|
); |
|
}; |
|
|
|
AST_DotHash.prototype.shallow_cmp = function(other) { |
|
return this.property === other.property; |
|
}; |
|
|
|
AST_Unary.prototype.shallow_cmp = function(other) { |
|
return this.operator === other.operator; |
|
}; |
|
|
|
AST_Binary.prototype.shallow_cmp = function(other) { |
|
return this.operator === other.operator; |
|
}; |
|
|
|
AST_PrivateIn.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Conditional.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Array.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Object.prototype.shallow_cmp = pass_through; |
|
|
|
AST_ObjectProperty.prototype.shallow_cmp = pass_through; |
|
|
|
AST_ObjectKeyVal.prototype.shallow_cmp = function(other) { |
|
return this.key === other.key && this.quote === other.quote; |
|
}; |
|
|
|
AST_ObjectSetter.prototype.shallow_cmp = function(other) { |
|
return this.static === other.static; |
|
}; |
|
|
|
AST_ObjectGetter.prototype.shallow_cmp = function(other) { |
|
return this.static === other.static; |
|
}; |
|
|
|
AST_ConciseMethod.prototype.shallow_cmp = function(other) { |
|
return this.static === other.static; |
|
}; |
|
|
|
AST_PrivateMethod.prototype.shallow_cmp = function(other) { |
|
return this.static === other.static; |
|
}; |
|
|
|
AST_Class.prototype.shallow_cmp = function(other) { |
|
return (this.name == null ? other.name == null : this.name === other.name) && (this.extends == null ? other.extends == null : this.extends === other.extends); |
|
}; |
|
|
|
AST_ClassProperty.prototype.shallow_cmp = function(other) { |
|
return this.static === other.static |
|
&& (typeof this.key === "string" |
|
? this.key === other.key |
|
: true /* AST_Node handled elsewhere */); |
|
}; |
|
|
|
AST_ClassPrivateProperty.prototype.shallow_cmp = function(other) { |
|
return this.static === other.static; |
|
}; |
|
|
|
AST_Symbol.prototype.shallow_cmp = function(other) { |
|
return this.name === other.name; |
|
}; |
|
|
|
AST_NewTarget.prototype.shallow_cmp = pass_through; |
|
|
|
AST_This.prototype.shallow_cmp = pass_through; |
|
|
|
AST_Super.prototype.shallow_cmp = pass_through; |
|
|
|
AST_String.prototype.shallow_cmp = function(other) { |
|
return this.value === other.value; |
|
}; |
|
|
|
AST_Number.prototype.shallow_cmp = function(other) { |
|
return this.value === other.value; |
|
}; |
|
|
|
AST_BigInt.prototype.shallow_cmp = function(other) { |
|
return this.value === other.value; |
|
}; |
|
|
|
AST_RegExp.prototype.shallow_cmp = function (other) { |
|
return ( |
|
this.value.flags === other.value.flags |
|
&& this.value.source === other.value.source |
|
); |
|
}; |
|
|
|
AST_Atom.prototype.shallow_cmp = pass_through; |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
const MASK_EXPORT_DONT_MANGLE = 1 << 0; |
|
const MASK_EXPORT_WANT_MANGLE = 1 << 1; |
|
|
|
let function_defs = null; |
|
let unmangleable_names = null; |
|
/** |
|
* When defined, there is a function declaration somewhere that's inside of a block. |
|
* See https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics |
|
*/ |
|
let scopes_with_block_defuns = null; |
|
|
|
class SymbolDef { |
|
constructor(scope, orig, init) { |
|
this.name = orig.name; |
|
this.orig = [ orig ]; |
|
this.init = init; |
|
this.eliminated = 0; |
|
this.assignments = 0; |
|
this.scope = scope; |
|
this.replaced = 0; |
|
this.global = false; |
|
this.export = 0; |
|
this.mangled_name = null; |
|
this.undeclared = false; |
|
this.id = SymbolDef.next_id++; |
|
this.chained = false; |
|
this.direct_access = false; |
|
this.escaped = 0; |
|
this.recursive_refs = 0; |
|
this.references = []; |
|
this.should_replace = undefined; |
|
this.single_use = false; |
|
this.fixed = false; |
|
Object.seal(this); |
|
} |
|
fixed_value() { |
|
if (!this.fixed || this.fixed instanceof AST_Node) return this.fixed; |
|
return this.fixed(); |
|
} |
|
unmangleable(options) { |
|
if (!options) options = {}; |
|
|
|
if ( |
|
function_defs && |
|
function_defs.has(this.id) && |
|
keep_name(options.keep_fnames, this.orig[0].name) |
|
) return true; |
|
|
|
return this.global && !options.toplevel |
|
|| (this.export & MASK_EXPORT_DONT_MANGLE) |
|
|| this.undeclared |
|
|| !options.eval && this.scope.pinned() |
|
|| (this.orig[0] instanceof AST_SymbolLambda |
|
|| this.orig[0] instanceof AST_SymbolDefun) && keep_name(options.keep_fnames, this.orig[0].name) |
|
|| this.orig[0] instanceof AST_SymbolMethod |
|
|| (this.orig[0] instanceof AST_SymbolClass |
|
|| this.orig[0] instanceof AST_SymbolDefClass) && keep_name(options.keep_classnames, this.orig[0].name); |
|
} |
|
mangle(options) { |
|
const cache = options.cache && options.cache.props; |
|
if (this.global && cache && cache.has(this.name)) { |
|
this.mangled_name = cache.get(this.name); |
|
} else if (!this.mangled_name && !this.unmangleable(options)) { |
|
var s = this.scope; |
|
var sym = this.orig[0]; |
|
if (options.ie8 && sym instanceof AST_SymbolLambda) |
|
s = s.parent_scope; |
|
const redefinition = redefined_catch_def(this); |
|
this.mangled_name = redefinition |
|
? redefinition.mangled_name || redefinition.name |
|
: s.next_mangled(options, this); |
|
if (this.global && cache) { |
|
cache.set(this.name, this.mangled_name); |
|
} |
|
} |
|
} |
|
} |
|
|
|
SymbolDef.next_id = 1; |
|
|
|
function redefined_catch_def(def) { |
|
if (def.orig[0] instanceof AST_SymbolCatch |
|
&& def.scope.is_block_scope() |
|
) { |
|
return def.scope.get_defun_scope().variables.get(def.name); |
|
} |
|
} |
|
|
|
AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = undefined, toplevel = this } = {}) { |
|
options = defaults(options, { |
|
cache: null, |
|
ie8: false, |
|
safari10: false, |
|
module: false, |
|
}); |
|
|
|
if (!(toplevel instanceof AST_Toplevel)) { |
|
throw new Error("Invalid toplevel scope"); |
|
} |
|
|
|
// pass 1: setup scope chaining and handle definitions |
|
var scope = this.parent_scope = parent_scope; |
|
var labels = new Map(); |
|
var defun = null; |
|
var in_destructuring = null; |
|
var for_scopes = []; |
|
var tw = new TreeWalker((node, descend) => { |
|
if (node.is_block_scope()) { |
|
const save_scope = scope; |
|
node.block_scope = scope = new AST_Scope(node); |
|
scope._block_scope = true; |
|
scope.init_scope_vars(save_scope); |
|
scope.uses_with = save_scope.uses_with; |
|
scope.uses_eval = save_scope.uses_eval; |
|
|
|
if (options.safari10) { |
|
if (node instanceof AST_For || node instanceof AST_ForIn || node instanceof AST_ForOf) { |
|
for_scopes.push(scope); |
|
} |
|
} |
|
|
|
if (node instanceof AST_Switch) { |
|
// XXX: HACK! Ensure the switch expression gets the correct scope (the parent scope) and the body gets the contained scope |
|
// AST_Switch has a scope within the body, but it itself "is a block scope" |
|
// This means the switched expression has to belong to the outer scope |
|
// while the body inside belongs to the switch itself. |
|
// This is pretty nasty and warrants an AST change |
|
const the_block_scope = scope; |
|
scope = save_scope; |
|
node.expression.walk(tw); |
|
scope = the_block_scope; |
|
for (let i = 0; i < node.body.length; i++) { |
|
node.body[i].walk(tw); |
|
} |
|
} else { |
|
descend(); |
|
} |
|
scope = save_scope; |
|
return true; |
|
} |
|
if (node instanceof AST_Destructuring) { |
|
const save_destructuring = in_destructuring; |
|
in_destructuring = node; |
|
descend(); |
|
in_destructuring = save_destructuring; |
|
return true; |
|
} |
|
if (node instanceof AST_Scope) { |
|
node.init_scope_vars(scope); |
|
var save_scope = scope; |
|
var save_defun = defun; |
|
var save_labels = labels; |
|
defun = scope = node; |
|
labels = new Map(); |
|
descend(); |
|
scope = save_scope; |
|
defun = save_defun; |
|
labels = save_labels; |
|
return true; // don't descend again in TreeWalker |
|
} |
|
if (node instanceof AST_LabeledStatement) { |
|
var l = node.label; |
|
if (labels.has(l.name)) { |
|
throw new Error(string_template("Label {name} defined twice", l)); |
|
} |
|
labels.set(l.name, l); |
|
descend(); |
|
labels.delete(l.name); |
|
return true; // no descend again |
|
} |
|
if (node instanceof AST_With) { |
|
for (var s = scope; s; s = s.parent_scope) |
|
s.uses_with = true; |
|
return; |
|
} |
|
if (node instanceof AST_Symbol) { |
|
node.scope = scope; |
|
} |
|
if (node instanceof AST_Label) { |
|
node.thedef = node; |
|
node.references = []; |
|
} |
|
if (node instanceof AST_SymbolLambda) { |
|
defun.def_function(node, node.name == "arguments" ? undefined : defun); |
|
} else if (node instanceof AST_SymbolDefun) { |
|
// Careful here, the scope where this should be defined is |
|
// the parent scope. The reason is that we enter a new |
|
// scope when we encounter the AST_Defun node (which is |
|
// instanceof AST_Scope) but we get to the symbol a bit |
|
// later. |
|
const closest_scope = defun.parent_scope; |
|
|
|
// In strict mode, function definitions are block-scoped |
|
node.scope = tw.directives["use strict"] |
|
? closest_scope |
|
: closest_scope.get_defun_scope(); |
|
|
|
mark_export(node.scope.def_function(node, defun), 1); |
|
} else if (node instanceof AST_SymbolClass) { |
|
mark_export(defun.def_variable(node, defun), 1); |
|
} else if (node instanceof AST_SymbolImport) { |
|
scope.def_variable(node); |
|
} else if (node instanceof AST_SymbolDefClass) { |
|
// This deals with the name of the class being available |
|
// inside the class. |
|
mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1); |
|
} else if ( |
|
node instanceof AST_SymbolVar |
|
|| node instanceof AST_SymbolLet |
|
|| node instanceof AST_SymbolConst |
|
|| node instanceof AST_SymbolUsing |
|
|| node instanceof AST_SymbolCatch |
|
) { |
|
var def; |
|
if (node instanceof AST_SymbolBlockDeclaration) { |
|
def = scope.def_variable(node, null); |
|
} else { |
|
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined); |
|
} |
|
if (!def.orig.every((sym) => { |
|
if (sym === node) return true; |
|
if (node instanceof AST_SymbolBlockDeclaration) { |
|
return sym instanceof AST_SymbolLambda; |
|
} |
|
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst || sym instanceof AST_SymbolUsing); |
|
})) { |
|
js_error( |
|
`"${node.name}" is redeclared`, |
|
node.start.file, |
|
node.start.line, |
|
node.start.col, |
|
node.start.pos |
|
); |
|
} |
|
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2); |
|
if (defun !== scope) { |
|
node.mark_enclosed(); |
|
var def = scope.find_variable(node); |
|
if (node.thedef !== def) { |
|
node.thedef = def; |
|
node.reference(); |
|
} |
|
} |
|
} else if (node instanceof AST_LabelRef) { |
|
var sym = labels.get(node.name); |
|
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", { |
|
name: node.name, |
|
line: node.start.line, |
|
col: node.start.col |
|
})); |
|
node.thedef = sym; |
|
} |
|
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) { |
|
js_error( |
|
`"${node.TYPE}" statement may only appear at the top level`, |
|
node.start.file, |
|
node.start.line, |
|
node.start.col, |
|
node.start.pos |
|
); |
|
} |
|
}); |
|
|
|
if (options.module) { |
|
tw.directives["use strict"] = true; |
|
} |
|
|
|
this.walk(tw); |
|
|
|
function mark_export(def, level) { |
|
if (in_destructuring) { |
|
var i = 0; |
|
do { |
|
level++; |
|
} while (tw.parent(i++) !== in_destructuring); |
|
} |
|
var node = tw.parent(level); |
|
if (def.export = node instanceof AST_Export ? MASK_EXPORT_DONT_MANGLE : 0) { |
|
var exported = node.exported_definition; |
|
if ((exported instanceof AST_Defun || exported instanceof AST_DefClass) && node.is_default) { |
|
def.export = MASK_EXPORT_WANT_MANGLE; |
|
} |
|
} |
|
} |
|
|
|
// pass 2: find back references and eval |
|
const is_toplevel = this instanceof AST_Toplevel; |
|
if (is_toplevel) { |
|
this.globals = new Map(); |
|
} |
|
|
|
var tw = new TreeWalker(node => { |
|
if (node instanceof AST_LoopControl && node.label) { |
|
node.label.thedef.references.push(node); |
|
return true; |
|
} |
|
if (node instanceof AST_SymbolRef) { |
|
var name = node.name; |
|
if (name == "eval" && tw.parent() instanceof AST_Call) { |
|
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { |
|
s.uses_eval = true; |
|
} |
|
} |
|
var sym; |
|
if (tw.parent() instanceof AST_NameMapping && tw.parent(1).module_name |
|
|| !(sym = node.scope.find_variable(name))) { |
|
|
|
sym = toplevel.def_global(node); |
|
if (node instanceof AST_SymbolExport) sym.export = MASK_EXPORT_DONT_MANGLE; |
|
} else if (sym.scope instanceof AST_Lambda && name == "arguments") { |
|
sym.scope.get_defun_scope().uses_arguments = true; |
|
} |
|
node.thedef = sym; |
|
node.reference(); |
|
if (node.scope.is_block_scope() |
|
&& !(sym.orig[0] instanceof AST_SymbolBlockDeclaration)) { |
|
node.scope = node.scope.get_defun_scope(); |
|
} |
|
return true; |
|
} |
|
// ensure mangling works if catch reuses a scope variable |
|
var def; |
|
if (node instanceof AST_SymbolCatch && (def = redefined_catch_def(node.definition()))) { |
|
var s = node.scope; |
|
while (s) { |
|
push_uniq(s.enclosed, def); |
|
if (s === def.scope) break; |
|
s = s.parent_scope; |
|
} |
|
} |
|
}); |
|
this.walk(tw); |
|
|
|
// pass 3: work around IE8 and Safari catch scope bugs |
|
if (options.ie8 || options.safari10) { |
|
walk(this, node => { |
|
if (node instanceof AST_SymbolCatch) { |
|
var name = node.name; |
|
var refs = node.thedef.references; |
|
var scope = node.scope.get_defun_scope(); |
|
var def = scope.find_variable(name) |
|
|| toplevel.globals.get(name) |
|
|| scope.def_variable(node); |
|
refs.forEach(function(ref) { |
|
ref.thedef = def; |
|
ref.reference(); |
|
}); |
|
node.thedef = def; |
|
node.reference(); |
|
return true; |
|
} |
|
}); |
|
} |
|
|
|
// pass 4: add symbol definitions to loop scopes |
|
// Safari/Webkit bug workaround - loop init let variable shadowing argument. |
|
// https://github.com/mishoo/UglifyJS2/issues/1753 |
|
// https://bugs.webkit.org/show_bug.cgi?id=171041 |
|
if (options.safari10) { |
|
for (const scope of for_scopes) { |
|
scope.parent_scope.variables.forEach(function(def) { |
|
push_uniq(scope.enclosed, def); |
|
}); |
|
} |
|
} |
|
}); |
|
|
|
AST_Toplevel.DEFMETHOD("def_global", function(node) { |
|
var globals = this.globals, name = node.name; |
|
if (globals.has(name)) { |
|
return globals.get(name); |
|
} else { |
|
var g = new SymbolDef(this, node); |
|
g.undeclared = true; |
|
g.global = true; |
|
globals.set(name, g); |
|
return g; |
|
} |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) { |
|
this.variables = new Map(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) |
|
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement |
|
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` |
|
this.parent_scope = parent_scope; // the parent scope |
|
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes |
|
this.cname = -1; // the current index for mangling functions/variables |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("conflicting_def", function (name) { |
|
return ( |
|
this.enclosed.find(def => def.name === name) |
|
|| this.variables.has(name) |
|
|| (this.parent_scope && this.parent_scope.conflicting_def(name)) |
|
); |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("conflicting_def_shallow", function (name) { |
|
return ( |
|
this.enclosed.find(def => def.name === name) |
|
|| this.variables.has(name) |
|
); |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("add_child_scope", function (scope) { |
|
// `scope` is going to be moved into `this` right now. |
|
// Update the required scopes' information |
|
|
|
if (scope.parent_scope === this) return; |
|
|
|
scope.parent_scope = this; |
|
|
|
// Propagate to this.uses_arguments from arrow functions |
|
if ((scope instanceof AST_Arrow) && (this instanceof AST_Lambda && !this.uses_arguments)) { |
|
this.uses_arguments = walk(scope, node => { |
|
if ( |
|
node instanceof AST_SymbolRef |
|
&& node.scope instanceof AST_Lambda |
|
&& node.name === "arguments" |
|
) { |
|
return walk_abort; |
|
} |
|
|
|
if (node instanceof AST_Lambda && !(node instanceof AST_Arrow)) { |
|
return true; |
|
} |
|
}); |
|
} |
|
|
|
this.uses_with = this.uses_with || scope.uses_with; |
|
this.uses_eval = this.uses_eval || scope.uses_eval; |
|
|
|
const scope_ancestry = (() => { |
|
const ancestry = []; |
|
let cur = this; |
|
do { |
|
ancestry.push(cur); |
|
} while ((cur = cur.parent_scope)); |
|
ancestry.reverse(); |
|
return ancestry; |
|
})(); |
|
|
|
const new_scope_enclosed_set = new Set(scope.enclosed); |
|
const to_enclose = []; |
|
for (const scope_topdown of scope_ancestry) { |
|
to_enclose.forEach(e => push_uniq(scope_topdown.enclosed, e)); |
|
for (const def of scope_topdown.variables.values()) { |
|
if (new_scope_enclosed_set.has(def)) { |
|
push_uniq(to_enclose, def); |
|
push_uniq(scope_topdown.enclosed, def); |
|
} |
|
} |
|
} |
|
}); |
|
|
|
function find_scopes_visible_from(scopes) { |
|
const found_scopes = new Set(); |
|
|
|
for (const scope of new Set(scopes)) { |
|
(function bubble_up(scope) { |
|
if (scope == null || found_scopes.has(scope)) return; |
|
|
|
found_scopes.add(scope); |
|
|
|
bubble_up(scope.parent_scope); |
|
})(scope); |
|
} |
|
|
|
return [...found_scopes]; |
|
} |
|
|
|
// Creates a symbol during compression |
|
AST_Scope.DEFMETHOD("create_symbol", function(SymClass, { |
|
source, |
|
tentative_name, |
|
scope, |
|
conflict_scopes = [scope], |
|
init = null |
|
} = {}) { |
|
let symbol_name; |
|
|
|
conflict_scopes = find_scopes_visible_from(conflict_scopes); |
|
|
|
if (tentative_name) { |
|
// Implement hygiene (no new names are conflicting with existing names) |
|
tentative_name = |
|
symbol_name = |
|
tentative_name.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_"); |
|
|
|
let i = 0; |
|
while (conflict_scopes.find(s => s.conflicting_def_shallow(symbol_name))) { |
|
symbol_name = tentative_name + "$" + i++; |
|
} |
|
} |
|
|
|
if (!symbol_name) { |
|
throw new Error("No symbol name could be generated in create_symbol()"); |
|
} |
|
|
|
const symbol = make_node(SymClass, source, { |
|
name: symbol_name, |
|
scope |
|
}); |
|
|
|
this.def_variable(symbol, init || null); |
|
|
|
symbol.mark_enclosed(); |
|
|
|
return symbol; |
|
}); |
|
|
|
|
|
AST_Node.DEFMETHOD("is_block_scope", return_false); |
|
AST_Class.DEFMETHOD("is_block_scope", return_false); |
|
AST_Lambda.DEFMETHOD("is_block_scope", return_false); |
|
AST_Toplevel.DEFMETHOD("is_block_scope", return_false); |
|
AST_SwitchBranch.DEFMETHOD("is_block_scope", return_false); |
|
AST_Block.DEFMETHOD("is_block_scope", return_true); |
|
AST_Scope.DEFMETHOD("is_block_scope", function () { |
|
return this._block_scope || false; |
|
}); |
|
AST_IterationStatement.DEFMETHOD("is_block_scope", return_true); |
|
|
|
AST_Lambda.DEFMETHOD("init_scope_vars", function() { |
|
AST_Scope.prototype.init_scope_vars.apply(this, arguments); |
|
this.uses_arguments = false; |
|
this.def_variable(new AST_SymbolFunarg({ |
|
name: "arguments", |
|
start: this.start, |
|
end: this.end |
|
})); |
|
}); |
|
|
|
AST_Arrow.DEFMETHOD("init_scope_vars", function() { |
|
AST_Scope.prototype.init_scope_vars.apply(this, arguments); |
|
this.uses_arguments = false; |
|
}); |
|
|
|
AST_Symbol.DEFMETHOD("mark_enclosed", function() { |
|
var def = this.definition(); |
|
var s = this.scope; |
|
while (s) { |
|
push_uniq(s.enclosed, def); |
|
if (s === def.scope) break; |
|
s = s.parent_scope; |
|
} |
|
}); |
|
|
|
AST_Symbol.DEFMETHOD("reference", function() { |
|
this.definition().references.push(this); |
|
this.mark_enclosed(); |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("find_variable", function(name) { |
|
if (name instanceof AST_Symbol) name = name.name; |
|
return this.variables.get(name) |
|
|| (this.parent_scope && this.parent_scope.find_variable(name)); |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("def_function", function(symbol, init) { |
|
var def = this.def_variable(symbol, init); |
|
if (!def.init || def.init instanceof AST_Defun) def.init = init; |
|
return def; |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("def_variable", function(symbol, init) { |
|
var def = this.variables.get(symbol.name); |
|
if (def) { |
|
def.orig.push(symbol); |
|
if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) { |
|
def.init = init; |
|
} |
|
} else { |
|
def = new SymbolDef(this, symbol, init); |
|
this.variables.set(symbol.name, def); |
|
def.global = !this.parent_scope; |
|
} |
|
return symbol.thedef = def; |
|
}); |
|
|
|
function next_mangled(scope, options) { |
|
let defun_scope; |
|
if ( |
|
scopes_with_block_defuns |
|
&& (defun_scope = scope.get_defun_scope()) |
|
&& scopes_with_block_defuns.has(defun_scope) |
|
) { |
|
scope = defun_scope; |
|
} |
|
|
|
var ext = scope.enclosed; |
|
var nth_identifier = options.nth_identifier; |
|
out: while (true) { |
|
var m = nth_identifier.get(++scope.cname); |
|
if (ALL_RESERVED_WORDS.has(m)) continue; // skip over "do" |
|
|
|
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not |
|
// shadow a name reserved from mangling. |
|
if (options.reserved.has(m)) continue; |
|
|
|
// Functions with short names might collide with base54 output |
|
// and therefore cause collisions when keep_fnames is true. |
|
if (unmangleable_names && unmangleable_names.has(m)) continue out; |
|
|
|
// we must ensure that the mangled name does not shadow a name |
|
// from some parent scope that is referenced in this or in |
|
// inner scopes. |
|
for (let i = ext.length; --i >= 0;) { |
|
const def = ext[i]; |
|
const name = def.mangled_name || (def.unmangleable(options) && def.name); |
|
if (m == name) continue out; |
|
} |
|
return m; |
|
} |
|
} |
|
|
|
AST_Scope.DEFMETHOD("next_mangled", function(options) { |
|
return next_mangled(this, options); |
|
}); |
|
|
|
AST_Toplevel.DEFMETHOD("next_mangled", function(options) { |
|
let name; |
|
const mangled_names = this.mangled_names; |
|
do { |
|
name = next_mangled(this, options); |
|
} while (mangled_names.has(name)); |
|
return name; |
|
}); |
|
|
|
AST_Function.DEFMETHOD("next_mangled", function(options, def) { |
|
// #179, #326 |
|
// in Safari strict mode, something like (function x(x){...}) is a syntax error; |
|
// a function expression's argument cannot shadow the function expression's name |
|
|
|
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition(); |
|
|
|
// the function's mangled_name is null when keep_fnames is true |
|
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null; |
|
|
|
while (true) { |
|
var name = next_mangled(this, options); |
|
if (!tricky_name || tricky_name != name) |
|
return name; |
|
} |
|
}); |
|
|
|
AST_Symbol.DEFMETHOD("unmangleable", function(options) { |
|
var def = this.definition(); |
|
return !def || def.unmangleable(options); |
|
}); |
|
|
|
// labels are always mangleable |
|
AST_Label.DEFMETHOD("unmangleable", return_false); |
|
|
|
AST_Symbol.DEFMETHOD("unreferenced", function() { |
|
return !this.definition().references.length && !this.scope.pinned(); |
|
}); |
|
|
|
AST_Symbol.DEFMETHOD("definition", function() { |
|
return this.thedef; |
|
}); |
|
|
|
AST_Symbol.DEFMETHOD("global", function() { |
|
return this.thedef.global; |
|
}); |
|
|
|
/** |
|
* Format the mangler options (if any) into their appropriate types |
|
*/ |
|
function format_mangler_options(options) { |
|
options = defaults(options, { |
|
eval : false, |
|
nth_identifier : base54, |
|
ie8 : false, |
|
keep_classnames: false, |
|
keep_fnames : false, |
|
module : false, |
|
reserved : [], |
|
toplevel : false, |
|
}); |
|
if (options.module) options.toplevel = true; |
|
if (!Array.isArray(options.reserved) |
|
&& !(options.reserved instanceof Set) |
|
) { |
|
options.reserved = []; |
|
} |
|
options.reserved = new Set(options.reserved); |
|
// Never mangle arguments |
|
options.reserved.add("arguments"); |
|
return options; |
|
} |
|
|
|
AST_Toplevel.DEFMETHOD("mangle_names", function(options) { |
|
options = format_mangler_options(options); |
|
var nth_identifier = options.nth_identifier; |
|
|
|
// We only need to mangle declaration nodes. Special logic wired |
|
// into the code generator will display the mangled name if it's |
|
// present (and for AST_SymbolRef-s it'll use the mangled name of |
|
// the AST_SymbolDeclaration that it points to). |
|
var lname = -1; |
|
var to_mangle = []; |
|
|
|
if (options.keep_fnames) { |
|
function_defs = new Set(); |
|
} |
|
|
|
const mangled_names = this.mangled_names = new Set(); |
|
unmangleable_names = new Set(); |
|
|
|
if (options.cache) { |
|
this.globals.forEach(collect); |
|
if (options.cache.props) { |
|
options.cache.props.forEach(function(mangled_name) { |
|
mangled_names.add(mangled_name); |
|
}); |
|
} |
|
} |
|
|
|
var tw = new TreeWalker(function(node, descend) { |
|
if (node instanceof AST_LabeledStatement) { |
|
// lname is incremented when we get to the AST_Label |
|
var save_nesting = lname; |
|
descend(); |
|
lname = save_nesting; |
|
return true; // don't descend again in TreeWalker |
|
} |
|
if ( |
|
node instanceof AST_Defun |
|
&& !(tw.parent() instanceof AST_Scope) |
|
) { |
|
scopes_with_block_defuns = scopes_with_block_defuns || new Set(); |
|
scopes_with_block_defuns.add(node.parent_scope.get_defun_scope()); |
|
} |
|
if (node instanceof AST_Scope) { |
|
node.variables.forEach(collect); |
|
return; |
|
} |
|
if (node.is_block_scope()) { |
|
node.block_scope.variables.forEach(collect); |
|
return; |
|
} |
|
if ( |
|
function_defs |
|
&& node instanceof AST_VarDef |
|
&& node.name instanceof AST_Symbol |
|
&& node.value instanceof AST_Lambda |
|
&& !node.value.name |
|
&& keep_name(options.keep_fnames, node.name.name) |
|
) { |
|
function_defs.add(node.name.definition().id); |
|
return; |
|
} |
|
if (node instanceof AST_Label) { |
|
let name; |
|
do { |
|
name = nth_identifier.get(++lname); |
|
} while (ALL_RESERVED_WORDS.has(name)); |
|
node.mangled_name = name; |
|
return true; |
|
} |
|
if (!(options.ie8 || options.safari10) && node instanceof AST_SymbolCatch) { |
|
to_mangle.push(node.definition()); |
|
return; |
|
} |
|
}); |
|
|
|
this.walk(tw); |
|
|
|
if (options.keep_fnames || options.keep_classnames) { |
|
// Collect a set of short names which are unmangleable, |
|
// for use in avoiding collisions in next_mangled. |
|
to_mangle.forEach(def => { |
|
if (def.name.length < 6 && def.unmangleable(options)) { |
|
unmangleable_names.add(def.name); |
|
} |
|
}); |
|
} |
|
|
|
to_mangle.forEach(def => { def.mangle(options); }); |
|
|
|
function_defs = null; |
|
unmangleable_names = null; |
|
scopes_with_block_defuns = null; |
|
|
|
function collect(symbol) { |
|
if (symbol.export & MASK_EXPORT_DONT_MANGLE) { |
|
unmangleable_names.add(symbol.name); |
|
} else if (!options.reserved.has(symbol.name)) { |
|
to_mangle.push(symbol); |
|
} |
|
} |
|
}); |
|
|
|
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { |
|
const cache = options.cache && options.cache.props; |
|
const avoid = new Set(); |
|
options.reserved.forEach(to_avoid); |
|
this.globals.forEach(add_def); |
|
this.walk(new TreeWalker(function(node) { |
|
if (node instanceof AST_Scope) node.variables.forEach(add_def); |
|
if (node instanceof AST_SymbolCatch) add_def(node.definition()); |
|
})); |
|
return avoid; |
|
|
|
function to_avoid(name) { |
|
avoid.add(name); |
|
} |
|
|
|
function add_def(def) { |
|
var name = def.name; |
|
if (def.global && cache && cache.has(name)) name = cache.get(name); |
|
else if (!def.unmangleable(options)) return; |
|
to_avoid(name); |
|
} |
|
}); |
|
|
|
AST_Toplevel.DEFMETHOD("expand_names", function(options) { |
|
options = format_mangler_options(options); |
|
var nth_identifier = options.nth_identifier; |
|
if (nth_identifier.reset && nth_identifier.sort) { |
|
nth_identifier.reset(); |
|
nth_identifier.sort(); |
|
} |
|
var avoid = this.find_colliding_names(options); |
|
var cname = 0; |
|
this.globals.forEach(rename); |
|
this.walk(new TreeWalker(function(node) { |
|
if (node instanceof AST_Scope) node.variables.forEach(rename); |
|
if (node instanceof AST_SymbolCatch) rename(node.definition()); |
|
})); |
|
|
|
function next_name() { |
|
var name; |
|
do { |
|
name = nth_identifier.get(cname++); |
|
} while (avoid.has(name) || ALL_RESERVED_WORDS.has(name)); |
|
return name; |
|
} |
|
|
|
function rename(def) { |
|
if (def.global && options.cache) return; |
|
if (def.unmangleable(options)) return; |
|
if (options.reserved.has(def.name)) return; |
|
const redefinition = redefined_catch_def(def); |
|
const name = def.name = redefinition ? redefinition.name : next_name(); |
|
def.orig.forEach(function(sym) { |
|
sym.name = name; |
|
}); |
|
def.references.forEach(function(sym) { |
|
sym.name = name; |
|
}); |
|
} |
|
}); |
|
|
|
AST_Node.DEFMETHOD("tail_node", return_this); |
|
AST_Sequence.DEFMETHOD("tail_node", function() { |
|
return this.expressions[this.expressions.length - 1]; |
|
}); |
|
|
|
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { |
|
options = format_mangler_options(options); |
|
var nth_identifier = options.nth_identifier; |
|
if (!nth_identifier.reset || !nth_identifier.consider || !nth_identifier.sort) { |
|
// If the identifier mangler is invariant, skip computing character frequency. |
|
return; |
|
} |
|
nth_identifier.reset(); |
|
|
|
try { |
|
AST_Node.prototype.print = function(stream, force_parens) { |
|
this._print(stream, force_parens); |
|
if (this instanceof AST_Symbol && !this.unmangleable(options)) { |
|
nth_identifier.consider(this.name, -1); |
|
} else if (options.properties) { |
|
if (this instanceof AST_DotHash) { |
|
nth_identifier.consider("#" + this.property, -1); |
|
} else if (this instanceof AST_Dot) { |
|
nth_identifier.consider(this.property, -1); |
|
} else if (this instanceof AST_Sub) { |
|
skip_string(this.property); |
|
} |
|
} |
|
}; |
|
nth_identifier.consider(this.print_to_string(), 1); |
|
} finally { |
|
AST_Node.prototype.print = AST_Node.prototype._print; |
|
} |
|
nth_identifier.sort(); |
|
|
|
function skip_string(node) { |
|
if (node instanceof AST_String) { |
|
nth_identifier.consider(node.value, -1); |
|
} else if (node instanceof AST_Conditional) { |
|
skip_string(node.consequent); |
|
skip_string(node.alternative); |
|
} else if (node instanceof AST_Sequence) { |
|
skip_string(node.tail_node()); |
|
} |
|
} |
|
}); |
|
|
|
const base54 = (() => { |
|
const leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split(""); |
|
const digits = "0123456789".split(""); |
|
let chars; |
|
let frequency; |
|
function reset() { |
|
frequency = new Map(); |
|
leading.forEach(function(ch) { |
|
frequency.set(ch, 0); |
|
}); |
|
digits.forEach(function(ch) { |
|
frequency.set(ch, 0); |
|
}); |
|
} |
|
function consider(str, delta) { |
|
for (var i = str.length; --i >= 0;) { |
|
frequency.set(str[i], frequency.get(str[i]) + delta); |
|
} |
|
} |
|
function compare(a, b) { |
|
return frequency.get(b) - frequency.get(a); |
|
} |
|
function sort() { |
|
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare)); |
|
} |
|
// Ensure this is in a usable initial state. |
|
reset(); |
|
sort(); |
|
function base54(num) { |
|
var ret = "", base = 54; |
|
num++; |
|
do { |
|
num--; |
|
ret += chars[num % base]; |
|
num = Math.floor(num / base); |
|
base = 64; |
|
} while (num > 0); |
|
return ret; |
|
} |
|
|
|
return { |
|
get: base54, |
|
consider, |
|
reset, |
|
sort |
|
}; |
|
})(); |
|
|
|
let mangle_options = undefined; |
|
AST_Node.prototype.size = function (compressor, stack) { |
|
mangle_options = compressor && compressor._mangle_options; |
|
|
|
let size = 0; |
|
walk_parent(this, (node, info) => { |
|
size += node._size(info); |
|
|
|
// Braceless arrow functions have fake "return" statements |
|
if (node instanceof AST_Arrow && node.is_braceless()) { |
|
size += node.body[0].value._size(info); |
|
return true; |
|
} |
|
}, stack || (compressor && compressor.stack)); |
|
|
|
// just to save a bit of memory |
|
mangle_options = undefined; |
|
|
|
return size; |
|
}; |
|
|
|
AST_Node.prototype._size = () => 0; |
|
|
|
AST_Debugger.prototype._size = () => 8; |
|
|
|
AST_Directive.prototype._size = function () { |
|
// TODO string encoding stuff |
|
return 2 + this.value.length; |
|
}; |
|
|
|
/** Count commas/semicolons necessary to show a list of expressions/statements */ |
|
const list_overhead = (array) => array.length && array.length - 1; |
|
|
|
AST_Block.prototype._size = function () { |
|
return 2 + list_overhead(this.body); |
|
}; |
|
|
|
AST_Toplevel.prototype._size = function() { |
|
return list_overhead(this.body); |
|
}; |
|
|
|
AST_EmptyStatement.prototype._size = () => 1; |
|
|
|
AST_LabeledStatement.prototype._size = () => 2; // x: |
|
|
|
AST_Do.prototype._size = () => 9; |
|
|
|
AST_While.prototype._size = () => 7; |
|
|
|
AST_For.prototype._size = () => 8; |
|
|
|
AST_ForIn.prototype._size = () => 8; |
|
// AST_ForOf inherits ^ |
|
|
|
AST_With.prototype._size = () => 6; |
|
|
|
AST_Expansion.prototype._size = () => 3; |
|
|
|
const lambda_modifiers = func => |
|
(func.is_generator ? 1 : 0) + (func.async ? 6 : 0); |
|
|
|
AST_Accessor.prototype._size = function () { |
|
return lambda_modifiers(this) + 4 + list_overhead(this.argnames) + list_overhead(this.body); |
|
}; |
|
|
|
AST_Function.prototype._size = function (info) { |
|
const first = !!first_in_statement(info); |
|
return (first * 2) + lambda_modifiers(this) + 12 + list_overhead(this.argnames) + list_overhead(this.body); |
|
}; |
|
|
|
AST_Defun.prototype._size = function () { |
|
return lambda_modifiers(this) + 13 + list_overhead(this.argnames) + list_overhead(this.body); |
|
}; |
|
|
|
AST_Arrow.prototype._size = function () { |
|
let args_and_arrow = 2 + list_overhead(this.argnames); |
|
|
|
if ( |
|
!( |
|
this.argnames.length === 1 |
|
&& this.argnames[0] instanceof AST_Symbol |
|
) |
|
) { |
|
args_and_arrow += 2; // parens around the args |
|
} |
|
|
|
const body_overhead = this.is_braceless() ? 0 : list_overhead(this.body) + 2; |
|
|
|
return lambda_modifiers(this) + args_and_arrow + body_overhead; |
|
}; |
|
|
|
AST_Destructuring.prototype._size = () => 2; |
|
|
|
AST_TemplateString.prototype._size = function () { |
|
return 2 + (Math.floor(this.segments.length / 2) * 3); /* "${}" */ |
|
}; |
|
|
|
AST_TemplateSegment.prototype._size = function () { |
|
return this.value.length; |
|
}; |
|
|
|
AST_Return.prototype._size = function () { |
|
return this.value ? 7 : 6; |
|
}; |
|
|
|
AST_Throw.prototype._size = () => 6; |
|
|
|
AST_Break.prototype._size = function () { |
|
return this.label ? 6 : 5; |
|
}; |
|
|
|
AST_Continue.prototype._size = function () { |
|
return this.label ? 9 : 8; |
|
}; |
|
|
|
AST_If.prototype._size = () => 4; |
|
|
|
AST_Switch.prototype._size = function () { |
|
return 8 + list_overhead(this.body); |
|
}; |
|
|
|
AST_Case.prototype._size = function () { |
|
return 5 + list_overhead(this.body); |
|
}; |
|
|
|
AST_Default.prototype._size = function () { |
|
return 8 + list_overhead(this.body); |
|
}; |
|
|
|
AST_Try.prototype._size = () => 3; |
|
|
|
AST_Catch.prototype._size = function () { |
|
let size = 7 + list_overhead(this.body); |
|
if (this.argname) { |
|
size += 2; |
|
} |
|
return size; |
|
}; |
|
|
|
AST_Finally.prototype._size = function () { |
|
return 7 + list_overhead(this.body); |
|
}; |
|
|
|
AST_Var.prototype._size = function () { |
|
return 4 + list_overhead(this.definitions); |
|
}; |
|
|
|
AST_Let.prototype._size = function () { |
|
return 4 + list_overhead(this.definitions); |
|
}; |
|
|
|
AST_Const.prototype._size = function () { |
|
return 6 + list_overhead(this.definitions); |
|
}; |
|
|
|
AST_Using.prototype._size = function () { |
|
const await_size = this.await ? 6 : 0; |
|
return await_size + 6 + list_overhead(this.definitions); |
|
}; |
|
|
|
AST_VarDefLike.prototype._size = function () { |
|
return this.value ? 1 : 0; |
|
}; |
|
|
|
AST_NameMapping.prototype._size = function () { |
|
// foreign name isn't mangled |
|
return this.name ? 4 : 0; |
|
}; |
|
|
|
AST_Import.prototype._size = function () { |
|
// import |
|
let size = 6; |
|
|
|
if (this.imported_name) size += 1; |
|
|
|
// from |
|
if (this.imported_name || this.imported_names) size += 5; |
|
|
|
// braces, and the commas |
|
if (this.imported_names) { |
|
size += 2 + list_overhead(this.imported_names); |
|
} |
|
|
|
return size; |
|
}; |
|
|
|
AST_ImportMeta.prototype._size = () => 11; |
|
|
|
AST_Export.prototype._size = function () { |
|
let size = 7 + (this.is_default ? 8 : 0); |
|
|
|
if (this.exported_value) { |
|
size += this.exported_value._size(); |
|
} |
|
|
|
if (this.exported_names) { |
|
// Braces and commas |
|
size += 2 + list_overhead(this.exported_names); |
|
} |
|
|
|
if (this.module_name) { |
|
// "from " |
|
size += 5; |
|
} |
|
|
|
return size; |
|
}; |
|
|
|
AST_Call.prototype._size = function () { |
|
if (this.optional) { |
|
return 4 + list_overhead(this.args); |
|
} |
|
return 2 + list_overhead(this.args); |
|
}; |
|
|
|
AST_New.prototype._size = function () { |
|
return 6 + list_overhead(this.args); |
|
}; |
|
|
|
AST_Sequence.prototype._size = function () { |
|
return list_overhead(this.expressions); |
|
}; |
|
|
|
AST_Dot.prototype._size = function () { |
|
if (this.optional) { |
|
return this.property.length + 2; |
|
} |
|
return this.property.length + 1; |
|
}; |
|
|
|
AST_DotHash.prototype._size = function () { |
|
if (this.optional) { |
|
return this.property.length + 3; |
|
} |
|
return this.property.length + 2; |
|
}; |
|
|
|
AST_Sub.prototype._size = function () { |
|
return this.optional ? 4 : 2; |
|
}; |
|
|
|
AST_Unary.prototype._size = function () { |
|
if (this.operator === "typeof") return 7; |
|
if (this.operator === "void") return 5; |
|
return this.operator.length; |
|
}; |
|
|
|
AST_Binary.prototype._size = function (info) { |
|
if (this.operator === "in") return 4; |
|
|
|
let size = this.operator.length; |
|
|
|
if ( |
|
(this.operator === "+" || this.operator === "-") |
|
&& this.right instanceof AST_Unary && this.right.operator === this.operator |
|
) { |
|
// 1+ +a > needs space between the + |
|
size += 1; |
|
} |
|
|
|
if (this.needs_parens(info)) { |
|
size += 2; |
|
} |
|
|
|
return size; |
|
}; |
|
|
|
AST_Conditional.prototype._size = () => 3; |
|
|
|
AST_Array.prototype._size = function () { |
|
return 2 + list_overhead(this.elements); |
|
}; |
|
|
|
AST_Object.prototype._size = function (info) { |
|
let base = 2; |
|
if (first_in_statement(info)) { |
|
base += 2; // parens |
|
} |
|
return base + list_overhead(this.properties); |
|
}; |
|
|
|
/*#__INLINE__*/ |
|
const key_size = key => |
|
typeof key === "string" ? key.length : 0; |
|
|
|
AST_ObjectKeyVal.prototype._size = function () { |
|
return key_size(this.key) + 1; |
|
}; |
|
|
|
/*#__INLINE__*/ |
|
const static_size = is_static => is_static ? 7 : 0; |
|
|
|
AST_ObjectGetter.prototype._size = function () { |
|
return 5 + static_size(this.static) + key_size(this.key); |
|
}; |
|
|
|
AST_ObjectSetter.prototype._size = function () { |
|
return 5 + static_size(this.static) + key_size(this.key); |
|
}; |
|
|
|
AST_ConciseMethod.prototype._size = function () { |
|
return static_size(this.static) + key_size(this.key); |
|
}; |
|
|
|
AST_PrivateMethod.prototype._size = function () { |
|
return AST_ConciseMethod.prototype._size.call(this) + 1; |
|
}; |
|
|
|
AST_PrivateGetter.prototype._size = function () { |
|
return AST_ConciseMethod.prototype._size.call(this) + 4; |
|
}; |
|
|
|
AST_PrivateSetter.prototype._size = function () { |
|
return AST_ConciseMethod.prototype._size.call(this) + 4; |
|
}; |
|
|
|
AST_PrivateIn.prototype._size = function () { |
|
return 5; // "#", and " in " |
|
}; |
|
|
|
AST_Class.prototype._size = function () { |
|
return ( |
|
(this.name ? 8 : 7) |
|
+ (this.extends ? 8 : 0) |
|
); |
|
}; |
|
|
|
AST_ClassStaticBlock.prototype._size = function () { |
|
// "static{}" + semicolons |
|
return 8 + list_overhead(this.body); |
|
}; |
|
|
|
AST_ClassProperty.prototype._size = function () { |
|
return ( |
|
static_size(this.static) |
|
+ (typeof this.key === "string" ? this.key.length + 2 : 0) |
|
+ (this.value ? 1 : 0) |
|
); |
|
}; |
|
|
|
AST_ClassPrivateProperty.prototype._size = function () { |
|
return AST_ClassProperty.prototype._size.call(this) + 1; |
|
}; |
|
|
|
AST_Symbol.prototype._size = function () { |
|
if (!(mangle_options && this.thedef && !this.thedef.unmangleable(mangle_options))) { |
|
return this.name.length; |
|
} else { |
|
return 1; |
|
} |
|
}; |
|
|
|
// TODO take propmangle into account |
|
AST_SymbolClassProperty.prototype._size = function () { |
|
return this.name.length; |
|
}; |
|
|
|
AST_SymbolRef.prototype._size = AST_SymbolDeclaration.prototype._size = function () { |
|
if (this.name === "arguments") return 9; |
|
|
|
return AST_Symbol.prototype._size.call(this); |
|
}; |
|
|
|
AST_NewTarget.prototype._size = () => 10; |
|
|
|
AST_SymbolImportForeign.prototype._size = function () { |
|
return this.name.length; |
|
}; |
|
|
|
AST_SymbolExportForeign.prototype._size = function () { |
|
return this.name.length; |
|
}; |
|
|
|
AST_This.prototype._size = () => 4; |
|
|
|
AST_Super.prototype._size = () => 5; |
|
|
|
AST_String.prototype._size = function () { |
|
return this.value.length + 2; |
|
}; |
|
|
|
AST_Number.prototype._size = function () { |
|
const { value } = this; |
|
if (value === 0) return 1; |
|
if (value > 0 && Math.floor(value) === value) { |
|
return Math.floor(Math.log10(value) + 1); |
|
} |
|
return value.toString().length; |
|
}; |
|
|
|
AST_BigInt.prototype._size = function () { |
|
return this.value.length; |
|
}; |
|
|
|
AST_RegExp.prototype._size = function () { |
|
return this.value.toString().length; |
|
}; |
|
|
|
AST_Null.prototype._size = () => 4; |
|
|
|
AST_NaN.prototype._size = () => 3; |
|
|
|
AST_Undefined.prototype._size = () => 6; // "void 0" |
|
|
|
AST_Hole.prototype._size = () => 0; // comma is taken into account by list_overhead() |
|
|
|
AST_Infinity.prototype._size = () => 8; |
|
|
|
AST_True.prototype._size = () => 4; |
|
|
|
AST_False.prototype._size = () => 5; |
|
|
|
AST_Await.prototype._size = () => 6; |
|
|
|
AST_Yield.prototype._size = () => 6; |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
// bitfield flags to be stored in node.flags. |
|
// These are set and unset during compression, and store information in the node without requiring multiple fields. |
|
const UNUSED = 0b00000001; |
|
const TRUTHY = 0b00000010; |
|
const FALSY = 0b00000100; |
|
const UNDEFINED = 0b00001000; |
|
const INLINED = 0b00010000; |
|
// Nodes to which values are ever written. Used when keep_assign is part of the unused option string. |
|
const WRITE_ONLY = 0b00100000; |
|
|
|
// information specific to a single compression pass |
|
const SQUEEZED = 0b0000000100000000; |
|
const OPTIMIZED = 0b0000001000000000; |
|
const TOP = 0b0000010000000000; |
|
const CLEAR_BETWEEN_PASSES = SQUEEZED | OPTIMIZED | TOP; |
|
|
|
const has_flag = (node, flag) => node.flags & flag; |
|
const set_flag = (node, flag) => { node.flags |= flag; }; |
|
const clear_flag = (node, flag) => { node.flags &= ~flag; }; |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
function merge_sequence(array, node) { |
|
if (node instanceof AST_Sequence) { |
|
array.push(...node.expressions); |
|
} else { |
|
array.push(node); |
|
} |
|
return array; |
|
} |
|
|
|
function make_sequence(orig, expressions) { |
|
if (expressions.length == 1) return expressions[0]; |
|
if (expressions.length == 0) throw new Error("trying to create a sequence with length zero!"); |
|
return make_node(AST_Sequence, orig, { |
|
expressions: expressions.reduce(merge_sequence, []) |
|
}); |
|
} |
|
|
|
function make_empty_function(self) { |
|
return make_node(AST_Function, self, { |
|
uses_arguments: false, |
|
argnames: [], |
|
body: [], |
|
is_generator: false, |
|
async: false, |
|
variables: new Map(), |
|
uses_with: false, |
|
uses_eval: false, |
|
parent_scope: null, |
|
enclosed: [], |
|
cname: 0, |
|
block_scope: undefined, |
|
}); |
|
} |
|
|
|
function make_node_from_constant(val, orig) { |
|
switch (typeof val) { |
|
case "string": |
|
return make_node(AST_String, orig, { |
|
value: val |
|
}); |
|
case "number": |
|
if (isNaN(val)) return make_node(AST_NaN, orig); |
|
if (isFinite(val)) { |
|
return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, { |
|
operator: "-", |
|
expression: make_node(AST_Number, orig, { value: -val }) |
|
}) : make_node(AST_Number, orig, { value: val }); |
|
} |
|
return val < 0 ? make_node(AST_UnaryPrefix, orig, { |
|
operator: "-", |
|
expression: make_node(AST_Infinity, orig) |
|
}) : make_node(AST_Infinity, orig); |
|
case "bigint": |
|
return make_node(AST_BigInt, orig, { value: val.toString() }); |
|
case "boolean": |
|
return make_node(val ? AST_True : AST_False, orig); |
|
case "undefined": |
|
return make_void_0(orig); |
|
default: |
|
if (val === null) { |
|
return make_node(AST_Null, orig, { value: null }); |
|
} |
|
if (val instanceof RegExp) { |
|
return make_node(AST_RegExp, orig, { |
|
value: { |
|
source: regexp_source_fix(val.source), |
|
flags: val.flags |
|
} |
|
}); |
|
} |
|
throw new Error(string_template("Can't handle constant of type: {type}", { |
|
type: typeof val |
|
})); |
|
} |
|
} |
|
|
|
function best_of_expression(ast1, ast2) { |
|
return ast1.size() > ast2.size() ? ast2 : ast1; |
|
} |
|
|
|
function best_of_statement(ast1, ast2) { |
|
return best_of_expression( |
|
make_node(AST_SimpleStatement, ast1, { |
|
body: ast1 |
|
}), |
|
make_node(AST_SimpleStatement, ast2, { |
|
body: ast2 |
|
}) |
|
).body; |
|
} |
|
|
|
/** Find which node is smaller, and return that */ |
|
function best_of(compressor, ast1, ast2) { |
|
if (first_in_statement(compressor)) { |
|
return best_of_statement(ast1, ast2); |
|
} else { |
|
return best_of_expression(ast1, ast2); |
|
} |
|
} |
|
|
|
/** Simplify an object property's key, if possible */ |
|
function get_simple_key(key) { |
|
if (key instanceof AST_Constant) { |
|
return key.getValue(); |
|
} |
|
if (key instanceof AST_UnaryPrefix |
|
&& key.operator == "void" |
|
&& key.expression instanceof AST_Constant) { |
|
return undefined; |
|
} |
|
return key; |
|
} |
|
|
|
function read_property(obj, key) { |
|
key = get_simple_key(key); |
|
if (key instanceof AST_Node) return; |
|
|
|
var value; |
|
if (obj instanceof AST_Array) { |
|
var elements = obj.elements; |
|
if (key == "length") return make_node_from_constant(elements.length, obj); |
|
if (typeof key == "number" && key in elements) value = elements[key]; |
|
} else if (obj instanceof AST_Object) { |
|
key = "" + key; |
|
var props = obj.properties; |
|
for (var i = props.length; --i >= 0;) { |
|
var prop = props[i]; |
|
if (!(prop instanceof AST_ObjectKeyVal)) return; |
|
if (!value && props[i].key === key) value = props[i].value; |
|
} |
|
} |
|
|
|
return value instanceof AST_SymbolRef && value.fixed_value() || value; |
|
} |
|
|
|
function has_break_or_continue(loop, parent) { |
|
var found = false; |
|
var tw = new TreeWalker(function(node) { |
|
if (found || node instanceof AST_Scope) return true; |
|
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) { |
|
return found = true; |
|
} |
|
}); |
|
if (parent instanceof AST_LabeledStatement) tw.push(parent); |
|
tw.push(loop); |
|
loop.body.walk(tw); |
|
return found; |
|
} |
|
|
|
// we shouldn't compress (1,func)(something) to |
|
// func(something) because that changes the meaning of |
|
// the func (becomes lexical instead of global). |
|
function maintain_this_binding(parent, orig, val) { |
|
if (requires_sequence_to_maintain_binding(parent, orig, val)) { |
|
const zero = make_node(AST_Number, orig, { value: 0 }); |
|
return make_sequence(orig, [ zero, val ]); |
|
} else { |
|
return val; |
|
} |
|
} |
|
|
|
/** Detect (1, x.noThis)(), (0, eval)(), which need sequences */ |
|
function requires_sequence_to_maintain_binding(parent, orig, val) { |
|
return ( |
|
parent instanceof AST_UnaryPrefix && parent.operator == "delete" |
|
|| parent instanceof AST_Call && parent.expression === orig |
|
&& ( |
|
val instanceof AST_Chain |
|
|| val instanceof AST_PropAccess |
|
|| val instanceof AST_SymbolRef && val.name == "eval" |
|
) |
|
); |
|
} |
|
|
|
function is_func_expr(node) { |
|
return node instanceof AST_Arrow || node instanceof AST_Function; |
|
} |
|
|
|
/** |
|
* Used to determine whether the node can benefit from negation. |
|
* Not the case with arrow functions (you need an extra set of parens). */ |
|
function is_iife_call(node) { |
|
if (node.TYPE != "Call") return false; |
|
return node.expression instanceof AST_Function || is_iife_call(node.expression); |
|
} |
|
|
|
function is_empty(thing) { |
|
if (thing === null) return true; |
|
if (thing instanceof AST_EmptyStatement) return true; |
|
if (thing instanceof AST_BlockStatement) return thing.body.length == 0; |
|
return false; |
|
} |
|
|
|
const identifier_atom = makePredicate("Infinity NaN undefined"); |
|
function is_identifier_atom(node) { |
|
return node instanceof AST_Infinity |
|
|| node instanceof AST_NaN |
|
|| node instanceof AST_Undefined; |
|
} |
|
|
|
/** Check if this is a SymbolRef node which has one def of a certain AST type */ |
|
function is_ref_of(ref, type) { |
|
if (!(ref instanceof AST_SymbolRef)) return false; |
|
var orig = ref.definition().orig; |
|
for (var i = orig.length; --i >= 0;) { |
|
if (orig[i] instanceof type) return true; |
|
} |
|
} |
|
|
|
/**Can we turn { block contents... } into just the block contents ? |
|
* Not if one of these is inside. |
|
**/ |
|
function can_be_evicted_from_block(node) { |
|
return !( |
|
node instanceof AST_DefClass || |
|
node instanceof AST_Defun || |
|
node instanceof AST_Let || |
|
node instanceof AST_Const || |
|
node instanceof AST_Using || |
|
node instanceof AST_Export || |
|
node instanceof AST_Import |
|
); |
|
} |
|
|
|
function as_statement_array(thing) { |
|
if (thing === null) return []; |
|
if (thing instanceof AST_BlockStatement) return thing.body; |
|
if (thing instanceof AST_EmptyStatement) return []; |
|
if (thing instanceof AST_Statement) return [ thing ]; |
|
throw new Error("Can't convert thing to statement array"); |
|
} |
|
|
|
function is_reachable(scope_node, defs) { |
|
const find_ref = node => { |
|
if (node instanceof AST_SymbolRef && defs.includes(node.definition())) { |
|
return walk_abort; |
|
} |
|
}; |
|
|
|
return walk_parent(scope_node, (node, info) => { |
|
if (node instanceof AST_Scope && node !== scope_node) { |
|
var parent = info.parent(); |
|
|
|
if ( |
|
parent instanceof AST_Call |
|
&& parent.expression === node |
|
// Async/Generators aren't guaranteed to sync evaluate all of |
|
// their body steps, so it's possible they close over the variable. |
|
&& !(node.async || node.is_generator) |
|
) { |
|
return; |
|
} |
|
|
|
if (walk(node, find_ref)) return walk_abort; |
|
|
|
return true; |
|
} |
|
}); |
|
} |
|
|
|
/** Check if a ref refers to the name of a function/class it's defined within */ |
|
function is_recursive_ref(tw, def) { |
|
var node; |
|
for (var i = 0; node = tw.parent(i); i++) { |
|
if (node instanceof AST_Lambda || node instanceof AST_Class) { |
|
var name = node.name; |
|
if (name && name.definition() === def) { |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
// TODO this only works with AST_Defun, shouldn't it work for other ways of defining functions? |
|
function retain_top_func(fn, compressor) { |
|
return compressor.top_retain |
|
&& fn instanceof AST_Defun |
|
&& has_flag(fn, TOP) |
|
&& fn.name |
|
&& compressor.top_retain(fn.name.definition()); |
|
} |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
// Lists of native methods, useful for `unsafe` option which assumes they exist. |
|
// Note: Lots of methods and functions are missing here, in case they aren't pure |
|
// or not available in all JS environments. |
|
|
|
const make_nested_lookup = (feature_callback) => (compressor) => { |
|
const obj = feature_callback(feature_variables(compressor)); |
|
|
|
const out = new Map(); |
|
for (var key of Object.keys(obj)) { |
|
if (obj[key]) { |
|
out.set(key, makePredicate(remove_false(obj[key]))); |
|
} |
|
} |
|
|
|
const does_have = (global_name, fname) => { |
|
const inner_map = out.get(global_name); |
|
return inner_map != null && inner_map.has(fname); |
|
}; |
|
return does_have; |
|
}; |
|
|
|
const make_lookup = (feature_callback) => (compressor) => { |
|
const obj = feature_callback(feature_variables(compressor)); |
|
|
|
const predicate = makePredicate(remove_false(obj)); |
|
const does_have = (global_name) => { |
|
return predicate.has(global_name); |
|
}; |
|
return does_have; |
|
}; |
|
|
|
function remove_false(arr) { |
|
for (let i = 0; i < arr.length; i++) { |
|
if (arr[i] === false) { |
|
arr.splice(i, 1); |
|
i--; |
|
} |
|
} |
|
return arr; |
|
} |
|
|
|
/** Generate the object with arguments seen below */ |
|
function feature_variables(compressor) { |
|
return { |
|
sloppy: compressor.option("unsafe"), |
|
es: compressor.option("builtins_ecma"), |
|
}; |
|
} |
|
|
|
// eslint-disable-next-line no-unused-vars |
|
const pure_access_globals = make_lookup(({ sloppy, es }) => [ |
|
"Array", |
|
"Boolean", |
|
"clearInterval", |
|
"clearTimeout", |
|
"console", |
|
"Date", |
|
"decodeURI", |
|
"decodeURIComponent", |
|
"encodeURI", |
|
"encodeURIComponent", |
|
"Error", |
|
"escape", |
|
"eval", |
|
"EvalError", |
|
"Function", |
|
es >= 2020 && "globalThis", |
|
"isFinite", |
|
"isNaN", |
|
"JSON", |
|
"Math", |
|
"Number", |
|
"parseFloat", |
|
"parseInt", |
|
"RangeError", |
|
"ReferenceError", |
|
"RegExp", |
|
"Object", |
|
"setInterval", |
|
"setTimeout", |
|
"String", |
|
"SyntaxError", |
|
"TypeError", |
|
"unescape", |
|
"URIError", |
|
]); |
|
|
|
// Objects which are safe to access without throwing or causing a side effect. |
|
// Usually we'd check the `unsafe` option first but these are way too common for that |
|
const pure_prop_access_globals = new Set([ |
|
"Number", |
|
"String", |
|
"Array", |
|
"Object", |
|
"Function", |
|
"Promise", |
|
]); |
|
|
|
// eslint-disable-next-line no-unused-vars |
|
const is_pure_native_fn = make_lookup(({ sloppy, es }) => [ |
|
sloppy && es >= 2021 && "AggregateError", |
|
"Array", |
|
"ArrayBuffer", |
|
es >= 2020 && "BigInt", |
|
es >= 2020 && "BigInt64Array", |
|
es >= 2020 && "BigUint64Array", |
|
"Boolean", |
|
"Date", |
|
sloppy && "decodeURI", |
|
sloppy && "decodeURIComponent", |
|
sloppy && "encodeURI", |
|
sloppy && "encodeURIComponent", |
|
"Error", |
|
"escape", |
|
"EvalError", |
|
es >= 2021 && "FinalizationRegistry", |
|
es >= 2026 && "Float16Array", |
|
"Float32Array", |
|
"Float64Array", |
|
"Int16Array", |
|
"Int32Array", |
|
"Int8Array", |
|
"isFinite", |
|
"isNaN", |
|
es >= 2026 && "Iterator", |
|
es >= 2015 && "Map", |
|
"Number", |
|
"parseFloat", |
|
"parseInt", |
|
es >= 2015 && "Promise", |
|
es >= 2015 && "Proxy", |
|
"RangeError", |
|
"ReferenceError", |
|
sloppy && "RegExp", |
|
es >= 2015 && "Set", |
|
"String", |
|
es >= 2015 && "Symbol", |
|
"SyntaxError", |
|
"TypeError", |
|
"Uint16Array", |
|
"Uint32Array", |
|
"Uint8Array", |
|
"Uint8ClampedArray", |
|
sloppy && "unescape", |
|
"URIError", |
|
sloppy && es >= 2015 && "WeakMap", |
|
sloppy && es >= 2021 && "WeakRef", |
|
sloppy && es >= 2015 && "WeakSet", |
|
]); |
|
|
|
const arg1_is_iterable = new Set([ |
|
"Map", |
|
"Set", |
|
"WeakMap", |
|
"WeakSet", |
|
]); |
|
const arg1_is_range_or_iterable = new Set([ |
|
"ArrayBuffer", |
|
"Float32Array", |
|
"Float64Array", |
|
"Int16Array", |
|
"Int32Array", |
|
"Int8Array", |
|
"Uint16Array", |
|
"Uint32Array", |
|
"Uint8Array", |
|
"Uint8ClampedArray", |
|
]); |
|
const lone_arg_is_range = new Set(["Array"]); |
|
|
|
const object_methods = [ |
|
"constructor", |
|
"toString", |
|
"valueOf", |
|
]; |
|
|
|
// eslint-disable-next-line no-unused-vars |
|
const is_pure_native_method = make_nested_lookup(({ sloppy, es }) => ({ |
|
Array: [ |
|
es >= 2022 && "at", |
|
es >= 2019 && "flat", |
|
es >= 2016 && "includes", |
|
"indexOf", |
|
"join", |
|
"lastIndexOf", |
|
"slice", |
|
...object_methods, |
|
], |
|
Boolean: object_methods, |
|
Function: object_methods, |
|
Number: [ |
|
"toExponential", |
|
"toFixed", |
|
"toPrecision", |
|
...object_methods, |
|
], |
|
Object: object_methods, |
|
RegExp: [ |
|
"test", |
|
...object_methods, |
|
], |
|
String: [ |
|
es >= 2022 && "at", |
|
"charAt", |
|
"charCodeAt", |
|
es >= 2015 && "codePointAt", |
|
"concat", |
|
es >= 2025 && "endsWith", |
|
es >= 2015 && "includes", |
|
"indexOf", |
|
"italics", |
|
"lastIndexOf", |
|
es >= 2020 && "localeCompare", |
|
"match", |
|
es >= 2020 && "matchAll", |
|
es >= 2015 && "normalize", |
|
es >= 2017 && "padStart", |
|
es >= 2017 && "padEnd", |
|
es >= 2015 && sloppy && "repeat", |
|
"replace", |
|
es >= 2021 && "replaceAll", |
|
"search", |
|
"slice", |
|
"split", |
|
es >= 2015 && "startsWith", |
|
"substr", |
|
"substring", |
|
es >= 2015 && "repeat", |
|
"toLocaleLowerCase", |
|
"toLocaleUpperCase", |
|
"toLowerCase", |
|
"toUpperCase", |
|
"trim", |
|
es >= 2019 && "trimEnd", |
|
es >= 2019 && "trimStart", |
|
es >= 2019 && "trimLeft", |
|
es >= 2019 && "trimRight", |
|
...object_methods, |
|
], |
|
})); |
|
|
|
// eslint-disable-next-line no-unused-vars |
|
const is_pure_native_static_fn = make_nested_lookup(({ sloppy, es }) => ({ |
|
Array: [ |
|
"isArray", |
|
es >= 2015 && "of", |
|
], |
|
ArrayBuffer: [ |
|
"isView", |
|
], |
|
BigInt: es >= 2020 && [ |
|
sloppy && "asIntN", |
|
sloppy && "asUintN", |
|
], |
|
BigInt64Array: sloppy && es >= 2020 && ["of"], |
|
BigUint64Array: sloppy && es >= 2020 && ["of"], |
|
Date: [ |
|
"now", |
|
"parse", |
|
"UTC", |
|
], |
|
Error: [ |
|
es >= 2026 && "isError", |
|
], |
|
Float16Array: sloppy && es >= 2026 && ["of"], |
|
Float32Array: sloppy && ["of"], |
|
Float64Array: sloppy && ["of"], |
|
Int16Array: sloppy && ["of"], |
|
Int32Array: sloppy && ["of"], |
|
Int8Array: sloppy && ["of"], |
|
Math: [ |
|
"abs", |
|
"acos", |
|
es >= 2015 && "acosh", |
|
"asin", |
|
es >= 2015 && "asinh", |
|
"atan", |
|
"atan2", |
|
es >= 2015 && "atanh", |
|
es >= 2015 && "cbrt", |
|
"ceil", |
|
es >= 2015 && "clz32", |
|
"cos", |
|
es >= 2015 && "cosh", |
|
"exp", |
|
es >= 2015 && "expm1", |
|
"floor", |
|
es >= 2026 && "f16round", |
|
es >= 2015 && "fround", |
|
es >= 2015 && "hypot", |
|
es >= 2015 && "imul", |
|
"log", |
|
es >= 2015 && "log10", |
|
es >= 2015 && "log1p", |
|
es >= 2015 && "log2", |
|
"max", |
|
"min", |
|
"pow", |
|
"round", |
|
es >= 2015 && "sign", |
|
"sin", |
|
es >= 2015 && "sinh", |
|
"sqrt", |
|
"tan", |
|
es >= 2015 && "tanh", |
|
es >= 2015 && "trunc", |
|
], |
|
Number: [ |
|
es >= 2015 && "isFinite", |
|
es >= 2015 && "isInteger", |
|
es >= 2015 && "isSafeInteger", |
|
es >= 2015 && "isNaN", |
|
es >= 2015 && "parseFloat", |
|
es >= 2015 && "parseInt", |
|
], |
|
Object: [ |
|
sloppy && "create", |
|
sloppy && "getOwnPropertyDescriptor", |
|
es >= 2017 && sloppy && "getOwnPropertyDescriptors", |
|
sloppy && "getOwnPropertyNames", |
|
es >= 2015 && sloppy && "getOwnPropertySymbols", |
|
sloppy && "getPrototypeOf", |
|
es >= 2022 && sloppy && "hasOwn", |
|
es >= 2015 && "is", |
|
"isExtensible", |
|
"isFrozen", |
|
"isSealed", |
|
es >= 2015 && sloppy && "keys", |
|
], |
|
Promise: es >= 2015 && [ |
|
es >= 2024 && "withResolvers", |
|
], |
|
Proxy: es >= 2015 && [ |
|
sloppy && "revocable", |
|
], |
|
Reflect: es >= 2015 && [ |
|
sloppy && "has", |
|
sloppy && "isExtensible", |
|
sloppy && "ownKeys", |
|
], |
|
RegExp: [ |
|
es >= 2026 && sloppy && "escape", |
|
], |
|
String: [ |
|
"fromCharCode", |
|
sloppy && es >= 2025 && "fromCodePoint", |
|
], |
|
Uint16Array: ["of"], |
|
Uint32Array: ["of"], |
|
Uint8Array: ["of"], |
|
Uint8ClampedArray: ["of"], |
|
})); |
|
|
|
// Known numeric values which come with JS environments |
|
// eslint-disable-next-line no-unused-vars |
|
const is_pure_native_static_property = make_nested_lookup(({ sloppy, es }) => ({ |
|
Math: [ |
|
"E", |
|
"LN10", |
|
"LN2", |
|
"LOG2E", |
|
"LOG10E", |
|
"PI", |
|
"SQRT1_2", |
|
"SQRT2", |
|
], |
|
Number: [ |
|
es >= 2015 && "EPSILON", |
|
es >= 2015 && "MAX_SAFE_VALUE", |
|
"MAX_VALUE", |
|
es >= 2015 && "MIN_SAFE_VALUE", |
|
"MIN_VALUE", |
|
"NaN", |
|
"NEGATIVE_INFINITY", |
|
"POSITIVE_INFINITY", |
|
], |
|
RegExp: [ |
|
"$_", |
|
"$0", |
|
"$1", |
|
"$2", |
|
"$3", |
|
"$4", |
|
"$5", |
|
"$6", |
|
"$7", |
|
"$8", |
|
"$9", |
|
"input", |
|
"lastMatch", |
|
"lastParen", |
|
"leftContext", |
|
"rightContext", |
|
], |
|
})); |
|
|
|
const re_uppercase_first_letter = /^[A-Z]/; |
|
function is_pure_builtin_call(compressor, call) { |
|
let builtin = ""; |
|
let method = ""; |
|
|
|
let exp = call.expression; |
|
if (is_undeclared_ref(exp)) { |
|
builtin = exp.name; |
|
} else if (exp instanceof AST_Dot) { |
|
method = exp.property; |
|
|
|
exp = exp.expression; |
|
if (is_undeclared_ref(exp)) { |
|
if ( |
|
// globalThis.pureFunc() |
|
exp.name === "globalThis" |
|
&& compressor.option("builtins_ecma") >= 2020 |
|
) { |
|
builtin = method; |
|
method = ""; |
|
} else { |
|
// SomeBuiltin.pureFunc() |
|
builtin = exp.name; |
|
} |
|
} else if (exp instanceof AST_Dot) { |
|
if ( |
|
is_undeclared_ref(exp.expression) |
|
&& exp.expression.name === "globalThis" |
|
&& compressor.option("builtins_ecma") >= 2020 |
|
) { |
|
// globalThis.SomeBuiltin.pureFunc() |
|
builtin = exp.property; |
|
} else { |
|
return false; |
|
} |
|
} else { |
|
return false; |
|
} |
|
} else { |
|
return false; |
|
} |
|
|
|
if (!method) { |
|
if (compressor.is_pure_native_fn(builtin)) { |
|
// some require `new`, others throw if you use it |
|
const is_new = call instanceof AST_New; |
|
const should_be_new = re_uppercase_first_letter.test(builtin); // true of all `is_pure_native_fn` |
|
if (is_new !== should_be_new) return false; |
|
|
|
if (!is_builtin_pure_with_these_args(builtin, call.args)) { |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
return false; |
|
} else { |
|
return compressor.is_pure_native_static_fn(builtin, method); |
|
} |
|
} |
|
|
|
/** Some builtins are listed above but their purity is subject to some conditions */ |
|
function is_builtin_pure_with_these_args(builtin, args) { |
|
// all the builtins we deal with here are ok with getting 0 args |
|
if (args.length === 0) return true; |
|
|
|
let arg1 = args[0]; |
|
if (arg1 instanceof AST_SymbolRef) { |
|
arg1 = arg1.fixed_value(); |
|
} |
|
|
|
if (lone_arg_is_range.has(builtin)) { // new Array(number) |
|
const arg_valid = args.length > 1 |
|
|| arg1 instanceof AST_Number |
|
&& arg1.value >= 0 && arg1.value <= 0xffffffff; |
|
// TODO: or, we are asked to ignore TypeError |
|
if (!arg_valid) return false; |
|
} |
|
|
|
if (arg1_is_range_or_iterable.has(builtin)) { // new Float32Array(number | Array) |
|
const arg_valid = args.length === 0 |
|
|| arg1 instanceof AST_Array |
|
|| arg1 instanceof AST_Number |
|
&& arg1.value >= 0 && arg1.value <= 0xffffffff; |
|
if (!arg_valid) return false; |
|
} |
|
|
|
if (arg1_is_iterable.has(builtin)) { // new Set(iterable) |
|
const arg_valid = args.length === 0 || arg1 instanceof AST_Array; |
|
if (!arg_valid) return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
// Functions and methods to infer certain facts about expressions |
|
// It's not always possible to be 100% sure about something just by static analysis, |
|
// so `true` means yes, and `false` means maybe |
|
|
|
const is_undeclared_ref = (node) => |
|
node instanceof AST_SymbolRef && node.definition().undeclared; |
|
|
|
const bitwise_binop = makePredicate("<<< >> << & | ^ ~"); |
|
const lazy_op = makePredicate("&& || ??"); |
|
const unary_side_effects = makePredicate("delete ++ --"); |
|
|
|
// methods to determine whether an expression has a boolean result type |
|
(function(def_is_boolean) { |
|
const unary_bool = makePredicate("! delete"); |
|
const binary_bool = makePredicate("in instanceof == != === !== < <= >= >"); |
|
def_is_boolean(AST_Node, return_false); |
|
def_is_boolean(AST_UnaryPrefix, function() { |
|
return unary_bool.has(this.operator); |
|
}); |
|
def_is_boolean(AST_Binary, function() { |
|
return binary_bool.has(this.operator) |
|
|| lazy_op.has(this.operator) |
|
&& this.left.is_boolean() |
|
&& this.right.is_boolean(); |
|
}); |
|
def_is_boolean(AST_Conditional, function() { |
|
return this.consequent.is_boolean() && this.alternative.is_boolean(); |
|
}); |
|
def_is_boolean(AST_Assign, function() { |
|
return this.operator == "=" && this.right.is_boolean(); |
|
}); |
|
def_is_boolean(AST_Sequence, function() { |
|
return this.tail_node().is_boolean(); |
|
}); |
|
def_is_boolean(AST_True, return_true); |
|
def_is_boolean(AST_False, return_true); |
|
})(function(node, func) { |
|
node.DEFMETHOD("is_boolean", func); |
|
}); |
|
|
|
// methods to determine if an expression has a numeric result type |
|
(function(def_is_number) { |
|
def_is_number(AST_Node, return_false); |
|
def_is_number(AST_Number, return_true); |
|
const unary = makePredicate("+ - ~ ++ --"); |
|
def_is_number(AST_Unary, function(compressor) { |
|
return unary.has(this.operator) && this.expression.is_number(compressor); |
|
}); |
|
const numeric_ops = makePredicate("- * / % & | ^ << >> >>>"); |
|
def_is_number(AST_Binary, function(compressor) { |
|
if (this.operator === "+") { |
|
// Both sides need to be `number`. Or one is a `number` and the other is number-ish. |
|
return this.left.is_number(compressor) && this.right.is_number_or_bigint(compressor) |
|
|| this.right.is_number(compressor) && this.left.is_number_or_bigint(compressor); |
|
} else if (numeric_ops.has(this.operator)) { |
|
return this.left.is_number(compressor) || this.right.is_number(compressor); |
|
} else { |
|
return false; |
|
} |
|
}); |
|
def_is_number(AST_Assign, function(compressor) { |
|
return (this.operator === "=" || numeric_ops.has(this.operator.slice(0, -1))) |
|
&& this.right.is_number(compressor); |
|
}); |
|
def_is_number(AST_Sequence, function(compressor) { |
|
return this.tail_node().is_number(compressor); |
|
}); |
|
def_is_number(AST_Conditional, function(compressor) { |
|
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor); |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("is_number", func); |
|
}); |
|
|
|
// methods to determine if an expression returns a BigInt |
|
(function(def_is_bigint) { |
|
def_is_bigint(AST_Node, return_false); |
|
def_is_bigint(AST_BigInt, return_true); |
|
const unary = makePredicate("+ - ~ ++ --"); |
|
def_is_bigint(AST_Unary, function(compressor) { |
|
return unary.has(this.operator) && this.expression.is_bigint(compressor); |
|
}); |
|
const numeric_ops = makePredicate("- * / % & | ^ << >>"); |
|
def_is_bigint(AST_Binary, function(compressor) { |
|
if (this.operator === "+") { |
|
return this.left.is_bigint(compressor) && this.right.is_number_or_bigint(compressor) |
|
|| this.right.is_bigint(compressor) && this.left.is_number_or_bigint(compressor); |
|
} else if (numeric_ops.has(this.operator)) { |
|
return this.left.is_bigint(compressor) || this.right.is_bigint(compressor); |
|
} else { |
|
return false; |
|
} |
|
}); |
|
def_is_bigint(AST_Assign, function(compressor) { |
|
return (numeric_ops.has(this.operator.slice(0, -1)) || this.operator == "=") |
|
&& this.right.is_bigint(compressor); |
|
}); |
|
def_is_bigint(AST_Sequence, function(compressor) { |
|
return this.tail_node().is_bigint(compressor); |
|
}); |
|
def_is_bigint(AST_Conditional, function(compressor) { |
|
return this.consequent.is_bigint(compressor) && this.alternative.is_bigint(compressor); |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("is_bigint", func); |
|
}); |
|
|
|
// methods to determine if an expression is a number or a bigint |
|
(function(def_is_number_or_bigint) { |
|
def_is_number_or_bigint(AST_Node, return_false); |
|
def_is_number_or_bigint(AST_Number, return_true); |
|
def_is_number_or_bigint(AST_BigInt, return_true); |
|
const numeric_unary_ops = makePredicate("+ - ~ ++ --"); |
|
def_is_number_or_bigint(AST_Unary, function(_compressor) { |
|
return numeric_unary_ops.has(this.operator); |
|
}); |
|
const numeric_ops = makePredicate("- * / % & | ^ << >>"); |
|
def_is_number_or_bigint(AST_Binary, function(compressor) { |
|
return this.operator === "+" |
|
? this.left.is_number_or_bigint(compressor) && this.right.is_number_or_bigint(compressor) |
|
: numeric_ops.has(this.operator); |
|
}); |
|
def_is_number_or_bigint(AST_Assign, function(compressor) { |
|
return numeric_ops.has(this.operator.slice(0, -1)) |
|
|| this.operator == "=" && this.right.is_number_or_bigint(compressor); |
|
}); |
|
def_is_number_or_bigint(AST_Sequence, function(compressor) { |
|
return this.tail_node().is_number_or_bigint(compressor); |
|
}); |
|
def_is_number_or_bigint(AST_Conditional, function(compressor) { |
|
return this.consequent.is_number_or_bigint(compressor) && this.alternative.is_number_or_bigint(compressor); |
|
}); |
|
}(function (node, func) { |
|
node.DEFMETHOD("is_number_or_bigint", func); |
|
})); |
|
|
|
|
|
// methods to determine if an expression is a 32 bit integer (IE results from bitwise ops, or is an integer constant fitting in that size |
|
(function(def_is_32_bit_integer) { |
|
def_is_32_bit_integer(AST_Node, return_false); |
|
def_is_32_bit_integer(AST_Number, function(_compressor) { |
|
return this.value === (this.value | 0); |
|
}); |
|
def_is_32_bit_integer(AST_UnaryPrefix, function(compressor) { |
|
return this.operator == "~" ? this.expression.is_number(compressor) |
|
: this.operator === "+" ? this.expression.is_32_bit_integer(compressor) |
|
: false; |
|
}); |
|
def_is_32_bit_integer(AST_Binary, function(compressor) { |
|
return bitwise_binop.has(this.operator) |
|
&& (this.left.is_number(compressor) || this.right.is_number(compressor)); |
|
}); |
|
}(function (node, func) { |
|
node.DEFMETHOD("is_32_bit_integer", func); |
|
})); |
|
|
|
// methods to determine if an expression has a string result type |
|
(function(def_is_string) { |
|
def_is_string(AST_Node, return_false); |
|
def_is_string(AST_String, return_true); |
|
def_is_string(AST_TemplateString, return_true); |
|
def_is_string(AST_UnaryPrefix, function() { |
|
return this.operator == "typeof"; |
|
}); |
|
def_is_string(AST_Binary, function(compressor) { |
|
return this.operator == "+" && |
|
(this.left.is_string(compressor) || this.right.is_string(compressor)); |
|
}); |
|
def_is_string(AST_Assign, function(compressor) { |
|
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor); |
|
}); |
|
def_is_string(AST_Sequence, function(compressor) { |
|
return this.tail_node().is_string(compressor); |
|
}); |
|
def_is_string(AST_Conditional, function(compressor) { |
|
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor); |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("is_string", func); |
|
}); |
|
|
|
function is_undefined(node, compressor) { |
|
return ( |
|
has_flag(node, UNDEFINED) |
|
|| node instanceof AST_Undefined |
|
|| node instanceof AST_UnaryPrefix |
|
&& node.operator == "void" |
|
&& !node.expression.has_side_effects(compressor) |
|
); |
|
} |
|
|
|
// Is the node explicitly null or undefined. |
|
function is_null_or_undefined(node, compressor) { |
|
let fixed; |
|
return ( |
|
node instanceof AST_Null |
|
|| is_undefined(node, compressor) |
|
|| ( |
|
node instanceof AST_SymbolRef |
|
&& (fixed = node.definition().fixed) instanceof AST_Node |
|
&& is_nullish(fixed, compressor) |
|
) |
|
); |
|
} |
|
|
|
// Find out if this expression is optionally chained from a base-point that we |
|
// can statically analyze as null or undefined. |
|
function is_nullish_shortcircuited(node, compressor) { |
|
if (node instanceof AST_PropAccess || node instanceof AST_Call) { |
|
return ( |
|
(node.optional && is_null_or_undefined(node.expression, compressor)) |
|
|| is_nullish_shortcircuited(node.expression, compressor) |
|
); |
|
} |
|
if (node instanceof AST_Chain) return is_nullish_shortcircuited(node.expression, compressor); |
|
return false; |
|
} |
|
|
|
// Find out if something is == null, or can short circuit into nullish. |
|
// Used to optimize ?. and ?? |
|
function is_nullish(node, compressor) { |
|
if (is_null_or_undefined(node, compressor)) return true; |
|
return is_nullish_shortcircuited(node, compressor); |
|
} |
|
|
|
// Determine if expression might cause side effects |
|
// If there's a possibility that a node may change something when it's executed, this returns true |
|
(function(def_has_side_effects) { |
|
def_has_side_effects(AST_Node, return_true); |
|
|
|
def_has_side_effects(AST_EmptyStatement, return_false); |
|
def_has_side_effects(AST_Constant, return_false); |
|
def_has_side_effects(AST_This, return_false); |
|
|
|
function any(list, compressor) { |
|
for (var i = list.length; --i >= 0;) |
|
if (list[i].has_side_effects(compressor)) |
|
return true; |
|
return false; |
|
} |
|
|
|
def_has_side_effects(AST_Block, function(compressor) { |
|
return any(this.body, compressor); |
|
}); |
|
def_has_side_effects(AST_Call, function(compressor) { |
|
if ( |
|
!this.is_callee_pure(compressor) |
|
&& (!this.expression.is_call_pure(compressor) |
|
|| this.expression.has_side_effects(compressor)) |
|
) { |
|
return true; |
|
} |
|
return any(this.args, compressor); |
|
}); |
|
def_has_side_effects(AST_Switch, function(compressor) { |
|
return this.expression.has_side_effects(compressor) |
|
|| any(this.body, compressor); |
|
}); |
|
def_has_side_effects(AST_Case, function(compressor) { |
|
return this.expression.has_side_effects(compressor) |
|
|| any(this.body, compressor); |
|
}); |
|
def_has_side_effects(AST_Try, function(compressor) { |
|
return this.body.has_side_effects(compressor) |
|
|| this.bcatch && this.bcatch.has_side_effects(compressor) |
|
|| this.bfinally && this.bfinally.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_If, function(compressor) { |
|
return this.condition.has_side_effects(compressor) |
|
|| this.body && this.body.has_side_effects(compressor) |
|
|| this.alternative && this.alternative.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_ImportMeta, return_false); |
|
def_has_side_effects(AST_LabeledStatement, function(compressor) { |
|
return this.body.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_SimpleStatement, function(compressor) { |
|
return this.body.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_Lambda, return_false); |
|
def_has_side_effects(AST_Class, function (compressor) { |
|
if (this.extends && this.extends.has_side_effects(compressor)) { |
|
return true; |
|
} |
|
return any(this.properties, compressor); |
|
}); |
|
def_has_side_effects(AST_ClassStaticBlock, function(compressor) { |
|
return any(this.body, compressor); |
|
}); |
|
def_has_side_effects(AST_Binary, function(compressor) { |
|
return this.left.has_side_effects(compressor) |
|
|| this.right.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_Assign, return_true); |
|
def_has_side_effects(AST_Conditional, function(compressor) { |
|
return this.condition.has_side_effects(compressor) |
|
|| this.consequent.has_side_effects(compressor) |
|
|| this.alternative.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_Unary, function(compressor) { |
|
return unary_side_effects.has(this.operator) |
|
|| this.expression.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_SymbolRef, function(compressor) { |
|
return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name); |
|
}); |
|
def_has_side_effects(AST_SymbolClassProperty, return_false); |
|
def_has_side_effects(AST_SymbolDeclaration, return_false); |
|
def_has_side_effects(AST_Object, function(compressor) { |
|
return any(this.properties, compressor); |
|
}); |
|
def_has_side_effects(AST_ObjectKeyVal, function(compressor) { |
|
return ( |
|
this.computed_key() && this.key.has_side_effects(compressor) |
|
|| this.value && this.value.has_side_effects(compressor) |
|
); |
|
}); |
|
def_has_side_effects([ |
|
AST_ClassProperty, |
|
AST_ClassPrivateProperty, |
|
], function(compressor) { |
|
return ( |
|
this.computed_key() && this.key.has_side_effects(compressor) |
|
|| this.static && this.value && this.value.has_side_effects(compressor) |
|
); |
|
}); |
|
def_has_side_effects([ |
|
AST_PrivateMethod, |
|
AST_PrivateGetter, |
|
AST_PrivateSetter, |
|
AST_ConciseMethod, |
|
AST_ObjectGetter, |
|
AST_ObjectSetter, |
|
], function(compressor) { |
|
return this.computed_key() && this.key.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_Array, function(compressor) { |
|
return any(this.elements, compressor); |
|
}); |
|
def_has_side_effects(AST_Dot, function(compressor) { |
|
if (is_nullish(this, compressor)) { |
|
return this.expression.has_side_effects(compressor); |
|
} |
|
if (!this.optional && this.expression.may_throw_on_access(compressor)) { |
|
return true; |
|
} |
|
|
|
return this.expression.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_Sub, function(compressor) { |
|
if (is_nullish(this, compressor)) { |
|
return this.expression.has_side_effects(compressor); |
|
} |
|
if (!this.optional && this.expression.may_throw_on_access(compressor)) { |
|
return true; |
|
} |
|
|
|
var property = this.property.has_side_effects(compressor); |
|
if (property && this.optional) return true; // "?." is a condition |
|
|
|
return property || this.expression.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_Chain, function (compressor) { |
|
return this.expression.has_side_effects(compressor); |
|
}); |
|
def_has_side_effects(AST_Sequence, function(compressor) { |
|
return any(this.expressions, compressor); |
|
}); |
|
def_has_side_effects(AST_Definitions, function(compressor) { |
|
return any(this.definitions, compressor); |
|
}); |
|
def_has_side_effects(AST_VarDef, function() { |
|
return this.value != null; |
|
}); |
|
def_has_side_effects(AST_TemplateSegment, return_false); |
|
def_has_side_effects(AST_TemplateString, function(compressor) { |
|
return any(this.segments, compressor); |
|
}); |
|
})(function(node_or_nodes, func) { |
|
for (const node of [].concat(node_or_nodes)) { |
|
node.DEFMETHOD("has_side_effects", func); |
|
} |
|
}); |
|
|
|
// determine if expression may throw |
|
(function(def_may_throw) { |
|
def_may_throw(AST_Node, return_true); |
|
|
|
def_may_throw(AST_Constant, return_false); |
|
def_may_throw(AST_EmptyStatement, return_false); |
|
def_may_throw(AST_Lambda, return_false); |
|
def_may_throw(AST_SymbolDeclaration, return_false); |
|
def_may_throw(AST_This, return_false); |
|
def_may_throw(AST_ImportMeta, return_false); |
|
|
|
function any(list, compressor) { |
|
for (var i = list.length; --i >= 0;) |
|
if (list[i].may_throw(compressor)) |
|
return true; |
|
return false; |
|
} |
|
|
|
def_may_throw(AST_Class, function(compressor) { |
|
if (this.extends && this.extends.may_throw(compressor)) return true; |
|
return any(this.properties, compressor); |
|
}); |
|
def_may_throw(AST_ClassStaticBlock, function (compressor) { |
|
return any(this.body, compressor); |
|
}); |
|
|
|
def_may_throw(AST_Array, function(compressor) { |
|
return any(this.elements, compressor); |
|
}); |
|
def_may_throw(AST_Assign, function(compressor) { |
|
if (this.right.may_throw(compressor)) return true; |
|
if (!compressor.has_directive("use strict") |
|
&& this.operator == "=" |
|
&& this.left instanceof AST_SymbolRef) { |
|
return false; |
|
} |
|
return this.left.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Binary, function(compressor) { |
|
return this.left.may_throw(compressor) |
|
|| this.right.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Block, function(compressor) { |
|
return any(this.body, compressor); |
|
}); |
|
def_may_throw(AST_Call, function(compressor) { |
|
if (is_nullish(this, compressor)) return false; |
|
if (any(this.args, compressor)) return true; |
|
if (this.is_callee_pure(compressor)) return false; |
|
if (this.expression.may_throw(compressor)) return true; |
|
return !(this.expression instanceof AST_Lambda) |
|
|| any(this.expression.body, compressor); |
|
}); |
|
def_may_throw(AST_Case, function(compressor) { |
|
return this.expression.may_throw(compressor) |
|
|| any(this.body, compressor); |
|
}); |
|
def_may_throw(AST_Conditional, function(compressor) { |
|
return this.condition.may_throw(compressor) |
|
|| this.consequent.may_throw(compressor) |
|
|| this.alternative.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Definitions, function(compressor) { |
|
return any(this.definitions, compressor); |
|
}); |
|
def_may_throw(AST_If, function(compressor) { |
|
return this.condition.may_throw(compressor) |
|
|| this.body && this.body.may_throw(compressor) |
|
|| this.alternative && this.alternative.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_LabeledStatement, function(compressor) { |
|
return this.body.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Object, function(compressor) { |
|
return any(this.properties, compressor); |
|
}); |
|
def_may_throw(AST_ObjectKeyVal, function(compressor) { |
|
return ( |
|
this.computed_key() && this.key.may_throw(compressor) |
|
|| this.value ? this.value.may_throw(compressor) : false |
|
); |
|
}); |
|
def_may_throw([ |
|
AST_ClassProperty, |
|
AST_ClassPrivateProperty, |
|
], function(compressor) { |
|
return ( |
|
this.computed_key() && this.key.may_throw(compressor) |
|
|| this.static && this.value && this.value.may_throw(compressor) |
|
); |
|
}); |
|
def_may_throw([ |
|
AST_ConciseMethod, |
|
AST_ObjectGetter, |
|
AST_ObjectSetter, |
|
], function(compressor) { |
|
return this.computed_key() && this.key.may_throw(compressor); |
|
}); |
|
def_may_throw([ |
|
AST_PrivateMethod, |
|
AST_PrivateGetter, |
|
AST_PrivateSetter, |
|
], return_false); |
|
def_may_throw(AST_Return, function(compressor) { |
|
return this.value && this.value.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Sequence, function(compressor) { |
|
return any(this.expressions, compressor); |
|
}); |
|
def_may_throw(AST_SimpleStatement, function(compressor) { |
|
return this.body.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Dot, function(compressor) { |
|
if (is_nullish(this, compressor)) return false; |
|
return !this.optional && this.expression.may_throw_on_access(compressor) |
|
|| this.expression.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Sub, function(compressor) { |
|
if (is_nullish(this, compressor)) return false; |
|
return !this.optional && this.expression.may_throw_on_access(compressor) |
|
|| this.expression.may_throw(compressor) |
|
|| this.property.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Chain, function(compressor) { |
|
return this.expression.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Switch, function(compressor) { |
|
return this.expression.may_throw(compressor) |
|
|| any(this.body, compressor); |
|
}); |
|
def_may_throw(AST_SymbolRef, function(compressor) { |
|
return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name); |
|
}); |
|
def_may_throw(AST_SymbolClassProperty, return_false); |
|
def_may_throw(AST_Try, function(compressor) { |
|
return this.bcatch ? this.bcatch.may_throw(compressor) : this.body.may_throw(compressor) |
|
|| this.bfinally && this.bfinally.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_Unary, function(compressor) { |
|
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) |
|
return false; |
|
return this.expression.may_throw(compressor); |
|
}); |
|
def_may_throw(AST_VarDef, function(compressor) { |
|
if (!this.value) return false; |
|
return this.value.may_throw(compressor); |
|
}); |
|
})(function(node_or_nodes, func) { |
|
for (const node of [].concat(node_or_nodes)) { |
|
node.DEFMETHOD("may_throw", func); |
|
} |
|
}); |
|
|
|
// determine if expression is constant |
|
(function(def_is_constant_expression) { |
|
function all_refs_local(scope) { |
|
let result = true; |
|
walk(this, node => { |
|
if (node instanceof AST_SymbolRef) { |
|
if (has_flag(this, INLINED)) { |
|
result = false; |
|
return walk_abort; |
|
} |
|
var def = node.definition(); |
|
if ( |
|
member(def, this.enclosed) |
|
&& !this.variables.has(def.name) |
|
) { |
|
if (scope) { |
|
var scope_def = scope.find_variable(node); |
|
if (def.undeclared ? !scope_def : scope_def === def) { |
|
result = "f"; |
|
return true; |
|
} |
|
} |
|
result = false; |
|
return walk_abort; |
|
} |
|
return true; |
|
} |
|
if (node instanceof AST_This && this instanceof AST_Arrow) { |
|
result = false; |
|
return walk_abort; |
|
} |
|
}); |
|
return result; |
|
} |
|
|
|
def_is_constant_expression(AST_Node, return_false); |
|
def_is_constant_expression(AST_Constant, return_true); |
|
def_is_constant_expression(AST_Class, function(scope) { |
|
if (this.extends && !this.extends.is_constant_expression(scope)) { |
|
return false; |
|
} |
|
|
|
for (const prop of this.properties) { |
|
if (prop.computed_key() && !prop.key.is_constant_expression(scope)) { |
|
return false; |
|
} |
|
if (prop.static && prop.value && !prop.value.is_constant_expression(scope)) { |
|
return false; |
|
} |
|
if (prop instanceof AST_ClassStaticBlock) { |
|
return false; |
|
} |
|
} |
|
|
|
return all_refs_local.call(this, scope); |
|
}); |
|
def_is_constant_expression(AST_Lambda, all_refs_local); |
|
def_is_constant_expression(AST_Unary, function() { |
|
return this.expression.is_constant_expression(); |
|
}); |
|
def_is_constant_expression(AST_Binary, function() { |
|
return this.left.is_constant_expression() |
|
&& this.right.is_constant_expression(); |
|
}); |
|
def_is_constant_expression(AST_Array, function() { |
|
return this.elements.every((l) => l.is_constant_expression()); |
|
}); |
|
def_is_constant_expression(AST_Object, function() { |
|
return this.properties.every((l) => l.is_constant_expression()); |
|
}); |
|
def_is_constant_expression(AST_ObjectProperty, function() { |
|
return !!(!(this.key instanceof AST_Node) && this.value && this.value.is_constant_expression()); |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("is_constant_expression", func); |
|
}); |
|
|
|
|
|
// may_throw_on_access() |
|
// returns true if this node may be null, undefined or contain `AST_Accessor` |
|
(function(def_may_throw_on_access) { |
|
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) { |
|
return !compressor.option("pure_getters") |
|
|| this._dot_throw(compressor); |
|
}); |
|
|
|
function is_strict(compressor) { |
|
return /strict/.test(compressor.option("pure_getters")); |
|
} |
|
|
|
def_may_throw_on_access(AST_Node, is_strict); |
|
def_may_throw_on_access(AST_Null, return_true); |
|
def_may_throw_on_access(AST_Undefined, return_true); |
|
def_may_throw_on_access(AST_Constant, return_false); |
|
def_may_throw_on_access(AST_Array, return_false); |
|
def_may_throw_on_access(AST_Object, function(compressor) { |
|
if (!is_strict(compressor)) return false; |
|
for (var i = this.properties.length; --i >=0;) |
|
if (this.properties[i]._dot_throw(compressor)) return true; |
|
return false; |
|
}); |
|
// Do not be as strict with classes as we are with objects. |
|
// Hopefully the community is not going to abuse static getters and setters. |
|
// https://github.com/terser/terser/issues/724#issuecomment-643655656 |
|
def_may_throw_on_access(AST_Class, return_false); |
|
def_may_throw_on_access(AST_ObjectProperty, return_false); |
|
def_may_throw_on_access(AST_ObjectGetter, return_true); |
|
def_may_throw_on_access(AST_Expansion, function(compressor) { |
|
return this.expression._dot_throw(compressor); |
|
}); |
|
def_may_throw_on_access(AST_Function, return_false); |
|
def_may_throw_on_access(AST_Arrow, return_false); |
|
def_may_throw_on_access(AST_UnaryPostfix, return_false); |
|
def_may_throw_on_access(AST_UnaryPrefix, function() { |
|
return this.operator == "void"; |
|
}); |
|
def_may_throw_on_access(AST_Binary, function(compressor) { |
|
return (this.operator == "&&" || this.operator == "||" || this.operator == "??") |
|
&& (this.left._dot_throw(compressor) || this.right._dot_throw(compressor)); |
|
}); |
|
def_may_throw_on_access(AST_Assign, function(compressor) { |
|
if (this.logical) return true; |
|
|
|
return this.operator == "=" |
|
&& this.right._dot_throw(compressor); |
|
}); |
|
def_may_throw_on_access(AST_Conditional, function(compressor) { |
|
return this.consequent._dot_throw(compressor) |
|
|| this.alternative._dot_throw(compressor); |
|
}); |
|
def_may_throw_on_access(AST_Dot, function(compressor) { |
|
if (!is_strict(compressor)) return false; |
|
|
|
if (this.property == "prototype") { |
|
return !( |
|
this.expression instanceof AST_Function |
|
|| this.expression instanceof AST_Class |
|
); |
|
} |
|
return true; |
|
}); |
|
def_may_throw_on_access(AST_Chain, function(compressor) { |
|
return this.expression._dot_throw(compressor); |
|
}); |
|
def_may_throw_on_access(AST_Sequence, function(compressor) { |
|
return this.tail_node()._dot_throw(compressor); |
|
}); |
|
def_may_throw_on_access(AST_SymbolRef, function(compressor) { |
|
if (this.name === "arguments" && this.scope instanceof AST_Lambda) return false; |
|
if (has_flag(this, UNDEFINED)) return true; |
|
if (!is_strict(compressor)) return false; |
|
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false; |
|
if (this.is_immutable()) return false; |
|
var fixed = this.fixed_value(); |
|
return !fixed || fixed._dot_throw(compressor); |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("_dot_throw", func); |
|
}); |
|
|
|
function is_lhs(node, parent) { |
|
if (parent instanceof AST_Unary && unary_side_effects.has(parent.operator)) return parent.expression; |
|
if (parent instanceof AST_Assign && parent.left === node) return node; |
|
if (parent instanceof AST_ForIn && parent.init === node) return node; |
|
} |
|
|
|
// method to negate an expression |
|
(function(def_negate) { |
|
function basic_negation(exp) { |
|
return make_node(AST_UnaryPrefix, exp, { |
|
operator: "!", |
|
expression: exp |
|
}); |
|
} |
|
function best(orig, alt, first_in_statement) { |
|
var negated = basic_negation(orig); |
|
if (first_in_statement) { |
|
var stat = make_node(AST_SimpleStatement, alt, { |
|
body: alt |
|
}); |
|
return best_of_expression(negated, stat) === stat ? alt : negated; |
|
} |
|
return best_of_expression(negated, alt); |
|
} |
|
def_negate(AST_Node, function() { |
|
return basic_negation(this); |
|
}); |
|
def_negate(AST_Statement, function() { |
|
throw new Error("Cannot negate a statement"); |
|
}); |
|
def_negate(AST_Function, function() { |
|
return basic_negation(this); |
|
}); |
|
def_negate(AST_Class, function() { |
|
return basic_negation(this); |
|
}); |
|
def_negate(AST_Arrow, function() { |
|
return basic_negation(this); |
|
}); |
|
def_negate(AST_UnaryPrefix, function() { |
|
if (this.operator == "!") |
|
return this.expression; |
|
return basic_negation(this); |
|
}); |
|
def_negate(AST_Sequence, function(compressor) { |
|
var expressions = this.expressions.slice(); |
|
expressions.push(expressions.pop().negate(compressor)); |
|
return make_sequence(this, expressions); |
|
}); |
|
def_negate(AST_Conditional, function(compressor, first_in_statement) { |
|
var self = this.clone(); |
|
self.consequent = self.consequent.negate(compressor); |
|
self.alternative = self.alternative.negate(compressor); |
|
return best(this, self, first_in_statement); |
|
}); |
|
def_negate(AST_Binary, function(compressor, first_in_statement) { |
|
var self = this.clone(), op = this.operator; |
|
if (compressor.option("unsafe_comps")) { |
|
switch (op) { |
|
case "<=" : self.operator = ">" ; return self; |
|
case "<" : self.operator = ">=" ; return self; |
|
case ">=" : self.operator = "<" ; return self; |
|
case ">" : self.operator = "<=" ; return self; |
|
} |
|
} |
|
switch (op) { |
|
case "==" : self.operator = "!="; return self; |
|
case "!=" : self.operator = "=="; return self; |
|
case "===": self.operator = "!=="; return self; |
|
case "!==": self.operator = "==="; return self; |
|
case "&&": |
|
self.operator = "||"; |
|
self.left = self.left.negate(compressor, first_in_statement); |
|
self.right = self.right.negate(compressor); |
|
return best(this, self, first_in_statement); |
|
case "||": |
|
self.operator = "&&"; |
|
self.left = self.left.negate(compressor, first_in_statement); |
|
self.right = self.right.negate(compressor); |
|
return best(this, self, first_in_statement); |
|
} |
|
return basic_negation(this); |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("negate", function(compressor, first_in_statement) { |
|
return func.call(this, compressor, first_in_statement); |
|
}); |
|
}); |
|
|
|
(function (def_bitwise_negate) { |
|
function basic_bitwise_negation(exp) { |
|
return make_node(AST_UnaryPrefix, exp, { |
|
operator: "~", |
|
expression: exp |
|
}); |
|
} |
|
|
|
def_bitwise_negate(AST_Node, function(_compressor) { |
|
return basic_bitwise_negation(this); |
|
}); |
|
|
|
def_bitwise_negate(AST_Number, function(_compressor) { |
|
const neg = ~this.value; |
|
if (neg.toString().length > this.value.toString().length) { |
|
return basic_bitwise_negation(this); |
|
} |
|
return make_node(AST_Number, this, { value: neg }); |
|
}); |
|
|
|
def_bitwise_negate(AST_UnaryPrefix, function(compressor, in_32_bit_context) { |
|
if ( |
|
this.operator == "~" |
|
&& ( |
|
this.expression.is_32_bit_integer(compressor) || |
|
(in_32_bit_context != null ? in_32_bit_context : compressor.in_32_bit_context()) |
|
) |
|
) { |
|
return this.expression; |
|
} else { |
|
return basic_bitwise_negation(this); |
|
} |
|
}); |
|
})(function (node, func) { |
|
node.DEFMETHOD("bitwise_negate", func); |
|
}); |
|
|
|
// Is the callee of this function pure? |
|
var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError"); |
|
AST_Call.DEFMETHOD("is_callee_pure", function(compressor) { |
|
if (compressor.option("unsafe")) { |
|
var expr = this.expression; |
|
var first_arg; |
|
if ( |
|
expr.expression && expr.expression.name === "hasOwnProperty" && |
|
( |
|
(first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor))) == null |
|
|| first_arg.thedef && first_arg.thedef.undeclared |
|
) |
|
) { |
|
return false; |
|
} |
|
if (is_undeclared_ref(expr) && global_pure_fns.has(expr.name)) return true; |
|
if (is_pure_builtin_call(compressor, this)) return true; |
|
} else if (compressor.option("builtins_pure")) { |
|
if (is_pure_builtin_call(compressor, this)) return true; |
|
} |
|
if ((this instanceof AST_New) && compressor.option("pure_new")) { |
|
return true; |
|
} |
|
if (compressor.option("side_effects") && has_annotation(this, _PURE)) { |
|
return true; |
|
} |
|
return !compressor.pure_funcs(this); |
|
}); |
|
|
|
// If I call this, is it a pure function? |
|
AST_Node.DEFMETHOD("is_call_pure", return_false); |
|
AST_Dot.DEFMETHOD("is_call_pure", function(compressor) { |
|
if (!compressor.option("unsafe")) return; |
|
const expr = this.expression; |
|
|
|
let native_obj; |
|
if (expr instanceof AST_Array) { |
|
native_obj = "Array"; |
|
} else if (expr.is_boolean()) { |
|
native_obj = "Boolean"; |
|
} else if (expr.is_number(compressor)) { |
|
native_obj = "Number"; |
|
} else if (expr instanceof AST_RegExp) { |
|
native_obj = "RegExp"; |
|
} else if (expr.is_string(compressor)) { |
|
native_obj = "String"; |
|
} else if (!this.may_throw_on_access(compressor)) { |
|
native_obj = "Object"; |
|
} |
|
return native_obj != null && compressor.is_pure_native_method(native_obj, this.property); |
|
}); |
|
|
|
// tell me if a statement aborts |
|
const aborts = (thing) => thing && thing.aborts(); |
|
|
|
(function(def_aborts) { |
|
def_aborts(AST_Statement, return_null); |
|
def_aborts(AST_Jump, return_this); |
|
function block_aborts() { |
|
for (var i = 0; i < this.body.length; i++) { |
|
if (aborts(this.body[i])) { |
|
return this.body[i]; |
|
} |
|
} |
|
return null; |
|
} |
|
def_aborts(AST_Import, return_null); |
|
def_aborts(AST_BlockStatement, block_aborts); |
|
def_aborts(AST_SwitchBranch, block_aborts); |
|
def_aborts(AST_DefClass, function () { |
|
for (const prop of this.properties) { |
|
if (prop instanceof AST_ClassStaticBlock) { |
|
if (prop.aborts()) return prop; |
|
} |
|
} |
|
return null; |
|
}); |
|
def_aborts(AST_ClassStaticBlock, block_aborts); |
|
def_aborts(AST_If, function() { |
|
return this.alternative && aborts(this.body) && aborts(this.alternative) && this; |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("aborts", func); |
|
}); |
|
|
|
AST_Node.DEFMETHOD("contains_this", function() { |
|
return walk(this, node => { |
|
if (node instanceof AST_This) return walk_abort; |
|
if ( |
|
node !== this |
|
&& node instanceof AST_Scope |
|
&& !(node instanceof AST_Arrow) |
|
) { |
|
return true; |
|
} |
|
}); |
|
}); |
|
|
|
function is_modified(compressor, tw, node, value, level, immutable) { |
|
var parent = tw.parent(level); |
|
var lhs = is_lhs(node, parent); |
|
if (lhs) return lhs; |
|
if (!immutable |
|
&& parent instanceof AST_Call |
|
&& parent.expression === node |
|
&& !(value instanceof AST_Arrow) |
|
&& !(value instanceof AST_Class) |
|
&& !parent.is_callee_pure(compressor) |
|
&& (!(value instanceof AST_Function) |
|
|| !(parent instanceof AST_New) && value.contains_this())) { |
|
return true; |
|
} |
|
if (parent instanceof AST_Array) { |
|
return is_modified(compressor, tw, parent, parent, level + 1); |
|
} |
|
if (parent instanceof AST_ObjectKeyVal && node === parent.value) { |
|
var obj = tw.parent(level + 1); |
|
return is_modified(compressor, tw, obj, obj, level + 2); |
|
} |
|
if (parent instanceof AST_PropAccess && parent.expression === node) { |
|
var prop = read_property(value, parent.property); |
|
return !immutable && is_modified(compressor, tw, parent, prop, level + 1); |
|
} |
|
} |
|
|
|
/** |
|
* Check if a node may be used by the expression it's in |
|
* void (0, 1, {node}, 2) -> false |
|
* console.log(0, {node}) -> true |
|
*/ |
|
function is_used_in_expression(tw) { |
|
for (let p = -1, node, parent; node = tw.parent(p), parent = tw.parent(p + 1); p++) { |
|
if (parent instanceof AST_Sequence) { |
|
const nth_expression = parent.expressions.indexOf(node); |
|
if (nth_expression !== parent.expressions.length - 1) { |
|
// Detect (0, x.noThis)() constructs |
|
const grandparent = tw.parent(p + 2); |
|
if ( |
|
parent.expressions.length > 2 |
|
|| parent.expressions.length === 1 |
|
|| !requires_sequence_to_maintain_binding(grandparent, parent, parent.expressions[1]) |
|
) { |
|
return false; |
|
} |
|
return true; |
|
} else { |
|
continue; |
|
} |
|
} |
|
if (parent instanceof AST_Unary) { |
|
const op = parent.operator; |
|
if (op === "void") { |
|
return false; |
|
} |
|
if (op === "typeof" || op === "+" || op === "-" || op === "!" || op === "~") { |
|
continue; |
|
} |
|
} |
|
if ( |
|
parent instanceof AST_SimpleStatement |
|
|| parent instanceof AST_LabeledStatement |
|
) { |
|
return false; |
|
} |
|
if (parent instanceof AST_Scope) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
// methods to evaluate a constant expression |
|
|
|
function def_eval(node, func) { |
|
node.DEFMETHOD("_eval", func); |
|
} |
|
|
|
// Used to propagate a nullish short-circuit signal upwards through the chain. |
|
const nullish = Symbol("This AST_Chain is nullish"); |
|
|
|
// If the node has been successfully reduced to a constant, |
|
// then its value is returned; otherwise the element itself |
|
// is returned. |
|
// They can be distinguished as constant value is never a |
|
// descendant of AST_Node. |
|
AST_Node.DEFMETHOD("evaluate", function (compressor) { |
|
if (!compressor.option("evaluate")) |
|
return this; |
|
var val = this._eval(compressor, 1); |
|
if (!val || val instanceof RegExp) |
|
return val; |
|
if (typeof val == "function" || typeof val == "object" || val == nullish) |
|
return this; |
|
|
|
// Evaluated strings can be larger than the original expression |
|
if (typeof val === "string") { |
|
const unevaluated_size = this.size(compressor); |
|
if (val.length + 2 > unevaluated_size) return this; |
|
} |
|
|
|
return val; |
|
}); |
|
|
|
var unaryPrefix = makePredicate("! ~ - + void"); |
|
AST_Node.DEFMETHOD("is_constant", function () { |
|
// Accomodate when compress option evaluate=false |
|
// as well as the common constant expressions !0 and -1 |
|
if (this instanceof AST_Constant) { |
|
return !(this instanceof AST_RegExp); |
|
} else { |
|
return this instanceof AST_UnaryPrefix |
|
&& unaryPrefix.has(this.operator) |
|
&& ( |
|
// `this.expression` may be an `AST_RegExp`, |
|
// so not only `.is_constant()`. |
|
this.expression instanceof AST_Constant |
|
|| this.expression.is_constant() |
|
); |
|
} |
|
}); |
|
|
|
def_eval(AST_Statement, function () { |
|
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); |
|
}); |
|
|
|
def_eval(AST_Lambda, return_this); |
|
def_eval(AST_Class, return_this); |
|
def_eval(AST_Node, return_this); |
|
def_eval(AST_Constant, function () { |
|
return this.getValue(); |
|
}); |
|
|
|
const supports_bigint = typeof BigInt === "function"; |
|
def_eval(AST_BigInt, function () { |
|
if (supports_bigint) { |
|
return BigInt(this.value); |
|
} else { |
|
return this; |
|
} |
|
}); |
|
|
|
def_eval(AST_RegExp, function (compressor) { |
|
let evaluated = compressor.evaluated_regexps.get(this.value); |
|
if (evaluated === undefined && regexp_is_safe(this.value.source)) { |
|
try { |
|
const { source, flags } = this.value; |
|
evaluated = new RegExp(source, flags); |
|
} catch (e) { |
|
evaluated = null; |
|
} |
|
compressor.evaluated_regexps.set(this.value, evaluated); |
|
} |
|
return evaluated || this; |
|
}); |
|
|
|
def_eval(AST_TemplateString, function () { |
|
if (this.segments.length !== 1) return this; |
|
return this.segments[0].value; |
|
}); |
|
|
|
def_eval(AST_Function, function (compressor) { |
|
if (compressor.option("unsafe")) { |
|
var fn = function () { }; |
|
fn.node = this; |
|
fn.toString = () => this.print_to_string(); |
|
return fn; |
|
} |
|
return this; |
|
}); |
|
|
|
def_eval(AST_Array, function (compressor, depth) { |
|
if (compressor.option("unsafe")) { |
|
var elements = []; |
|
for (var i = 0, len = this.elements.length; i < len; i++) { |
|
var element = this.elements[i]; |
|
var value = element._eval(compressor, depth); |
|
if (element === value) |
|
return this; |
|
elements.push(value); |
|
} |
|
return elements; |
|
} |
|
return this; |
|
}); |
|
|
|
def_eval(AST_Object, function (compressor, depth) { |
|
if (compressor.option("unsafe")) { |
|
var val = {}; |
|
for (var i = 0, len = this.properties.length; i < len; i++) { |
|
var prop = this.properties[i]; |
|
if (prop instanceof AST_Expansion) |
|
return this; |
|
var key = prop.key; |
|
if (key instanceof AST_Symbol) { |
|
key = key.name; |
|
} else if (key instanceof AST_Node) { |
|
key = key._eval(compressor, depth); |
|
if (key === prop.key) |
|
return this; |
|
} |
|
if (typeof Object.prototype[key] === "function") { |
|
return this; |
|
} |
|
if (prop.value instanceof AST_Function) |
|
continue; |
|
val[key] = prop.value._eval(compressor, depth); |
|
if (val[key] === prop.value) |
|
return this; |
|
} |
|
return val; |
|
} |
|
return this; |
|
}); |
|
|
|
var non_converting_unary = makePredicate("! typeof void"); |
|
def_eval(AST_UnaryPrefix, function (compressor, depth) { |
|
var e = this.expression; |
|
if (compressor.option("typeofs") |
|
&& this.operator == "typeof") { |
|
// Function would be evaluated to an array and so typeof would |
|
// incorrectly return 'object'. Hence making is a special case. |
|
if (e instanceof AST_Lambda |
|
|| e instanceof AST_SymbolRef |
|
&& e.fixed_value() instanceof AST_Lambda) { |
|
return typeof function () { }; |
|
} |
|
if ( |
|
(e instanceof AST_Object |
|
|| e instanceof AST_Array |
|
|| (e instanceof AST_SymbolRef |
|
&& (e.fixed_value() instanceof AST_Object |
|
|| e.fixed_value() instanceof AST_Array))) |
|
&& !e.has_side_effects(compressor) |
|
) { |
|
return typeof {}; |
|
} |
|
} |
|
if (!non_converting_unary.has(this.operator)) |
|
depth++; |
|
e = e._eval(compressor, depth); |
|
if (e === this.expression) |
|
return this; |
|
switch (this.operator) { |
|
case "!": return !e; |
|
case "typeof": |
|
// typeof <RegExp> returns "object" or "function" on different platforms |
|
// so cannot evaluate reliably |
|
if (e instanceof RegExp) |
|
return this; |
|
return typeof e; |
|
case "void": return void e; |
|
case "~": return ~e; |
|
case "-": return -e; |
|
case "+": return +e; |
|
} |
|
return this; |
|
}); |
|
|
|
var non_converting_binary = makePredicate("&& || ?? === !=="); |
|
const identity_comparison = makePredicate("== != === !=="); |
|
const has_identity = value => typeof value === "object" |
|
|| typeof value === "function" |
|
|| typeof value === "symbol"; |
|
|
|
def_eval(AST_Binary, function (compressor, depth) { |
|
if (!non_converting_binary.has(this.operator)) |
|
depth++; |
|
|
|
var left = this.left._eval(compressor, depth); |
|
if (left === this.left) |
|
return this; |
|
var right = this.right._eval(compressor, depth); |
|
if (right === this.right) |
|
return this; |
|
|
|
if (left != null |
|
&& right != null |
|
&& identity_comparison.has(this.operator) |
|
&& has_identity(left) |
|
&& has_identity(right) |
|
&& typeof left === typeof right) { |
|
// Do not compare by reference |
|
return this; |
|
} |
|
|
|
// Do not mix BigInt and Number; Don't use `>>>` on BigInt or `/ 0n` |
|
if ( |
|
(typeof left === "bigint") !== (typeof right === "bigint") |
|
|| typeof left === "bigint" |
|
&& (this.operator === ">>>" |
|
|| this.operator === "/" && Number(right) === 0) |
|
) { |
|
return this; |
|
} |
|
|
|
var result; |
|
switch (this.operator) { |
|
case "&&": result = left && right; break; |
|
case "||": result = left || right; break; |
|
case "??": result = left != null ? left : right; break; |
|
case "|": result = left | right; break; |
|
case "&": result = left & right; break; |
|
case "^": result = left ^ right; break; |
|
case "+": result = left + right; break; |
|
case "*": result = left * right; break; |
|
case "**": result = left ** right; break; |
|
case "/": result = left / right; break; |
|
case "%": result = left % right; break; |
|
case "-": result = left - right; break; |
|
case "<<": result = left << right; break; |
|
case ">>": result = left >> right; break; |
|
case ">>>": result = left >>> right; break; |
|
case "==": result = left == right; break; |
|
case "===": result = left === right; break; |
|
case "!=": result = left != right; break; |
|
case "!==": result = left !== right; break; |
|
case "<": result = left < right; break; |
|
case "<=": result = left <= right; break; |
|
case ">": result = left > right; break; |
|
case ">=": result = left >= right; break; |
|
default: |
|
return this; |
|
} |
|
if (typeof result === "number" && isNaN(result) && compressor.find_parent(AST_With)) { |
|
// leave original expression as is |
|
return this; |
|
} |
|
return result; |
|
}); |
|
|
|
def_eval(AST_Conditional, function (compressor, depth) { |
|
var condition = this.condition._eval(compressor, depth); |
|
if (condition === this.condition) |
|
return this; |
|
var node = condition ? this.consequent : this.alternative; |
|
var value = node._eval(compressor, depth); |
|
return value === node ? this : value; |
|
}); |
|
|
|
// Set of AST_SymbolRef which are currently being evaluated. |
|
// Avoids infinite recursion of ._eval() |
|
const reentrant_ref_eval = new Set(); |
|
def_eval(AST_SymbolRef, function (compressor, depth) { |
|
if (reentrant_ref_eval.has(this)) |
|
return this; |
|
|
|
var fixed = this.fixed_value(); |
|
if (!fixed) |
|
return this; |
|
|
|
reentrant_ref_eval.add(this); |
|
const value = fixed._eval(compressor, depth); |
|
reentrant_ref_eval.delete(this); |
|
|
|
if (value === fixed) |
|
return this; |
|
|
|
if (value && typeof value == "object") { |
|
var escaped = this.definition().escaped; |
|
if (escaped && depth > escaped) |
|
return this; |
|
} |
|
return value; |
|
}); |
|
|
|
def_eval(AST_Chain, function (compressor, depth) { |
|
const evaluated = this.expression._eval(compressor, depth, /*ast_chain=*/true); |
|
return evaluated === nullish |
|
? undefined |
|
: evaluated === this.expression |
|
? this |
|
: evaluated; |
|
}); |
|
|
|
const global_objs = { Array, Math, Number, Object, String }; |
|
|
|
const regexp_flags = new Set([ |
|
"dotAll", |
|
"global", |
|
"ignoreCase", |
|
"multiline", |
|
"sticky", |
|
"unicode", |
|
]); |
|
|
|
def_eval(AST_PropAccess, function (compressor, depth, ast_chain) { |
|
let obj = (ast_chain || this.property === "length" || compressor.option("unsafe")) |
|
&& this.expression._eval(compressor, depth + 1, ast_chain); |
|
|
|
if (ast_chain) { |
|
if (obj === nullish || (this.optional && obj == null)) return nullish; |
|
} |
|
|
|
// `.length` of strings and arrays is always safe |
|
if (this.property === "length") { |
|
if (typeof obj === "string") { |
|
return obj.length; |
|
} |
|
|
|
const is_spreadless_array = |
|
obj instanceof AST_Array |
|
&& obj.elements.every(el => !(el instanceof AST_Expansion)); |
|
|
|
if ( |
|
is_spreadless_array |
|
&& obj.elements.every(el => !el.has_side_effects(compressor)) |
|
) { |
|
return obj.elements.length; |
|
} |
|
} |
|
|
|
if (compressor.option("unsafe")) { |
|
var key = this.property; |
|
if (key instanceof AST_Node) { |
|
key = key._eval(compressor, depth); |
|
if (key === this.property) |
|
return this; |
|
} |
|
|
|
var exp = this.expression; |
|
if (is_undeclared_ref(exp)) { |
|
var aa; |
|
var first_arg = exp.name === "hasOwnProperty" |
|
&& key === "call" |
|
&& (aa = compressor.parent() && compressor.parent().args) |
|
&& (aa && aa[0] |
|
&& aa[0].evaluate(compressor)); |
|
|
|
first_arg = first_arg instanceof AST_Dot ? first_arg.expression : first_arg; |
|
|
|
if (first_arg == null || first_arg.thedef && first_arg.thedef.undeclared) { |
|
return this.clone(); |
|
} |
|
if (!compressor.is_pure_native_static_property(exp.name, key)) |
|
return this; |
|
obj = global_objs[exp.name]; |
|
} else { |
|
if (obj instanceof RegExp) { |
|
if (key == "source") { |
|
return regexp_source_fix(obj.source); |
|
} else if (key == "flags" || regexp_flags.has(key)) { |
|
return obj[key]; |
|
} |
|
} |
|
if (!obj || obj === exp || !HOP(obj, key)) |
|
return this; |
|
|
|
if (typeof obj == "function") |
|
switch (key) { |
|
case "name": |
|
return obj.node.name ? obj.node.name.name : ""; |
|
case "length": |
|
return obj.node.length_property(); |
|
default: |
|
return this; |
|
} |
|
} |
|
return obj[key]; |
|
} |
|
return this; |
|
}); |
|
|
|
def_eval(AST_Call, function (compressor, depth, ast_chain) { |
|
var exp = this.expression; |
|
|
|
if (ast_chain) { |
|
const callee = exp._eval(compressor, depth, ast_chain); |
|
if (callee === nullish || (this.optional && callee == null)) return nullish; |
|
} |
|
|
|
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { |
|
var key = exp.property; |
|
if (key instanceof AST_Node) { |
|
key = key._eval(compressor, depth); |
|
if (typeof key !== "string" && typeof key !== "number") |
|
return this; |
|
} |
|
var val; |
|
var e = exp.expression; |
|
if (is_undeclared_ref(e)) { |
|
var first_arg = e.name === "hasOwnProperty" && |
|
key === "call" && |
|
(this.args[0] && this.args[0].evaluate(compressor)); |
|
|
|
first_arg = first_arg instanceof AST_Dot ? first_arg.expression : first_arg; |
|
|
|
if ((first_arg == null || first_arg.thedef && first_arg.thedef.undeclared)) { |
|
return this.clone(); |
|
} |
|
if (!compressor.is_pure_native_static_fn(e.name, key)) return this; |
|
val = global_objs[e.name]; |
|
} else { |
|
val = e._eval(compressor, depth + 1, /* don't pass ast_chain (exponential work) */); |
|
|
|
if (val === e || !val) |
|
return this; |
|
if (!compressor.is_pure_native_method(val.constructor.name, key)) |
|
return this; |
|
} |
|
var args = []; |
|
for (var i = 0, len = this.args.length; i < len; i++) { |
|
var arg = this.args[i]; |
|
var value = arg._eval(compressor, depth); |
|
if (arg === value) |
|
return this; |
|
if (arg instanceof AST_Lambda) |
|
return this; |
|
args.push(value); |
|
} |
|
try { |
|
return val[key].apply(val, args); |
|
} catch (ex) { |
|
// We don't really care |
|
} |
|
} |
|
return this; |
|
}); |
|
|
|
// Also a subclass of AST_Call |
|
def_eval(AST_New, return_this); |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
// AST_Node#drop_side_effect_free() gets called when we don't care about the value, |
|
// only about side effects. We'll be defining this method for each node type in this module |
|
// |
|
// Examples: |
|
// foo++ -> foo++ |
|
// 1 + func() -> func() |
|
// 10 -> (nothing) |
|
// knownPureFunc(foo++) -> foo++ |
|
|
|
function def_drop_side_effect_free(node_or_nodes, func) { |
|
for (const node of [].concat(node_or_nodes)) { |
|
node.DEFMETHOD("drop_side_effect_free", func); |
|
} |
|
} |
|
|
|
// Drop side-effect-free elements from an array of expressions. |
|
// Returns an array of expressions with side-effects or null |
|
// if all elements were dropped. Note: original array may be |
|
// returned if nothing changed. |
|
function trim(nodes, compressor, first_in_statement) { |
|
var len = nodes.length; |
|
if (!len) return null; |
|
|
|
var ret = [], changed = false; |
|
for (var i = 0; i < len; i++) { |
|
var node = nodes[i].drop_side_effect_free(compressor, first_in_statement); |
|
changed |= node !== nodes[i]; |
|
if (node) { |
|
ret.push(node); |
|
first_in_statement = false; |
|
} |
|
} |
|
return changed ? ret.length ? ret : null : nodes; |
|
} |
|
|
|
def_drop_side_effect_free(AST_Node, return_this); |
|
def_drop_side_effect_free(AST_Constant, return_null); |
|
def_drop_side_effect_free(AST_This, return_null); |
|
|
|
def_drop_side_effect_free(AST_Call, function (compressor, first_in_statement) { |
|
if (is_nullish_shortcircuited(this, compressor)) { |
|
return this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
} |
|
|
|
if (!this.is_callee_pure(compressor)) { |
|
if (this.expression.is_call_pure(compressor)) { |
|
var exprs = this.args.slice(); |
|
exprs.unshift(this.expression.expression); |
|
exprs = trim(exprs, compressor, first_in_statement); |
|
return exprs && make_sequence(this, exprs); |
|
} |
|
if (is_func_expr(this.expression) |
|
&& (!this.expression.name || !this.expression.name.definition().references.length)) { |
|
var node = this.clone(); |
|
node.expression.process_expression(false, compressor); |
|
return node; |
|
} |
|
return this; |
|
} |
|
|
|
var args = trim(this.args, compressor, first_in_statement); |
|
return args && make_sequence(this, args); |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Accessor, return_null); |
|
|
|
def_drop_side_effect_free(AST_Function, return_null); |
|
|
|
def_drop_side_effect_free(AST_Arrow, return_null); |
|
|
|
def_drop_side_effect_free(AST_Class, function (compressor) { |
|
const with_effects = []; |
|
|
|
if (this.is_self_referential() && this.has_side_effects(compressor)) { |
|
return this; |
|
} |
|
|
|
const trimmed_extends = this.extends && this.extends.drop_side_effect_free(compressor); |
|
if (trimmed_extends) with_effects.push(trimmed_extends); |
|
|
|
for (const prop of this.properties) { |
|
if (prop instanceof AST_ClassStaticBlock) { |
|
if (prop.has_side_effects(compressor)) { |
|
return this; // Be cautious about these |
|
} |
|
} else { |
|
const trimmed_prop = prop.drop_side_effect_free(compressor); |
|
if (trimmed_prop) with_effects.push(trimmed_prop); |
|
} |
|
} |
|
|
|
if (!with_effects.length) |
|
return null; |
|
|
|
const exprs = make_sequence(this, with_effects); |
|
if (this instanceof AST_DefClass) { |
|
// We want a statement |
|
return make_node(AST_SimpleStatement, this, { body: exprs }); |
|
} else { |
|
return exprs; |
|
} |
|
}); |
|
|
|
def_drop_side_effect_free([ |
|
AST_ClassProperty, |
|
AST_ClassPrivateProperty, |
|
], function (compressor) { |
|
const key = this.computed_key() && this.key.drop_side_effect_free(compressor); |
|
|
|
const value = this.static && this.value |
|
&& this.value.drop_side_effect_free(compressor); |
|
|
|
if (key && value) |
|
return make_sequence(this, [key, value]); |
|
return key || value || null; |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Binary, function (compressor, first_in_statement) { |
|
var right = this.right.drop_side_effect_free(compressor); |
|
if (!right) |
|
return this.left.drop_side_effect_free(compressor, first_in_statement); |
|
if (lazy_op.has(this.operator)) { |
|
if (right === this.right) |
|
return this; |
|
var node = this.clone(); |
|
node.right = right; |
|
return node; |
|
} else { |
|
var left = this.left.drop_side_effect_free(compressor, first_in_statement); |
|
if (!left) |
|
return this.right.drop_side_effect_free(compressor, first_in_statement); |
|
return make_sequence(this, [left, right]); |
|
} |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Assign, function (compressor) { |
|
if (this.logical) |
|
return this; |
|
|
|
var left = this.left; |
|
if (left.has_side_effects(compressor) |
|
|| compressor.has_directive("use strict") |
|
&& left instanceof AST_PropAccess |
|
&& left.expression.is_constant()) { |
|
return this; |
|
} |
|
set_flag(this, WRITE_ONLY); |
|
while (left instanceof AST_PropAccess) { |
|
left = left.expression; |
|
} |
|
if (left.is_constant_expression(compressor.find_parent(AST_Scope))) { |
|
return this.right.drop_side_effect_free(compressor); |
|
} |
|
return this; |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Conditional, function (compressor) { |
|
var consequent = this.consequent.drop_side_effect_free(compressor); |
|
var alternative = this.alternative.drop_side_effect_free(compressor); |
|
if (consequent === this.consequent && alternative === this.alternative) |
|
return this; |
|
if (!consequent) |
|
return alternative ? make_node(AST_Binary, this, { |
|
operator: "||", |
|
left: this.condition, |
|
right: alternative |
|
}) : this.condition.drop_side_effect_free(compressor); |
|
if (!alternative) |
|
return make_node(AST_Binary, this, { |
|
operator: "&&", |
|
left: this.condition, |
|
right: consequent |
|
}); |
|
var node = this.clone(); |
|
node.consequent = consequent; |
|
node.alternative = alternative; |
|
return node; |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Unary, function (compressor, first_in_statement) { |
|
if (unary_side_effects.has(this.operator)) { |
|
if (!this.expression.has_side_effects(compressor)) { |
|
set_flag(this, WRITE_ONLY); |
|
} else { |
|
clear_flag(this, WRITE_ONLY); |
|
} |
|
return this; |
|
} |
|
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) |
|
return null; |
|
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
if (first_in_statement && expression && is_iife_call(expression)) { |
|
if (expression === this.expression && this.operator == "!") |
|
return this; |
|
return expression.negate(compressor, first_in_statement); |
|
} |
|
return expression; |
|
}); |
|
|
|
def_drop_side_effect_free(AST_SymbolRef, function (compressor) { |
|
const safe_access = this.is_declared(compressor) |
|
|| pure_prop_access_globals.has(this.name); |
|
return safe_access ? null : this; |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Object, function (compressor, first_in_statement) { |
|
var values = trim(this.properties, compressor, first_in_statement); |
|
return values && make_sequence(this, values); |
|
}); |
|
|
|
def_drop_side_effect_free(AST_ObjectKeyVal, function (compressor, first_in_statement) { |
|
const computed_key = this.key instanceof AST_Node; |
|
const key = computed_key && this.key.drop_side_effect_free(compressor, first_in_statement); |
|
const value = this.value.drop_side_effect_free(compressor, first_in_statement); |
|
if (key && value) { |
|
return make_sequence(this, [key, value]); |
|
} |
|
return key || value; |
|
}); |
|
|
|
def_drop_side_effect_free([ |
|
AST_ConciseMethod, |
|
AST_ObjectGetter, |
|
AST_ObjectSetter, |
|
], function (compressor, first_in_statement) { |
|
return this.computed_key() ? this.key.drop_side_effect_free(compressor, first_in_statement) : null; |
|
}); |
|
|
|
def_drop_side_effect_free([ |
|
AST_PrivateMethod, |
|
AST_PrivateGetter, |
|
AST_PrivateSetter, |
|
], function () { |
|
return null; |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Array, function (compressor, first_in_statement) { |
|
var values = trim(this.elements, compressor, first_in_statement); |
|
return values && make_sequence(this, values); |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Dot, function (compressor, first_in_statement) { |
|
if (is_nullish_shortcircuited(this, compressor)) { |
|
return this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
} |
|
if (!this.optional && this.expression.may_throw_on_access(compressor)) { |
|
return this; |
|
} |
|
|
|
return this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Sub, function (compressor, first_in_statement) { |
|
if (is_nullish_shortcircuited(this, compressor)) { |
|
return this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
} |
|
if (!this.optional && this.expression.may_throw_on_access(compressor)) { |
|
return this; |
|
} |
|
|
|
var property = this.property.drop_side_effect_free(compressor); |
|
if (property && this.optional) return this; |
|
|
|
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
|
|
if (expression && property) return make_sequence(this, [expression, property]); |
|
return expression || property; |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Chain, function (compressor, first_in_statement) { |
|
return this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Sequence, function (compressor) { |
|
var last = this.tail_node(); |
|
var expr = last.drop_side_effect_free(compressor); |
|
if (expr === last) |
|
return this; |
|
var expressions = this.expressions.slice(0, -1); |
|
if (expr) |
|
expressions.push(expr); |
|
if (!expressions.length) { |
|
return make_node(AST_Number, this, { value: 0 }); |
|
} |
|
return make_sequence(this, expressions); |
|
}); |
|
|
|
def_drop_side_effect_free(AST_Expansion, function (compressor, first_in_statement) { |
|
return this.expression.drop_side_effect_free(compressor, first_in_statement); |
|
}); |
|
|
|
def_drop_side_effect_free(AST_TemplateSegment, return_null); |
|
|
|
def_drop_side_effect_free(AST_TemplateString, function (compressor) { |
|
var values = trim(this.segments, compressor, first_in_statement); |
|
return values && make_sequence(this, values); |
|
}); |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
const r_keep_assign = /keep_assign/; |
|
|
|
/** Drop unused variables from this scope */ |
|
AST_Scope.DEFMETHOD("drop_unused", function(compressor) { |
|
if (!compressor.option("unused")) return; |
|
if (compressor.has_directive("use asm")) return; |
|
if (!this.variables) return; // not really a scope (eg: AST_Class) |
|
|
|
var self = this; |
|
if (self.pinned()) return; |
|
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs; |
|
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars; |
|
const assign_as_unused = r_keep_assign.test(compressor.option("unused")) ? return_false : function(node) { |
|
if (node instanceof AST_Assign |
|
&& !node.logical |
|
&& (has_flag(node, WRITE_ONLY) || node.operator == "=") |
|
) { |
|
return node.left; |
|
} |
|
if (node instanceof AST_Unary && has_flag(node, WRITE_ONLY)) { |
|
return node.expression; |
|
} |
|
}; |
|
var in_use_ids = new Map(); |
|
var fixed_ids = new Map(); |
|
if (self instanceof AST_Toplevel && compressor.top_retain) { |
|
self.variables.forEach(function(def) { |
|
if (compressor.top_retain(def)) { |
|
in_use_ids.set(def.id, def); |
|
} |
|
}); |
|
} |
|
var var_defs_by_id = new Map(); |
|
var initializations = new Map(); |
|
|
|
// pass 1: find out which symbols are directly used in |
|
// this scope (not in nested scopes). |
|
var scope = this; |
|
var tw = new TreeWalker(function(node, descend) { |
|
if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) { |
|
node.argnames.forEach(function(argname) { |
|
if (!(argname instanceof AST_SymbolDeclaration)) return; |
|
var def = argname.definition(); |
|
in_use_ids.set(def.id, def); |
|
}); |
|
} |
|
if (node === self) return; |
|
if (node instanceof AST_Class && node.has_side_effects(compressor)) { |
|
if (node.is_self_referential()) { |
|
descend(); |
|
} else { |
|
node.visit_nondeferred_class_parts(tw); |
|
} |
|
} |
|
if (node instanceof AST_Defun || node instanceof AST_DefClass) { |
|
var node_def = node.name.definition(); |
|
const in_export = tw.parent() instanceof AST_Export; |
|
if (in_export || !drop_funcs && scope === self) { |
|
if (node_def.global) { |
|
in_use_ids.set(node_def.id, node_def); |
|
} |
|
} |
|
|
|
map_add(initializations, node_def.id, node); |
|
return true; // don't go in nested scopes |
|
} |
|
// In the root scope, we drop things. In inner scopes, we just check for uses. |
|
const in_root_scope = scope === self; |
|
if (node instanceof AST_SymbolFunarg && in_root_scope) { |
|
map_add(var_defs_by_id, node.definition().id, node); |
|
} |
|
if (node instanceof AST_Definitions && in_root_scope) { |
|
const in_export = tw.parent() instanceof AST_Export; |
|
node.definitions.forEach(function(def) { |
|
if (def.name instanceof AST_SymbolVar) { |
|
map_add(var_defs_by_id, def.name.definition().id, def); |
|
} |
|
if (in_export || !drop_vars) { |
|
walk(def.name, node => { |
|
if (node instanceof AST_SymbolDeclaration) { |
|
const def = node.definition(); |
|
if (def.global) { |
|
in_use_ids.set(def.id, def); |
|
} |
|
} |
|
}); |
|
} |
|
if (def.name instanceof AST_Destructuring) { |
|
def.walk(tw); |
|
} |
|
if (def.name instanceof AST_SymbolDeclaration && def.value) { |
|
var node_def = def.name.definition(); |
|
map_add(initializations, node_def.id, def.value); |
|
if (!node_def.chained && def.name.fixed_value() === def.value) { |
|
fixed_ids.set(node_def.id, def); |
|
} |
|
if (def.value.has_side_effects(compressor)) { |
|
def.value.walk(tw); |
|
} |
|
} |
|
}); |
|
return true; |
|
} |
|
return scan_ref_scoped(node, descend); |
|
}); |
|
self.walk(tw); |
|
|
|
// pass 2: for every used symbol we need to walk its |
|
// initialization code to figure out if it uses other |
|
// symbols (that may not be in_use). |
|
tw = new TreeWalker(scan_ref_scoped); |
|
in_use_ids.forEach(function (def) { |
|
var init = initializations.get(def.id); |
|
if (init) init.forEach(function(init) { |
|
init.walk(tw); |
|
}); |
|
}); |
|
|
|
// pass 3: we should drop declarations not in_use |
|
var tt = new TreeTransformer( |
|
function before(node, descend, in_list) { |
|
var parent = tt.parent(); |
|
if (drop_vars) { |
|
const sym = assign_as_unused(node); |
|
if (sym instanceof AST_SymbolRef) { |
|
var def = sym.definition(); |
|
var in_use = in_use_ids.has(def.id); |
|
if (node instanceof AST_Assign) { |
|
if (!in_use || fixed_ids.has(def.id) && fixed_ids.get(def.id) !== node) { |
|
const assignee = node.right.transform(tt); |
|
if (!in_use && !assignee.has_side_effects(compressor) && !is_used_in_expression(tt)) { |
|
return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 }); |
|
} |
|
return maintain_this_binding(parent, node, assignee); |
|
} |
|
} else if (!in_use) { |
|
return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 }); |
|
} |
|
} |
|
} |
|
if (scope !== self) return; |
|
var def; |
|
if (node.name |
|
&& (node instanceof AST_ClassExpression |
|
&& !keep_name(compressor.option("keep_classnames"), (def = node.name.definition()).name) |
|
|| node instanceof AST_Function |
|
&& !keep_name(compressor.option("keep_fnames"), (def = node.name.definition()).name))) { |
|
// any declarations with same name will overshadow |
|
// name of this anonymous function and can therefore |
|
// never be used anywhere |
|
if (!in_use_ids.has(def.id) || def.orig.length > 1) node.name = null; |
|
} |
|
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { |
|
var trim = |
|
!compressor.option("keep_fargs") |
|
// Is this an IIFE that won't refer to its name? |
|
|| parent instanceof AST_Call |
|
&& parent.expression === node |
|
&& !node.pinned() |
|
&& (!node.name || node.name.unreferenced()); |
|
for (var a = node.argnames, i = a.length; --i >= 0;) { |
|
var sym = a[i]; |
|
if (sym instanceof AST_Expansion) { |
|
sym = sym.expression; |
|
} |
|
if (sym instanceof AST_DefaultAssign) { |
|
sym = sym.left; |
|
} |
|
// Do not drop destructuring arguments. |
|
// They constitute a type assertion of sorts |
|
if ( |
|
!(sym instanceof AST_Destructuring) |
|
&& !in_use_ids.has(sym.definition().id) |
|
) { |
|
set_flag(sym, UNUSED); |
|
if (trim) { |
|
a.pop(); |
|
} |
|
} else { |
|
trim = false; |
|
} |
|
} |
|
} |
|
if (node instanceof AST_DefClass && node !== self) { |
|
const def = node.name.definition(); |
|
descend(node, this); |
|
const keep_class = def.global && !drop_funcs || in_use_ids.has(def.id); |
|
if (!keep_class) { |
|
const kept = node.drop_side_effect_free(compressor); |
|
if (kept == null) { |
|
def.eliminated++; |
|
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); |
|
} |
|
return kept; |
|
} |
|
return node; |
|
} |
|
if (node instanceof AST_Defun && node !== self) { |
|
const def = node.name.definition(); |
|
const keep = def.global && !drop_funcs || in_use_ids.has(def.id); |
|
if (!keep) { |
|
def.eliminated++; |
|
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); |
|
} |
|
} |
|
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) { |
|
var drop_block = !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var); |
|
// place uninitialized names at the start |
|
var body = [], head = [], tail = []; |
|
// for unused names whose initialization has |
|
// side effects, we can cascade the init. code |
|
// into the next one, or next statement. |
|
var side_effects = []; |
|
node.definitions.forEach(function(def) { |
|
if (def.value) def.value = def.value.transform(tt); |
|
var is_destructure = def.name instanceof AST_Destructuring; |
|
var sym = is_destructure |
|
? new SymbolDef(null, { name: "<destructure>" }) /* fake SymbolDef */ |
|
: def.name.definition(); |
|
if (drop_block && sym.global) return tail.push(def); |
|
if (!(drop_vars || drop_block) |
|
|| is_destructure |
|
&& (def.name.names.length |
|
|| def.name.is_array |
|
|| compressor.option("pure_getters") != true) |
|
|| in_use_ids.has(sym.id) |
|
) { |
|
if (def.value && fixed_ids.has(sym.id) && fixed_ids.get(sym.id) !== def) { |
|
def.value = def.value.drop_side_effect_free(compressor); |
|
} |
|
if (def.name instanceof AST_SymbolVar) { |
|
var var_defs = var_defs_by_id.get(sym.id); |
|
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) { |
|
if (def.value) { |
|
var ref = make_node(AST_SymbolRef, def.name, def.name); |
|
sym.references.push(ref); |
|
var assign = make_node(AST_Assign, def, { |
|
operator: "=", |
|
logical: false, |
|
left: ref, |
|
right: def.value |
|
}); |
|
if (fixed_ids.get(sym.id) === def) { |
|
fixed_ids.set(sym.id, assign); |
|
} |
|
side_effects.push(assign.transform(tt)); |
|
} |
|
remove(var_defs, def); |
|
sym.eliminated++; |
|
return; |
|
} |
|
} |
|
if (def.value) { |
|
if (side_effects.length > 0) { |
|
if (tail.length > 0) { |
|
side_effects.push(def.value); |
|
def.value = make_sequence(def.value, side_effects); |
|
} else { |
|
body.push(make_node(AST_SimpleStatement, node, { |
|
body: make_sequence(node, side_effects) |
|
})); |
|
} |
|
side_effects = []; |
|
} |
|
tail.push(def); |
|
} else { |
|
head.push(def); |
|
} |
|
} else if (sym.orig[0] instanceof AST_SymbolCatch) { |
|
var value = def.value && def.value.drop_side_effect_free(compressor); |
|
if (value) side_effects.push(value); |
|
def.value = null; |
|
head.push(def); |
|
} else { |
|
var value = def.value && def.value.drop_side_effect_free(compressor); |
|
if (value) { |
|
side_effects.push(value); |
|
} |
|
sym.eliminated++; |
|
} |
|
}); |
|
if (head.length > 0 || tail.length > 0) { |
|
node.definitions = head.concat(tail); |
|
body.push(node); |
|
} |
|
if (side_effects.length > 0) { |
|
body.push(make_node(AST_SimpleStatement, node, { |
|
body: make_sequence(node, side_effects) |
|
})); |
|
} |
|
switch (body.length) { |
|
case 0: |
|
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); |
|
case 1: |
|
return body[0]; |
|
default: |
|
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { body }); |
|
} |
|
} |
|
// certain combination of unused name + side effect leads to: |
|
// https://github.com/mishoo/UglifyJS2/issues/44 |
|
// https://github.com/mishoo/UglifyJS2/issues/1830 |
|
// https://github.com/mishoo/UglifyJS2/issues/1838 |
|
// that's an invalid AST. |
|
// We fix it at this stage by moving the `var` outside the `for`. |
|
if (node instanceof AST_For) { |
|
descend(node, this); |
|
var block; |
|
if (node.init instanceof AST_BlockStatement) { |
|
block = node.init; |
|
node.init = block.body.pop(); |
|
block.body.push(node); |
|
} |
|
if (node.init instanceof AST_SimpleStatement) { |
|
node.init = node.init.body; |
|
} else if (is_empty(node.init)) { |
|
node.init = null; |
|
} |
|
return !block ? node : in_list ? MAP.splice(block.body) : block; |
|
} |
|
if (node instanceof AST_LabeledStatement |
|
&& node.body instanceof AST_For |
|
) { |
|
descend(node, this); |
|
if (node.body instanceof AST_BlockStatement) { |
|
var block = node.body; |
|
node.body = block.body.pop(); |
|
block.body.push(node); |
|
return in_list ? MAP.splice(block.body) : block; |
|
} |
|
return node; |
|
} |
|
if (node instanceof AST_BlockStatement) { |
|
descend(node, this); |
|
if (in_list && node.body.every(can_be_evicted_from_block)) { |
|
return MAP.splice(node.body); |
|
} |
|
return node; |
|
} |
|
if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) { |
|
const save_scope = scope; |
|
scope = node; |
|
descend(node, this); |
|
scope = save_scope; |
|
return node; |
|
} |
|
}, |
|
function after(node, in_list) { |
|
if (node instanceof AST_Sequence) { |
|
switch (node.expressions.length) { |
|
case 0: return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 }); |
|
case 1: return node.expressions[0]; |
|
} |
|
} |
|
} |
|
); |
|
|
|
self.transform(tt); |
|
|
|
function scan_ref_scoped(node, descend) { |
|
var node_def; |
|
const sym = assign_as_unused(node); |
|
if (sym instanceof AST_SymbolRef |
|
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration) |
|
&& self.variables.get(sym.name) === (node_def = sym.definition()) |
|
) { |
|
if (node instanceof AST_Assign) { |
|
node.right.walk(tw); |
|
if (!node_def.chained && node.left.fixed_value() === node.right) { |
|
fixed_ids.set(node_def.id, node); |
|
} |
|
} |
|
return true; |
|
} |
|
if (node instanceof AST_SymbolRef) { |
|
node_def = node.definition(); |
|
if (!in_use_ids.has(node_def.id)) { |
|
in_use_ids.set(node_def.id, node_def); |
|
if (node_def.orig[0] instanceof AST_SymbolCatch) { |
|
const redef = node_def.scope.is_block_scope() |
|
&& node_def.scope.get_defun_scope().variables.get(node_def.name); |
|
if (redef) in_use_ids.set(redef.id, redef); |
|
} |
|
} |
|
return true; |
|
} |
|
if (node instanceof AST_Class) { |
|
descend(); |
|
return true; |
|
} |
|
if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) { |
|
var save_scope = scope; |
|
scope = node; |
|
descend(); |
|
scope = save_scope; |
|
return true; |
|
} |
|
} |
|
}); |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
/** |
|
* Define the method AST_Node#reduce_vars, which goes through the AST in |
|
* execution order to perform basic flow analysis |
|
*/ |
|
function def_reduce_vars(node, func) { |
|
node.DEFMETHOD("reduce_vars", func); |
|
} |
|
|
|
def_reduce_vars(AST_Node, noop); |
|
|
|
/** Clear definition properties */ |
|
function reset_def(compressor, def) { |
|
def.assignments = 0; |
|
def.chained = false; |
|
def.direct_access = false; |
|
def.escaped = 0; |
|
def.recursive_refs = 0; |
|
def.references = []; |
|
def.single_use = undefined; |
|
if ( |
|
def.scope.pinned() |
|
|| (def.orig[0] instanceof AST_SymbolFunarg && def.scope.uses_arguments) |
|
) { |
|
def.fixed = false; |
|
} else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) { |
|
def.fixed = def.init; |
|
} else { |
|
def.fixed = false; |
|
} |
|
} |
|
|
|
function reset_variables(tw, compressor, node) { |
|
node.variables.forEach(function(def) { |
|
reset_def(compressor, def); |
|
if (def.fixed === null) { |
|
tw.defs_to_safe_ids.set(def.id, tw.safe_ids); |
|
mark(tw, def, true); |
|
} else if (def.fixed) { |
|
tw.loop_ids.set(def.id, tw.in_loop); |
|
mark(tw, def, true); |
|
} |
|
}); |
|
} |
|
|
|
function reset_block_variables(compressor, node) { |
|
if (node.block_scope) node.block_scope.variables.forEach((def) => { |
|
reset_def(compressor, def); |
|
}); |
|
} |
|
|
|
function push(tw) { |
|
tw.safe_ids = Object.create(tw.safe_ids); |
|
} |
|
|
|
function pop(tw) { |
|
tw.safe_ids = Object.getPrototypeOf(tw.safe_ids); |
|
} |
|
|
|
function mark(tw, def, safe) { |
|
tw.safe_ids[def.id] = safe; |
|
} |
|
|
|
function safe_to_read(tw, def) { |
|
if (def.single_use == "m") return false; |
|
if (tw.safe_ids[def.id]) { |
|
if (def.fixed == null) { |
|
var orig = def.orig[0]; |
|
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false; |
|
def.fixed = make_void_0(orig); |
|
} |
|
return true; |
|
} |
|
return def.fixed instanceof AST_Defun; |
|
} |
|
|
|
function safe_to_assign(tw, def, scope, value) { |
|
if (def.fixed === undefined) return true; |
|
let def_safe_ids; |
|
if (def.fixed === null |
|
&& (def_safe_ids = tw.defs_to_safe_ids.get(def.id)) |
|
) { |
|
def_safe_ids[def.id] = false; |
|
tw.defs_to_safe_ids.delete(def.id); |
|
return true; |
|
} |
|
if (!HOP(tw.safe_ids, def.id)) return false; |
|
if (!safe_to_read(tw, def)) return false; |
|
if (def.fixed === false) return false; |
|
if (def.fixed != null && (!value || def.references.length > def.assignments)) return false; |
|
if (def.fixed instanceof AST_Defun) { |
|
return value instanceof AST_Node && def.fixed.parent_scope === scope; |
|
} |
|
return def.orig.every((sym) => { |
|
return !(sym instanceof AST_SymbolConst |
|
|| sym instanceof AST_SymbolDefun |
|
|| sym instanceof AST_SymbolLambda); |
|
}); |
|
} |
|
|
|
function ref_once(tw, compressor, def) { |
|
return compressor.option("unused") |
|
&& !def.scope.pinned() |
|
&& def.references.length - def.recursive_refs == 1 |
|
&& tw.loop_ids.get(def.id) === tw.in_loop; |
|
} |
|
|
|
function is_immutable(value) { |
|
if (!value) return false; |
|
return value.is_constant() |
|
|| value instanceof AST_Lambda |
|
|| value instanceof AST_This; |
|
} |
|
|
|
// A definition "escapes" when its value can leave the point of use. |
|
// Example: `a = b || c` |
|
// In this example, "b" and "c" are escaping, because they're going into "a" |
|
// |
|
// def.escaped is != 0 when it escapes. |
|
// |
|
// When greater than 1, it means that N chained properties will be read off |
|
// of that def before an escape occurs. This is useful for evaluating |
|
// property accesses, where you need to know when to stop. |
|
function mark_escaped(tw, d, scope, node, value, level = 0, depth = 1) { |
|
var parent = tw.parent(level); |
|
if (value) { |
|
if (value.is_constant()) return; |
|
if (value instanceof AST_ClassExpression) return; |
|
} |
|
|
|
if ( |
|
parent instanceof AST_Assign && (parent.operator === "=" || parent.logical) && node === parent.right |
|
|| parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New) |
|
|| parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope |
|
|| parent instanceof AST_VarDefLike && node === parent.value |
|
|| parent instanceof AST_Yield && node === parent.value && node.scope !== d.scope |
|
) { |
|
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1; |
|
if (!d.escaped || d.escaped > depth) d.escaped = depth; |
|
return; |
|
} else if ( |
|
parent instanceof AST_Array |
|
|| parent instanceof AST_Await |
|
|| parent instanceof AST_Binary && lazy_op.has(parent.operator) |
|
|| parent instanceof AST_Conditional && node !== parent.condition |
|
|| parent instanceof AST_Expansion |
|
|| parent instanceof AST_Sequence && node === parent.tail_node() |
|
) { |
|
mark_escaped(tw, d, scope, parent, parent, level + 1, depth); |
|
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) { |
|
var obj = tw.parent(level + 1); |
|
|
|
mark_escaped(tw, d, scope, obj, obj, level + 2, depth); |
|
} else if (parent instanceof AST_PropAccess && node === parent.expression) { |
|
value = read_property(value, parent.property); |
|
|
|
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1); |
|
if (value) return; |
|
} |
|
|
|
if (level > 0) return; |
|
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return; |
|
if (parent instanceof AST_SimpleStatement) return; |
|
|
|
d.direct_access = true; |
|
} |
|
|
|
const suppress = node => walk(node, node => { |
|
if (!(node instanceof AST_Symbol)) return; |
|
var d = node.definition(); |
|
if (!d) return; |
|
if (node instanceof AST_SymbolRef) d.references.push(node); |
|
d.fixed = false; |
|
}); |
|
|
|
def_reduce_vars(AST_Accessor, function(tw, descend, compressor) { |
|
push(tw); |
|
reset_variables(tw, compressor, this); |
|
descend(); |
|
pop(tw); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Assign, function(tw, descend, compressor) { |
|
var node = this; |
|
if (node.left instanceof AST_Destructuring) { |
|
suppress(node.left); |
|
return; |
|
} |
|
|
|
const finish_walk = () => { |
|
if (node.logical) { |
|
node.left.walk(tw); |
|
|
|
push(tw); |
|
node.right.walk(tw); |
|
pop(tw); |
|
|
|
return true; |
|
} |
|
}; |
|
|
|
var sym = node.left; |
|
if (!(sym instanceof AST_SymbolRef)) return finish_walk(); |
|
|
|
var def = sym.definition(); |
|
var safe = safe_to_assign(tw, def, sym.scope, node.right); |
|
def.assignments++; |
|
if (!safe) return finish_walk(); |
|
|
|
var fixed = def.fixed; |
|
if (!fixed && node.operator != "=" && !node.logical) return finish_walk(); |
|
|
|
var eq = node.operator == "="; |
|
var value = eq ? node.right : node; |
|
if (is_modified(compressor, tw, node, value, 0)) return finish_walk(); |
|
|
|
def.references.push(sym); |
|
|
|
if (!node.logical) { |
|
if (!eq) def.chained = true; |
|
|
|
def.fixed = eq ? function() { |
|
return node.right; |
|
} : function() { |
|
return make_node(AST_Binary, node, { |
|
operator: node.operator.slice(0, -1), |
|
left: fixed instanceof AST_Node ? fixed : fixed(), |
|
right: node.right |
|
}); |
|
}; |
|
} |
|
|
|
if (node.logical) { |
|
mark(tw, def, false); |
|
push(tw); |
|
node.right.walk(tw); |
|
pop(tw); |
|
return true; |
|
} |
|
|
|
mark(tw, def, false); |
|
node.right.walk(tw); |
|
mark(tw, def, true); |
|
|
|
mark_escaped(tw, def, sym.scope, node, value, 0, 1); |
|
|
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Binary, function(tw) { |
|
if (!lazy_op.has(this.operator)) return; |
|
this.left.walk(tw); |
|
push(tw); |
|
this.right.walk(tw); |
|
pop(tw); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Block, function(tw, descend, compressor) { |
|
reset_block_variables(compressor, this); |
|
}); |
|
|
|
def_reduce_vars(AST_Case, function(tw) { |
|
push(tw); |
|
this.expression.walk(tw); |
|
pop(tw); |
|
push(tw); |
|
walk_body(this, tw); |
|
pop(tw); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Class, function(tw, descend) { |
|
clear_flag(this, INLINED); |
|
push(tw); |
|
descend(); |
|
pop(tw); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_ClassStaticBlock, function(tw, descend, compressor) { |
|
reset_block_variables(compressor, this); |
|
}); |
|
|
|
def_reduce_vars(AST_Conditional, function(tw) { |
|
this.condition.walk(tw); |
|
push(tw); |
|
this.consequent.walk(tw); |
|
pop(tw); |
|
push(tw); |
|
this.alternative.walk(tw); |
|
pop(tw); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Chain, function(tw, descend) { |
|
// Chains' conditions apply left-to-right, cumulatively. |
|
// If we walk normally we don't go in that order because we would pop before pushing again |
|
// Solution: AST_PropAccess and AST_Call push when they are optional, and never pop. |
|
// Then we pop everything when they are done being walked. |
|
const safe_ids = tw.safe_ids; |
|
|
|
descend(); |
|
|
|
// Unroll back to start |
|
tw.safe_ids = safe_ids; |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Call, function (tw) { |
|
this.expression.walk(tw); |
|
|
|
if (this.optional) { |
|
// Never pop -- it's popped at AST_Chain above |
|
push(tw); |
|
} |
|
|
|
for (const arg of this.args) arg.walk(tw); |
|
|
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_PropAccess, function (tw) { |
|
if (!this.optional) return; |
|
|
|
this.expression.walk(tw); |
|
|
|
// Never pop -- it's popped at AST_Chain above |
|
push(tw); |
|
|
|
if (this.property instanceof AST_Node) this.property.walk(tw); |
|
|
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Default, function(tw, descend) { |
|
push(tw); |
|
descend(); |
|
pop(tw); |
|
return true; |
|
}); |
|
|
|
function mark_lambda(tw, descend, compressor) { |
|
clear_flag(this, INLINED); |
|
push(tw); |
|
reset_variables(tw, compressor, this); |
|
|
|
var iife; |
|
if (!this.name |
|
&& !this.uses_arguments |
|
&& !this.pinned() |
|
&& (iife = tw.parent()) instanceof AST_Call |
|
&& iife.expression === this |
|
&& !iife.args.some(arg => arg instanceof AST_Expansion) |
|
&& this.argnames.every(arg_name => arg_name instanceof AST_Symbol) |
|
) { |
|
// Virtually turn IIFE parameters into variable definitions: |
|
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() |
|
// So existing transformation rules can work on them. |
|
this.argnames.forEach((arg, i) => { |
|
if (!arg.definition) return; |
|
var d = arg.definition(); |
|
// Avoid setting fixed when there's more than one origin for a variable value |
|
if (d.orig.length > 1) return; |
|
if (d.fixed === undefined && (!this.uses_arguments || tw.has_directive("use strict"))) { |
|
d.fixed = function() { |
|
return iife.args[i] || make_void_0(iife); |
|
}; |
|
tw.loop_ids.set(d.id, tw.in_loop); |
|
mark(tw, d, true); |
|
} else { |
|
d.fixed = false; |
|
} |
|
}); |
|
} |
|
|
|
descend(); |
|
pop(tw); |
|
|
|
handle_defined_after_hoist(this); |
|
|
|
return true; |
|
} |
|
|
|
/** |
|
* It's possible for a hoisted function to use something that's not defined yet. Example: |
|
* |
|
* hoisted(); |
|
* var defined_after = true; |
|
* function hoisted() { |
|
* // use defined_after |
|
* } |
|
* |
|
* Or even indirectly: |
|
* |
|
* B(); |
|
* var defined_after = true; |
|
* function A() { |
|
* // use defined_after |
|
* } |
|
* function B() { |
|
* A(); |
|
* } |
|
* |
|
* Access a variable before declaration will either throw a ReferenceError |
|
* (if the variable is declared with `let` or `const`), |
|
* or get an `undefined` (if the variable is declared with `var`). |
|
* |
|
* If the variable is inlined into the function, the behavior will change. |
|
* |
|
* This function is called on the parent to disallow inlining of such variables, |
|
*/ |
|
function handle_defined_after_hoist(parent) { |
|
const defuns = []; |
|
walk(parent, node => { |
|
if (node === parent) return; |
|
if (node instanceof AST_Defun) { |
|
defuns.push(node); |
|
return true; |
|
} |
|
if ( |
|
node instanceof AST_Scope |
|
|| node instanceof AST_SimpleStatement |
|
) return true; |
|
}); |
|
|
|
// `defun` id to array of `defun` it uses |
|
const defun_dependencies_map = new Map(); |
|
// `defun` id to array of enclosing `def` that are used by the function |
|
const dependencies_map = new Map(); |
|
// all symbol ids that will be tracked for read/write |
|
const symbols_of_interest = new Set(); |
|
const defuns_of_interest = new Set(); |
|
|
|
for (const defun of defuns) { |
|
const fname_def = defun.name.definition(); |
|
const enclosing_defs = []; |
|
|
|
for (const def of defun.enclosed) { |
|
if ( |
|
def.fixed === false |
|
|| def === fname_def |
|
|| def.scope.get_defun_scope() !== parent |
|
) { |
|
continue; |
|
} |
|
|
|
symbols_of_interest.add(def.id); |
|
|
|
// found a reference to another function |
|
if ( |
|
def.assignments === 0 |
|
&& def.orig.length === 1 |
|
&& def.orig[0] instanceof AST_SymbolDefun |
|
) { |
|
defuns_of_interest.add(def.id); |
|
symbols_of_interest.add(def.id); |
|
|
|
defuns_of_interest.add(fname_def.id); |
|
symbols_of_interest.add(fname_def.id); |
|
|
|
if (!defun_dependencies_map.has(fname_def.id)) { |
|
defun_dependencies_map.set(fname_def.id, []); |
|
} |
|
defun_dependencies_map.get(fname_def.id).push(def.id); |
|
|
|
continue; |
|
} |
|
|
|
enclosing_defs.push(def); |
|
} |
|
|
|
if (enclosing_defs.length) { |
|
dependencies_map.set(fname_def.id, enclosing_defs); |
|
defuns_of_interest.add(fname_def.id); |
|
symbols_of_interest.add(fname_def.id); |
|
} |
|
} |
|
|
|
// No defuns use outside constants |
|
if (!dependencies_map.size) { |
|
return; |
|
} |
|
|
|
// Increment to count "symbols of interest" (defuns or defs) that we found. |
|
// These are tracked in AST order so we can check which is after which. |
|
let symbol_index = 1; |
|
// Map a defun ID to its first read (a `symbol_index`) |
|
const defun_first_read_map = new Map(); |
|
// Map a symbol ID to its last write (a `symbol_index`) |
|
const symbol_last_write_map = new Map(); |
|
|
|
walk_parent(parent, (node, walk_info) => { |
|
if (node instanceof AST_Symbol && node.thedef) { |
|
const id = node.definition().id; |
|
|
|
symbol_index++; |
|
|
|
// Track last-writes to symbols |
|
if (symbols_of_interest.has(id)) { |
|
if (node instanceof AST_SymbolDeclaration || is_lhs(node, walk_info.parent())) { |
|
symbol_last_write_map.set(id, symbol_index); |
|
} |
|
} |
|
|
|
// Track first-reads of defuns (refined later) |
|
if (defuns_of_interest.has(id)) { |
|
if (!defun_first_read_map.has(id) && !is_recursive_ref(walk_info, id)) { |
|
defun_first_read_map.set(id, symbol_index); |
|
} |
|
} |
|
} |
|
}); |
|
|
|
// Refine `defun_first_read_map` to be as high as possible |
|
for (const [defun, defun_first_read] of defun_first_read_map) { |
|
// Update all dependencies of `defun` |
|
const queue = new Set(defun_dependencies_map.get(defun)); |
|
for (const enclosed_defun of queue) { |
|
let enclosed_defun_first_read = defun_first_read_map.get(enclosed_defun); |
|
if (enclosed_defun_first_read != null && enclosed_defun_first_read < defun_first_read) { |
|
continue; |
|
} |
|
|
|
defun_first_read_map.set(enclosed_defun, defun_first_read); |
|
|
|
for (const enclosed_enclosed_defun of defun_dependencies_map.get(enclosed_defun) || []) { |
|
queue.add(enclosed_enclosed_defun); |
|
} |
|
} |
|
} |
|
|
|
// ensure write-then-read order, otherwise clear `fixed` |
|
// This is safe because last-writes (found_symbol_writes) are assumed to be as late as possible, and first-reads (defun_first_read_map) are assumed to be as early as possible. |
|
for (const [defun, defs] of dependencies_map) { |
|
const defun_first_read = defun_first_read_map.get(defun); |
|
if (defun_first_read === undefined) { |
|
continue; |
|
} |
|
|
|
for (const def of defs) { |
|
if (def.fixed === false) { |
|
continue; |
|
} |
|
|
|
let def_last_write = symbol_last_write_map.get(def.id) || 0; |
|
|
|
if (defun_first_read < def_last_write) { |
|
def.fixed = false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
def_reduce_vars(AST_Lambda, mark_lambda); |
|
|
|
def_reduce_vars(AST_Do, function(tw, descend, compressor) { |
|
reset_block_variables(compressor, this); |
|
const saved_loop = tw.in_loop; |
|
tw.in_loop = this; |
|
push(tw); |
|
this.body.walk(tw); |
|
if (has_break_or_continue(this)) { |
|
pop(tw); |
|
push(tw); |
|
} |
|
this.condition.walk(tw); |
|
pop(tw); |
|
tw.in_loop = saved_loop; |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_For, function(tw, descend, compressor) { |
|
reset_block_variables(compressor, this); |
|
if (this.init) this.init.walk(tw); |
|
const saved_loop = tw.in_loop; |
|
tw.in_loop = this; |
|
push(tw); |
|
if (this.condition) this.condition.walk(tw); |
|
this.body.walk(tw); |
|
if (this.step) { |
|
if (has_break_or_continue(this)) { |
|
pop(tw); |
|
push(tw); |
|
} |
|
this.step.walk(tw); |
|
} |
|
pop(tw); |
|
tw.in_loop = saved_loop; |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_ForIn, function(tw, descend, compressor) { |
|
reset_block_variables(compressor, this); |
|
suppress(this.init); |
|
this.object.walk(tw); |
|
const saved_loop = tw.in_loop; |
|
tw.in_loop = this; |
|
push(tw); |
|
this.body.walk(tw); |
|
pop(tw); |
|
tw.in_loop = saved_loop; |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_If, function(tw) { |
|
this.condition.walk(tw); |
|
push(tw); |
|
this.body.walk(tw); |
|
pop(tw); |
|
if (this.alternative) { |
|
push(tw); |
|
this.alternative.walk(tw); |
|
pop(tw); |
|
} |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_LabeledStatement, function(tw) { |
|
push(tw); |
|
this.body.walk(tw); |
|
pop(tw); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_SymbolCatch, function() { |
|
this.definition().fixed = false; |
|
}); |
|
|
|
def_reduce_vars(AST_SymbolRef, function(tw, descend, compressor) { |
|
var d = this.definition(); |
|
d.references.push(this); |
|
if (d.references.length == 1 |
|
&& !d.fixed |
|
&& d.orig[0] instanceof AST_SymbolDefun) { |
|
tw.loop_ids.set(d.id, tw.in_loop); |
|
} |
|
var fixed_value; |
|
if (d.fixed === undefined || !safe_to_read(tw, d)) { |
|
d.fixed = false; |
|
} else if (d.fixed) { |
|
fixed_value = this.fixed_value(); |
|
if ( |
|
fixed_value instanceof AST_Lambda |
|
&& is_recursive_ref(tw, d) |
|
) { |
|
d.recursive_refs++; |
|
} else if (fixed_value |
|
&& !compressor.exposed(d) |
|
&& ref_once(tw, compressor, d) |
|
) { |
|
d.single_use = |
|
fixed_value instanceof AST_Lambda && !fixed_value.pinned() |
|
|| fixed_value instanceof AST_Class |
|
|| d.scope === this.scope && fixed_value.is_constant_expression(); |
|
} else { |
|
d.single_use = false; |
|
} |
|
if (is_modified(compressor, tw, this, fixed_value, 0, is_immutable(fixed_value))) { |
|
if (d.single_use) { |
|
d.single_use = "m"; |
|
} else { |
|
d.fixed = false; |
|
} |
|
} |
|
} |
|
mark_escaped(tw, d, this.scope, this, fixed_value, 0, 1); |
|
}); |
|
|
|
def_reduce_vars(AST_Toplevel, function(tw, descend, compressor) { |
|
this.globals.forEach(function(def) { |
|
reset_def(compressor, def); |
|
}); |
|
reset_variables(tw, compressor, this); |
|
descend(); |
|
handle_defined_after_hoist(this); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Try, function(tw, descend, compressor) { |
|
reset_block_variables(compressor, this); |
|
push(tw); |
|
this.body.walk(tw); |
|
pop(tw); |
|
if (this.bcatch) { |
|
push(tw); |
|
this.bcatch.walk(tw); |
|
pop(tw); |
|
} |
|
if (this.bfinally) this.bfinally.walk(tw); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_Unary, function(tw) { |
|
var node = this; |
|
if (node.operator !== "++" && node.operator !== "--") return; |
|
var exp = node.expression; |
|
if (!(exp instanceof AST_SymbolRef)) return; |
|
var def = exp.definition(); |
|
var safe = safe_to_assign(tw, def, exp.scope, true); |
|
def.assignments++; |
|
if (!safe) return; |
|
var fixed = def.fixed; |
|
if (!fixed) return; |
|
def.references.push(exp); |
|
def.chained = true; |
|
def.fixed = function() { |
|
return make_node(AST_Binary, node, { |
|
operator: node.operator.slice(0, -1), |
|
left: make_node(AST_UnaryPrefix, node, { |
|
operator: "+", |
|
expression: fixed instanceof AST_Node ? fixed : fixed() |
|
}), |
|
right: make_node(AST_Number, node, { |
|
value: 1 |
|
}) |
|
}); |
|
}; |
|
mark(tw, def, true); |
|
return true; |
|
}); |
|
|
|
def_reduce_vars(AST_VarDef, function(tw, descend) { |
|
var node = this; |
|
if (node.name instanceof AST_Destructuring) { |
|
suppress(node.name); |
|
return; |
|
} |
|
var d = node.name.definition(); |
|
if (node.value) { |
|
if (safe_to_assign(tw, d, node.name.scope, node.value)) { |
|
d.fixed = function() { |
|
return node.value; |
|
}; |
|
tw.loop_ids.set(d.id, tw.in_loop); |
|
mark(tw, d, false); |
|
descend(); |
|
mark(tw, d, true); |
|
return true; |
|
} else { |
|
d.fixed = false; |
|
} |
|
} |
|
}); |
|
|
|
def_reduce_vars(AST_UsingDef, function() { |
|
suppress(this.name); |
|
}); |
|
|
|
def_reduce_vars(AST_While, function(tw, descend, compressor) { |
|
reset_block_variables(compressor, this); |
|
const saved_loop = tw.in_loop; |
|
tw.in_loop = this; |
|
push(tw); |
|
descend(); |
|
pop(tw); |
|
tw.in_loop = saved_loop; |
|
return true; |
|
}); |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
function loop_body(x) { |
|
if (x instanceof AST_IterationStatement) { |
|
return x.body instanceof AST_BlockStatement ? x.body : x; |
|
} |
|
return x; |
|
} |
|
|
|
function is_lhs_read_only(lhs) { |
|
if (lhs instanceof AST_This) return true; |
|
if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda; |
|
if (lhs instanceof AST_PropAccess) { |
|
lhs = lhs.expression; |
|
if (lhs instanceof AST_SymbolRef) { |
|
if (lhs.is_immutable()) return false; |
|
lhs = lhs.fixed_value(); |
|
} |
|
if (!lhs) return true; |
|
if (lhs instanceof AST_RegExp) return false; |
|
if (lhs instanceof AST_Constant) return true; |
|
return is_lhs_read_only(lhs); |
|
} |
|
return false; |
|
} |
|
|
|
/** var a = 1 --> var a*/ |
|
function remove_initializers(var_statement) { |
|
var decls = []; |
|
var_statement.definitions.forEach(function(def) { |
|
if (def.name instanceof AST_SymbolDeclaration) { |
|
def.value = null; |
|
decls.push(def); |
|
} else { |
|
def.declarations_as_names().forEach(name => { |
|
decls.push(make_node(AST_VarDef, def, { |
|
name, |
|
value: null |
|
})); |
|
}); |
|
} |
|
}); |
|
return decls.length ? make_node(AST_Var, var_statement, { definitions: decls }) : null; |
|
} |
|
|
|
/** Called on code which won't be executed but has an effect outside of itself: `var`, `function` statements, `export`, `import`. */ |
|
function extract_from_unreachable_code(compressor, stat, target) { |
|
walk(stat, node => { |
|
if (node instanceof AST_Var) { |
|
const no_initializers = remove_initializers(node); |
|
if (no_initializers) target.push(no_initializers); |
|
return true; |
|
} |
|
if ( |
|
node instanceof AST_Defun |
|
&& (node === stat || !compressor.has_directive("use strict")) |
|
) { |
|
target.push(node === stat ? node : make_node(AST_Var, node, { |
|
definitions: [ |
|
make_node(AST_VarDef, node, { |
|
name: make_node(AST_SymbolVar, node.name, node.name), |
|
value: null |
|
}) |
|
] |
|
})); |
|
return true; |
|
} |
|
if (node instanceof AST_Export || node instanceof AST_Import) { |
|
target.push(node); |
|
return true; |
|
} |
|
if (node instanceof AST_Scope || node instanceof AST_Class) { |
|
// Do not go into nested scopes |
|
return true; |
|
} |
|
}); |
|
} |
|
|
|
/** Tighten a bunch of statements together, and perform statement-level optimization. */ |
|
function tighten_body(statements, compressor) { |
|
const nearest_scope = compressor.find_scope(); |
|
const defun_scope = nearest_scope.get_defun_scope(); |
|
const { in_loop, in_try } = find_loop_scope_try(); |
|
|
|
var CHANGED, max_iter = 10; |
|
do { |
|
CHANGED = false; |
|
eliminate_spurious_blocks(statements); |
|
if (compressor.option("dead_code")) { |
|
eliminate_dead_code(statements, compressor); |
|
} |
|
if (compressor.option("if_return")) { |
|
handle_if_return(statements, compressor); |
|
} |
|
if (compressor.sequences_limit > 0) { |
|
sequencesize(statements, compressor); |
|
sequencesize_2(statements, compressor); |
|
} |
|
if (compressor.option("join_vars")) { |
|
join_consecutive_vars(statements); |
|
} |
|
if (compressor.option("collapse_vars")) { |
|
collapse(statements, compressor); |
|
} |
|
} while (CHANGED && max_iter-- > 0); |
|
|
|
function find_loop_scope_try() { |
|
var node = compressor.self(), level = 0, in_loop = false, in_try = false; |
|
do { |
|
if (node instanceof AST_IterationStatement) { |
|
in_loop = true; |
|
} else if (node instanceof AST_Scope) { |
|
break; |
|
} else if (node instanceof AST_TryBlock) { |
|
in_try = true; |
|
} |
|
} while (node = compressor.parent(level++)); |
|
|
|
return { in_loop, in_try }; |
|
} |
|
|
|
// Search from right to left for assignment-like expressions: |
|
// - `var a = x;` |
|
// - `a = x;` |
|
// - `++a` |
|
// For each candidate, scan from left to right for first usage, then try |
|
// to fold assignment into the site for compression. |
|
// Will not attempt to collapse assignments into or past code blocks |
|
// which are not sequentially executed, e.g. loops and conditionals. |
|
function collapse(statements, compressor) { |
|
if (nearest_scope.pinned() || defun_scope.pinned()) |
|
return statements; |
|
var args; |
|
var candidates = []; |
|
var stat_index = statements.length; |
|
var scanner = new TreeTransformer(function (node) { |
|
if (abort) |
|
return node; |
|
// Skip nodes before `candidate` as quickly as possible |
|
if (!hit) { |
|
if (node !== hit_stack[hit_index]) |
|
return node; |
|
hit_index++; |
|
if (hit_index < hit_stack.length) |
|
return handle_custom_scan_order(node); |
|
hit = true; |
|
stop_after = find_stop(node, 0); |
|
if (stop_after === node) |
|
abort = true; |
|
return node; |
|
} |
|
// Stop immediately if these node types are encountered |
|
var parent = scanner.parent(); |
|
if (node instanceof AST_Assign |
|
&& (node.logical || node.operator != "=" && lhs.equivalent_to(node.left)) |
|
|| node instanceof AST_Await |
|
|| node instanceof AST_Using |
|
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression) |
|
|| |
|
(node instanceof AST_Call || node instanceof AST_PropAccess) |
|
&& node.optional |
|
|| node instanceof AST_Debugger |
|
|| node instanceof AST_Destructuring |
|
|| node instanceof AST_Expansion |
|
&& node.expression instanceof AST_Symbol |
|
&& ( |
|
node.expression instanceof AST_This |
|
|| node.expression.definition().references.length > 1 |
|
) |
|
|| node instanceof AST_IterationStatement && !(node instanceof AST_For) |
|
|| node instanceof AST_LoopControl |
|
|| node instanceof AST_Try |
|
|| node instanceof AST_With |
|
|| node instanceof AST_Yield |
|
|| node instanceof AST_Export |
|
|| node instanceof AST_Class |
|
|| parent instanceof AST_For && node !== parent.init |
|
|| !replace_all |
|
&& ( |
|
node instanceof AST_SymbolRef |
|
&& !node.is_declared(compressor) |
|
&& !pure_prop_access_globals.has(node) |
|
) |
|
|| node instanceof AST_SymbolRef |
|
&& parent instanceof AST_Call |
|
&& has_annotation(parent, _NOINLINE) |
|
|| node instanceof AST_ObjectProperty && node.key instanceof AST_Node |
|
) { |
|
abort = true; |
|
return node; |
|
} |
|
// Stop only if candidate is found within conditional branches |
|
if (!stop_if_hit && (!lhs_local || !replace_all) |
|
&& (parent instanceof AST_Binary && lazy_op.has(parent.operator) && parent.left !== node |
|
|| parent instanceof AST_Conditional && parent.condition !== node |
|
|| parent instanceof AST_If && parent.condition !== node)) { |
|
stop_if_hit = parent; |
|
} |
|
// Replace variable with assignment when found |
|
if ( |
|
can_replace |
|
&& !(node instanceof AST_SymbolDeclaration) |
|
&& lhs.equivalent_to(node) |
|
&& !shadows(scanner.find_scope() || nearest_scope, lvalues) |
|
) { |
|
if (stop_if_hit) { |
|
abort = true; |
|
return node; |
|
} |
|
if (is_lhs(node, parent)) { |
|
if (value_def) |
|
replaced++; |
|
return node; |
|
} else { |
|
replaced++; |
|
if (value_def && candidate instanceof AST_VarDef) |
|
return node; |
|
} |
|
CHANGED = abort = true; |
|
if (candidate instanceof AST_UnaryPostfix) { |
|
return make_node(AST_UnaryPrefix, candidate, candidate); |
|
} |
|
if (candidate instanceof AST_VarDef) { |
|
var def = candidate.name.definition(); |
|
var value = candidate.value; |
|
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { |
|
def.replaced++; |
|
if (funarg && is_identifier_atom(value)) { |
|
return value.transform(compressor); |
|
} else { |
|
return maintain_this_binding(parent, node, value); |
|
} |
|
} |
|
return make_node(AST_Assign, candidate, { |
|
operator: "=", |
|
logical: false, |
|
left: make_node(AST_SymbolRef, candidate.name, candidate.name), |
|
right: value |
|
}); |
|
} |
|
clear_flag(candidate, WRITE_ONLY); |
|
return candidate; |
|
} |
|
// These node types have child nodes that execute sequentially, |
|
// but are otherwise not safe to scan into or beyond them. |
|
var sym; |
|
if (node instanceof AST_Call |
|
|| node instanceof AST_Exit |
|
&& (side_effects || lhs instanceof AST_PropAccess || may_modify(lhs)) |
|
|| node instanceof AST_PropAccess |
|
&& (side_effects || node.expression.may_throw_on_access(compressor)) |
|
|| node instanceof AST_SymbolRef |
|
&& ((lvalues.has(node.name) && lvalues.get(node.name).modified) || side_effects && may_modify(node)) |
|
|| node instanceof AST_VarDef && node.value |
|
&& (lvalues.has(node.name.name) || side_effects && may_modify(node.name)) |
|
|| node instanceof AST_Using |
|
|| (sym = is_lhs(node.left, node)) |
|
&& (sym instanceof AST_PropAccess || lvalues.has(sym.name)) |
|
|| may_throw |
|
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))) { |
|
stop_after = node; |
|
if (node instanceof AST_Scope) |
|
abort = true; |
|
} |
|
return handle_custom_scan_order(node); |
|
}, function (node) { |
|
if (abort) |
|
return; |
|
if (stop_after === node) |
|
abort = true; |
|
if (stop_if_hit === node) |
|
stop_if_hit = null; |
|
}); |
|
|
|
var multi_replacer = new TreeTransformer(function (node) { |
|
if (abort) |
|
return node; |
|
// Skip nodes before `candidate` as quickly as possible |
|
if (!hit) { |
|
if (node !== hit_stack[hit_index]) |
|
return node; |
|
hit_index++; |
|
if (hit_index < hit_stack.length) |
|
return; |
|
hit = true; |
|
return node; |
|
} |
|
// Replace variable when found |
|
if (node instanceof AST_SymbolRef |
|
&& node.name == def.name) { |
|
if (!--replaced) |
|
abort = true; |
|
if (is_lhs(node, multi_replacer.parent())) |
|
return node; |
|
def.replaced++; |
|
value_def.replaced--; |
|
return candidate.value; |
|
} |
|
// Skip (non-executed) functions and (leading) default case in switch statements |
|
if (node instanceof AST_Default || node instanceof AST_Scope) |
|
return node; |
|
}); |
|
|
|
while (--stat_index >= 0) { |
|
// Treat parameters as collapsible in IIFE, i.e. |
|
// function(a, b){ ... }(x()); |
|
// would be translated into equivalent assignments: |
|
// var a = x(), b = undefined; |
|
if (stat_index == 0 && compressor.option("unused")) |
|
extract_args(); |
|
// Find collapsible assignments |
|
var hit_stack = []; |
|
extract_candidates(statements[stat_index]); |
|
while (candidates.length > 0) { |
|
hit_stack = candidates.pop(); |
|
var hit_index = 0; |
|
var candidate = hit_stack[hit_stack.length - 1]; |
|
var value_def = null; |
|
var stop_after = null; |
|
var stop_if_hit = null; |
|
var lhs = get_lhs(candidate); |
|
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) |
|
continue; |
|
// Locate symbols which may execute code outside of scanning range |
|
var lvalues = get_lvalues(candidate); |
|
var lhs_local = is_lhs_local(lhs); |
|
if (lhs instanceof AST_SymbolRef) { |
|
lvalues.set(lhs.name, { def: lhs.definition(), modified: false }); |
|
} |
|
var side_effects = value_has_side_effects(candidate); |
|
var replace_all = replace_all_symbols(); |
|
var may_throw = candidate.may_throw(compressor); |
|
var funarg = candidate.name instanceof AST_SymbolFunarg; |
|
var hit = funarg; |
|
var abort = false, replaced = 0, can_replace = !args || !hit; |
|
if (!can_replace) { |
|
for ( |
|
let j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; |
|
!abort && j < args.length; |
|
j++ |
|
) { |
|
args[j].transform(scanner); |
|
} |
|
can_replace = true; |
|
} |
|
for (var i = stat_index; !abort && i < statements.length; i++) { |
|
statements[i].transform(scanner); |
|
} |
|
if (value_def) { |
|
var def = candidate.name.definition(); |
|
if (abort && def.references.length - def.replaced > replaced) |
|
replaced = false; |
|
else { |
|
abort = false; |
|
hit_index = 0; |
|
hit = funarg; |
|
for (var i = stat_index; !abort && i < statements.length; i++) { |
|
statements[i].transform(multi_replacer); |
|
} |
|
value_def.single_use = false; |
|
} |
|
} |
|
if (replaced && !remove_candidate(candidate)) |
|
statements.splice(stat_index, 1); |
|
} |
|
} |
|
|
|
function handle_custom_scan_order(node) { |
|
// Skip (non-executed) functions |
|
if (node instanceof AST_Scope) |
|
return node; |
|
|
|
// Scan case expressions first in a switch statement |
|
if (node instanceof AST_Switch) { |
|
node.expression = node.expression.transform(scanner); |
|
for (var i = 0, len = node.body.length; !abort && i < len; i++) { |
|
var branch = node.body[i]; |
|
if (branch instanceof AST_Case) { |
|
if (!hit) { |
|
if (branch !== hit_stack[hit_index]) |
|
continue; |
|
hit_index++; |
|
} |
|
branch.expression = branch.expression.transform(scanner); |
|
if (!replace_all) |
|
break; |
|
} |
|
} |
|
abort = true; |
|
return node; |
|
} |
|
} |
|
|
|
function redefined_within_scope(def, scope) { |
|
if (def.global) |
|
return false; |
|
let cur_scope = def.scope; |
|
while (cur_scope && cur_scope !== scope) { |
|
if (cur_scope.variables.has(def.name)) { |
|
return true; |
|
} |
|
cur_scope = cur_scope.parent_scope; |
|
} |
|
return false; |
|
} |
|
|
|
function has_overlapping_symbol(fn, arg, fn_strict) { |
|
var found = false, scan_this = !(fn instanceof AST_Arrow); |
|
arg.walk(new TreeWalker(function (node, descend) { |
|
if (found) |
|
return true; |
|
if (node instanceof AST_SymbolRef && (fn.variables.has(node.name) || redefined_within_scope(node.definition(), fn))) { |
|
var s = node.definition().scope; |
|
if (s !== defun_scope) |
|
while (s = s.parent_scope) { |
|
if (s === defun_scope) |
|
return true; |
|
} |
|
return found = true; |
|
} |
|
if ((fn_strict || scan_this) && node instanceof AST_This) { |
|
return found = true; |
|
} |
|
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) { |
|
var prev = scan_this; |
|
scan_this = false; |
|
descend(); |
|
scan_this = prev; |
|
return true; |
|
} |
|
})); |
|
return found; |
|
} |
|
|
|
function arg_is_injectable(arg) { |
|
if (arg instanceof AST_Expansion) return false; |
|
const contains_await = walk(arg, (node) => { |
|
if (node instanceof AST_Await) return walk_abort; |
|
}); |
|
if (contains_await) return false; |
|
return true; |
|
} |
|
function extract_args() { |
|
var iife, fn = compressor.self(); |
|
if (is_func_expr(fn) |
|
&& !fn.name |
|
&& !fn.uses_arguments |
|
&& !fn.pinned() |
|
&& (iife = compressor.parent()) instanceof AST_Call |
|
&& iife.expression === fn |
|
&& iife.args.every(arg_is_injectable) |
|
) { |
|
var fn_strict = compressor.has_directive("use strict"); |
|
if (fn_strict && !member(fn_strict, fn.body)) |
|
fn_strict = false; |
|
var len = fn.argnames.length; |
|
args = iife.args.slice(len); |
|
var names = new Set(); |
|
for (var i = len; --i >= 0;) { |
|
var sym = fn.argnames[i]; |
|
var arg = iife.args[i]; |
|
// The following two line fix is a duplicate of the fix at |
|
// https://github.com/terser/terser/commit/011d3eb08cefe6922c7d1bdfa113fc4aeaca1b75 |
|
// This might mean that these two pieces of code (one here in collapse_vars and another in reduce_vars |
|
// Might be doing the exact same thing. |
|
const def = sym.definition && sym.definition(); |
|
const is_reassigned = def && def.orig.length > 1; |
|
if (is_reassigned) |
|
continue; |
|
args.unshift(make_node(AST_VarDef, sym, { |
|
name: sym, |
|
value: arg |
|
})); |
|
if (names.has(sym.name)) |
|
continue; |
|
names.add(sym.name); |
|
if (sym instanceof AST_Expansion) { |
|
var elements = iife.args.slice(i); |
|
if (elements.every((arg) => !has_overlapping_symbol(fn, arg, fn_strict) |
|
)) { |
|
candidates.unshift([make_node(AST_VarDef, sym, { |
|
name: sym.expression, |
|
value: make_node(AST_Array, iife, { |
|
elements: elements |
|
}) |
|
})]); |
|
} |
|
} else { |
|
if (!arg) { |
|
arg = make_void_0(sym).transform(compressor); |
|
} else if (arg instanceof AST_Lambda && arg.pinned() |
|
|| has_overlapping_symbol(fn, arg, fn_strict)) { |
|
arg = null; |
|
} |
|
if (arg) |
|
candidates.unshift([make_node(AST_VarDef, sym, { |
|
name: sym, |
|
value: arg |
|
})]); |
|
} |
|
} |
|
} |
|
} |
|
|
|
function extract_candidates(expr) { |
|
hit_stack.push(expr); |
|
if (expr instanceof AST_Assign) { |
|
if (!expr.left.has_side_effects(compressor) |
|
&& !(expr.right instanceof AST_Chain)) { |
|
candidates.push(hit_stack.slice()); |
|
} |
|
extract_candidates(expr.right); |
|
} else if (expr instanceof AST_Binary) { |
|
extract_candidates(expr.left); |
|
extract_candidates(expr.right); |
|
} else if (expr instanceof AST_Call && !has_annotation(expr, _NOINLINE)) { |
|
extract_candidates(expr.expression); |
|
expr.args.forEach(extract_candidates); |
|
} else if (expr instanceof AST_Case) { |
|
extract_candidates(expr.expression); |
|
} else if (expr instanceof AST_Conditional) { |
|
extract_candidates(expr.condition); |
|
extract_candidates(expr.consequent); |
|
extract_candidates(expr.alternative); |
|
} else if (expr instanceof AST_Definitions) { |
|
var len = expr.definitions.length; |
|
// limit number of trailing variable definitions for consideration |
|
var i = len - 200; |
|
if (i < 0) |
|
i = 0; |
|
for (; i < len; i++) { |
|
extract_candidates(expr.definitions[i]); |
|
} |
|
} else if (expr instanceof AST_DWLoop) { |
|
extract_candidates(expr.condition); |
|
if (!(expr.body instanceof AST_Block)) { |
|
extract_candidates(expr.body); |
|
} |
|
} else if (expr instanceof AST_Exit) { |
|
if (expr.value) |
|
extract_candidates(expr.value); |
|
} else if (expr instanceof AST_For) { |
|
if (expr.init) |
|
extract_candidates(expr.init); |
|
if (expr.condition) |
|
extract_candidates(expr.condition); |
|
if (expr.step) |
|
extract_candidates(expr.step); |
|
if (!(expr.body instanceof AST_Block)) { |
|
extract_candidates(expr.body); |
|
} |
|
} else if (expr instanceof AST_ForIn) { |
|
extract_candidates(expr.object); |
|
if (!(expr.body instanceof AST_Block)) { |
|
extract_candidates(expr.body); |
|
} |
|
} else if (expr instanceof AST_If) { |
|
extract_candidates(expr.condition); |
|
if (!(expr.body instanceof AST_Block)) { |
|
extract_candidates(expr.body); |
|
} |
|
if (expr.alternative && !(expr.alternative instanceof AST_Block)) { |
|
extract_candidates(expr.alternative); |
|
} |
|
} else if (expr instanceof AST_Sequence) { |
|
expr.expressions.forEach(extract_candidates); |
|
} else if (expr instanceof AST_SimpleStatement) { |
|
extract_candidates(expr.body); |
|
} else if (expr instanceof AST_Switch) { |
|
extract_candidates(expr.expression); |
|
expr.body.forEach(extract_candidates); |
|
} else if (expr instanceof AST_Unary) { |
|
if (expr.operator == "++" || expr.operator == "--") { |
|
candidates.push(hit_stack.slice()); |
|
} |
|
} else if (expr instanceof AST_VarDef) { |
|
if (expr.value && !(expr.value instanceof AST_Chain)) { |
|
candidates.push(hit_stack.slice()); |
|
extract_candidates(expr.value); |
|
} |
|
} |
|
hit_stack.pop(); |
|
} |
|
|
|
function find_stop(node, level, write_only) { |
|
var parent = scanner.parent(level); |
|
if (parent instanceof AST_Assign) { |
|
if (write_only |
|
&& !parent.logical |
|
&& !(parent.left instanceof AST_PropAccess |
|
|| lvalues.has(parent.left.name))) { |
|
return find_stop(parent, level + 1, write_only); |
|
} |
|
return node; |
|
} |
|
if (parent instanceof AST_Binary) { |
|
if (write_only && (!lazy_op.has(parent.operator) || parent.left === node)) { |
|
return find_stop(parent, level + 1, write_only); |
|
} |
|
return node; |
|
} |
|
if (parent instanceof AST_Call) |
|
return node; |
|
if (parent instanceof AST_Case) |
|
return node; |
|
if (parent instanceof AST_Conditional) { |
|
if (write_only && parent.condition === node) { |
|
return find_stop(parent, level + 1, write_only); |
|
} |
|
return node; |
|
} |
|
if (parent instanceof AST_Definitions) { |
|
return find_stop(parent, level + 1, true); |
|
} |
|
if (parent instanceof AST_Exit) { |
|
return write_only ? find_stop(parent, level + 1, write_only) : node; |
|
} |
|
if (parent instanceof AST_If) { |
|
if (write_only && parent.condition === node) { |
|
return find_stop(parent, level + 1, write_only); |
|
} |
|
return node; |
|
} |
|
if (parent instanceof AST_IterationStatement) |
|
return node; |
|
if (parent instanceof AST_Sequence) { |
|
return find_stop(parent, level + 1, parent.tail_node() !== node); |
|
} |
|
if (parent instanceof AST_SimpleStatement) { |
|
return find_stop(parent, level + 1, true); |
|
} |
|
if (parent instanceof AST_Switch) |
|
return node; |
|
if (parent instanceof AST_VarDef) |
|
return node; |
|
return null; |
|
} |
|
|
|
function mangleable_var(var_def) { |
|
var value = var_def.value; |
|
if (!(value instanceof AST_SymbolRef)) |
|
return; |
|
if (value.name == "arguments") |
|
return; |
|
var def = value.definition(); |
|
if (def.undeclared) |
|
return; |
|
return value_def = def; |
|
} |
|
|
|
function get_lhs(expr) { |
|
if (expr instanceof AST_Assign && expr.logical) { |
|
return false; |
|
} else if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) { |
|
var def = expr.name.definition(); |
|
if (!member(expr.name, def.orig)) |
|
return; |
|
var referenced = def.references.length - def.replaced; |
|
if (!referenced) |
|
return; |
|
var declared = def.orig.length - def.eliminated; |
|
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg) |
|
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) { |
|
return make_node(AST_SymbolRef, expr.name, expr.name); |
|
} |
|
} else { |
|
const lhs = expr instanceof AST_Assign |
|
? expr.left |
|
: expr.expression; |
|
return !is_ref_of(lhs, AST_SymbolConst) |
|
&& !is_ref_of(lhs, AST_SymbolLet) |
|
&& !is_ref_of(lhs, AST_SymbolUsing) |
|
&& lhs; |
|
} |
|
} |
|
|
|
function get_rvalue(expr) { |
|
if (expr instanceof AST_Assign) { |
|
return expr.right; |
|
} else { |
|
return expr.value; |
|
} |
|
} |
|
|
|
function get_lvalues(expr) { |
|
var lvalues = new Map(); |
|
if (expr instanceof AST_Unary) |
|
return lvalues; |
|
var tw = new TreeWalker(function (node) { |
|
var sym = node; |
|
while (sym instanceof AST_PropAccess) |
|
sym = sym.expression; |
|
if (sym instanceof AST_SymbolRef) { |
|
const prev = lvalues.get(sym.name); |
|
if (!prev || !prev.modified) { |
|
lvalues.set(sym.name, { |
|
def: sym.definition(), |
|
modified: is_modified(compressor, tw, node, node, 0) |
|
}); |
|
} |
|
} |
|
}); |
|
get_rvalue(expr).walk(tw); |
|
return lvalues; |
|
} |
|
|
|
function remove_candidate(expr) { |
|
if (expr.name instanceof AST_SymbolFunarg) { |
|
var iife = compressor.parent(), argnames = compressor.self().argnames; |
|
var index = argnames.indexOf(expr.name); |
|
if (index < 0) { |
|
iife.args.length = Math.min(iife.args.length, argnames.length - 1); |
|
} else { |
|
var args = iife.args; |
|
if (args[index]) |
|
args[index] = make_node(AST_Number, args[index], { |
|
value: 0 |
|
}); |
|
} |
|
return true; |
|
} |
|
var found = false; |
|
return statements[stat_index].transform(new TreeTransformer(function (node, descend, in_list) { |
|
if (found) |
|
return node; |
|
if (node === expr || node.body === expr) { |
|
found = true; |
|
if (node instanceof AST_VarDef) { |
|
node.value = node.name instanceof AST_SymbolConst |
|
? make_void_0(node.value) // `const` always needs value. |
|
: null; |
|
return node; |
|
} |
|
return in_list ? MAP.skip : null; |
|
} |
|
}, function (node) { |
|
if (node instanceof AST_Sequence) |
|
switch (node.expressions.length) { |
|
case 0: return null; |
|
case 1: return node.expressions[0]; |
|
} |
|
})); |
|
} |
|
|
|
function is_lhs_local(lhs) { |
|
while (lhs instanceof AST_PropAccess) |
|
lhs = lhs.expression; |
|
return lhs instanceof AST_SymbolRef |
|
&& lhs.definition().scope.get_defun_scope() === defun_scope |
|
&& !(in_loop |
|
&& (lvalues.has(lhs.name) |
|
|| candidate instanceof AST_Unary |
|
|| (candidate instanceof AST_Assign |
|
&& !candidate.logical |
|
&& candidate.operator != "="))); |
|
} |
|
|
|
function value_has_side_effects(expr) { |
|
if (expr instanceof AST_Unary) |
|
return unary_side_effects.has(expr.operator); |
|
return get_rvalue(expr).has_side_effects(compressor); |
|
} |
|
|
|
function replace_all_symbols() { |
|
if (side_effects) |
|
return false; |
|
if (value_def) |
|
return true; |
|
if (lhs instanceof AST_SymbolRef) { |
|
var def = lhs.definition(); |
|
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
function may_modify(sym) { |
|
if (!sym.definition) |
|
return true; // AST_Destructuring |
|
var def = sym.definition(); |
|
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) |
|
return false; |
|
if (def.scope.get_defun_scope() !== defun_scope) |
|
return true; |
|
return def.references.some((ref) => |
|
ref.scope.get_defun_scope() !== defun_scope |
|
); |
|
} |
|
|
|
function side_effects_external(node, lhs) { |
|
if (node instanceof AST_Assign) |
|
return side_effects_external(node.left, true); |
|
if (node instanceof AST_Unary) |
|
return side_effects_external(node.expression, true); |
|
if (node instanceof AST_VarDef) |
|
return node.value && side_effects_external(node.value); |
|
if (lhs) { |
|
if (node instanceof AST_Dot) |
|
return side_effects_external(node.expression, true); |
|
if (node instanceof AST_Sub) |
|
return side_effects_external(node.expression, true); |
|
if (node instanceof AST_SymbolRef) |
|
return node.definition().scope.get_defun_scope() !== defun_scope; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Will any of the pulled-in lvalues shadow a variable in newScope or parents? |
|
* similar to scope_encloses_variables_in_this_scope */ |
|
function shadows(my_scope, lvalues) { |
|
for (const { def } of lvalues.values()) { |
|
const looked_up = my_scope.find_variable(def.name); |
|
if (looked_up) { |
|
if (looked_up === def) continue; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
} |
|
|
|
function eliminate_spurious_blocks(statements) { |
|
var seen_dirs = []; |
|
for (var i = 0; i < statements.length;) { |
|
var stat = statements[i]; |
|
if (stat instanceof AST_BlockStatement && stat.body.every(can_be_evicted_from_block)) { |
|
CHANGED = true; |
|
eliminate_spurious_blocks(stat.body); |
|
statements.splice(i, 1, ...stat.body); |
|
i += stat.body.length; |
|
} else if (stat instanceof AST_EmptyStatement) { |
|
CHANGED = true; |
|
statements.splice(i, 1); |
|
} else if (stat instanceof AST_Directive) { |
|
if (seen_dirs.indexOf(stat.value) < 0) { |
|
i++; |
|
seen_dirs.push(stat.value); |
|
} else { |
|
CHANGED = true; |
|
statements.splice(i, 1); |
|
} |
|
} else |
|
i++; |
|
} |
|
} |
|
|
|
function handle_if_return(statements, compressor) { |
|
var self = compressor.self(); |
|
var multiple_if_returns = has_multiple_if_returns(statements); |
|
var in_lambda = self instanceof AST_Lambda; |
|
// Prevent extremely deep nesting |
|
// https://github.com/terser/terser/issues/1432 |
|
// https://github.com/webpack/webpack/issues/17548 |
|
const iteration_start = Math.min(statements.length, 500); |
|
for (var i = iteration_start; --i >= 0;) { |
|
var stat = statements[i]; |
|
var j = next_index(i); |
|
var next = statements[j]; |
|
|
|
if (in_lambda && !next && stat instanceof AST_Return) { |
|
if (!stat.value) { |
|
CHANGED = true; |
|
statements.splice(i, 1); |
|
continue; |
|
} |
|
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") { |
|
CHANGED = true; |
|
statements[i] = make_node(AST_SimpleStatement, stat, { |
|
body: stat.value.expression |
|
}); |
|
continue; |
|
} |
|
} |
|
|
|
if (stat instanceof AST_If) { |
|
let ab, new_else; |
|
|
|
ab = aborts(stat.body); |
|
if ( |
|
can_merge_flow(ab) |
|
&& (new_else = as_statement_array_with_return(stat.body, ab)) |
|
) { |
|
if (ab.label) { |
|
remove(ab.label.thedef.references, ab); |
|
} |
|
CHANGED = true; |
|
stat = stat.clone(); |
|
stat.condition = stat.condition.negate(compressor); |
|
stat.body = make_node(AST_BlockStatement, stat, { |
|
body: as_statement_array(stat.alternative).concat(extract_defuns()) |
|
}); |
|
stat.alternative = make_node(AST_BlockStatement, stat, { |
|
body: new_else |
|
}); |
|
statements[i] = stat.transform(compressor); |
|
continue; |
|
} |
|
|
|
ab = aborts(stat.alternative); |
|
if ( |
|
can_merge_flow(ab) |
|
&& (new_else = as_statement_array_with_return(stat.alternative, ab)) |
|
) { |
|
if (ab.label) { |
|
remove(ab.label.thedef.references, ab); |
|
} |
|
CHANGED = true; |
|
stat = stat.clone(); |
|
stat.body = make_node(AST_BlockStatement, stat.body, { |
|
body: as_statement_array(stat.body).concat(extract_defuns()) |
|
}); |
|
stat.alternative = make_node(AST_BlockStatement, stat.alternative, { |
|
body: new_else |
|
}); |
|
statements[i] = stat.transform(compressor); |
|
continue; |
|
} |
|
} |
|
|
|
if (stat instanceof AST_If && stat.body instanceof AST_Return) { |
|
var value = stat.body.value; |
|
//--- |
|
// pretty silly case, but: |
|
// if (foo()) return; return; ==> foo(); return; |
|
if (!value && !stat.alternative |
|
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) { |
|
CHANGED = true; |
|
statements[i] = make_node(AST_SimpleStatement, stat.condition, { |
|
body: stat.condition |
|
}); |
|
continue; |
|
} |
|
//--- |
|
// if (foo()) return x; return y; ==> return foo() ? x : y; |
|
if (value && !stat.alternative && next instanceof AST_Return && next.value) { |
|
CHANGED = true; |
|
stat = stat.clone(); |
|
stat.alternative = next; |
|
statements[i] = stat.transform(compressor); |
|
statements.splice(j, 1); |
|
continue; |
|
} |
|
//--- |
|
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined; |
|
if (value && !stat.alternative |
|
&& (!next && in_lambda && multiple_if_returns |
|
|| next instanceof AST_Return)) { |
|
CHANGED = true; |
|
stat = stat.clone(); |
|
stat.alternative = next || make_node(AST_Return, stat, { |
|
value: null |
|
}); |
|
statements[i] = stat.transform(compressor); |
|
if (next) |
|
statements.splice(j, 1); |
|
continue; |
|
} |
|
//--- |
|
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e; |
|
// |
|
// if sequences is not enabled, this can lead to an endless loop (issue #866). |
|
// however, with sequences on this helps producing slightly better output for |
|
// the example code. |
|
var prev = statements[prev_index(i)]; |
|
if (compressor.option("sequences") && in_lambda && !stat.alternative |
|
&& prev instanceof AST_If && prev.body instanceof AST_Return |
|
&& next_index(j) == statements.length && next instanceof AST_SimpleStatement) { |
|
CHANGED = true; |
|
stat = stat.clone(); |
|
stat.alternative = make_node(AST_BlockStatement, next, { |
|
body: [ |
|
next, |
|
make_node(AST_Return, next, { |
|
value: null |
|
}) |
|
] |
|
}); |
|
statements[i] = stat.transform(compressor); |
|
statements.splice(j, 1); |
|
continue; |
|
} |
|
} |
|
} |
|
|
|
function has_multiple_if_returns(statements) { |
|
var n = 0; |
|
for (var i = statements.length; --i >= 0;) { |
|
var stat = statements[i]; |
|
if (stat instanceof AST_If && stat.body instanceof AST_Return) { |
|
if (++n > 1) |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
function is_return_void(value) { |
|
return !value || value instanceof AST_UnaryPrefix && value.operator == "void"; |
|
} |
|
|
|
function can_merge_flow(ab) { |
|
if (!ab) |
|
return false; |
|
for (var j = i + 1, len = statements.length; j < len; j++) { |
|
var stat = statements[j]; |
|
if (stat instanceof AST_DefinitionsLike && !(stat instanceof AST_Var)) |
|
return false; |
|
} |
|
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; |
|
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value) |
|
|| ab instanceof AST_Continue && self === loop_body(lct) |
|
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct; |
|
} |
|
|
|
function extract_defuns() { |
|
var tail = statements.slice(i + 1); |
|
statements.length = i + 1; |
|
return tail.filter(function (stat) { |
|
if (stat instanceof AST_Defun) { |
|
statements.push(stat); |
|
return false; |
|
} |
|
return true; |
|
}); |
|
} |
|
|
|
function as_statement_array_with_return(node, ab) { |
|
var body = as_statement_array(node); |
|
if (ab !== body[body.length - 1]) { |
|
return undefined; |
|
} |
|
body = body.slice(0, -1); |
|
if (!body.every(stat => can_be_evicted_from_block(stat))) { |
|
return undefined; |
|
} |
|
if (ab.value) { |
|
body.push(make_node(AST_SimpleStatement, ab.value, { |
|
body: ab.value.expression |
|
})); |
|
} |
|
return body; |
|
} |
|
|
|
function next_index(i) { |
|
for (var j = i + 1, len = statements.length; j < len; j++) { |
|
var stat = statements[j]; |
|
if (!(stat instanceof AST_Var && declarations_only(stat))) { |
|
break; |
|
} |
|
} |
|
return j; |
|
} |
|
|
|
function prev_index(i) { |
|
for (var j = i; --j >= 0;) { |
|
var stat = statements[j]; |
|
if (!(stat instanceof AST_Var && declarations_only(stat))) { |
|
break; |
|
} |
|
} |
|
return j; |
|
} |
|
} |
|
|
|
function eliminate_dead_code(statements, compressor) { |
|
var has_quit; |
|
var self = compressor.self(); |
|
for (var i = 0, n = 0, len = statements.length; i < len; i++) { |
|
var stat = statements[i]; |
|
if (stat instanceof AST_LoopControl) { |
|
var lct = compressor.loopcontrol_target(stat); |
|
if (stat instanceof AST_Break |
|
&& !(lct instanceof AST_IterationStatement) |
|
&& loop_body(lct) === self |
|
|| stat instanceof AST_Continue |
|
&& loop_body(lct) === self) { |
|
if (stat.label) { |
|
remove(stat.label.thedef.references, stat); |
|
} |
|
} else { |
|
statements[n++] = stat; |
|
} |
|
} else { |
|
statements[n++] = stat; |
|
} |
|
if (aborts(stat)) { |
|
has_quit = statements.slice(i + 1); |
|
break; |
|
} |
|
} |
|
statements.length = n; |
|
CHANGED = n != len; |
|
if (has_quit) |
|
has_quit.forEach(function (stat) { |
|
extract_from_unreachable_code(compressor, stat, statements); |
|
}); |
|
} |
|
|
|
function declarations_only(node) { |
|
return node.definitions.every((var_def) => !var_def.value); |
|
} |
|
|
|
function sequencesize(statements, compressor) { |
|
if (statements.length < 2) |
|
return; |
|
var seq = [], n = 0; |
|
function push_seq() { |
|
if (!seq.length) |
|
return; |
|
var body = make_sequence(seq[0], seq); |
|
statements[n++] = make_node(AST_SimpleStatement, body, { body: body }); |
|
seq = []; |
|
} |
|
for (var i = 0, len = statements.length; i < len; i++) { |
|
var stat = statements[i]; |
|
if (stat instanceof AST_SimpleStatement) { |
|
if (seq.length >= compressor.sequences_limit) |
|
push_seq(); |
|
var body = stat.body; |
|
if (seq.length > 0) |
|
body = body.drop_side_effect_free(compressor); |
|
if (body) |
|
merge_sequence(seq, body); |
|
} else if (stat instanceof AST_Definitions && declarations_only(stat) |
|
|| stat instanceof AST_Defun) { |
|
statements[n++] = stat; |
|
} else { |
|
push_seq(); |
|
statements[n++] = stat; |
|
} |
|
} |
|
push_seq(); |
|
statements.length = n; |
|
if (n != len) |
|
CHANGED = true; |
|
} |
|
|
|
function to_simple_statement(block, decls) { |
|
if (!(block instanceof AST_BlockStatement)) |
|
return block; |
|
var stat = null; |
|
for (var i = 0, len = block.body.length; i < len; i++) { |
|
var line = block.body[i]; |
|
if (line instanceof AST_Var && declarations_only(line)) { |
|
decls.push(line); |
|
} else if (stat || line instanceof AST_DefinitionsLike && !(line instanceof AST_Var)) { |
|
return false; |
|
} else { |
|
stat = line; |
|
} |
|
} |
|
return stat; |
|
} |
|
|
|
function sequencesize_2(statements, compressor) { |
|
function cons_seq(right) { |
|
n--; |
|
CHANGED = true; |
|
var left = prev.body; |
|
return make_sequence(left, [left, right]).transform(compressor); |
|
} |
|
var n = 0, prev; |
|
for (var i = 0; i < statements.length; i++) { |
|
var stat = statements[i]; |
|
if (prev) { |
|
if (stat instanceof AST_Exit) { |
|
stat.value = cons_seq(stat.value || make_void_0(stat).transform(compressor)); |
|
} else if (stat instanceof AST_For) { |
|
if (!(stat.init instanceof AST_DefinitionsLike)) { |
|
const abort = walk(prev.body, node => { |
|
if (node instanceof AST_Scope) |
|
return true; |
|
if (node instanceof AST_Binary |
|
&& node.operator === "in") { |
|
return walk_abort; |
|
} |
|
}); |
|
if (!abort) { |
|
if (stat.init) |
|
stat.init = cons_seq(stat.init); |
|
else { |
|
stat.init = prev.body; |
|
n--; |
|
CHANGED = true; |
|
} |
|
} |
|
} |
|
} else if (stat instanceof AST_ForIn) { |
|
if (!(stat.init instanceof AST_DefinitionsLike) || stat.init instanceof AST_Var) { |
|
stat.object = cons_seq(stat.object); |
|
} |
|
} else if (stat instanceof AST_If) { |
|
stat.condition = cons_seq(stat.condition); |
|
} else if (stat instanceof AST_Switch) { |
|
stat.expression = cons_seq(stat.expression); |
|
} else if (stat instanceof AST_With) { |
|
stat.expression = cons_seq(stat.expression); |
|
} |
|
} |
|
if (compressor.option("conditionals") && stat instanceof AST_If) { |
|
var decls = []; |
|
var body = to_simple_statement(stat.body, decls); |
|
var alt = to_simple_statement(stat.alternative, decls); |
|
if (body !== false && alt !== false && decls.length > 0) { |
|
var len = decls.length; |
|
decls.push(make_node(AST_If, stat, { |
|
condition: stat.condition, |
|
body: body || make_node(AST_EmptyStatement, stat.body), |
|
alternative: alt |
|
})); |
|
decls.unshift(n, 1); |
|
[].splice.apply(statements, decls); |
|
i += len; |
|
n += len + 1; |
|
prev = null; |
|
CHANGED = true; |
|
continue; |
|
} |
|
} |
|
statements[n++] = stat; |
|
prev = stat instanceof AST_SimpleStatement ? stat : null; |
|
} |
|
statements.length = n; |
|
} |
|
|
|
function join_object_assignments(defn, body) { |
|
if (!(defn instanceof AST_Definitions)) |
|
return; |
|
var def = defn.definitions[defn.definitions.length - 1]; |
|
if (!(def.value instanceof AST_Object)) |
|
return; |
|
var exprs; |
|
if (body instanceof AST_Assign && !body.logical) { |
|
exprs = [body]; |
|
} else if (body instanceof AST_Sequence) { |
|
exprs = body.expressions.slice(); |
|
} |
|
if (!exprs) |
|
return; |
|
var trimmed = false; |
|
do { |
|
var node = exprs[0]; |
|
if (!(node instanceof AST_Assign)) |
|
break; |
|
if (node.operator != "=") |
|
break; |
|
if (!(node.left instanceof AST_PropAccess)) |
|
break; |
|
var sym = node.left.expression; |
|
if (!(sym instanceof AST_SymbolRef)) |
|
break; |
|
if (def.name.name != sym.name) |
|
break; |
|
if (!node.right.is_constant_expression(nearest_scope)) |
|
break; |
|
var prop = node.left.property; |
|
if (prop instanceof AST_Node) { |
|
prop = prop.evaluate(compressor); |
|
} |
|
if (prop instanceof AST_Node) |
|
break; |
|
prop = "" + prop; |
|
var diff = compressor.option("ecma") < 2015 |
|
&& compressor.has_directive("use strict") ? function (node) { |
|
return node.key != prop && (node.key && node.key.name != prop); |
|
} : function (node) { |
|
return node.key && node.key.name != prop; |
|
}; |
|
if (!def.value.properties.every(diff)) |
|
break; |
|
var p = def.value.properties.filter(function (p) { return p.key === prop; })[0]; |
|
if (!p) { |
|
def.value.properties.push(make_node(AST_ObjectKeyVal, node, { |
|
key: prop, |
|
value: node.right |
|
})); |
|
} else { |
|
p.value = new AST_Sequence({ |
|
start: p.start, |
|
expressions: [p.value.clone(), node.right.clone()], |
|
end: p.end |
|
}); |
|
} |
|
exprs.shift(); |
|
trimmed = true; |
|
} while (exprs.length); |
|
return trimmed && exprs; |
|
} |
|
|
|
function join_consecutive_vars(statements) { |
|
var defs; |
|
for (var i = 0, j = -1, len = statements.length; i < len; i++) { |
|
var stat = statements[i]; |
|
var prev = statements[j]; |
|
if (stat instanceof AST_Definitions) { |
|
if (prev && prev.TYPE == stat.TYPE) { |
|
prev.definitions = prev.definitions.concat(stat.definitions); |
|
CHANGED = true; |
|
} else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) { |
|
defs.definitions = defs.definitions.concat(stat.definitions); |
|
CHANGED = true; |
|
} else { |
|
statements[++j] = stat; |
|
defs = stat; |
|
} |
|
} else if ( |
|
stat instanceof AST_Using |
|
&& prev instanceof AST_Using |
|
&& prev.await === stat.await |
|
) { |
|
prev.definitions = prev.definitions.concat(stat.definitions); |
|
} else if (stat instanceof AST_Exit) { |
|
stat.value = extract_object_assignments(stat.value); |
|
} else if (stat instanceof AST_For) { |
|
var exprs = join_object_assignments(prev, stat.init); |
|
if (exprs) { |
|
CHANGED = true; |
|
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null; |
|
statements[++j] = stat; |
|
} else if ( |
|
prev instanceof AST_Var |
|
&& (!stat.init || stat.init.TYPE == prev.TYPE) |
|
) { |
|
if (stat.init) { |
|
prev.definitions = prev.definitions.concat(stat.init.definitions); |
|
} |
|
stat.init = prev; |
|
statements[j] = stat; |
|
CHANGED = true; |
|
} else if ( |
|
defs instanceof AST_Var |
|
&& stat.init instanceof AST_Var |
|
&& declarations_only(stat.init) |
|
) { |
|
defs.definitions = defs.definitions.concat(stat.init.definitions); |
|
stat.init = null; |
|
statements[++j] = stat; |
|
CHANGED = true; |
|
} else { |
|
statements[++j] = stat; |
|
} |
|
} else if (stat instanceof AST_ForIn) { |
|
stat.object = extract_object_assignments(stat.object); |
|
} else if (stat instanceof AST_If) { |
|
stat.condition = extract_object_assignments(stat.condition); |
|
} else if (stat instanceof AST_SimpleStatement) { |
|
var exprs = join_object_assignments(prev, stat.body); |
|
if (exprs) { |
|
CHANGED = true; |
|
if (!exprs.length) |
|
continue; |
|
stat.body = make_sequence(stat.body, exprs); |
|
} |
|
statements[++j] = stat; |
|
} else if (stat instanceof AST_Switch) { |
|
stat.expression = extract_object_assignments(stat.expression); |
|
} else if (stat instanceof AST_With) { |
|
stat.expression = extract_object_assignments(stat.expression); |
|
} else { |
|
statements[++j] = stat; |
|
} |
|
} |
|
statements.length = j + 1; |
|
|
|
function extract_object_assignments(value) { |
|
statements[++j] = stat; |
|
var exprs = join_object_assignments(prev, value); |
|
if (exprs) { |
|
CHANGED = true; |
|
if (exprs.length) { |
|
return make_sequence(value, exprs); |
|
} else if (value instanceof AST_Sequence) { |
|
return value.tail_node().left; |
|
} else { |
|
return value.left; |
|
} |
|
} |
|
return value; |
|
} |
|
} |
|
} |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
/** |
|
* Module that contains the inlining logic. |
|
* |
|
* @module |
|
* |
|
* The stars of the show are `inline_into_symbolref` and `inline_into_call`. |
|
*/ |
|
|
|
function within_array_or_object_literal(compressor) { |
|
var node, level = 0; |
|
while (node = compressor.parent(level++)) { |
|
if (node instanceof AST_Statement) return false; |
|
if (node instanceof AST_Array |
|
|| node instanceof AST_ObjectKeyVal |
|
|| node instanceof AST_Object) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
function scope_encloses_variables_in_this_scope(scope, pulled_scope) { |
|
for (const enclosed of pulled_scope.enclosed) { |
|
if (pulled_scope.variables.has(enclosed.name)) { |
|
continue; |
|
} |
|
const looked_up = scope.find_variable(enclosed.name); |
|
if (looked_up) { |
|
if (looked_up === enclosed) continue; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* An extra check function for `top_retain` option, compare the length of const identifier |
|
* and init value length and return true if init value is longer than identifier. for example: |
|
* ``` |
|
* // top_retain: ["example"] |
|
* const example = 100 |
|
* ``` |
|
* it will return false because length of "100" is short than identifier "example". |
|
*/ |
|
function is_const_symbol_short_than_init_value(def, fixed_value) { |
|
if (def.orig.length === 1 && fixed_value) { |
|
const init_value_length = fixed_value.size(); |
|
const identifer_length = def.name.length; |
|
return init_value_length > identifer_length; |
|
} |
|
return true; |
|
} |
|
|
|
function inline_into_symbolref(self, compressor) { |
|
if (compressor.in_computed_key()) return self; |
|
|
|
const parent = compressor.parent(); |
|
const def = self.definition(); |
|
const nearest_scope = compressor.find_scope(); |
|
let fixed = self.fixed_value(); |
|
if ( |
|
compressor.top_retain && |
|
def.global && |
|
compressor.top_retain(def) && |
|
// when identifier is in top_retain option dose not mean we can always inline it. |
|
// if identifier name is longer then init value, we can replace it. |
|
is_const_symbol_short_than_init_value(def, fixed) |
|
) { |
|
// keep it |
|
def.fixed = false; |
|
def.single_use = false; |
|
return self; |
|
} |
|
|
|
if (dont_inline_lambda_in_loop(compressor, fixed)) return self; |
|
|
|
let single_use = def.single_use |
|
&& !(parent instanceof AST_Call |
|
&& (parent.is_callee_pure(compressor)) |
|
|| has_annotation(parent, _NOINLINE)) |
|
&& !(parent instanceof AST_Export |
|
&& fixed instanceof AST_Lambda |
|
&& fixed.name); |
|
|
|
if (single_use && fixed instanceof AST_Node) { |
|
single_use = |
|
!fixed.has_side_effects(compressor) |
|
&& !fixed.may_throw(compressor); |
|
} |
|
|
|
if (fixed instanceof AST_Class && def.scope !== self.scope) { |
|
return self; |
|
} |
|
|
|
if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) { |
|
if (retain_top_func(fixed, compressor)) { |
|
single_use = false; |
|
} else if (def.scope !== self.scope |
|
&& (def.escaped == 1 |
|
|| has_flag(fixed, INLINED) |
|
|| within_array_or_object_literal(compressor) |
|
|| !compressor.option("reduce_funcs"))) { |
|
single_use = false; |
|
} else if (is_recursive_ref(compressor, def)) { |
|
single_use = false; |
|
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) { |
|
single_use = fixed.is_constant_expression(self.scope); |
|
if (single_use == "f") { |
|
var scope = self.scope; |
|
do { |
|
if (scope instanceof AST_Defun || is_func_expr(scope)) { |
|
set_flag(scope, INLINED); |
|
} |
|
} while (scope = scope.parent_scope); |
|
} |
|
} |
|
} |
|
|
|
if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) { |
|
single_use = |
|
def.scope === self.scope |
|
&& !scope_encloses_variables_in_this_scope(nearest_scope, fixed) |
|
|| parent instanceof AST_Call |
|
&& parent.expression === self |
|
&& !scope_encloses_variables_in_this_scope(nearest_scope, fixed) |
|
&& !(fixed.name && fixed.name.definition().recursive_refs > 0); |
|
} |
|
|
|
if (single_use && fixed) { |
|
if (fixed instanceof AST_DefClass) { |
|
set_flag(fixed, SQUEEZED); |
|
fixed = make_node(AST_ClassExpression, fixed, fixed); |
|
} |
|
if (fixed instanceof AST_Defun) { |
|
set_flag(fixed, SQUEEZED); |
|
fixed = make_node(AST_Function, fixed, fixed); |
|
} |
|
if (def.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) { |
|
const defun_def = fixed.name.definition(); |
|
let lambda_def = fixed.variables.get(fixed.name.name); |
|
let name = lambda_def && lambda_def.orig[0]; |
|
if (!(name instanceof AST_SymbolLambda)) { |
|
name = make_node(AST_SymbolLambda, fixed.name, fixed.name); |
|
name.scope = fixed; |
|
fixed.name = name; |
|
lambda_def = fixed.def_function(name); |
|
} |
|
walk(fixed, node => { |
|
if (node instanceof AST_SymbolRef && node.definition() === defun_def) { |
|
node.thedef = lambda_def; |
|
lambda_def.references.push(node); |
|
} |
|
}); |
|
} |
|
if ( |
|
(fixed instanceof AST_Lambda || fixed instanceof AST_Class) |
|
&& fixed.parent_scope !== nearest_scope |
|
) { |
|
fixed = fixed.clone(true, compressor.get_toplevel()); |
|
|
|
nearest_scope.add_child_scope(fixed); |
|
} |
|
return fixed.optimize(compressor); |
|
} |
|
|
|
// multiple uses |
|
if (fixed) { |
|
let replace; |
|
|
|
if (fixed instanceof AST_This) { |
|
if (!(def.orig[0] instanceof AST_SymbolFunarg) |
|
&& def.references.every((ref) => |
|
def.scope === ref.scope |
|
)) { |
|
replace = fixed; |
|
} |
|
} else { |
|
var ev = fixed.evaluate(compressor); |
|
if ( |
|
ev !== fixed |
|
&& (compressor.option("unsafe_regexp") || !(ev instanceof RegExp)) |
|
) { |
|
replace = make_node_from_constant(ev, fixed); |
|
} |
|
} |
|
|
|
if (replace) { |
|
const name_length = self.size(compressor); |
|
const replace_size = replace.size(compressor); |
|
|
|
let overhead = 0; |
|
if (compressor.option("unused") && !compressor.exposed(def)) { |
|
overhead = |
|
(name_length + 2 + fixed.size(compressor)) / |
|
(def.references.length - def.assignments); |
|
} |
|
|
|
if (replace_size <= name_length + overhead) { |
|
return replace; |
|
} |
|
} |
|
} |
|
|
|
return self; |
|
} |
|
|
|
function inline_into_call(self, compressor) { |
|
if (compressor.in_computed_key()) return self; |
|
|
|
var exp = self.expression; |
|
var fn = exp; |
|
var simple_args = self.args.every((arg) => !(arg instanceof AST_Expansion)); |
|
|
|
if (compressor.option("reduce_vars") |
|
&& fn instanceof AST_SymbolRef |
|
&& !has_annotation(self, _NOINLINE) |
|
) { |
|
const fixed = fn.fixed_value(); |
|
|
|
if ( |
|
retain_top_func(fixed, compressor) |
|
|| !compressor.toplevel.funcs && exp.definition().global |
|
) { |
|
return self; |
|
} |
|
|
|
fn = fixed; |
|
} |
|
|
|
if ( |
|
dont_inline_lambda_in_loop(compressor, fn) |
|
&& !has_annotation(self, _INLINE) |
|
) return self; |
|
|
|
var is_func = fn instanceof AST_Lambda; |
|
|
|
var stat = is_func && fn.body[0]; |
|
var is_regular_func = is_func && !fn.is_generator && !fn.async; |
|
var can_inline = is_regular_func && compressor.option("inline") && !self.is_callee_pure(compressor); |
|
if (can_inline && stat instanceof AST_Return) { |
|
let returned = stat.value; |
|
if (!returned || returned.is_constant_expression()) { |
|
if (returned) { |
|
returned = returned.clone(true); |
|
} else { |
|
returned = make_void_0(self); |
|
} |
|
const args = self.args.concat(returned); |
|
return make_sequence(self, args).optimize(compressor); |
|
} |
|
|
|
// optimize identity function |
|
if ( |
|
fn.argnames.length === 1 |
|
&& (fn.argnames[0] instanceof AST_SymbolFunarg) |
|
&& self.args.length < 2 |
|
&& !(self.args[0] instanceof AST_Expansion) |
|
&& returned instanceof AST_SymbolRef |
|
&& returned.name === fn.argnames[0].name |
|
) { |
|
const replacement = |
|
(self.args[0] || make_void_0()).optimize(compressor); |
|
|
|
let parent; |
|
if ( |
|
replacement instanceof AST_PropAccess |
|
&& (parent = compressor.parent()) instanceof AST_Call |
|
&& parent.expression === self |
|
) { |
|
// identity function was being used to remove `this`, like in |
|
// |
|
// id(bag.no_this)(...) |
|
// |
|
// Replace with a larger but more effish (0, bag.no_this) wrapper. |
|
|
|
return make_sequence(self, [ |
|
make_node(AST_Number, self, { value: 0 }), |
|
replacement |
|
]); |
|
} |
|
// replace call with first argument or undefined if none passed |
|
return replacement; |
|
} |
|
} |
|
|
|
if (can_inline) { |
|
var scope, in_loop, level = -1; |
|
let def; |
|
let returned_value; |
|
let nearest_scope; |
|
if (simple_args |
|
&& !fn.uses_arguments |
|
&& !(compressor.parent() instanceof AST_Class) |
|
&& !(fn.name && fn instanceof AST_Function) |
|
&& (returned_value = can_flatten_body(stat)) |
|
&& (exp === fn |
|
|| has_annotation(self, _INLINE) |
|
|| compressor.option("unused") |
|
&& (def = exp.definition()).references.length == 1 |
|
&& !is_recursive_ref(compressor, def) |
|
&& fn.is_constant_expression(exp.scope)) |
|
&& !has_annotation(self, _PURE | _NOINLINE) |
|
&& !fn.contains_this() |
|
&& can_inject_symbols() |
|
&& (nearest_scope = compressor.find_scope()) |
|
&& !scope_encloses_variables_in_this_scope(nearest_scope, fn) |
|
&& !(function in_default_assign() { |
|
// Due to the fact function parameters have their own scope |
|
// which can't use `var something` in the function body within, |
|
// we simply don't inline into DefaultAssign. |
|
let i = 0; |
|
let p; |
|
while ((p = compressor.parent(i++))) { |
|
if (p instanceof AST_DefaultAssign) return true; |
|
if (p instanceof AST_Block) break; |
|
} |
|
return false; |
|
})() |
|
&& !(scope instanceof AST_Class) |
|
) { |
|
set_flag(fn, SQUEEZED); |
|
nearest_scope.add_child_scope(fn); |
|
return make_sequence(self, flatten_fn(returned_value)).optimize(compressor); |
|
} |
|
} |
|
|
|
if (can_inline && has_annotation(self, _INLINE)) { |
|
set_flag(fn, SQUEEZED); |
|
fn = make_node(fn.CTOR === AST_Defun ? AST_Function : fn.CTOR, fn, fn); |
|
fn = fn.clone(true); |
|
fn.figure_out_scope({}, { |
|
parent_scope: compressor.find_scope(), |
|
toplevel: compressor.get_toplevel() |
|
}); |
|
|
|
return make_node(AST_Call, self, { |
|
expression: fn, |
|
args: self.args, |
|
}).optimize(compressor); |
|
} |
|
|
|
const can_drop_this_call = is_regular_func && compressor.option("side_effects") && fn.body.every(is_empty); |
|
if (can_drop_this_call) { |
|
var args = self.args.concat(make_void_0(self)); |
|
return make_sequence(self, args).optimize(compressor); |
|
} |
|
|
|
if (compressor.option("negate_iife") |
|
&& compressor.parent() instanceof AST_SimpleStatement |
|
&& is_iife_call(self)) { |
|
return self.negate(compressor, true); |
|
} |
|
|
|
var ev = self.evaluate(compressor); |
|
if (ev !== self) { |
|
ev = make_node_from_constant(ev, self).optimize(compressor); |
|
return best_of(compressor, ev, self); |
|
} |
|
|
|
return self; |
|
|
|
function return_value(stat) { |
|
if (!stat) return make_void_0(self); |
|
if (stat instanceof AST_Return) { |
|
if (!stat.value) return make_void_0(self); |
|
return stat.value.clone(true); |
|
} |
|
if (stat instanceof AST_SimpleStatement) { |
|
return make_node(AST_UnaryPrefix, stat, { |
|
operator: "void", |
|
expression: stat.body.clone(true) |
|
}); |
|
} |
|
} |
|
|
|
function can_flatten_body(stat) { |
|
var body = fn.body; |
|
var len = body.length; |
|
if (compressor.option("inline") < 3) { |
|
return len == 1 && return_value(stat); |
|
} |
|
stat = null; |
|
for (var i = 0; i < len; i++) { |
|
var line = body[i]; |
|
if (line instanceof AST_Var) { |
|
if (stat && !line.definitions.every((var_def) => |
|
!var_def.value |
|
)) { |
|
return false; |
|
} |
|
} else if (stat) { |
|
return false; |
|
} else if (!(line instanceof AST_EmptyStatement)) { |
|
stat = line; |
|
} |
|
} |
|
return return_value(stat); |
|
} |
|
|
|
function can_inject_args(block_scoped, safe_to_inject) { |
|
for (var i = 0, len = fn.argnames.length; i < len; i++) { |
|
var arg = fn.argnames[i]; |
|
if (arg instanceof AST_DefaultAssign) { |
|
if (has_flag(arg.left, UNUSED)) continue; |
|
return false; |
|
} |
|
if (arg instanceof AST_Destructuring) return false; |
|
if (arg instanceof AST_Expansion) { |
|
if (has_flag(arg.expression, UNUSED)) continue; |
|
return false; |
|
} |
|
if (has_flag(arg, UNUSED)) continue; |
|
if (!safe_to_inject |
|
|| block_scoped.has(arg.name) |
|
|| identifier_atom.has(arg.name) |
|
|| scope.conflicting_def(arg.name)) { |
|
return false; |
|
} |
|
if (in_loop) in_loop.push(arg.definition()); |
|
} |
|
return true; |
|
} |
|
|
|
function can_inject_vars(block_scoped, safe_to_inject) { |
|
var len = fn.body.length; |
|
for (var i = 0; i < len; i++) { |
|
var stat = fn.body[i]; |
|
if (!(stat instanceof AST_Var)) continue; |
|
if (!safe_to_inject) return false; |
|
for (var j = stat.definitions.length; --j >= 0;) { |
|
var name = stat.definitions[j].name; |
|
if (name instanceof AST_Destructuring |
|
|| block_scoped.has(name.name) |
|
|| identifier_atom.has(name.name) |
|
|| scope.conflicting_def(name.name)) { |
|
return false; |
|
} |
|
if (in_loop) in_loop.push(name.definition()); |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
function can_inject_symbols() { |
|
var block_scoped = new Set(); |
|
do { |
|
scope = compressor.parent(++level); |
|
if (scope.is_block_scope() && scope.block_scope) { |
|
// TODO this is sometimes undefined during compression. |
|
// But it should always have a value! |
|
scope.block_scope.variables.forEach(function (variable) { |
|
block_scoped.add(variable.name); |
|
}); |
|
} |
|
if (scope instanceof AST_Catch) { |
|
// TODO can we delete? AST_Catch is a block scope. |
|
if (scope.argname) { |
|
block_scoped.add(scope.argname.name); |
|
} |
|
} else if (scope instanceof AST_IterationStatement) { |
|
in_loop = []; |
|
} else if (scope instanceof AST_SymbolRef) { |
|
if (scope.fixed_value() instanceof AST_Scope) return false; |
|
} |
|
} while (!(scope instanceof AST_Scope)); |
|
|
|
var safe_to_inject = !(scope instanceof AST_Toplevel) || compressor.toplevel.vars; |
|
var inline = compressor.option("inline"); |
|
if (!can_inject_vars(block_scoped, inline >= 3 && safe_to_inject)) return false; |
|
if (!can_inject_args(block_scoped, inline >= 2 && safe_to_inject)) return false; |
|
return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop); |
|
} |
|
|
|
function append_var(decls, expressions, name, value) { |
|
var def = name.definition(); |
|
|
|
// Name already exists, only when a function argument had the same name |
|
const already_appended = scope.variables.has(name.name); |
|
if (!already_appended) { |
|
scope.variables.set(name.name, def); |
|
scope.enclosed.push(def); |
|
decls.push(make_node(AST_VarDef, name, { |
|
name: name, |
|
value: null |
|
})); |
|
} |
|
|
|
var sym = make_node(AST_SymbolRef, name, name); |
|
def.references.push(sym); |
|
if (value) expressions.push(make_node(AST_Assign, self, { |
|
operator: "=", |
|
logical: false, |
|
left: sym, |
|
right: value.clone() |
|
})); |
|
} |
|
|
|
function flatten_args(decls, expressions) { |
|
var len = fn.argnames.length; |
|
for (var i = self.args.length; --i >= len;) { |
|
expressions.push(self.args[i]); |
|
} |
|
for (i = len; --i >= 0;) { |
|
var name = fn.argnames[i]; |
|
var value = self.args[i]; |
|
if (has_flag(name, UNUSED) || !name.name || scope.conflicting_def(name.name)) { |
|
if (value) expressions.push(value); |
|
} else { |
|
var symbol = make_node(AST_SymbolVar, name, name); |
|
name.definition().orig.push(symbol); |
|
if (!value && in_loop) value = make_void_0(self); |
|
append_var(decls, expressions, symbol, value); |
|
} |
|
} |
|
decls.reverse(); |
|
expressions.reverse(); |
|
} |
|
|
|
function flatten_vars(decls, expressions) { |
|
var pos = expressions.length; |
|
for (var i = 0, lines = fn.body.length; i < lines; i++) { |
|
var stat = fn.body[i]; |
|
if (!(stat instanceof AST_Var)) continue; |
|
for (var j = 0, defs = stat.definitions.length; j < defs; j++) { |
|
var var_def = stat.definitions[j]; |
|
var name = var_def.name; |
|
append_var(decls, expressions, name, var_def.value); |
|
if (in_loop && fn.argnames.every((argname) => |
|
argname.name != name.name |
|
)) { |
|
var def = fn.variables.get(name.name); |
|
var sym = make_node(AST_SymbolRef, name, name); |
|
def.references.push(sym); |
|
expressions.splice(pos++, 0, make_node(AST_Assign, var_def, { |
|
operator: "=", |
|
logical: false, |
|
left: sym, |
|
right: make_void_0(name), |
|
})); |
|
} |
|
} |
|
} |
|
} |
|
|
|
function flatten_fn(returned_value) { |
|
var decls = []; |
|
var expressions = []; |
|
flatten_args(decls, expressions); |
|
flatten_vars(decls, expressions); |
|
expressions.push(returned_value); |
|
|
|
if (decls.length) { |
|
const i = scope.body.indexOf(compressor.parent(level - 1)) + 1; |
|
scope.body.splice(i, 0, make_node(AST_Var, fn, { |
|
definitions: decls |
|
})); |
|
} |
|
|
|
return expressions.map(exp => exp.clone(true)); |
|
} |
|
} |
|
|
|
/** prevent inlining functions into loops, for performance reasons */ |
|
function dont_inline_lambda_in_loop(compressor, maybe_lambda) { |
|
return ( |
|
(maybe_lambda instanceof AST_Lambda || maybe_lambda instanceof AST_Class) |
|
&& !!compressor.is_within_loop() |
|
); |
|
} |
|
|
|
(function(def_find_defs) { |
|
function to_node(value, orig) { |
|
if (value instanceof AST_Node) { |
|
if (!(value instanceof AST_Constant)) { |
|
// Value may be a function, an array including functions and even a complex assign / block expression, |
|
// so it should never be shared in different places. |
|
// Otherwise wrong information may be used in the compression phase |
|
value = value.clone(true); |
|
} |
|
return make_node(value.CTOR, orig, value); |
|
} |
|
if (Array.isArray(value)) return make_node(AST_Array, orig, { |
|
elements: value.map(function(value) { |
|
return to_node(value, orig); |
|
}) |
|
}); |
|
if (value && typeof value == "object") { |
|
var props = []; |
|
for (var key in value) if (HOP(value, key)) { |
|
props.push(make_node(AST_ObjectKeyVal, orig, { |
|
key: key, |
|
value: to_node(value[key], orig) |
|
})); |
|
} |
|
return make_node(AST_Object, orig, { |
|
properties: props |
|
}); |
|
} |
|
return make_node_from_constant(value, orig); |
|
} |
|
|
|
AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) { |
|
if (!compressor.option("global_defs")) return this; |
|
this.figure_out_scope({ ie8: compressor.option("ie8") }); |
|
return this.transform(new TreeTransformer(function(node) { |
|
var def = node._find_defs(compressor, ""); |
|
if (!def) return; |
|
var level = 0, child = node, parent; |
|
while (parent = this.parent(level++)) { |
|
if (!(parent instanceof AST_PropAccess)) break; |
|
if (parent.expression !== child) break; |
|
child = parent; |
|
} |
|
if (is_lhs(child, parent)) { |
|
return; |
|
} |
|
return def; |
|
})); |
|
}); |
|
def_find_defs(AST_Node, noop); |
|
def_find_defs(AST_Chain, function(compressor, suffix) { |
|
return this.expression._find_defs(compressor, suffix); |
|
}); |
|
def_find_defs(AST_Dot, function(compressor, suffix) { |
|
return this.expression._find_defs(compressor, "." + this.property + suffix); |
|
}); |
|
def_find_defs(AST_SymbolDeclaration, function() { |
|
if (!this.global()) return; |
|
}); |
|
def_find_defs(AST_SymbolRef, function(compressor, suffix) { |
|
if (!this.global()) return; |
|
var defines = compressor.option("global_defs"); |
|
var name = this.name + suffix; |
|
if (HOP(defines, name)) return to_node(defines[name], this); |
|
}); |
|
def_find_defs(AST_ImportMeta, function(compressor, suffix) { |
|
var defines = compressor.option("global_defs"); |
|
var name = "import.meta" + suffix; |
|
if (HOP(defines, name)) return to_node(defines[name], this); |
|
}); |
|
})(function(node, func) { |
|
node.DEFMETHOD("_find_defs", func); |
|
}); |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
class Compressor extends TreeWalker { |
|
constructor(options, { false_by_default = false, mangle_options = false }) { |
|
super(); |
|
if (options.defaults !== undefined && !options.defaults) false_by_default = true; |
|
this.options = defaults(options, { |
|
arguments : false, |
|
arrows : !false_by_default, |
|
booleans : !false_by_default, |
|
booleans_as_integers : false, |
|
collapse_vars : !false_by_default, |
|
comparisons : !false_by_default, |
|
computed_props: !false_by_default, |
|
conditionals : !false_by_default, |
|
dead_code : !false_by_default, |
|
defaults : true, |
|
directives : !false_by_default, |
|
drop_console : false, |
|
drop_debugger : !false_by_default, |
|
ecma : 5, |
|
builtins_ecma : 5, |
|
builtins_pure : false, |
|
evaluate : !false_by_default, |
|
expression : false, |
|
global_defs : false, |
|
hoist_funs : false, |
|
hoist_props : !false_by_default, |
|
hoist_vars : false, |
|
ie8 : false, |
|
if_return : !false_by_default, |
|
inline : !false_by_default, |
|
join_vars : !false_by_default, |
|
keep_classnames: false, |
|
keep_fargs : true, |
|
keep_fnames : false, |
|
keep_infinity : false, |
|
lhs_constants : !false_by_default, |
|
loops : !false_by_default, |
|
module : false, |
|
negate_iife : !false_by_default, |
|
passes : 1, |
|
properties : !false_by_default, |
|
pure_getters : !false_by_default && "strict", |
|
pure_funcs : null, |
|
pure_new : false, |
|
reduce_funcs : !false_by_default, |
|
reduce_vars : !false_by_default, |
|
sequences : !false_by_default, |
|
side_effects : !false_by_default, |
|
switches : !false_by_default, |
|
top_retain : null, |
|
toplevel : !!(options && options["top_retain"]), |
|
typeofs : !false_by_default, |
|
unsafe : false, |
|
unsafe_arrows : false, |
|
unsafe_comps : false, |
|
unsafe_Function: false, |
|
unsafe_math : false, |
|
unsafe_symbols: false, |
|
unsafe_methods: false, |
|
unsafe_proto : false, |
|
unsafe_regexp : false, |
|
unsafe_undefined: false, |
|
unused : !false_by_default, |
|
warnings : false // legacy |
|
}, true); |
|
var global_defs = this.options["global_defs"]; |
|
if (typeof global_defs == "object") for (var key in global_defs) { |
|
if (key[0] === "@" && HOP(global_defs, key)) { |
|
global_defs[key.slice(1)] = parse(global_defs[key], { |
|
expression: true |
|
}); |
|
} |
|
} |
|
if (this.options["inline"] === true) this.options["inline"] = 3; |
|
var pure_funcs = this.options["pure_funcs"]; |
|
if (typeof pure_funcs == "function") { |
|
this.pure_funcs = pure_funcs; |
|
} else { |
|
this.pure_funcs = pure_funcs ? function(node) { |
|
return !pure_funcs.includes(node.expression.print_to_string()); |
|
} : return_true; |
|
} |
|
var top_retain = this.options["top_retain"]; |
|
if (top_retain instanceof RegExp) { |
|
this.top_retain = function(def) { |
|
return top_retain.test(def.name); |
|
}; |
|
} else if (typeof top_retain == "function") { |
|
this.top_retain = top_retain; |
|
} else if (top_retain) { |
|
if (typeof top_retain == "string") { |
|
top_retain = top_retain.split(/,/); |
|
} |
|
this.top_retain = function(def) { |
|
return top_retain.includes(def.name); |
|
}; |
|
} |
|
if (this.options["module"]) { |
|
this.directives["use strict"] = true; |
|
this.options["toplevel"] = true; |
|
} |
|
var toplevel = this.options["toplevel"]; |
|
this.toplevel = typeof toplevel == "string" ? { |
|
funcs: /funcs/.test(toplevel), |
|
vars: /vars/.test(toplevel) |
|
} : { |
|
funcs: toplevel, |
|
vars: toplevel |
|
}; |
|
var sequences = this.options["sequences"]; |
|
this.sequences_limit = sequences == 1 ? 800 : sequences | 0; |
|
this.evaluated_regexps = new Map(); |
|
this._toplevel = undefined; |
|
this._mangle_options = mangle_options |
|
? format_mangler_options(mangle_options) |
|
: mangle_options; |
|
|
|
this.pure_access_globals = pure_access_globals(this); |
|
this.is_pure_native_fn = is_pure_native_fn(this); |
|
this.is_pure_native_method = is_pure_native_method(this); |
|
this.is_pure_native_static_fn = is_pure_native_static_fn(this); |
|
this.is_pure_native_static_property = is_pure_native_static_property(this); |
|
} |
|
|
|
mangle_options() { |
|
var nth_identifier = this._mangle_options && this._mangle_options.nth_identifier || base54; |
|
var module = this._mangle_options && this._mangle_options.module || this.option("module"); |
|
return { ie8: this.option("ie8"), nth_identifier, module }; |
|
} |
|
|
|
option(key) { |
|
return this.options[key]; |
|
} |
|
|
|
exposed(def) { |
|
if (def.export) return true; |
|
if (def.global) for (var i = 0, len = def.orig.length; i < len; i++) |
|
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"]) |
|
return true; |
|
return false; |
|
} |
|
|
|
in_boolean_context() { |
|
if (!this.option("booleans")) return false; |
|
var self = this.self(); |
|
for (var i = 0, p; p = this.parent(i); i++) { |
|
if (p instanceof AST_SimpleStatement |
|
|| p instanceof AST_Conditional && p.condition === self |
|
|| p instanceof AST_DWLoop && p.condition === self |
|
|| p instanceof AST_For && p.condition === self |
|
|| p instanceof AST_If && p.condition === self |
|
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) { |
|
return true; |
|
} |
|
if ( |
|
p instanceof AST_Binary |
|
&& ( |
|
p.operator == "&&" |
|
|| p.operator == "||" |
|
|| p.operator == "??" |
|
) |
|
|| p instanceof AST_Conditional |
|
|| p.tail_node() === self |
|
) { |
|
self = p; |
|
} else { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
/** True if compressor.self()'s result will be turned into a 32-bit integer. |
|
* ex: |
|
* ~{expr} |
|
* (1, 2, {expr}) | 0 |
|
**/ |
|
in_32_bit_context(other_operand_must_be_number) { |
|
if (!this.option("evaluate")) return false; |
|
var self = this.self(); |
|
for (var i = 0, p; p = this.parent(i); i++) { |
|
if (p instanceof AST_Binary && bitwise_binop.has(p.operator)) { |
|
if (other_operand_must_be_number) { |
|
return (self === p.left ? p.right : p.left).is_number(this); |
|
} else { |
|
return true; |
|
} |
|
} |
|
if (p instanceof AST_UnaryPrefix) { |
|
return p.operator === "~"; |
|
} |
|
if ( |
|
p instanceof AST_Binary |
|
&& ( |
|
// Don't talk about p.left. Can change branch taken |
|
p.operator == "&&" && p.right === self |
|
|| p.operator == "||" && p.right === self |
|
|| p.operator == "??" && p.right === self |
|
) |
|
|| p instanceof AST_Conditional && p.condition !== self |
|
|| p.tail_node() === self |
|
) { |
|
self = p; |
|
} else { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
in_computed_key() { |
|
if (!this.option("evaluate")) return false; |
|
var self = this.self(); |
|
for (var i = 0, p; p = this.parent(i); i++) { |
|
if (p instanceof AST_ObjectProperty && p.key === self) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
get_toplevel() { |
|
return this._toplevel; |
|
} |
|
|
|
compress(toplevel) { |
|
toplevel = toplevel.resolve_defines(this); |
|
this._toplevel = toplevel; |
|
if (this.option("expression")) { |
|
this._toplevel.process_expression(true); |
|
} |
|
var passes = +this.options.passes || 1; |
|
var min_count = 1 / 0; |
|
var stopping = false; |
|
var mangle = this.mangle_options(); |
|
for (var pass = 0; pass < passes; pass++) { |
|
this._toplevel.figure_out_scope(mangle); |
|
if (pass === 0 && this.option("drop_console")) { |
|
// must be run before reduce_vars and compress pass |
|
this._toplevel = this._toplevel.drop_console(this.option("drop_console")); |
|
} |
|
if (pass > 0 || this.option("reduce_vars")) { |
|
this._toplevel.reset_opt_flags(this); |
|
} |
|
this._toplevel = this._toplevel.transform(this); |
|
if (passes > 1) { |
|
let count = 0; |
|
walk(this._toplevel, () => { count++; }); |
|
if (count < min_count) { |
|
min_count = count; |
|
stopping = false; |
|
} else if (stopping) { |
|
break; |
|
} else { |
|
stopping = true; |
|
} |
|
} |
|
} |
|
if (this.option("expression")) { |
|
this._toplevel.process_expression(false); |
|
} |
|
toplevel = this._toplevel; |
|
this._toplevel = undefined; |
|
return toplevel; |
|
} |
|
|
|
before(node, descend) { |
|
if (has_flag(node, SQUEEZED)) return node; |
|
var was_scope = false; |
|
if (node instanceof AST_Scope) { |
|
node = node.hoist_properties(this); |
|
node = node.hoist_declarations(this); |
|
was_scope = true; |
|
} |
|
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize() |
|
// would call AST_Node.transform() if a different instance of AST_Node is |
|
// produced after def_optimize(). |
|
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction. |
|
// Migrate and defer all children's AST_Node.transform() to below, which |
|
// will now happen after this parent AST_Node has been properly substituted |
|
// thus gives a consistent AST snapshot. |
|
descend(node, this); |
|
// Existing code relies on how AST_Node.optimize() worked, and omitting the |
|
// following replacement call would result in degraded efficiency of both |
|
// output and performance. |
|
descend(node, this); |
|
var opt = node.optimize(this); |
|
if (was_scope && opt instanceof AST_Scope) { |
|
opt.drop_unused(this); |
|
descend(opt, this); |
|
} |
|
if (opt === node) set_flag(opt, SQUEEZED); |
|
return opt; |
|
} |
|
|
|
/** Alternative to plain is_lhs() which doesn't work within .optimize() */ |
|
is_lhs() { |
|
const self = this.stack[this.stack.length - 1]; |
|
const parent = this.stack[this.stack.length - 2]; |
|
return is_lhs(self, parent); |
|
} |
|
} |
|
|
|
|
|
function def_optimize(node, optimizer) { |
|
node.DEFMETHOD("optimize", function(compressor) { |
|
var self = this; |
|
if (has_flag(self, OPTIMIZED)) return self; |
|
if (compressor.has_directive("use asm")) return self; |
|
var opt = optimizer(self, compressor); |
|
set_flag(opt, OPTIMIZED); |
|
return opt; |
|
}); |
|
} |
|
|
|
def_optimize(AST_Node, function(self) { |
|
return self; |
|
}); |
|
|
|
AST_Toplevel.DEFMETHOD("drop_console", function(options) { |
|
const isArray = Array.isArray(options); |
|
const tt = new TreeTransformer(function(self) { |
|
if (self.TYPE !== "Call") { |
|
return; |
|
} |
|
|
|
var exp = self.expression; |
|
|
|
if (!(exp instanceof AST_PropAccess)) { |
|
return; |
|
} |
|
|
|
var name = exp.expression; |
|
var property = exp.property; |
|
var depth = 2; |
|
while (name.expression) { |
|
property = name.property; |
|
name = name.expression; |
|
depth++; |
|
} |
|
|
|
if (isArray && !options.includes(property)) { |
|
return; |
|
} |
|
|
|
if (is_undeclared_ref(name) && name.name == "console") { |
|
if ( |
|
depth === 3 |
|
&& !["call", "apply"].includes(exp.property) |
|
&& is_used_in_expression(tt) |
|
) { |
|
// a (used) call to Function.prototype methods (eg: console.log.bind(console)) |
|
// but not .call and .apply which would also return undefined. |
|
exp.expression = make_empty_function(self); |
|
set_flag(exp.expression, SQUEEZED); |
|
self.args = []; |
|
} else { |
|
return make_void_0(self); |
|
} |
|
} |
|
}); |
|
|
|
return this.transform(tt); |
|
}); |
|
|
|
AST_Node.DEFMETHOD("equivalent_to", function(node) { |
|
return equivalent_to(this, node); |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("process_expression", function(insert, compressor) { |
|
var self = this; |
|
var tt = new TreeTransformer(function(node) { |
|
if (insert && node instanceof AST_SimpleStatement) { |
|
return make_node(AST_Return, node, { |
|
value: node.body |
|
}); |
|
} |
|
if (!insert && node instanceof AST_Return) { |
|
if (compressor) { |
|
var value = node.value && node.value.drop_side_effect_free(compressor, true); |
|
return value |
|
? make_node(AST_SimpleStatement, node, { body: value }) |
|
: make_node(AST_EmptyStatement, node); |
|
} |
|
return make_node(AST_SimpleStatement, node, { |
|
body: node.value || make_void_0(node) |
|
}); |
|
} |
|
if (node instanceof AST_Class || node instanceof AST_Lambda && node !== self) { |
|
return node; |
|
} |
|
if (node instanceof AST_Block) { |
|
var index = node.body.length - 1; |
|
if (index >= 0) { |
|
node.body[index] = node.body[index].transform(tt); |
|
} |
|
} else if (node instanceof AST_If) { |
|
node.body = node.body.transform(tt); |
|
if (node.alternative) { |
|
node.alternative = node.alternative.transform(tt); |
|
} |
|
} else if (node instanceof AST_With) { |
|
node.body = node.body.transform(tt); |
|
} |
|
return node; |
|
}); |
|
self.transform(tt); |
|
}); |
|
|
|
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) { |
|
const self = this; |
|
const reduce_vars = compressor.option("reduce_vars"); |
|
|
|
const preparation = new TreeWalker(function(node, descend) { |
|
clear_flag(node, CLEAR_BETWEEN_PASSES); |
|
if (reduce_vars) { |
|
if (compressor.top_retain |
|
&& node instanceof AST_Defun // Only functions are retained |
|
&& preparation.parent() === self |
|
) { |
|
set_flag(node, TOP); |
|
} |
|
return node.reduce_vars(preparation, descend, compressor); |
|
} |
|
}); |
|
// Stack of look-up tables to keep track of whether a `SymbolDef` has been |
|
// properly assigned before use: |
|
// - `push()` & `pop()` when visiting conditional branches |
|
preparation.safe_ids = Object.create(null); |
|
preparation.in_loop = null; |
|
preparation.loop_ids = new Map(); |
|
preparation.defs_to_safe_ids = new Map(); |
|
self.walk(preparation); |
|
}); |
|
|
|
AST_Symbol.DEFMETHOD("fixed_value", function() { |
|
var fixed = this.thedef.fixed; |
|
if (!fixed || fixed instanceof AST_Node) return fixed; |
|
return fixed(); |
|
}); |
|
|
|
AST_SymbolRef.DEFMETHOD("is_immutable", function() { |
|
var orig = this.definition().orig; |
|
return orig.length == 1 && orig[0] instanceof AST_SymbolLambda; |
|
}); |
|
|
|
function find_variable(compressor, name) { |
|
var scope, i = 0; |
|
while (scope = compressor.parent(i++)) { |
|
if (scope instanceof AST_Scope) break; |
|
if (scope instanceof AST_Catch && scope.argname) { |
|
scope = scope.argname.definition().scope; |
|
break; |
|
} |
|
} |
|
return scope.find_variable(name); |
|
} |
|
|
|
AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { |
|
return !this.definition().undeclared |
|
|| (compressor.option("unsafe") || compressor.option("builtins_pure")) && compressor.pure_access_globals(this.name); |
|
}); |
|
|
|
/* -----[ optimizers ]----- */ |
|
|
|
var directives = new Set(["use asm", "use strict"]); |
|
def_optimize(AST_Directive, function(self, compressor) { |
|
if (compressor.option("directives") |
|
&& (!directives.has(self.value) || compressor.has_directive(self.value) !== self)) { |
|
return make_node(AST_EmptyStatement, self); |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Debugger, function(self, compressor) { |
|
if (compressor.option("drop_debugger")) |
|
return make_node(AST_EmptyStatement, self); |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_LabeledStatement, function(self, compressor) { |
|
if (self.body instanceof AST_Break |
|
&& compressor.loopcontrol_target(self.body) === self.body) { |
|
return make_node(AST_EmptyStatement, self); |
|
} |
|
return self.label.references.length == 0 ? self.body : self; |
|
}); |
|
|
|
def_optimize(AST_Block, function(self, compressor) { |
|
tighten_body(self.body, compressor); |
|
return self; |
|
}); |
|
|
|
function can_be_extracted_from_if_block(node) { |
|
return !( |
|
node instanceof AST_Const |
|
|| node instanceof AST_Let |
|
|| node instanceof AST_Using |
|
|| node instanceof AST_Class |
|
); |
|
} |
|
|
|
def_optimize(AST_BlockStatement, function(self, compressor) { |
|
tighten_body(self.body, compressor); |
|
switch (self.body.length) { |
|
case 1: |
|
if (!compressor.has_directive("use strict") |
|
&& compressor.parent() instanceof AST_If |
|
&& can_be_extracted_from_if_block(self.body[0]) |
|
|| can_be_evicted_from_block(self.body[0])) { |
|
return self.body[0]; |
|
} |
|
break; |
|
case 0: return make_node(AST_EmptyStatement, self); |
|
} |
|
return self; |
|
}); |
|
|
|
function opt_AST_Lambda(self, compressor) { |
|
tighten_body(self.body, compressor); |
|
if (compressor.option("side_effects") |
|
&& self.body.length == 1 |
|
&& self.body[0] === compressor.has_directive("use strict")) { |
|
self.body.length = 0; |
|
} |
|
return self; |
|
} |
|
def_optimize(AST_Lambda, opt_AST_Lambda); |
|
|
|
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) { |
|
var self = this; |
|
if (compressor.has_directive("use asm")) return self; |
|
|
|
var hoist_funs = compressor.option("hoist_funs"); |
|
var hoist_vars = compressor.option("hoist_vars"); |
|
|
|
if (hoist_funs || hoist_vars) { |
|
var dirs = []; |
|
var hoisted = []; |
|
var vars = new Map(), vars_found = 0, var_decl = 0; |
|
// let's count var_decl first, we seem to waste a lot of |
|
// space if we hoist `var` when there's only one. |
|
walk(self, node => { |
|
if (node instanceof AST_Scope && node !== self) |
|
return true; |
|
if (node instanceof AST_Var) { |
|
++var_decl; |
|
return true; |
|
} |
|
}); |
|
hoist_vars = hoist_vars && var_decl > 1; |
|
var tt = new TreeTransformer( |
|
function before(node) { |
|
if (node !== self) { |
|
if (node instanceof AST_Directive) { |
|
dirs.push(node); |
|
return make_node(AST_EmptyStatement, node); |
|
} |
|
if (hoist_funs && node instanceof AST_Defun |
|
&& !(tt.parent() instanceof AST_Export) |
|
&& tt.parent() === self) { |
|
hoisted.push(node); |
|
return make_node(AST_EmptyStatement, node); |
|
} |
|
if ( |
|
hoist_vars |
|
&& node instanceof AST_Var |
|
&& !node.definitions.some(def => def.name instanceof AST_Destructuring) |
|
) { |
|
node.definitions.forEach(function(def) { |
|
vars.set(def.name.name, def); |
|
++vars_found; |
|
}); |
|
var seq = node.to_assignments(compressor); |
|
var p = tt.parent(); |
|
if (p instanceof AST_ForIn && p.init === node) { |
|
if (seq == null) { |
|
var def = node.definitions[0].name; |
|
return make_node(AST_SymbolRef, def, def); |
|
} |
|
return seq; |
|
} |
|
if (p instanceof AST_For && p.init === node) { |
|
return seq; |
|
} |
|
if (!seq) return make_node(AST_EmptyStatement, node); |
|
return make_node(AST_SimpleStatement, node, { |
|
body: seq |
|
}); |
|
} |
|
if (node instanceof AST_Scope) |
|
return node; // to avoid descending in nested scopes |
|
} |
|
} |
|
); |
|
self = self.transform(tt); |
|
if (vars_found > 0) { |
|
// collect only vars which don't show up in self's arguments list |
|
var defs = []; |
|
const is_lambda = self instanceof AST_Lambda; |
|
const args_as_names = is_lambda ? self.args_as_names() : null; |
|
vars.forEach((def, name) => { |
|
if (is_lambda && args_as_names.some((x) => x.name === def.name.name)) { |
|
vars.delete(name); |
|
} else { |
|
def = def.clone(); |
|
def.value = null; |
|
defs.push(def); |
|
vars.set(name, def); |
|
} |
|
}); |
|
if (defs.length > 0) { |
|
// try to merge in assignments |
|
for (var i = 0; i < self.body.length;) { |
|
if (self.body[i] instanceof AST_SimpleStatement) { |
|
var expr = self.body[i].body, sym, assign; |
|
if (expr instanceof AST_Assign |
|
&& expr.operator == "=" |
|
&& (sym = expr.left) instanceof AST_Symbol |
|
&& vars.has(sym.name) |
|
) { |
|
var def = vars.get(sym.name); |
|
if (def.value) break; |
|
def.value = expr.right; |
|
remove(defs, def); |
|
defs.push(def); |
|
self.body.splice(i, 1); |
|
continue; |
|
} |
|
if (expr instanceof AST_Sequence |
|
&& (assign = expr.expressions[0]) instanceof AST_Assign |
|
&& assign.operator == "=" |
|
&& (sym = assign.left) instanceof AST_Symbol |
|
&& vars.has(sym.name) |
|
) { |
|
var def = vars.get(sym.name); |
|
if (def.value) break; |
|
def.value = assign.right; |
|
remove(defs, def); |
|
defs.push(def); |
|
self.body[i].body = make_sequence(expr, expr.expressions.slice(1)); |
|
continue; |
|
} |
|
} |
|
if (self.body[i] instanceof AST_EmptyStatement) { |
|
self.body.splice(i, 1); |
|
continue; |
|
} |
|
if (self.body[i] instanceof AST_BlockStatement) { |
|
self.body.splice(i, 1, ...self.body[i].body); |
|
continue; |
|
} |
|
break; |
|
} |
|
defs = make_node(AST_Var, self, { |
|
definitions: defs |
|
}); |
|
hoisted.push(defs); |
|
} |
|
} |
|
self.body = dirs.concat(hoisted, self.body); |
|
} |
|
return self; |
|
}); |
|
|
|
AST_Scope.DEFMETHOD("hoist_properties", function(compressor) { |
|
var self = this; |
|
if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self; |
|
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; |
|
var defs_by_id = new Map(); |
|
var hoister = new TreeTransformer(function(node, descend) { |
|
if (node instanceof AST_VarDef) { |
|
const sym = node.name; |
|
let def; |
|
let value; |
|
if (sym.scope === self |
|
&& !(sym instanceof AST_SymbolUsing) |
|
&& (def = sym.definition()).escaped != 1 |
|
&& !def.assignments |
|
&& !def.direct_access |
|
&& !def.single_use |
|
&& !compressor.exposed(def) |
|
&& !top_retain(def) |
|
&& (value = sym.fixed_value()) === node.value |
|
&& value instanceof AST_Object |
|
&& !value.properties.some(prop => |
|
prop instanceof AST_Expansion || prop.computed_key() |
|
) |
|
) { |
|
descend(node, this); |
|
const defs = new Map(); |
|
const assignments = []; |
|
value.properties.forEach(({ key, value }) => { |
|
const scope = hoister.find_scope(); |
|
const symbol = self.create_symbol(sym.CTOR, { |
|
source: sym, |
|
scope, |
|
conflict_scopes: new Set([ |
|
scope, |
|
...sym.definition().references.map(ref => ref.scope) |
|
]), |
|
tentative_name: sym.name + "_" + key |
|
}); |
|
|
|
defs.set(String(key), symbol.definition()); |
|
|
|
assignments.push(make_node(AST_VarDef, node, { |
|
name: symbol, |
|
value |
|
})); |
|
}); |
|
defs_by_id.set(def.id, defs); |
|
return MAP.splice(assignments); |
|
} |
|
} else if (node instanceof AST_PropAccess |
|
&& node.expression instanceof AST_SymbolRef |
|
) { |
|
const defs = defs_by_id.get(node.expression.definition().id); |
|
if (defs) { |
|
const def = defs.get(String(get_simple_key(node.property))); |
|
const sym = make_node(AST_SymbolRef, node, { |
|
name: def.name, |
|
scope: node.expression.scope, |
|
thedef: def |
|
}); |
|
sym.reference({}); |
|
return sym; |
|
} |
|
} |
|
}); |
|
return self.transform(hoister); |
|
}); |
|
|
|
def_optimize(AST_SimpleStatement, function(self, compressor) { |
|
if (compressor.option("side_effects")) { |
|
var body = self.body; |
|
var node = body.drop_side_effect_free(compressor, true); |
|
if (!node) { |
|
return make_node(AST_EmptyStatement, self); |
|
} |
|
if (node !== body) { |
|
return make_node(AST_SimpleStatement, self, { body: node }); |
|
} |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_While, function(self, compressor) { |
|
return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self; |
|
}); |
|
|
|
def_optimize(AST_Do, function(self, compressor) { |
|
if (!compressor.option("loops")) return self; |
|
var cond = self.condition.tail_node().evaluate(compressor); |
|
if (!(cond instanceof AST_Node)) { |
|
if (cond) return make_node(AST_For, self, { |
|
body: make_node(AST_BlockStatement, self.body, { |
|
body: [ |
|
self.body, |
|
make_node(AST_SimpleStatement, self.condition, { |
|
body: self.condition |
|
}) |
|
] |
|
}) |
|
}).optimize(compressor); |
|
if (!has_break_or_continue(self, compressor.parent())) { |
|
return make_node(AST_BlockStatement, self.body, { |
|
body: [ |
|
self.body, |
|
make_node(AST_SimpleStatement, self.condition, { |
|
body: self.condition |
|
}) |
|
] |
|
}).optimize(compressor); |
|
} |
|
} |
|
return self; |
|
}); |
|
|
|
function if_break_in_loop(self, compressor) { |
|
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body; |
|
if (compressor.option("dead_code") && is_break(first)) { |
|
var body = []; |
|
if (self.init instanceof AST_Statement) { |
|
body.push(self.init); |
|
} else if (self.init) { |
|
body.push(make_node(AST_SimpleStatement, self.init, { |
|
body: self.init |
|
})); |
|
} |
|
if (self.condition) { |
|
body.push(make_node(AST_SimpleStatement, self.condition, { |
|
body: self.condition |
|
})); |
|
} |
|
extract_from_unreachable_code(compressor, self.body, body); |
|
return make_node(AST_BlockStatement, self, { |
|
body: body |
|
}); |
|
} |
|
if (first instanceof AST_If) { |
|
if (is_break(first.body)) { |
|
if (self.condition) { |
|
self.condition = make_node(AST_Binary, self.condition, { |
|
left: self.condition, |
|
operator: "&&", |
|
right: first.condition.negate(compressor), |
|
}); |
|
} else { |
|
self.condition = first.condition.negate(compressor); |
|
} |
|
drop_it(first.alternative); |
|
} else if (is_break(first.alternative)) { |
|
if (self.condition) { |
|
self.condition = make_node(AST_Binary, self.condition, { |
|
left: self.condition, |
|
operator: "&&", |
|
right: first.condition, |
|
}); |
|
} else { |
|
self.condition = first.condition; |
|
} |
|
drop_it(first.body); |
|
} |
|
} |
|
return self; |
|
|
|
function is_break(node) { |
|
return node instanceof AST_Break |
|
&& compressor.loopcontrol_target(node) === compressor.self(); |
|
} |
|
|
|
function drop_it(rest) { |
|
rest = as_statement_array(rest); |
|
if (self.body instanceof AST_BlockStatement) { |
|
self.body = self.body.clone(); |
|
self.body.body = rest.concat(self.body.body.slice(1)); |
|
self.body = self.body.transform(compressor); |
|
} else { |
|
self.body = make_node(AST_BlockStatement, self.body, { |
|
body: rest |
|
}).transform(compressor); |
|
} |
|
self = if_break_in_loop(self, compressor); |
|
} |
|
} |
|
|
|
def_optimize(AST_For, function(self, compressor) { |
|
if (!compressor.option("loops")) return self; |
|
if (compressor.option("side_effects") && self.init) { |
|
self.init = self.init.drop_side_effect_free(compressor); |
|
} |
|
if (self.condition) { |
|
var cond = self.condition.evaluate(compressor); |
|
if (!(cond instanceof AST_Node)) { |
|
if (cond) self.condition = null; |
|
else if (!compressor.option("dead_code")) { |
|
var orig = self.condition; |
|
self.condition = make_node_from_constant(cond, self.condition); |
|
self.condition = best_of_expression(self.condition.transform(compressor), orig); |
|
} |
|
} |
|
if (compressor.option("dead_code")) { |
|
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor); |
|
if (!cond) { |
|
var body = []; |
|
extract_from_unreachable_code(compressor, self.body, body); |
|
if (self.init instanceof AST_Statement) { |
|
body.push(self.init); |
|
} else if (self.init) { |
|
body.push(make_node(AST_SimpleStatement, self.init, { |
|
body: self.init |
|
})); |
|
} |
|
body.push(make_node(AST_SimpleStatement, self.condition, { |
|
body: self.condition |
|
})); |
|
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); |
|
} |
|
} |
|
} |
|
return if_break_in_loop(self, compressor); |
|
}); |
|
|
|
def_optimize(AST_If, function(self, compressor) { |
|
if (is_empty(self.alternative)) self.alternative = null; |
|
|
|
if (!compressor.option("conditionals")) return self; |
|
// if condition can be statically determined, drop |
|
// one of the blocks. note, statically determined implies |
|
// “has no side effects”; also it doesn't work for cases like |
|
// `x && true`, though it probably should. |
|
var cond = self.condition.evaluate(compressor); |
|
if (!compressor.option("dead_code") && !(cond instanceof AST_Node)) { |
|
var orig = self.condition; |
|
self.condition = make_node_from_constant(cond, orig); |
|
self.condition = best_of_expression(self.condition.transform(compressor), orig); |
|
} |
|
if (compressor.option("dead_code")) { |
|
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor); |
|
if (!cond) { |
|
var body = []; |
|
extract_from_unreachable_code(compressor, self.body, body); |
|
body.push(make_node(AST_SimpleStatement, self.condition, { |
|
body: self.condition |
|
})); |
|
if (self.alternative) body.push(self.alternative); |
|
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); |
|
} else if (!(cond instanceof AST_Node)) { |
|
var body = []; |
|
body.push(make_node(AST_SimpleStatement, self.condition, { |
|
body: self.condition |
|
})); |
|
body.push(self.body); |
|
if (self.alternative) { |
|
extract_from_unreachable_code(compressor, self.alternative, body); |
|
} |
|
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); |
|
} |
|
} |
|
var negated = self.condition.negate(compressor); |
|
var self_condition_length = self.condition.size(); |
|
var negated_length = negated.size(); |
|
var negated_is_best = negated_length < self_condition_length; |
|
if (self.alternative && negated_is_best) { |
|
negated_is_best = false; // because we already do the switch here. |
|
// no need to swap values of self_condition_length and negated_length |
|
// here because they are only used in an equality comparison later on. |
|
self.condition = negated; |
|
var tmp = self.body; |
|
self.body = self.alternative || make_node(AST_EmptyStatement, self); |
|
self.alternative = tmp; |
|
} |
|
if (is_empty(self.body) && is_empty(self.alternative)) { |
|
return make_node(AST_SimpleStatement, self.condition, { |
|
body: self.condition.clone() |
|
}).optimize(compressor); |
|
} |
|
if (self.body instanceof AST_SimpleStatement |
|
&& self.alternative instanceof AST_SimpleStatement) { |
|
return make_node(AST_SimpleStatement, self, { |
|
body: make_node(AST_Conditional, self, { |
|
condition : self.condition, |
|
consequent : self.body.body, |
|
alternative : self.alternative.body |
|
}) |
|
}).optimize(compressor); |
|
} |
|
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) { |
|
if (self_condition_length === negated_length && !negated_is_best |
|
&& self.condition instanceof AST_Binary && self.condition.operator == "||") { |
|
// although the code length of self.condition and negated are the same, |
|
// negated does not require additional surrounding parentheses. |
|
// see https://github.com/mishoo/UglifyJS2/issues/979 |
|
negated_is_best = true; |
|
} |
|
if (negated_is_best) return make_node(AST_SimpleStatement, self, { |
|
body: make_node(AST_Binary, self, { |
|
operator : "||", |
|
left : negated, |
|
right : self.body.body |
|
}) |
|
}).optimize(compressor); |
|
return make_node(AST_SimpleStatement, self, { |
|
body: make_node(AST_Binary, self, { |
|
operator : "&&", |
|
left : self.condition, |
|
right : self.body.body |
|
}) |
|
}).optimize(compressor); |
|
} |
|
if (self.body instanceof AST_EmptyStatement |
|
&& self.alternative instanceof AST_SimpleStatement) { |
|
return make_node(AST_SimpleStatement, self, { |
|
body: make_node(AST_Binary, self, { |
|
operator : "||", |
|
left : self.condition, |
|
right : self.alternative.body |
|
}) |
|
}).optimize(compressor); |
|
} |
|
if (self.body instanceof AST_Exit |
|
&& self.alternative instanceof AST_Exit |
|
&& self.body.TYPE == self.alternative.TYPE) { |
|
return make_node(self.body.CTOR, self, { |
|
value: make_node(AST_Conditional, self, { |
|
condition : self.condition, |
|
consequent : self.body.value || make_void_0(self.body), |
|
alternative : self.alternative.value || make_void_0(self.alternative), |
|
}).transform(compressor) |
|
}).optimize(compressor); |
|
} |
|
if (self.body instanceof AST_If |
|
&& !self.body.alternative |
|
&& !self.alternative) { |
|
self = make_node(AST_If, self, { |
|
condition: make_node(AST_Binary, self.condition, { |
|
operator: "&&", |
|
left: self.condition, |
|
right: self.body.condition |
|
}), |
|
body: self.body.body, |
|
alternative: null |
|
}); |
|
} |
|
if (aborts(self.body)) { |
|
if (self.alternative) { |
|
var alt = self.alternative; |
|
self.alternative = null; |
|
return make_node(AST_BlockStatement, self, { |
|
body: [ self, alt ] |
|
}).optimize(compressor); |
|
} |
|
} |
|
if (aborts(self.alternative)) { |
|
var body = self.body; |
|
self.body = self.alternative; |
|
self.condition = negated_is_best ? negated : self.condition.negate(compressor); |
|
self.alternative = null; |
|
return make_node(AST_BlockStatement, self, { |
|
body: [ self, body ] |
|
}).optimize(compressor); |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Switch, function(self, compressor) { |
|
if (!compressor.option("switches")) return self; |
|
var branch; |
|
var value = self.expression.evaluate(compressor); |
|
if (!(value instanceof AST_Node)) { |
|
var orig = self.expression; |
|
self.expression = make_node_from_constant(value, orig); |
|
self.expression = best_of_expression(self.expression.transform(compressor), orig); |
|
} |
|
if (!compressor.option("dead_code")) return self; |
|
if (value instanceof AST_Node) { |
|
value = self.expression.tail_node().evaluate(compressor); |
|
} |
|
var decl = []; |
|
var body = []; |
|
var default_branch; |
|
var exact_match; |
|
// - compress self.body into `body` |
|
// - find and deduplicate default branch |
|
// - find the exact match (`case 1234` inside `switch(1234)`) |
|
for (var i = 0, len = self.body.length; i < len && !exact_match; i++) { |
|
branch = self.body[i]; |
|
if (branch instanceof AST_Default) { |
|
if (!default_branch) { |
|
default_branch = branch; |
|
} else { |
|
eliminate_branch(branch, body[body.length - 1]); |
|
} |
|
} else if (!(value instanceof AST_Node)) { |
|
var exp = branch.expression.evaluate(compressor); |
|
if (!(exp instanceof AST_Node) && exp !== value) { |
|
eliminate_branch(branch, body[body.length - 1]); |
|
continue; |
|
} |
|
if (exp instanceof AST_Node && !exp.has_side_effects(compressor)) { |
|
exp = branch.expression.tail_node().evaluate(compressor); |
|
} |
|
if (exp === value) { |
|
exact_match = branch; |
|
if (default_branch) { |
|
var default_index = body.indexOf(default_branch); |
|
body.splice(default_index, 1); |
|
eliminate_branch(default_branch, body[default_index - 1]); |
|
default_branch = null; |
|
} |
|
} |
|
} |
|
body.push(branch); |
|
} |
|
// i < len if we found an exact_match. eliminate the rest |
|
while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]); |
|
self.body = body; |
|
|
|
let default_or_exact = default_branch || exact_match; |
|
default_branch = null; |
|
exact_match = null; |
|
|
|
// group equivalent branches so they will be located next to each other, |
|
// that way the next micro-optimization will merge them. |
|
// ** bail micro-optimization if not a simple switch case with breaks |
|
if (body.every((branch, i) => |
|
(branch === default_or_exact || branch.expression instanceof AST_Constant) |
|
&& (branch.body.length === 0 || aborts(branch) || body.length - 1 === i)) |
|
) { |
|
for (let i = 0; i < body.length; i++) { |
|
const branch = body[i]; |
|
for (let j = i + 1; j < body.length; j++) { |
|
const next = body[j]; |
|
if (next.body.length === 0) continue; |
|
const last_branch = j === (body.length - 1); |
|
const equivalentBranch = branches_equivalent(next, branch, false); |
|
if (equivalentBranch || (last_branch && branches_equivalent(next, branch, true))) { |
|
if (!equivalentBranch && last_branch) { |
|
next.body.push(make_node(AST_Break)); |
|
} |
|
|
|
// let's find previous siblings with inert fallthrough... |
|
let x = j - 1; |
|
let fallthroughDepth = 0; |
|
while (x > i) { |
|
if (is_inert_body(body[x--])) { |
|
fallthroughDepth++; |
|
} else { |
|
break; |
|
} |
|
} |
|
|
|
const plucked = body.splice(j - fallthroughDepth, 1 + fallthroughDepth); |
|
body.splice(i + 1, 0, ...plucked); |
|
i += plucked.length; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// merge equivalent branches in a row |
|
for (let i = 0; i < body.length; i++) { |
|
let branch = body[i]; |
|
if (branch.body.length === 0) continue; |
|
if (!aborts(branch)) continue; |
|
|
|
for (let j = i + 1; j < body.length; i++, j++) { |
|
let next = body[j]; |
|
if (next.body.length === 0) continue; |
|
if ( |
|
branches_equivalent(next, branch, false) |
|
|| (j === body.length - 1 && branches_equivalent(next, branch, true)) |
|
) { |
|
branch.body = []; |
|
branch = next; |
|
continue; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
// Prune any empty branches at the end of the switch statement. |
|
{ |
|
let i = body.length - 1; |
|
for (; i >= 0; i--) { |
|
let bbody = body[i].body; |
|
while (is_break(bbody[bbody.length - 1], compressor)) bbody.pop(); |
|
if (!is_inert_body(body[i])) break; |
|
} |
|
// i now points to the index of a branch that contains a body. By incrementing, it's |
|
// pointing to the first branch that's empty. |
|
i++; |
|
if (!default_or_exact || body.indexOf(default_or_exact) >= i) { |
|
// The default behavior is to do nothing. We can take advantage of that to |
|
// remove all case expressions that are side-effect free that also do |
|
// nothing, since they'll default to doing nothing. But we can't remove any |
|
// case expressions before one that would side-effect, since they may cause |
|
// the side-effect to be skipped. |
|
for (let j = body.length - 1; j >= i; j--) { |
|
let branch = body[j]; |
|
if (branch === default_or_exact) { |
|
default_or_exact = null; |
|
eliminate_branch(body.pop()); |
|
} else if (!branch.expression.has_side_effects(compressor)) { |
|
eliminate_branch(body.pop()); |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
// Prune side-effect free branches that fall into default. |
|
DEFAULT: if (default_or_exact) { |
|
let default_index = body.indexOf(default_or_exact); |
|
let default_body_index = default_index; |
|
for (; default_body_index < body.length - 1; default_body_index++) { |
|
if (!is_inert_body(body[default_body_index])) break; |
|
} |
|
if (default_body_index < body.length - 1) { |
|
break DEFAULT; |
|
} |
|
|
|
let side_effect_index = body.length - 1; |
|
for (; side_effect_index >= 0; side_effect_index--) { |
|
let branch = body[side_effect_index]; |
|
if (branch === default_or_exact) continue; |
|
if (branch.expression.has_side_effects(compressor)) break; |
|
} |
|
// If the default behavior comes after any side-effect case expressions, |
|
// then we can fold all side-effect free cases into the default branch. |
|
// If the side-effect case is after the default, then any side-effect |
|
// free cases could prevent the side-effect from occurring. |
|
if (default_body_index > side_effect_index) { |
|
let prev_body_index = default_index - 1; |
|
for (; prev_body_index >= 0; prev_body_index--) { |
|
if (!is_inert_body(body[prev_body_index])) break; |
|
} |
|
let before = Math.max(side_effect_index, prev_body_index) + 1; |
|
let after = default_index; |
|
if (side_effect_index > default_index) { |
|
// If the default falls into the same body as a side-effect |
|
// case, then we need preserve that case and only prune the |
|
// cases after it. |
|
after = side_effect_index; |
|
body[side_effect_index].body = body[default_body_index].body; |
|
} else { |
|
// The default will be the last branch. |
|
default_or_exact.body = body[default_body_index].body; |
|
} |
|
|
|
// Prune everything after the default (or last side-effect case) |
|
// until the next case with a body. |
|
body.splice(after + 1, default_body_index - after); |
|
// Prune everything before the default that falls into it. |
|
body.splice(before, default_index - before); |
|
} |
|
} |
|
|
|
// See if we can remove the switch entirely if all cases (the default) fall into the same case body. |
|
DEFAULT: if (default_or_exact) { |
|
let i = body.findIndex(branch => !is_inert_body(branch)); |
|
let caseBody; |
|
// `i` is equal to one of the following: |
|
// - `-1`, there is no body in the switch statement. |
|
// - `body.length - 1`, all cases fall into the same body. |
|
// - anything else, there are multiple bodies in the switch. |
|
if (i === body.length - 1) { |
|
// All cases fall into the case body. |
|
let branch = body[i]; |
|
if (has_nested_break(self)) break DEFAULT; |
|
|
|
// This is the last case body, and we've already pruned any breaks, so it's |
|
// safe to hoist. |
|
caseBody = make_node(AST_BlockStatement, branch, { |
|
body: branch.body |
|
}); |
|
branch.body = []; |
|
} else if (i !== -1) { |
|
// If there are multiple bodies, then we cannot optimize anything. |
|
break DEFAULT; |
|
} |
|
|
|
let sideEffect = body.find( |
|
branch => branch !== default_or_exact && branch.expression.has_side_effects(compressor) |
|
); |
|
// If no cases cause a side-effect, we can eliminate the switch entirely. |
|
if (!sideEffect) { |
|
return make_node(AST_BlockStatement, self, { |
|
body: decl.concat( |
|
statement(self.expression), |
|
default_or_exact.expression ? statement(default_or_exact.expression) : [], |
|
caseBody || [] |
|
) |
|
}).optimize(compressor); |
|
} |
|
|
|
// If we're this far, either there was no body or all cases fell into the same body. |
|
// If there was no body, then we don't need a default branch (because the default is |
|
// do nothing). If there was a body, we'll extract it to after the switch, so the |
|
// switch's new default is to do nothing and we can still prune it. |
|
const default_index = body.indexOf(default_or_exact); |
|
body.splice(default_index, 1); |
|
default_or_exact = null; |
|
|
|
if (caseBody) { |
|
// Recurse into switch statement one more time so that we can append the case body |
|
// outside of the switch. This recursion will only happen once since we've pruned |
|
// the default case. |
|
return make_node(AST_BlockStatement, self, { |
|
body: decl.concat(self, caseBody) |
|
}).optimize(compressor); |
|
} |
|
// If we fall here, there is a default branch somewhere, there are no case bodies, |
|
// and there's a side-effect somewhere. Just let the below paths take care of it. |
|
} |
|
|
|
// Reintegrate `decl` (var statements) |
|
if (body.length > 0) { |
|
body[0].body = decl.concat(body[0].body); |
|
} |
|
if (body.length == 0) { |
|
return make_node(AST_BlockStatement, self, { |
|
body: decl.concat(statement(self.expression)) |
|
}).optimize(compressor); |
|
} |
|
|
|
if (body.length == 1 && !has_nested_break(self)) { |
|
// This is the last case body, and we've already pruned any breaks, so it's |
|
// safe to hoist. |
|
let branch = body[0]; |
|
return make_node(AST_If, self, { |
|
condition: make_node(AST_Binary, self, { |
|
operator: "===", |
|
left: self.expression, |
|
right: branch.expression, |
|
}), |
|
body: make_node(AST_BlockStatement, branch, { |
|
body: branch.body |
|
}), |
|
alternative: null |
|
}).optimize(compressor); |
|
} |
|
if (body.length === 2 && default_or_exact && !has_nested_break(self)) { |
|
let branch = body[0] === default_or_exact ? body[1] : body[0]; |
|
let exact_exp = default_or_exact.expression && statement(default_or_exact.expression); |
|
if (aborts(body[0])) { |
|
// Only the first branch body could have a break (at the last statement) |
|
let first = body[0]; |
|
if (is_break(first.body[first.body.length - 1], compressor)) { |
|
first.body.pop(); |
|
} |
|
return make_node(AST_If, self, { |
|
condition: make_node(AST_Binary, self, { |
|
operator: "===", |
|
left: self.expression, |
|
right: branch.expression, |
|
}), |
|
body: make_node(AST_BlockStatement, branch, { |
|
body: branch.body |
|
}), |
|
alternative: make_node(AST_BlockStatement, default_or_exact, { |
|
body: [].concat( |
|
exact_exp || [], |
|
default_or_exact.body |
|
) |
|
}) |
|
}).optimize(compressor); |
|
} |
|
let operator = "==="; |
|
let consequent = make_node(AST_BlockStatement, branch, { |
|
body: branch.body, |
|
}); |
|
let always = make_node(AST_BlockStatement, default_or_exact, { |
|
body: [].concat( |
|
exact_exp || [], |
|
default_or_exact.body |
|
) |
|
}); |
|
if (body[0] === default_or_exact) { |
|
operator = "!=="; |
|
let tmp = always; |
|
always = consequent; |
|
consequent = tmp; |
|
} |
|
return make_node(AST_BlockStatement, self, { |
|
body: [ |
|
make_node(AST_If, self, { |
|
condition: make_node(AST_Binary, self, { |
|
operator: operator, |
|
left: self.expression, |
|
right: branch.expression, |
|
}), |
|
body: consequent, |
|
alternative: null, |
|
}), |
|
always, |
|
], |
|
}).optimize(compressor); |
|
} |
|
return self; |
|
|
|
function eliminate_branch(branch, prev) { |
|
if (prev && !aborts(prev)) { |
|
prev.body = prev.body.concat(branch.body); |
|
} else { |
|
extract_from_unreachable_code(compressor, branch, decl); |
|
} |
|
} |
|
function branches_equivalent(branch, prev, insertBreak) { |
|
let bbody = branch.body; |
|
let pbody = prev.body; |
|
if (insertBreak) { |
|
bbody = bbody.concat(make_node(AST_Break)); |
|
} |
|
if (bbody.length !== pbody.length) return false; |
|
let bblock = make_node(AST_BlockStatement, branch, { body: bbody }); |
|
let pblock = make_node(AST_BlockStatement, prev, { body: pbody }); |
|
return bblock.equivalent_to(pblock); |
|
} |
|
function statement(body) { |
|
return make_node(AST_SimpleStatement, body, { body }); |
|
} |
|
function has_nested_break(root) { |
|
let has_break = false; |
|
|
|
let tw = new TreeWalker(node => { |
|
if (has_break) return true; |
|
if (node instanceof AST_Lambda) return true; |
|
if (node instanceof AST_SimpleStatement) return true; |
|
if (!is_break(node, tw)) return; |
|
let parent = tw.parent(); |
|
if ( |
|
parent instanceof AST_SwitchBranch |
|
&& parent.body[parent.body.length - 1] === node |
|
) { |
|
return; |
|
} |
|
has_break = true; |
|
}); |
|
root.walk(tw); |
|
return has_break; |
|
} |
|
function is_break(node, stack) { |
|
return node instanceof AST_Break |
|
&& stack.loopcontrol_target(node) === self; |
|
} |
|
function is_inert_body(branch) { |
|
return !aborts(branch) && !make_node(AST_BlockStatement, branch, { |
|
body: branch.body |
|
}).has_side_effects(compressor); |
|
} |
|
}); |
|
|
|
def_optimize(AST_Try, function(self, compressor) { |
|
if (self.bcatch && self.bfinally && self.bfinally.body.every(is_empty)) self.bfinally = null; |
|
|
|
if (compressor.option("dead_code") && self.body.body.every(is_empty)) { |
|
var body = []; |
|
if (self.bcatch) { |
|
extract_from_unreachable_code(compressor, self.bcatch, body); |
|
} |
|
if (self.bfinally) body.push(...self.bfinally.body); |
|
return make_node(AST_BlockStatement, self, { |
|
body: body |
|
}).optimize(compressor); |
|
} |
|
return self; |
|
}); |
|
|
|
AST_Definitions.DEFMETHOD("to_assignments", function(compressor) { |
|
var reduce_vars = compressor.option("reduce_vars"); |
|
var assignments = []; |
|
|
|
for (const def of this.definitions) { |
|
if (def.value) { |
|
var name = make_node(AST_SymbolRef, def.name, def.name); |
|
assignments.push(make_node(AST_Assign, def, { |
|
operator : "=", |
|
logical: false, |
|
left : name, |
|
right : def.value |
|
})); |
|
if (reduce_vars) name.definition().fixed = false; |
|
} |
|
const thedef = def.name.definition(); |
|
thedef.eliminated++; |
|
thedef.replaced--; |
|
} |
|
|
|
if (assignments.length == 0) return null; |
|
return make_sequence(this, assignments); |
|
}); |
|
|
|
def_optimize(AST_Definitions, function(self) { |
|
if (self.definitions.length == 0) { |
|
return make_node(AST_EmptyStatement, self); |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_VarDef, function(self, compressor) { |
|
if ( |
|
self.name instanceof AST_SymbolLet |
|
&& self.value != null |
|
&& is_undefined(self.value, compressor) |
|
) { |
|
self.value = null; |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Import, function(self) { |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Call, function(self, compressor) { |
|
var exp = self.expression; |
|
var fn = exp; |
|
inline_array_like_spread(self.args); |
|
var simple_args = self.args.every((arg) => !(arg instanceof AST_Expansion)); |
|
|
|
if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) { |
|
fn = fn.fixed_value(); |
|
} |
|
|
|
var is_func = fn instanceof AST_Lambda; |
|
|
|
if (is_func && fn.pinned()) return self; |
|
|
|
if (compressor.option("unused") |
|
&& simple_args |
|
&& is_func |
|
&& !fn.uses_arguments) { |
|
var pos = 0, last = 0; |
|
for (var i = 0, len = self.args.length; i < len; i++) { |
|
if (fn.argnames[i] instanceof AST_Expansion) { |
|
if (has_flag(fn.argnames[i].expression, UNUSED)) while (i < len) { |
|
var node = self.args[i++].drop_side_effect_free(compressor); |
|
if (node) { |
|
self.args[pos++] = node; |
|
} |
|
} else while (i < len) { |
|
self.args[pos++] = self.args[i++]; |
|
} |
|
last = pos; |
|
break; |
|
} |
|
var trim = i >= fn.argnames.length; |
|
if (trim || has_flag(fn.argnames[i], UNUSED)) { |
|
var node = self.args[i].drop_side_effect_free(compressor); |
|
if (node) { |
|
self.args[pos++] = node; |
|
} else if (!trim) { |
|
self.args[pos++] = make_node(AST_Number, self.args[i], { |
|
value: 0 |
|
}); |
|
continue; |
|
} |
|
} else { |
|
self.args[pos++] = self.args[i]; |
|
} |
|
last = pos; |
|
} |
|
self.args.length = last; |
|
} |
|
|
|
if ( |
|
exp instanceof AST_Dot |
|
&& exp.expression instanceof AST_SymbolRef |
|
&& exp.expression.name === "console" |
|
&& exp.expression.definition().undeclared |
|
&& exp.property === "assert" |
|
) { |
|
const condition = self.args[0]; |
|
if (condition) { |
|
const value = condition.evaluate(compressor); |
|
|
|
if (value === 1 || value === true) { |
|
return make_void_0(self).optimize(compressor); |
|
} |
|
} |
|
} |
|
|
|
if (compressor.option("unsafe") && !exp.contains_optional()) { |
|
if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) { |
|
const [argument] = self.args; |
|
if (argument instanceof AST_Array) { |
|
return make_node(AST_Array, argument, { |
|
elements: argument.elements |
|
}).optimize(compressor); |
|
} |
|
} |
|
if (is_undeclared_ref(exp)) switch (exp.name) { |
|
case "Array": |
|
if (self.args.length != 1) { |
|
return make_node(AST_Array, self, { |
|
elements: self.args |
|
}).optimize(compressor); |
|
} else if (self.args[0] instanceof AST_Number && self.args[0].value <= 11) { |
|
const elements = []; |
|
for (let i = 0; i < self.args[0].value; i++) elements.push(new AST_Hole); |
|
return new AST_Array({ elements }); |
|
} |
|
break; |
|
case "Object": |
|
if (self.args.length == 0) { |
|
return make_node(AST_Object, self, { |
|
properties: [] |
|
}); |
|
} |
|
break; |
|
case "String": |
|
if (self.args.length == 0) return make_node(AST_String, self, { |
|
value: "" |
|
}); |
|
if (self.args.length <= 1) return make_node(AST_Binary, self, { |
|
left: self.args[0], |
|
operator: "+", |
|
right: make_node(AST_String, self, { value: "" }) |
|
}).optimize(compressor); |
|
break; |
|
case "Number": |
|
if (self.args.length == 0) return make_node(AST_Number, self, { |
|
value: 0 |
|
}); |
|
if (self.args.length == 1 && compressor.option("unsafe_math")) { |
|
return make_node(AST_UnaryPrefix, self, { |
|
expression: self.args[0], |
|
operator: "+" |
|
}).optimize(compressor); |
|
} |
|
break; |
|
case "Symbol": |
|
if (self.args.length == 1 && self.args[0] instanceof AST_String && compressor.option("unsafe_symbols")) |
|
self.args.length = 0; |
|
break; |
|
case "Boolean": |
|
if (self.args.length == 0) return make_node(AST_False, self); |
|
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { |
|
expression: make_node(AST_UnaryPrefix, self, { |
|
expression: self.args[0], |
|
operator: "!" |
|
}), |
|
operator: "!" |
|
}).optimize(compressor); |
|
break; |
|
case "RegExp": |
|
var params = []; |
|
if (self.args.length >= 1 |
|
&& self.args.length <= 2 |
|
&& self.args.every((arg) => { |
|
var value = arg.evaluate(compressor); |
|
params.push(value); |
|
return arg !== value; |
|
}) |
|
&& regexp_is_safe(params[0]) |
|
) { |
|
let [ source, flags ] = params; |
|
source = regexp_source_fix(new RegExp(source).source); |
|
const rx = make_node(AST_RegExp, self, { |
|
value: { source, flags } |
|
}); |
|
if (rx._eval(compressor) !== rx) { |
|
return rx; |
|
} |
|
} |
|
break; |
|
} else if (exp instanceof AST_Dot) switch(exp.property) { |
|
case "toString": |
|
if (self.args.length == 0 && !exp.expression.may_throw_on_access(compressor)) { |
|
return make_node(AST_Binary, self, { |
|
left: make_node(AST_String, self, { value: "" }), |
|
operator: "+", |
|
right: exp.expression |
|
}).optimize(compressor); |
|
} |
|
break; |
|
case "join": |
|
if (exp.expression instanceof AST_Array) EXIT: { |
|
var separator; |
|
if (self.args.length > 0) { |
|
separator = self.args[0].evaluate(compressor); |
|
if (separator === self.args[0]) break EXIT; // not a constant |
|
} |
|
var elements = []; |
|
var consts = []; |
|
for (var i = 0, len = exp.expression.elements.length; i < len; i++) { |
|
var el = exp.expression.elements[i]; |
|
if (el instanceof AST_Expansion) break EXIT; |
|
var value = el.evaluate(compressor); |
|
if (value !== el) { |
|
consts.push(value); |
|
} else { |
|
if (consts.length > 0) { |
|
elements.push(make_node(AST_String, self, { |
|
value: consts.join(separator) |
|
})); |
|
consts.length = 0; |
|
} |
|
elements.push(el); |
|
} |
|
} |
|
if (consts.length > 0) { |
|
elements.push(make_node(AST_String, self, { |
|
value: consts.join(separator) |
|
})); |
|
} |
|
if (elements.length == 0) return make_node(AST_String, self, { value: "" }); |
|
if (elements.length == 1) { |
|
if (elements[0].is_string(compressor)) { |
|
return elements[0]; |
|
} |
|
return make_node(AST_Binary, elements[0], { |
|
operator : "+", |
|
left : make_node(AST_String, self, { value: "" }), |
|
right : elements[0] |
|
}); |
|
} |
|
if (separator == "") { |
|
var first; |
|
if (elements[0].is_string(compressor) |
|
|| elements[1].is_string(compressor)) { |
|
first = elements.shift(); |
|
} else { |
|
first = make_node(AST_String, self, { value: "" }); |
|
} |
|
return elements.reduce(function(prev, el) { |
|
return make_node(AST_Binary, el, { |
|
operator : "+", |
|
left : prev, |
|
right : el |
|
}); |
|
}, first).optimize(compressor); |
|
} |
|
// need this awkward cloning to not affect original element |
|
// best_of will decide which one to get through. |
|
var node = self.clone(); |
|
node.expression = node.expression.clone(); |
|
node.expression.expression = node.expression.expression.clone(); |
|
node.expression.expression.elements = elements; |
|
return best_of(compressor, self, node); |
|
} |
|
break; |
|
case "charAt": |
|
if (exp.expression.is_string(compressor)) { |
|
var arg = self.args[0]; |
|
var index = arg ? arg.evaluate(compressor) : 0; |
|
if (index !== arg) { |
|
return make_node(AST_Sub, exp, { |
|
expression: exp.expression, |
|
property: make_node_from_constant(index | 0, arg || exp) |
|
}).optimize(compressor); |
|
} |
|
} |
|
break; |
|
case "apply": |
|
if (self.args.length == 2 && self.args[1] instanceof AST_Array) { |
|
var args = self.args[1].elements.slice(); |
|
args.unshift(self.args[0]); |
|
return make_node(AST_Call, self, { |
|
expression: make_node(AST_Dot, exp, { |
|
expression: exp.expression, |
|
optional: false, |
|
property: "call" |
|
}), |
|
args: args |
|
}).optimize(compressor); |
|
} |
|
break; |
|
case "call": |
|
var func = exp.expression; |
|
if (func instanceof AST_SymbolRef) { |
|
func = func.fixed_value(); |
|
} |
|
if (func instanceof AST_Lambda && !func.contains_this()) { |
|
return (self.args.length ? make_sequence(this, [ |
|
self.args[0], |
|
make_node(AST_Call, self, { |
|
expression: exp.expression, |
|
args: self.args.slice(1) |
|
}) |
|
]) : make_node(AST_Call, self, { |
|
expression: exp.expression, |
|
args: [] |
|
})).optimize(compressor); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
if (compressor.option("unsafe_Function") |
|
&& is_undeclared_ref(exp) |
|
&& exp.name == "Function") { |
|
// new Function() => function(){} |
|
if (self.args.length == 0) return make_empty_function(self).optimize(compressor); |
|
if (self.args.every((x) => x instanceof AST_String)) { |
|
// quite a corner-case, but we can handle it: |
|
// https://github.com/mishoo/UglifyJS2/issues/203 |
|
// if the code argument is a constant, then we can minify it. |
|
try { |
|
var code = "n(function(" + self.args.slice(0, -1).map(function(arg) { |
|
return arg.value; |
|
}).join(",") + "){" + self.args[self.args.length - 1].value + "})"; |
|
var ast = parse(code); |
|
var mangle = compressor.mangle_options(); |
|
ast.figure_out_scope(mangle); |
|
var comp = new Compressor(compressor.options, { |
|
mangle_options: compressor._mangle_options |
|
}); |
|
ast = ast.transform(comp); |
|
ast.figure_out_scope(mangle); |
|
ast.compute_char_frequency(mangle); |
|
ast.mangle_names(mangle); |
|
var fun; |
|
walk(ast, node => { |
|
if (is_func_expr(node)) { |
|
fun = node; |
|
return walk_abort; |
|
} |
|
}); |
|
var code = OutputStream(); |
|
AST_BlockStatement.prototype._codegen.call(fun, fun, code); |
|
self.args = [ |
|
make_node(AST_String, self, { |
|
value: fun.argnames.map(function(arg) { |
|
return arg.print_to_string(); |
|
}).join(",") |
|
}), |
|
make_node(AST_String, self.args[self.args.length - 1], { |
|
value: code.get().replace(/^{|}$/g, "") |
|
}) |
|
]; |
|
return self; |
|
} catch (ex) { |
|
if (!(ex instanceof JS_Parse_Error)) { |
|
throw ex; |
|
} |
|
|
|
// Otherwise, it crashes at runtime. Or maybe it's nonstandard syntax. |
|
} |
|
} |
|
} |
|
|
|
return inline_into_call(self, compressor); |
|
}); |
|
|
|
/** Does this node contain optional property access or optional call? */ |
|
AST_Node.DEFMETHOD("contains_optional", function() { |
|
if ( |
|
this instanceof AST_PropAccess |
|
|| this instanceof AST_Call |
|
|| this instanceof AST_Chain |
|
) { |
|
if (this.optional) { |
|
return true; |
|
} else { |
|
return this.expression.contains_optional(); |
|
} |
|
} else { |
|
return false; |
|
} |
|
}); |
|
|
|
def_optimize(AST_New, function(self, compressor) { |
|
if ( |
|
compressor.option("unsafe") && |
|
is_undeclared_ref(self.expression) && |
|
["Object", "RegExp", "Function", "Error", "Array"].includes(self.expression.name) |
|
) return make_node(AST_Call, self, self).transform(compressor); |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Sequence, function(self, compressor) { |
|
if (!compressor.option("side_effects")) return self; |
|
var expressions = []; |
|
filter_for_side_effects(); |
|
var end = expressions.length - 1; |
|
trim_right_for_undefined(); |
|
if (end == 0) { |
|
self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]); |
|
if (!(self instanceof AST_Sequence)) self = self.optimize(compressor); |
|
return self; |
|
} |
|
self.expressions = expressions; |
|
return self; |
|
|
|
function filter_for_side_effects() { |
|
var first = first_in_statement(compressor); |
|
var last = self.expressions.length - 1; |
|
self.expressions.forEach(function(expr, index) { |
|
if (index < last) expr = expr.drop_side_effect_free(compressor, first); |
|
if (expr) { |
|
merge_sequence(expressions, expr); |
|
first = false; |
|
} |
|
}); |
|
} |
|
|
|
function trim_right_for_undefined() { |
|
while (end > 0 && is_undefined(expressions[end], compressor)) end--; |
|
if (end < expressions.length - 1) { |
|
expressions[end] = make_node(AST_UnaryPrefix, self, { |
|
operator : "void", |
|
expression : expressions[end] |
|
}); |
|
expressions.length = end + 1; |
|
} |
|
} |
|
}); |
|
|
|
AST_Unary.DEFMETHOD("lift_sequences", function(compressor) { |
|
if (compressor.option("sequences")) { |
|
if (this.expression instanceof AST_Sequence) { |
|
var x = this.expression.expressions.slice(); |
|
var e = this.clone(); |
|
e.expression = x.pop(); |
|
x.push(e); |
|
return make_sequence(this, x).optimize(compressor); |
|
} |
|
} |
|
return this; |
|
}); |
|
|
|
def_optimize(AST_UnaryPostfix, function(self, compressor) { |
|
return self.lift_sequences(compressor); |
|
}); |
|
|
|
def_optimize(AST_UnaryPrefix, function(self, compressor) { |
|
var e = self.expression; |
|
if ( |
|
self.operator == "delete" && |
|
!( |
|
e instanceof AST_SymbolRef || |
|
e instanceof AST_PropAccess || |
|
e instanceof AST_Chain || |
|
is_identifier_atom(e) |
|
) |
|
) { |
|
return make_sequence(self, [e, make_node(AST_True, self)]).optimize(compressor); |
|
} |
|
// Short-circuit common `void 0` |
|
if (self.operator === "void" && e instanceof AST_Number && e.value === 0) { |
|
return unsafe_undefined_ref(self, compressor) || self; |
|
} |
|
var seq = self.lift_sequences(compressor); |
|
if (seq !== self) { |
|
return seq; |
|
} |
|
if (compressor.option("side_effects") && self.operator == "void") { |
|
e = e.drop_side_effect_free(compressor); |
|
if (e) { |
|
self.expression = e; |
|
return self; |
|
} else { |
|
return make_void_0(self).optimize(compressor); |
|
} |
|
} |
|
if (compressor.in_boolean_context()) { |
|
switch (self.operator) { |
|
case "!": |
|
if (e instanceof AST_UnaryPrefix && e.operator == "!") { |
|
// !!foo ==> foo, if we're in boolean context |
|
return e.expression; |
|
} |
|
if (e instanceof AST_Binary) { |
|
self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor))); |
|
} |
|
break; |
|
case "typeof": |
|
// typeof always returns a non-empty string, thus it's |
|
// always true in booleans |
|
// And we don't need to check if it's undeclared, because in typeof, that's OK |
|
return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [ |
|
e, |
|
make_node(AST_True, self) |
|
])).optimize(compressor); |
|
} |
|
} |
|
if (self.operator == "-" && e instanceof AST_Infinity) { |
|
e = e.transform(compressor); |
|
} |
|
if (e instanceof AST_Binary |
|
&& (self.operator == "+" || self.operator == "-") |
|
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) { |
|
return make_node(AST_Binary, self, { |
|
operator: e.operator, |
|
left: make_node(AST_UnaryPrefix, e.left, { |
|
operator: self.operator, |
|
expression: e.left |
|
}), |
|
right: e.right |
|
}); |
|
} |
|
|
|
if (compressor.option("evaluate")) { |
|
// ~~x => x (in 32-bit context) |
|
// ~~{32 bit integer} => {32 bit integer} |
|
if ( |
|
self.operator === "~" |
|
&& self.expression instanceof AST_UnaryPrefix |
|
&& self.expression.operator === "~" |
|
&& (compressor.in_32_bit_context(false) || self.expression.expression.is_32_bit_integer(compressor)) |
|
) { |
|
return self.expression.expression; |
|
} |
|
|
|
// ~(x ^ y) => x ^ ~y |
|
if ( |
|
self.operator === "~" |
|
&& e instanceof AST_Binary |
|
&& e.operator === "^" |
|
) { |
|
if (e.left instanceof AST_UnaryPrefix && e.left.operator === "~") { |
|
// ~(~x ^ y) => x ^ y |
|
e.left = e.left.bitwise_negate(compressor, true); |
|
} else { |
|
e.right = e.right.bitwise_negate(compressor, true); |
|
} |
|
return e; |
|
} |
|
} |
|
|
|
if ( |
|
self.operator != "-" |
|
// avoid infinite recursion of numerals |
|
|| !(e instanceof AST_Number || e instanceof AST_Infinity || e instanceof AST_BigInt) |
|
) { |
|
var ev = self.evaluate(compressor); |
|
if (ev !== self) { |
|
ev = make_node_from_constant(ev, self).optimize(compressor); |
|
return best_of(compressor, ev, self); |
|
} |
|
} |
|
return self; |
|
}); |
|
|
|
AST_Binary.DEFMETHOD("lift_sequences", function(compressor) { |
|
if (compressor.option("sequences")) { |
|
if (this.left instanceof AST_Sequence) { |
|
var x = this.left.expressions.slice(); |
|
var e = this.clone(); |
|
e.left = x.pop(); |
|
x.push(e); |
|
return make_sequence(this, x).optimize(compressor); |
|
} |
|
if (this.right instanceof AST_Sequence && !this.left.has_side_effects(compressor)) { |
|
var assign = this.operator == "=" && this.left instanceof AST_SymbolRef; |
|
var x = this.right.expressions; |
|
var last = x.length - 1; |
|
for (var i = 0; i < last; i++) { |
|
if (!assign && x[i].has_side_effects(compressor)) break; |
|
} |
|
if (i == last) { |
|
x = x.slice(); |
|
var e = this.clone(); |
|
e.right = x.pop(); |
|
x.push(e); |
|
return make_sequence(this, x).optimize(compressor); |
|
} else if (i > 0) { |
|
var e = this.clone(); |
|
e.right = make_sequence(this.right, x.slice(i)); |
|
x = x.slice(0, i); |
|
x.push(e); |
|
return make_sequence(this, x).optimize(compressor); |
|
} |
|
} |
|
} |
|
return this; |
|
}); |
|
|
|
var commutativeOperators = makePredicate("== === != !== * & | ^"); |
|
function is_object(node) { |
|
return node instanceof AST_Array |
|
|| node instanceof AST_Lambda |
|
|| node instanceof AST_Object |
|
|| node instanceof AST_Class; |
|
} |
|
|
|
def_optimize(AST_Binary, function(self, compressor) { |
|
function reversible() { |
|
return self.left.is_constant() |
|
|| self.right.is_constant() |
|
|| !self.left.has_side_effects(compressor) |
|
&& !self.right.has_side_effects(compressor); |
|
} |
|
function reverse(op) { |
|
if (reversible()) { |
|
if (op) self.operator = op; |
|
var tmp = self.left; |
|
self.left = self.right; |
|
self.right = tmp; |
|
} |
|
} |
|
if (compressor.option("lhs_constants") && commutativeOperators.has(self.operator)) { |
|
if (self.right.is_constant() |
|
&& !self.left.is_constant()) { |
|
// if right is a constant, whatever side effects the |
|
// left side might have could not influence the |
|
// result. hence, force switch. |
|
|
|
if (!(self.left instanceof AST_Binary |
|
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { |
|
reverse(); |
|
} |
|
} |
|
} |
|
self = self.lift_sequences(compressor); |
|
if (compressor.option("comparisons")) switch (self.operator) { |
|
case "===": |
|
case "!==": |
|
var is_strict_comparison = true; |
|
if ( |
|
(self.left.is_string(compressor) && self.right.is_string(compressor)) || |
|
(self.left.is_number(compressor) && self.right.is_number(compressor)) || |
|
(self.left.is_bigint(compressor) && self.right.is_bigint(compressor)) || |
|
(self.left.is_boolean() && self.right.is_boolean()) || |
|
self.left.equivalent_to(self.right) |
|
) { |
|
self.operator = self.operator.substr(0, 2); |
|
} |
|
|
|
// XXX: intentionally falling down to the next case |
|
case "==": |
|
case "!=": |
|
// void 0 == x => null == x |
|
if (!is_strict_comparison && is_undefined(self.left, compressor)) { |
|
self.left = make_node(AST_Null, self.left); |
|
// x == void 0 => x == null |
|
} else if (!is_strict_comparison && is_undefined(self.right, compressor)) { |
|
self.right = make_node(AST_Null, self.right); |
|
} else if (compressor.option("typeofs") |
|
// "undefined" == typeof x => undefined === x |
|
&& self.left instanceof AST_String |
|
&& self.left.value == "undefined" |
|
&& self.right instanceof AST_UnaryPrefix |
|
&& self.right.operator == "typeof") { |
|
var expr = self.right.expression; |
|
if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor) |
|
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) { |
|
self.right = expr; |
|
self.left = make_void_0(self.left).optimize(compressor); |
|
if (self.operator.length == 2) self.operator += "="; |
|
} |
|
} else if (compressor.option("typeofs") |
|
// typeof x === "undefined" => x === undefined |
|
&& self.left instanceof AST_UnaryPrefix |
|
&& self.left.operator == "typeof" |
|
&& self.right instanceof AST_String |
|
&& self.right.value == "undefined") { |
|
var expr = self.left.expression; |
|
if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor) |
|
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) { |
|
self.left = expr; |
|
self.right = make_void_0(self.right).optimize(compressor); |
|
if (self.operator.length == 2) self.operator += "="; |
|
} |
|
} else if (self.left instanceof AST_SymbolRef |
|
// obj !== obj => false |
|
&& self.right instanceof AST_SymbolRef |
|
&& self.left.definition() === self.right.definition() |
|
&& is_object(self.left.fixed_value())) { |
|
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self); |
|
} else if (self.left.is_32_bit_integer(compressor) && self.right.is_32_bit_integer(compressor)) { |
|
const not = node => make_node(AST_UnaryPrefix, node, { |
|
operator: "!", |
|
expression: node |
|
}); |
|
const booleanify = (node, truthy) => { |
|
if (truthy) { |
|
return compressor.in_boolean_context() |
|
? node |
|
: not(not(node)); |
|
} else { |
|
return not(node); |
|
} |
|
}; |
|
|
|
// The only falsy 32-bit integer is 0 |
|
if (self.left instanceof AST_Number && self.left.value === 0) { |
|
return booleanify(self.right, self.operator[0] === "!"); |
|
} |
|
if (self.right instanceof AST_Number && self.right.value === 0) { |
|
return booleanify(self.left, self.operator[0] === "!"); |
|
} |
|
|
|
// Mask all-bits check |
|
// (x & 0xFF) != 0xFF => !(~x & 0xFF) |
|
let and_op, x, mask; |
|
if ( |
|
(and_op = |
|
self.left instanceof AST_Binary ? self.left |
|
: self.right instanceof AST_Binary ? self.right : null) |
|
&& (mask = and_op === self.left ? self.right : self.left) |
|
&& and_op.operator === "&" |
|
&& mask instanceof AST_Number |
|
&& mask.is_32_bit_integer(compressor) |
|
&& (x = |
|
and_op.left.equivalent_to(mask) ? and_op.right |
|
: and_op.right.equivalent_to(mask) ? and_op.left : null) |
|
) { |
|
let optimized = booleanify(make_node(AST_Binary, self, { |
|
operator: "&", |
|
left: mask, |
|
right: make_node(AST_UnaryPrefix, self, { |
|
operator: "~", |
|
expression: x |
|
}) |
|
}), self.operator[0] === "!"); |
|
|
|
return best_of(compressor, optimized, self); |
|
} |
|
} |
|
break; |
|
case "&&": |
|
case "||": |
|
var lhs = self.left; |
|
if (lhs.operator == self.operator) { |
|
lhs = lhs.right; |
|
} |
|
if (lhs instanceof AST_Binary |
|
&& lhs.operator == (self.operator == "&&" ? "!==" : "===") |
|
&& self.right instanceof AST_Binary |
|
&& lhs.operator == self.right.operator |
|
&& (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null |
|
|| lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor)) |
|
&& !lhs.right.has_side_effects(compressor) |
|
&& lhs.right.equivalent_to(self.right.right)) { |
|
var combined = make_node(AST_Binary, self, { |
|
operator: lhs.operator.slice(0, -1), |
|
left: make_node(AST_Null, self), |
|
right: lhs.right |
|
}); |
|
if (lhs !== self.left) { |
|
combined = make_node(AST_Binary, self, { |
|
operator: self.operator, |
|
left: self.left.left, |
|
right: combined |
|
}); |
|
} |
|
return combined; |
|
} |
|
break; |
|
} |
|
if (self.operator == "+" && compressor.in_boolean_context()) { |
|
var ll = self.left.evaluate(compressor); |
|
var rr = self.right.evaluate(compressor); |
|
if (ll && typeof ll == "string") { |
|
return make_sequence(self, [ |
|
self.right, |
|
make_node(AST_True, self) |
|
]).optimize(compressor); |
|
} |
|
if (rr && typeof rr == "string") { |
|
return make_sequence(self, [ |
|
self.left, |
|
make_node(AST_True, self) |
|
]).optimize(compressor); |
|
} |
|
} |
|
if (compressor.option("comparisons") && self.is_boolean()) { |
|
if (!(compressor.parent() instanceof AST_Binary) |
|
|| compressor.parent() instanceof AST_Assign) { |
|
var negated = make_node(AST_UnaryPrefix, self, { |
|
operator: "!", |
|
expression: self.negate(compressor, first_in_statement(compressor)) |
|
}); |
|
self = best_of(compressor, self, negated); |
|
} |
|
if (compressor.option("unsafe_comps")) { |
|
switch (self.operator) { |
|
case "<": reverse(">"); break; |
|
case "<=": reverse(">="); break; |
|
} |
|
} |
|
} |
|
if (self.operator == "+") { |
|
if (self.right instanceof AST_String |
|
&& self.right.getValue() == "" |
|
&& self.left.is_string(compressor)) { |
|
return self.left; |
|
} |
|
if (self.left instanceof AST_String |
|
&& self.left.getValue() == "" |
|
&& self.right.is_string(compressor)) { |
|
return self.right; |
|
} |
|
if (self.left instanceof AST_Binary |
|
&& self.left.operator == "+" |
|
&& self.left.left instanceof AST_String |
|
&& self.left.left.getValue() == "" |
|
&& self.right.is_string(compressor)) { |
|
self.left = self.left.right; |
|
return self; |
|
} |
|
} |
|
if (compressor.option("evaluate")) { |
|
switch (self.operator) { |
|
case "&&": |
|
var ll = has_flag(self.left, TRUTHY) |
|
? true |
|
: has_flag(self.left, FALSY) |
|
? false |
|
: self.left.evaluate(compressor); |
|
if (!ll) { |
|
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor); |
|
} else if (!(ll instanceof AST_Node)) { |
|
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); |
|
} |
|
var rr = self.right.evaluate(compressor); |
|
if (!rr) { |
|
if (compressor.in_boolean_context()) { |
|
return make_sequence(self, [ |
|
self.left, |
|
make_node(AST_False, self) |
|
]).optimize(compressor); |
|
} else { |
|
set_flag(self, FALSY); |
|
} |
|
} else if (!(rr instanceof AST_Node)) { |
|
var parent = compressor.parent(); |
|
if (parent.operator == "&&" && parent.left === compressor.self() || compressor.in_boolean_context()) { |
|
return self.left.optimize(compressor); |
|
} |
|
} |
|
// x || false && y ---> x ? y : false |
|
if (self.left.operator == "||") { |
|
var lr = self.left.right.evaluate(compressor); |
|
if (!lr) return make_node(AST_Conditional, self, { |
|
condition: self.left.left, |
|
consequent: self.right, |
|
alternative: self.left.right |
|
}).optimize(compressor); |
|
} |
|
break; |
|
case "||": |
|
var ll = has_flag(self.left, TRUTHY) |
|
? true |
|
: has_flag(self.left, FALSY) |
|
? false |
|
: self.left.evaluate(compressor); |
|
if (!ll) { |
|
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); |
|
} else if (!(ll instanceof AST_Node)) { |
|
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor); |
|
} |
|
var rr = self.right.evaluate(compressor); |
|
if (!rr) { |
|
var parent = compressor.parent(); |
|
if (parent.operator == "||" && parent.left === compressor.self() || compressor.in_boolean_context()) { |
|
return self.left.optimize(compressor); |
|
} |
|
} else if (!(rr instanceof AST_Node)) { |
|
if (compressor.in_boolean_context()) { |
|
return make_sequence(self, [ |
|
self.left, |
|
make_node(AST_True, self) |
|
]).optimize(compressor); |
|
} else { |
|
set_flag(self, TRUTHY); |
|
} |
|
} |
|
if (self.left.operator == "&&") { |
|
var lr = self.left.right.evaluate(compressor); |
|
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, { |
|
condition: self.left.left, |
|
consequent: self.left.right, |
|
alternative: self.right |
|
}).optimize(compressor); |
|
} |
|
break; |
|
case "??": |
|
if (is_nullish(self.left, compressor)) { |
|
return self.right; |
|
} |
|
|
|
var ll = self.left.evaluate(compressor); |
|
if (!(ll instanceof AST_Node)) { |
|
// if we know the value for sure we can simply compute right away. |
|
return ll == null ? self.right : self.left; |
|
} |
|
|
|
if (compressor.in_boolean_context()) { |
|
const rr = self.right.evaluate(compressor); |
|
if (!(rr instanceof AST_Node) && !rr) { |
|
return self.left; |
|
} |
|
} |
|
} |
|
var associative = true; |
|
switch (self.operator) { |
|
case "+": |
|
// (x + "foo") + "bar" => x + "foobar" |
|
if (self.right instanceof AST_Constant |
|
&& self.left instanceof AST_Binary |
|
&& self.left.operator == "+" |
|
&& self.left.is_string(compressor)) { |
|
var binary = make_node(AST_Binary, self, { |
|
operator: "+", |
|
left: self.left.right, |
|
right: self.right, |
|
}); |
|
var r = binary.optimize(compressor); |
|
if (binary !== r) { |
|
self = make_node(AST_Binary, self, { |
|
operator: "+", |
|
left: self.left.left, |
|
right: r |
|
}); |
|
} |
|
} |
|
// (x + "foo") + ("bar" + y) => (x + "foobar") + y |
|
if (self.left instanceof AST_Binary |
|
&& self.left.operator == "+" |
|
&& self.left.is_string(compressor) |
|
&& self.right instanceof AST_Binary |
|
&& self.right.operator == "+" |
|
&& self.right.is_string(compressor)) { |
|
var binary = make_node(AST_Binary, self, { |
|
operator: "+", |
|
left: self.left.right, |
|
right: self.right.left, |
|
}); |
|
var m = binary.optimize(compressor); |
|
if (binary !== m) { |
|
self = make_node(AST_Binary, self, { |
|
operator: "+", |
|
left: make_node(AST_Binary, self.left, { |
|
operator: "+", |
|
left: self.left.left, |
|
right: m |
|
}), |
|
right: self.right.right |
|
}); |
|
} |
|
} |
|
// a + -b => a - b |
|
if (self.right instanceof AST_UnaryPrefix |
|
&& self.right.operator == "-" |
|
&& self.left.is_number_or_bigint(compressor)) { |
|
self = make_node(AST_Binary, self, { |
|
operator: "-", |
|
left: self.left, |
|
right: self.right.expression |
|
}); |
|
break; |
|
} |
|
// -a + b => b - a |
|
if (self.left instanceof AST_UnaryPrefix |
|
&& self.left.operator == "-" |
|
&& reversible() |
|
&& self.right.is_number_or_bigint(compressor)) { |
|
self = make_node(AST_Binary, self, { |
|
operator: "-", |
|
left: self.right, |
|
right: self.left.expression |
|
}); |
|
break; |
|
} |
|
// `foo${bar}baz` + 1 => `foo${bar}baz1` |
|
if (self.left instanceof AST_TemplateString) { |
|
var l = self.left; |
|
var r = self.right.evaluate(compressor); |
|
if (r != self.right) { |
|
l.segments[l.segments.length - 1].value += String(r); |
|
return l; |
|
} |
|
} |
|
// 1 + `foo${bar}baz` => `1foo${bar}baz` |
|
if (self.right instanceof AST_TemplateString) { |
|
var r = self.right; |
|
var l = self.left.evaluate(compressor); |
|
if (l != self.left) { |
|
r.segments[0].value = String(l) + r.segments[0].value; |
|
return r; |
|
} |
|
} |
|
// `1${bar}2` + `foo${bar}baz` => `1${bar}2foo${bar}baz` |
|
if (self.left instanceof AST_TemplateString |
|
&& self.right instanceof AST_TemplateString) { |
|
var l = self.left; |
|
var segments = l.segments; |
|
var r = self.right; |
|
segments[segments.length - 1].value += r.segments[0].value; |
|
for (var i = 1; i < r.segments.length; i++) { |
|
segments.push(r.segments[i]); |
|
} |
|
return l; |
|
} |
|
case "*": |
|
associative = compressor.option("unsafe_math"); |
|
case "&": |
|
case "|": |
|
case "^": |
|
// a + +b => +b + a |
|
if ( |
|
self.left.is_number_or_bigint(compressor) |
|
&& self.right.is_number_or_bigint(compressor) |
|
&& reversible() |
|
&& !(self.left instanceof AST_Binary |
|
&& self.left.operator != self.operator |
|
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { |
|
var reversed = make_node(AST_Binary, self, { |
|
operator: self.operator, |
|
left: self.right, |
|
right: self.left |
|
}); |
|
if (self.right instanceof AST_Constant |
|
&& !(self.left instanceof AST_Constant)) { |
|
self = best_of(compressor, reversed, self); |
|
} else { |
|
self = best_of(compressor, self, reversed); |
|
} |
|
} |
|
if (associative && self.is_number_or_bigint(compressor)) { |
|
// a + (b + c) => (a + b) + c |
|
if (self.right instanceof AST_Binary |
|
&& self.right.operator == self.operator) { |
|
self = make_node(AST_Binary, self, { |
|
operator: self.operator, |
|
left: make_node(AST_Binary, self.left, { |
|
operator: self.operator, |
|
left: self.left, |
|
right: self.right.left, |
|
start: self.left.start, |
|
end: self.right.left.end |
|
}), |
|
right: self.right.right |
|
}); |
|
} |
|
// (n + 2) + 3 => 5 + n |
|
// (2 * n) * 3 => 6 + n |
|
if (self.right instanceof AST_Constant |
|
&& self.left instanceof AST_Binary |
|
&& self.left.operator == self.operator) { |
|
if (self.left.left instanceof AST_Constant) { |
|
self = make_node(AST_Binary, self, { |
|
operator: self.operator, |
|
left: make_node(AST_Binary, self.left, { |
|
operator: self.operator, |
|
left: self.left.left, |
|
right: self.right, |
|
start: self.left.left.start, |
|
end: self.right.end |
|
}), |
|
right: self.left.right |
|
}); |
|
} else if (self.left.right instanceof AST_Constant) { |
|
self = make_node(AST_Binary, self, { |
|
operator: self.operator, |
|
left: make_node(AST_Binary, self.left, { |
|
operator: self.operator, |
|
left: self.left.right, |
|
right: self.right, |
|
start: self.left.right.start, |
|
end: self.right.end |
|
}), |
|
right: self.left.left |
|
}); |
|
} |
|
} |
|
// (a | 1) | (2 | d) => (3 | a) | b |
|
if (self.left instanceof AST_Binary |
|
&& self.left.operator == self.operator |
|
&& self.left.right instanceof AST_Constant |
|
&& self.right instanceof AST_Binary |
|
&& self.right.operator == self.operator |
|
&& self.right.left instanceof AST_Constant) { |
|
self = make_node(AST_Binary, self, { |
|
operator: self.operator, |
|
left: make_node(AST_Binary, self.left, { |
|
operator: self.operator, |
|
left: make_node(AST_Binary, self.left.left, { |
|
operator: self.operator, |
|
left: self.left.right, |
|
right: self.right.left, |
|
start: self.left.right.start, |
|
end: self.right.left.end |
|
}), |
|
right: self.left.left |
|
}), |
|
right: self.right.right |
|
}); |
|
} |
|
} |
|
} |
|
|
|
// bitwise ops |
|
if (bitwise_binop.has(self.operator)) { |
|
// Use De Morgan's laws |
|
// z & (X | y) |
|
// => z & X (given y & z === 0) |
|
// => z & X | {y & z} (given y & z !== 0) |
|
let y, z, x_node, y_node, z_node = self.left; |
|
if ( |
|
self.operator === "&" |
|
&& self.right instanceof AST_Binary |
|
&& self.right.operator === "|" |
|
&& typeof (z = self.left.evaluate(compressor)) === "number" |
|
) { |
|
if (typeof (y = self.right.right.evaluate(compressor)) === "number") { |
|
// z & (X | y) |
|
x_node = self.right.left; |
|
y_node = self.right.right; |
|
} else if (typeof (y = self.right.left.evaluate(compressor)) === "number") { |
|
// z & (y | X) |
|
x_node = self.right.right; |
|
y_node = self.right.left; |
|
} |
|
|
|
if (x_node && y_node) { |
|
if ((y & z) === 0) { |
|
self = make_node(AST_Binary, self, { |
|
operator: self.operator, |
|
left: z_node, |
|
right: x_node |
|
}); |
|
} else { |
|
const reordered_ops = make_node(AST_Binary, self, { |
|
operator: "|", |
|
left: make_node(AST_Binary, self, { |
|
operator: "&", |
|
left: x_node, |
|
right: z_node |
|
}), |
|
right: make_node_from_constant(y & z, y_node), |
|
}); |
|
|
|
self = best_of(compressor, self, reordered_ops); |
|
} |
|
} |
|
} |
|
|
|
// x | x => 0 | x |
|
// x & x => 0 | x |
|
if ( |
|
(self.operator === "|" || self.operator === "&") |
|
&& self.left.equivalent_to(self.right) |
|
&& !self.left.has_side_effects(compressor) |
|
&& compressor.in_32_bit_context(true) |
|
) { |
|
self.left = make_node(AST_Number, self, { value: 0 }); |
|
self.operator = "|"; |
|
} |
|
|
|
// ~x ^ ~y => x ^ y |
|
if ( |
|
self.operator === "^" |
|
&& self.left instanceof AST_UnaryPrefix |
|
&& self.left.operator === "~" |
|
&& self.right instanceof AST_UnaryPrefix |
|
&& self.right.operator === "~" |
|
) { |
|
self = make_node(AST_Binary, self, { |
|
operator: "^", |
|
left: self.left.expression, |
|
right: self.right.expression |
|
}); |
|
} |
|
|
|
|
|
// Shifts that do nothing |
|
// {anything} >> 0 => {anything} | 0 |
|
// {anything} << 0 => {anything} | 0 |
|
if ( |
|
(self.operator === "<<" || self.operator === ">>") |
|
&& self.right instanceof AST_Number && self.right.value === 0 |
|
) { |
|
self.operator = "|"; |
|
} |
|
|
|
// Find useless to-bitwise conversions |
|
// {32 bit integer} | 0 => {32 bit integer} |
|
// {32 bit integer} ^ 0 => {32 bit integer} |
|
const zero_side = self.right instanceof AST_Number && self.right.value === 0 ? self.right |
|
: self.left instanceof AST_Number && self.left.value === 0 ? self.left |
|
: null; |
|
const non_zero_side = zero_side && (zero_side === self.right ? self.left : self.right); |
|
if ( |
|
zero_side |
|
&& (self.operator === "|" || self.operator === "^") |
|
&& (non_zero_side.is_32_bit_integer(compressor) || compressor.in_32_bit_context(true)) |
|
) { |
|
return non_zero_side; |
|
} |
|
|
|
// {anything} & 0 => 0 |
|
if ( |
|
zero_side |
|
&& self.operator === "&" |
|
&& !non_zero_side.has_side_effects(compressor) |
|
&& non_zero_side.is_32_bit_integer(compressor) |
|
) { |
|
return zero_side; |
|
} |
|
|
|
// ~0 is all ones, as well as -1. |
|
// We can ellide some operations with it. |
|
const is_full_mask = (node) => |
|
node instanceof AST_Number && node.value === -1 |
|
|| |
|
node instanceof AST_UnaryPrefix |
|
&& node.operator === "-" |
|
&& node.expression instanceof AST_Number |
|
&& node.expression.value === 1; |
|
|
|
const full_mask = is_full_mask(self.right) ? self.right |
|
: is_full_mask(self.left) ? self.left |
|
: null; |
|
const other_side = (full_mask === self.right ? self.left : self.right); |
|
|
|
// {32 bit integer} & -1 => {32 bit integer} |
|
if ( |
|
full_mask |
|
&& self.operator === "&" |
|
&& ( |
|
other_side.is_32_bit_integer(compressor) |
|
|| compressor.in_32_bit_context(true) |
|
) |
|
) { |
|
return other_side; |
|
} |
|
|
|
// {anything} ^ -1 => ~{anything} |
|
if ( |
|
full_mask |
|
&& self.operator === "^" |
|
&& ( |
|
other_side.is_32_bit_integer(compressor) |
|
|| compressor.in_32_bit_context(true) |
|
) |
|
) { |
|
return other_side.bitwise_negate(compressor); |
|
} |
|
} |
|
} |
|
// x && (y && z) ==> x && y && z |
|
// x || (y || z) ==> x || y || z |
|
// x + ("y" + z) ==> x + "y" + z |
|
// "x" + (y + "z")==> "x" + y + "z" |
|
if (self.right instanceof AST_Binary |
|
&& self.right.operator == self.operator |
|
&& (lazy_op.has(self.operator) |
|
|| (self.operator == "+" |
|
&& (self.right.left.is_string(compressor) |
|
|| (self.left.is_string(compressor) |
|
&& self.right.right.is_string(compressor))))) |
|
) { |
|
self.left = make_node(AST_Binary, self.left, { |
|
operator : self.operator, |
|
left : self.left.transform(compressor), |
|
right : self.right.left.transform(compressor) |
|
}); |
|
self.right = self.right.right.transform(compressor); |
|
return self.transform(compressor); |
|
} |
|
var ev = self.evaluate(compressor); |
|
if (ev !== self) { |
|
ev = make_node_from_constant(ev, self).optimize(compressor); |
|
return best_of(compressor, ev, self); |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_SymbolExport, function(self) { |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_SymbolRef, function(self, compressor) { |
|
if ( |
|
!compressor.option("ie8") |
|
&& is_undeclared_ref(self) |
|
&& !compressor.find_parent(AST_With) |
|
) { |
|
switch (self.name) { |
|
case "undefined": |
|
return make_node(AST_Undefined, self).optimize(compressor); |
|
case "NaN": |
|
return make_node(AST_NaN, self).optimize(compressor); |
|
case "Infinity": |
|
return make_node(AST_Infinity, self).optimize(compressor); |
|
} |
|
} |
|
|
|
if (compressor.option("reduce_vars") && !compressor.is_lhs()) { |
|
return inline_into_symbolref(self, compressor); |
|
} else { |
|
return self; |
|
} |
|
}); |
|
|
|
function is_atomic(lhs, self) { |
|
return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE; |
|
} |
|
|
|
/** Apply the `unsafe_undefined` option: find a variable called `undefined` and turn `self` into a reference to it. */ |
|
function unsafe_undefined_ref(self, compressor) { |
|
if (compressor.option("unsafe_undefined")) { |
|
var undef = find_variable(compressor, "undefined"); |
|
if (undef) { |
|
var ref = make_node(AST_SymbolRef, self, { |
|
name : "undefined", |
|
scope : undef.scope, |
|
thedef : undef |
|
}); |
|
set_flag(ref, UNDEFINED); |
|
return ref; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
def_optimize(AST_Undefined, function(self, compressor) { |
|
var symbolref = unsafe_undefined_ref(self, compressor); |
|
if (symbolref) return symbolref; |
|
var lhs = compressor.is_lhs(); |
|
if (lhs && is_atomic(lhs, self)) return self; |
|
return make_void_0(self); |
|
}); |
|
|
|
def_optimize(AST_Infinity, function(self, compressor) { |
|
var lhs = compressor.is_lhs(); |
|
if (lhs && is_atomic(lhs, self)) return self; |
|
if ( |
|
compressor.option("keep_infinity") |
|
&& !(lhs && !is_atomic(lhs, self)) |
|
&& !find_variable(compressor, "Infinity") |
|
) { |
|
return self; |
|
} |
|
return make_node(AST_Binary, self, { |
|
operator: "/", |
|
left: make_node(AST_Number, self, { |
|
value: 1 |
|
}), |
|
right: make_node(AST_Number, self, { |
|
value: 0 |
|
}) |
|
}); |
|
}); |
|
|
|
def_optimize(AST_NaN, function(self, compressor) { |
|
var lhs = compressor.is_lhs(); |
|
if (lhs && !is_atomic(lhs, self) |
|
|| find_variable(compressor, "NaN")) { |
|
return make_node(AST_Binary, self, { |
|
operator: "/", |
|
left: make_node(AST_Number, self, { |
|
value: 0 |
|
}), |
|
right: make_node(AST_Number, self, { |
|
value: 0 |
|
}) |
|
}); |
|
} |
|
return self; |
|
}); |
|
|
|
const ASSIGN_OPS = makePredicate("+ - / * % >> << >>> | ^ &"); |
|
const ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &"); |
|
def_optimize(AST_Assign, function(self, compressor) { |
|
if (self.logical) { |
|
return self.lift_sequences(compressor); |
|
} |
|
|
|
var def; |
|
// x = x ---> x |
|
if ( |
|
self.operator === "=" |
|
&& self.left instanceof AST_SymbolRef |
|
&& self.left.name !== "arguments" |
|
&& !(def = self.left.definition()).undeclared |
|
&& self.right.equivalent_to(self.left) |
|
) { |
|
return self.right; |
|
} |
|
|
|
if (compressor.option("dead_code") |
|
&& self.left instanceof AST_SymbolRef |
|
&& (def = self.left.definition()).scope === compressor.find_parent(AST_Lambda)) { |
|
var level = 0, node, parent = self; |
|
do { |
|
node = parent; |
|
parent = compressor.parent(level++); |
|
if (parent instanceof AST_Exit) { |
|
if (in_try(level, parent)) break; |
|
if (is_reachable(def.scope, [ def ])) break; |
|
if (self.operator == "=") return self.right; |
|
def.fixed = false; |
|
return make_node(AST_Binary, self, { |
|
operator: self.operator.slice(0, -1), |
|
left: self.left, |
|
right: self.right |
|
}).optimize(compressor); |
|
} |
|
} while (parent instanceof AST_Binary && parent.right === node |
|
|| parent instanceof AST_Sequence && parent.tail_node() === node); |
|
} |
|
self = self.lift_sequences(compressor); |
|
|
|
if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) { |
|
// x = expr1 OP expr2 |
|
if (self.right.left instanceof AST_SymbolRef |
|
&& self.right.left.name == self.left.name |
|
&& ASSIGN_OPS.has(self.right.operator)) { |
|
// x = x - 2 ---> x -= 2 |
|
self.operator = self.right.operator + "="; |
|
self.right = self.right.right; |
|
} else if (self.right.right instanceof AST_SymbolRef |
|
&& self.right.right.name == self.left.name |
|
&& ASSIGN_OPS_COMMUTATIVE.has(self.right.operator) |
|
&& !self.right.left.has_side_effects(compressor)) { |
|
// x = 2 & x ---> x &= 2 |
|
self.operator = self.right.operator + "="; |
|
self.right = self.right.left; |
|
} |
|
} |
|
return self; |
|
|
|
function in_try(level, node) { |
|
function may_assignment_throw() { |
|
const right = self.right; |
|
self.right = make_node(AST_Null, right); |
|
const may_throw = node.may_throw(compressor); |
|
self.right = right; |
|
|
|
return may_throw; |
|
} |
|
|
|
var stop_at = self.left.definition().scope.get_defun_scope(); |
|
var parent; |
|
while ((parent = compressor.parent(level++)) !== stop_at) { |
|
if (parent instanceof AST_Try) { |
|
if (parent.bfinally) return true; |
|
if (parent.bcatch && may_assignment_throw()) return true; |
|
} |
|
} |
|
} |
|
}); |
|
|
|
def_optimize(AST_DefaultAssign, function(self, compressor) { |
|
if (!compressor.option("evaluate")) { |
|
return self; |
|
} |
|
var evaluateRight = self.right.evaluate(compressor); |
|
|
|
// `[x = undefined] = foo` ---> `[x] = foo` |
|
// `(arg = undefined) => ...` ---> `(arg) => ...` (unless `keep_fargs`) |
|
// `((arg = undefined) => ...)()` ---> `((arg) => ...)()` |
|
let lambda, iife; |
|
if (evaluateRight === undefined) { |
|
if ( |
|
(lambda = compressor.parent()) instanceof AST_Lambda |
|
? ( |
|
compressor.option("keep_fargs") === false |
|
|| (iife = compressor.parent(1)).TYPE === "Call" |
|
&& iife.expression === lambda |
|
) |
|
: true |
|
) { |
|
self = self.left; |
|
} |
|
} else if (evaluateRight !== self.right) { |
|
evaluateRight = make_node_from_constant(evaluateRight, self.right); |
|
self.right = best_of_expression(evaluateRight, self.right); |
|
} |
|
|
|
return self; |
|
}); |
|
|
|
function is_nullish_check(check, check_subject, compressor) { |
|
if (check_subject.may_throw(compressor)) return false; |
|
|
|
let nullish_side; |
|
|
|
// foo == null |
|
if ( |
|
check instanceof AST_Binary |
|
&& check.operator === "==" |
|
// which side is nullish? |
|
&& ( |
|
(nullish_side = is_nullish(check.left, compressor) && check.left) |
|
|| (nullish_side = is_nullish(check.right, compressor) && check.right) |
|
) |
|
// is the other side the same as the check_subject |
|
&& ( |
|
nullish_side === check.left |
|
? check.right |
|
: check.left |
|
).equivalent_to(check_subject) |
|
) { |
|
return true; |
|
} |
|
|
|
// foo === null || foo === undefined |
|
if (check instanceof AST_Binary && check.operator === "||") { |
|
let null_cmp; |
|
let undefined_cmp; |
|
|
|
const find_comparison = cmp => { |
|
if (!( |
|
cmp instanceof AST_Binary |
|
&& (cmp.operator === "===" || cmp.operator === "==") |
|
)) { |
|
return false; |
|
} |
|
|
|
let found = 0; |
|
let defined_side; |
|
|
|
if (cmp.left instanceof AST_Null) { |
|
found++; |
|
null_cmp = cmp; |
|
defined_side = cmp.right; |
|
} |
|
if (cmp.right instanceof AST_Null) { |
|
found++; |
|
null_cmp = cmp; |
|
defined_side = cmp.left; |
|
} |
|
if (is_undefined(cmp.left, compressor)) { |
|
found++; |
|
undefined_cmp = cmp; |
|
defined_side = cmp.right; |
|
} |
|
if (is_undefined(cmp.right, compressor)) { |
|
found++; |
|
undefined_cmp = cmp; |
|
defined_side = cmp.left; |
|
} |
|
|
|
if (found !== 1) { |
|
return false; |
|
} |
|
|
|
if (!defined_side.equivalent_to(check_subject)) { |
|
return false; |
|
} |
|
|
|
return true; |
|
}; |
|
|
|
if (!find_comparison(check.left)) return false; |
|
if (!find_comparison(check.right)) return false; |
|
|
|
if (null_cmp && undefined_cmp && null_cmp !== undefined_cmp) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
def_optimize(AST_Conditional, function(self, compressor) { |
|
if (!compressor.option("conditionals")) return self; |
|
// This looks like lift_sequences(), should probably be under "sequences" |
|
if (self.condition instanceof AST_Sequence) { |
|
var expressions = self.condition.expressions.slice(); |
|
self.condition = expressions.pop(); |
|
expressions.push(self); |
|
return make_sequence(self, expressions); |
|
} |
|
var cond = self.condition.evaluate(compressor); |
|
if (cond !== self.condition) { |
|
if (cond) { |
|
return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent); |
|
} else { |
|
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative); |
|
} |
|
} |
|
var negated = cond.negate(compressor, first_in_statement(compressor)); |
|
if (best_of(compressor, cond, negated) === negated) { |
|
self = make_node(AST_Conditional, self, { |
|
condition: negated, |
|
consequent: self.alternative, |
|
alternative: self.consequent |
|
}); |
|
} |
|
var condition = self.condition; |
|
var consequent = self.consequent; |
|
var alternative = self.alternative; |
|
// x?x:y --> x||y |
|
if (condition instanceof AST_SymbolRef |
|
&& consequent instanceof AST_SymbolRef |
|
&& condition.definition() === consequent.definition()) { |
|
return make_node(AST_Binary, self, { |
|
operator: "||", |
|
left: condition, |
|
right: alternative |
|
}); |
|
} |
|
// if (foo) exp = something; else exp = something_else; |
|
// | |
|
// v |
|
// exp = foo ? something : something_else; |
|
if ( |
|
consequent instanceof AST_Assign |
|
&& alternative instanceof AST_Assign |
|
&& consequent.operator === alternative.operator |
|
&& consequent.logical === alternative.logical |
|
&& consequent.left.equivalent_to(alternative.left) |
|
&& (!self.condition.has_side_effects(compressor) |
|
|| consequent.operator == "=" |
|
&& !consequent.left.has_side_effects(compressor)) |
|
) { |
|
return make_node(AST_Assign, self, { |
|
operator: consequent.operator, |
|
left: consequent.left, |
|
logical: consequent.logical, |
|
right: make_node(AST_Conditional, self, { |
|
condition: self.condition, |
|
consequent: consequent.right, |
|
alternative: alternative.right |
|
}) |
|
}); |
|
} |
|
// x ? y(a) : y(b) --> y(x ? a : b) |
|
var arg_index; |
|
if (consequent instanceof AST_Call |
|
&& alternative.TYPE === consequent.TYPE |
|
&& consequent.args.length > 0 |
|
&& consequent.args.length == alternative.args.length |
|
&& consequent.expression.equivalent_to(alternative.expression) |
|
&& !self.condition.has_side_effects(compressor) |
|
&& !consequent.expression.has_side_effects(compressor) |
|
&& typeof (arg_index = single_arg_diff()) == "number") { |
|
var node = consequent.clone(); |
|
node.args[arg_index] = make_node(AST_Conditional, self, { |
|
condition: self.condition, |
|
consequent: consequent.args[arg_index], |
|
alternative: alternative.args[arg_index] |
|
}); |
|
return node; |
|
} |
|
// a ? b : c ? b : d --> (a || c) ? b : d |
|
if (alternative instanceof AST_Conditional |
|
&& consequent.equivalent_to(alternative.consequent)) { |
|
return make_node(AST_Conditional, self, { |
|
condition: make_node(AST_Binary, self, { |
|
operator: "||", |
|
left: condition, |
|
right: alternative.condition |
|
}), |
|
consequent: consequent, |
|
alternative: alternative.alternative |
|
}).optimize(compressor); |
|
} |
|
|
|
// a == null ? b : a -> a ?? b |
|
if ( |
|
compressor.option("ecma") >= 2020 && |
|
is_nullish_check(condition, alternative, compressor) |
|
) { |
|
return make_node(AST_Binary, self, { |
|
operator: "??", |
|
left: alternative, |
|
right: consequent |
|
}).optimize(compressor); |
|
} |
|
|
|
// a ? b : (c, b) --> (a || c), b |
|
if (alternative instanceof AST_Sequence |
|
&& consequent.equivalent_to(alternative.expressions[alternative.expressions.length - 1])) { |
|
return make_sequence(self, [ |
|
make_node(AST_Binary, self, { |
|
operator: "||", |
|
left: condition, |
|
right: make_sequence(self, alternative.expressions.slice(0, -1)) |
|
}), |
|
consequent |
|
]).optimize(compressor); |
|
} |
|
// a ? b : (c && b) --> (a || c) && b |
|
if (alternative instanceof AST_Binary |
|
&& alternative.operator == "&&" |
|
&& consequent.equivalent_to(alternative.right)) { |
|
return make_node(AST_Binary, self, { |
|
operator: "&&", |
|
left: make_node(AST_Binary, self, { |
|
operator: "||", |
|
left: condition, |
|
right: alternative.left |
|
}), |
|
right: consequent |
|
}).optimize(compressor); |
|
} |
|
// x?y?z:a:a --> x&&y?z:a |
|
if (consequent instanceof AST_Conditional |
|
&& consequent.alternative.equivalent_to(alternative)) { |
|
return make_node(AST_Conditional, self, { |
|
condition: make_node(AST_Binary, self, { |
|
left: self.condition, |
|
operator: "&&", |
|
right: consequent.condition |
|
}), |
|
consequent: consequent.consequent, |
|
alternative: alternative |
|
}); |
|
} |
|
// x ? y : y --> x, y |
|
if (consequent.equivalent_to(alternative)) { |
|
return make_sequence(self, [ |
|
self.condition, |
|
consequent |
|
]).optimize(compressor); |
|
} |
|
// x ? y || z : z --> x && y || z |
|
if (consequent instanceof AST_Binary |
|
&& consequent.operator == "||" |
|
&& consequent.right.equivalent_to(alternative)) { |
|
return make_node(AST_Binary, self, { |
|
operator: "||", |
|
left: make_node(AST_Binary, self, { |
|
operator: "&&", |
|
left: self.condition, |
|
right: consequent.left |
|
}), |
|
right: alternative |
|
}).optimize(compressor); |
|
} |
|
|
|
const in_bool = compressor.in_boolean_context(); |
|
if (is_true(self.consequent)) { |
|
if (is_false(self.alternative)) { |
|
// c ? true : false ---> !!c |
|
return booleanize(self.condition); |
|
} |
|
// c ? true : x ---> !!c || x |
|
return make_node(AST_Binary, self, { |
|
operator: "||", |
|
left: booleanize(self.condition), |
|
right: self.alternative |
|
}); |
|
} |
|
if (is_false(self.consequent)) { |
|
if (is_true(self.alternative)) { |
|
// c ? false : true ---> !c |
|
return booleanize(self.condition.negate(compressor)); |
|
} |
|
// c ? false : x ---> !c && x |
|
return make_node(AST_Binary, self, { |
|
operator: "&&", |
|
left: booleanize(self.condition.negate(compressor)), |
|
right: self.alternative |
|
}); |
|
} |
|
if (is_true(self.alternative)) { |
|
// c ? x : true ---> !c || x |
|
return make_node(AST_Binary, self, { |
|
operator: "||", |
|
left: booleanize(self.condition.negate(compressor)), |
|
right: self.consequent |
|
}); |
|
} |
|
if (is_false(self.alternative)) { |
|
// c ? x : false ---> !!c && x |
|
return make_node(AST_Binary, self, { |
|
operator: "&&", |
|
left: booleanize(self.condition), |
|
right: self.consequent |
|
}); |
|
} |
|
|
|
return self; |
|
|
|
function booleanize(node) { |
|
if (node.is_boolean()) return node; |
|
// !!expression |
|
return make_node(AST_UnaryPrefix, node, { |
|
operator: "!", |
|
expression: node.negate(compressor) |
|
}); |
|
} |
|
|
|
// AST_True or !0 |
|
function is_true(node) { |
|
return node instanceof AST_True |
|
|| in_bool |
|
&& node instanceof AST_Constant |
|
&& node.getValue() |
|
|| (node instanceof AST_UnaryPrefix |
|
&& node.operator == "!" |
|
&& node.expression instanceof AST_Constant |
|
&& !node.expression.getValue()); |
|
} |
|
// AST_False or !1 |
|
function is_false(node) { |
|
return node instanceof AST_False |
|
|| in_bool |
|
&& node instanceof AST_Constant |
|
&& !node.getValue() |
|
|| (node instanceof AST_UnaryPrefix |
|
&& node.operator == "!" |
|
&& node.expression instanceof AST_Constant |
|
&& node.expression.getValue()); |
|
} |
|
|
|
function single_arg_diff() { |
|
var a = consequent.args; |
|
var b = alternative.args; |
|
for (var i = 0, len = a.length; i < len; i++) { |
|
if (a[i] instanceof AST_Expansion) return; |
|
if (!a[i].equivalent_to(b[i])) { |
|
if (b[i] instanceof AST_Expansion) return; |
|
for (var j = i + 1; j < len; j++) { |
|
if (a[j] instanceof AST_Expansion) return; |
|
if (!a[j].equivalent_to(b[j])) return; |
|
} |
|
return i; |
|
} |
|
} |
|
} |
|
}); |
|
|
|
def_optimize(AST_Boolean, function(self, compressor) { |
|
if (compressor.in_boolean_context()) return make_node(AST_Number, self, { |
|
value: +self.value |
|
}); |
|
var p = compressor.parent(); |
|
if (compressor.option("booleans_as_integers")) { |
|
if (p instanceof AST_Binary && (p.operator == "===" || p.operator == "!==")) { |
|
p.operator = p.operator.replace(/=$/, ""); |
|
} |
|
return make_node(AST_Number, self, { |
|
value: +self.value |
|
}); |
|
} |
|
if (compressor.option("booleans")) { |
|
if (p instanceof AST_Binary && (p.operator == "==" |
|
|| p.operator == "!=")) { |
|
return make_node(AST_Number, self, { |
|
value: +self.value |
|
}); |
|
} |
|
return make_node(AST_UnaryPrefix, self, { |
|
operator: "!", |
|
expression: make_node(AST_Number, self, { |
|
value: 1 - self.value |
|
}) |
|
}); |
|
} |
|
return self; |
|
}); |
|
|
|
function safe_to_flatten(value, compressor) { |
|
if (value instanceof AST_SymbolRef) { |
|
value = value.fixed_value(); |
|
} |
|
if (!value) return false; |
|
if (!(value instanceof AST_Lambda || value instanceof AST_Class)) return true; |
|
if (!(value instanceof AST_Lambda && value.contains_this())) return true; |
|
return compressor.parent() instanceof AST_New; |
|
} |
|
|
|
AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) { |
|
if (!compressor.option("properties")) return; |
|
if (key === "__proto__") return; |
|
if (this instanceof AST_DotHash) return; |
|
|
|
var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015; |
|
var expr = this.expression; |
|
if (expr instanceof AST_Object) { |
|
var props = expr.properties; |
|
|
|
for (var i = props.length; --i >= 0;) { |
|
var prop = props[i]; |
|
|
|
if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) { |
|
const all_props_flattenable = props.every((p) => |
|
(p instanceof AST_ObjectKeyVal |
|
|| arrows && p instanceof AST_ConciseMethod && !p.value.is_generator |
|
) |
|
&& !p.computed_key() |
|
); |
|
|
|
if (!all_props_flattenable) return; |
|
if (!safe_to_flatten(prop.value, compressor)) return; |
|
|
|
return make_node(AST_Sub, this, { |
|
expression: make_node(AST_Array, expr, { |
|
elements: props.map(function(prop) { |
|
var v = prop.value; |
|
if (v instanceof AST_Accessor) { |
|
v = make_node(AST_Function, v, v); |
|
} |
|
|
|
var k = prop.key; |
|
if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) { |
|
return make_sequence(prop, [ k, v ]); |
|
} |
|
|
|
return v; |
|
}) |
|
}), |
|
property: make_node(AST_Number, this, { |
|
value: i |
|
}) |
|
}); |
|
} |
|
} |
|
} |
|
}); |
|
|
|
def_optimize(AST_Sub, function(self, compressor) { |
|
var expr = self.expression; |
|
var prop = self.property; |
|
if (compressor.option("properties")) { |
|
var key = prop.evaluate(compressor); |
|
if (key !== prop) { |
|
if (typeof key == "string") { |
|
if (key == "undefined") { |
|
key = undefined; |
|
} else { |
|
var value = parseFloat(key); |
|
if (value.toString() == key) { |
|
key = value; |
|
} |
|
} |
|
} |
|
prop = self.property = best_of_expression( |
|
prop, |
|
make_node_from_constant(key, prop).transform(compressor) |
|
); |
|
var property = "" + key; |
|
if (is_basic_identifier_string(property) |
|
&& property.length <= prop.size() + 1) { |
|
return make_node(AST_Dot, self, { |
|
expression: expr, |
|
optional: self.optional, |
|
property: property, |
|
quote: prop.quote, |
|
}).optimize(compressor); |
|
} |
|
} |
|
} |
|
var fn; |
|
OPT_ARGUMENTS: if (compressor.option("arguments") |
|
&& expr instanceof AST_SymbolRef |
|
&& expr.name == "arguments" |
|
&& expr.definition().orig.length == 1 |
|
&& (fn = expr.scope) instanceof AST_Lambda |
|
&& fn.uses_arguments |
|
&& !(fn instanceof AST_Arrow) |
|
&& prop instanceof AST_Number) { |
|
var index = prop.getValue(); |
|
var params = new Set(); |
|
var argnames = fn.argnames; |
|
for (var n = 0; n < argnames.length; n++) { |
|
if (!(argnames[n] instanceof AST_SymbolFunarg)) { |
|
break OPT_ARGUMENTS; // destructuring parameter - bail |
|
} |
|
var param = argnames[n].name; |
|
if (params.has(param)) { |
|
break OPT_ARGUMENTS; // duplicate parameter - bail |
|
} |
|
params.add(param); |
|
} |
|
var argname = fn.argnames[index]; |
|
if (argname && compressor.has_directive("use strict")) { |
|
var def = argname.definition(); |
|
if (!compressor.option("reduce_vars") || def.assignments || def.orig.length > 1) { |
|
argname = null; |
|
} |
|
} else if (!argname && !compressor.option("keep_fargs") && index < fn.argnames.length + 5) { |
|
while (index >= fn.argnames.length) { |
|
argname = fn.create_symbol(AST_SymbolFunarg, { |
|
source: fn, |
|
scope: fn, |
|
tentative_name: "argument_" + fn.argnames.length, |
|
}); |
|
fn.argnames.push(argname); |
|
} |
|
} |
|
if (argname) { |
|
var sym = make_node(AST_SymbolRef, self, argname); |
|
sym.reference({}); |
|
clear_flag(argname, UNUSED); |
|
return sym; |
|
} |
|
} |
|
if (compressor.is_lhs()) return self; |
|
if (key !== prop) { |
|
var sub = self.flatten_object(property, compressor); |
|
if (sub) { |
|
expr = self.expression = sub.expression; |
|
prop = self.property = sub.property; |
|
} |
|
} |
|
if (compressor.option("properties") && compressor.option("side_effects") |
|
&& prop instanceof AST_Number && expr instanceof AST_Array) { |
|
var index = prop.getValue(); |
|
var elements = expr.elements; |
|
var retValue = elements[index]; |
|
FLATTEN: if (safe_to_flatten(retValue, compressor)) { |
|
var flatten = true; |
|
var values = []; |
|
for (var i = elements.length; --i > index;) { |
|
var value = elements[i].drop_side_effect_free(compressor); |
|
if (value) { |
|
values.unshift(value); |
|
if (flatten && value.has_side_effects(compressor)) flatten = false; |
|
} |
|
} |
|
if (retValue instanceof AST_Expansion) break FLATTEN; |
|
retValue = retValue instanceof AST_Hole ? make_void_0(retValue) : retValue; |
|
if (!flatten) values.unshift(retValue); |
|
while (--i >= 0) { |
|
var value = elements[i]; |
|
if (value instanceof AST_Expansion) break FLATTEN; |
|
value = value.drop_side_effect_free(compressor); |
|
if (value) values.unshift(value); |
|
else index--; |
|
} |
|
if (flatten) { |
|
values.push(retValue); |
|
return make_sequence(self, values).optimize(compressor); |
|
} else return make_node(AST_Sub, self, { |
|
expression: make_node(AST_Array, expr, { |
|
elements: values |
|
}), |
|
property: make_node(AST_Number, prop, { |
|
value: index |
|
}) |
|
}); |
|
} |
|
} |
|
var ev = self.evaluate(compressor); |
|
if (ev !== self) { |
|
ev = make_node_from_constant(ev, self).optimize(compressor); |
|
return best_of(compressor, ev, self); |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Chain, function (self, compressor) { |
|
if (is_nullish(self.expression, compressor)) { |
|
let parent = compressor.parent(); |
|
// It's valid to delete a nullish optional chain, but if we optimized |
|
// this to `delete undefined` then it would appear to be a syntax error |
|
// when we try to optimize the delete. Thankfully, `delete 0` is fine. |
|
if (parent instanceof AST_UnaryPrefix && parent.operator === "delete") { |
|
return make_node_from_constant(0, self); |
|
} |
|
return make_void_0(self).optimize(compressor); |
|
} |
|
if ( |
|
self.expression instanceof AST_PropAccess |
|
|| self.expression instanceof AST_Call |
|
) { |
|
return self; |
|
} else { |
|
// Keep the AST valid, in case the child swapped itself |
|
return self.expression; |
|
} |
|
}); |
|
|
|
def_optimize(AST_Dot, function(self, compressor) { |
|
const parent = compressor.parent(); |
|
if (compressor.is_lhs()) return self; |
|
if (compressor.option("unsafe_proto") |
|
&& self.expression instanceof AST_Dot |
|
&& self.expression.property == "prototype") { |
|
var exp = self.expression.expression; |
|
if (is_undeclared_ref(exp)) switch (exp.name) { |
|
case "Array": |
|
self.expression = make_node(AST_Array, self.expression, { |
|
elements: [] |
|
}); |
|
break; |
|
case "Function": |
|
self.expression = make_empty_function(self.expression); |
|
break; |
|
case "Number": |
|
self.expression = make_node(AST_Number, self.expression, { |
|
value: 0 |
|
}); |
|
break; |
|
case "Object": |
|
self.expression = make_node(AST_Object, self.expression, { |
|
properties: [] |
|
}); |
|
break; |
|
case "RegExp": |
|
self.expression = make_node(AST_RegExp, self.expression, { |
|
value: { source: "t", flags: "" } |
|
}); |
|
break; |
|
case "String": |
|
self.expression = make_node(AST_String, self.expression, { |
|
value: "" |
|
}); |
|
break; |
|
} |
|
} |
|
if (!(parent instanceof AST_Call) || !has_annotation(parent, _NOINLINE)) { |
|
const sub = self.flatten_object(self.property, compressor); |
|
if (sub) return sub.optimize(compressor); |
|
} |
|
|
|
if (self.expression instanceof AST_PropAccess |
|
&& parent instanceof AST_PropAccess) { |
|
return self; |
|
} |
|
|
|
let ev = self.evaluate(compressor); |
|
if (ev !== self) { |
|
ev = make_node_from_constant(ev, self).optimize(compressor); |
|
return best_of(compressor, ev, self); |
|
} |
|
return self; |
|
}); |
|
|
|
function literals_in_boolean_context(self, compressor) { |
|
if (compressor.in_boolean_context()) { |
|
return best_of(compressor, self, make_sequence(self, [ |
|
self, |
|
make_node(AST_True, self) |
|
]).optimize(compressor)); |
|
} |
|
return self; |
|
} |
|
|
|
function inline_array_like_spread(elements) { |
|
for (var i = 0; i < elements.length; i++) { |
|
var el = elements[i]; |
|
if (el instanceof AST_Expansion) { |
|
var expr = el.expression; |
|
if ( |
|
expr instanceof AST_Array |
|
&& !expr.elements.some(elm => elm instanceof AST_Hole) |
|
) { |
|
elements.splice(i, 1, ...expr.elements); |
|
// Step back one, as the element at i is now new. |
|
i--; |
|
} |
|
// In array-like spread, spreading a non-iterable value is TypeError. |
|
// We therefore can’t optimize anything else, unlike with object spread. |
|
} |
|
} |
|
} |
|
|
|
def_optimize(AST_Array, function(self, compressor) { |
|
var optimized = literals_in_boolean_context(self, compressor); |
|
if (optimized !== self) { |
|
return optimized; |
|
} |
|
inline_array_like_spread(self.elements); |
|
return self; |
|
}); |
|
|
|
function inline_object_prop_spread(props) { |
|
for (var i = 0; i < props.length; i++) { |
|
var prop = props[i]; |
|
if (prop instanceof AST_Expansion) { |
|
const expr = prop.expression; |
|
if ( |
|
expr instanceof AST_Object |
|
&& expr.properties.every(prop => prop instanceof AST_ObjectKeyVal) |
|
) { |
|
props.splice(i, 1, ...expr.properties); |
|
// Step back one, as the property at i is now new. |
|
i--; |
|
} else if (( |
|
// `expr.is_constant()` returns `false` for `AST_RegExp`, so need both. |
|
expr instanceof AST_Constant |
|
|| expr.is_constant() |
|
) && !(expr instanceof AST_String)) { |
|
// Unlike array-like spread, in object spread, spreading a |
|
// non-iterable value silently does nothing; it is thus safe |
|
// to remove. AST_String is the only iterable constant. |
|
props.splice(i, 1); |
|
i--; |
|
} |
|
} |
|
} |
|
} |
|
|
|
def_optimize(AST_Object, function(self, compressor) { |
|
var optimized = literals_in_boolean_context(self, compressor); |
|
if (optimized !== self) { |
|
return optimized; |
|
} |
|
inline_object_prop_spread(self.properties); |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_RegExp, literals_in_boolean_context); |
|
|
|
def_optimize(AST_Return, function(self, compressor) { |
|
if (self.value && is_undefined(self.value, compressor)) { |
|
self.value = null; |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Arrow, opt_AST_Lambda); |
|
|
|
def_optimize(AST_Function, function(self, compressor) { |
|
self = opt_AST_Lambda(self, compressor); |
|
if (compressor.option("unsafe_arrows") |
|
&& compressor.option("ecma") >= 2015 |
|
&& !self.name |
|
&& !self.is_generator |
|
&& !self.uses_arguments |
|
&& !self.pinned()) { |
|
const uses_this = walk(self, node => { |
|
if (node instanceof AST_This) return walk_abort; |
|
}); |
|
if (!uses_this) return make_node(AST_Arrow, self, self).optimize(compressor); |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Class, function(self) { |
|
for (let i = 0; i < self.properties.length; i++) { |
|
const prop = self.properties[i]; |
|
if (prop instanceof AST_ClassStaticBlock && prop.body.length == 0) { |
|
self.properties.splice(i, 1); |
|
i--; |
|
} |
|
} |
|
|
|
return self; |
|
}); |
|
|
|
def_optimize(AST_ClassStaticBlock, function(self, compressor) { |
|
tighten_body(self.body, compressor); |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Yield, function(self, compressor) { |
|
if (self.expression && !self.is_star && is_undefined(self.expression, compressor)) { |
|
self.expression = null; |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_TemplateString, function(self, compressor) { |
|
if ( |
|
!compressor.option("evaluate") |
|
|| compressor.parent() instanceof AST_PrefixedTemplateString |
|
) { |
|
return self; |
|
} |
|
|
|
var segments = []; |
|
for (var i = 0; i < self.segments.length; i++) { |
|
var segment = self.segments[i]; |
|
if (segment instanceof AST_Node) { |
|
var result = segment.evaluate(compressor); |
|
// Evaluate to constant value |
|
// Constant value shorter than ${segment} |
|
if (result !== segment && (result + "").length <= segment.size() + "${}".length) { |
|
// There should always be a previous and next segment if segment is a node |
|
segments[segments.length - 1].value = segments[segments.length - 1].value + result + self.segments[++i].value; |
|
continue; |
|
} |
|
// `before ${`innerBefore ${any} innerAfter`} after` => `before innerBefore ${any} innerAfter after` |
|
// TODO: |
|
// `before ${'test' + foo} after` => `before innerBefore ${any} innerAfter after` |
|
// `before ${foo + 'test} after` => `before innerBefore ${any} innerAfter after` |
|
if (segment instanceof AST_TemplateString) { |
|
var inners = segment.segments; |
|
segments[segments.length - 1].value += inners[0].value; |
|
for (var j = 1; j < inners.length; j++) { |
|
segment = inners[j]; |
|
segments.push(segment); |
|
} |
|
continue; |
|
} |
|
} |
|
segments.push(segment); |
|
} |
|
self.segments = segments; |
|
|
|
// `foo` => "foo" |
|
if (segments.length == 1) { |
|
return make_node(AST_String, self, segments[0]); |
|
} |
|
|
|
if ( |
|
segments.length === 3 |
|
&& segments[1] instanceof AST_Node |
|
&& ( |
|
segments[1].is_string(compressor) |
|
|| segments[1].is_number_or_bigint(compressor) |
|
|| is_nullish(segments[1], compressor) |
|
|| compressor.option("unsafe") |
|
) |
|
) { |
|
// `foo${bar}` => "foo" + bar |
|
if (segments[2].value === "") { |
|
return make_node(AST_Binary, self, { |
|
operator: "+", |
|
left: make_node(AST_String, self, { |
|
value: segments[0].value, |
|
}), |
|
right: segments[1], |
|
}); |
|
} |
|
// `${bar}baz` => bar + "baz" |
|
if (segments[0].value === "") { |
|
return make_node(AST_Binary, self, { |
|
operator: "+", |
|
left: segments[1], |
|
right: make_node(AST_String, self, { |
|
value: segments[2].value, |
|
}), |
|
}); |
|
} |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_PrefixedTemplateString, function(self) { |
|
return self; |
|
}); |
|
|
|
// ["p"]:1 ---> p:1 |
|
// [42]:1 ---> 42:1 |
|
function lift_key(self, compressor) { |
|
if (!compressor.option("computed_props")) return self; |
|
// save a comparison in the typical case |
|
if (!(self.key instanceof AST_Constant)) return self; |
|
// allow certain acceptable props as not all AST_Constants are true constants |
|
if (self.key instanceof AST_String || self.key instanceof AST_Number) { |
|
const key = self.key.value.toString(); |
|
|
|
if (key === "__proto__") return self; |
|
if (key == "constructor" |
|
&& compressor.parent() instanceof AST_Class) return self; |
|
if (self instanceof AST_ObjectKeyVal) { |
|
self.quote = self.key.quote; |
|
self.key = key; |
|
} else if (self instanceof AST_ClassProperty) { |
|
self.quote = self.key.quote; |
|
self.key = make_node(AST_SymbolClassProperty, self.key, { |
|
name: key, |
|
}); |
|
} else { |
|
self.quote = self.key.quote; |
|
self.key = make_node(AST_SymbolMethod, self.key, { |
|
name: key, |
|
}); |
|
} |
|
} |
|
return self; |
|
} |
|
|
|
def_optimize(AST_ObjectProperty, lift_key); |
|
|
|
def_optimize(AST_ConciseMethod, function(self, compressor) { |
|
lift_key(self, compressor); |
|
// p(){return x;} ---> p:()=>x |
|
if (compressor.option("arrows") |
|
&& compressor.parent() instanceof AST_Object |
|
&& !self.value.is_generator |
|
&& !self.value.uses_arguments |
|
&& !self.value.pinned() |
|
&& self.value.body.length == 1 |
|
&& self.value.body[0] instanceof AST_Return |
|
&& self.value.body[0].value |
|
&& !self.value.contains_this()) { |
|
var arrow = make_node(AST_Arrow, self.value, self.value); |
|
arrow.async = self.value.async; |
|
arrow.is_generator = self.value.is_generator; |
|
return make_node(AST_ObjectKeyVal, self, { |
|
key: self.key instanceof AST_SymbolMethod ? self.key.name : self.key, |
|
value: arrow, |
|
quote: self.quote, |
|
}); |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_ObjectKeyVal, function(self, compressor) { |
|
lift_key(self, compressor); |
|
// p:function(){} ---> p(){} |
|
// p:function*(){} ---> *p(){} |
|
// p:async function(){} ---> async p(){} |
|
// p:()=>{} ---> p(){} |
|
// p:async()=>{} ---> async p(){} |
|
var unsafe_methods = compressor.option("unsafe_methods"); |
|
if (unsafe_methods |
|
&& compressor.option("ecma") >= 2015 |
|
&& (!(unsafe_methods instanceof RegExp) || unsafe_methods.test(self.key + ""))) { |
|
var key = self.key; |
|
var value = self.value; |
|
var is_arrow_with_block = value instanceof AST_Arrow |
|
&& Array.isArray(value.body) |
|
&& !value.contains_this(); |
|
if ((is_arrow_with_block || value instanceof AST_Function) && !value.name) { |
|
return make_node(AST_ConciseMethod, self, { |
|
key: key instanceof AST_Node ? key : make_node(AST_SymbolMethod, self, { |
|
name: key, |
|
}), |
|
value: make_node(AST_Accessor, value, value), |
|
quote: self.quote, |
|
}); |
|
} |
|
} |
|
return self; |
|
}); |
|
|
|
def_optimize(AST_Destructuring, function(self, compressor) { |
|
if (compressor.option("pure_getters") == true |
|
&& compressor.option("unused") |
|
&& !self.is_array |
|
&& Array.isArray(self.names) |
|
&& !is_destructuring_export_decl(compressor) |
|
&& !(self.names[self.names.length - 1] instanceof AST_Expansion)) { |
|
var keep = []; |
|
for (var i = 0; i < self.names.length; i++) { |
|
var elem = self.names[i]; |
|
if (!(elem instanceof AST_ObjectKeyVal |
|
&& typeof elem.key == "string" |
|
&& elem.value instanceof AST_SymbolDeclaration |
|
&& !should_retain(compressor, elem.value.definition()))) { |
|
keep.push(elem); |
|
} |
|
} |
|
if (keep.length != self.names.length) { |
|
self.names = keep; |
|
} |
|
} |
|
return self; |
|
|
|
function is_destructuring_export_decl(compressor) { |
|
var ancestors = [/^VarDef$/, /^(Const|Let|Var)$/, /^Export$/]; |
|
for (var a = 0, p = 0, len = ancestors.length; a < len; p++) { |
|
var parent = compressor.parent(p); |
|
if (!parent) return false; |
|
if (a === 0 && parent.TYPE == "Destructuring") continue; |
|
if (!ancestors[a].test(parent.TYPE)) { |
|
return false; |
|
} |
|
a++; |
|
} |
|
return true; |
|
} |
|
|
|
function should_retain(compressor, def) { |
|
if (def.references.length) return true; |
|
if (!def.global) return false; |
|
if (compressor.toplevel.vars) { |
|
if (compressor.top_retain) { |
|
return compressor.top_retain(def); |
|
} |
|
return false; |
|
} |
|
return true; |
|
} |
|
}); |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
// a small wrapper around source-map and @jridgewell/source-map |
|
function* SourceMap(options) { |
|
options = defaults(options, { |
|
file : null, |
|
root : null, |
|
orig : null, |
|
files: {}, |
|
}); |
|
|
|
var orig_map; |
|
var generator = new sourceMap.SourceMapGenerator({ |
|
file : options.file, |
|
sourceRoot : options.root |
|
}); |
|
|
|
let sourcesContent = {__proto__: null}; |
|
let files = options.files; |
|
for (var name in files) if (HOP(files, name)) { |
|
sourcesContent[name] = files[name]; |
|
} |
|
if (options.orig) { |
|
// We support both @jridgewell/source-map (which has a sync |
|
// SourceMapConsumer) and source-map (which has an async |
|
// SourceMapConsumer). |
|
orig_map = yield new sourceMap.SourceMapConsumer(options.orig); |
|
if (orig_map.sourcesContent) { |
|
orig_map.sources.forEach(function(source, i) { |
|
var content = orig_map.sourcesContent[i]; |
|
if (content) { |
|
sourcesContent[source] = content; |
|
} |
|
}); |
|
} |
|
} |
|
|
|
function add(source, gen_line, gen_col, orig_line, orig_col, name) { |
|
let generatedPos = { line: gen_line, column: gen_col }; |
|
|
|
if (orig_map) { |
|
var info = orig_map.originalPositionFor({ |
|
line: orig_line, |
|
column: orig_col |
|
}); |
|
if (info.source === null) { |
|
generator.addMapping({ |
|
generated: generatedPos, |
|
original: null, |
|
source: null, |
|
name: null |
|
}); |
|
return; |
|
} |
|
source = info.source; |
|
orig_line = info.line; |
|
orig_col = info.column; |
|
name = info.name || name; |
|
} |
|
generator.addMapping({ |
|
generated : generatedPos, |
|
original : { line: orig_line, column: orig_col }, |
|
source : source, |
|
name : name |
|
}); |
|
generator.setSourceContent(source, sourcesContent[source]); |
|
} |
|
|
|
function clean(map) { |
|
const allNull = map.sourcesContent && map.sourcesContent.every(c => c == null); |
|
if (allNull) delete map.sourcesContent; |
|
if (map.file === undefined) delete map.file; |
|
if (map.sourceRoot === undefined) delete map.sourceRoot; |
|
return map; |
|
} |
|
|
|
function getDecoded() { |
|
if (!generator.toDecodedMap) return null; |
|
return clean(generator.toDecodedMap()); |
|
} |
|
|
|
function getEncoded() { |
|
return clean(generator.toJSON()); |
|
} |
|
|
|
function destroy() { |
|
// @jridgewell/source-map's SourceMapConsumer does not need to be |
|
// manually freed. |
|
if (orig_map && orig_map.destroy) orig_map.destroy(); |
|
} |
|
|
|
return { |
|
add, |
|
getDecoded, |
|
getEncoded, |
|
destroy, |
|
}; |
|
} |
|
|
|
var domprops = [ |
|
"$&", |
|
"$'", |
|
"$*", |
|
"$+", |
|
"$1", |
|
"$2", |
|
"$3", |
|
"$4", |
|
"$5", |
|
"$6", |
|
"$7", |
|
"$8", |
|
"$9", |
|
"$_", |
|
"$`", |
|
"$input", |
|
"-moz-animation", |
|
"-moz-animation-delay", |
|
"-moz-animation-direction", |
|
"-moz-animation-duration", |
|
"-moz-animation-fill-mode", |
|
"-moz-animation-iteration-count", |
|
"-moz-animation-name", |
|
"-moz-animation-play-state", |
|
"-moz-animation-timing-function", |
|
"-moz-appearance", |
|
"-moz-backface-visibility", |
|
"-moz-border-end", |
|
"-moz-border-end-color", |
|
"-moz-border-end-style", |
|
"-moz-border-end-width", |
|
"-moz-border-image", |
|
"-moz-border-start", |
|
"-moz-border-start-color", |
|
"-moz-border-start-style", |
|
"-moz-border-start-width", |
|
"-moz-box-align", |
|
"-moz-box-direction", |
|
"-moz-box-flex", |
|
"-moz-box-ordinal-group", |
|
"-moz-box-orient", |
|
"-moz-box-pack", |
|
"-moz-box-sizing", |
|
"-moz-float-edge", |
|
"-moz-font-feature-settings", |
|
"-moz-font-language-override", |
|
"-moz-force-broken-image-icon", |
|
"-moz-hyphens", |
|
"-moz-image-region", |
|
"-moz-margin-end", |
|
"-moz-margin-start", |
|
"-moz-orient", |
|
"-moz-osx-font-smoothing", |
|
"-moz-outline-radius", |
|
"-moz-outline-radius-bottomleft", |
|
"-moz-outline-radius-bottomright", |
|
"-moz-outline-radius-topleft", |
|
"-moz-outline-radius-topright", |
|
"-moz-padding-end", |
|
"-moz-padding-start", |
|
"-moz-perspective", |
|
"-moz-perspective-origin", |
|
"-moz-tab-size", |
|
"-moz-text-size-adjust", |
|
"-moz-transform", |
|
"-moz-transform-origin", |
|
"-moz-transform-style", |
|
"-moz-transition", |
|
"-moz-transition-delay", |
|
"-moz-transition-duration", |
|
"-moz-transition-property", |
|
"-moz-transition-timing-function", |
|
"-moz-user-focus", |
|
"-moz-user-input", |
|
"-moz-user-modify", |
|
"-moz-user-select", |
|
"-moz-window-dragging", |
|
"-webkit-align-content", |
|
"-webkit-align-items", |
|
"-webkit-align-self", |
|
"-webkit-animation", |
|
"-webkit-animation-delay", |
|
"-webkit-animation-direction", |
|
"-webkit-animation-duration", |
|
"-webkit-animation-fill-mode", |
|
"-webkit-animation-iteration-count", |
|
"-webkit-animation-name", |
|
"-webkit-animation-play-state", |
|
"-webkit-animation-timing-function", |
|
"-webkit-appearance", |
|
"-webkit-backface-visibility", |
|
"-webkit-background-clip", |
|
"-webkit-background-origin", |
|
"-webkit-background-size", |
|
"-webkit-border-bottom-left-radius", |
|
"-webkit-border-bottom-right-radius", |
|
"-webkit-border-image", |
|
"-webkit-border-radius", |
|
"-webkit-border-top-left-radius", |
|
"-webkit-border-top-right-radius", |
|
"-webkit-box-align", |
|
"-webkit-box-direction", |
|
"-webkit-box-flex", |
|
"-webkit-box-ordinal-group", |
|
"-webkit-box-orient", |
|
"-webkit-box-pack", |
|
"-webkit-box-shadow", |
|
"-webkit-box-sizing", |
|
"-webkit-clip-path", |
|
"-webkit-filter", |
|
"-webkit-flex", |
|
"-webkit-flex-basis", |
|
"-webkit-flex-direction", |
|
"-webkit-flex-flow", |
|
"-webkit-flex-grow", |
|
"-webkit-flex-shrink", |
|
"-webkit-flex-wrap", |
|
"-webkit-font-feature-settings", |
|
"-webkit-justify-content", |
|
"-webkit-line-clamp", |
|
"-webkit-mask", |
|
"-webkit-mask-clip", |
|
"-webkit-mask-composite", |
|
"-webkit-mask-image", |
|
"-webkit-mask-origin", |
|
"-webkit-mask-position", |
|
"-webkit-mask-position-x", |
|
"-webkit-mask-position-y", |
|
"-webkit-mask-repeat", |
|
"-webkit-mask-size", |
|
"-webkit-order", |
|
"-webkit-perspective", |
|
"-webkit-perspective-origin", |
|
"-webkit-text-fill-color", |
|
"-webkit-text-security", |
|
"-webkit-text-size-adjust", |
|
"-webkit-text-stroke", |
|
"-webkit-text-stroke-color", |
|
"-webkit-text-stroke-width", |
|
"-webkit-transform", |
|
"-webkit-transform-origin", |
|
"-webkit-transform-style", |
|
"-webkit-transition", |
|
"-webkit-transition-delay", |
|
"-webkit-transition-duration", |
|
"-webkit-transition-property", |
|
"-webkit-transition-timing-function", |
|
"-webkit-user-select", |
|
"@@iterator", |
|
"ABORT_ERR", |
|
"ACTIVE", |
|
"ACTIVE_ATTRIBUTES", |
|
"ACTIVE_TEXTURE", |
|
"ACTIVE_UNIFORMS", |
|
"ACTIVE_UNIFORM_BLOCKS", |
|
"ADDITION", |
|
"ALIASED_LINE_WIDTH_RANGE", |
|
"ALIASED_POINT_SIZE_RANGE", |
|
"ALL", |
|
"ALLOW_KEYBOARD_INPUT", |
|
"ALLPASS", |
|
"ALPHA", |
|
"ALPHA_BITS", |
|
"ALREADY_SIGNALED", |
|
"ALT_MASK", |
|
"ALWAYS", |
|
"ANY_SAMPLES_PASSED", |
|
"ANY_SAMPLES_PASSED_CONSERVATIVE", |
|
"ANY_TYPE", |
|
"ANY_UNORDERED_NODE_TYPE", |
|
"ARRAY_BUFFER", |
|
"ARRAY_BUFFER_BINDING", |
|
"ATTACHED_SHADERS", |
|
"ATTRIBUTE_NODE", |
|
"AT_TARGET", |
|
"AbortController", |
|
"AbortSignal", |
|
"AbsoluteOrientationSensor", |
|
"AbstractRange", |
|
"Accelerometer", |
|
"AddSearchProvider", |
|
"AggregateError", |
|
"AnalyserNode", |
|
"Animation", |
|
"AnimationEffect", |
|
"AnimationEvent", |
|
"AnimationPlaybackEvent", |
|
"AnimationTimeline", |
|
"AnonXMLHttpRequest", |
|
"Any", |
|
"AnyPermissions", |
|
"ApplicationCache", |
|
"ApplicationCacheErrorEvent", |
|
"Array", |
|
"ArrayBuffer", |
|
"ArrayType", |
|
"AsyncDisposableStack", |
|
"Atomics", |
|
"Attr", |
|
"Audio", |
|
"AudioBuffer", |
|
"AudioBufferSourceNode", |
|
"AudioContext", |
|
"AudioData", |
|
"AudioDecoder", |
|
"AudioDestinationNode", |
|
"AudioEncoder", |
|
"AudioListener", |
|
"AudioNode", |
|
"AudioParam", |
|
"AudioParamMap", |
|
"AudioProcessingEvent", |
|
"AudioScheduledSourceNode", |
|
"AudioSinkInfo", |
|
"AudioStreamTrack", |
|
"AudioWorklet", |
|
"AudioWorkletNode", |
|
"AuthenticatorAssertionResponse", |
|
"AuthenticatorAttestationResponse", |
|
"AuthenticatorResponse", |
|
"AutocompleteErrorEvent", |
|
"BACK", |
|
"BAD_BOUNDARYPOINTS_ERR", |
|
"BAD_REQUEST", |
|
"BANDPASS", |
|
"BLEND", |
|
"BLEND_COLOR", |
|
"BLEND_DST_ALPHA", |
|
"BLEND_DST_RGB", |
|
"BLEND_EQUATION", |
|
"BLEND_EQUATION_ALPHA", |
|
"BLEND_EQUATION_RGB", |
|
"BLEND_SRC_ALPHA", |
|
"BLEND_SRC_RGB", |
|
"BLUE", |
|
"BLUE_BITS", |
|
"BLUR", |
|
"BOOL", |
|
"BOOLEAN_TYPE", |
|
"BOOL_VEC2", |
|
"BOOL_VEC3", |
|
"BOOL_VEC4", |
|
"BOTH", |
|
"BROWSER_DEFAULT_WEBGL", |
|
"BUBBLING_PHASE", |
|
"BUFFER_SIZE", |
|
"BUFFER_USAGE", |
|
"BYTE", |
|
"BYTES_PER_ELEMENT", |
|
"BackgroundFetchManager", |
|
"BackgroundFetchRecord", |
|
"BackgroundFetchRegistration", |
|
"BarProp", |
|
"BarcodeDetector", |
|
"BaseAudioContext", |
|
"BaseHref", |
|
"BatteryManager", |
|
"BeforeInstallPromptEvent", |
|
"BeforeLoadEvent", |
|
"BeforeUnloadEvent", |
|
"BigInt", |
|
"BigInt64Array", |
|
"BigUint64Array", |
|
"BiquadFilterNode", |
|
"Blob", |
|
"BlobEvent", |
|
"Bluetooth", |
|
"BluetoothCharacteristicProperties", |
|
"BluetoothDevice", |
|
"BluetoothRemoteGATTCharacteristic", |
|
"BluetoothRemoteGATTDescriptor", |
|
"BluetoothRemoteGATTServer", |
|
"BluetoothRemoteGATTService", |
|
"BluetoothUUID", |
|
"Boolean", |
|
"BroadcastChannel", |
|
"BrowserCaptureMediaStreamTrack", |
|
"BrowserInfo", |
|
"ByteLengthQueuingStrategy", |
|
"CAPTURING_PHASE", |
|
"CCW", |
|
"CDATASection", |
|
"CDATA_SECTION_NODE", |
|
"CHANGE", |
|
"CHARSET_RULE", |
|
"CHECKING", |
|
"CLAMP_TO_EDGE", |
|
"CLICK", |
|
"CLOSED", |
|
"CLOSING", |
|
"COLOR", |
|
"COLOR_ATTACHMENT0", |
|
"COLOR_ATTACHMENT1", |
|
"COLOR_ATTACHMENT10", |
|
"COLOR_ATTACHMENT11", |
|
"COLOR_ATTACHMENT12", |
|
"COLOR_ATTACHMENT13", |
|
"COLOR_ATTACHMENT14", |
|
"COLOR_ATTACHMENT15", |
|
"COLOR_ATTACHMENT2", |
|
"COLOR_ATTACHMENT3", |
|
"COLOR_ATTACHMENT4", |
|
"COLOR_ATTACHMENT5", |
|
"COLOR_ATTACHMENT6", |
|
"COLOR_ATTACHMENT7", |
|
"COLOR_ATTACHMENT8", |
|
"COLOR_ATTACHMENT9", |
|
"COLOR_BUFFER_BIT", |
|
"COLOR_CLEAR_VALUE", |
|
"COLOR_WRITEMASK", |
|
"COMMENT_NODE", |
|
"COMPARE_REF_TO_TEXTURE", |
|
"COMPILE_STATUS", |
|
"COMPLETION_STATUS_KHR", |
|
"COMPRESSED_RGBA_S3TC_DXT1_EXT", |
|
"COMPRESSED_RGBA_S3TC_DXT3_EXT", |
|
"COMPRESSED_RGBA_S3TC_DXT5_EXT", |
|
"COMPRESSED_RGB_S3TC_DXT1_EXT", |
|
"COMPRESSED_TEXTURE_FORMATS", |
|
"COMPUTE", |
|
"CONDITION_SATISFIED", |
|
"CONFIGURATION_UNSUPPORTED", |
|
"CONNECTING", |
|
"CONSTANT_ALPHA", |
|
"CONSTANT_COLOR", |
|
"CONSTRAINT_ERR", |
|
"CONTEXT_LOST_WEBGL", |
|
"CONTROL_MASK", |
|
"COPY_DST", |
|
"COPY_READ_BUFFER", |
|
"COPY_READ_BUFFER_BINDING", |
|
"COPY_SRC", |
|
"COPY_WRITE_BUFFER", |
|
"COPY_WRITE_BUFFER_BINDING", |
|
"COUNTER_STYLE_RULE", |
|
"CSPViolationReportBody", |
|
"CSS", |
|
"CSS2Properties", |
|
"CSSAnimation", |
|
"CSSCharsetRule", |
|
"CSSConditionRule", |
|
"CSSContainerRule", |
|
"CSSCounterStyleRule", |
|
"CSSFontFaceRule", |
|
"CSSFontFeatureValuesRule", |
|
"CSSFontPaletteValuesRule", |
|
"CSSFunctionDeclarations", |
|
"CSSFunctionDescriptors", |
|
"CSSFunctionRule", |
|
"CSSGroupingRule", |
|
"CSSImageValue", |
|
"CSSImportRule", |
|
"CSSKeyframeRule", |
|
"CSSKeyframesRule", |
|
"CSSKeywordValue", |
|
"CSSLayerBlockRule", |
|
"CSSLayerStatementRule", |
|
"CSSMarginRule", |
|
"CSSMathClamp", |
|
"CSSMathInvert", |
|
"CSSMathMax", |
|
"CSSMathMin", |
|
"CSSMathNegate", |
|
"CSSMathProduct", |
|
"CSSMathSum", |
|
"CSSMathValue", |
|
"CSSMatrixComponent", |
|
"CSSMediaRule", |
|
"CSSMozDocumentRule", |
|
"CSSNameSpaceRule", |
|
"CSSNamespaceRule", |
|
"CSSNestedDeclarations", |
|
"CSSNumericArray", |
|
"CSSNumericValue", |
|
"CSSPageDescriptors", |
|
"CSSPageRule", |
|
"CSSPerspective", |
|
"CSSPositionTryDescriptors", |
|
"CSSPositionTryRule", |
|
"CSSPositionValue", |
|
"CSSPrimitiveValue", |
|
"CSSPropertyRule", |
|
"CSSRotate", |
|
"CSSRule", |
|
"CSSRuleList", |
|
"CSSScale", |
|
"CSSScopeRule", |
|
"CSSSkew", |
|
"CSSSkewX", |
|
"CSSSkewY", |
|
"CSSStartingStyleRule", |
|
"CSSStyleDeclaration", |
|
"CSSStyleProperties", |
|
"CSSStyleRule", |
|
"CSSStyleSheet", |
|
"CSSStyleValue", |
|
"CSSSupportsRule", |
|
"CSSTransformComponent", |
|
"CSSTransformValue", |
|
"CSSTransition", |
|
"CSSTranslate", |
|
"CSSUnitValue", |
|
"CSSUnknownRule", |
|
"CSSUnparsedValue", |
|
"CSSValue", |
|
"CSSValueList", |
|
"CSSVariableReferenceValue", |
|
"CSSVariablesDeclaration", |
|
"CSSVariablesRule", |
|
"CSSViewTransitionRule", |
|
"CSSViewportRule", |
|
"CSS_ATTR", |
|
"CSS_CM", |
|
"CSS_COUNTER", |
|
"CSS_CUSTOM", |
|
"CSS_DEG", |
|
"CSS_DIMENSION", |
|
"CSS_EMS", |
|
"CSS_EXS", |
|
"CSS_FILTER_BLUR", |
|
"CSS_FILTER_BRIGHTNESS", |
|
"CSS_FILTER_CONTRAST", |
|
"CSS_FILTER_CUSTOM", |
|
"CSS_FILTER_DROP_SHADOW", |
|
"CSS_FILTER_GRAYSCALE", |
|
"CSS_FILTER_HUE_ROTATE", |
|
"CSS_FILTER_INVERT", |
|
"CSS_FILTER_OPACITY", |
|
"CSS_FILTER_REFERENCE", |
|
"CSS_FILTER_SATURATE", |
|
"CSS_FILTER_SEPIA", |
|
"CSS_GRAD", |
|
"CSS_HZ", |
|
"CSS_IDENT", |
|
"CSS_IN", |
|
"CSS_INHERIT", |
|
"CSS_KHZ", |
|
"CSS_MATRIX", |
|
"CSS_MATRIX3D", |
|
"CSS_MM", |
|
"CSS_MS", |
|
"CSS_NUMBER", |
|
"CSS_PC", |
|
"CSS_PERCENTAGE", |
|
"CSS_PERSPECTIVE", |
|
"CSS_PRIMITIVE_VALUE", |
|
"CSS_PT", |
|
"CSS_PX", |
|
"CSS_RAD", |
|
"CSS_RECT", |
|
"CSS_RGBCOLOR", |
|
"CSS_ROTATE", |
|
"CSS_ROTATE3D", |
|
"CSS_ROTATEX", |
|
"CSS_ROTATEY", |
|
"CSS_ROTATEZ", |
|
"CSS_S", |
|
"CSS_SCALE", |
|
"CSS_SCALE3D", |
|
"CSS_SCALEX", |
|
"CSS_SCALEY", |
|
"CSS_SCALEZ", |
|
"CSS_SKEW", |
|
"CSS_SKEWX", |
|
"CSS_SKEWY", |
|
"CSS_STRING", |
|
"CSS_TRANSLATE", |
|
"CSS_TRANSLATE3D", |
|
"CSS_TRANSLATEX", |
|
"CSS_TRANSLATEY", |
|
"CSS_TRANSLATEZ", |
|
"CSS_UNKNOWN", |
|
"CSS_URI", |
|
"CSS_VALUE_LIST", |
|
"CSS_VH", |
|
"CSS_VMAX", |
|
"CSS_VMIN", |
|
"CSS_VW", |
|
"CULL_FACE", |
|
"CULL_FACE_MODE", |
|
"CURRENT_PROGRAM", |
|
"CURRENT_QUERY", |
|
"CURRENT_VERTEX_ATTRIB", |
|
"CUSTOM", |
|
"CW", |
|
"Cache", |
|
"CacheStorage", |
|
"CanvasCaptureMediaStream", |
|
"CanvasCaptureMediaStreamTrack", |
|
"CanvasGradient", |
|
"CanvasPattern", |
|
"CanvasRenderingContext2D", |
|
"CaptureController", |
|
"CaretPosition", |
|
"ChannelMergerNode", |
|
"ChannelSplitterNode", |
|
"ChapterInformation", |
|
"CharacterBoundsUpdateEvent", |
|
"CharacterData", |
|
"ClientRect", |
|
"ClientRectList", |
|
"Clipboard", |
|
"ClipboardEvent", |
|
"ClipboardItem", |
|
"CloseEvent", |
|
"CloseWatcher", |
|
"Collator", |
|
"ColorArray", |
|
"ColorValue", |
|
"CommandEvent", |
|
"Comment", |
|
"CompileError", |
|
"CompositionEvent", |
|
"CompressionStream", |
|
"Console", |
|
"ConstantSourceNode", |
|
"ContentVisibilityAutoStateChangeEvent", |
|
"ContextFilter", |
|
"ContextType", |
|
"Controllers", |
|
"ConvolverNode", |
|
"CookieChangeEvent", |
|
"CookieStore", |
|
"CookieStoreManager", |
|
"CountQueuingStrategy", |
|
"Counter", |
|
"CreateMonitor", |
|
"CreateType", |
|
"Credential", |
|
"CredentialsContainer", |
|
"CropTarget", |
|
"Crypto", |
|
"CryptoKey", |
|
"CustomElementRegistry", |
|
"CustomEvent", |
|
"CustomStateSet", |
|
"DATABASE_ERR", |
|
"DATA_CLONE_ERR", |
|
"DATA_ERR", |
|
"DBLCLICK", |
|
"DECR", |
|
"DECR_WRAP", |
|
"DELETE_STATUS", |
|
"DEPTH", |
|
"DEPTH24_STENCIL8", |
|
"DEPTH32F_STENCIL8", |
|
"DEPTH_ATTACHMENT", |
|
"DEPTH_BITS", |
|
"DEPTH_BUFFER_BIT", |
|
"DEPTH_CLEAR_VALUE", |
|
"DEPTH_COMPONENT", |
|
"DEPTH_COMPONENT16", |
|
"DEPTH_COMPONENT24", |
|
"DEPTH_COMPONENT32F", |
|
"DEPTH_FUNC", |
|
"DEPTH_RANGE", |
|
"DEPTH_STENCIL", |
|
"DEPTH_STENCIL_ATTACHMENT", |
|
"DEPTH_TEST", |
|
"DEPTH_WRITEMASK", |
|
"DEVICE_INELIGIBLE", |
|
"DIRECTION_DOWN", |
|
"DIRECTION_LEFT", |
|
"DIRECTION_RIGHT", |
|
"DIRECTION_UP", |
|
"DISABLED", |
|
"DISPATCH_REQUEST_ERR", |
|
"DITHER", |
|
"DOCUMENT_FRAGMENT_NODE", |
|
"DOCUMENT_NODE", |
|
"DOCUMENT_POSITION_CONTAINED_BY", |
|
"DOCUMENT_POSITION_CONTAINS", |
|
"DOCUMENT_POSITION_DISCONNECTED", |
|
"DOCUMENT_POSITION_FOLLOWING", |
|
"DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", |
|
"DOCUMENT_POSITION_PRECEDING", |
|
"DOCUMENT_TYPE_NODE", |
|
"DOMCursor", |
|
"DOMError", |
|
"DOMException", |
|
"DOMImplementation", |
|
"DOMImplementationLS", |
|
"DOMMatrix", |
|
"DOMMatrixReadOnly", |
|
"DOMParser", |
|
"DOMPoint", |
|
"DOMPointReadOnly", |
|
"DOMQuad", |
|
"DOMRect", |
|
"DOMRectList", |
|
"DOMRectReadOnly", |
|
"DOMRequest", |
|
"DOMSTRING_SIZE_ERR", |
|
"DOMSettableTokenList", |
|
"DOMStringList", |
|
"DOMStringMap", |
|
"DOMTokenList", |
|
"DOMTransactionEvent", |
|
"DOM_DELTA_LINE", |
|
"DOM_DELTA_PAGE", |
|
"DOM_DELTA_PIXEL", |
|
"DOM_INPUT_METHOD_DROP", |
|
"DOM_INPUT_METHOD_HANDWRITING", |
|
"DOM_INPUT_METHOD_IME", |
|
"DOM_INPUT_METHOD_KEYBOARD", |
|
"DOM_INPUT_METHOD_MULTIMODAL", |
|
"DOM_INPUT_METHOD_OPTION", |
|
"DOM_INPUT_METHOD_PASTE", |
|
"DOM_INPUT_METHOD_SCRIPT", |
|
"DOM_INPUT_METHOD_UNKNOWN", |
|
"DOM_INPUT_METHOD_VOICE", |
|
"DOM_KEY_LOCATION_JOYSTICK", |
|
"DOM_KEY_LOCATION_LEFT", |
|
"DOM_KEY_LOCATION_MOBILE", |
|
"DOM_KEY_LOCATION_NUMPAD", |
|
"DOM_KEY_LOCATION_RIGHT", |
|
"DOM_KEY_LOCATION_STANDARD", |
|
"DOM_VK_0", |
|
"DOM_VK_1", |
|
"DOM_VK_2", |
|
"DOM_VK_3", |
|
"DOM_VK_4", |
|
"DOM_VK_5", |
|
"DOM_VK_6", |
|
"DOM_VK_7", |
|
"DOM_VK_8", |
|
"DOM_VK_9", |
|
"DOM_VK_A", |
|
"DOM_VK_ACCEPT", |
|
"DOM_VK_ADD", |
|
"DOM_VK_ALT", |
|
"DOM_VK_ALTGR", |
|
"DOM_VK_AMPERSAND", |
|
"DOM_VK_ASTERISK", |
|
"DOM_VK_AT", |
|
"DOM_VK_ATTN", |
|
"DOM_VK_B", |
|
"DOM_VK_BACKSPACE", |
|
"DOM_VK_BACK_QUOTE", |
|
"DOM_VK_BACK_SLASH", |
|
"DOM_VK_BACK_SPACE", |
|
"DOM_VK_C", |
|
"DOM_VK_CANCEL", |
|
"DOM_VK_CAPS_LOCK", |
|
"DOM_VK_CIRCUMFLEX", |
|
"DOM_VK_CLEAR", |
|
"DOM_VK_CLOSE_BRACKET", |
|
"DOM_VK_CLOSE_CURLY_BRACKET", |
|
"DOM_VK_CLOSE_PAREN", |
|
"DOM_VK_COLON", |
|
"DOM_VK_COMMA", |
|
"DOM_VK_CONTEXT_MENU", |
|
"DOM_VK_CONTROL", |
|
"DOM_VK_CONVERT", |
|
"DOM_VK_CRSEL", |
|
"DOM_VK_CTRL", |
|
"DOM_VK_D", |
|
"DOM_VK_DECIMAL", |
|
"DOM_VK_DELETE", |
|
"DOM_VK_DIVIDE", |
|
"DOM_VK_DOLLAR", |
|
"DOM_VK_DOUBLE_QUOTE", |
|
"DOM_VK_DOWN", |
|
"DOM_VK_E", |
|
"DOM_VK_EISU", |
|
"DOM_VK_END", |
|
"DOM_VK_ENTER", |
|
"DOM_VK_EQUALS", |
|
"DOM_VK_EREOF", |
|
"DOM_VK_ESCAPE", |
|
"DOM_VK_EXCLAMATION", |
|
"DOM_VK_EXECUTE", |
|
"DOM_VK_EXSEL", |
|
"DOM_VK_F", |
|
"DOM_VK_F1", |
|
"DOM_VK_F10", |
|
"DOM_VK_F11", |
|
"DOM_VK_F12", |
|
"DOM_VK_F13", |
|
"DOM_VK_F14", |
|
"DOM_VK_F15", |
|
"DOM_VK_F16", |
|
"DOM_VK_F17", |
|
"DOM_VK_F18", |
|
"DOM_VK_F19", |
|
"DOM_VK_F2", |
|
"DOM_VK_F20", |
|
"DOM_VK_F21", |
|
"DOM_VK_F22", |
|
"DOM_VK_F23", |
|
"DOM_VK_F24", |
|
"DOM_VK_F25", |
|
"DOM_VK_F26", |
|
"DOM_VK_F27", |
|
"DOM_VK_F28", |
|
"DOM_VK_F29", |
|
"DOM_VK_F3", |
|
"DOM_VK_F30", |
|
"DOM_VK_F31", |
|
"DOM_VK_F32", |
|
"DOM_VK_F33", |
|
"DOM_VK_F34", |
|
"DOM_VK_F35", |
|
"DOM_VK_F36", |
|
"DOM_VK_F4", |
|
"DOM_VK_F5", |
|
"DOM_VK_F6", |
|
"DOM_VK_F7", |
|
"DOM_VK_F8", |
|
"DOM_VK_F9", |
|
"DOM_VK_FINAL", |
|
"DOM_VK_FRONT", |
|
"DOM_VK_G", |
|
"DOM_VK_GREATER_THAN", |
|
"DOM_VK_H", |
|
"DOM_VK_HANGUL", |
|
"DOM_VK_HANJA", |
|
"DOM_VK_HASH", |
|
"DOM_VK_HELP", |
|
"DOM_VK_HK_TOGGLE", |
|
"DOM_VK_HOME", |
|
"DOM_VK_HYPHEN_MINUS", |
|
"DOM_VK_I", |
|
"DOM_VK_INSERT", |
|
"DOM_VK_J", |
|
"DOM_VK_JUNJA", |
|
"DOM_VK_K", |
|
"DOM_VK_KANA", |
|
"DOM_VK_KANJI", |
|
"DOM_VK_L", |
|
"DOM_VK_LEFT", |
|
"DOM_VK_LEFT_TAB", |
|
"DOM_VK_LESS_THAN", |
|
"DOM_VK_M", |
|
"DOM_VK_META", |
|
"DOM_VK_MODECHANGE", |
|
"DOM_VK_MULTIPLY", |
|
"DOM_VK_N", |
|
"DOM_VK_NONCONVERT", |
|
"DOM_VK_NUMPAD0", |
|
"DOM_VK_NUMPAD1", |
|
"DOM_VK_NUMPAD2", |
|
"DOM_VK_NUMPAD3", |
|
"DOM_VK_NUMPAD4", |
|
"DOM_VK_NUMPAD5", |
|
"DOM_VK_NUMPAD6", |
|
"DOM_VK_NUMPAD7", |
|
"DOM_VK_NUMPAD8", |
|
"DOM_VK_NUMPAD9", |
|
"DOM_VK_NUM_LOCK", |
|
"DOM_VK_O", |
|
"DOM_VK_OEM_1", |
|
"DOM_VK_OEM_102", |
|
"DOM_VK_OEM_2", |
|
"DOM_VK_OEM_3", |
|
"DOM_VK_OEM_4", |
|
"DOM_VK_OEM_5", |
|
"DOM_VK_OEM_6", |
|
"DOM_VK_OEM_7", |
|
"DOM_VK_OEM_8", |
|
"DOM_VK_OEM_COMMA", |
|
"DOM_VK_OEM_MINUS", |
|
"DOM_VK_OEM_PERIOD", |
|
"DOM_VK_OEM_PLUS", |
|
"DOM_VK_OPEN_BRACKET", |
|
"DOM_VK_OPEN_CURLY_BRACKET", |
|
"DOM_VK_OPEN_PAREN", |
|
"DOM_VK_P", |
|
"DOM_VK_PA1", |
|
"DOM_VK_PAGEDOWN", |
|
"DOM_VK_PAGEUP", |
|
"DOM_VK_PAGE_DOWN", |
|
"DOM_VK_PAGE_UP", |
|
"DOM_VK_PAUSE", |
|
"DOM_VK_PERCENT", |
|
"DOM_VK_PERIOD", |
|
"DOM_VK_PIPE", |
|
"DOM_VK_PLAY", |
|
"DOM_VK_PLUS", |
|
"DOM_VK_PRINT", |
|
"DOM_VK_PRINTSCREEN", |
|
"DOM_VK_PROCESSKEY", |
|
"DOM_VK_PROPERITES", |
|
"DOM_VK_Q", |
|
"DOM_VK_QUESTION_MARK", |
|
"DOM_VK_QUOTE", |
|
"DOM_VK_R", |
|
"DOM_VK_REDO", |
|
"DOM_VK_RETURN", |
|
"DOM_VK_RIGHT", |
|
"DOM_VK_S", |
|
"DOM_VK_SCROLL_LOCK", |
|
"DOM_VK_SELECT", |
|
"DOM_VK_SEMICOLON", |
|
"DOM_VK_SEPARATOR", |
|
"DOM_VK_SHIFT", |
|
"DOM_VK_SLASH", |
|
"DOM_VK_SLEEP", |
|
"DOM_VK_SPACE", |
|
"DOM_VK_SUBTRACT", |
|
"DOM_VK_T", |
|
"DOM_VK_TAB", |
|
"DOM_VK_TILDE", |
|
"DOM_VK_U", |
|
"DOM_VK_UNDERSCORE", |
|
"DOM_VK_UNDO", |
|
"DOM_VK_UNICODE", |
|
"DOM_VK_UP", |
|
"DOM_VK_V", |
|
"DOM_VK_VOLUME_DOWN", |
|
"DOM_VK_VOLUME_MUTE", |
|
"DOM_VK_VOLUME_UP", |
|
"DOM_VK_W", |
|
"DOM_VK_WIN", |
|
"DOM_VK_WINDOW", |
|
"DOM_VK_WIN_ICO_00", |
|
"DOM_VK_WIN_ICO_CLEAR", |
|
"DOM_VK_WIN_ICO_HELP", |
|
"DOM_VK_WIN_OEM_ATTN", |
|
"DOM_VK_WIN_OEM_AUTO", |
|
"DOM_VK_WIN_OEM_BACKTAB", |
|
"DOM_VK_WIN_OEM_CLEAR", |
|
"DOM_VK_WIN_OEM_COPY", |
|
"DOM_VK_WIN_OEM_CUSEL", |
|
"DOM_VK_WIN_OEM_ENLW", |
|
"DOM_VK_WIN_OEM_FINISH", |
|
"DOM_VK_WIN_OEM_FJ_JISHO", |
|
"DOM_VK_WIN_OEM_FJ_LOYA", |
|
"DOM_VK_WIN_OEM_FJ_MASSHOU", |
|
"DOM_VK_WIN_OEM_FJ_ROYA", |
|
"DOM_VK_WIN_OEM_FJ_TOUROKU", |
|
"DOM_VK_WIN_OEM_JUMP", |
|
"DOM_VK_WIN_OEM_PA1", |
|
"DOM_VK_WIN_OEM_PA2", |
|
"DOM_VK_WIN_OEM_PA3", |
|
"DOM_VK_WIN_OEM_RESET", |
|
"DOM_VK_WIN_OEM_WSCTRL", |
|
"DOM_VK_X", |
|
"DOM_VK_XF86XK_ADD_FAVORITE", |
|
"DOM_VK_XF86XK_APPLICATION_LEFT", |
|
"DOM_VK_XF86XK_APPLICATION_RIGHT", |
|
"DOM_VK_XF86XK_AUDIO_CYCLE_TRACK", |
|
"DOM_VK_XF86XK_AUDIO_FORWARD", |
|
"DOM_VK_XF86XK_AUDIO_LOWER_VOLUME", |
|
"DOM_VK_XF86XK_AUDIO_MEDIA", |
|
"DOM_VK_XF86XK_AUDIO_MUTE", |
|
"DOM_VK_XF86XK_AUDIO_NEXT", |
|
"DOM_VK_XF86XK_AUDIO_PAUSE", |
|
"DOM_VK_XF86XK_AUDIO_PLAY", |
|
"DOM_VK_XF86XK_AUDIO_PREV", |
|
"DOM_VK_XF86XK_AUDIO_RAISE_VOLUME", |
|
"DOM_VK_XF86XK_AUDIO_RANDOM_PLAY", |
|
"DOM_VK_XF86XK_AUDIO_RECORD", |
|
"DOM_VK_XF86XK_AUDIO_REPEAT", |
|
"DOM_VK_XF86XK_AUDIO_REWIND", |
|
"DOM_VK_XF86XK_AUDIO_STOP", |
|
"DOM_VK_XF86XK_AWAY", |
|
"DOM_VK_XF86XK_BACK", |
|
"DOM_VK_XF86XK_BACK_FORWARD", |
|
"DOM_VK_XF86XK_BATTERY", |
|
"DOM_VK_XF86XK_BLUE", |
|
"DOM_VK_XF86XK_BLUETOOTH", |
|
"DOM_VK_XF86XK_BOOK", |
|
"DOM_VK_XF86XK_BRIGHTNESS_ADJUST", |
|
"DOM_VK_XF86XK_CALCULATOR", |
|
"DOM_VK_XF86XK_CALENDAR", |
|
"DOM_VK_XF86XK_CD", |
|
"DOM_VK_XF86XK_CLOSE", |
|
"DOM_VK_XF86XK_COMMUNITY", |
|
"DOM_VK_XF86XK_CONTRAST_ADJUST", |
|
"DOM_VK_XF86XK_COPY", |
|
"DOM_VK_XF86XK_CUT", |
|
"DOM_VK_XF86XK_CYCLE_ANGLE", |
|
"DOM_VK_XF86XK_DISPLAY", |
|
"DOM_VK_XF86XK_DOCUMENTS", |
|
"DOM_VK_XF86XK_DOS", |
|
"DOM_VK_XF86XK_EJECT", |
|
"DOM_VK_XF86XK_EXCEL", |
|
"DOM_VK_XF86XK_EXPLORER", |
|
"DOM_VK_XF86XK_FAVORITES", |
|
"DOM_VK_XF86XK_FINANCE", |
|
"DOM_VK_XF86XK_FORWARD", |
|
"DOM_VK_XF86XK_FRAME_BACK", |
|
"DOM_VK_XF86XK_FRAME_FORWARD", |
|
"DOM_VK_XF86XK_GAME", |
|
"DOM_VK_XF86XK_GO", |
|
"DOM_VK_XF86XK_GREEN", |
|
"DOM_VK_XF86XK_HIBERNATE", |
|
"DOM_VK_XF86XK_HISTORY", |
|
"DOM_VK_XF86XK_HOME_PAGE", |
|
"DOM_VK_XF86XK_HOT_LINKS", |
|
"DOM_VK_XF86XK_I_TOUCH", |
|
"DOM_VK_XF86XK_KBD_BRIGHTNESS_DOWN", |
|
"DOM_VK_XF86XK_KBD_BRIGHTNESS_UP", |
|
"DOM_VK_XF86XK_KBD_LIGHT_ON_OFF", |
|
"DOM_VK_XF86XK_LAUNCH0", |
|
"DOM_VK_XF86XK_LAUNCH1", |
|
"DOM_VK_XF86XK_LAUNCH2", |
|
"DOM_VK_XF86XK_LAUNCH3", |
|
"DOM_VK_XF86XK_LAUNCH4", |
|
"DOM_VK_XF86XK_LAUNCH5", |
|
"DOM_VK_XF86XK_LAUNCH6", |
|
"DOM_VK_XF86XK_LAUNCH7", |
|
"DOM_VK_XF86XK_LAUNCH8", |
|
"DOM_VK_XF86XK_LAUNCH9", |
|
"DOM_VK_XF86XK_LAUNCH_A", |
|
"DOM_VK_XF86XK_LAUNCH_B", |
|
"DOM_VK_XF86XK_LAUNCH_C", |
|
"DOM_VK_XF86XK_LAUNCH_D", |
|
"DOM_VK_XF86XK_LAUNCH_E", |
|
"DOM_VK_XF86XK_LAUNCH_F", |
|
"DOM_VK_XF86XK_LIGHT_BULB", |
|
"DOM_VK_XF86XK_LOG_OFF", |
|
"DOM_VK_XF86XK_MAIL", |
|
"DOM_VK_XF86XK_MAIL_FORWARD", |
|
"DOM_VK_XF86XK_MARKET", |
|
"DOM_VK_XF86XK_MEETING", |
|
"DOM_VK_XF86XK_MEMO", |
|
"DOM_VK_XF86XK_MENU_KB", |
|
"DOM_VK_XF86XK_MENU_PB", |
|
"DOM_VK_XF86XK_MESSENGER", |
|
"DOM_VK_XF86XK_MON_BRIGHTNESS_DOWN", |
|
"DOM_VK_XF86XK_MON_BRIGHTNESS_UP", |
|
"DOM_VK_XF86XK_MUSIC", |
|
"DOM_VK_XF86XK_MY_COMPUTER", |
|
"DOM_VK_XF86XK_MY_SITES", |
|
"DOM_VK_XF86XK_NEW", |
|
"DOM_VK_XF86XK_NEWS", |
|
"DOM_VK_XF86XK_OFFICE_HOME", |
|
"DOM_VK_XF86XK_OPEN", |
|
"DOM_VK_XF86XK_OPEN_URL", |
|
"DOM_VK_XF86XK_OPTION", |
|
"DOM_VK_XF86XK_PASTE", |
|
"DOM_VK_XF86XK_PHONE", |
|
"DOM_VK_XF86XK_PICTURES", |
|
"DOM_VK_XF86XK_POWER_DOWN", |
|
"DOM_VK_XF86XK_POWER_OFF", |
|
"DOM_VK_XF86XK_RED", |
|
"DOM_VK_XF86XK_REFRESH", |
|
"DOM_VK_XF86XK_RELOAD", |
|
"DOM_VK_XF86XK_REPLY", |
|
"DOM_VK_XF86XK_ROCKER_DOWN", |
|
"DOM_VK_XF86XK_ROCKER_ENTER", |
|
"DOM_VK_XF86XK_ROCKER_UP", |
|
"DOM_VK_XF86XK_ROTATE_WINDOWS", |
|
"DOM_VK_XF86XK_ROTATION_KB", |
|
"DOM_VK_XF86XK_ROTATION_PB", |
|
"DOM_VK_XF86XK_SAVE", |
|
"DOM_VK_XF86XK_SCREEN_SAVER", |
|
"DOM_VK_XF86XK_SCROLL_CLICK", |
|
"DOM_VK_XF86XK_SCROLL_DOWN", |
|
"DOM_VK_XF86XK_SCROLL_UP", |
|
"DOM_VK_XF86XK_SEARCH", |
|
"DOM_VK_XF86XK_SEND", |
|
"DOM_VK_XF86XK_SHOP", |
|
"DOM_VK_XF86XK_SPELL", |
|
"DOM_VK_XF86XK_SPLIT_SCREEN", |
|
"DOM_VK_XF86XK_STANDBY", |
|
"DOM_VK_XF86XK_START", |
|
"DOM_VK_XF86XK_STOP", |
|
"DOM_VK_XF86XK_SUBTITLE", |
|
"DOM_VK_XF86XK_SUPPORT", |
|
"DOM_VK_XF86XK_SUSPEND", |
|
"DOM_VK_XF86XK_TASK_PANE", |
|
"DOM_VK_XF86XK_TERMINAL", |
|
"DOM_VK_XF86XK_TIME", |
|
"DOM_VK_XF86XK_TOOLS", |
|
"DOM_VK_XF86XK_TOP_MENU", |
|
"DOM_VK_XF86XK_TO_DO_LIST", |
|
"DOM_VK_XF86XK_TRAVEL", |
|
"DOM_VK_XF86XK_USER1KB", |
|
"DOM_VK_XF86XK_USER2KB", |
|
"DOM_VK_XF86XK_USER_PB", |
|
"DOM_VK_XF86XK_UWB", |
|
"DOM_VK_XF86XK_VENDOR_HOME", |
|
"DOM_VK_XF86XK_VIDEO", |
|
"DOM_VK_XF86XK_VIEW", |
|
"DOM_VK_XF86XK_WAKE_UP", |
|
"DOM_VK_XF86XK_WEB_CAM", |
|
"DOM_VK_XF86XK_WHEEL_BUTTON", |
|
"DOM_VK_XF86XK_WLAN", |
|
"DOM_VK_XF86XK_WORD", |
|
"DOM_VK_XF86XK_WWW", |
|
"DOM_VK_XF86XK_XFER", |
|
"DOM_VK_XF86XK_YELLOW", |
|
"DOM_VK_XF86XK_ZOOM_IN", |
|
"DOM_VK_XF86XK_ZOOM_OUT", |
|
"DOM_VK_Y", |
|
"DOM_VK_Z", |
|
"DOM_VK_ZOOM", |
|
"DONE", |
|
"DONT_CARE", |
|
"DOWNLOADING", |
|
"DRAGDROP", |
|
"DRAW_BUFFER0", |
|
"DRAW_BUFFER1", |
|
"DRAW_BUFFER10", |
|
"DRAW_BUFFER11", |
|
"DRAW_BUFFER12", |
|
"DRAW_BUFFER13", |
|
"DRAW_BUFFER14", |
|
"DRAW_BUFFER15", |
|
"DRAW_BUFFER2", |
|
"DRAW_BUFFER3", |
|
"DRAW_BUFFER4", |
|
"DRAW_BUFFER5", |
|
"DRAW_BUFFER6", |
|
"DRAW_BUFFER7", |
|
"DRAW_BUFFER8", |
|
"DRAW_BUFFER9", |
|
"DRAW_FRAMEBUFFER", |
|
"DRAW_FRAMEBUFFER_BINDING", |
|
"DST_ALPHA", |
|
"DST_COLOR", |
|
"DYNAMIC_COPY", |
|
"DYNAMIC_DRAW", |
|
"DYNAMIC_READ", |
|
"DataChannel", |
|
"DataTransfer", |
|
"DataTransferItem", |
|
"DataTransferItemList", |
|
"DataView", |
|
"Date", |
|
"DateTimeFormat", |
|
"DecompressionStream", |
|
"DelayNode", |
|
"DelegatedInkTrailPresenter", |
|
"DeprecationReportBody", |
|
"DesktopNotification", |
|
"DesktopNotificationCenter", |
|
"Details", |
|
"DeviceLightEvent", |
|
"DeviceMotionEvent", |
|
"DeviceMotionEventAcceleration", |
|
"DeviceMotionEventRotationRate", |
|
"DeviceOrientationEvent", |
|
"DevicePosture", |
|
"DeviceProximityEvent", |
|
"DeviceStorage", |
|
"DeviceStorageChangeEvent", |
|
"DigitalCredential", |
|
"Directory", |
|
"DisplayNames", |
|
"DisposableStack", |
|
"Document", |
|
"DocumentFragment", |
|
"DocumentPictureInPicture", |
|
"DocumentPictureInPictureEvent", |
|
"DocumentTimeline", |
|
"DocumentType", |
|
"DragEvent", |
|
"Duration", |
|
"DurationFormat", |
|
"DynamicsCompressorNode", |
|
"E", |
|
"ELEMENT_ARRAY_BUFFER", |
|
"ELEMENT_ARRAY_BUFFER_BINDING", |
|
"ELEMENT_NODE", |
|
"EMPTY", |
|
"ENCODING_ERR", |
|
"ENDED", |
|
"END_TO_END", |
|
"END_TO_START", |
|
"ENTITY_NODE", |
|
"ENTITY_REFERENCE_NODE", |
|
"EPSILON", |
|
"EQUAL", |
|
"EQUALPOWER", |
|
"ERROR", |
|
"EXPONENTIAL_DISTANCE", |
|
"EditContext", |
|
"Element", |
|
"ElementInternals", |
|
"ElementQuery", |
|
"EncodedAudioChunk", |
|
"EncodedVideoChunk", |
|
"EnterPictureInPictureEvent", |
|
"Entity", |
|
"EntityReference", |
|
"Error", |
|
"ErrorEvent", |
|
"EvalError", |
|
"Event", |
|
"EventCounts", |
|
"EventException", |
|
"EventSource", |
|
"EventTarget", |
|
"Exception", |
|
"ExtensionContext", |
|
"ExtensionDisabledReason", |
|
"ExtensionInfo", |
|
"ExtensionInstallType", |
|
"ExtensionType", |
|
"External", |
|
"EyeDropper", |
|
"FASTEST", |
|
"FIDOSDK", |
|
"FILTER_ACCEPT", |
|
"FILTER_INTERRUPT", |
|
"FILTER_REJECT", |
|
"FILTER_SKIP", |
|
"FINISHED_STATE", |
|
"FIRST_ORDERED_NODE_TYPE", |
|
"FLOAT", |
|
"FLOAT_32_UNSIGNED_INT_24_8_REV", |
|
"FLOAT_MAT2", |
|
"FLOAT_MAT2x3", |
|
"FLOAT_MAT2x4", |
|
"FLOAT_MAT3", |
|
"FLOAT_MAT3x2", |
|
"FLOAT_MAT3x4", |
|
"FLOAT_MAT4", |
|
"FLOAT_MAT4x2", |
|
"FLOAT_MAT4x3", |
|
"FLOAT_VEC2", |
|
"FLOAT_VEC3", |
|
"FLOAT_VEC4", |
|
"FOCUS", |
|
"FONT_FACE_RULE", |
|
"FONT_FEATURE_VALUES_RULE", |
|
"FRAGMENT", |
|
"FRAGMENT_SHADER", |
|
"FRAGMENT_SHADER_DERIVATIVE_HINT", |
|
"FRAGMENT_SHADER_DERIVATIVE_HINT_OES", |
|
"FRAMEBUFFER", |
|
"FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE", |
|
"FRAMEBUFFER_ATTACHMENT_BLUE_SIZE", |
|
"FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING", |
|
"FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE", |
|
"FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE", |
|
"FRAMEBUFFER_ATTACHMENT_GREEN_SIZE", |
|
"FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", |
|
"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", |
|
"FRAMEBUFFER_ATTACHMENT_RED_SIZE", |
|
"FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE", |
|
"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE", |
|
"FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER", |
|
"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", |
|
"FRAMEBUFFER_BINDING", |
|
"FRAMEBUFFER_COMPLETE", |
|
"FRAMEBUFFER_DEFAULT", |
|
"FRAMEBUFFER_INCOMPLETE_ATTACHMENT", |
|
"FRAMEBUFFER_INCOMPLETE_DIMENSIONS", |
|
"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", |
|
"FRAMEBUFFER_INCOMPLETE_MULTISAMPLE", |
|
"FRAMEBUFFER_UNSUPPORTED", |
|
"FRONT", |
|
"FRONT_AND_BACK", |
|
"FRONT_FACE", |
|
"FUNC_ADD", |
|
"FUNC_REVERSE_SUBTRACT", |
|
"FUNC_SUBTRACT", |
|
"FeaturePolicy", |
|
"FeaturePolicyViolationReportBody", |
|
"FederatedCredential", |
|
"Feed", |
|
"FeedEntry", |
|
"Fence", |
|
"FencedFrameConfig", |
|
"FetchLaterResult", |
|
"File", |
|
"FileError", |
|
"FileList", |
|
"FileReader", |
|
"FileSystem", |
|
"FileSystemDirectoryEntry", |
|
"FileSystemDirectoryHandle", |
|
"FileSystemDirectoryReader", |
|
"FileSystemEntry", |
|
"FileSystemFileEntry", |
|
"FileSystemFileHandle", |
|
"FileSystemHandle", |
|
"FileSystemObserver", |
|
"FileSystemWritableFileStream", |
|
"FinalizationRegistry", |
|
"FindInPage", |
|
"Float16Array", |
|
"Float32Array", |
|
"Float64Array", |
|
"FocusEvent", |
|
"FontData", |
|
"FontFace", |
|
"FontFaceSet", |
|
"FontFaceSetLoadEvent", |
|
"FormData", |
|
"FormDataEvent", |
|
"FragmentDirective", |
|
"Function", |
|
"GENERATE_MIPMAP_HINT", |
|
"GEQUAL", |
|
"GPU", |
|
"GPUAdapter", |
|
"GPUAdapterInfo", |
|
"GPUBindGroup", |
|
"GPUBindGroupLayout", |
|
"GPUBuffer", |
|
"GPUBufferUsage", |
|
"GPUCanvasContext", |
|
"GPUColorWrite", |
|
"GPUCommandBuffer", |
|
"GPUCommandEncoder", |
|
"GPUCompilationInfo", |
|
"GPUCompilationMessage", |
|
"GPUComputePassEncoder", |
|
"GPUComputePipeline", |
|
"GPUDevice", |
|
"GPUDeviceLostInfo", |
|
"GPUError", |
|
"GPUExternalTexture", |
|
"GPUInternalError", |
|
"GPUMapMode", |
|
"GPUOutOfMemoryError", |
|
"GPUPipelineError", |
|
"GPUPipelineLayout", |
|
"GPUQuerySet", |
|
"GPUQueue", |
|
"GPURenderBundle", |
|
"GPURenderBundleEncoder", |
|
"GPURenderPassEncoder", |
|
"GPURenderPipeline", |
|
"GPUSampler", |
|
"GPUShaderModule", |
|
"GPUShaderStage", |
|
"GPUSupportedFeatures", |
|
"GPUSupportedLimits", |
|
"GPUTexture", |
|
"GPUTextureUsage", |
|
"GPUTextureView", |
|
"GPUUncapturedErrorEvent", |
|
"GPUValidationError", |
|
"GREATER", |
|
"GREEN", |
|
"GREEN_BITS", |
|
"GainNode", |
|
"Gamepad", |
|
"GamepadAxisMoveEvent", |
|
"GamepadButton", |
|
"GamepadButtonEvent", |
|
"GamepadEvent", |
|
"GamepadHapticActuator", |
|
"GamepadPose", |
|
"Geolocation", |
|
"GeolocationCoordinates", |
|
"GeolocationPosition", |
|
"GeolocationPositionError", |
|
"GestureEvent", |
|
"GetInfo", |
|
"Global", |
|
"GravitySensor", |
|
"Gyroscope", |
|
"HALF_FLOAT", |
|
"HAVE_CURRENT_DATA", |
|
"HAVE_ENOUGH_DATA", |
|
"HAVE_FUTURE_DATA", |
|
"HAVE_METADATA", |
|
"HAVE_NOTHING", |
|
"HEADERS_RECEIVED", |
|
"HID", |
|
"HIDConnectionEvent", |
|
"HIDDEN", |
|
"HIDDevice", |
|
"HIDInputReportEvent", |
|
"HIERARCHY_REQUEST_ERR", |
|
"HIGHPASS", |
|
"HIGHSHELF", |
|
"HIGH_FLOAT", |
|
"HIGH_INT", |
|
"HORIZONTAL", |
|
"HORIZONTAL_AXIS", |
|
"HRTF", |
|
"HTMLAllCollection", |
|
"HTMLAnchorElement", |
|
"HTMLAppletElement", |
|
"HTMLAreaElement", |
|
"HTMLAudioElement", |
|
"HTMLBRElement", |
|
"HTMLBaseElement", |
|
"HTMLBaseFontElement", |
|
"HTMLBlockquoteElement", |
|
"HTMLBodyElement", |
|
"HTMLButtonElement", |
|
"HTMLCanvasElement", |
|
"HTMLCollection", |
|
"HTMLCommandElement", |
|
"HTMLContentElement", |
|
"HTMLDListElement", |
|
"HTMLDataElement", |
|
"HTMLDataListElement", |
|
"HTMLDetailsElement", |
|
"HTMLDialogElement", |
|
"HTMLDirectoryElement", |
|
"HTMLDivElement", |
|
"HTMLDocument", |
|
"HTMLElement", |
|
"HTMLEmbedElement", |
|
"HTMLFencedFrameElement", |
|
"HTMLFieldSetElement", |
|
"HTMLFontElement", |
|
"HTMLFormControlsCollection", |
|
"HTMLFormElement", |
|
"HTMLFrameElement", |
|
"HTMLFrameSetElement", |
|
"HTMLHRElement", |
|
"HTMLHeadElement", |
|
"HTMLHeadingElement", |
|
"HTMLHtmlElement", |
|
"HTMLIFrameElement", |
|
"HTMLImageElement", |
|
"HTMLInputElement", |
|
"HTMLIsIndexElement", |
|
"HTMLKeygenElement", |
|
"HTMLLIElement", |
|
"HTMLLabelElement", |
|
"HTMLLegendElement", |
|
"HTMLLinkElement", |
|
"HTMLMapElement", |
|
"HTMLMarqueeElement", |
|
"HTMLMediaElement", |
|
"HTMLMenuElement", |
|
"HTMLMenuItemElement", |
|
"HTMLMetaElement", |
|
"HTMLMeterElement", |
|
"HTMLModElement", |
|
"HTMLOListElement", |
|
"HTMLObjectElement", |
|
"HTMLOptGroupElement", |
|
"HTMLOptionElement", |
|
"HTMLOptionsCollection", |
|
"HTMLOutputElement", |
|
"HTMLParagraphElement", |
|
"HTMLParamElement", |
|
"HTMLPictureElement", |
|
"HTMLPreElement", |
|
"HTMLProgressElement", |
|
"HTMLPropertiesCollection", |
|
"HTMLQuoteElement", |
|
"HTMLScriptElement", |
|
"HTMLSelectElement", |
|
"HTMLSelectedContentElement", |
|
"HTMLShadowElement", |
|
"HTMLSlotElement", |
|
"HTMLSourceElement", |
|
"HTMLSpanElement", |
|
"HTMLStyleElement", |
|
"HTMLTableCaptionElement", |
|
"HTMLTableCellElement", |
|
"HTMLTableColElement", |
|
"HTMLTableElement", |
|
"HTMLTableRowElement", |
|
"HTMLTableSectionElement", |
|
"HTMLTemplateElement", |
|
"HTMLTextAreaElement", |
|
"HTMLTimeElement", |
|
"HTMLTitleElement", |
|
"HTMLTrackElement", |
|
"HTMLUListElement", |
|
"HTMLUnknownElement", |
|
"HTMLVideoElement", |
|
"HashChangeEvent", |
|
"Headers", |
|
"Highlight", |
|
"HighlightRegistry", |
|
"History", |
|
"Hz", |
|
"ICE_CHECKING", |
|
"ICE_CLOSED", |
|
"ICE_COMPLETED", |
|
"ICE_CONNECTED", |
|
"ICE_FAILED", |
|
"ICE_GATHERING", |
|
"ICE_WAITING", |
|
"IDBCursor", |
|
"IDBCursorWithValue", |
|
"IDBDatabase", |
|
"IDBDatabaseException", |
|
"IDBFactory", |
|
"IDBFileHandle", |
|
"IDBFileRequest", |
|
"IDBIndex", |
|
"IDBKeyRange", |
|
"IDBMutableFile", |
|
"IDBObjectStore", |
|
"IDBOpenDBRequest", |
|
"IDBRecord", |
|
"IDBRequest", |
|
"IDBTransaction", |
|
"IDBVersionChangeEvent", |
|
"IDLE", |
|
"IIRFilterNode", |
|
"IMPLEMENTATION_COLOR_READ_FORMAT", |
|
"IMPLEMENTATION_COLOR_READ_TYPE", |
|
"IMPORT_RULE", |
|
"INCR", |
|
"INCR_WRAP", |
|
"INDEX", |
|
"INDEX_SIZE_ERR", |
|
"INDIRECT", |
|
"INT", |
|
"INTERLEAVED_ATTRIBS", |
|
"INT_2_10_10_10_REV", |
|
"INT_SAMPLER_2D", |
|
"INT_SAMPLER_2D_ARRAY", |
|
"INT_SAMPLER_3D", |
|
"INT_SAMPLER_CUBE", |
|
"INT_VEC2", |
|
"INT_VEC3", |
|
"INT_VEC4", |
|
"INUSE_ATTRIBUTE_ERR", |
|
"INVALID_ACCESS_ERR", |
|
"INVALID_CHARACTER_ERR", |
|
"INVALID_ENUM", |
|
"INVALID_EXPRESSION_ERR", |
|
"INVALID_FRAMEBUFFER_OPERATION", |
|
"INVALID_INDEX", |
|
"INVALID_MODIFICATION_ERR", |
|
"INVALID_NODE_TYPE_ERR", |
|
"INVALID_OPERATION", |
|
"INVALID_STATE_ERR", |
|
"INVALID_VALUE", |
|
"INVERSE_DISTANCE", |
|
"INVERT", |
|
"IceCandidate", |
|
"IconInfo", |
|
"IdentityCredential", |
|
"IdentityCredentialError", |
|
"IdentityProvider", |
|
"IdleDeadline", |
|
"IdleDetector", |
|
"Image", |
|
"ImageBitmap", |
|
"ImageBitmapRenderingContext", |
|
"ImageCapture", |
|
"ImageData", |
|
"ImageDataType", |
|
"ImageDecoder", |
|
"ImageTrack", |
|
"ImageTrackList", |
|
"Infinity", |
|
"Ink", |
|
"InputDeviceCapabilities", |
|
"InputDeviceInfo", |
|
"InputEvent", |
|
"InputMethodContext", |
|
"InstallTrigger", |
|
"InstallTriggerImpl", |
|
"Instance", |
|
"Instant", |
|
"Int16Array", |
|
"Int32Array", |
|
"Int8Array", |
|
"IntegrityViolationReportBody", |
|
"Intent", |
|
"InterestEvent", |
|
"InternalError", |
|
"IntersectionObserver", |
|
"IntersectionObserverEntry", |
|
"Intl", |
|
"IsSearchProviderInstalled", |
|
"Iterator", |
|
"JSON", |
|
"JSTag", |
|
"KEEP", |
|
"KEYDOWN", |
|
"KEYFRAMES_RULE", |
|
"KEYFRAME_RULE", |
|
"KEYPRESS", |
|
"KEYUP", |
|
"KeyEvent", |
|
"Keyboard", |
|
"KeyboardEvent", |
|
"KeyboardLayoutMap", |
|
"KeyframeEffect", |
|
"LENGTHADJUST_SPACING", |
|
"LENGTHADJUST_SPACINGANDGLYPHS", |
|
"LENGTHADJUST_UNKNOWN", |
|
"LEQUAL", |
|
"LESS", |
|
"LINEAR", |
|
"LINEAR_DISTANCE", |
|
"LINEAR_MIPMAP_LINEAR", |
|
"LINEAR_MIPMAP_NEAREST", |
|
"LINES", |
|
"LINE_LOOP", |
|
"LINE_STRIP", |
|
"LINE_WIDTH", |
|
"LINK_STATUS", |
|
"LIVE", |
|
"LN10", |
|
"LN2", |
|
"LOADED", |
|
"LOADING", |
|
"LOG10E", |
|
"LOG2E", |
|
"LOWPASS", |
|
"LOWSHELF", |
|
"LOW_FLOAT", |
|
"LOW_INT", |
|
"LSException", |
|
"LSParserFilter", |
|
"LUMINANCE", |
|
"LUMINANCE_ALPHA", |
|
"LanguageCode", |
|
"LanguageDetector", |
|
"LargestContentfulPaint", |
|
"LaunchParams", |
|
"LaunchQueue", |
|
"LaunchType", |
|
"LayoutShift", |
|
"LayoutShiftAttribution", |
|
"LinearAccelerationSensor", |
|
"LinkError", |
|
"ListFormat", |
|
"LocalMediaStream", |
|
"Locale", |
|
"Location", |
|
"Lock", |
|
"LockManager", |
|
"MAP_READ", |
|
"MAP_WRITE", |
|
"MARGIN_RULE", |
|
"MAX", |
|
"MAX_3D_TEXTURE_SIZE", |
|
"MAX_ARRAY_TEXTURE_LAYERS", |
|
"MAX_CAPTURE_VISIBLE_TAB_CALLS_PER_SECOND", |
|
"MAX_CLIENT_WAIT_TIMEOUT_WEBGL", |
|
"MAX_COLOR_ATTACHMENTS", |
|
"MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", |
|
"MAX_COMBINED_TEXTURE_IMAGE_UNITS", |
|
"MAX_COMBINED_UNIFORM_BLOCKS", |
|
"MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", |
|
"MAX_CUBE_MAP_TEXTURE_SIZE", |
|
"MAX_DRAW_BUFFERS", |
|
"MAX_ELEMENTS_INDICES", |
|
"MAX_ELEMENTS_VERTICES", |
|
"MAX_ELEMENT_INDEX", |
|
"MAX_FRAGMENT_INPUT_COMPONENTS", |
|
"MAX_FRAGMENT_UNIFORM_BLOCKS", |
|
"MAX_FRAGMENT_UNIFORM_COMPONENTS", |
|
"MAX_FRAGMENT_UNIFORM_VECTORS", |
|
"MAX_PROGRAM_TEXEL_OFFSET", |
|
"MAX_RENDERBUFFER_SIZE", |
|
"MAX_SAFE_INTEGER", |
|
"MAX_SAMPLES", |
|
"MAX_SERVER_WAIT_TIMEOUT", |
|
"MAX_TEXTURE_IMAGE_UNITS", |
|
"MAX_TEXTURE_LOD_BIAS", |
|
"MAX_TEXTURE_MAX_ANISOTROPY_EXT", |
|
"MAX_TEXTURE_SIZE", |
|
"MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", |
|
"MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", |
|
"MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", |
|
"MAX_UNIFORM_BLOCK_SIZE", |
|
"MAX_UNIFORM_BUFFER_BINDINGS", |
|
"MAX_VALUE", |
|
"MAX_VARYING_COMPONENTS", |
|
"MAX_VARYING_VECTORS", |
|
"MAX_VERTEX_ATTRIBS", |
|
"MAX_VERTEX_OUTPUT_COMPONENTS", |
|
"MAX_VERTEX_TEXTURE_IMAGE_UNITS", |
|
"MAX_VERTEX_UNIFORM_BLOCKS", |
|
"MAX_VERTEX_UNIFORM_COMPONENTS", |
|
"MAX_VERTEX_UNIFORM_VECTORS", |
|
"MAX_VIEWPORT_DIMS", |
|
"MEDIA_ERR_ABORTED", |
|
"MEDIA_ERR_DECODE", |
|
"MEDIA_ERR_ENCRYPTED", |
|
"MEDIA_ERR_NETWORK", |
|
"MEDIA_ERR_SRC_NOT_SUPPORTED", |
|
"MEDIA_KEYERR_CLIENT", |
|
"MEDIA_KEYERR_DOMAIN", |
|
"MEDIA_KEYERR_HARDWARECHANGE", |
|
"MEDIA_KEYERR_OUTPUT", |
|
"MEDIA_KEYERR_SERVICE", |
|
"MEDIA_KEYERR_UNKNOWN", |
|
"MEDIA_RULE", |
|
"MEDIUM_FLOAT", |
|
"MEDIUM_INT", |
|
"META_MASK", |
|
"MIDIAccess", |
|
"MIDIConnectionEvent", |
|
"MIDIInput", |
|
"MIDIInputMap", |
|
"MIDIMessageEvent", |
|
"MIDIOutput", |
|
"MIDIOutputMap", |
|
"MIDIPort", |
|
"MIN", |
|
"MIN_PROGRAM_TEXEL_OFFSET", |
|
"MIN_SAFE_INTEGER", |
|
"MIN_VALUE", |
|
"MIRRORED_REPEAT", |
|
"MODE_ASYNCHRONOUS", |
|
"MODE_SYNCHRONOUS", |
|
"MODIFICATION", |
|
"MOUSEDOWN", |
|
"MOUSEDRAG", |
|
"MOUSEMOVE", |
|
"MOUSEOUT", |
|
"MOUSEOVER", |
|
"MOUSEUP", |
|
"MOZ_KEYFRAMES_RULE", |
|
"MOZ_KEYFRAME_RULE", |
|
"MOZ_SOURCE_CURSOR", |
|
"MOZ_SOURCE_ERASER", |
|
"MOZ_SOURCE_KEYBOARD", |
|
"MOZ_SOURCE_MOUSE", |
|
"MOZ_SOURCE_PEN", |
|
"MOZ_SOURCE_TOUCH", |
|
"MOZ_SOURCE_UNKNOWN", |
|
"MSGESTURE_FLAG_BEGIN", |
|
"MSGESTURE_FLAG_CANCEL", |
|
"MSGESTURE_FLAG_END", |
|
"MSGESTURE_FLAG_INERTIA", |
|
"MSGESTURE_FLAG_NONE", |
|
"MSPOINTER_TYPE_MOUSE", |
|
"MSPOINTER_TYPE_PEN", |
|
"MSPOINTER_TYPE_TOUCH", |
|
"MS_ASYNC_CALLBACK_STATUS_ASSIGN_DELEGATE", |
|
"MS_ASYNC_CALLBACK_STATUS_CANCEL", |
|
"MS_ASYNC_CALLBACK_STATUS_CHOOSEANY", |
|
"MS_ASYNC_CALLBACK_STATUS_ERROR", |
|
"MS_ASYNC_CALLBACK_STATUS_JOIN", |
|
"MS_ASYNC_OP_STATUS_CANCELED", |
|
"MS_ASYNC_OP_STATUS_ERROR", |
|
"MS_ASYNC_OP_STATUS_SUCCESS", |
|
"MS_MANIPULATION_STATE_ACTIVE", |
|
"MS_MANIPULATION_STATE_CANCELLED", |
|
"MS_MANIPULATION_STATE_COMMITTED", |
|
"MS_MANIPULATION_STATE_DRAGGING", |
|
"MS_MANIPULATION_STATE_INERTIA", |
|
"MS_MANIPULATION_STATE_PRESELECT", |
|
"MS_MANIPULATION_STATE_SELECTING", |
|
"MS_MANIPULATION_STATE_STOPPED", |
|
"MS_MEDIA_ERR_ENCRYPTED", |
|
"MS_MEDIA_KEYERR_CLIENT", |
|
"MS_MEDIA_KEYERR_DOMAIN", |
|
"MS_MEDIA_KEYERR_HARDWARECHANGE", |
|
"MS_MEDIA_KEYERR_OUTPUT", |
|
"MS_MEDIA_KEYERR_SERVICE", |
|
"MS_MEDIA_KEYERR_UNKNOWN", |
|
"Map", |
|
"Math", |
|
"MathMLElement", |
|
"MediaCapabilities", |
|
"MediaCapabilitiesInfo", |
|
"MediaController", |
|
"MediaDeviceInfo", |
|
"MediaDevices", |
|
"MediaElementAudioSourceNode", |
|
"MediaEncryptedEvent", |
|
"MediaError", |
|
"MediaKeyError", |
|
"MediaKeyEvent", |
|
"MediaKeyMessageEvent", |
|
"MediaKeyNeededEvent", |
|
"MediaKeySession", |
|
"MediaKeyStatusMap", |
|
"MediaKeySystemAccess", |
|
"MediaKeys", |
|
"MediaList", |
|
"MediaMetadata", |
|
"MediaQueryList", |
|
"MediaQueryListEvent", |
|
"MediaRecorder", |
|
"MediaRecorderErrorEvent", |
|
"MediaSession", |
|
"MediaSettingsRange", |
|
"MediaSource", |
|
"MediaSourceHandle", |
|
"MediaStream", |
|
"MediaStreamAudioDestinationNode", |
|
"MediaStreamAudioSourceNode", |
|
"MediaStreamEvent", |
|
"MediaStreamTrack", |
|
"MediaStreamTrackAudioSourceNode", |
|
"MediaStreamTrackAudioStats", |
|
"MediaStreamTrackEvent", |
|
"MediaStreamTrackGenerator", |
|
"MediaStreamTrackProcessor", |
|
"MediaStreamTrackVideoStats", |
|
"Memory", |
|
"MessageChannel", |
|
"MessageEvent", |
|
"MessagePort", |
|
"MessageSender", |
|
"Methods", |
|
"MimeType", |
|
"MimeTypeArray", |
|
"Module", |
|
"MouseEvent", |
|
"MouseScrollEvent", |
|
"MozAnimation", |
|
"MozAnimationDelay", |
|
"MozAnimationDirection", |
|
"MozAnimationDuration", |
|
"MozAnimationFillMode", |
|
"MozAnimationIterationCount", |
|
"MozAnimationName", |
|
"MozAnimationPlayState", |
|
"MozAnimationTimingFunction", |
|
"MozAppearance", |
|
"MozBackfaceVisibility", |
|
"MozBinding", |
|
"MozBorderBottomColors", |
|
"MozBorderEnd", |
|
"MozBorderEndColor", |
|
"MozBorderEndStyle", |
|
"MozBorderEndWidth", |
|
"MozBorderImage", |
|
"MozBorderLeftColors", |
|
"MozBorderRightColors", |
|
"MozBorderStart", |
|
"MozBorderStartColor", |
|
"MozBorderStartStyle", |
|
"MozBorderStartWidth", |
|
"MozBorderTopColors", |
|
"MozBoxAlign", |
|
"MozBoxDirection", |
|
"MozBoxFlex", |
|
"MozBoxOrdinalGroup", |
|
"MozBoxOrient", |
|
"MozBoxPack", |
|
"MozBoxSizing", |
|
"MozCSSKeyframeRule", |
|
"MozCSSKeyframesRule", |
|
"MozColumnCount", |
|
"MozColumnFill", |
|
"MozColumnGap", |
|
"MozColumnRule", |
|
"MozColumnRuleColor", |
|
"MozColumnRuleStyle", |
|
"MozColumnRuleWidth", |
|
"MozColumnWidth", |
|
"MozColumns", |
|
"MozContactChangeEvent", |
|
"MozFloatEdge", |
|
"MozFontFeatureSettings", |
|
"MozFontLanguageOverride", |
|
"MozForceBrokenImageIcon", |
|
"MozHyphens", |
|
"MozImageRegion", |
|
"MozMarginEnd", |
|
"MozMarginStart", |
|
"MozMmsEvent", |
|
"MozMmsMessage", |
|
"MozMobileMessageThread", |
|
"MozOSXFontSmoothing", |
|
"MozOrient", |
|
"MozOsxFontSmoothing", |
|
"MozOutlineRadius", |
|
"MozOutlineRadiusBottomleft", |
|
"MozOutlineRadiusBottomright", |
|
"MozOutlineRadiusTopleft", |
|
"MozOutlineRadiusTopright", |
|
"MozPaddingEnd", |
|
"MozPaddingStart", |
|
"MozPerspective", |
|
"MozPerspectiveOrigin", |
|
"MozPowerManager", |
|
"MozSettingsEvent", |
|
"MozSmsEvent", |
|
"MozSmsMessage", |
|
"MozStackSizing", |
|
"MozTabSize", |
|
"MozTextAlignLast", |
|
"MozTextDecorationColor", |
|
"MozTextDecorationLine", |
|
"MozTextDecorationStyle", |
|
"MozTextSizeAdjust", |
|
"MozTransform", |
|
"MozTransformOrigin", |
|
"MozTransformStyle", |
|
"MozTransition", |
|
"MozTransitionDelay", |
|
"MozTransitionDuration", |
|
"MozTransitionProperty", |
|
"MozTransitionTimingFunction", |
|
"MozUserFocus", |
|
"MozUserInput", |
|
"MozUserModify", |
|
"MozUserSelect", |
|
"MozWindowDragging", |
|
"MozWindowShadow", |
|
"MutationEvent", |
|
"MutationObserver", |
|
"MutationRecord", |
|
"MutedInfo", |
|
"MutedInfoReason", |
|
"NAMESPACE_ERR", |
|
"NAMESPACE_RULE", |
|
"NEAREST", |
|
"NEAREST_MIPMAP_LINEAR", |
|
"NEAREST_MIPMAP_NEAREST", |
|
"NEGATIVE_INFINITY", |
|
"NETWORK_EMPTY", |
|
"NETWORK_ERR", |
|
"NETWORK_IDLE", |
|
"NETWORK_LOADED", |
|
"NETWORK_LOADING", |
|
"NETWORK_NO_SOURCE", |
|
"NEVER", |
|
"NEW", |
|
"NEXT", |
|
"NEXT_NO_DUPLICATE", |
|
"NICEST", |
|
"NODE_AFTER", |
|
"NODE_BEFORE", |
|
"NODE_BEFORE_AND_AFTER", |
|
"NODE_INSIDE", |
|
"NONE", |
|
"NON_TRANSIENT_ERR", |
|
"NOTATION_NODE", |
|
"NOTCH", |
|
"NOTEQUAL", |
|
"NOT_ALLOWED_ERR", |
|
"NOT_FOUND_ERR", |
|
"NOT_READABLE_ERR", |
|
"NOT_SUPPORTED_ERR", |
|
"NO_DATA_ALLOWED_ERR", |
|
"NO_ERR", |
|
"NO_ERROR", |
|
"NO_MODIFICATION_ALLOWED_ERR", |
|
"NUMBER_TYPE", |
|
"NUM_COMPRESSED_TEXTURE_FORMATS", |
|
"NaN", |
|
"NamedNodeMap", |
|
"NavigateEvent", |
|
"Navigation", |
|
"NavigationActivation", |
|
"NavigationCurrentEntryChangeEvent", |
|
"NavigationDestination", |
|
"NavigationHistoryEntry", |
|
"NavigationPrecommitController", |
|
"NavigationPreloadManager", |
|
"NavigationTransition", |
|
"Navigator", |
|
"NavigatorLogin", |
|
"NavigatorManagedData", |
|
"NavigatorUAData", |
|
"NearbyLinks", |
|
"NetworkInformation", |
|
"Node", |
|
"NodeFilter", |
|
"NodeIterator", |
|
"NodeList", |
|
"NotRestoredReasonDetails", |
|
"NotRestoredReasons", |
|
"Notation", |
|
"Notification", |
|
"NotifyPaintEvent", |
|
"Now", |
|
"Number", |
|
"NumberFormat", |
|
"OBJECT_TYPE", |
|
"OBSOLETE", |
|
"OK", |
|
"ONE", |
|
"ONE_MINUS_CONSTANT_ALPHA", |
|
"ONE_MINUS_CONSTANT_COLOR", |
|
"ONE_MINUS_DST_ALPHA", |
|
"ONE_MINUS_DST_COLOR", |
|
"ONE_MINUS_SRC_ALPHA", |
|
"ONE_MINUS_SRC_COLOR", |
|
"OPEN", |
|
"OPENED", |
|
"OPENING", |
|
"ORDERED_NODE_ITERATOR_TYPE", |
|
"ORDERED_NODE_SNAPSHOT_TYPE", |
|
"OTHER_ERROR", |
|
"OTPCredential", |
|
"OUT_OF_MEMORY", |
|
"Object", |
|
"Observable", |
|
"OfflineAudioCompletionEvent", |
|
"OfflineAudioContext", |
|
"OfflineResourceList", |
|
"OffscreenCanvas", |
|
"OffscreenCanvasRenderingContext2D", |
|
"OnClickData", |
|
"OnInstalledReason", |
|
"OnPerformanceWarningCategory", |
|
"OnPerformanceWarningSeverity", |
|
"OnRestartRequiredReason", |
|
"Option", |
|
"OrientationSensor", |
|
"OscillatorNode", |
|
"OverconstrainedError", |
|
"OverflowEvent", |
|
"PACK_ALIGNMENT", |
|
"PACK_ROW_LENGTH", |
|
"PACK_SKIP_PIXELS", |
|
"PACK_SKIP_ROWS", |
|
"PAGE_RULE", |
|
"PARSE_ERR", |
|
"PATHSEG_ARC_ABS", |
|
"PATHSEG_ARC_REL", |
|
"PATHSEG_CLOSEPATH", |
|
"PATHSEG_CURVETO_CUBIC_ABS", |
|
"PATHSEG_CURVETO_CUBIC_REL", |
|
"PATHSEG_CURVETO_CUBIC_SMOOTH_ABS", |
|
"PATHSEG_CURVETO_CUBIC_SMOOTH_REL", |
|
"PATHSEG_CURVETO_QUADRATIC_ABS", |
|
"PATHSEG_CURVETO_QUADRATIC_REL", |
|
"PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS", |
|
"PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL", |
|
"PATHSEG_LINETO_ABS", |
|
"PATHSEG_LINETO_HORIZONTAL_ABS", |
|
"PATHSEG_LINETO_HORIZONTAL_REL", |
|
"PATHSEG_LINETO_REL", |
|
"PATHSEG_LINETO_VERTICAL_ABS", |
|
"PATHSEG_LINETO_VERTICAL_REL", |
|
"PATHSEG_MOVETO_ABS", |
|
"PATHSEG_MOVETO_REL", |
|
"PATHSEG_UNKNOWN", |
|
"PATH_EXISTS_ERR", |
|
"PEAKING", |
|
"PERMISSION_DENIED", |
|
"PERSISTENT", |
|
"PI", |
|
"PIXEL_PACK_BUFFER", |
|
"PIXEL_PACK_BUFFER_BINDING", |
|
"PIXEL_UNPACK_BUFFER", |
|
"PIXEL_UNPACK_BUFFER_BINDING", |
|
"PLAYING_STATE", |
|
"POINTS", |
|
"POLYGON_OFFSET_FACTOR", |
|
"POLYGON_OFFSET_FILL", |
|
"POLYGON_OFFSET_UNITS", |
|
"POSITION_UNAVAILABLE", |
|
"POSITIVE_INFINITY", |
|
"PREV", |
|
"PREV_NO_DUPLICATE", |
|
"PROCESSING_INSTRUCTION_NODE", |
|
"PageChangeEvent", |
|
"PageRevealEvent", |
|
"PageSettings", |
|
"PageSwapEvent", |
|
"PageTransitionEvent", |
|
"PaintRequest", |
|
"PaintRequestList", |
|
"PannerNode", |
|
"PasswordCredential", |
|
"Path2D", |
|
"PaymentAddress", |
|
"PaymentInstruments", |
|
"PaymentManager", |
|
"PaymentMethodChangeEvent", |
|
"PaymentRequest", |
|
"PaymentRequestUpdateEvent", |
|
"PaymentResponse", |
|
"Performance", |
|
"PerformanceElementTiming", |
|
"PerformanceEntry", |
|
"PerformanceEventTiming", |
|
"PerformanceLongAnimationFrameTiming", |
|
"PerformanceLongTaskTiming", |
|
"PerformanceMark", |
|
"PerformanceMeasure", |
|
"PerformanceNavigation", |
|
"PerformanceNavigationTiming", |
|
"PerformanceObserver", |
|
"PerformanceObserverEntryList", |
|
"PerformancePaintTiming", |
|
"PerformanceResourceTiming", |
|
"PerformanceScriptTiming", |
|
"PerformanceServerTiming", |
|
"PerformanceTiming", |
|
"PeriodicSyncManager", |
|
"PeriodicWave", |
|
"PermissionStatus", |
|
"Permissions", |
|
"PhotoCapabilities", |
|
"PictureInPictureEvent", |
|
"PictureInPictureWindow", |
|
"PlainDate", |
|
"PlainDateTime", |
|
"PlainMonthDay", |
|
"PlainTime", |
|
"PlainYearMonth", |
|
"PlatformArch", |
|
"PlatformInfo", |
|
"PlatformNaclArch", |
|
"PlatformOs", |
|
"Plugin", |
|
"PluginArray", |
|
"PluralRules", |
|
"PointerEvent", |
|
"PopStateEvent", |
|
"PopupBlockedEvent", |
|
"Port", |
|
"Presentation", |
|
"PresentationAvailability", |
|
"PresentationConnection", |
|
"PresentationConnectionAvailableEvent", |
|
"PresentationConnectionCloseEvent", |
|
"PresentationConnectionList", |
|
"PresentationReceiver", |
|
"PresentationRequest", |
|
"PressureObserver", |
|
"PressureRecord", |
|
"ProcessingInstruction", |
|
"Profiler", |
|
"ProgressEvent", |
|
"Promise", |
|
"PromiseRejectionEvent", |
|
"PropertyNodeList", |
|
"ProtectedAudience", |
|
"Proxy", |
|
"PublicKeyCredential", |
|
"PushManager", |
|
"PushSubscription", |
|
"PushSubscriptionOptions", |
|
"Q", |
|
"QUERY_RESOLVE", |
|
"QUERY_RESULT", |
|
"QUERY_RESULT_AVAILABLE", |
|
"QUOTA_ERR", |
|
"QUOTA_EXCEEDED_ERR", |
|
"QueryInterface", |
|
"QuotaExceededError", |
|
"R11F_G11F_B10F", |
|
"R16F", |
|
"R16I", |
|
"R16UI", |
|
"R32F", |
|
"R32I", |
|
"R32UI", |
|
"R8", |
|
"R8I", |
|
"R8UI", |
|
"R8_SNORM", |
|
"RASTERIZER_DISCARD", |
|
"READ", |
|
"READ_BUFFER", |
|
"READ_FRAMEBUFFER", |
|
"READ_FRAMEBUFFER_BINDING", |
|
"READ_ONLY", |
|
"READ_ONLY_ERR", |
|
"READ_WRITE", |
|
"RED", |
|
"RED_BITS", |
|
"RED_INTEGER", |
|
"REMOVAL", |
|
"RENDERBUFFER", |
|
"RENDERBUFFER_ALPHA_SIZE", |
|
"RENDERBUFFER_BINDING", |
|
"RENDERBUFFER_BLUE_SIZE", |
|
"RENDERBUFFER_DEPTH_SIZE", |
|
"RENDERBUFFER_GREEN_SIZE", |
|
"RENDERBUFFER_HEIGHT", |
|
"RENDERBUFFER_INTERNAL_FORMAT", |
|
"RENDERBUFFER_RED_SIZE", |
|
"RENDERBUFFER_SAMPLES", |
|
"RENDERBUFFER_STENCIL_SIZE", |
|
"RENDERBUFFER_WIDTH", |
|
"RENDERER", |
|
"RENDERING_INTENT_ABSOLUTE_COLORIMETRIC", |
|
"RENDERING_INTENT_AUTO", |
|
"RENDERING_INTENT_PERCEPTUAL", |
|
"RENDERING_INTENT_RELATIVE_COLORIMETRIC", |
|
"RENDERING_INTENT_SATURATION", |
|
"RENDERING_INTENT_UNKNOWN", |
|
"RENDER_ATTACHMENT", |
|
"REPEAT", |
|
"REPLACE", |
|
"RG", |
|
"RG16F", |
|
"RG16I", |
|
"RG16UI", |
|
"RG32F", |
|
"RG32I", |
|
"RG32UI", |
|
"RG8", |
|
"RG8I", |
|
"RG8UI", |
|
"RG8_SNORM", |
|
"RGB", |
|
"RGB10_A2", |
|
"RGB10_A2UI", |
|
"RGB16F", |
|
"RGB16I", |
|
"RGB16UI", |
|
"RGB32F", |
|
"RGB32I", |
|
"RGB32UI", |
|
"RGB565", |
|
"RGB5_A1", |
|
"RGB8", |
|
"RGB8I", |
|
"RGB8UI", |
|
"RGB8_SNORM", |
|
"RGB9_E5", |
|
"RGBA", |
|
"RGBA16F", |
|
"RGBA16I", |
|
"RGBA16UI", |
|
"RGBA32F", |
|
"RGBA32I", |
|
"RGBA32UI", |
|
"RGBA4", |
|
"RGBA8", |
|
"RGBA8I", |
|
"RGBA8UI", |
|
"RGBA8_SNORM", |
|
"RGBA_INTEGER", |
|
"RGBColor", |
|
"RGB_INTEGER", |
|
"RG_INTEGER", |
|
"ROTATION_CLOCKWISE", |
|
"ROTATION_COUNTERCLOCKWISE", |
|
"RTCCertificate", |
|
"RTCDTMFSender", |
|
"RTCDTMFToneChangeEvent", |
|
"RTCDataChannel", |
|
"RTCDataChannelEvent", |
|
"RTCDtlsTransport", |
|
"RTCEncodedAudioFrame", |
|
"RTCEncodedVideoFrame", |
|
"RTCError", |
|
"RTCErrorEvent", |
|
"RTCIceCandidate", |
|
"RTCIceTransport", |
|
"RTCPeerConnection", |
|
"RTCPeerConnectionIceErrorEvent", |
|
"RTCPeerConnectionIceEvent", |
|
"RTCRtpReceiver", |
|
"RTCRtpScriptTransform", |
|
"RTCRtpSender", |
|
"RTCRtpTransceiver", |
|
"RTCSctpTransport", |
|
"RTCSessionDescription", |
|
"RTCStatsReport", |
|
"RTCTrackEvent", |
|
"RadioNodeList", |
|
"Range", |
|
"RangeError", |
|
"RangeException", |
|
"ReadableByteStreamController", |
|
"ReadableStream", |
|
"ReadableStreamBYOBReader", |
|
"ReadableStreamBYOBRequest", |
|
"ReadableStreamDefaultController", |
|
"ReadableStreamDefaultReader", |
|
"RecordErrorEvent", |
|
"Rect", |
|
"ReferenceError", |
|
"Reflect", |
|
"RegExp", |
|
"RelativeOrientationSensor", |
|
"RelativeTimeFormat", |
|
"RemotePlayback", |
|
"Report", |
|
"ReportBody", |
|
"ReportingObserver", |
|
"Request", |
|
"RequestUpdateCheckStatus", |
|
"ResizeObserver", |
|
"ResizeObserverEntry", |
|
"ResizeObserverSize", |
|
"Response", |
|
"RestrictionTarget", |
|
"RuntimeError", |
|
"SAMPLER_2D", |
|
"SAMPLER_2D_ARRAY", |
|
"SAMPLER_2D_ARRAY_SHADOW", |
|
"SAMPLER_2D_SHADOW", |
|
"SAMPLER_3D", |
|
"SAMPLER_BINDING", |
|
"SAMPLER_CUBE", |
|
"SAMPLER_CUBE_SHADOW", |
|
"SAMPLES", |
|
"SAMPLE_ALPHA_TO_COVERAGE", |
|
"SAMPLE_BUFFERS", |
|
"SAMPLE_COVERAGE", |
|
"SAMPLE_COVERAGE_INVERT", |
|
"SAMPLE_COVERAGE_VALUE", |
|
"SAWTOOTH", |
|
"SCHEDULED_STATE", |
|
"SCISSOR_BOX", |
|
"SCISSOR_TEST", |
|
"SCROLL_PAGE_DOWN", |
|
"SCROLL_PAGE_UP", |
|
"SDP_ANSWER", |
|
"SDP_OFFER", |
|
"SDP_PRANSWER", |
|
"SECURITY_ERR", |
|
"SELECT", |
|
"SEPARATE_ATTRIBS", |
|
"SERIALIZE_ERR", |
|
"SEVERITY_ERROR", |
|
"SEVERITY_FATAL_ERROR", |
|
"SEVERITY_WARNING", |
|
"SHADER_COMPILER", |
|
"SHADER_TYPE", |
|
"SHADING_LANGUAGE_VERSION", |
|
"SHIFT_MASK", |
|
"SHORT", |
|
"SHOWING", |
|
"SHOW_ALL", |
|
"SHOW_ATTRIBUTE", |
|
"SHOW_CDATA_SECTION", |
|
"SHOW_COMMENT", |
|
"SHOW_DOCUMENT", |
|
"SHOW_DOCUMENT_FRAGMENT", |
|
"SHOW_DOCUMENT_TYPE", |
|
"SHOW_ELEMENT", |
|
"SHOW_ENTITY", |
|
"SHOW_ENTITY_REFERENCE", |
|
"SHOW_NOTATION", |
|
"SHOW_PROCESSING_INSTRUCTION", |
|
"SHOW_TEXT", |
|
"SIGNALED", |
|
"SIGNED_NORMALIZED", |
|
"SINE", |
|
"SOUNDFIELD", |
|
"SQLException", |
|
"SQRT1_2", |
|
"SQRT2", |
|
"SQUARE", |
|
"SRC_ALPHA", |
|
"SRC_ALPHA_SATURATE", |
|
"SRC_COLOR", |
|
"SRGB", |
|
"SRGB8", |
|
"SRGB8_ALPHA8", |
|
"START_TO_END", |
|
"START_TO_START", |
|
"STATIC_COPY", |
|
"STATIC_DRAW", |
|
"STATIC_READ", |
|
"STENCIL", |
|
"STENCIL_ATTACHMENT", |
|
"STENCIL_BACK_FAIL", |
|
"STENCIL_BACK_FUNC", |
|
"STENCIL_BACK_PASS_DEPTH_FAIL", |
|
"STENCIL_BACK_PASS_DEPTH_PASS", |
|
"STENCIL_BACK_REF", |
|
"STENCIL_BACK_VALUE_MASK", |
|
"STENCIL_BACK_WRITEMASK", |
|
"STENCIL_BITS", |
|
"STENCIL_BUFFER_BIT", |
|
"STENCIL_CLEAR_VALUE", |
|
"STENCIL_FAIL", |
|
"STENCIL_FUNC", |
|
"STENCIL_INDEX", |
|
"STENCIL_INDEX8", |
|
"STENCIL_PASS_DEPTH_FAIL", |
|
"STENCIL_PASS_DEPTH_PASS", |
|
"STENCIL_REF", |
|
"STENCIL_TEST", |
|
"STENCIL_VALUE_MASK", |
|
"STENCIL_WRITEMASK", |
|
"STORAGE", |
|
"STORAGE_BINDING", |
|
"STREAM_COPY", |
|
"STREAM_DRAW", |
|
"STREAM_READ", |
|
"STRING_TYPE", |
|
"STYLE_RULE", |
|
"SUBPIXEL_BITS", |
|
"SUPPORTS_RULE", |
|
"SVGAElement", |
|
"SVGAltGlyphDefElement", |
|
"SVGAltGlyphElement", |
|
"SVGAltGlyphItemElement", |
|
"SVGAngle", |
|
"SVGAnimateColorElement", |
|
"SVGAnimateElement", |
|
"SVGAnimateMotionElement", |
|
"SVGAnimateTransformElement", |
|
"SVGAnimatedAngle", |
|
"SVGAnimatedBoolean", |
|
"SVGAnimatedEnumeration", |
|
"SVGAnimatedInteger", |
|
"SVGAnimatedLength", |
|
"SVGAnimatedLengthList", |
|
"SVGAnimatedNumber", |
|
"SVGAnimatedNumberList", |
|
"SVGAnimatedPreserveAspectRatio", |
|
"SVGAnimatedRect", |
|
"SVGAnimatedString", |
|
"SVGAnimatedTransformList", |
|
"SVGAnimationElement", |
|
"SVGCircleElement", |
|
"SVGClipPathElement", |
|
"SVGColor", |
|
"SVGComponentTransferFunctionElement", |
|
"SVGCursorElement", |
|
"SVGDefsElement", |
|
"SVGDescElement", |
|
"SVGDiscardElement", |
|
"SVGDocument", |
|
"SVGElement", |
|
"SVGElementInstance", |
|
"SVGElementInstanceList", |
|
"SVGEllipseElement", |
|
"SVGException", |
|
"SVGFEBlendElement", |
|
"SVGFEColorMatrixElement", |
|
"SVGFEComponentTransferElement", |
|
"SVGFECompositeElement", |
|
"SVGFEConvolveMatrixElement", |
|
"SVGFEDiffuseLightingElement", |
|
"SVGFEDisplacementMapElement", |
|
"SVGFEDistantLightElement", |
|
"SVGFEDropShadowElement", |
|
"SVGFEFloodElement", |
|
"SVGFEFuncAElement", |
|
"SVGFEFuncBElement", |
|
"SVGFEFuncGElement", |
|
"SVGFEFuncRElement", |
|
"SVGFEGaussianBlurElement", |
|
"SVGFEImageElement", |
|
"SVGFEMergeElement", |
|
"SVGFEMergeNodeElement", |
|
"SVGFEMorphologyElement", |
|
"SVGFEOffsetElement", |
|
"SVGFEPointLightElement", |
|
"SVGFESpecularLightingElement", |
|
"SVGFESpotLightElement", |
|
"SVGFETileElement", |
|
"SVGFETurbulenceElement", |
|
"SVGFilterElement", |
|
"SVGFontElement", |
|
"SVGFontFaceElement", |
|
"SVGFontFaceFormatElement", |
|
"SVGFontFaceNameElement", |
|
"SVGFontFaceSrcElement", |
|
"SVGFontFaceUriElement", |
|
"SVGForeignObjectElement", |
|
"SVGGElement", |
|
"SVGGeometryElement", |
|
"SVGGlyphElement", |
|
"SVGGlyphRefElement", |
|
"SVGGradientElement", |
|
"SVGGraphicsElement", |
|
"SVGHKernElement", |
|
"SVGImageElement", |
|
"SVGLength", |
|
"SVGLengthList", |
|
"SVGLineElement", |
|
"SVGLinearGradientElement", |
|
"SVGMPathElement", |
|
"SVGMarkerElement", |
|
"SVGMaskElement", |
|
"SVGMatrix", |
|
"SVGMetadataElement", |
|
"SVGMissingGlyphElement", |
|
"SVGNumber", |
|
"SVGNumberList", |
|
"SVGPaint", |
|
"SVGPathElement", |
|
"SVGPathSeg", |
|
"SVGPathSegArcAbs", |
|
"SVGPathSegArcRel", |
|
"SVGPathSegClosePath", |
|
"SVGPathSegCurvetoCubicAbs", |
|
"SVGPathSegCurvetoCubicRel", |
|
"SVGPathSegCurvetoCubicSmoothAbs", |
|
"SVGPathSegCurvetoCubicSmoothRel", |
|
"SVGPathSegCurvetoQuadraticAbs", |
|
"SVGPathSegCurvetoQuadraticRel", |
|
"SVGPathSegCurvetoQuadraticSmoothAbs", |
|
"SVGPathSegCurvetoQuadraticSmoothRel", |
|
"SVGPathSegLinetoAbs", |
|
"SVGPathSegLinetoHorizontalAbs", |
|
"SVGPathSegLinetoHorizontalRel", |
|
"SVGPathSegLinetoRel", |
|
"SVGPathSegLinetoVerticalAbs", |
|
"SVGPathSegLinetoVerticalRel", |
|
"SVGPathSegList", |
|
"SVGPathSegMovetoAbs", |
|
"SVGPathSegMovetoRel", |
|
"SVGPatternElement", |
|
"SVGPoint", |
|
"SVGPointList", |
|
"SVGPolygonElement", |
|
"SVGPolylineElement", |
|
"SVGPreserveAspectRatio", |
|
"SVGRadialGradientElement", |
|
"SVGRect", |
|
"SVGRectElement", |
|
"SVGRenderingIntent", |
|
"SVGSVGElement", |
|
"SVGScriptElement", |
|
"SVGSetElement", |
|
"SVGStopElement", |
|
"SVGStringList", |
|
"SVGStyleElement", |
|
"SVGSwitchElement", |
|
"SVGSymbolElement", |
|
"SVGTRefElement", |
|
"SVGTSpanElement", |
|
"SVGTextContentElement", |
|
"SVGTextElement", |
|
"SVGTextPathElement", |
|
"SVGTextPositioningElement", |
|
"SVGTitleElement", |
|
"SVGTransform", |
|
"SVGTransformList", |
|
"SVGUnitTypes", |
|
"SVGUseElement", |
|
"SVGVKernElement", |
|
"SVGViewElement", |
|
"SVGViewSpec", |
|
"SVGZoomAndPan", |
|
"SVGZoomEvent", |
|
"SVG_ANGLETYPE_DEG", |
|
"SVG_ANGLETYPE_GRAD", |
|
"SVG_ANGLETYPE_RAD", |
|
"SVG_ANGLETYPE_UNKNOWN", |
|
"SVG_ANGLETYPE_UNSPECIFIED", |
|
"SVG_CHANNEL_A", |
|
"SVG_CHANNEL_B", |
|
"SVG_CHANNEL_G", |
|
"SVG_CHANNEL_R", |
|
"SVG_CHANNEL_UNKNOWN", |
|
"SVG_COLORTYPE_CURRENTCOLOR", |
|
"SVG_COLORTYPE_RGBCOLOR", |
|
"SVG_COLORTYPE_RGBCOLOR_ICCCOLOR", |
|
"SVG_COLORTYPE_UNKNOWN", |
|
"SVG_EDGEMODE_DUPLICATE", |
|
"SVG_EDGEMODE_NONE", |
|
"SVG_EDGEMODE_UNKNOWN", |
|
"SVG_EDGEMODE_WRAP", |
|
"SVG_FEBLEND_MODE_COLOR", |
|
"SVG_FEBLEND_MODE_COLOR_BURN", |
|
"SVG_FEBLEND_MODE_COLOR_DODGE", |
|
"SVG_FEBLEND_MODE_DARKEN", |
|
"SVG_FEBLEND_MODE_DIFFERENCE", |
|
"SVG_FEBLEND_MODE_EXCLUSION", |
|
"SVG_FEBLEND_MODE_HARD_LIGHT", |
|
"SVG_FEBLEND_MODE_HUE", |
|
"SVG_FEBLEND_MODE_LIGHTEN", |
|
"SVG_FEBLEND_MODE_LUMINOSITY", |
|
"SVG_FEBLEND_MODE_MULTIPLY", |
|
"SVG_FEBLEND_MODE_NORMAL", |
|
"SVG_FEBLEND_MODE_OVERLAY", |
|
"SVG_FEBLEND_MODE_SATURATION", |
|
"SVG_FEBLEND_MODE_SCREEN", |
|
"SVG_FEBLEND_MODE_SOFT_LIGHT", |
|
"SVG_FEBLEND_MODE_UNKNOWN", |
|
"SVG_FECOLORMATRIX_TYPE_HUEROTATE", |
|
"SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA", |
|
"SVG_FECOLORMATRIX_TYPE_MATRIX", |
|
"SVG_FECOLORMATRIX_TYPE_SATURATE", |
|
"SVG_FECOLORMATRIX_TYPE_UNKNOWN", |
|
"SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE", |
|
"SVG_FECOMPONENTTRANSFER_TYPE_GAMMA", |
|
"SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY", |
|
"SVG_FECOMPONENTTRANSFER_TYPE_LINEAR", |
|
"SVG_FECOMPONENTTRANSFER_TYPE_TABLE", |
|
"SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN", |
|
"SVG_FECOMPOSITE_OPERATOR_ARITHMETIC", |
|
"SVG_FECOMPOSITE_OPERATOR_ATOP", |
|
"SVG_FECOMPOSITE_OPERATOR_IN", |
|
"SVG_FECOMPOSITE_OPERATOR_LIGHTER", |
|
"SVG_FECOMPOSITE_OPERATOR_OUT", |
|
"SVG_FECOMPOSITE_OPERATOR_OVER", |
|
"SVG_FECOMPOSITE_OPERATOR_UNKNOWN", |
|
"SVG_FECOMPOSITE_OPERATOR_XOR", |
|
"SVG_INVALID_VALUE_ERR", |
|
"SVG_LENGTHTYPE_CM", |
|
"SVG_LENGTHTYPE_EMS", |
|
"SVG_LENGTHTYPE_EXS", |
|
"SVG_LENGTHTYPE_IN", |
|
"SVG_LENGTHTYPE_MM", |
|
"SVG_LENGTHTYPE_NUMBER", |
|
"SVG_LENGTHTYPE_PC", |
|
"SVG_LENGTHTYPE_PERCENTAGE", |
|
"SVG_LENGTHTYPE_PT", |
|
"SVG_LENGTHTYPE_PX", |
|
"SVG_LENGTHTYPE_UNKNOWN", |
|
"SVG_MARKERUNITS_STROKEWIDTH", |
|
"SVG_MARKERUNITS_UNKNOWN", |
|
"SVG_MARKERUNITS_USERSPACEONUSE", |
|
"SVG_MARKER_ORIENT_ANGLE", |
|
"SVG_MARKER_ORIENT_AUTO", |
|
"SVG_MARKER_ORIENT_AUTO_START_REVERSE", |
|
"SVG_MARKER_ORIENT_UNKNOWN", |
|
"SVG_MASKTYPE_ALPHA", |
|
"SVG_MASKTYPE_LUMINANCE", |
|
"SVG_MATRIX_NOT_INVERTABLE", |
|
"SVG_MEETORSLICE_MEET", |
|
"SVG_MEETORSLICE_SLICE", |
|
"SVG_MEETORSLICE_UNKNOWN", |
|
"SVG_MORPHOLOGY_OPERATOR_DILATE", |
|
"SVG_MORPHOLOGY_OPERATOR_ERODE", |
|
"SVG_MORPHOLOGY_OPERATOR_UNKNOWN", |
|
"SVG_PAINTTYPE_CURRENTCOLOR", |
|
"SVG_PAINTTYPE_NONE", |
|
"SVG_PAINTTYPE_RGBCOLOR", |
|
"SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR", |
|
"SVG_PAINTTYPE_UNKNOWN", |
|
"SVG_PAINTTYPE_URI", |
|
"SVG_PAINTTYPE_URI_CURRENTCOLOR", |
|
"SVG_PAINTTYPE_URI_NONE", |
|
"SVG_PAINTTYPE_URI_RGBCOLOR", |
|
"SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR", |
|
"SVG_PRESERVEASPECTRATIO_NONE", |
|
"SVG_PRESERVEASPECTRATIO_UNKNOWN", |
|
"SVG_PRESERVEASPECTRATIO_XMAXYMAX", |
|
"SVG_PRESERVEASPECTRATIO_XMAXYMID", |
|
"SVG_PRESERVEASPECTRATIO_XMAXYMIN", |
|
"SVG_PRESERVEASPECTRATIO_XMIDYMAX", |
|
"SVG_PRESERVEASPECTRATIO_XMIDYMID", |
|
"SVG_PRESERVEASPECTRATIO_XMIDYMIN", |
|
"SVG_PRESERVEASPECTRATIO_XMINYMAX", |
|
"SVG_PRESERVEASPECTRATIO_XMINYMID", |
|
"SVG_PRESERVEASPECTRATIO_XMINYMIN", |
|
"SVG_SPREADMETHOD_PAD", |
|
"SVG_SPREADMETHOD_REFLECT", |
|
"SVG_SPREADMETHOD_REPEAT", |
|
"SVG_SPREADMETHOD_UNKNOWN", |
|
"SVG_STITCHTYPE_NOSTITCH", |
|
"SVG_STITCHTYPE_STITCH", |
|
"SVG_STITCHTYPE_UNKNOWN", |
|
"SVG_TRANSFORM_MATRIX", |
|
"SVG_TRANSFORM_ROTATE", |
|
"SVG_TRANSFORM_SCALE", |
|
"SVG_TRANSFORM_SKEWX", |
|
"SVG_TRANSFORM_SKEWY", |
|
"SVG_TRANSFORM_TRANSLATE", |
|
"SVG_TRANSFORM_UNKNOWN", |
|
"SVG_TURBULENCE_TYPE_FRACTALNOISE", |
|
"SVG_TURBULENCE_TYPE_TURBULENCE", |
|
"SVG_TURBULENCE_TYPE_UNKNOWN", |
|
"SVG_UNIT_TYPE_OBJECTBOUNDINGBOX", |
|
"SVG_UNIT_TYPE_UNKNOWN", |
|
"SVG_UNIT_TYPE_USERSPACEONUSE", |
|
"SVG_WRONG_TYPE_ERR", |
|
"SVG_ZOOMANDPAN_DISABLE", |
|
"SVG_ZOOMANDPAN_MAGNIFY", |
|
"SVG_ZOOMANDPAN_UNKNOWN", |
|
"SYNC_CONDITION", |
|
"SYNC_FENCE", |
|
"SYNC_FLAGS", |
|
"SYNC_FLUSH_COMMANDS_BIT", |
|
"SYNC_GPU_COMMANDS_COMPLETE", |
|
"SYNC_STATUS", |
|
"SYNTAX_ERR", |
|
"SavedPages", |
|
"Scheduler", |
|
"Scheduling", |
|
"Screen", |
|
"ScreenDetailed", |
|
"ScreenDetails", |
|
"ScreenOrientation", |
|
"Script", |
|
"ScriptProcessorNode", |
|
"ScrollAreaEvent", |
|
"ScrollTimeline", |
|
"SecurityPolicyViolationEvent", |
|
"Segmenter", |
|
"Selection", |
|
"Sensor", |
|
"SensorErrorEvent", |
|
"Serial", |
|
"SerialPort", |
|
"ServiceWorker", |
|
"ServiceWorkerContainer", |
|
"ServiceWorkerRegistration", |
|
"SessionDescription", |
|
"Set", |
|
"ShadowRoot", |
|
"SharedArrayBuffer", |
|
"SharedStorage", |
|
"SharedStorageAppendMethod", |
|
"SharedStorageClearMethod", |
|
"SharedStorageDeleteMethod", |
|
"SharedStorageModifierMethod", |
|
"SharedStorageSetMethod", |
|
"SharedStorageWorklet", |
|
"SharedWorker", |
|
"SharingState", |
|
"SimpleGestureEvent", |
|
"SnapEvent", |
|
"SourceBuffer", |
|
"SourceBufferList", |
|
"SpeechGrammar", |
|
"SpeechGrammarList", |
|
"SpeechRecognition", |
|
"SpeechRecognitionErrorEvent", |
|
"SpeechRecognitionEvent", |
|
"SpeechRecognitionPhrase", |
|
"SpeechSynthesis", |
|
"SpeechSynthesisErrorEvent", |
|
"SpeechSynthesisEvent", |
|
"SpeechSynthesisUtterance", |
|
"SpeechSynthesisVoice", |
|
"StaticRange", |
|
"StereoPannerNode", |
|
"StopIteration", |
|
"Storage", |
|
"StorageBucket", |
|
"StorageBucketManager", |
|
"StorageEvent", |
|
"StorageManager", |
|
"String", |
|
"StructType", |
|
"StylePropertyMap", |
|
"StylePropertyMapReadOnly", |
|
"StyleSheet", |
|
"StyleSheetList", |
|
"SubmitEvent", |
|
"Subscriber", |
|
"SubtleCrypto", |
|
"Summarizer", |
|
"SuppressedError", |
|
"SuspendError", |
|
"Suspending", |
|
"Symbol", |
|
"SyncManager", |
|
"SyntaxError", |
|
"TAB_ID_NONE", |
|
"TAB_INDEX_NONE", |
|
"TEMPORARY", |
|
"TEXTPATH_METHODTYPE_ALIGN", |
|
"TEXTPATH_METHODTYPE_STRETCH", |
|
"TEXTPATH_METHODTYPE_UNKNOWN", |
|
"TEXTPATH_SPACINGTYPE_AUTO", |
|
"TEXTPATH_SPACINGTYPE_EXACT", |
|
"TEXTPATH_SPACINGTYPE_UNKNOWN", |
|
"TEXTURE", |
|
"TEXTURE0", |
|
"TEXTURE1", |
|
"TEXTURE10", |
|
"TEXTURE11", |
|
"TEXTURE12", |
|
"TEXTURE13", |
|
"TEXTURE14", |
|
"TEXTURE15", |
|
"TEXTURE16", |
|
"TEXTURE17", |
|
"TEXTURE18", |
|
"TEXTURE19", |
|
"TEXTURE2", |
|
"TEXTURE20", |
|
"TEXTURE21", |
|
"TEXTURE22", |
|
"TEXTURE23", |
|
"TEXTURE24", |
|
"TEXTURE25", |
|
"TEXTURE26", |
|
"TEXTURE27", |
|
"TEXTURE28", |
|
"TEXTURE29", |
|
"TEXTURE3", |
|
"TEXTURE30", |
|
"TEXTURE31", |
|
"TEXTURE4", |
|
"TEXTURE5", |
|
"TEXTURE6", |
|
"TEXTURE7", |
|
"TEXTURE8", |
|
"TEXTURE9", |
|
"TEXTURE_2D", |
|
"TEXTURE_2D_ARRAY", |
|
"TEXTURE_3D", |
|
"TEXTURE_BASE_LEVEL", |
|
"TEXTURE_BINDING", |
|
"TEXTURE_BINDING_2D", |
|
"TEXTURE_BINDING_2D_ARRAY", |
|
"TEXTURE_BINDING_3D", |
|
"TEXTURE_BINDING_CUBE_MAP", |
|
"TEXTURE_COMPARE_FUNC", |
|
"TEXTURE_COMPARE_MODE", |
|
"TEXTURE_CUBE_MAP", |
|
"TEXTURE_CUBE_MAP_NEGATIVE_X", |
|
"TEXTURE_CUBE_MAP_NEGATIVE_Y", |
|
"TEXTURE_CUBE_MAP_NEGATIVE_Z", |
|
"TEXTURE_CUBE_MAP_POSITIVE_X", |
|
"TEXTURE_CUBE_MAP_POSITIVE_Y", |
|
"TEXTURE_CUBE_MAP_POSITIVE_Z", |
|
"TEXTURE_IMMUTABLE_FORMAT", |
|
"TEXTURE_IMMUTABLE_LEVELS", |
|
"TEXTURE_MAG_FILTER", |
|
"TEXTURE_MAX_ANISOTROPY_EXT", |
|
"TEXTURE_MAX_LEVEL", |
|
"TEXTURE_MAX_LOD", |
|
"TEXTURE_MIN_FILTER", |
|
"TEXTURE_MIN_LOD", |
|
"TEXTURE_WRAP_R", |
|
"TEXTURE_WRAP_S", |
|
"TEXTURE_WRAP_T", |
|
"TEXT_NODE", |
|
"TIMEOUT", |
|
"TIMEOUT_ERR", |
|
"TIMEOUT_EXPIRED", |
|
"TIMEOUT_IGNORED", |
|
"TOO_LARGE_ERR", |
|
"TRANSACTION_INACTIVE_ERR", |
|
"TRANSFORM_FEEDBACK", |
|
"TRANSFORM_FEEDBACK_ACTIVE", |
|
"TRANSFORM_FEEDBACK_BINDING", |
|
"TRANSFORM_FEEDBACK_BUFFER", |
|
"TRANSFORM_FEEDBACK_BUFFER_BINDING", |
|
"TRANSFORM_FEEDBACK_BUFFER_MODE", |
|
"TRANSFORM_FEEDBACK_BUFFER_SIZE", |
|
"TRANSFORM_FEEDBACK_BUFFER_START", |
|
"TRANSFORM_FEEDBACK_PAUSED", |
|
"TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", |
|
"TRANSFORM_FEEDBACK_VARYINGS", |
|
"TRIANGLE", |
|
"TRIANGLES", |
|
"TRIANGLE_FAN", |
|
"TRIANGLE_STRIP", |
|
"TYPE_BACK_FORWARD", |
|
"TYPE_ERR", |
|
"TYPE_MISMATCH_ERR", |
|
"TYPE_NAVIGATE", |
|
"TYPE_RELOAD", |
|
"TYPE_RESERVED", |
|
"Tab", |
|
"TabStatus", |
|
"Table", |
|
"Tag", |
|
"TaskAttributionTiming", |
|
"TaskController", |
|
"TaskPriorityChangeEvent", |
|
"TaskSignal", |
|
"Temporal", |
|
"Text", |
|
"TextDecoder", |
|
"TextDecoderStream", |
|
"TextEncoder", |
|
"TextEncoderStream", |
|
"TextEvent", |
|
"TextFormat", |
|
"TextFormatUpdateEvent", |
|
"TextMetrics", |
|
"TextTrack", |
|
"TextTrackCue", |
|
"TextTrackCueList", |
|
"TextTrackList", |
|
"TextUpdateEvent", |
|
"TimeEvent", |
|
"TimeRanges", |
|
"ToggleEvent", |
|
"Touch", |
|
"TouchEvent", |
|
"TouchList", |
|
"TrackEvent", |
|
"TransformStream", |
|
"TransformStreamDefaultController", |
|
"TransitionEvent", |
|
"Translator", |
|
"TreeWalker", |
|
"TrustedHTML", |
|
"TrustedScript", |
|
"TrustedScriptURL", |
|
"TrustedTypePolicy", |
|
"TrustedTypePolicyFactory", |
|
"TypeError", |
|
"TypedObject", |
|
"U2F", |
|
"UIEvent", |
|
"UNCACHED", |
|
"UNIFORM", |
|
"UNIFORM_ARRAY_STRIDE", |
|
"UNIFORM_BLOCK_ACTIVE_UNIFORMS", |
|
"UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES", |
|
"UNIFORM_BLOCK_BINDING", |
|
"UNIFORM_BLOCK_DATA_SIZE", |
|
"UNIFORM_BLOCK_INDEX", |
|
"UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER", |
|
"UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER", |
|
"UNIFORM_BUFFER", |
|
"UNIFORM_BUFFER_BINDING", |
|
"UNIFORM_BUFFER_OFFSET_ALIGNMENT", |
|
"UNIFORM_BUFFER_SIZE", |
|
"UNIFORM_BUFFER_START", |
|
"UNIFORM_IS_ROW_MAJOR", |
|
"UNIFORM_MATRIX_STRIDE", |
|
"UNIFORM_OFFSET", |
|
"UNIFORM_SIZE", |
|
"UNIFORM_TYPE", |
|
"UNKNOWN_ERR", |
|
"UNKNOWN_RULE", |
|
"UNMASKED_RENDERER_WEBGL", |
|
"UNMASKED_VENDOR_WEBGL", |
|
"UNORDERED_NODE_ITERATOR_TYPE", |
|
"UNORDERED_NODE_SNAPSHOT_TYPE", |
|
"UNPACK_ALIGNMENT", |
|
"UNPACK_COLORSPACE_CONVERSION_WEBGL", |
|
"UNPACK_FLIP_Y_WEBGL", |
|
"UNPACK_IMAGE_HEIGHT", |
|
"UNPACK_PREMULTIPLY_ALPHA_WEBGL", |
|
"UNPACK_ROW_LENGTH", |
|
"UNPACK_SKIP_IMAGES", |
|
"UNPACK_SKIP_PIXELS", |
|
"UNPACK_SKIP_ROWS", |
|
"UNSCHEDULED_STATE", |
|
"UNSENT", |
|
"UNSIGNALED", |
|
"UNSIGNED_BYTE", |
|
"UNSIGNED_INT", |
|
"UNSIGNED_INT_10F_11F_11F_REV", |
|
"UNSIGNED_INT_24_8", |
|
"UNSIGNED_INT_2_10_10_10_REV", |
|
"UNSIGNED_INT_5_9_9_9_REV", |
|
"UNSIGNED_INT_SAMPLER_2D", |
|
"UNSIGNED_INT_SAMPLER_2D_ARRAY", |
|
"UNSIGNED_INT_SAMPLER_3D", |
|
"UNSIGNED_INT_SAMPLER_CUBE", |
|
"UNSIGNED_INT_VEC2", |
|
"UNSIGNED_INT_VEC3", |
|
"UNSIGNED_INT_VEC4", |
|
"UNSIGNED_NORMALIZED", |
|
"UNSIGNED_SHORT", |
|
"UNSIGNED_SHORT_4_4_4_4", |
|
"UNSIGNED_SHORT_5_5_5_1", |
|
"UNSIGNED_SHORT_5_6_5", |
|
"UNSPECIFIED_EVENT_TYPE_ERR", |
|
"UPDATEREADY", |
|
"URIError", |
|
"URL", |
|
"URLPattern", |
|
"URLSearchParams", |
|
"URLUnencoded", |
|
"URL_MISMATCH_ERR", |
|
"USB", |
|
"USBAlternateInterface", |
|
"USBConfiguration", |
|
"USBConnectionEvent", |
|
"USBDevice", |
|
"USBEndpoint", |
|
"USBInTransferResult", |
|
"USBInterface", |
|
"USBIsochronousInTransferPacket", |
|
"USBIsochronousInTransferResult", |
|
"USBIsochronousOutTransferPacket", |
|
"USBIsochronousOutTransferResult", |
|
"USBOutTransferResult", |
|
"UTC", |
|
"Uint16Array", |
|
"Uint32Array", |
|
"Uint8Array", |
|
"Uint8ClampedArray", |
|
"UpdateFilter", |
|
"UpdatePropertyName", |
|
"UserActivation", |
|
"UserMessageHandler", |
|
"UserMessageHandlersNamespace", |
|
"UserProximityEvent", |
|
"VALIDATE_STATUS", |
|
"VALIDATION_ERR", |
|
"VARIABLES_RULE", |
|
"VENDOR", |
|
"VERSION", |
|
"VERSION_CHANGE", |
|
"VERSION_ERR", |
|
"VERTEX", |
|
"VERTEX_ARRAY_BINDING", |
|
"VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", |
|
"VERTEX_ATTRIB_ARRAY_DIVISOR", |
|
"VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE", |
|
"VERTEX_ATTRIB_ARRAY_ENABLED", |
|
"VERTEX_ATTRIB_ARRAY_INTEGER", |
|
"VERTEX_ATTRIB_ARRAY_NORMALIZED", |
|
"VERTEX_ATTRIB_ARRAY_POINTER", |
|
"VERTEX_ATTRIB_ARRAY_SIZE", |
|
"VERTEX_ATTRIB_ARRAY_STRIDE", |
|
"VERTEX_ATTRIB_ARRAY_TYPE", |
|
"VERTEX_SHADER", |
|
"VERTICAL", |
|
"VERTICAL_AXIS", |
|
"VER_ERR", |
|
"VIEWPORT", |
|
"VIEWPORT_RULE", |
|
"VRDisplay", |
|
"VRDisplayCapabilities", |
|
"VRDisplayEvent", |
|
"VREyeParameters", |
|
"VRFieldOfView", |
|
"VRFrameData", |
|
"VRPose", |
|
"VRStageParameters", |
|
"VTTCue", |
|
"VTTRegion", |
|
"ValidityState", |
|
"VideoColorSpace", |
|
"VideoDecoder", |
|
"VideoEncoder", |
|
"VideoFrame", |
|
"VideoPlaybackQuality", |
|
"VideoStreamTrack", |
|
"ViewTimeline", |
|
"ViewTransition", |
|
"ViewTransitionTypeSet", |
|
"ViewType", |
|
"Viewport", |
|
"VirtualKeyboard", |
|
"VirtualKeyboardGeometryChangeEvent", |
|
"VisibilityStateEntry", |
|
"VisualViewport", |
|
"WAIT_FAILED", |
|
"WEBKIT_FILTER_RULE", |
|
"WEBKIT_KEYFRAMES_RULE", |
|
"WEBKIT_KEYFRAME_RULE", |
|
"WEBKIT_REGION_RULE", |
|
"WGSLLanguageFeatures", |
|
"WINDOW_ID_CURRENT", |
|
"WINDOW_ID_NONE", |
|
"WRITE", |
|
"WRONG_DOCUMENT_ERR", |
|
"WakeLock", |
|
"WakeLockSentinel", |
|
"WasmAnyRef", |
|
"WaveShaperNode", |
|
"WeakMap", |
|
"WeakRef", |
|
"WeakSet", |
|
"WebAssembly", |
|
"WebGL2RenderingContext", |
|
"WebGLActiveInfo", |
|
"WebGLBuffer", |
|
"WebGLContextEvent", |
|
"WebGLFramebuffer", |
|
"WebGLObject", |
|
"WebGLProgram", |
|
"WebGLQuery", |
|
"WebGLRenderbuffer", |
|
"WebGLRenderingContext", |
|
"WebGLSampler", |
|
"WebGLShader", |
|
"WebGLShaderPrecisionFormat", |
|
"WebGLSync", |
|
"WebGLTexture", |
|
"WebGLTransformFeedback", |
|
"WebGLUniformLocation", |
|
"WebGLVertexArray", |
|
"WebGLVertexArrayObject", |
|
"WebKitAnimationEvent", |
|
"WebKitBlobBuilder", |
|
"WebKitCSSFilterRule", |
|
"WebKitCSSFilterValue", |
|
"WebKitCSSKeyframeRule", |
|
"WebKitCSSKeyframesRule", |
|
"WebKitCSSMatrix", |
|
"WebKitCSSRegionRule", |
|
"WebKitCSSTransformValue", |
|
"WebKitDataCue", |
|
"WebKitGamepad", |
|
"WebKitMediaKeyError", |
|
"WebKitMediaKeyMessageEvent", |
|
"WebKitMediaKeySession", |
|
"WebKitMediaKeys", |
|
"WebKitMediaSource", |
|
"WebKitMutationObserver", |
|
"WebKitNamespace", |
|
"WebKitPlaybackTargetAvailabilityEvent", |
|
"WebKitPoint", |
|
"WebKitShadowRoot", |
|
"WebKitSourceBuffer", |
|
"WebKitSourceBufferList", |
|
"WebKitTransitionEvent", |
|
"WebSocket", |
|
"WebSocketError", |
|
"WebSocketStream", |
|
"WebTransport", |
|
"WebTransportBidirectionalStream", |
|
"WebTransportDatagramDuplexStream", |
|
"WebTransportError", |
|
"WebTransportReceiveStream", |
|
"WebTransportSendStream", |
|
"WebkitAlignContent", |
|
"WebkitAlignItems", |
|
"WebkitAlignSelf", |
|
"WebkitAnimation", |
|
"WebkitAnimationDelay", |
|
"WebkitAnimationDirection", |
|
"WebkitAnimationDuration", |
|
"WebkitAnimationFillMode", |
|
"WebkitAnimationIterationCount", |
|
"WebkitAnimationName", |
|
"WebkitAnimationPlayState", |
|
"WebkitAnimationTimingFunction", |
|
"WebkitAppearance", |
|
"WebkitBackfaceVisibility", |
|
"WebkitBackgroundClip", |
|
"WebkitBackgroundOrigin", |
|
"WebkitBackgroundSize", |
|
"WebkitBorderBottomLeftRadius", |
|
"WebkitBorderBottomRightRadius", |
|
"WebkitBorderImage", |
|
"WebkitBorderRadius", |
|
"WebkitBorderTopLeftRadius", |
|
"WebkitBorderTopRightRadius", |
|
"WebkitBoxAlign", |
|
"WebkitBoxDirection", |
|
"WebkitBoxFlex", |
|
"WebkitBoxOrdinalGroup", |
|
"WebkitBoxOrient", |
|
"WebkitBoxPack", |
|
"WebkitBoxShadow", |
|
"WebkitBoxSizing", |
|
"WebkitClipPath", |
|
"WebkitFilter", |
|
"WebkitFlex", |
|
"WebkitFlexBasis", |
|
"WebkitFlexDirection", |
|
"WebkitFlexFlow", |
|
"WebkitFlexGrow", |
|
"WebkitFlexShrink", |
|
"WebkitFlexWrap", |
|
"WebkitFontFeatureSettings", |
|
"WebkitJustifyContent", |
|
"WebkitLineClamp", |
|
"WebkitMask", |
|
"WebkitMaskClip", |
|
"WebkitMaskComposite", |
|
"WebkitMaskImage", |
|
"WebkitMaskOrigin", |
|
"WebkitMaskPosition", |
|
"WebkitMaskPositionX", |
|
"WebkitMaskPositionY", |
|
"WebkitMaskRepeat", |
|
"WebkitMaskSize", |
|
"WebkitOrder", |
|
"WebkitPerspective", |
|
"WebkitPerspectiveOrigin", |
|
"WebkitTextFillColor", |
|
"WebkitTextSecurity", |
|
"WebkitTextSizeAdjust", |
|
"WebkitTextStroke", |
|
"WebkitTextStrokeColor", |
|
"WebkitTextStrokeWidth", |
|
"WebkitTransform", |
|
"WebkitTransformOrigin", |
|
"WebkitTransformStyle", |
|
"WebkitTransition", |
|
"WebkitTransitionDelay", |
|
"WebkitTransitionDuration", |
|
"WebkitTransitionProperty", |
|
"WebkitTransitionTimingFunction", |
|
"WebkitUserSelect", |
|
"WheelEvent", |
|
"Window", |
|
"WindowControlsOverlay", |
|
"WindowControlsOverlayGeometryChangeEvent", |
|
"WindowState", |
|
"WindowType", |
|
"Worker", |
|
"Worklet", |
|
"WritableStream", |
|
"WritableStreamDefaultController", |
|
"WritableStreamDefaultWriter", |
|
"XMLDocument", |
|
"XMLHttpRequest", |
|
"XMLHttpRequestEventTarget", |
|
"XMLHttpRequestException", |
|
"XMLHttpRequestProgressEvent", |
|
"XMLHttpRequestUpload", |
|
"XMLSerializer", |
|
"XMLStylesheetProcessingInstruction", |
|
"XPathEvaluator", |
|
"XPathException", |
|
"XPathExpression", |
|
"XPathNSResolver", |
|
"XPathResult", |
|
"XRAnchor", |
|
"XRAnchorSet", |
|
"XRBoundedReferenceSpace", |
|
"XRCPUDepthInformation", |
|
"XRCamera", |
|
"XRDOMOverlayState", |
|
"XRDepthInformation", |
|
"XRFrame", |
|
"XRHand", |
|
"XRHitTestResult", |
|
"XRHitTestSource", |
|
"XRInputSource", |
|
"XRInputSourceArray", |
|
"XRInputSourceEvent", |
|
"XRInputSourcesChangeEvent", |
|
"XRJointPose", |
|
"XRJointSpace", |
|
"XRLayer", |
|
"XRLightEstimate", |
|
"XRLightProbe", |
|
"XRPose", |
|
"XRRay", |
|
"XRReferenceSpace", |
|
"XRReferenceSpaceEvent", |
|
"XRRenderState", |
|
"XRRigidTransform", |
|
"XRSession", |
|
"XRSessionEvent", |
|
"XRSpace", |
|
"XRSystem", |
|
"XRTransientInputHitTestResult", |
|
"XRTransientInputHitTestSource", |
|
"XRView", |
|
"XRViewerPose", |
|
"XRViewport", |
|
"XRWebGLBinding", |
|
"XRWebGLDepthInformation", |
|
"XRWebGLLayer", |
|
"XSLTProcessor", |
|
"ZERO", |
|
"ZonedDateTime", |
|
"ZoomSettings", |
|
"ZoomSettingsMode", |
|
"ZoomSettingsScope", |
|
"_XD0M_", |
|
"_YD0M_", |
|
"__REACT_DEVTOOLS_GLOBAL_HOOK__", |
|
"__brand", |
|
"__defineGetter__", |
|
"__defineSetter__", |
|
"__lookupGetter__", |
|
"__lookupSetter__", |
|
"__opera", |
|
"__proto__", |
|
"_browserjsran", |
|
"a", |
|
"aLink", |
|
"abbr", |
|
"abort", |
|
"aborted", |
|
"aboutConfigPrefs", |
|
"abs", |
|
"absolute", |
|
"acceleration", |
|
"accelerationIncludingGravity", |
|
"accelerator", |
|
"accent-color", |
|
"accentColor", |
|
"accept", |
|
"acceptCharset", |
|
"acceptNode", |
|
"access", |
|
"accessKey", |
|
"accessKeyLabel", |
|
"accuracy", |
|
"acos", |
|
"acosh", |
|
"action", |
|
"actionURL", |
|
"actions", |
|
"activated", |
|
"activation", |
|
"activationStart", |
|
"active", |
|
"activeCues", |
|
"activeElement", |
|
"activeSourceBuffers", |
|
"activeSourceCount", |
|
"activeTexture", |
|
"activeVRDisplays", |
|
"activeViewTransition", |
|
"activityLog", |
|
"actualBoundingBoxAscent", |
|
"actualBoundingBoxDescent", |
|
"actualBoundingBoxLeft", |
|
"actualBoundingBoxRight", |
|
"adAuctionComponents", |
|
"adAuctionHeaders", |
|
"adapterInfo", |
|
"add", |
|
"addAll", |
|
"addBehavior", |
|
"addCandidate", |
|
"addColorStop", |
|
"addCue", |
|
"addElement", |
|
"addEventListener", |
|
"addFilter", |
|
"addFromString", |
|
"addFromUri", |
|
"addIceCandidate", |
|
"addImport", |
|
"addListener", |
|
"addModule", |
|
"addNamed", |
|
"addPageRule", |
|
"addPath", |
|
"addPointer", |
|
"addRange", |
|
"addRegion", |
|
"addRule", |
|
"addSearchEngine", |
|
"addSourceBuffer", |
|
"addStream", |
|
"addTeardown", |
|
"addTextTrack", |
|
"addTrack", |
|
"addTransceiver", |
|
"addWakeLockListener", |
|
"added", |
|
"addedNodes", |
|
"additionalName", |
|
"additiveSymbols", |
|
"addons", |
|
"address", |
|
"addressLine", |
|
"addressModeU", |
|
"addressModeV", |
|
"addressModeW", |
|
"adopt", |
|
"adoptNode", |
|
"adoptedCallback", |
|
"adoptedStyleSheets", |
|
"adr", |
|
"advance", |
|
"after", |
|
"alarms", |
|
"album", |
|
"alert", |
|
"algorithm", |
|
"align", |
|
"align-content", |
|
"align-items", |
|
"align-self", |
|
"alignContent", |
|
"alignItems", |
|
"alignSelf", |
|
"alignmentBaseline", |
|
"alinkColor", |
|
"all", |
|
"allSettled", |
|
"allocationSize", |
|
"allow", |
|
"allowFullscreen", |
|
"allowPaymentRequest", |
|
"allowedDirections", |
|
"allowedFeatures", |
|
"allowedToPlay", |
|
"allowsFeature", |
|
"alpha", |
|
"alphaMode", |
|
"alphaToCoverageEnabled", |
|
"alphabeticBaseline", |
|
"alt", |
|
"altGraphKey", |
|
"altHtml", |
|
"altKey", |
|
"altLeft", |
|
"alternate", |
|
"alternateSetting", |
|
"alternates", |
|
"altitude", |
|
"altitudeAccuracy", |
|
"altitudeAngle", |
|
"amplitude", |
|
"ancestorOrigins", |
|
"anchor", |
|
"anchorName", |
|
"anchorNode", |
|
"anchorOffset", |
|
"anchorScope", |
|
"anchorSpace", |
|
"anchors", |
|
"and", |
|
"angle", |
|
"angularAcceleration", |
|
"angularVelocity", |
|
"animVal", |
|
"animate", |
|
"animated", |
|
"animatedInstanceRoot", |
|
"animatedNormalizedPathSegList", |
|
"animatedPathSegList", |
|
"animatedPoints", |
|
"animation", |
|
"animation-composition", |
|
"animation-delay", |
|
"animation-direction", |
|
"animation-duration", |
|
"animation-fill-mode", |
|
"animation-iteration-count", |
|
"animation-name", |
|
"animation-play-state", |
|
"animation-timing-function", |
|
"animationComposition", |
|
"animationDelay", |
|
"animationDirection", |
|
"animationDuration", |
|
"animationFillMode", |
|
"animationIterationCount", |
|
"animationName", |
|
"animationPlayState", |
|
"animationStartTime", |
|
"animationTimingFunction", |
|
"animationsPaused", |
|
"anniversary", |
|
"annotation", |
|
"antialias", |
|
"anticipatedRemoval", |
|
"any", |
|
"app", |
|
"appCodeName", |
|
"appMinorVersion", |
|
"appName", |
|
"appNotifications", |
|
"appVersion", |
|
"appearance", |
|
"append", |
|
"appendBuffer", |
|
"appendChild", |
|
"appendData", |
|
"appendItem", |
|
"appendMedium", |
|
"appendNamed", |
|
"appendRule", |
|
"appendStream", |
|
"appendWindowEnd", |
|
"appendWindowStart", |
|
"applets", |
|
"applicationCache", |
|
"applicationServerKey", |
|
"apply", |
|
"applyConstraints", |
|
"applyElement", |
|
"arc", |
|
"arcTo", |
|
"arch", |
|
"architecture", |
|
"archive", |
|
"areas", |
|
"arguments", |
|
"ariaActiveDescendantElement", |
|
"ariaAtomic", |
|
"ariaAutoComplete", |
|
"ariaBrailleLabel", |
|
"ariaBrailleRoleDescription", |
|
"ariaBusy", |
|
"ariaChecked", |
|
"ariaColCount", |
|
"ariaColIndex", |
|
"ariaColIndexText", |
|
"ariaColSpan", |
|
"ariaControlsElements", |
|
"ariaCurrent", |
|
"ariaDescribedByElements", |
|
"ariaDescription", |
|
"ariaDetailsElements", |
|
"ariaDisabled", |
|
"ariaErrorMessageElements", |
|
"ariaExpanded", |
|
"ariaFlowToElements", |
|
"ariaHasPopup", |
|
"ariaHidden", |
|
"ariaInvalid", |
|
"ariaKeyShortcuts", |
|
"ariaLabel", |
|
"ariaLabelledByElements", |
|
"ariaLevel", |
|
"ariaLive", |
|
"ariaModal", |
|
"ariaMultiLine", |
|
"ariaMultiSelectable", |
|
"ariaNotify", |
|
"ariaOrientation", |
|
"ariaOwnsElements", |
|
"ariaPlaceholder", |
|
"ariaPosInSet", |
|
"ariaPressed", |
|
"ariaReadOnly", |
|
"ariaRelevant", |
|
"ariaRequired", |
|
"ariaRoleDescription", |
|
"ariaRowCount", |
|
"ariaRowIndex", |
|
"ariaRowIndexText", |
|
"ariaRowSpan", |
|
"ariaSelected", |
|
"ariaSetSize", |
|
"ariaSort", |
|
"ariaValueMax", |
|
"ariaValueMin", |
|
"ariaValueNow", |
|
"ariaValueText", |
|
"arrayBuffer", |
|
"arrayLayerCount", |
|
"arrayStride", |
|
"artist", |
|
"artwork", |
|
"as", |
|
"asIntN", |
|
"asUintN", |
|
"ascentOverride", |
|
"asin", |
|
"asinh", |
|
"aspect", |
|
"aspect-ratio", |
|
"aspectRatio", |
|
"assert", |
|
"assign", |
|
"assignedElements", |
|
"assignedNodes", |
|
"assignedSlot", |
|
"async", |
|
"asyncDispose", |
|
"asyncIterator", |
|
"at", |
|
"atEnd", |
|
"atan", |
|
"atan2", |
|
"atanh", |
|
"atob", |
|
"attachEvent", |
|
"attachInternals", |
|
"attachShader", |
|
"attachShadow", |
|
"attachedElements", |
|
"attachments", |
|
"attack", |
|
"attestationObject", |
|
"attrChange", |
|
"attrName", |
|
"attributeChangedCallback", |
|
"attributeFilter", |
|
"attributeName", |
|
"attributeNamespace", |
|
"attributeOldValue", |
|
"attributeStyleMap", |
|
"attributes", |
|
"attribution", |
|
"attributionSrc", |
|
"audioBitrateMode", |
|
"audioBitsPerSecond", |
|
"audioTracks", |
|
"audioWorklet", |
|
"authenticatedSignedWrites", |
|
"authenticatorAttachment", |
|
"authenticatorData", |
|
"autoIncrement", |
|
"autobuffer", |
|
"autocapitalize", |
|
"autocomplete", |
|
"autocorrect", |
|
"autofocus", |
|
"automationRate", |
|
"autoplay", |
|
"availHeight", |
|
"availLeft", |
|
"availTop", |
|
"availWidth", |
|
"availability", |
|
"available", |
|
"averageLatency", |
|
"aversion", |
|
"ax", |
|
"axes", |
|
"axis", |
|
"ay", |
|
"azimuth", |
|
"azimuthAngle", |
|
"b", |
|
"back", |
|
"backdrop-filter", |
|
"backdropFilter", |
|
"backends", |
|
"backface-visibility", |
|
"backfaceVisibility", |
|
"background", |
|
"background-attachment", |
|
"background-blend-mode", |
|
"background-clip", |
|
"background-color", |
|
"background-image", |
|
"background-origin", |
|
"background-position", |
|
"background-position-x", |
|
"background-position-y", |
|
"background-repeat", |
|
"background-size", |
|
"backgroundAttachment", |
|
"backgroundBlendMode", |
|
"backgroundClip", |
|
"backgroundColor", |
|
"backgroundFetch", |
|
"backgroundImage", |
|
"backgroundOrigin", |
|
"backgroundPosition", |
|
"backgroundPositionX", |
|
"backgroundPositionY", |
|
"backgroundRepeat", |
|
"backgroundSize", |
|
"badInput", |
|
"badge", |
|
"balance", |
|
"baseArrayLayer", |
|
"baseFrequencyX", |
|
"baseFrequencyY", |
|
"baseLatency", |
|
"baseLayer", |
|
"baseMipLevel", |
|
"baseNode", |
|
"baseOffset", |
|
"basePalette", |
|
"baseURI", |
|
"baseVal", |
|
"baseline-source", |
|
"baselineShift", |
|
"baselineSource", |
|
"batchUpdate", |
|
"battery", |
|
"bday", |
|
"before", |
|
"beginComputePass", |
|
"beginElement", |
|
"beginElementAt", |
|
"beginOcclusionQuery", |
|
"beginPath", |
|
"beginQuery", |
|
"beginRenderPass", |
|
"beginTransformFeedback", |
|
"beginningOfPassWriteIndex", |
|
"behavior", |
|
"behaviorCookie", |
|
"behaviorPart", |
|
"behaviorUrns", |
|
"beta", |
|
"bezierCurveTo", |
|
"bgColor", |
|
"bgProperties", |
|
"bias", |
|
"big", |
|
"bigint64", |
|
"biguint64", |
|
"binaryType", |
|
"bind", |
|
"bindAttribLocation", |
|
"bindBuffer", |
|
"bindBufferBase", |
|
"bindBufferRange", |
|
"bindFramebuffer", |
|
"bindGroupLayouts", |
|
"bindRenderbuffer", |
|
"bindSampler", |
|
"bindTexture", |
|
"bindTransformFeedback", |
|
"bindVertexArray", |
|
"binding", |
|
"bitness", |
|
"blend", |
|
"blendColor", |
|
"blendEquation", |
|
"blendEquationSeparate", |
|
"blendFunc", |
|
"blendFuncSeparate", |
|
"blink", |
|
"blitFramebuffer", |
|
"blob", |
|
"block-size", |
|
"blockDirection", |
|
"blockSize", |
|
"blockedURI", |
|
"blockedURL", |
|
"blocking", |
|
"blockingDuration", |
|
"blue", |
|
"bluetooth", |
|
"blur", |
|
"body", |
|
"bodyUsed", |
|
"bold", |
|
"bookmarks", |
|
"booleanValue", |
|
"boost", |
|
"border", |
|
"border-block", |
|
"border-block-color", |
|
"border-block-end", |
|
"border-block-end-color", |
|
"border-block-end-style", |
|
"border-block-end-width", |
|
"border-block-start", |
|
"border-block-start-color", |
|
"border-block-start-style", |
|
"border-block-start-width", |
|
"border-block-style", |
|
"border-block-width", |
|
"border-bottom", |
|
"border-bottom-color", |
|
"border-bottom-left-radius", |
|
"border-bottom-right-radius", |
|
"border-bottom-style", |
|
"border-bottom-width", |
|
"border-collapse", |
|
"border-color", |
|
"border-end-end-radius", |
|
"border-end-start-radius", |
|
"border-image", |
|
"border-image-outset", |
|
"border-image-repeat", |
|
"border-image-slice", |
|
"border-image-source", |
|
"border-image-width", |
|
"border-inline", |
|
"border-inline-color", |
|
"border-inline-end", |
|
"border-inline-end-color", |
|
"border-inline-end-style", |
|
"border-inline-end-width", |
|
"border-inline-start", |
|
"border-inline-start-color", |
|
"border-inline-start-style", |
|
"border-inline-start-width", |
|
"border-inline-style", |
|
"border-inline-width", |
|
"border-left", |
|
"border-left-color", |
|
"border-left-style", |
|
"border-left-width", |
|
"border-radius", |
|
"border-right", |
|
"border-right-color", |
|
"border-right-style", |
|
"border-right-width", |
|
"border-spacing", |
|
"border-start-end-radius", |
|
"border-start-start-radius", |
|
"border-style", |
|
"border-top", |
|
"border-top-color", |
|
"border-top-left-radius", |
|
"border-top-right-radius", |
|
"border-top-style", |
|
"border-top-width", |
|
"border-width", |
|
"borderBlock", |
|
"borderBlockColor", |
|
"borderBlockEnd", |
|
"borderBlockEndColor", |
|
"borderBlockEndStyle", |
|
"borderBlockEndWidth", |
|
"borderBlockStart", |
|
"borderBlockStartColor", |
|
"borderBlockStartStyle", |
|
"borderBlockStartWidth", |
|
"borderBlockStyle", |
|
"borderBlockWidth", |
|
"borderBottom", |
|
"borderBottomColor", |
|
"borderBottomLeftRadius", |
|
"borderBottomRightRadius", |
|
"borderBottomStyle", |
|
"borderBottomWidth", |
|
"borderBoxSize", |
|
"borderCollapse", |
|
"borderColor", |
|
"borderColorDark", |
|
"borderColorLight", |
|
"borderEndEndRadius", |
|
"borderEndStartRadius", |
|
"borderImage", |
|
"borderImageOutset", |
|
"borderImageRepeat", |
|
"borderImageSlice", |
|
"borderImageSource", |
|
"borderImageWidth", |
|
"borderInline", |
|
"borderInlineColor", |
|
"borderInlineEnd", |
|
"borderInlineEndColor", |
|
"borderInlineEndStyle", |
|
"borderInlineEndWidth", |
|
"borderInlineStart", |
|
"borderInlineStartColor", |
|
"borderInlineStartStyle", |
|
"borderInlineStartWidth", |
|
"borderInlineStyle", |
|
"borderInlineWidth", |
|
"borderLeft", |
|
"borderLeftColor", |
|
"borderLeftStyle", |
|
"borderLeftWidth", |
|
"borderRadius", |
|
"borderRight", |
|
"borderRightColor", |
|
"borderRightStyle", |
|
"borderRightWidth", |
|
"borderSpacing", |
|
"borderStartEndRadius", |
|
"borderStartStartRadius", |
|
"borderStyle", |
|
"borderTop", |
|
"borderTopColor", |
|
"borderTopLeftRadius", |
|
"borderTopRightRadius", |
|
"borderTopStyle", |
|
"borderTopWidth", |
|
"borderWidth", |
|
"bottom", |
|
"bottomMargin", |
|
"bound", |
|
"boundElements", |
|
"boundingClientRect", |
|
"boundingHeight", |
|
"boundingLeft", |
|
"boundingRect", |
|
"boundingTop", |
|
"boundingWidth", |
|
"bounds", |
|
"boundsGeometry", |
|
"box-decoration-break", |
|
"box-shadow", |
|
"box-sizing", |
|
"boxDecorationBreak", |
|
"boxShadow", |
|
"boxSizing", |
|
"brand", |
|
"brands", |
|
"break-after", |
|
"break-before", |
|
"break-inside", |
|
"breakAfter", |
|
"breakBefore", |
|
"breakInside", |
|
"broadcast", |
|
"browser", |
|
"browserLanguage", |
|
"browserSettings", |
|
"browsingData", |
|
"browsingTopics", |
|
"btoa", |
|
"bubbles", |
|
"buffer", |
|
"bufferData", |
|
"bufferDepth", |
|
"bufferSize", |
|
"bufferSubData", |
|
"buffered", |
|
"bufferedAmount", |
|
"bufferedAmountLowThreshold", |
|
"buffers", |
|
"buildID", |
|
"buildNumber", |
|
"button", |
|
"buttonID", |
|
"buttons", |
|
"byobRequest", |
|
"byteLength", |
|
"byteOffset", |
|
"bytes", |
|
"bytesPerRow", |
|
"bytesWritten", |
|
"c", |
|
"cache", |
|
"caches", |
|
"calendar", |
|
"call", |
|
"caller", |
|
"camera", |
|
"canBeFormatted", |
|
"canBeMounted", |
|
"canBeShared", |
|
"canConstructInDedicatedWorker", |
|
"canGoBack", |
|
"canGoForward", |
|
"canHaveChildren", |
|
"canHaveHTML", |
|
"canInsertDTMF", |
|
"canIntercept", |
|
"canLoadAdAuctionFencedFrame", |
|
"canLoadOpaqueURL", |
|
"canMakePayment", |
|
"canParse", |
|
"canPlayType", |
|
"canPresent", |
|
"canShare", |
|
"canTransition", |
|
"canTrickleIceCandidates", |
|
"cancel", |
|
"cancelAndHoldAtTime", |
|
"cancelAnimationFrame", |
|
"cancelBubble", |
|
"cancelIdleCallback", |
|
"cancelScheduledValues", |
|
"cancelVideoFrameCallback", |
|
"cancelWatchAvailability", |
|
"cancelable", |
|
"candidate", |
|
"canonicalUUID", |
|
"canvas", |
|
"cap", |
|
"capabilities", |
|
"caption", |
|
"caption-side", |
|
"captionSide", |
|
"captivePortal", |
|
"capture", |
|
"captureEvents", |
|
"captureStackTrace", |
|
"captureStream", |
|
"captureTab", |
|
"captureVisibleTab", |
|
"caret-color", |
|
"caretBidiLevel", |
|
"caretColor", |
|
"caretPositionFromPoint", |
|
"caretRangeFromPoint", |
|
"cast", |
|
"catch", |
|
"category", |
|
"cause", |
|
"cbrt", |
|
"cd", |
|
"ceil", |
|
"cellIndex", |
|
"cellPadding", |
|
"cellSpacing", |
|
"cells", |
|
"ch", |
|
"chOff", |
|
"chain", |
|
"challenge", |
|
"changeType", |
|
"changed", |
|
"changedTouches", |
|
"channel", |
|
"channelCount", |
|
"channelCountMode", |
|
"channelInterpretation", |
|
"chapterInfo", |
|
"char", |
|
"charAt", |
|
"charCode", |
|
"charCodeAt", |
|
"charIndex", |
|
"charLength", |
|
"characterBounds", |
|
"characterBoundsRangeStart", |
|
"characterData", |
|
"characterDataOldValue", |
|
"characterSet", |
|
"characterVariant", |
|
"characteristic", |
|
"charging", |
|
"chargingTime", |
|
"charset", |
|
"check", |
|
"checkDCE", |
|
"checkEnclosure", |
|
"checkFramebufferStatus", |
|
"checkIntersection", |
|
"checkValidity", |
|
"checkVisibility", |
|
"checked", |
|
"childElementCount", |
|
"childList", |
|
"childNodes", |
|
"children", |
|
"chrome", |
|
"ciphertext", |
|
"cite", |
|
"city", |
|
"claimInterface", |
|
"claimed", |
|
"classList", |
|
"className", |
|
"classid", |
|
"clear", |
|
"clearAppBadge", |
|
"clearAttributes", |
|
"clearBuffer", |
|
"clearBufferfi", |
|
"clearBufferfv", |
|
"clearBufferiv", |
|
"clearBufferuiv", |
|
"clearColor", |
|
"clearData", |
|
"clearDepth", |
|
"clearHalt", |
|
"clearImmediate", |
|
"clearInterval", |
|
"clearLiveSeekableRange", |
|
"clearMarks", |
|
"clearMaxGCPauseAccumulator", |
|
"clearMeasures", |
|
"clearOriginJoinedAdInterestGroups", |
|
"clearParameters", |
|
"clearRect", |
|
"clearResourceTimings", |
|
"clearShadow", |
|
"clearStencil", |
|
"clearTimeout", |
|
"clearValue", |
|
"clearWatch", |
|
"click", |
|
"clickCount", |
|
"clientDataJSON", |
|
"clientHeight", |
|
"clientInformation", |
|
"clientLeft", |
|
"clientRect", |
|
"clientRects", |
|
"clientTop", |
|
"clientWaitSync", |
|
"clientWidth", |
|
"clientX", |
|
"clientY", |
|
"clip", |
|
"clip-path", |
|
"clip-rule", |
|
"clipBottom", |
|
"clipLeft", |
|
"clipPath", |
|
"clipPathUnits", |
|
"clipRight", |
|
"clipRule", |
|
"clipTop", |
|
"clipboard", |
|
"clipboardData", |
|
"clonable", |
|
"clone", |
|
"cloneContents", |
|
"cloneNode", |
|
"cloneRange", |
|
"close", |
|
"closeCode", |
|
"closePath", |
|
"closed", |
|
"closedBy", |
|
"closest", |
|
"clz", |
|
"clz32", |
|
"cm", |
|
"cmp", |
|
"code", |
|
"codeBase", |
|
"codePointAt", |
|
"codeType", |
|
"codedHeight", |
|
"codedRect", |
|
"codedWidth", |
|
"colSpan", |
|
"collapse", |
|
"collapseToEnd", |
|
"collapseToStart", |
|
"collapsed", |
|
"collect", |
|
"collections", |
|
"colno", |
|
"color", |
|
"color-adjust", |
|
"color-interpolation", |
|
"color-interpolation-filters", |
|
"color-scheme", |
|
"colorAdjust", |
|
"colorAttachments", |
|
"colorDepth", |
|
"colorFormats", |
|
"colorInterpolation", |
|
"colorInterpolationFilters", |
|
"colorMask", |
|
"colorScheme", |
|
"colorSpace", |
|
"colorType", |
|
"cols", |
|
"column-count", |
|
"column-fill", |
|
"column-gap", |
|
"column-rule", |
|
"column-rule-color", |
|
"column-rule-style", |
|
"column-rule-width", |
|
"column-span", |
|
"column-width", |
|
"columnCount", |
|
"columnFill", |
|
"columnGap", |
|
"columnNumber", |
|
"columnRule", |
|
"columnRuleColor", |
|
"columnRuleStyle", |
|
"columnRuleWidth", |
|
"columnSpan", |
|
"columnWidth", |
|
"columns", |
|
"command", |
|
"commandForElement", |
|
"commands", |
|
"commit", |
|
"commitLoadTime", |
|
"commitPreferences", |
|
"commitStyles", |
|
"committed", |
|
"commonAncestorContainer", |
|
"compact", |
|
"compare", |
|
"compareBoundaryPoints", |
|
"compareDocumentPosition", |
|
"compareEndPoints", |
|
"compareExchange", |
|
"compareNode", |
|
"comparePoint", |
|
"compatMode", |
|
"compatible", |
|
"compile", |
|
"compileShader", |
|
"compileStreaming", |
|
"complete", |
|
"completed", |
|
"component", |
|
"componentFromPoint", |
|
"composed", |
|
"composedPath", |
|
"composite", |
|
"compositionEndOffset", |
|
"compositionStartOffset", |
|
"compressedTexImage2D", |
|
"compressedTexImage3D", |
|
"compressedTexSubImage2D", |
|
"compressedTexSubImage3D", |
|
"compute", |
|
"computedStyleMap", |
|
"concat", |
|
"conditionText", |
|
"coneInnerAngle", |
|
"coneOuterAngle", |
|
"coneOuterGain", |
|
"config", |
|
"configURL", |
|
"configurable", |
|
"configuration", |
|
"configurationName", |
|
"configurationValue", |
|
"configurations", |
|
"configure", |
|
"confirm", |
|
"confirmComposition", |
|
"confirmSiteSpecificTrackingException", |
|
"confirmWebWideTrackingException", |
|
"congestionControl", |
|
"connect", |
|
"connectEnd", |
|
"connectNative", |
|
"connectShark", |
|
"connectStart", |
|
"connected", |
|
"connectedCallback", |
|
"connectedMoveCallback", |
|
"connection", |
|
"connectionInfo", |
|
"connectionList", |
|
"connectionSpeed", |
|
"connectionState", |
|
"connections", |
|
"console", |
|
"consolidate", |
|
"constants", |
|
"constraint", |
|
"constrictionActive", |
|
"construct", |
|
"constructor", |
|
"contactID", |
|
"contain", |
|
"contain-intrinsic-block-size", |
|
"contain-intrinsic-height", |
|
"contain-intrinsic-inline-size", |
|
"contain-intrinsic-size", |
|
"contain-intrinsic-width", |
|
"containIntrinsicBlockSize", |
|
"containIntrinsicHeight", |
|
"containIntrinsicInlineSize", |
|
"containIntrinsicSize", |
|
"containIntrinsicWidth", |
|
"container", |
|
"container-name", |
|
"container-type", |
|
"containerId", |
|
"containerName", |
|
"containerQuery", |
|
"containerSrc", |
|
"containerType", |
|
"contains", |
|
"containsNode", |
|
"content", |
|
"content-visibility", |
|
"contentBoxSize", |
|
"contentDocument", |
|
"contentEditable", |
|
"contentEncoding", |
|
"contentHint", |
|
"contentOverflow", |
|
"contentRect", |
|
"contentScriptType", |
|
"contentStyleType", |
|
"contentType", |
|
"contentVisibility", |
|
"contentWindow", |
|
"context", |
|
"contextId", |
|
"contextIds", |
|
"contextMenu", |
|
"contextMenus", |
|
"contextType", |
|
"contextTypes", |
|
"contextmenu", |
|
"contextualIdentities", |
|
"continue", |
|
"continuePrimaryKey", |
|
"continuous", |
|
"control", |
|
"controlTransferIn", |
|
"controlTransferOut", |
|
"controller", |
|
"controls", |
|
"controlsList", |
|
"convertPointFromNode", |
|
"convertQuadFromNode", |
|
"convertRectFromNode", |
|
"convertToBlob", |
|
"convertToSpecifiedUnits", |
|
"cookie", |
|
"cookieEnabled", |
|
"cookieStore", |
|
"cookies", |
|
"coords", |
|
"copyBufferSubData", |
|
"copyBufferToBuffer", |
|
"copyBufferToTexture", |
|
"copyExternalImageToTexture", |
|
"copyFromChannel", |
|
"copyTexImage2D", |
|
"copyTexSubImage2D", |
|
"copyTexSubImage3D", |
|
"copyTextureToBuffer", |
|
"copyTextureToTexture", |
|
"copyTo", |
|
"copyToChannel", |
|
"copyWithin", |
|
"correspondingElement", |
|
"correspondingUseElement", |
|
"corruptedVideoFrames", |
|
"cos", |
|
"cosh", |
|
"count", |
|
"countReset", |
|
"counter-increment", |
|
"counter-reset", |
|
"counter-set", |
|
"counterIncrement", |
|
"counterReset", |
|
"counterSet", |
|
"country", |
|
"cpuClass", |
|
"cpuSleepAllowed", |
|
"cqb", |
|
"cqh", |
|
"cqi", |
|
"cqmax", |
|
"cqmin", |
|
"cqw", |
|
"create", |
|
"createAnalyser", |
|
"createAnchor", |
|
"createAnswer", |
|
"createAttribute", |
|
"createAttributeNS", |
|
"createAuctionNonce", |
|
"createBidirectionalStream", |
|
"createBindGroup", |
|
"createBindGroupLayout", |
|
"createBiquadFilter", |
|
"createBuffer", |
|
"createBufferSource", |
|
"createCDATASection", |
|
"createCSSStyleSheet", |
|
"createCaption", |
|
"createChannelMerger", |
|
"createChannelSplitter", |
|
"createCommandEncoder", |
|
"createComment", |
|
"createComputePipeline", |
|
"createComputePipelineAsync", |
|
"createConicGradient", |
|
"createConstantSource", |
|
"createContextualFragment", |
|
"createControlRange", |
|
"createConvolver", |
|
"createDTMFSender", |
|
"createDataChannel", |
|
"createDelay", |
|
"createDelayNode", |
|
"createDocument", |
|
"createDocumentFragment", |
|
"createDocumentType", |
|
"createDynamicsCompressor", |
|
"createElement", |
|
"createElementNS", |
|
"createEncodedStreams", |
|
"createEntityReference", |
|
"createEvent", |
|
"createEventObject", |
|
"createExpression", |
|
"createFramebuffer", |
|
"createFunction", |
|
"createGain", |
|
"createGainNode", |
|
"createHTML", |
|
"createHTMLDocument", |
|
"createIIRFilter", |
|
"createImageBitmap", |
|
"createImageData", |
|
"createIndex", |
|
"createJavaScriptNode", |
|
"createLinearGradient", |
|
"createMediaElementSource", |
|
"createMediaKeys", |
|
"createMediaStreamDestination", |
|
"createMediaStreamSource", |
|
"createMediaStreamTrackSource", |
|
"createMutableFile", |
|
"createNSResolver", |
|
"createNodeIterator", |
|
"createNotification", |
|
"createObjectStore", |
|
"createObjectURL", |
|
"createOffer", |
|
"createOscillator", |
|
"createPanner", |
|
"createPattern", |
|
"createPeriodicWave", |
|
"createPipelineLayout", |
|
"createPolicy", |
|
"createPopup", |
|
"createProcessingInstruction", |
|
"createProgram", |
|
"createQuery", |
|
"createQuerySet", |
|
"createRadialGradient", |
|
"createRange", |
|
"createRangeCollection", |
|
"createReader", |
|
"createRenderBundleEncoder", |
|
"createRenderPipeline", |
|
"createRenderPipelineAsync", |
|
"createRenderbuffer", |
|
"createSVGAngle", |
|
"createSVGLength", |
|
"createSVGMatrix", |
|
"createSVGNumber", |
|
"createSVGPathSegArcAbs", |
|
"createSVGPathSegArcRel", |
|
"createSVGPathSegClosePath", |
|
"createSVGPathSegCurvetoCubicAbs", |
|
"createSVGPathSegCurvetoCubicRel", |
|
"createSVGPathSegCurvetoCubicSmoothAbs", |
|
"createSVGPathSegCurvetoCubicSmoothRel", |
|
"createSVGPathSegCurvetoQuadraticAbs", |
|
"createSVGPathSegCurvetoQuadraticRel", |
|
"createSVGPathSegCurvetoQuadraticSmoothAbs", |
|
"createSVGPathSegCurvetoQuadraticSmoothRel", |
|
"createSVGPathSegLinetoAbs", |
|
"createSVGPathSegLinetoHorizontalAbs", |
|
"createSVGPathSegLinetoHorizontalRel", |
|
"createSVGPathSegLinetoRel", |
|
"createSVGPathSegLinetoVerticalAbs", |
|
"createSVGPathSegLinetoVerticalRel", |
|
"createSVGPathSegMovetoAbs", |
|
"createSVGPathSegMovetoRel", |
|
"createSVGPoint", |
|
"createSVGRect", |
|
"createSVGTransform", |
|
"createSVGTransformFromMatrix", |
|
"createSampler", |
|
"createScript", |
|
"createScriptProcessor", |
|
"createScriptURL", |
|
"createSession", |
|
"createShader", |
|
"createShaderModule", |
|
"createShadowRoot", |
|
"createStereoPanner", |
|
"createStyleSheet", |
|
"createTBody", |
|
"createTFoot", |
|
"createTHead", |
|
"createTask", |
|
"createTextNode", |
|
"createTextRange", |
|
"createTexture", |
|
"createTouch", |
|
"createTouchList", |
|
"createTransformFeedback", |
|
"createTreeWalker", |
|
"createUnidirectionalStream", |
|
"createVertexArray", |
|
"createView", |
|
"createWaveShaper", |
|
"createWorklet", |
|
"createWritable", |
|
"creationTime", |
|
"credentialless", |
|
"credentials", |
|
"criticalCHRestart", |
|
"cropTo", |
|
"crossOrigin", |
|
"crossOriginIsolated", |
|
"crypto", |
|
"csi", |
|
"csp", |
|
"cssFloat", |
|
"cssRules", |
|
"cssText", |
|
"cssValueType", |
|
"ctrlKey", |
|
"ctrlLeft", |
|
"cues", |
|
"cullFace", |
|
"cullMode", |
|
"currentCSSZoom", |
|
"currentDirection", |
|
"currentEntry", |
|
"currentLocalDescription", |
|
"currentNode", |
|
"currentPage", |
|
"currentRect", |
|
"currentRemoteDescription", |
|
"currentScale", |
|
"currentScreen", |
|
"currentScript", |
|
"currentSrc", |
|
"currentState", |
|
"currentStyle", |
|
"currentTarget", |
|
"currentTime", |
|
"currentTranslate", |
|
"currentView", |
|
"cursor", |
|
"curve", |
|
"customElements", |
|
"customError", |
|
"cx", |
|
"cy", |
|
"d", |
|
"data", |
|
"dataFld", |
|
"dataFormatAs", |
|
"dataLoss", |
|
"dataLossMessage", |
|
"dataPageSize", |
|
"dataSrc", |
|
"dataTransfer", |
|
"database", |
|
"databases", |
|
"datagrams", |
|
"dataset", |
|
"dateStyle", |
|
"dateTime", |
|
"day", |
|
"dayPeriod", |
|
"days", |
|
"db", |
|
"debug", |
|
"debuggerEnabled", |
|
"declarativeNetRequest", |
|
"declare", |
|
"decode", |
|
"decodeAudioData", |
|
"decodeQueueSize", |
|
"decodeURI", |
|
"decodeURIComponent", |
|
"decodedBodySize", |
|
"decoding", |
|
"decodingInfo", |
|
"decreaseZoomLevel", |
|
"decrypt", |
|
"default", |
|
"defaultCharset", |
|
"defaultChecked", |
|
"defaultMuted", |
|
"defaultPlaybackRate", |
|
"defaultPolicy", |
|
"defaultPrevented", |
|
"defaultQueue", |
|
"defaultRequest", |
|
"defaultSelected", |
|
"defaultStatus", |
|
"defaultURL", |
|
"defaultValue", |
|
"defaultView", |
|
"defaultstatus", |
|
"defer", |
|
"define", |
|
"defineMagicFunction", |
|
"defineMagicVariable", |
|
"defineProperties", |
|
"defineProperty", |
|
"deg", |
|
"delay", |
|
"delayTime", |
|
"delegatesFocus", |
|
"delete", |
|
"deleteBuffer", |
|
"deleteCaption", |
|
"deleteCell", |
|
"deleteContents", |
|
"deleteData", |
|
"deleteDatabase", |
|
"deleteFramebuffer", |
|
"deleteFromDocument", |
|
"deleteIndex", |
|
"deleteMedium", |
|
"deleteObjectStore", |
|
"deleteProgram", |
|
"deleteProperty", |
|
"deleteQuery", |
|
"deleteRenderbuffer", |
|
"deleteRow", |
|
"deleteRule", |
|
"deleteSampler", |
|
"deleteShader", |
|
"deleteSync", |
|
"deleteTFoot", |
|
"deleteTHead", |
|
"deleteTexture", |
|
"deleteTransformFeedback", |
|
"deleteVertexArray", |
|
"deleted", |
|
"deliverChangeRecords", |
|
"deliveredFrames", |
|
"deliveredFramesDuration", |
|
"delivery", |
|
"deliveryInfo", |
|
"deliveryStatus", |
|
"deliveryTimestamp", |
|
"deliveryType", |
|
"delta", |
|
"deltaMode", |
|
"deltaX", |
|
"deltaY", |
|
"deltaZ", |
|
"dependentLocality", |
|
"deprecatedReplaceInURN", |
|
"deprecatedRunAdAuctionEnforcesKAnonymity", |
|
"deprecatedURNToURL", |
|
"depthActive", |
|
"depthBias", |
|
"depthBiasClamp", |
|
"depthBiasSlopeScale", |
|
"depthClearValue", |
|
"depthCompare", |
|
"depthDataFormat", |
|
"depthFailOp", |
|
"depthFar", |
|
"depthFunc", |
|
"depthLoadOp", |
|
"depthMask", |
|
"depthNear", |
|
"depthOrArrayLayers", |
|
"depthRange", |
|
"depthReadOnly", |
|
"depthStencil", |
|
"depthStencilAttachment", |
|
"depthStencilFormat", |
|
"depthStoreOp", |
|
"depthType", |
|
"depthUsage", |
|
"depthWriteEnabled", |
|
"deref", |
|
"deriveBits", |
|
"deriveKey", |
|
"descentOverride", |
|
"description", |
|
"deselectAll", |
|
"designMode", |
|
"desiredSize", |
|
"destination", |
|
"destinationURL", |
|
"destroy", |
|
"detach", |
|
"detachEvent", |
|
"detachShader", |
|
"detached", |
|
"detail", |
|
"details", |
|
"detect", |
|
"detectLanguage", |
|
"detune", |
|
"device", |
|
"deviceClass", |
|
"deviceId", |
|
"deviceMemory", |
|
"devicePixelContentBoxSize", |
|
"devicePixelRatio", |
|
"devicePosture", |
|
"deviceProtocol", |
|
"deviceSubclass", |
|
"deviceVersionMajor", |
|
"deviceVersionMinor", |
|
"deviceVersionSubminor", |
|
"deviceXDPI", |
|
"deviceYDPI", |
|
"devtools", |
|
"devtools_panels", |
|
"didTimeout", |
|
"difference", |
|
"diffuseConstant", |
|
"digest", |
|
"dimension", |
|
"dimensions", |
|
"dir", |
|
"dirName", |
|
"direction", |
|
"dirxml", |
|
"disable", |
|
"disablePictureInPicture", |
|
"disableRemotePlayback", |
|
"disableVertexAttribArray", |
|
"disabled", |
|
"discard", |
|
"discardedFrames", |
|
"dischargingTime", |
|
"disconnect", |
|
"disconnectShark", |
|
"disconnectedCallback", |
|
"dispatchEvent", |
|
"dispatchWorkgroups", |
|
"dispatchWorkgroupsIndirect", |
|
"display", |
|
"displayHeight", |
|
"displayId", |
|
"displayName", |
|
"displayWidth", |
|
"dispose", |
|
"disposeAsync", |
|
"disposed", |
|
"disposition", |
|
"distanceModel", |
|
"div", |
|
"divisor", |
|
"djsapi", |
|
"djsproxy", |
|
"dns", |
|
"doImport", |
|
"doNotTrack", |
|
"doScroll", |
|
"doctype", |
|
"document", |
|
"documentElement", |
|
"documentId", |
|
"documentIds", |
|
"documentLifecycle", |
|
"documentMode", |
|
"documentOrigin", |
|
"documentOrigins", |
|
"documentPictureInPicture", |
|
"documentURI", |
|
"documentURL", |
|
"documentUrl", |
|
"documentUrls", |
|
"dolphin", |
|
"dolphinGameCenter", |
|
"dolphininfo", |
|
"dolphinmeta", |
|
"dom", |
|
"domComplete", |
|
"domContentLoadedEventEnd", |
|
"domContentLoadedEventStart", |
|
"domInteractive", |
|
"domLoading", |
|
"domOverlayState", |
|
"domain", |
|
"domainLookupEnd", |
|
"domainLookupStart", |
|
"dominant-baseline", |
|
"dominantBaseline", |
|
"done", |
|
"dopplerFactor", |
|
"dotAll", |
|
"downDegrees", |
|
"downlink", |
|
"download", |
|
"downloadRequest", |
|
"downloadTotal", |
|
"downloaded", |
|
"downloads", |
|
"dpcm", |
|
"dpi", |
|
"dppx", |
|
"dragDrop", |
|
"draggable", |
|
"draw", |
|
"drawArrays", |
|
"drawArraysInstanced", |
|
"drawArraysInstancedANGLE", |
|
"drawBuffers", |
|
"drawCustomFocusRing", |
|
"drawElements", |
|
"drawElementsInstanced", |
|
"drawElementsInstancedANGLE", |
|
"drawFocusIfNeeded", |
|
"drawImage", |
|
"drawImageFromRect", |
|
"drawIndexed", |
|
"drawIndexedIndirect", |
|
"drawIndirect", |
|
"drawRangeElements", |
|
"drawSystemFocusRing", |
|
"drawingBufferColorSpace", |
|
"drawingBufferFormat", |
|
"drawingBufferHeight", |
|
"drawingBufferStorage", |
|
"drawingBufferWidth", |
|
"drop", |
|
"dropEffect", |
|
"droppedVideoFrames", |
|
"dropzone", |
|
"dstFactor", |
|
"dtmf", |
|
"dump", |
|
"dumpProfile", |
|
"duplex", |
|
"duplicate", |
|
"durability", |
|
"duration", |
|
"dvb", |
|
"dvh", |
|
"dvi", |
|
"dvmax", |
|
"dvmin", |
|
"dvname", |
|
"dvnum", |
|
"dvw", |
|
"dx", |
|
"dy", |
|
"dynamicId", |
|
"dynsrc", |
|
"e", |
|
"edgeMode", |
|
"editContext", |
|
"effect", |
|
"effectAllowed", |
|
"effectiveDirective", |
|
"effectiveType", |
|
"effects", |
|
"elapsedTime", |
|
"element", |
|
"elementFromPoint", |
|
"elementTiming", |
|
"elements", |
|
"elementsFromPoint", |
|
"elevation", |
|
"ellipse", |
|
"em", |
|
"emHeightAscent", |
|
"emHeightDescent", |
|
"email", |
|
"embeds", |
|
"emit", |
|
"emma", |
|
"empty", |
|
"empty-cells", |
|
"emptyCells", |
|
"emptyHTML", |
|
"emptyScript", |
|
"emulatedPosition", |
|
"enable", |
|
"enableBackground", |
|
"enableDelegations", |
|
"enableStyleSheetsForSet", |
|
"enableVertexAttribArray", |
|
"enabled", |
|
"enabledFeatures", |
|
"enabledPlugin", |
|
"encode", |
|
"encodeInto", |
|
"encodeQueueSize", |
|
"encodeURI", |
|
"encodeURIComponent", |
|
"encodedBodySize", |
|
"encoding", |
|
"encodingInfo", |
|
"encrypt", |
|
"enctype", |
|
"end", |
|
"endContainer", |
|
"endElement", |
|
"endElementAt", |
|
"endOcclusionQuery", |
|
"endOfPassWriteIndex", |
|
"endOfStream", |
|
"endOffset", |
|
"endQuery", |
|
"endTime", |
|
"endTransformFeedback", |
|
"ended", |
|
"endpoint", |
|
"endpointNumber", |
|
"endpoints", |
|
"endsWith", |
|
"enqueue", |
|
"enterKeyHint", |
|
"entities", |
|
"entries", |
|
"entry", |
|
"entryPoint", |
|
"entryType", |
|
"enumerable", |
|
"enumerate", |
|
"enumerateDevices", |
|
"enumerateEditable", |
|
"environmentBlendMode", |
|
"equals", |
|
"era", |
|
"error", |
|
"errorCode", |
|
"errorDetail", |
|
"errorText", |
|
"escape", |
|
"estimate", |
|
"eval", |
|
"evaluate", |
|
"event", |
|
"eventCounts", |
|
"eventPhase", |
|
"events", |
|
"every", |
|
"ex", |
|
"exception", |
|
"exchange", |
|
"exec", |
|
"execCommand", |
|
"execCommandShowHelp", |
|
"execScript", |
|
"executeBundles", |
|
"executionStart", |
|
"exitFullscreen", |
|
"exitPictureInPicture", |
|
"exitPointerLock", |
|
"exitPresent", |
|
"exp", |
|
"expand", |
|
"expandEntityReferences", |
|
"expando", |
|
"expansion", |
|
"expectedContextLanguages", |
|
"expectedImprovement", |
|
"expectedInputLanguages", |
|
"experiments", |
|
"expiration", |
|
"expirationTime", |
|
"expires", |
|
"expiryDate", |
|
"explicitOriginalTarget", |
|
"expm1", |
|
"exponent", |
|
"exponentialRampToValueAtTime", |
|
"exportKey", |
|
"exports", |
|
"extend", |
|
"extension", |
|
"extensionTypes", |
|
"extensions", |
|
"extentNode", |
|
"extentOffset", |
|
"external", |
|
"externalResourcesRequired", |
|
"externalTexture", |
|
"extractContents", |
|
"extractable", |
|
"eye", |
|
"f", |
|
"f16round", |
|
"face", |
|
"factoryReset", |
|
"failOp", |
|
"failureReason", |
|
"fallback", |
|
"family", |
|
"familyName", |
|
"farthestViewportElement", |
|
"fastSeek", |
|
"fatal", |
|
"featureId", |
|
"featurePolicy", |
|
"featureSettings", |
|
"features", |
|
"fence", |
|
"fenceSync", |
|
"fetch", |
|
"fetchLater", |
|
"fetchPriority", |
|
"fetchStart", |
|
"fftSize", |
|
"fgColor", |
|
"fieldOfView", |
|
"file", |
|
"fileCreatedDate", |
|
"fileHandle", |
|
"fileModifiedDate", |
|
"fileName", |
|
"fileSize", |
|
"fileUpdatedDate", |
|
"filename", |
|
"files", |
|
"filesystem", |
|
"fill", |
|
"fill-opacity", |
|
"fill-rule", |
|
"fillJointRadii", |
|
"fillLightMode", |
|
"fillOpacity", |
|
"fillPoses", |
|
"fillRect", |
|
"fillRule", |
|
"fillStyle", |
|
"fillText", |
|
"filter", |
|
"filterResX", |
|
"filterResY", |
|
"filterUnits", |
|
"filters", |
|
"finalResponseHeadersStart", |
|
"finally", |
|
"find", |
|
"findIndex", |
|
"findLast", |
|
"findLastIndex", |
|
"findRule", |
|
"findText", |
|
"finish", |
|
"finishDocumentLoadTime", |
|
"finishLoadTime", |
|
"finished", |
|
"fireEvent", |
|
"firesTouchEvents", |
|
"first", |
|
"firstChild", |
|
"firstElementChild", |
|
"firstInterimResponseStart", |
|
"firstPage", |
|
"firstPaintAfterLoadTime", |
|
"firstPaintTime", |
|
"firstUIEventTimestamp", |
|
"fixed", |
|
"flags", |
|
"flat", |
|
"flatMap", |
|
"flex", |
|
"flex-basis", |
|
"flex-direction", |
|
"flex-flow", |
|
"flex-grow", |
|
"flex-shrink", |
|
"flex-wrap", |
|
"flexBasis", |
|
"flexDirection", |
|
"flexFlow", |
|
"flexGrow", |
|
"flexShrink", |
|
"flexWrap", |
|
"flip", |
|
"flipX", |
|
"flipY", |
|
"float", |
|
"float32", |
|
"float64", |
|
"flood-color", |
|
"flood-opacity", |
|
"floodColor", |
|
"floodOpacity", |
|
"floor", |
|
"flush", |
|
"focus", |
|
"focusNode", |
|
"focusOffset", |
|
"font", |
|
"font-family", |
|
"font-feature-settings", |
|
"font-kerning", |
|
"font-language-override", |
|
"font-optical-sizing", |
|
"font-palette", |
|
"font-size", |
|
"font-size-adjust", |
|
"font-stretch", |
|
"font-style", |
|
"font-synthesis", |
|
"font-synthesis-position", |
|
"font-synthesis-small-caps", |
|
"font-synthesis-style", |
|
"font-synthesis-weight", |
|
"font-variant", |
|
"font-variant-alternates", |
|
"font-variant-caps", |
|
"font-variant-east-asian", |
|
"font-variant-ligatures", |
|
"font-variant-numeric", |
|
"font-variant-position", |
|
"font-variation-settings", |
|
"font-weight", |
|
"fontBoundingBoxAscent", |
|
"fontBoundingBoxDescent", |
|
"fontFamily", |
|
"fontFeatureSettings", |
|
"fontKerning", |
|
"fontLanguageOverride", |
|
"fontOpticalSizing", |
|
"fontPalette", |
|
"fontSize", |
|
"fontSizeAdjust", |
|
"fontSmoothingEnabled", |
|
"fontStretch", |
|
"fontStyle", |
|
"fontSynthesis", |
|
"fontSynthesisPosition", |
|
"fontSynthesisSmallCaps", |
|
"fontSynthesisStyle", |
|
"fontSynthesisWeight", |
|
"fontVariant", |
|
"fontVariantAlternates", |
|
"fontVariantCaps", |
|
"fontVariantEastAsian", |
|
"fontVariantEmoji", |
|
"fontVariantLigatures", |
|
"fontVariantNumeric", |
|
"fontVariantPosition", |
|
"fontVariationSettings", |
|
"fontWeight", |
|
"fontcolor", |
|
"fontfaces", |
|
"fonts", |
|
"fontsize", |
|
"for", |
|
"forEach", |
|
"force", |
|
"forceFallbackAdapter", |
|
"forceRedraw", |
|
"forced-color-adjust", |
|
"forcedColorAdjust", |
|
"forcedStyleAndLayoutDuration", |
|
"forget", |
|
"form", |
|
"formAction", |
|
"formData", |
|
"formEnctype", |
|
"formMethod", |
|
"formNoValidate", |
|
"formTarget", |
|
"format", |
|
"formatToParts", |
|
"forms", |
|
"forward", |
|
"forwardWheel", |
|
"forwardX", |
|
"forwardY", |
|
"forwardZ", |
|
"foundation", |
|
"fr", |
|
"fractionalSecondDigits", |
|
"fragment", |
|
"fragmentDirective", |
|
"frame", |
|
"frameBorder", |
|
"frameCount", |
|
"frameElement", |
|
"frameId", |
|
"frameIds", |
|
"frameSpacing", |
|
"framebuffer", |
|
"framebufferHeight", |
|
"framebufferRenderbuffer", |
|
"framebufferTexture2D", |
|
"framebufferTextureLayer", |
|
"framebufferWidth", |
|
"frames", |
|
"freeSpace", |
|
"freeze", |
|
"frequency", |
|
"frequencyBinCount", |
|
"from", |
|
"fromAsync", |
|
"fromBase64", |
|
"fromCharCode", |
|
"fromCodePoint", |
|
"fromElement", |
|
"fromEntries", |
|
"fromFloat32Array", |
|
"fromFloat64Array", |
|
"fromHex", |
|
"fromMatrix", |
|
"fromPoint", |
|
"fromQuad", |
|
"fromRect", |
|
"frontFace", |
|
"fround", |
|
"fullName", |
|
"fullPath", |
|
"fullRange", |
|
"fullScreen", |
|
"fullVersionList", |
|
"fullscreen", |
|
"fullscreenElement", |
|
"fullscreenEnabled", |
|
"fx", |
|
"fy", |
|
"g", |
|
"gain", |
|
"gamepad", |
|
"gamma", |
|
"gap", |
|
"gatheringState", |
|
"gatt", |
|
"geckoProfiler", |
|
"genderIdentity", |
|
"generateCertificate", |
|
"generateKey", |
|
"generateMipmap", |
|
"generateRequest", |
|
"geolocation", |
|
"gestureObject", |
|
"get", |
|
"getAcceptLanguages", |
|
"getActiveAttrib", |
|
"getActiveUniform", |
|
"getActiveUniformBlockName", |
|
"getActiveUniformBlockParameter", |
|
"getActiveUniforms", |
|
"getAdjacentText", |
|
"getAll", |
|
"getAllKeys", |
|
"getAllRecords", |
|
"getAllResponseHeaders", |
|
"getAllowlistForFeature", |
|
"getAnimations", |
|
"getAsFile", |
|
"getAsFileSystemHandle", |
|
"getAsString", |
|
"getAttachedShaders", |
|
"getAttribLocation", |
|
"getAttribute", |
|
"getAttributeNS", |
|
"getAttributeNames", |
|
"getAttributeNode", |
|
"getAttributeNodeNS", |
|
"getAttributeType", |
|
"getAudioTracks", |
|
"getAuthenticatorData", |
|
"getAutoplayPolicy", |
|
"getAvailability", |
|
"getBBox", |
|
"getBackgroundPage", |
|
"getBadgeBackgroundColor", |
|
"getBadgeText", |
|
"getBadgeTextColor", |
|
"getBattery", |
|
"getBigInt64", |
|
"getBigUint64", |
|
"getBindGroupLayout", |
|
"getBlob", |
|
"getBookmark", |
|
"getBoundingClientRect", |
|
"getBounds", |
|
"getBoxQuads", |
|
"getBrowserInfo", |
|
"getBufferParameter", |
|
"getBufferSubData", |
|
"getByteFrequencyData", |
|
"getByteTimeDomainData", |
|
"getCSSCanvasContext", |
|
"getCTM", |
|
"getCameraImage", |
|
"getCandidateWindowClientRect", |
|
"getCanonicalLocales", |
|
"getCapabilities", |
|
"getCaptureHandle", |
|
"getChannelData", |
|
"getCharNumAtPosition", |
|
"getCharacteristic", |
|
"getCharacteristics", |
|
"getClientCapabilities", |
|
"getClientExtensionResults", |
|
"getClientRect", |
|
"getClientRects", |
|
"getCoalescedEvents", |
|
"getCompilationInfo", |
|
"getComposedRanges", |
|
"getCompositionAlternatives", |
|
"getComputedStyle", |
|
"getComputedTextLength", |
|
"getComputedTiming", |
|
"getConfiguration", |
|
"getConstraints", |
|
"getContext", |
|
"getContextAttributes", |
|
"getContexts", |
|
"getContributingSources", |
|
"getCounterValue", |
|
"getCueAsHTML", |
|
"getCueById", |
|
"getCurrent", |
|
"getCurrentPosition", |
|
"getCurrentTexture", |
|
"getCurrentTime", |
|
"getData", |
|
"getDatabaseNames", |
|
"getDate", |
|
"getDay", |
|
"getDefaultComputedStyle", |
|
"getDepthInMeters", |
|
"getDepthInformation", |
|
"getDescriptor", |
|
"getDescriptors", |
|
"getDestinationInsertionPoints", |
|
"getDevices", |
|
"getDirectory", |
|
"getDirectoryHandle", |
|
"getDisplayMedia", |
|
"getDistributedNodes", |
|
"getEditable", |
|
"getElementById", |
|
"getElementsByClassName", |
|
"getElementsByName", |
|
"getElementsByTagName", |
|
"getElementsByTagNameNS", |
|
"getEnclosureList", |
|
"getEndPositionOfChar", |
|
"getEntries", |
|
"getEntriesByName", |
|
"getEntriesByType", |
|
"getError", |
|
"getExtension", |
|
"getExtentOfChar", |
|
"getEyeParameters", |
|
"getFeature", |
|
"getFiberRoots", |
|
"getFile", |
|
"getFileHandle", |
|
"getFiles", |
|
"getFilesAndDirectories", |
|
"getFingerprints", |
|
"getFloat16", |
|
"getFloat32", |
|
"getFloat64", |
|
"getFloatFrequencyData", |
|
"getFloatTimeDomainData", |
|
"getFloatValue", |
|
"getFragDataLocation", |
|
"getFrameData", |
|
"getFrameId", |
|
"getFramebufferAttachmentParameter", |
|
"getFrequencyResponse", |
|
"getFullYear", |
|
"getGamepads", |
|
"getHTML", |
|
"getHeaderExtensionsToNegotiate", |
|
"getHighEntropyValues", |
|
"getHitTestResults", |
|
"getHitTestResultsForTransientInput", |
|
"getHours", |
|
"getIdentityAssertion", |
|
"getIds", |
|
"getImageData", |
|
"getIndexedParameter", |
|
"getInfo", |
|
"getInnerHTML", |
|
"getInstalledRelatedApps", |
|
"getInt16", |
|
"getInt32", |
|
"getInt8", |
|
"getInterestGroupAdAuctionData", |
|
"getInternalModuleRanges", |
|
"getInternalformatParameter", |
|
"getIntersectionList", |
|
"getItem", |
|
"getItems", |
|
"getJointPose", |
|
"getKey", |
|
"getKeyframes", |
|
"getLastFocused", |
|
"getLayers", |
|
"getLayoutMap", |
|
"getLightEstimate", |
|
"getLineDash", |
|
"getLocalCandidates", |
|
"getLocalParameters", |
|
"getLocalStreams", |
|
"getManagedConfiguration", |
|
"getManifest", |
|
"getMappedRange", |
|
"getMarks", |
|
"getMatchedCSSRules", |
|
"getMaxGCPauseSinceClear", |
|
"getMeasures", |
|
"getMessage", |
|
"getMetadata", |
|
"getMilliseconds", |
|
"getMinutes", |
|
"getModifierState", |
|
"getMonth", |
|
"getName", |
|
"getNamedItem", |
|
"getNamedItemNS", |
|
"getNativeFramebufferScaleFactor", |
|
"getNegotiatedHeaderExtensions", |
|
"getNestedConfigs", |
|
"getNotifications", |
|
"getNotifier", |
|
"getNumberOfChars", |
|
"getOffsetReferenceSpace", |
|
"getOrInsert", |
|
"getOrInsertComputed", |
|
"getOutputTimestamp", |
|
"getOverrideHistoryNavigationMode", |
|
"getOverrideStyle", |
|
"getOwnPropertyDescriptor", |
|
"getOwnPropertyDescriptors", |
|
"getOwnPropertyNames", |
|
"getOwnPropertySymbols", |
|
"getPackageDirectoryEntry", |
|
"getParameter", |
|
"getParameters", |
|
"getParent", |
|
"getPathData", |
|
"getPathSegAtLength", |
|
"getPathSegmentAtLength", |
|
"getPermissionWarningsByManifest", |
|
"getPhotoCapabilities", |
|
"getPhotoSettings", |
|
"getPlatformInfo", |
|
"getPointAtLength", |
|
"getPopup", |
|
"getPorts", |
|
"getPose", |
|
"getPredictedEvents", |
|
"getPreference", |
|
"getPreferenceDefault", |
|
"getPreferredCanvasFormat", |
|
"getPresentationAttribute", |
|
"getPreventDefault", |
|
"getPrimaryService", |
|
"getPrimaryServices", |
|
"getProgramInfoLog", |
|
"getProgramParameter", |
|
"getPropertyCSSValue", |
|
"getPropertyPriority", |
|
"getPropertyShorthand", |
|
"getPropertyType", |
|
"getPropertyValue", |
|
"getPrototypeOf", |
|
"getPublicKey", |
|
"getPublicKeyAlgorithm", |
|
"getQuery", |
|
"getQueryParameter", |
|
"getRGBColorValue", |
|
"getRandomValues", |
|
"getRangeAt", |
|
"getReader", |
|
"getReceivers", |
|
"getRectValue", |
|
"getReflectionCubeMap", |
|
"getRegistration", |
|
"getRegistrations", |
|
"getRemoteCandidates", |
|
"getRemoteCertificates", |
|
"getRemoteParameters", |
|
"getRemoteStreams", |
|
"getRenderbufferParameter", |
|
"getResponseHeader", |
|
"getRoot", |
|
"getRootNode", |
|
"getRotationOfChar", |
|
"getSVGDocument", |
|
"getSamplerParameter", |
|
"getScreenCTM", |
|
"getScreenDetails", |
|
"getSeconds", |
|
"getSelectedCandidatePair", |
|
"getSelection", |
|
"getSelf", |
|
"getSenders", |
|
"getService", |
|
"getSetCookie", |
|
"getSettings", |
|
"getShaderInfoLog", |
|
"getShaderParameter", |
|
"getShaderPrecisionFormat", |
|
"getShaderSource", |
|
"getSignals", |
|
"getSimpleDuration", |
|
"getSiteIcons", |
|
"getSources", |
|
"getSpeculativeParserUrls", |
|
"getStartPositionOfChar", |
|
"getStartTime", |
|
"getState", |
|
"getStats", |
|
"getStatusForPolicy", |
|
"getStorageUpdates", |
|
"getStreamById", |
|
"getStringValue", |
|
"getSubStringLength", |
|
"getSubscription", |
|
"getSubscriptions", |
|
"getSupportedConstraints", |
|
"getSupportedExtensions", |
|
"getSupportedFormats", |
|
"getSupportedZoomLevels", |
|
"getSyncParameter", |
|
"getSynchronizationSources", |
|
"getTags", |
|
"getTargetRanges", |
|
"getTexParameter", |
|
"getTextFormats", |
|
"getTime", |
|
"getTimezoneOffset", |
|
"getTiming", |
|
"getTitle", |
|
"getTitlebarAreaRect", |
|
"getTotalLength", |
|
"getTrackById", |
|
"getTracks", |
|
"getTransceivers", |
|
"getTransform", |
|
"getTransformFeedbackVarying", |
|
"getTransformToElement", |
|
"getTransports", |
|
"getType", |
|
"getTypeMapping", |
|
"getUILanguage", |
|
"getURL", |
|
"getUTCDate", |
|
"getUTCDay", |
|
"getUTCFullYear", |
|
"getUTCHours", |
|
"getUTCMilliseconds", |
|
"getUTCMinutes", |
|
"getUTCMonth", |
|
"getUTCSeconds", |
|
"getUint16", |
|
"getUint32", |
|
"getUint8", |
|
"getUniform", |
|
"getUniformBlockIndex", |
|
"getUniformIndices", |
|
"getUniformLocation", |
|
"getUserInfo", |
|
"getUserMedia", |
|
"getUserSettings", |
|
"getVRDisplays", |
|
"getValues", |
|
"getVarDate", |
|
"getVariableValue", |
|
"getVertexAttrib", |
|
"getVertexAttribOffset", |
|
"getVideoPlaybackQuality", |
|
"getVideoTracks", |
|
"getViewerPose", |
|
"getViewport", |
|
"getViews", |
|
"getVoices", |
|
"getWakeLockState", |
|
"getWriter", |
|
"getYear", |
|
"getZoom", |
|
"getZoomSettings", |
|
"givenName", |
|
"global", |
|
"globalAlpha", |
|
"globalCompositeOperation", |
|
"globalPrivacyControl", |
|
"globalThis", |
|
"glyphOrientationHorizontal", |
|
"glyphOrientationVertical", |
|
"glyphRef", |
|
"go", |
|
"goBack", |
|
"goForward", |
|
"gpu", |
|
"grabFrame", |
|
"grad", |
|
"gradientTransform", |
|
"gradientUnits", |
|
"grammars", |
|
"green", |
|
"grid", |
|
"grid-area", |
|
"grid-auto-columns", |
|
"grid-auto-flow", |
|
"grid-auto-rows", |
|
"grid-column", |
|
"grid-column-end", |
|
"grid-column-gap", |
|
"grid-column-start", |
|
"grid-gap", |
|
"grid-row", |
|
"grid-row-end", |
|
"grid-row-gap", |
|
"grid-row-start", |
|
"grid-template", |
|
"grid-template-areas", |
|
"grid-template-columns", |
|
"grid-template-rows", |
|
"gridArea", |
|
"gridAutoColumns", |
|
"gridAutoFlow", |
|
"gridAutoRows", |
|
"gridColumn", |
|
"gridColumnEnd", |
|
"gridColumnGap", |
|
"gridColumnStart", |
|
"gridGap", |
|
"gridRow", |
|
"gridRowEnd", |
|
"gridRowGap", |
|
"gridRowStart", |
|
"gridTemplate", |
|
"gridTemplateAreas", |
|
"gridTemplateColumns", |
|
"gridTemplateRows", |
|
"gripSpace", |
|
"group", |
|
"groupBy", |
|
"groupCollapsed", |
|
"groupEnd", |
|
"groupId", |
|
"groups", |
|
"grow", |
|
"growable", |
|
"guestProcessId", |
|
"guestRenderFrameRoutingId", |
|
"hadRecentInput", |
|
"hand", |
|
"handedness", |
|
"hangingBaseline", |
|
"hapticActuators", |
|
"hardwareConcurrency", |
|
"has", |
|
"hasAttribute", |
|
"hasAttributeNS", |
|
"hasAttributes", |
|
"hasBeenActive", |
|
"hasChildNodes", |
|
"hasComposition", |
|
"hasDynamicOffset", |
|
"hasEnrolledInstrument", |
|
"hasExtension", |
|
"hasExternalDisplay", |
|
"hasFeature", |
|
"hasFocus", |
|
"hasIndices", |
|
"hasInstance", |
|
"hasLayout", |
|
"hasOrientation", |
|
"hasOwn", |
|
"hasOwnProperty", |
|
"hasPointerCapture", |
|
"hasPosition", |
|
"hasPrivateToken", |
|
"hasReading", |
|
"hasRedemptionRecord", |
|
"hasRegExpGroups", |
|
"hasStorageAccess", |
|
"hasUAVisualTransition", |
|
"hasUnpartitionedCookieAccess", |
|
"hash", |
|
"hashChange", |
|
"head", |
|
"headers", |
|
"heading", |
|
"height", |
|
"hid", |
|
"hidden", |
|
"hide", |
|
"hideFocus", |
|
"hidePopover", |
|
"high", |
|
"highWaterMark", |
|
"highlight", |
|
"highlights", |
|
"highlightsFromPoint", |
|
"hint", |
|
"hints", |
|
"history", |
|
"honorificPrefix", |
|
"honorificSuffix", |
|
"horizontalOverflow", |
|
"host", |
|
"hostCandidate", |
|
"hostname", |
|
"hour", |
|
"hour12", |
|
"hourCycle", |
|
"hours", |
|
"href", |
|
"hrefTranslate", |
|
"hreflang", |
|
"hspace", |
|
"html5TagCheckInerface", |
|
"htmlFor", |
|
"htmlText", |
|
"httpEquiv", |
|
"httpRequestStatusCode", |
|
"hwTimestamp", |
|
"hyphenate-character", |
|
"hyphenateCharacter", |
|
"hyphenateLimitChars", |
|
"hyphens", |
|
"hypot", |
|
"i18n", |
|
"ic", |
|
"iccId", |
|
"iceConnectionState", |
|
"iceGatheringState", |
|
"iceTransport", |
|
"icon", |
|
"iconURL", |
|
"id", |
|
"identifier", |
|
"identity", |
|
"ideographicBaseline", |
|
"idle", |
|
"idpLoginUrl", |
|
"ignoreBOM", |
|
"ignoreCase", |
|
"ignoreDepthValues", |
|
"image", |
|
"image-orientation", |
|
"image-rendering", |
|
"imageHeight", |
|
"imageOrientation", |
|
"imageRendering", |
|
"imageSizes", |
|
"imageSmoothingEnabled", |
|
"imageSmoothingQuality", |
|
"imageSrcset", |
|
"imageWidth", |
|
"images", |
|
"ime-mode", |
|
"imeMode", |
|
"implementation", |
|
"importExternalTexture", |
|
"importKey", |
|
"importNode", |
|
"importStylesheet", |
|
"imports", |
|
"impp", |
|
"imul", |
|
"in", |
|
"in1", |
|
"in2", |
|
"inBandMetadataTrackDispatchType", |
|
"inIncognitoContext", |
|
"inRange", |
|
"includes", |
|
"incognito", |
|
"incomingBidirectionalStreams", |
|
"incomingHighWaterMark", |
|
"incomingMaxAge", |
|
"incomingUnidirectionalStreams", |
|
"increaseZoomLevel", |
|
"incremental", |
|
"indeterminate", |
|
"index", |
|
"indexNames", |
|
"indexOf", |
|
"indexedDB", |
|
"indicate", |
|
"indices", |
|
"inert", |
|
"inertiaDestinationX", |
|
"inertiaDestinationY", |
|
"info", |
|
"inherits", |
|
"init", |
|
"initAnimationEvent", |
|
"initBeforeLoadEvent", |
|
"initClipboardEvent", |
|
"initCloseEvent", |
|
"initCommandEvent", |
|
"initCompositionEvent", |
|
"initCustomEvent", |
|
"initData", |
|
"initDataType", |
|
"initDeviceMotionEvent", |
|
"initDeviceOrientationEvent", |
|
"initDragEvent", |
|
"initErrorEvent", |
|
"initEvent", |
|
"initFocusEvent", |
|
"initGestureEvent", |
|
"initHashChangeEvent", |
|
"initKeyEvent", |
|
"initKeyboardEvent", |
|
"initMSManipulationEvent", |
|
"initMessageEvent", |
|
"initMouseEvent", |
|
"initMouseScrollEvent", |
|
"initMouseWheelEvent", |
|
"initMutationEvent", |
|
"initNSMouseEvent", |
|
"initOverflowEvent", |
|
"initPageEvent", |
|
"initPageTransitionEvent", |
|
"initPointerEvent", |
|
"initPopStateEvent", |
|
"initProgressEvent", |
|
"initScrollAreaEvent", |
|
"initSimpleGestureEvent", |
|
"initStorageEvent", |
|
"initTextEvent", |
|
"initTimeEvent", |
|
"initTouchEvent", |
|
"initTransitionEvent", |
|
"initUIEvent", |
|
"initWebKitAnimationEvent", |
|
"initWebKitTransitionEvent", |
|
"initWebKitWheelEvent", |
|
"initWheelEvent", |
|
"initialTime", |
|
"initialValue", |
|
"initialize", |
|
"initiatorType", |
|
"inject", |
|
"ink", |
|
"inline-size", |
|
"inlineSize", |
|
"inlineVerticalFieldOfView", |
|
"inner", |
|
"innerHTML", |
|
"innerHeight", |
|
"innerText", |
|
"innerWidth", |
|
"input", |
|
"inputBuffer", |
|
"inputEncoding", |
|
"inputMethod", |
|
"inputMode", |
|
"inputQuota", |
|
"inputSource", |
|
"inputSources", |
|
"inputType", |
|
"inputs", |
|
"insertAdjacentElement", |
|
"insertAdjacentHTML", |
|
"insertAdjacentText", |
|
"insertBefore", |
|
"insertCell", |
|
"insertDTMF", |
|
"insertData", |
|
"insertDebugMarker", |
|
"insertItemBefore", |
|
"insertNode", |
|
"insertRow", |
|
"insertRule", |
|
"inset", |
|
"inset-block", |
|
"inset-block-end", |
|
"inset-block-start", |
|
"inset-inline", |
|
"inset-inline-end", |
|
"inset-inline-start", |
|
"insetBlock", |
|
"insetBlockEnd", |
|
"insetBlockStart", |
|
"insetInline", |
|
"insetInlineEnd", |
|
"insetInlineStart", |
|
"inspect", |
|
"install", |
|
"installing", |
|
"instanceRoot", |
|
"instantiate", |
|
"instantiateStreaming", |
|
"instruments", |
|
"int16", |
|
"int32", |
|
"int8", |
|
"integrity", |
|
"interactionCount", |
|
"interactionId", |
|
"interactionMode", |
|
"intercept", |
|
"interestForElement", |
|
"interfaceClass", |
|
"interfaceName", |
|
"interfaceNumber", |
|
"interfaceProtocol", |
|
"interfaceSubclass", |
|
"interfaces", |
|
"interimResults", |
|
"internalSubset", |
|
"interpretation", |
|
"intersection", |
|
"intersectionRatio", |
|
"intersectionRect", |
|
"intersectsNode", |
|
"interval", |
|
"invalidIteratorState", |
|
"invalidateFramebuffer", |
|
"invalidateSubFramebuffer", |
|
"inverse", |
|
"invertSelf", |
|
"invoker", |
|
"invokerType", |
|
"is", |
|
"is2D", |
|
"isActive", |
|
"isAllowedFileSchemeAccess", |
|
"isAllowedIncognitoAccess", |
|
"isAlternate", |
|
"isArray", |
|
"isAutoSelected", |
|
"isBingCurrentSearchDefault", |
|
"isBuffer", |
|
"isCandidateWindowVisible", |
|
"isChar", |
|
"isCollapsed", |
|
"isComposing", |
|
"isConcatSpreadable", |
|
"isConditionalMediationAvailable", |
|
"isConfigSupported", |
|
"isConnected", |
|
"isContentEditable", |
|
"isContentHandlerRegistered", |
|
"isContextLost", |
|
"isDefaultNamespace", |
|
"isDirectory", |
|
"isDisabled", |
|
"isDisjointFrom", |
|
"isEnabled", |
|
"isEqual", |
|
"isEqualNode", |
|
"isError", |
|
"isExtended", |
|
"isExtensible", |
|
"isExternalCTAP2SecurityKeySupported", |
|
"isFallbackAdapter", |
|
"isFile", |
|
"isFinite", |
|
"isFirstPersonObserver", |
|
"isFramebuffer", |
|
"isFrozen", |
|
"isGenerator", |
|
"isHTML", |
|
"isHistoryNavigation", |
|
"isId", |
|
"isIdentity", |
|
"isInjected", |
|
"isInputPending", |
|
"isInteger", |
|
"isInternal", |
|
"isIntersecting", |
|
"isLockFree", |
|
"isMap", |
|
"isMultiLine", |
|
"isNaN", |
|
"isOpen", |
|
"isPointInFill", |
|
"isPointInPath", |
|
"isPointInRange", |
|
"isPointInStroke", |
|
"isPrefAlternate", |
|
"isPresenting", |
|
"isPrimary", |
|
"isProgram", |
|
"isPropertyImplicit", |
|
"isProtocolHandlerRegistered", |
|
"isPrototypeOf", |
|
"isQuery", |
|
"isRawJSON", |
|
"isRenderbuffer", |
|
"isSafeInteger", |
|
"isSameEntry", |
|
"isSameNode", |
|
"isSampler", |
|
"isScript", |
|
"isScriptURL", |
|
"isSealed", |
|
"isSecureContext", |
|
"isSessionSupported", |
|
"isShader", |
|
"isSubsetOf", |
|
"isSupersetOf", |
|
"isSupported", |
|
"isSync", |
|
"isTextEdit", |
|
"isTexture", |
|
"isTransformFeedback", |
|
"isTrusted", |
|
"isTypeSupported", |
|
"isUserVerifyingPlatformAuthenticatorAvailable", |
|
"isVertexArray", |
|
"isView", |
|
"isVisible", |
|
"isWellFormed", |
|
"isochronousTransferIn", |
|
"isochronousTransferOut", |
|
"isolation", |
|
"italics", |
|
"item", |
|
"itemId", |
|
"itemProp", |
|
"itemRef", |
|
"itemScope", |
|
"itemType", |
|
"itemValue", |
|
"items", |
|
"iterateNext", |
|
"iterationComposite", |
|
"iterator", |
|
"javaEnabled", |
|
"jitterBufferTarget", |
|
"jobTitle", |
|
"join", |
|
"joinAdInterestGroup", |
|
"jointName", |
|
"json", |
|
"justify-content", |
|
"justify-items", |
|
"justify-self", |
|
"justifyContent", |
|
"justifyItems", |
|
"justifySelf", |
|
"k1", |
|
"k2", |
|
"k3", |
|
"k4", |
|
"kHz", |
|
"keepalive", |
|
"kernelMatrix", |
|
"kernelUnitLengthX", |
|
"kernelUnitLengthY", |
|
"kerning", |
|
"key", |
|
"keyCode", |
|
"keyFor", |
|
"keyIdentifier", |
|
"keyLightEnabled", |
|
"keyLocation", |
|
"keyPath", |
|
"keyStatuses", |
|
"keySystem", |
|
"keyText", |
|
"keyUsage", |
|
"keyboard", |
|
"keys", |
|
"keytype", |
|
"kind", |
|
"knee", |
|
"knownSources", |
|
"label", |
|
"labels", |
|
"lang", |
|
"language", |
|
"languages", |
|
"largeArcFlag", |
|
"last", |
|
"lastChild", |
|
"lastElementChild", |
|
"lastError", |
|
"lastEventId", |
|
"lastIndex", |
|
"lastIndexOf", |
|
"lastInputTime", |
|
"lastMatch", |
|
"lastMessageSubject", |
|
"lastMessageType", |
|
"lastModified", |
|
"lastModifiedDate", |
|
"lastPage", |
|
"lastParen", |
|
"lastState", |
|
"lastStyleSheetSet", |
|
"latency", |
|
"latitude", |
|
"launchQueue", |
|
"layerName", |
|
"layerX", |
|
"layerY", |
|
"layout", |
|
"layoutFlow", |
|
"layoutGrid", |
|
"layoutGridChar", |
|
"layoutGridLine", |
|
"layoutGridMode", |
|
"layoutGridType", |
|
"lbound", |
|
"leaveAdInterestGroup", |
|
"left", |
|
"leftContext", |
|
"leftDegrees", |
|
"leftMargin", |
|
"leftProjectionMatrix", |
|
"leftViewMatrix", |
|
"length", |
|
"lengthAdjust", |
|
"lengthComputable", |
|
"letter-spacing", |
|
"letterSpacing", |
|
"level", |
|
"lh", |
|
"lighting-color", |
|
"lightingColor", |
|
"limitingConeAngle", |
|
"limits", |
|
"line", |
|
"line-break", |
|
"line-height", |
|
"lineAlign", |
|
"lineBreak", |
|
"lineCap", |
|
"lineDashOffset", |
|
"lineGapOverride", |
|
"lineHeight", |
|
"lineJoin", |
|
"lineNum", |
|
"lineNumber", |
|
"linePos", |
|
"lineTo", |
|
"lineWidth", |
|
"linearAcceleration", |
|
"linearRampToValueAtTime", |
|
"linearVelocity", |
|
"lineno", |
|
"lines", |
|
"link", |
|
"linkColor", |
|
"linkProgram", |
|
"links", |
|
"list", |
|
"list-style", |
|
"list-style-image", |
|
"list-style-position", |
|
"list-style-type", |
|
"listStyle", |
|
"listStyleImage", |
|
"listStylePosition", |
|
"listStyleType", |
|
"listener", |
|
"listeners", |
|
"load", |
|
"loadEventEnd", |
|
"loadEventStart", |
|
"loadOp", |
|
"loadTime", |
|
"loadTimes", |
|
"loaded", |
|
"loading", |
|
"localDescription", |
|
"localName", |
|
"localService", |
|
"localStorage", |
|
"locale", |
|
"localeCompare", |
|
"location", |
|
"locationbar", |
|
"lock", |
|
"locked", |
|
"lockedFile", |
|
"locks", |
|
"lodMaxClamp", |
|
"lodMinClamp", |
|
"log", |
|
"log10", |
|
"log1p", |
|
"log2", |
|
"logicalXDPI", |
|
"logicalYDPI", |
|
"login", |
|
"loglevel", |
|
"longDesc", |
|
"longitude", |
|
"lookupNamespaceURI", |
|
"lookupPrefix", |
|
"loop", |
|
"loopEnd", |
|
"loopStart", |
|
"looping", |
|
"lost", |
|
"low", |
|
"lower", |
|
"lowerBound", |
|
"lowerOpen", |
|
"lowsrc", |
|
"lvb", |
|
"lvh", |
|
"lvi", |
|
"lvmax", |
|
"lvmin", |
|
"lvw", |
|
"m11", |
|
"m12", |
|
"m13", |
|
"m14", |
|
"m21", |
|
"m22", |
|
"m23", |
|
"m24", |
|
"m31", |
|
"m32", |
|
"m33", |
|
"m34", |
|
"m41", |
|
"m42", |
|
"m43", |
|
"m44", |
|
"magFilter", |
|
"makeXRCompatible", |
|
"managed", |
|
"management", |
|
"manifest", |
|
"manufacturer", |
|
"manufacturerName", |
|
"map", |
|
"mapAsync", |
|
"mapState", |
|
"mappedAtCreation", |
|
"mapping", |
|
"margin", |
|
"margin-block", |
|
"margin-block-end", |
|
"margin-block-start", |
|
"margin-bottom", |
|
"margin-inline", |
|
"margin-inline-end", |
|
"margin-inline-start", |
|
"margin-left", |
|
"margin-right", |
|
"margin-top", |
|
"marginBlock", |
|
"marginBlockEnd", |
|
"marginBlockStart", |
|
"marginBottom", |
|
"marginHeight", |
|
"marginInline", |
|
"marginInlineEnd", |
|
"marginInlineStart", |
|
"marginLeft", |
|
"marginRight", |
|
"marginTop", |
|
"marginWidth", |
|
"mark", |
|
"marker", |
|
"marker-end", |
|
"marker-mid", |
|
"marker-offset", |
|
"marker-start", |
|
"markerEnd", |
|
"markerHeight", |
|
"markerMid", |
|
"markerOffset", |
|
"markerStart", |
|
"markerUnits", |
|
"markerWidth", |
|
"marks", |
|
"mask", |
|
"mask-clip", |
|
"mask-composite", |
|
"mask-image", |
|
"mask-mode", |
|
"mask-origin", |
|
"mask-position", |
|
"mask-position-x", |
|
"mask-position-y", |
|
"mask-repeat", |
|
"mask-size", |
|
"mask-type", |
|
"maskClip", |
|
"maskComposite", |
|
"maskContentUnits", |
|
"maskImage", |
|
"maskMode", |
|
"maskOrigin", |
|
"maskPosition", |
|
"maskPositionX", |
|
"maskPositionY", |
|
"maskRepeat", |
|
"maskSize", |
|
"maskType", |
|
"maskUnits", |
|
"match", |
|
"matchAll", |
|
"matchMedia", |
|
"matchMedium", |
|
"matchPatterns", |
|
"matches", |
|
"math-depth", |
|
"math-style", |
|
"mathDepth", |
|
"mathShift", |
|
"mathStyle", |
|
"matrix", |
|
"matrixTransform", |
|
"max", |
|
"max-block-size", |
|
"max-height", |
|
"max-inline-size", |
|
"max-width", |
|
"maxActions", |
|
"maxAlternatives", |
|
"maxAnisotropy", |
|
"maxBindGroups", |
|
"maxBindGroupsPlusVertexBuffers", |
|
"maxBindingsPerBindGroup", |
|
"maxBlockSize", |
|
"maxBufferSize", |
|
"maxByteLength", |
|
"maxChannelCount", |
|
"maxChannels", |
|
"maxColorAttachmentBytesPerSample", |
|
"maxColorAttachments", |
|
"maxComputeInvocationsPerWorkgroup", |
|
"maxComputeWorkgroupSizeX", |
|
"maxComputeWorkgroupSizeY", |
|
"maxComputeWorkgroupSizeZ", |
|
"maxComputeWorkgroupStorageSize", |
|
"maxComputeWorkgroupsPerDimension", |
|
"maxConnectionsPerServer", |
|
"maxDatagramSize", |
|
"maxDecibels", |
|
"maxDistance", |
|
"maxDrawCount", |
|
"maxDynamicStorageBuffersPerPipelineLayout", |
|
"maxDynamicUniformBuffersPerPipelineLayout", |
|
"maxHeight", |
|
"maxInlineSize", |
|
"maxInterStageShaderComponents", |
|
"maxInterStageShaderVariables", |
|
"maxLayers", |
|
"maxLength", |
|
"maxMessageSize", |
|
"maxPacketLifeTime", |
|
"maxRetransmits", |
|
"maxSampledTexturesPerShaderStage", |
|
"maxSamplersPerShaderStage", |
|
"maxStorageBufferBindingSize", |
|
"maxStorageBuffersPerShaderStage", |
|
"maxStorageTexturesPerShaderStage", |
|
"maxTextureArrayLayers", |
|
"maxTextureDimension1D", |
|
"maxTextureDimension2D", |
|
"maxTextureDimension3D", |
|
"maxTouchPoints", |
|
"maxUniformBufferBindingSize", |
|
"maxUniformBuffersPerShaderStage", |
|
"maxValue", |
|
"maxVertexAttributes", |
|
"maxVertexBufferArrayStride", |
|
"maxVertexBuffers", |
|
"maxWidth", |
|
"maximumLatency", |
|
"measure", |
|
"measureInputUsage", |
|
"measureText", |
|
"media", |
|
"mediaCapabilities", |
|
"mediaDevices", |
|
"mediaElement", |
|
"mediaGroup", |
|
"mediaKeys", |
|
"mediaSession", |
|
"mediaStream", |
|
"mediaText", |
|
"meetOrSlice", |
|
"memory", |
|
"menubar", |
|
"menus", |
|
"menusChild", |
|
"menusInternal", |
|
"mergeAttributes", |
|
"message", |
|
"messageClass", |
|
"messageHandlers", |
|
"messageType", |
|
"messages", |
|
"metaKey", |
|
"metadata", |
|
"method", |
|
"methodDetails", |
|
"methodName", |
|
"microseconds", |
|
"mid", |
|
"milliseconds", |
|
"mimeType", |
|
"mimeTypes", |
|
"min", |
|
"min-block-size", |
|
"min-height", |
|
"min-inline-size", |
|
"min-width", |
|
"minBindingSize", |
|
"minBlockSize", |
|
"minDecibels", |
|
"minFilter", |
|
"minHeight", |
|
"minInlineSize", |
|
"minLength", |
|
"minStorageBufferOffsetAlignment", |
|
"minUniformBufferOffsetAlignment", |
|
"minValue", |
|
"minWidth", |
|
"minimumLatency", |
|
"minute", |
|
"minutes", |
|
"mipLevel", |
|
"mipLevelCount", |
|
"mipmapFilter", |
|
"miterLimit", |
|
"mix-blend-mode", |
|
"mixBlendMode", |
|
"mm", |
|
"mobile", |
|
"mode", |
|
"model", |
|
"modify", |
|
"module", |
|
"month", |
|
"months", |
|
"mount", |
|
"move", |
|
"moveBefore", |
|
"moveBy", |
|
"moveEnd", |
|
"moveFirst", |
|
"moveFocusDown", |
|
"moveFocusLeft", |
|
"moveFocusRight", |
|
"moveFocusUp", |
|
"moveInSuccession", |
|
"moveNext", |
|
"moveRow", |
|
"moveStart", |
|
"moveTo", |
|
"moveToBookmark", |
|
"moveToElementText", |
|
"moveToPoint", |
|
"movementX", |
|
"movementY", |
|
"mozAdd", |
|
"mozAnimationStartTime", |
|
"mozAnon", |
|
"mozApps", |
|
"mozAudioCaptured", |
|
"mozAudioChannelType", |
|
"mozAutoplayEnabled", |
|
"mozCancelAnimationFrame", |
|
"mozCancelFullScreen", |
|
"mozCancelRequestAnimationFrame", |
|
"mozCaptureStream", |
|
"mozCaptureStreamUntilEnded", |
|
"mozClearDataAt", |
|
"mozContact", |
|
"mozContacts", |
|
"mozCreateFileHandle", |
|
"mozCurrentTransform", |
|
"mozCurrentTransformInverse", |
|
"mozCursor", |
|
"mozDash", |
|
"mozDashOffset", |
|
"mozDecodedFrames", |
|
"mozExitPointerLock", |
|
"mozFillRule", |
|
"mozFragmentEnd", |
|
"mozFrameDelay", |
|
"mozFullScreen", |
|
"mozFullScreenElement", |
|
"mozFullScreenEnabled", |
|
"mozGetAll", |
|
"mozGetAllKeys", |
|
"mozGetAsFile", |
|
"mozGetDataAt", |
|
"mozGetMetadata", |
|
"mozGetUserMedia", |
|
"mozHasAudio", |
|
"mozHasItem", |
|
"mozHidden", |
|
"mozImageSmoothingEnabled", |
|
"mozIndexedDB", |
|
"mozInnerScreenX", |
|
"mozInnerScreenY", |
|
"mozInputSource", |
|
"mozIsTextField", |
|
"mozItem", |
|
"mozItemCount", |
|
"mozItems", |
|
"mozLength", |
|
"mozLockOrientation", |
|
"mozMatchesSelector", |
|
"mozMovementX", |
|
"mozMovementY", |
|
"mozOpaque", |
|
"mozOrientation", |
|
"mozPaintCount", |
|
"mozPaintedFrames", |
|
"mozParsedFrames", |
|
"mozPay", |
|
"mozPointerLockElement", |
|
"mozPresentedFrames", |
|
"mozPreservesPitch", |
|
"mozPressure", |
|
"mozPrintCallback", |
|
"mozRTCIceCandidate", |
|
"mozRTCPeerConnection", |
|
"mozRTCSessionDescription", |
|
"mozRemove", |
|
"mozRequestAnimationFrame", |
|
"mozRequestFullScreen", |
|
"mozRequestPointerLock", |
|
"mozSetDataAt", |
|
"mozSetImageElement", |
|
"mozSourceNode", |
|
"mozSrcObject", |
|
"mozSystem", |
|
"mozTCPSocket", |
|
"mozTextStyle", |
|
"mozTypesAt", |
|
"mozUnlockOrientation", |
|
"mozUserCancelled", |
|
"mozVisibilityState", |
|
"ms", |
|
"msAnimation", |
|
"msAnimationDelay", |
|
"msAnimationDirection", |
|
"msAnimationDuration", |
|
"msAnimationFillMode", |
|
"msAnimationIterationCount", |
|
"msAnimationName", |
|
"msAnimationPlayState", |
|
"msAnimationStartTime", |
|
"msAnimationTimingFunction", |
|
"msBackfaceVisibility", |
|
"msBlockProgression", |
|
"msCSSOMElementFloatMetrics", |
|
"msCaching", |
|
"msCachingEnabled", |
|
"msCancelRequestAnimationFrame", |
|
"msCapsLockWarningOff", |
|
"msClearImmediate", |
|
"msClose", |
|
"msContentZoomChaining", |
|
"msContentZoomFactor", |
|
"msContentZoomLimit", |
|
"msContentZoomLimitMax", |
|
"msContentZoomLimitMin", |
|
"msContentZoomSnap", |
|
"msContentZoomSnapPoints", |
|
"msContentZoomSnapType", |
|
"msContentZooming", |
|
"msConvertURL", |
|
"msCrypto", |
|
"msDoNotTrack", |
|
"msElementsFromPoint", |
|
"msElementsFromRect", |
|
"msExitFullscreen", |
|
"msExtendedCode", |
|
"msFillRule", |
|
"msFirstPaint", |
|
"msFlex", |
|
"msFlexAlign", |
|
"msFlexDirection", |
|
"msFlexFlow", |
|
"msFlexItemAlign", |
|
"msFlexLinePack", |
|
"msFlexNegative", |
|
"msFlexOrder", |
|
"msFlexPack", |
|
"msFlexPositive", |
|
"msFlexPreferredSize", |
|
"msFlexWrap", |
|
"msFlowFrom", |
|
"msFlowInto", |
|
"msFontFeatureSettings", |
|
"msFullscreenElement", |
|
"msFullscreenEnabled", |
|
"msGetInputContext", |
|
"msGetRegionContent", |
|
"msGetUntransformedBounds", |
|
"msGraphicsTrustStatus", |
|
"msGridColumn", |
|
"msGridColumnAlign", |
|
"msGridColumnSpan", |
|
"msGridColumns", |
|
"msGridRow", |
|
"msGridRowAlign", |
|
"msGridRowSpan", |
|
"msGridRows", |
|
"msHidden", |
|
"msHighContrastAdjust", |
|
"msHyphenateLimitChars", |
|
"msHyphenateLimitLines", |
|
"msHyphenateLimitZone", |
|
"msHyphens", |
|
"msImageSmoothingEnabled", |
|
"msImeAlign", |
|
"msIndexedDB", |
|
"msInterpolationMode", |
|
"msIsStaticHTML", |
|
"msKeySystem", |
|
"msKeys", |
|
"msLaunchUri", |
|
"msLockOrientation", |
|
"msManipulationViewsEnabled", |
|
"msMatchMedia", |
|
"msMatchesSelector", |
|
"msMaxTouchPoints", |
|
"msOrientation", |
|
"msOverflowStyle", |
|
"msPerspective", |
|
"msPerspectiveOrigin", |
|
"msPlayToDisabled", |
|
"msPlayToPreferredSourceUri", |
|
"msPlayToPrimary", |
|
"msPointerEnabled", |
|
"msRegionOverflow", |
|
"msReleasePointerCapture", |
|
"msRequestAnimationFrame", |
|
"msRequestFullscreen", |
|
"msSaveBlob", |
|
"msSaveOrOpenBlob", |
|
"msScrollChaining", |
|
"msScrollLimit", |
|
"msScrollLimitXMax", |
|
"msScrollLimitXMin", |
|
"msScrollLimitYMax", |
|
"msScrollLimitYMin", |
|
"msScrollRails", |
|
"msScrollSnapPointsX", |
|
"msScrollSnapPointsY", |
|
"msScrollSnapType", |
|
"msScrollSnapX", |
|
"msScrollSnapY", |
|
"msScrollTranslation", |
|
"msSetImmediate", |
|
"msSetMediaKeys", |
|
"msSetPointerCapture", |
|
"msTextCombineHorizontal", |
|
"msTextSizeAdjust", |
|
"msToBlob", |
|
"msTouchAction", |
|
"msTouchSelect", |
|
"msTraceAsyncCallbackCompleted", |
|
"msTraceAsyncCallbackStarting", |
|
"msTraceAsyncOperationCompleted", |
|
"msTraceAsyncOperationStarting", |
|
"msTransform", |
|
"msTransformOrigin", |
|
"msTransformStyle", |
|
"msTransition", |
|
"msTransitionDelay", |
|
"msTransitionDuration", |
|
"msTransitionProperty", |
|
"msTransitionTimingFunction", |
|
"msUnlockOrientation", |
|
"msUpdateAsyncCallbackRelation", |
|
"msUserSelect", |
|
"msVisibilityState", |
|
"msWrapFlow", |
|
"msWrapMargin", |
|
"msWrapThrough", |
|
"msWriteProfilerMark", |
|
"msZoom", |
|
"msZoomTo", |
|
"mt", |
|
"mul", |
|
"multiEntry", |
|
"multiSelectionObj", |
|
"multiline", |
|
"multiple", |
|
"multiply", |
|
"multiplySelf", |
|
"multisample", |
|
"multisampled", |
|
"mutableFile", |
|
"muted", |
|
"n", |
|
"nacl_arch", |
|
"name", |
|
"nameList", |
|
"nameProp", |
|
"namedItem", |
|
"namedRecordset", |
|
"names", |
|
"namespaceURI", |
|
"namespaces", |
|
"nanoseconds", |
|
"nativeApplication", |
|
"nativeMap", |
|
"nativeObjectCreate", |
|
"nativeSet", |
|
"nativeWeakMap", |
|
"naturalHeight", |
|
"naturalWidth", |
|
"navigate", |
|
"navigation", |
|
"navigationMode", |
|
"navigationPreload", |
|
"navigationStart", |
|
"navigationType", |
|
"navigator", |
|
"near", |
|
"nearestViewportElement", |
|
"negative", |
|
"negotiated", |
|
"netscape", |
|
"networkState", |
|
"networkStatus", |
|
"newScale", |
|
"newState", |
|
"newTranslate", |
|
"newURL", |
|
"newValue", |
|
"newValueSpecifiedUnits", |
|
"newVersion", |
|
"newhome", |
|
"next", |
|
"nextElementSibling", |
|
"nextHopProtocol", |
|
"nextNode", |
|
"nextPage", |
|
"nextSibling", |
|
"nickname", |
|
"noHref", |
|
"noModule", |
|
"noResize", |
|
"noShade", |
|
"noValidate", |
|
"noWrap", |
|
"node", |
|
"nodeName", |
|
"nodeType", |
|
"nodeValue", |
|
"nonce", |
|
"normDepthBufferFromNormView", |
|
"normalize", |
|
"normalizedPathSegList", |
|
"normandyAddonStudy", |
|
"notRestoredReasons", |
|
"notationName", |
|
"notations", |
|
"note", |
|
"noteGrainOn", |
|
"noteOff", |
|
"noteOn", |
|
"notifications", |
|
"notify", |
|
"now", |
|
"npnNegotiatedProtocol", |
|
"numOctaves", |
|
"number", |
|
"numberOfChannels", |
|
"numberOfFrames", |
|
"numberOfInputs", |
|
"numberOfItems", |
|
"numberOfOutputs", |
|
"numberValue", |
|
"numberingSystem", |
|
"numeric", |
|
"oMatchesSelector", |
|
"object", |
|
"object-fit", |
|
"object-position", |
|
"objectFit", |
|
"objectPosition", |
|
"objectStore", |
|
"objectStoreNames", |
|
"objectType", |
|
"observe", |
|
"observedAttributes", |
|
"occlusionQuerySet", |
|
"of", |
|
"off", |
|
"offscreenBuffering", |
|
"offset", |
|
"offset-anchor", |
|
"offset-distance", |
|
"offset-path", |
|
"offset-position", |
|
"offset-rotate", |
|
"offsetAnchor", |
|
"offsetDistance", |
|
"offsetHeight", |
|
"offsetLeft", |
|
"offsetNode", |
|
"offsetParent", |
|
"offsetPath", |
|
"offsetPosition", |
|
"offsetRotate", |
|
"offsetTop", |
|
"offsetWidth", |
|
"offsetX", |
|
"offsetY", |
|
"ok", |
|
"oldState", |
|
"oldURL", |
|
"oldValue", |
|
"oldVersion", |
|
"olderShadowRoot", |
|
"omnibox", |
|
"on", |
|
"onActivated", |
|
"onAdded", |
|
"onAttached", |
|
"onBoundsChanged", |
|
"onBrowserUpdateAvailable", |
|
"onClicked", |
|
"onCommitFiberRoot", |
|
"onCommitFiberUnmount", |
|
"onConnect", |
|
"onConnectExternal", |
|
"onConnectNative", |
|
"onCreated", |
|
"onDetached", |
|
"onDisabled", |
|
"onEnabled", |
|
"onFocusChanged", |
|
"onHighlighted", |
|
"onInstalled", |
|
"onLine", |
|
"onMessage", |
|
"onMessageExternal", |
|
"onMoved", |
|
"onPerformanceWarning", |
|
"onPostCommitFiberRoot", |
|
"onRemoved", |
|
"onReplaced", |
|
"onRestartRequired", |
|
"onStartup", |
|
"onSubmittedWorkDone", |
|
"onSuspend", |
|
"onSuspendCanceled", |
|
"onUninstalled", |
|
"onUpdateAvailable", |
|
"onUpdated", |
|
"onUserScriptConnect", |
|
"onUserScriptMessage", |
|
"onUserSettingsChanged", |
|
"onZoomChange", |
|
"onabort", |
|
"onabsolutedeviceorientation", |
|
"onactivate", |
|
"onactive", |
|
"onaddsourcebuffer", |
|
"onaddstream", |
|
"onaddtrack", |
|
"onafterprint", |
|
"onafterscriptexecute", |
|
"onafterupdate", |
|
"onanimationcancel", |
|
"onanimationend", |
|
"onanimationiteration", |
|
"onanimationstart", |
|
"onappinstalled", |
|
"onaudioend", |
|
"onaudioprocess", |
|
"onaudiostart", |
|
"onautocomplete", |
|
"onautocompleteerror", |
|
"onauxclick", |
|
"onbeforeactivate", |
|
"onbeforecopy", |
|
"onbeforecut", |
|
"onbeforedeactivate", |
|
"onbeforeeditfocus", |
|
"onbeforeinput", |
|
"onbeforeinstallprompt", |
|
"onbeforematch", |
|
"onbeforepaste", |
|
"onbeforeprint", |
|
"onbeforescriptexecute", |
|
"onbeforetoggle", |
|
"onbeforeunload", |
|
"onbeforeupdate", |
|
"onbeforexrselect", |
|
"onbegin", |
|
"onblocked", |
|
"onblur", |
|
"onbounce", |
|
"onboundary", |
|
"onbufferedamountlow", |
|
"oncached", |
|
"oncancel", |
|
"oncandidatewindowhide", |
|
"oncandidatewindowshow", |
|
"oncandidatewindowupdate", |
|
"oncanplay", |
|
"oncanplaythrough", |
|
"oncapturehandlechange", |
|
"once", |
|
"oncellchange", |
|
"onchange", |
|
"oncharacterboundsupdate", |
|
"oncharacteristicvaluechanged", |
|
"onchargingchange", |
|
"onchargingtimechange", |
|
"onchecking", |
|
"onclick", |
|
"onclose", |
|
"onclosing", |
|
"oncommand", |
|
"oncompassneedscalibration", |
|
"oncomplete", |
|
"oncompositionend", |
|
"oncompositionstart", |
|
"onconnect", |
|
"onconnecting", |
|
"onconnectionavailable", |
|
"onconnectionstatechange", |
|
"oncontentvisibilityautostatechange", |
|
"oncontextlost", |
|
"oncontextmenu", |
|
"oncontextrestored", |
|
"oncontrollerchange", |
|
"oncontrolselect", |
|
"oncopy", |
|
"oncuechange", |
|
"oncurrententrychange", |
|
"oncurrentscreenchange", |
|
"oncut", |
|
"ondataavailable", |
|
"ondatachannel", |
|
"ondatasetchanged", |
|
"ondatasetcomplete", |
|
"ondblclick", |
|
"ondeactivate", |
|
"ondequeue", |
|
"ondevicechange", |
|
"ondevicelight", |
|
"ondevicemotion", |
|
"ondeviceorientation", |
|
"ondeviceorientationabsolute", |
|
"ondeviceproximity", |
|
"ondischargingtimechange", |
|
"ondisconnect", |
|
"ondisplay", |
|
"ondispose", |
|
"ondownloading", |
|
"ondownloadprogress", |
|
"ondrag", |
|
"ondragend", |
|
"ondragenter", |
|
"ondragexit", |
|
"ondragleave", |
|
"ondragover", |
|
"ondragstart", |
|
"ondrop", |
|
"ondurationchange", |
|
"onemptied", |
|
"onencrypted", |
|
"onend", |
|
"onended", |
|
"onenter", |
|
"onenterpictureinpicture", |
|
"onerror", |
|
"onerrorupdate", |
|
"onexit", |
|
"onfencedtreeclick", |
|
"onfilterchange", |
|
"onfinish", |
|
"onfocus", |
|
"onfocusin", |
|
"onfocusout", |
|
"onformdata", |
|
"onfreeze", |
|
"onfullscreenchange", |
|
"onfullscreenerror", |
|
"ongamepadconnected", |
|
"ongamepaddisconnected", |
|
"ongatheringstatechange", |
|
"ongattserverdisconnected", |
|
"ongeometrychange", |
|
"ongesturechange", |
|
"ongestureend", |
|
"ongesturestart", |
|
"ongotpointercapture", |
|
"onhashchange", |
|
"onhelp", |
|
"onicecandidate", |
|
"onicecandidateerror", |
|
"oniceconnectionstatechange", |
|
"onicegatheringstatechange", |
|
"oninactive", |
|
"oninput", |
|
"oninputreport", |
|
"oninputsourceschange", |
|
"oninvalid", |
|
"onkeydown", |
|
"onkeypress", |
|
"onkeystatuseschange", |
|
"onkeyup", |
|
"onlanguagechange", |
|
"onlayoutcomplete", |
|
"onleavepictureinpicture", |
|
"onlevelchange", |
|
"onload", |
|
"onloadT", |
|
"onloadeddata", |
|
"onloadedmetadata", |
|
"onloadend", |
|
"onloading", |
|
"onloadingdone", |
|
"onloadingerror", |
|
"onloadstart", |
|
"onlosecapture", |
|
"onlostpointercapture", |
|
"only", |
|
"onmanagedconfigurationchange", |
|
"onmark", |
|
"onmessage", |
|
"onmessageerror", |
|
"onmidimessage", |
|
"onmousedown", |
|
"onmouseenter", |
|
"onmouseleave", |
|
"onmousemove", |
|
"onmouseout", |
|
"onmouseover", |
|
"onmouseup", |
|
"onmousewheel", |
|
"onmove", |
|
"onmoveend", |
|
"onmovestart", |
|
"onmozfullscreenchange", |
|
"onmozfullscreenerror", |
|
"onmozorientationchange", |
|
"onmozpointerlockchange", |
|
"onmozpointerlockerror", |
|
"onmscontentzoom", |
|
"onmsfullscreenchange", |
|
"onmsfullscreenerror", |
|
"onmsgesturechange", |
|
"onmsgesturedoubletap", |
|
"onmsgestureend", |
|
"onmsgesturehold", |
|
"onmsgesturestart", |
|
"onmsgesturetap", |
|
"onmsgotpointercapture", |
|
"onmsinertiastart", |
|
"onmslostpointercapture", |
|
"onmsmanipulationstatechanged", |
|
"onmsneedkey", |
|
"onmsorientationchange", |
|
"onmspointercancel", |
|
"onmspointerdown", |
|
"onmspointerenter", |
|
"onmspointerhover", |
|
"onmspointerleave", |
|
"onmspointermove", |
|
"onmspointerout", |
|
"onmspointerover", |
|
"onmspointerup", |
|
"onmssitemodejumplistitemremoved", |
|
"onmsthumbnailclick", |
|
"onmute", |
|
"onnavigate", |
|
"onnavigateerror", |
|
"onnavigatesuccess", |
|
"onnegotiationneeded", |
|
"onnomatch", |
|
"onnoupdate", |
|
"onobsolete", |
|
"onoffline", |
|
"ononline", |
|
"onopen", |
|
"onorientationchange", |
|
"onpagechange", |
|
"onpagehide", |
|
"onpagereveal", |
|
"onpageshow", |
|
"onpageswap", |
|
"onpaste", |
|
"onpause", |
|
"onpayerdetailchange", |
|
"onpaymentmethodchange", |
|
"onplay", |
|
"onplaying", |
|
"onpluginstreamstart", |
|
"onpointercancel", |
|
"onpointerdown", |
|
"onpointerenter", |
|
"onpointerleave", |
|
"onpointerlockchange", |
|
"onpointerlockerror", |
|
"onpointermove", |
|
"onpointerout", |
|
"onpointerover", |
|
"onpointerrawupdate", |
|
"onpointerup", |
|
"onpopstate", |
|
"onprerenderingchange", |
|
"onprioritychange", |
|
"onprocessorerror", |
|
"onprogress", |
|
"onpropertychange", |
|
"onratechange", |
|
"onreading", |
|
"onreadystatechange", |
|
"onreflectionchange", |
|
"onrejectionhandled", |
|
"onrelease", |
|
"onremove", |
|
"onremovesourcebuffer", |
|
"onremovestream", |
|
"onremovetrack", |
|
"onrepeat", |
|
"onreset", |
|
"onresize", |
|
"onresizeend", |
|
"onresizestart", |
|
"onresourcetimingbufferfull", |
|
"onresult", |
|
"onresume", |
|
"onrowenter", |
|
"onrowexit", |
|
"onrowsdelete", |
|
"onrowsinserted", |
|
"onscreenschange", |
|
"onscroll", |
|
"onscrollend", |
|
"onscrollsnapchange", |
|
"onscrollsnapchanging", |
|
"onsearch", |
|
"onsecuritypolicyviolation", |
|
"onseeked", |
|
"onseeking", |
|
"onselect", |
|
"onselectedcandidatepairchange", |
|
"onselectend", |
|
"onselectionchange", |
|
"onselectstart", |
|
"onshippingaddresschange", |
|
"onshippingoptionchange", |
|
"onshow", |
|
"onsignalingstatechange", |
|
"onsinkchange", |
|
"onslotchange", |
|
"onsoundend", |
|
"onsoundstart", |
|
"onsourceclose", |
|
"onsourceclosed", |
|
"onsourceended", |
|
"onsourceopen", |
|
"onspeechend", |
|
"onspeechstart", |
|
"onsqueeze", |
|
"onsqueezeend", |
|
"onsqueezestart", |
|
"onstalled", |
|
"onstart", |
|
"onstatechange", |
|
"onstop", |
|
"onstorage", |
|
"onstoragecommit", |
|
"onsubmit", |
|
"onsuccess", |
|
"onsuspend", |
|
"onterminate", |
|
"ontextformatupdate", |
|
"ontextinput", |
|
"ontextupdate", |
|
"ontimeout", |
|
"ontimeupdate", |
|
"ontoggle", |
|
"ontonechange", |
|
"ontouchcancel", |
|
"ontouchend", |
|
"ontouchmove", |
|
"ontouchstart", |
|
"ontrack", |
|
"ontransitioncancel", |
|
"ontransitionend", |
|
"ontransitionrun", |
|
"ontransitionstart", |
|
"onuncapturederror", |
|
"onunhandledrejection", |
|
"onunload", |
|
"onunmute", |
|
"onupdate", |
|
"onupdateend", |
|
"onupdatefound", |
|
"onupdateready", |
|
"onupdatestart", |
|
"onupgradeneeded", |
|
"onuserproximity", |
|
"onversionchange", |
|
"onvisibilitychange", |
|
"onvoiceschanged", |
|
"onvolumechange", |
|
"onvrdisplayactivate", |
|
"onvrdisplayconnect", |
|
"onvrdisplaydeactivate", |
|
"onvrdisplaydisconnect", |
|
"onvrdisplaypresentchange", |
|
"onwaiting", |
|
"onwaitingforkey", |
|
"onwarning", |
|
"onwebkitanimationend", |
|
"onwebkitanimationiteration", |
|
"onwebkitanimationstart", |
|
"onwebkitcurrentplaybacktargetiswirelesschanged", |
|
"onwebkitfullscreenchange", |
|
"onwebkitfullscreenerror", |
|
"onwebkitkeyadded", |
|
"onwebkitkeyerror", |
|
"onwebkitkeymessage", |
|
"onwebkitneedkey", |
|
"onwebkitorientationchange", |
|
"onwebkitplaybacktargetavailabilitychanged", |
|
"onwebkitpointerlockchange", |
|
"onwebkitpointerlockerror", |
|
"onwebkitresourcetimingbufferfull", |
|
"onwebkittransitionend", |
|
"onwheel", |
|
"onzoom", |
|
"onzoomlevelchange", |
|
"opacity", |
|
"open", |
|
"openCursor", |
|
"openDatabase", |
|
"openKeyCursor", |
|
"openOptionsPage", |
|
"openOrClosedShadowRoot", |
|
"openPopup", |
|
"opened", |
|
"opener", |
|
"opera", |
|
"operation", |
|
"operationType", |
|
"operator", |
|
"opr", |
|
"optimum", |
|
"options", |
|
"or", |
|
"order", |
|
"orderX", |
|
"orderY", |
|
"ordered", |
|
"org", |
|
"organization", |
|
"orient", |
|
"orientAngle", |
|
"orientType", |
|
"orientation", |
|
"orientationX", |
|
"orientationY", |
|
"orientationZ", |
|
"origin", |
|
"originAgentCluster", |
|
"originalPolicy", |
|
"originalTarget", |
|
"ornaments", |
|
"orphans", |
|
"os", |
|
"oscpu", |
|
"outerHTML", |
|
"outerHeight", |
|
"outerText", |
|
"outerWidth", |
|
"outgoingHighWaterMark", |
|
"outgoingMaxAge", |
|
"outline", |
|
"outline-color", |
|
"outline-offset", |
|
"outline-style", |
|
"outline-width", |
|
"outlineColor", |
|
"outlineOffset", |
|
"outlineStyle", |
|
"outlineWidth", |
|
"outputBuffer", |
|
"outputChannelCount", |
|
"outputLanguage", |
|
"outputLatency", |
|
"outputs", |
|
"overallProgress", |
|
"overflow", |
|
"overflow-anchor", |
|
"overflow-block", |
|
"overflow-clip-margin", |
|
"overflow-inline", |
|
"overflow-wrap", |
|
"overflow-x", |
|
"overflow-y", |
|
"overflowAnchor", |
|
"overflowBlock", |
|
"overflowClipMargin", |
|
"overflowInline", |
|
"overflowWrap", |
|
"overflowX", |
|
"overflowY", |
|
"overlaysContent", |
|
"overrideColors", |
|
"overrideMimeType", |
|
"oversample", |
|
"overscroll-behavior", |
|
"overscroll-behavior-block", |
|
"overscroll-behavior-inline", |
|
"overscroll-behavior-x", |
|
"overscroll-behavior-y", |
|
"overscrollBehavior", |
|
"overscrollBehaviorBlock", |
|
"overscrollBehaviorInline", |
|
"overscrollBehaviorX", |
|
"overscrollBehaviorY", |
|
"ownKeys", |
|
"ownerDocument", |
|
"ownerElement", |
|
"ownerNode", |
|
"ownerRule", |
|
"ownerSVGElement", |
|
"owningElement", |
|
"p1", |
|
"p2", |
|
"p3", |
|
"p4", |
|
"packetSize", |
|
"packets", |
|
"pad", |
|
"padEnd", |
|
"padStart", |
|
"padding", |
|
"padding-block", |
|
"padding-block-end", |
|
"padding-block-start", |
|
"padding-bottom", |
|
"padding-inline", |
|
"padding-inline-end", |
|
"padding-inline-start", |
|
"padding-left", |
|
"padding-right", |
|
"padding-top", |
|
"paddingBlock", |
|
"paddingBlockEnd", |
|
"paddingBlockStart", |
|
"paddingBottom", |
|
"paddingInline", |
|
"paddingInlineEnd", |
|
"paddingInlineStart", |
|
"paddingLeft", |
|
"paddingRight", |
|
"paddingTop", |
|
"page", |
|
"page-break-after", |
|
"page-break-before", |
|
"page-break-inside", |
|
"page-orientation", |
|
"pageAction", |
|
"pageBreakAfter", |
|
"pageBreakBefore", |
|
"pageBreakInside", |
|
"pageCount", |
|
"pageLeft", |
|
"pageOrientation", |
|
"pageT", |
|
"pageTop", |
|
"pageX", |
|
"pageXOffset", |
|
"pageY", |
|
"pageYOffset", |
|
"pages", |
|
"paint-order", |
|
"paintOrder", |
|
"paintRequests", |
|
"paintTime", |
|
"paintType", |
|
"paintWorklet", |
|
"palette", |
|
"pan", |
|
"panningModel", |
|
"parameterData", |
|
"parameters", |
|
"parent", |
|
"parentElement", |
|
"parentNode", |
|
"parentRule", |
|
"parentStyleSheet", |
|
"parentTextEdit", |
|
"parentWindow", |
|
"parse", |
|
"parseAll", |
|
"parseCreationOptionsFromJSON", |
|
"parseFloat", |
|
"parseFromString", |
|
"parseHTMLUnsafe", |
|
"parseInt", |
|
"parseRequestOptionsFromJSON", |
|
"part", |
|
"participants", |
|
"passOp", |
|
"passive", |
|
"password", |
|
"pasteHTML", |
|
"path", |
|
"pathLength", |
|
"pathSegList", |
|
"pathSegType", |
|
"pathSegTypeAsLetter", |
|
"pathname", |
|
"pattern", |
|
"patternContentUnits", |
|
"patternMismatch", |
|
"patternTransform", |
|
"patternUnits", |
|
"pause", |
|
"pauseAnimations", |
|
"pauseDepthSensing", |
|
"pauseDuration", |
|
"pauseOnExit", |
|
"pauseProfilers", |
|
"pauseTransformFeedback", |
|
"paused", |
|
"payerEmail", |
|
"payerName", |
|
"payerPhone", |
|
"paymentManager", |
|
"pc", |
|
"pdfViewerEnabled", |
|
"peerIdentity", |
|
"pending", |
|
"pendingLocalDescription", |
|
"pendingRemoteDescription", |
|
"percent", |
|
"performance", |
|
"periodicSync", |
|
"permission", |
|
"permissionState", |
|
"permissions", |
|
"persist", |
|
"persisted", |
|
"persistentDeviceId", |
|
"personalbar", |
|
"perspective", |
|
"perspective-origin", |
|
"perspectiveOrigin", |
|
"phone", |
|
"phoneticFamilyName", |
|
"phoneticGivenName", |
|
"photo", |
|
"phrase", |
|
"phrases", |
|
"pictureInPictureChild", |
|
"pictureInPictureElement", |
|
"pictureInPictureEnabled", |
|
"pictureInPictureWindow", |
|
"ping", |
|
"pipeThrough", |
|
"pipeTo", |
|
"pitch", |
|
"pixelBottom", |
|
"pixelDepth", |
|
"pixelFormat", |
|
"pixelHeight", |
|
"pixelLeft", |
|
"pixelRight", |
|
"pixelStorei", |
|
"pixelTop", |
|
"pixelUnitToMillimeterX", |
|
"pixelUnitToMillimeterY", |
|
"pixelWidth", |
|
"pkcs11", |
|
"place-content", |
|
"place-items", |
|
"place-self", |
|
"placeContent", |
|
"placeItems", |
|
"placeSelf", |
|
"placeholder", |
|
"platform", |
|
"platformVersion", |
|
"platforms", |
|
"play", |
|
"playEffect", |
|
"playState", |
|
"playbackRate", |
|
"playbackState", |
|
"playbackTime", |
|
"played", |
|
"playoutDelayHint", |
|
"playsInline", |
|
"plugins", |
|
"pluginspage", |
|
"pname", |
|
"pointer-events", |
|
"pointerBeforeReferenceNode", |
|
"pointerEnabled", |
|
"pointerEvents", |
|
"pointerId", |
|
"pointerLockElement", |
|
"pointerType", |
|
"points", |
|
"pointsAtX", |
|
"pointsAtY", |
|
"pointsAtZ", |
|
"polygonOffset", |
|
"pop", |
|
"popDebugGroup", |
|
"popErrorScope", |
|
"popover", |
|
"popoverTargetAction", |
|
"popoverTargetElement", |
|
"populateMatrix", |
|
"popupWindowFeatures", |
|
"popupWindowName", |
|
"popupWindowURI", |
|
"port", |
|
"port1", |
|
"port2", |
|
"ports", |
|
"posBottom", |
|
"posHeight", |
|
"posLeft", |
|
"posRight", |
|
"posTop", |
|
"posWidth", |
|
"pose", |
|
"position", |
|
"position-anchor", |
|
"position-area", |
|
"positionAlign", |
|
"positionAnchor", |
|
"positionArea", |
|
"positionTry", |
|
"positionTryFallbacks", |
|
"positionVisibility", |
|
"positionX", |
|
"positionY", |
|
"positionZ", |
|
"postError", |
|
"postMessage", |
|
"postTask", |
|
"postalCode", |
|
"poster", |
|
"postscriptName", |
|
"pow", |
|
"powerEfficient", |
|
"powerOff", |
|
"powerPreference", |
|
"preMultiplySelf", |
|
"precision", |
|
"preferredReflectionFormat", |
|
"preferredStyleSheetSet", |
|
"preferredStylesheetSet", |
|
"prefix", |
|
"preload", |
|
"premultipliedAlpha", |
|
"prepend", |
|
"prerendering", |
|
"presentation", |
|
"presentationArea", |
|
"presentationStyle", |
|
"presentationTime", |
|
"preserveAlpha", |
|
"preserveAspectRatio", |
|
"preserveAspectRatioString", |
|
"preservesPitch", |
|
"pressed", |
|
"pressure", |
|
"prevValue", |
|
"preventDefault", |
|
"preventExtensions", |
|
"preventSilentAccess", |
|
"previousElementSibling", |
|
"previousNode", |
|
"previousPage", |
|
"previousPriority", |
|
"previousRect", |
|
"previousScale", |
|
"previousSibling", |
|
"previousTranslate", |
|
"primaries", |
|
"primaryKey", |
|
"primaryLightDirection", |
|
"primaryLightIntensity", |
|
"primitive", |
|
"primitiveType", |
|
"primitiveUnits", |
|
"principals", |
|
"print", |
|
"print-color-adjust", |
|
"printColorAdjust", |
|
"printPreview", |
|
"priority", |
|
"privacy", |
|
"privateKey", |
|
"privateToken", |
|
"probablySupportsContext", |
|
"probeSpace", |
|
"process", |
|
"processIceMessage", |
|
"processLocally", |
|
"processingEnd", |
|
"processingStart", |
|
"processorOptions", |
|
"product", |
|
"productId", |
|
"productName", |
|
"productSub", |
|
"profile", |
|
"profileEnd", |
|
"profiles", |
|
"projectionMatrix", |
|
"promise", |
|
"promising", |
|
"prompt", |
|
"properties", |
|
"propertyIsEnumerable", |
|
"propertyName", |
|
"protectedAudience", |
|
"protocol", |
|
"protocolLong", |
|
"prototype", |
|
"provider", |
|
"proxy", |
|
"pseudoClass", |
|
"pseudoElement", |
|
"pt", |
|
"publicId", |
|
"publicKey", |
|
"published", |
|
"pulse", |
|
"push", |
|
"pushDebugGroup", |
|
"pushErrorScope", |
|
"pushManager", |
|
"pushNotification", |
|
"pushState", |
|
"put", |
|
"putImageData", |
|
"px", |
|
"quadraticCurveTo", |
|
"qualifier", |
|
"quaternion", |
|
"query", |
|
"queryCommandEnabled", |
|
"queryCommandIndeterm", |
|
"queryCommandState", |
|
"queryCommandSupported", |
|
"queryCommandText", |
|
"queryCommandValue", |
|
"queryFeatureSupport", |
|
"queryLocalFonts", |
|
"queryPermission", |
|
"querySelector", |
|
"querySelectorAll", |
|
"querySet", |
|
"queue", |
|
"queueMicrotask", |
|
"quota", |
|
"quote", |
|
"quotes", |
|
"r", |
|
"r1", |
|
"r2", |
|
"race", |
|
"rad", |
|
"radiogroup", |
|
"radius", |
|
"radiusX", |
|
"radiusY", |
|
"random", |
|
"randomUUID", |
|
"range", |
|
"rangeCount", |
|
"rangeEnd", |
|
"rangeMax", |
|
"rangeMin", |
|
"rangeOffset", |
|
"rangeOverflow", |
|
"rangeParent", |
|
"rangeStart", |
|
"rangeUnderflow", |
|
"rate", |
|
"ratio", |
|
"raw", |
|
"rawId", |
|
"rawJSON", |
|
"rawValueToMeters", |
|
"rcap", |
|
"rch", |
|
"read", |
|
"readAsArrayBuffer", |
|
"readAsBinaryString", |
|
"readAsBlob", |
|
"readAsDataURL", |
|
"readAsText", |
|
"readBuffer", |
|
"readEntries", |
|
"readOnly", |
|
"readPixels", |
|
"readReportRequested", |
|
"readText", |
|
"readValue", |
|
"readable", |
|
"ready", |
|
"readyState", |
|
"reason", |
|
"reasons", |
|
"reboot", |
|
"receiveFeatureReport", |
|
"receivedAlert", |
|
"receiver", |
|
"receivers", |
|
"recipient", |
|
"recommendedViewportScale", |
|
"reconnect", |
|
"recordNumber", |
|
"recordsAvailable", |
|
"recordset", |
|
"rect", |
|
"red", |
|
"redEyeReduction", |
|
"redirect", |
|
"redirectCount", |
|
"redirectEnd", |
|
"redirectStart", |
|
"redirected", |
|
"reduce", |
|
"reduceRight", |
|
"reduction", |
|
"refDistance", |
|
"refX", |
|
"refY", |
|
"referenceNode", |
|
"referenceSpace", |
|
"referrer", |
|
"referrerPolicy", |
|
"refresh", |
|
"region", |
|
"regionAnchorX", |
|
"regionAnchorY", |
|
"regionId", |
|
"regions", |
|
"register", |
|
"registerContentHandler", |
|
"registerElement", |
|
"registerInternalModuleStart", |
|
"registerInternalModuleStop", |
|
"registerProperty", |
|
"registerProtocolHandler", |
|
"reject", |
|
"rel", |
|
"relList", |
|
"relatedAddress", |
|
"relatedNode", |
|
"relatedPort", |
|
"relatedTarget", |
|
"relayProtocol", |
|
"release", |
|
"releaseCapture", |
|
"releaseEvents", |
|
"releaseInterface", |
|
"releaseLock", |
|
"releasePointerCapture", |
|
"releaseShaderCompiler", |
|
"released", |
|
"reliability", |
|
"reliable", |
|
"reliableWrite", |
|
"reload", |
|
"rem", |
|
"remainingSpace", |
|
"remote", |
|
"remoteDescription", |
|
"remove", |
|
"removeAllRanges", |
|
"removeAttribute", |
|
"removeAttributeNS", |
|
"removeAttributeNode", |
|
"removeBehavior", |
|
"removeChild", |
|
"removeCue", |
|
"removeEntry", |
|
"removeEventListener", |
|
"removeFilter", |
|
"removeImport", |
|
"removeItem", |
|
"removeListener", |
|
"removeNamedItem", |
|
"removeNamedItemNS", |
|
"removeNode", |
|
"removeParameter", |
|
"removeProperty", |
|
"removeRange", |
|
"removeRegion", |
|
"removeRule", |
|
"removeSiteSpecificTrackingException", |
|
"removeSourceBuffer", |
|
"removeStream", |
|
"removeTrack", |
|
"removeVariable", |
|
"removeWakeLockListener", |
|
"removeWebWideTrackingException", |
|
"removed", |
|
"removedNodes", |
|
"renderBlockingStatus", |
|
"renderHeight", |
|
"renderStart", |
|
"renderState", |
|
"renderTime", |
|
"renderWidth", |
|
"renderbufferStorage", |
|
"renderbufferStorageMultisample", |
|
"renderedBuffer", |
|
"rendererInterfaces", |
|
"renderers", |
|
"renderingMode", |
|
"renotify", |
|
"repeat", |
|
"repetitionCount", |
|
"replace", |
|
"replaceAdjacentText", |
|
"replaceAll", |
|
"replaceChild", |
|
"replaceChildren", |
|
"replaceData", |
|
"replaceId", |
|
"replaceItem", |
|
"replaceNode", |
|
"replaceState", |
|
"replaceSync", |
|
"replaceTrack", |
|
"replaceWholeText", |
|
"replaceWith", |
|
"reportError", |
|
"reportEvent", |
|
"reportId", |
|
"reportOnly", |
|
"reportValidity", |
|
"request", |
|
"requestAdapter", |
|
"requestAdapterInfo", |
|
"requestAnimationFrame", |
|
"requestAutocomplete", |
|
"requestClose", |
|
"requestData", |
|
"requestDevice", |
|
"requestFrame", |
|
"requestFullscreen", |
|
"requestHitTestSource", |
|
"requestHitTestSourceForTransientInput", |
|
"requestId", |
|
"requestIdleCallback", |
|
"requestLightProbe", |
|
"requestMIDIAccess", |
|
"requestMediaKeySystemAccess", |
|
"requestPermission", |
|
"requestPictureInPicture", |
|
"requestPointerLock", |
|
"requestPort", |
|
"requestPresent", |
|
"requestPresenter", |
|
"requestReferenceSpace", |
|
"requestSession", |
|
"requestStart", |
|
"requestStorageAccess", |
|
"requestStorageAccessFor", |
|
"requestSubmit", |
|
"requestTime", |
|
"requestUpdateCheck", |
|
"requestVideoFrameCallback", |
|
"requestViewportScale", |
|
"requestWindow", |
|
"requested", |
|
"requestingWindow", |
|
"requireInteraction", |
|
"required", |
|
"requiredExtensions", |
|
"requiredFeatures", |
|
"requiredLimits", |
|
"reset", |
|
"resetLatency", |
|
"resetPose", |
|
"resetTransform", |
|
"resetZoomLevel", |
|
"resizable", |
|
"resize", |
|
"resizeBy", |
|
"resizeTo", |
|
"resolve", |
|
"resolveQuerySet", |
|
"resolveTarget", |
|
"resource", |
|
"respond", |
|
"respondWithNewView", |
|
"response", |
|
"responseBody", |
|
"responseEnd", |
|
"responseReady", |
|
"responseStart", |
|
"responseStatus", |
|
"responseText", |
|
"responseType", |
|
"responseURL", |
|
"responseXML", |
|
"restart", |
|
"restartAfterDelay", |
|
"restartIce", |
|
"restore", |
|
"restrictTo", |
|
"result", |
|
"resultIndex", |
|
"resultType", |
|
"results", |
|
"resume", |
|
"resumeDepthSensing", |
|
"resumeProfilers", |
|
"resumeTransformFeedback", |
|
"retry", |
|
"returnType", |
|
"returnValue", |
|
"rev", |
|
"reverse", |
|
"reversed", |
|
"revocable", |
|
"revokeObjectURL", |
|
"rex", |
|
"rgbColor", |
|
"ric", |
|
"right", |
|
"rightContext", |
|
"rightDegrees", |
|
"rightMargin", |
|
"rightProjectionMatrix", |
|
"rightViewMatrix", |
|
"rlh", |
|
"role", |
|
"rolloffFactor", |
|
"root", |
|
"rootBounds", |
|
"rootElement", |
|
"rootMargin", |
|
"rotate", |
|
"rotateAxisAngle", |
|
"rotateAxisAngleSelf", |
|
"rotateFromVector", |
|
"rotateFromVectorSelf", |
|
"rotateSelf", |
|
"rotation", |
|
"rotationAngle", |
|
"rotationRate", |
|
"round", |
|
"roundRect", |
|
"row-gap", |
|
"rowGap", |
|
"rowIndex", |
|
"rowSpan", |
|
"rows", |
|
"rowsPerImage", |
|
"rtcpTransport", |
|
"rtt", |
|
"ruby-align", |
|
"ruby-position", |
|
"rubyAlign", |
|
"rubyOverhang", |
|
"rubyPosition", |
|
"rules", |
|
"run", |
|
"runAdAuction", |
|
"runtime", |
|
"runtimeStyle", |
|
"rx", |
|
"ry", |
|
"s", |
|
"safari", |
|
"sameDocument", |
|
"sample", |
|
"sampleCount", |
|
"sampleCoverage", |
|
"sampleInterval", |
|
"sampleRate", |
|
"sampleType", |
|
"sampler", |
|
"samplerParameterf", |
|
"samplerParameteri", |
|
"sandbox", |
|
"save", |
|
"saveAsPDF", |
|
"saveData", |
|
"scale", |
|
"scale3d", |
|
"scale3dSelf", |
|
"scaleNonUniform", |
|
"scaleNonUniformSelf", |
|
"scaleSelf", |
|
"scheduler", |
|
"scheduling", |
|
"scheme", |
|
"scissor", |
|
"scope", |
|
"scopeName", |
|
"scoped", |
|
"screen", |
|
"screenBrightness", |
|
"screenEnabled", |
|
"screenLeft", |
|
"screenPixelToMillimeterX", |
|
"screenPixelToMillimeterY", |
|
"screenState", |
|
"screenTop", |
|
"screenX", |
|
"screenY", |
|
"screens", |
|
"scriptURL", |
|
"scripting", |
|
"scripts", |
|
"scroll", |
|
"scroll-behavior", |
|
"scroll-margin", |
|
"scroll-margin-block", |
|
"scroll-margin-block-end", |
|
"scroll-margin-block-start", |
|
"scroll-margin-bottom", |
|
"scroll-margin-inline", |
|
"scroll-margin-inline-end", |
|
"scroll-margin-inline-start", |
|
"scroll-margin-left", |
|
"scroll-margin-right", |
|
"scroll-margin-top", |
|
"scroll-padding", |
|
"scroll-padding-block", |
|
"scroll-padding-block-end", |
|
"scroll-padding-block-start", |
|
"scroll-padding-bottom", |
|
"scroll-padding-inline", |
|
"scroll-padding-inline-end", |
|
"scroll-padding-inline-start", |
|
"scroll-padding-left", |
|
"scroll-padding-right", |
|
"scroll-padding-top", |
|
"scroll-snap-align", |
|
"scroll-snap-stop", |
|
"scroll-snap-type", |
|
"scrollAmount", |
|
"scrollBehavior", |
|
"scrollBy", |
|
"scrollByLines", |
|
"scrollByPages", |
|
"scrollDelay", |
|
"scrollHeight", |
|
"scrollIntoView", |
|
"scrollIntoViewIfNeeded", |
|
"scrollLeft", |
|
"scrollLeftMax", |
|
"scrollMargin", |
|
"scrollMarginBlock", |
|
"scrollMarginBlockEnd", |
|
"scrollMarginBlockStart", |
|
"scrollMarginBottom", |
|
"scrollMarginInline", |
|
"scrollMarginInlineEnd", |
|
"scrollMarginInlineStart", |
|
"scrollMarginLeft", |
|
"scrollMarginRight", |
|
"scrollMarginTop", |
|
"scrollMaxX", |
|
"scrollMaxY", |
|
"scrollPadding", |
|
"scrollPaddingBlock", |
|
"scrollPaddingBlockEnd", |
|
"scrollPaddingBlockStart", |
|
"scrollPaddingBottom", |
|
"scrollPaddingInline", |
|
"scrollPaddingInlineEnd", |
|
"scrollPaddingInlineStart", |
|
"scrollPaddingLeft", |
|
"scrollPaddingRight", |
|
"scrollPaddingTop", |
|
"scrollRestoration", |
|
"scrollSnapAlign", |
|
"scrollSnapStop", |
|
"scrollSnapType", |
|
"scrollTo", |
|
"scrollTop", |
|
"scrollTopMax", |
|
"scrollWidth", |
|
"scrollX", |
|
"scrollY", |
|
"scrollbar-color", |
|
"scrollbar-gutter", |
|
"scrollbar-width", |
|
"scrollbar3dLightColor", |
|
"scrollbarArrowColor", |
|
"scrollbarBaseColor", |
|
"scrollbarColor", |
|
"scrollbarDarkShadowColor", |
|
"scrollbarFaceColor", |
|
"scrollbarGutter", |
|
"scrollbarHighlightColor", |
|
"scrollbarShadowColor", |
|
"scrollbarTrackColor", |
|
"scrollbarWidth", |
|
"scrollbars", |
|
"scrolling", |
|
"scrollingElement", |
|
"sctp", |
|
"sctpCauseCode", |
|
"sdp", |
|
"sdpLineNumber", |
|
"sdpMLineIndex", |
|
"sdpMid", |
|
"seal", |
|
"search", |
|
"searchBox", |
|
"searchBoxJavaBridge_", |
|
"searchParams", |
|
"second", |
|
"seconds", |
|
"sectionRowIndex", |
|
"secureConnectionStart", |
|
"securePaymentConfirmationAvailability", |
|
"security", |
|
"seed", |
|
"seek", |
|
"seekToNextFrame", |
|
"seekable", |
|
"seeking", |
|
"segments", |
|
"select", |
|
"selectAllChildren", |
|
"selectAlternateInterface", |
|
"selectAudioOutput", |
|
"selectConfiguration", |
|
"selectNode", |
|
"selectNodeContents", |
|
"selectNodes", |
|
"selectSingleNode", |
|
"selectSubString", |
|
"selectURL", |
|
"selected", |
|
"selectedIndex", |
|
"selectedOptions", |
|
"selectedStyleSheetSet", |
|
"selectedStylesheetSet", |
|
"selectedTrack", |
|
"selection", |
|
"selectionDirection", |
|
"selectionEnd", |
|
"selectionStart", |
|
"selector", |
|
"selectorText", |
|
"self", |
|
"send", |
|
"sendAsBinary", |
|
"sendBeacon", |
|
"sendFeatureReport", |
|
"sendMessage", |
|
"sendNativeMessage", |
|
"sendOrder", |
|
"sendReport", |
|
"sender", |
|
"sentAlert", |
|
"sentTimestamp", |
|
"separator", |
|
"serial", |
|
"serialNumber", |
|
"serializable", |
|
"serializeToString", |
|
"serverTiming", |
|
"service", |
|
"serviceWorker", |
|
"session", |
|
"sessionId", |
|
"sessionStorage", |
|
"sessions", |
|
"set", |
|
"setActionHandler", |
|
"setActive", |
|
"setAlpha", |
|
"setAppBadge", |
|
"setAttribute", |
|
"setAttributeNS", |
|
"setAttributeNode", |
|
"setAttributeNodeNS", |
|
"setAttributionReporting", |
|
"setBadgeBackgroundColor", |
|
"setBadgeText", |
|
"setBadgeTextColor", |
|
"setBaseAndExtent", |
|
"setBigInt64", |
|
"setBigUint64", |
|
"setBindGroup", |
|
"setBingCurrentSearchDefault", |
|
"setBlendConstant", |
|
"setCameraActive", |
|
"setCapture", |
|
"setCaptureHandleConfig", |
|
"setCodecPreferences", |
|
"setColor", |
|
"setCompositeOperation", |
|
"setConfiguration", |
|
"setConsumer", |
|
"setCurrentTime", |
|
"setCustomValidity", |
|
"setData", |
|
"setDate", |
|
"setDragImage", |
|
"setEnabled", |
|
"setEnd", |
|
"setEndAfter", |
|
"setEndBefore", |
|
"setEndPoint", |
|
"setExpires", |
|
"setFillColor", |
|
"setFilterRes", |
|
"setFloat16", |
|
"setFloat32", |
|
"setFloat64", |
|
"setFloatValue", |
|
"setFocusBehavior", |
|
"setFormValue", |
|
"setFromBase64", |
|
"setFromHex", |
|
"setFullYear", |
|
"setHTMLUnsafe", |
|
"setHeaderExtensionsToNegotiate", |
|
"setHeaderValue", |
|
"setHours", |
|
"setIcon", |
|
"setIdentityProvider", |
|
"setImmediate", |
|
"setIndexBuffer", |
|
"setInt16", |
|
"setInt32", |
|
"setInt8", |
|
"setInterval", |
|
"setItem", |
|
"setKeyframes", |
|
"setLineCap", |
|
"setLineDash", |
|
"setLineJoin", |
|
"setLineWidth", |
|
"setLiveSeekableRange", |
|
"setLocalDescription", |
|
"setMatrix", |
|
"setMatrixValue", |
|
"setMediaKeys", |
|
"setMicrophoneActive", |
|
"setMilliseconds", |
|
"setMinutes", |
|
"setMiterLimit", |
|
"setMonth", |
|
"setNamedItem", |
|
"setNamedItemNS", |
|
"setNonUserCodeExceptions", |
|
"setOrientToAngle", |
|
"setOrientToAuto", |
|
"setOrientation", |
|
"setOverrideHistoryNavigationMode", |
|
"setPaint", |
|
"setParameter", |
|
"setParameters", |
|
"setPathData", |
|
"setPeriodicWave", |
|
"setPipeline", |
|
"setPointerCapture", |
|
"setPopup", |
|
"setPosition", |
|
"setPositionState", |
|
"setPreference", |
|
"setPriority", |
|
"setPrivateToken", |
|
"setProperty", |
|
"setPrototypeOf", |
|
"setRGBColor", |
|
"setRGBColorICCColor", |
|
"setRadius", |
|
"setRangeText", |
|
"setRemoteDescription", |
|
"setReportEventDataForAutomaticBeacons", |
|
"setRequestHeader", |
|
"setResizable", |
|
"setResourceTimingBufferSize", |
|
"setRotate", |
|
"setScale", |
|
"setScissorRect", |
|
"setSeconds", |
|
"setSelectionRange", |
|
"setServerCertificate", |
|
"setShadow", |
|
"setSharedStorageContext", |
|
"setSignals", |
|
"setSinkId", |
|
"setSkewX", |
|
"setSkewY", |
|
"setStart", |
|
"setStartAfter", |
|
"setStartBefore", |
|
"setStatus", |
|
"setStdDeviation", |
|
"setStencilReference", |
|
"setStreams", |
|
"setStrictMode", |
|
"setStringValue", |
|
"setStrokeColor", |
|
"setSuggestResult", |
|
"setTargetAtTime", |
|
"setTargetValueAtTime", |
|
"setTime", |
|
"setTimeout", |
|
"setTitle", |
|
"setTransform", |
|
"setTranslate", |
|
"setUTCDate", |
|
"setUTCFullYear", |
|
"setUTCHours", |
|
"setUTCMilliseconds", |
|
"setUTCMinutes", |
|
"setUTCMonth", |
|
"setUTCSeconds", |
|
"setUint16", |
|
"setUint32", |
|
"setUint8", |
|
"setUninstallURL", |
|
"setUpdateUrlData", |
|
"setUri", |
|
"setValidity", |
|
"setValueAtTime", |
|
"setValueCurveAtTime", |
|
"setVariable", |
|
"setVelocity", |
|
"setVersion", |
|
"setVertexBuffer", |
|
"setViewport", |
|
"setYear", |
|
"setZoom", |
|
"setZoomSettings", |
|
"settingName", |
|
"settingValue", |
|
"sex", |
|
"shaderLocation", |
|
"shaderSource", |
|
"shadowBlur", |
|
"shadowColor", |
|
"shadowOffsetX", |
|
"shadowOffsetY", |
|
"shadowRoot", |
|
"shadowRootClonable", |
|
"shadowRootDelegatesFocus", |
|
"shadowRootMode", |
|
"shadowRootSerializable", |
|
"shape", |
|
"shape-image-threshold", |
|
"shape-margin", |
|
"shape-outside", |
|
"shape-rendering", |
|
"shapeImageThreshold", |
|
"shapeMargin", |
|
"shapeOutside", |
|
"shapeRendering", |
|
"share", |
|
"sharedContext", |
|
"sharedStorage", |
|
"sharedStorageWritable", |
|
"sheet", |
|
"shift", |
|
"shiftKey", |
|
"shiftLeft", |
|
"shippingAddress", |
|
"shippingOption", |
|
"shippingType", |
|
"show", |
|
"showDirectoryPicker", |
|
"showHelp", |
|
"showModal", |
|
"showModalDialog", |
|
"showModelessDialog", |
|
"showNotification", |
|
"showOpenFilePicker", |
|
"showPicker", |
|
"showPopover", |
|
"showSaveFilePicker", |
|
"sidebar", |
|
"sidebarAction", |
|
"sign", |
|
"signal", |
|
"signalAllAcceptedCredentials", |
|
"signalCurrentUserDetails", |
|
"signalUnknownCredential", |
|
"signalingState", |
|
"signature", |
|
"silent", |
|
"sin", |
|
"singleNodeValue", |
|
"sinh", |
|
"sinkId", |
|
"sittingToStandingTransform", |
|
"size", |
|
"sizeAdjust", |
|
"sizeToContent", |
|
"sizeX", |
|
"sizeZ", |
|
"sizes", |
|
"skewX", |
|
"skewXSelf", |
|
"skewY", |
|
"skewYSelf", |
|
"skipTransition", |
|
"skipped", |
|
"slice", |
|
"slope", |
|
"slot", |
|
"slotAssignment", |
|
"small", |
|
"smil", |
|
"smooth", |
|
"smoothingTimeConstant", |
|
"snapTargetBlock", |
|
"snapTargetInline", |
|
"snapToLines", |
|
"snapshotItem", |
|
"snapshotLength", |
|
"some", |
|
"sort", |
|
"sortingCode", |
|
"source", |
|
"sourceBuffer", |
|
"sourceBuffers", |
|
"sourceCapabilities", |
|
"sourceCharPosition", |
|
"sourceElement", |
|
"sourceFile", |
|
"sourceFunctionName", |
|
"sourceIndex", |
|
"sourceLanguage", |
|
"sourceMap", |
|
"sourceURL", |
|
"sources", |
|
"spacing", |
|
"span", |
|
"speak", |
|
"speakAs", |
|
"speaking", |
|
"species", |
|
"specified", |
|
"specularConstant", |
|
"specularExponent", |
|
"speechSynthesis", |
|
"speed", |
|
"speedOfSound", |
|
"spellcheck", |
|
"sphericalHarmonicsCoefficients", |
|
"splice", |
|
"split", |
|
"splitText", |
|
"spreadMethod", |
|
"sqrt", |
|
"src", |
|
"srcElement", |
|
"srcFactor", |
|
"srcFilter", |
|
"srcObject", |
|
"srcUrn", |
|
"srcdoc", |
|
"srclang", |
|
"srcset", |
|
"stack", |
|
"stackTraceLimit", |
|
"stacktrace", |
|
"stageParameters", |
|
"standalone", |
|
"standby", |
|
"start", |
|
"startContainer", |
|
"startE", |
|
"startIce", |
|
"startLoadTime", |
|
"startMessages", |
|
"startNotifications", |
|
"startOffset", |
|
"startProfiling", |
|
"startRendering", |
|
"startShark", |
|
"startTime", |
|
"startViewTransition", |
|
"startsWith", |
|
"state", |
|
"states", |
|
"stats", |
|
"status", |
|
"statusCode", |
|
"statusMessage", |
|
"statusText", |
|
"statusbar", |
|
"stdDeviationX", |
|
"stdDeviationY", |
|
"stencilBack", |
|
"stencilClearValue", |
|
"stencilFront", |
|
"stencilFunc", |
|
"stencilFuncSeparate", |
|
"stencilLoadOp", |
|
"stencilMask", |
|
"stencilMaskSeparate", |
|
"stencilOp", |
|
"stencilOpSeparate", |
|
"stencilReadMask", |
|
"stencilReadOnly", |
|
"stencilStoreOp", |
|
"stencilWriteMask", |
|
"step", |
|
"stepDown", |
|
"stepMismatch", |
|
"stepMode", |
|
"stepUp", |
|
"sticky", |
|
"stitchTiles", |
|
"stop", |
|
"stop-color", |
|
"stop-opacity", |
|
"stopColor", |
|
"stopImmediatePropagation", |
|
"stopNotifications", |
|
"stopOpacity", |
|
"stopProfiling", |
|
"stopPropagation", |
|
"stopShark", |
|
"stopped", |
|
"storage", |
|
"storageArea", |
|
"storageBuckets", |
|
"storageName", |
|
"storageStatus", |
|
"storageTexture", |
|
"store", |
|
"storeOp", |
|
"storeSiteSpecificTrackingException", |
|
"storeWebWideTrackingException", |
|
"stpVersion", |
|
"stream", |
|
"streamErrorCode", |
|
"streams", |
|
"stretch", |
|
"strike", |
|
"string", |
|
"stringValue", |
|
"stringify", |
|
"stripIndexFormat", |
|
"stroke", |
|
"stroke-dasharray", |
|
"stroke-dashoffset", |
|
"stroke-linecap", |
|
"stroke-linejoin", |
|
"stroke-miterlimit", |
|
"stroke-opacity", |
|
"stroke-width", |
|
"strokeDasharray", |
|
"strokeDashoffset", |
|
"strokeLinecap", |
|
"strokeLinejoin", |
|
"strokeMiterlimit", |
|
"strokeOpacity", |
|
"strokeRect", |
|
"strokeStyle", |
|
"strokeText", |
|
"strokeWidth", |
|
"structuredClone", |
|
"style", |
|
"styleAndLayoutStart", |
|
"styleFloat", |
|
"styleMap", |
|
"styleMedia", |
|
"styleSheet", |
|
"styleSheetSets", |
|
"styleSheets", |
|
"styleset", |
|
"stylistic", |
|
"sub", |
|
"subarray", |
|
"subgroupMaxSize", |
|
"subgroupMinSize", |
|
"subject", |
|
"submit", |
|
"submitFrame", |
|
"submitter", |
|
"subscribe", |
|
"substr", |
|
"substring", |
|
"substringData", |
|
"subtle", |
|
"subtree", |
|
"suffix", |
|
"suffixes", |
|
"sumPrecise", |
|
"summarize", |
|
"summarizeStreaming", |
|
"summary", |
|
"sup", |
|
"supported", |
|
"supportedContentEncodings", |
|
"supportedEntryTypes", |
|
"supportedValuesOf", |
|
"supports", |
|
"supportsFiber", |
|
"supportsSession", |
|
"supportsText", |
|
"suppressed", |
|
"surfaceScale", |
|
"surroundContents", |
|
"suspend", |
|
"suspendRedraw", |
|
"svb", |
|
"svh", |
|
"svi", |
|
"svmax", |
|
"svmin", |
|
"svw", |
|
"swapCache", |
|
"swapNode", |
|
"swash", |
|
"sweepFlag", |
|
"switchMap", |
|
"symbols", |
|
"symmetricDifference", |
|
"sync", |
|
"syntax", |
|
"sysexEnabled", |
|
"system", |
|
"systemCode", |
|
"systemId", |
|
"systemLanguage", |
|
"systemXDPI", |
|
"systemYDPI", |
|
"tBodies", |
|
"tFoot", |
|
"tHead", |
|
"tab", |
|
"tab-size", |
|
"tabId", |
|
"tabIds", |
|
"tabIndex", |
|
"tabSize", |
|
"table", |
|
"table-layout", |
|
"tableLayout", |
|
"tableValues", |
|
"tabs", |
|
"tag", |
|
"tagName", |
|
"tagUrn", |
|
"tags", |
|
"taintEnabled", |
|
"take", |
|
"takePhoto", |
|
"takeRecords", |
|
"takeUntil", |
|
"tan", |
|
"tangentialPressure", |
|
"tanh", |
|
"target", |
|
"targetAddressSpace", |
|
"targetElement", |
|
"targetLanguage", |
|
"targetRayMode", |
|
"targetRaySpace", |
|
"targetTouches", |
|
"targetURL", |
|
"targetX", |
|
"targetY", |
|
"targets", |
|
"tcpType", |
|
"tee", |
|
"tel", |
|
"telemetry", |
|
"terminate", |
|
"test", |
|
"texImage2D", |
|
"texImage3D", |
|
"texParameterf", |
|
"texParameteri", |
|
"texStorage2D", |
|
"texStorage3D", |
|
"texSubImage2D", |
|
"texSubImage3D", |
|
"text", |
|
"text-align", |
|
"text-align-last", |
|
"text-anchor", |
|
"text-combine-upright", |
|
"text-decoration", |
|
"text-decoration-color", |
|
"text-decoration-line", |
|
"text-decoration-skip-ink", |
|
"text-decoration-style", |
|
"text-decoration-thickness", |
|
"text-emphasis", |
|
"text-emphasis-color", |
|
"text-emphasis-position", |
|
"text-emphasis-style", |
|
"text-indent", |
|
"text-justify", |
|
"text-orientation", |
|
"text-overflow", |
|
"text-rendering", |
|
"text-shadow", |
|
"text-transform", |
|
"text-underline-offset", |
|
"text-underline-position", |
|
"text-wrap", |
|
"text-wrap-mode", |
|
"text-wrap-style", |
|
"textAlign", |
|
"textAlignLast", |
|
"textAnchor", |
|
"textAutospace", |
|
"textBaseline", |
|
"textCombineUpright", |
|
"textContent", |
|
"textDecoration", |
|
"textDecorationBlink", |
|
"textDecorationColor", |
|
"textDecorationInset", |
|
"textDecorationLine", |
|
"textDecorationLineThrough", |
|
"textDecorationNone", |
|
"textDecorationOverline", |
|
"textDecorationSkipInk", |
|
"textDecorationStyle", |
|
"textDecorationThickness", |
|
"textDecorationUnderline", |
|
"textEmphasis", |
|
"textEmphasisColor", |
|
"textEmphasisPosition", |
|
"textEmphasisStyle", |
|
"textIndent", |
|
"textJustify", |
|
"textJustifyTrim", |
|
"textKashida", |
|
"textKashidaSpace", |
|
"textLength", |
|
"textOrientation", |
|
"textOverflow", |
|
"textRendering", |
|
"textShadow", |
|
"textTracks", |
|
"textTransform", |
|
"textUnderlineOffset", |
|
"textUnderlinePosition", |
|
"textWrap", |
|
"textWrapMode", |
|
"textWrapStyle", |
|
"texture", |
|
"theme", |
|
"then", |
|
"threadId", |
|
"threshold", |
|
"thresholds", |
|
"throwIfAborted", |
|
"tiltX", |
|
"tiltY", |
|
"time", |
|
"timeEnd", |
|
"timeLog", |
|
"timeOrigin", |
|
"timeRemaining", |
|
"timeStamp", |
|
"timeStyle", |
|
"timeZone", |
|
"timeZoneName", |
|
"timecode", |
|
"timeline", |
|
"timelineTime", |
|
"timeout", |
|
"timestamp", |
|
"timestampOffset", |
|
"timestampWrites", |
|
"timing", |
|
"title", |
|
"titlebarAreaRect", |
|
"tlsChannelId", |
|
"to", |
|
"toArray", |
|
"toBase64", |
|
"toBlob", |
|
"toDataURL", |
|
"toDateString", |
|
"toElement", |
|
"toExponential", |
|
"toFixed", |
|
"toFloat32Array", |
|
"toFloat64Array", |
|
"toGMTString", |
|
"toHex", |
|
"toISOString", |
|
"toJSON", |
|
"toLocaleDateString", |
|
"toLocaleFormat", |
|
"toLocaleLowerCase", |
|
"toLocaleString", |
|
"toLocaleTimeString", |
|
"toLocaleUpperCase", |
|
"toLowerCase", |
|
"toMatrix", |
|
"toMethod", |
|
"toPrecision", |
|
"toPrimitive", |
|
"toReversed", |
|
"toSdp", |
|
"toSorted", |
|
"toSource", |
|
"toSpliced", |
|
"toStaticHTML", |
|
"toString", |
|
"toStringTag", |
|
"toSum", |
|
"toTemporalInstant", |
|
"toTimeString", |
|
"toUTCString", |
|
"toUpperCase", |
|
"toWellFormed", |
|
"toggle", |
|
"toggleAttribute", |
|
"toggleLongPressEnabled", |
|
"togglePopover", |
|
"toggleReaderMode", |
|
"token", |
|
"tone", |
|
"toneBuffer", |
|
"tooLong", |
|
"tooShort", |
|
"toolbar", |
|
"top", |
|
"topMargin", |
|
"topSites", |
|
"topology", |
|
"total", |
|
"totalFrameDelay", |
|
"totalFrames", |
|
"totalFramesDuration", |
|
"totalVideoFrames", |
|
"touch-action", |
|
"touchAction", |
|
"touched", |
|
"touches", |
|
"trace", |
|
"track", |
|
"trackVisibility", |
|
"trackedAnchors", |
|
"tracks", |
|
"tran", |
|
"transaction", |
|
"transactions", |
|
"transceiver", |
|
"transfer", |
|
"transferControlToOffscreen", |
|
"transferFromImageBitmap", |
|
"transferImageBitmap", |
|
"transferIn", |
|
"transferOut", |
|
"transferSize", |
|
"transferToFixedLength", |
|
"transferToImageBitmap", |
|
"transform", |
|
"transform-box", |
|
"transform-origin", |
|
"transform-style", |
|
"transformBox", |
|
"transformFeedbackVaryings", |
|
"transformOrigin", |
|
"transformPoint", |
|
"transformString", |
|
"transformStyle", |
|
"transformToDocument", |
|
"transformToFragment", |
|
"transition", |
|
"transition-behavior", |
|
"transition-delay", |
|
"transition-duration", |
|
"transition-property", |
|
"transition-timing-function", |
|
"transitionBehavior", |
|
"transitionDelay", |
|
"transitionDuration", |
|
"transitionProperty", |
|
"transitionTimingFunction", |
|
"translate", |
|
"translateSelf", |
|
"translateStreaming", |
|
"translationX", |
|
"translationY", |
|
"transport", |
|
"traverseTo", |
|
"trim", |
|
"trimEnd", |
|
"trimLeft", |
|
"trimRight", |
|
"trimStart", |
|
"trueSpeed", |
|
"trunc", |
|
"truncate", |
|
"trustedTypes", |
|
"try", |
|
"turn", |
|
"twist", |
|
"type", |
|
"typeDetail", |
|
"typeMismatch", |
|
"typeMustMatch", |
|
"types", |
|
"u2f", |
|
"ubound", |
|
"uint16", |
|
"uint32", |
|
"uint8", |
|
"uint8Clamped", |
|
"unadjustedMovement", |
|
"unclippedDepth", |
|
"unconfigure", |
|
"undefined", |
|
"underlineStyle", |
|
"underlineThickness", |
|
"unescape", |
|
"uneval", |
|
"ungroup", |
|
"unicode", |
|
"unicode-bidi", |
|
"unicodeBidi", |
|
"unicodeRange", |
|
"unicodeSets", |
|
"uniform1f", |
|
"uniform1fv", |
|
"uniform1i", |
|
"uniform1iv", |
|
"uniform1ui", |
|
"uniform1uiv", |
|
"uniform2f", |
|
"uniform2fv", |
|
"uniform2i", |
|
"uniform2iv", |
|
"uniform2ui", |
|
"uniform2uiv", |
|
"uniform3f", |
|
"uniform3fv", |
|
"uniform3i", |
|
"uniform3iv", |
|
"uniform3ui", |
|
"uniform3uiv", |
|
"uniform4f", |
|
"uniform4fv", |
|
"uniform4i", |
|
"uniform4iv", |
|
"uniform4ui", |
|
"uniform4uiv", |
|
"uniformBlockBinding", |
|
"uniformMatrix2fv", |
|
"uniformMatrix2x3fv", |
|
"uniformMatrix2x4fv", |
|
"uniformMatrix3fv", |
|
"uniformMatrix3x2fv", |
|
"uniformMatrix3x4fv", |
|
"uniformMatrix4fv", |
|
"uniformMatrix4x2fv", |
|
"uniformMatrix4x3fv", |
|
"uninstallSelf", |
|
"union", |
|
"unique", |
|
"uniqueID", |
|
"uniqueNumber", |
|
"unit", |
|
"unitType", |
|
"units", |
|
"unloadEventEnd", |
|
"unloadEventStart", |
|
"unlock", |
|
"unmap", |
|
"unmount", |
|
"unobserve", |
|
"unpackColorSpace", |
|
"unpause", |
|
"unpauseAnimations", |
|
"unreadCount", |
|
"unregister", |
|
"unregisterContentHandler", |
|
"unregisterProtocolHandler", |
|
"unscopables", |
|
"unselectable", |
|
"unshift", |
|
"unsubscribe", |
|
"unsuspendRedraw", |
|
"unsuspendRedrawAll", |
|
"unwatch", |
|
"unwrapKey", |
|
"upDegrees", |
|
"upX", |
|
"upY", |
|
"upZ", |
|
"update", |
|
"updateAdInterestGroups", |
|
"updateCallbackDone", |
|
"updateCharacterBounds", |
|
"updateCommands", |
|
"updateControlBounds", |
|
"updateCurrentEntry", |
|
"updateIce", |
|
"updateInkTrailStartPoint", |
|
"updateInterval", |
|
"updatePlaybackRate", |
|
"updateRangeEnd", |
|
"updateRangeStart", |
|
"updateRenderState", |
|
"updateSelection", |
|
"updateSelectionBounds", |
|
"updateSettings", |
|
"updateText", |
|
"updateTiming", |
|
"updateViaCache", |
|
"updateWith", |
|
"updated", |
|
"updating", |
|
"upgrade", |
|
"upload", |
|
"uploadTotal", |
|
"uploaded", |
|
"upper", |
|
"upperBound", |
|
"upperOpen", |
|
"uri", |
|
"url", |
|
"urn", |
|
"urns", |
|
"usage", |
|
"usages", |
|
"usb", |
|
"usbVersionMajor", |
|
"usbVersionMinor", |
|
"usbVersionSubminor", |
|
"use", |
|
"useCurrentView", |
|
"useMap", |
|
"useProgram", |
|
"usedSpace", |
|
"user-select", |
|
"userActivation", |
|
"userAgent", |
|
"userAgentAllowsProtocol", |
|
"userAgentData", |
|
"userChoice", |
|
"userHandle", |
|
"userHint", |
|
"userInitiated", |
|
"userLanguage", |
|
"userSelect", |
|
"userState", |
|
"userVisibleOnly", |
|
"username", |
|
"usernameFragment", |
|
"utterance", |
|
"uuid", |
|
"v8BreakIterator", |
|
"vAlign", |
|
"vLink", |
|
"valid", |
|
"validate", |
|
"validateProgram", |
|
"validationMessage", |
|
"validity", |
|
"value", |
|
"valueAsDate", |
|
"valueAsNumber", |
|
"valueAsString", |
|
"valueInSpecifiedUnits", |
|
"valueMissing", |
|
"valueOf", |
|
"valueText", |
|
"valueType", |
|
"values", |
|
"variable", |
|
"variant", |
|
"variationSettings", |
|
"vb", |
|
"vector-effect", |
|
"vectorEffect", |
|
"velocityAngular", |
|
"velocityExpansion", |
|
"velocityX", |
|
"velocityY", |
|
"vendor", |
|
"vendorId", |
|
"vendorSub", |
|
"verify", |
|
"version", |
|
"vertex", |
|
"vertexAttrib1f", |
|
"vertexAttrib1fv", |
|
"vertexAttrib2f", |
|
"vertexAttrib2fv", |
|
"vertexAttrib3f", |
|
"vertexAttrib3fv", |
|
"vertexAttrib4f", |
|
"vertexAttrib4fv", |
|
"vertexAttribDivisor", |
|
"vertexAttribDivisorANGLE", |
|
"vertexAttribI4i", |
|
"vertexAttribI4iv", |
|
"vertexAttribI4ui", |
|
"vertexAttribI4uiv", |
|
"vertexAttribIPointer", |
|
"vertexAttribPointer", |
|
"vertical", |
|
"vertical-align", |
|
"verticalAlign", |
|
"verticalOverflow", |
|
"vh", |
|
"vi", |
|
"vibrate", |
|
"vibrationActuator", |
|
"videoBitsPerSecond", |
|
"videoHeight", |
|
"videoTracks", |
|
"videoWidth", |
|
"view", |
|
"viewBox", |
|
"viewBoxString", |
|
"viewDimension", |
|
"viewFormats", |
|
"viewTarget", |
|
"viewTargetString", |
|
"viewTransition", |
|
"viewTransitionClass", |
|
"viewTransitionName", |
|
"viewport", |
|
"viewportAnchorX", |
|
"viewportAnchorY", |
|
"viewportElement", |
|
"views", |
|
"violatedDirective", |
|
"virtualKeyboard", |
|
"virtualKeyboardPolicy", |
|
"visibility", |
|
"visibilityState", |
|
"visible", |
|
"visibleRect", |
|
"visualViewport", |
|
"vlinkColor", |
|
"vmax", |
|
"vmin", |
|
"voice", |
|
"voiceURI", |
|
"volume", |
|
"vrml", |
|
"vspace", |
|
"vw", |
|
"w", |
|
"wait", |
|
"waitAsync", |
|
"waitSync", |
|
"waiting", |
|
"wake", |
|
"wakeLock", |
|
"wand", |
|
"warmup", |
|
"warn", |
|
"wasAlternateProtocolAvailable", |
|
"wasClean", |
|
"wasDiscarded", |
|
"wasFetchedViaSpdy", |
|
"wasNpnNegotiated", |
|
"watch", |
|
"watchAvailability", |
|
"watchPosition", |
|
"webNavigation", |
|
"webRequest", |
|
"webdriver", |
|
"webkitAddKey", |
|
"webkitAlignContent", |
|
"webkitAlignItems", |
|
"webkitAlignSelf", |
|
"webkitAnimation", |
|
"webkitAnimationDelay", |
|
"webkitAnimationDirection", |
|
"webkitAnimationDuration", |
|
"webkitAnimationFillMode", |
|
"webkitAnimationIterationCount", |
|
"webkitAnimationName", |
|
"webkitAnimationPlayState", |
|
"webkitAnimationTimingFunction", |
|
"webkitAppearance", |
|
"webkitAudioContext", |
|
"webkitAudioDecodedByteCount", |
|
"webkitAudioPannerNode", |
|
"webkitBackfaceVisibility", |
|
"webkitBackground", |
|
"webkitBackgroundAttachment", |
|
"webkitBackgroundClip", |
|
"webkitBackgroundColor", |
|
"webkitBackgroundImage", |
|
"webkitBackgroundOrigin", |
|
"webkitBackgroundPosition", |
|
"webkitBackgroundPositionX", |
|
"webkitBackgroundPositionY", |
|
"webkitBackgroundRepeat", |
|
"webkitBackgroundSize", |
|
"webkitBackingStorePixelRatio", |
|
"webkitBorderBottomLeftRadius", |
|
"webkitBorderBottomRightRadius", |
|
"webkitBorderImage", |
|
"webkitBorderImageOutset", |
|
"webkitBorderImageRepeat", |
|
"webkitBorderImageSlice", |
|
"webkitBorderImageSource", |
|
"webkitBorderImageWidth", |
|
"webkitBorderRadius", |
|
"webkitBorderTopLeftRadius", |
|
"webkitBorderTopRightRadius", |
|
"webkitBoxAlign", |
|
"webkitBoxDirection", |
|
"webkitBoxFlex", |
|
"webkitBoxOrdinalGroup", |
|
"webkitBoxOrient", |
|
"webkitBoxPack", |
|
"webkitBoxShadow", |
|
"webkitBoxSizing", |
|
"webkitCancelAnimationFrame", |
|
"webkitCancelFullScreen", |
|
"webkitCancelKeyRequest", |
|
"webkitCancelRequestAnimationFrame", |
|
"webkitClearResourceTimings", |
|
"webkitClipPath", |
|
"webkitClosedCaptionsVisible", |
|
"webkitConvertPointFromNodeToPage", |
|
"webkitConvertPointFromPageToNode", |
|
"webkitCreateShadowRoot", |
|
"webkitCurrentFullScreenElement", |
|
"webkitCurrentPlaybackTargetIsWireless", |
|
"webkitDecodedFrameCount", |
|
"webkitDirectionInvertedFromDevice", |
|
"webkitDisplayingFullscreen", |
|
"webkitDroppedFrameCount", |
|
"webkitEnterFullScreen", |
|
"webkitEnterFullscreen", |
|
"webkitEntries", |
|
"webkitExitFullScreen", |
|
"webkitExitFullscreen", |
|
"webkitExitPointerLock", |
|
"webkitFilter", |
|
"webkitFlex", |
|
"webkitFlexBasis", |
|
"webkitFlexDirection", |
|
"webkitFlexFlow", |
|
"webkitFlexGrow", |
|
"webkitFlexShrink", |
|
"webkitFlexWrap", |
|
"webkitFontFeatureSettings", |
|
"webkitFullScreenKeyboardInputAllowed", |
|
"webkitFullscreenElement", |
|
"webkitFullscreenEnabled", |
|
"webkitGenerateKeyRequest", |
|
"webkitGetAsEntry", |
|
"webkitGetDatabaseNames", |
|
"webkitGetEntries", |
|
"webkitGetEntriesByName", |
|
"webkitGetEntriesByType", |
|
"webkitGetFlowByName", |
|
"webkitGetGamepads", |
|
"webkitGetImageDataHD", |
|
"webkitGetNamedFlows", |
|
"webkitGetRegionFlowRanges", |
|
"webkitGetUserMedia", |
|
"webkitHasClosedCaptions", |
|
"webkitHidden", |
|
"webkitIDBCursor", |
|
"webkitIDBDatabase", |
|
"webkitIDBDatabaseError", |
|
"webkitIDBDatabaseException", |
|
"webkitIDBFactory", |
|
"webkitIDBIndex", |
|
"webkitIDBKeyRange", |
|
"webkitIDBObjectStore", |
|
"webkitIDBRequest", |
|
"webkitIDBTransaction", |
|
"webkitImageSmoothingEnabled", |
|
"webkitIndexedDB", |
|
"webkitInitMessageEvent", |
|
"webkitIsFullScreen", |
|
"webkitJustifyContent", |
|
"webkitKeys", |
|
"webkitLineClamp", |
|
"webkitLineDashOffset", |
|
"webkitLockOrientation", |
|
"webkitMask", |
|
"webkitMaskClip", |
|
"webkitMaskComposite", |
|
"webkitMaskImage", |
|
"webkitMaskOrigin", |
|
"webkitMaskPosition", |
|
"webkitMaskPositionX", |
|
"webkitMaskPositionY", |
|
"webkitMaskRepeat", |
|
"webkitMaskSize", |
|
"webkitMatchesSelector", |
|
"webkitMediaStream", |
|
"webkitNotifications", |
|
"webkitOfflineAudioContext", |
|
"webkitOrder", |
|
"webkitOrientation", |
|
"webkitPeerConnection00", |
|
"webkitPersistentStorage", |
|
"webkitPerspective", |
|
"webkitPerspectiveOrigin", |
|
"webkitPointerLockElement", |
|
"webkitPostMessage", |
|
"webkitPreservesPitch", |
|
"webkitPutImageDataHD", |
|
"webkitRTCPeerConnection", |
|
"webkitRegionOverset", |
|
"webkitRelativePath", |
|
"webkitRequestAnimationFrame", |
|
"webkitRequestFileSystem", |
|
"webkitRequestFullScreen", |
|
"webkitRequestFullscreen", |
|
"webkitRequestPointerLock", |
|
"webkitResolveLocalFileSystemURL", |
|
"webkitSetMediaKeys", |
|
"webkitSetResourceTimingBufferSize", |
|
"webkitShadowRoot", |
|
"webkitShowPlaybackTargetPicker", |
|
"webkitSlice", |
|
"webkitSpeechGrammar", |
|
"webkitSpeechGrammarList", |
|
"webkitSpeechRecognition", |
|
"webkitSpeechRecognitionError", |
|
"webkitSpeechRecognitionEvent", |
|
"webkitStorageInfo", |
|
"webkitSupportsFullscreen", |
|
"webkitTemporaryStorage", |
|
"webkitTextFillColor", |
|
"webkitTextSecurity", |
|
"webkitTextSizeAdjust", |
|
"webkitTextStroke", |
|
"webkitTextStrokeColor", |
|
"webkitTextStrokeWidth", |
|
"webkitTransform", |
|
"webkitTransformOrigin", |
|
"webkitTransformStyle", |
|
"webkitTransition", |
|
"webkitTransitionDelay", |
|
"webkitTransitionDuration", |
|
"webkitTransitionProperty", |
|
"webkitTransitionTimingFunction", |
|
"webkitURL", |
|
"webkitUnlockOrientation", |
|
"webkitUserSelect", |
|
"webkitVideoDecodedByteCount", |
|
"webkitVisibilityState", |
|
"webkitWirelessVideoPlaybackDisabled", |
|
"webkitdirectory", |
|
"webkitdropzone", |
|
"webstore", |
|
"weekday", |
|
"weeks", |
|
"weight", |
|
"wgslLanguageFeatures", |
|
"whatToShow", |
|
"wheelDelta", |
|
"wheelDeltaX", |
|
"wheelDeltaY", |
|
"when", |
|
"whenDefined", |
|
"which", |
|
"white-space", |
|
"white-space-collapse", |
|
"whiteSpace", |
|
"whiteSpaceCollapse", |
|
"wholeText", |
|
"widows", |
|
"width", |
|
"will-change", |
|
"willChange", |
|
"willValidate", |
|
"window", |
|
"windowAttribution", |
|
"windowControlsOverlay", |
|
"windowId", |
|
"windowIds", |
|
"windows", |
|
"with", |
|
"withCredentials", |
|
"withResolvers", |
|
"word-break", |
|
"word-spacing", |
|
"word-wrap", |
|
"wordBreak", |
|
"wordSpacing", |
|
"wordWrap", |
|
"workerCacheLookupStart", |
|
"workerFinalSourceType", |
|
"workerMatchedSourceType", |
|
"workerRouterEvaluationStart", |
|
"workerStart", |
|
"worklet", |
|
"wow64", |
|
"wrap", |
|
"wrapKey", |
|
"writable", |
|
"writableAuxiliaries", |
|
"write", |
|
"writeBuffer", |
|
"writeMask", |
|
"writeText", |
|
"writeTexture", |
|
"writeTimestamp", |
|
"writeValue", |
|
"writeValueWithResponse", |
|
"writeValueWithoutResponse", |
|
"writeWithoutResponse", |
|
"writeln", |
|
"writing-mode", |
|
"writingMode", |
|
"writingSuggestions", |
|
"x", |
|
"x1", |
|
"x2", |
|
"xChannelSelector", |
|
"xmlEncoding", |
|
"xmlStandalone", |
|
"xmlVersion", |
|
"xmlbase", |
|
"xmllang", |
|
"xmlspace", |
|
"xor", |
|
"xr", |
|
"y", |
|
"y1", |
|
"y2", |
|
"yChannelSelector", |
|
"yandex", |
|
"year", |
|
"years", |
|
"yield", |
|
"z", |
|
"z-index", |
|
"zIndex", |
|
"zoom", |
|
"zoomAndPan", |
|
"zoomLevel", |
|
"zoomRectScreen", |
|
]; |
|
|
|
/*********************************************************************** |
|
|
|
A JavaScript tokenizer / parser / beautifier / compressor. |
|
https://github.com/mishoo/UglifyJS2 |
|
|
|
-------------------------------- (C) --------------------------------- |
|
|
|
Author: Mihai Bazon |
|
<mihai.bazon@gmail.com> |
|
http://mihai.bazon.net/blog |
|
|
|
Distributed under the BSD license: |
|
|
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions |
|
are met: |
|
|
|
* Redistributions of source code must retain the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer. |
|
|
|
* Redistributions in binary form must reproduce the above |
|
copyright notice, this list of conditions and the following |
|
disclaimer in the documentation and/or other materials |
|
provided with the distribution. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
SUCH DAMAGE. |
|
|
|
***********************************************************************/ |
|
|
|
function find_builtins(reserved) { |
|
domprops.forEach(add); |
|
|
|
// Compatibility fix for some standard defined globals not defined on every js environment |
|
var new_globals = ["Symbol", "Map", "Promise", "Proxy", "Reflect", "Set", "WeakMap", "WeakSet"]; |
|
var objects = {}; |
|
var global_ref = typeof global === "object" ? global : self; |
|
|
|
new_globals.forEach(function (new_global) { |
|
objects[new_global] = global_ref[new_global] || function() {}; |
|
}); |
|
|
|
[ |
|
"null", |
|
"true", |
|
"false", |
|
"NaN", |
|
"Infinity", |
|
"-Infinity", |
|
"undefined", |
|
].forEach(add); |
|
[ Object, Array, Function, Number, |
|
String, Boolean, Error, Math, |
|
Date, RegExp, objects.Symbol, ArrayBuffer, |
|
DataView, decodeURI, decodeURIComponent, |
|
encodeURI, encodeURIComponent, eval, EvalError, |
|
Float32Array, Float64Array, Int8Array, Int16Array, |
|
Int32Array, isFinite, isNaN, JSON, objects.Map, parseFloat, |
|
parseInt, objects.Promise, objects.Proxy, RangeError, ReferenceError, |
|
objects.Reflect, objects.Set, SyntaxError, TypeError, Uint8Array, |
|
Uint8ClampedArray, Uint16Array, Uint32Array, URIError, |
|
objects.WeakMap, objects.WeakSet |
|
].forEach(function(ctor) { |
|
Object.getOwnPropertyNames(ctor).map(add); |
|
if (ctor.prototype) { |
|
Object.getOwnPropertyNames(ctor.prototype).map(add); |
|
} |
|
}); |
|
function add(name) { |
|
reserved.add(name); |
|
} |
|
} |
|
|
|
function reserve_quoted_keys(ast, reserved) { |
|
function add(name) { |
|
push_uniq(reserved, name); |
|
} |
|
|
|
ast.walk(new TreeWalker(function(node) { |
|
if (node instanceof AST_ObjectKeyVal && node.quote) { |
|
add(node.key); |
|
} else if (node instanceof AST_ObjectProperty && node.quote) { |
|
add(node.key.name); |
|
} else if (node instanceof AST_Sub) { |
|
addStrings(node.property, add); |
|
} |
|
})); |
|
} |
|
|
|
function addStrings(node, add) { |
|
node.walk(new TreeWalker(function(node) { |
|
if (node instanceof AST_Sequence) { |
|
addStrings(node.tail_node(), add); |
|
} else if (node instanceof AST_String) { |
|
add(node.value); |
|
} else if (node instanceof AST_Conditional) { |
|
addStrings(node.consequent, add); |
|
addStrings(node.alternative, add); |
|
} |
|
return true; |
|
})); |
|
} |
|
|
|
function mangle_private_properties(ast, options) { |
|
var cprivate = -1; |
|
var private_cache = new Map(); |
|
var nth_identifier = options.nth_identifier || base54; |
|
|
|
ast = ast.transform(new TreeTransformer(function(node) { |
|
if ( |
|
node instanceof AST_ClassPrivateProperty |
|
|| node instanceof AST_PrivateMethod |
|
|| node instanceof AST_PrivateGetter |
|
|| node instanceof AST_PrivateSetter |
|
|| node instanceof AST_PrivateIn |
|
) { |
|
node.key.name = mangle_private(node.key.name); |
|
} else if (node instanceof AST_DotHash) { |
|
node.property = mangle_private(node.property); |
|
} |
|
})); |
|
return ast; |
|
|
|
function mangle_private(name) { |
|
let mangled = private_cache.get(name); |
|
if (!mangled) { |
|
mangled = nth_identifier.get(++cprivate); |
|
private_cache.set(name, mangled); |
|
} |
|
|
|
return mangled; |
|
} |
|
} |
|
|
|
function find_annotated_props(ast) { |
|
var annotated_props = new Set(); |
|
walk(ast, node => { |
|
if ( |
|
node instanceof AST_ClassPrivateProperty |
|
|| node instanceof AST_PrivateMethod |
|
|| node instanceof AST_PrivateGetter |
|
|| node instanceof AST_PrivateSetter |
|
|| node instanceof AST_DotHash |
|
) ; else if (node instanceof AST_ObjectKeyVal) { |
|
if (typeof node.key == "string" && has_annotation(node, _MANGLEPROP)) { |
|
annotated_props.add(node.key); |
|
} |
|
} else if (node instanceof AST_ObjectProperty) { |
|
// setter or getter, since KeyVal is handled above |
|
if (has_annotation(node, _MANGLEPROP)) { |
|
annotated_props.add(node.key.name); |
|
} |
|
} else if (node instanceof AST_Dot) { |
|
if (has_annotation(node, _MANGLEPROP)) { |
|
annotated_props.add(node.property); |
|
} |
|
} else if (node instanceof AST_Sub) { |
|
if (node.property instanceof AST_String && has_annotation(node, _MANGLEPROP)) { |
|
annotated_props.add(node.property.value); |
|
} |
|
} |
|
}); |
|
return annotated_props; |
|
} |
|
|
|
function mangle_properties(ast, options, annotated_props = find_annotated_props(ast)) { |
|
options = defaults(options, { |
|
builtins: false, |
|
cache: null, |
|
debug: false, |
|
keep_quoted: false, |
|
nth_identifier: base54, |
|
only_cache: false, |
|
regex: null, |
|
reserved: null, |
|
undeclared: false, |
|
only_annotated: false, |
|
}, true); |
|
|
|
var nth_identifier = options.nth_identifier; |
|
|
|
var reserved_option = options.reserved; |
|
if (!Array.isArray(reserved_option)) reserved_option = [reserved_option]; |
|
var reserved = new Set(reserved_option); |
|
if (!options.builtins) find_builtins(reserved); |
|
|
|
var cname = -1; |
|
|
|
var cache; |
|
if (options.cache) { |
|
cache = options.cache.props; |
|
} else { |
|
cache = new Map(); |
|
} |
|
|
|
var only_annotated = options.only_annotated; |
|
var regex = options.regex && new RegExp(options.regex); |
|
|
|
// note debug is either false (disabled), or a string of the debug suffix to use (enabled). |
|
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' |
|
// the same as passing an empty string. |
|
var debug = options.debug !== false; |
|
var debug_name_suffix; |
|
if (debug) { |
|
debug_name_suffix = (options.debug === true ? "" : options.debug); |
|
} |
|
|
|
var names_to_mangle = new Set(); |
|
var unmangleable = new Set(); |
|
// Track each already-mangled name to prevent nth_identifier from generating |
|
// the same name. |
|
cache.forEach((mangled_name) => unmangleable.add(mangled_name)); |
|
|
|
var keep_quoted = !!options.keep_quoted; |
|
|
|
// step 1: find candidates to mangle |
|
ast.walk(new TreeWalker(function(node) { |
|
if ( |
|
node instanceof AST_ClassPrivateProperty |
|
|| node instanceof AST_PrivateMethod |
|
|| node instanceof AST_PrivateGetter |
|
|| node instanceof AST_PrivateSetter |
|
|| node instanceof AST_DotHash |
|
) ; else if (node instanceof AST_ObjectKeyVal) { |
|
if (typeof node.key == "string" && (!keep_quoted || !node.quote)) { |
|
add(node.key); |
|
} |
|
} else if (node instanceof AST_ObjectProperty) { |
|
// setter or getter, since KeyVal is handled above |
|
if (!keep_quoted || !node.quote) { |
|
add(node.key.name); |
|
} |
|
} else if (node instanceof AST_Dot) { |
|
var declared = !!options.undeclared; |
|
if (!declared) { |
|
var root = node; |
|
while (root.expression) { |
|
root = root.expression; |
|
} |
|
declared = !(root.thedef && root.thedef.undeclared); |
|
} |
|
if (declared && |
|
(!keep_quoted || !node.quote)) { |
|
add(node.property); |
|
} |
|
} else if (node instanceof AST_Sub) { |
|
if (!keep_quoted) { |
|
addStrings(node.property, add); |
|
} |
|
} else if (node instanceof AST_Call |
|
&& node.expression.print_to_string() == "Object.defineProperty") { |
|
addStrings(node.args[1], add); |
|
} else if (node instanceof AST_Binary && node.operator === "in") { |
|
addStrings(node.left, add); |
|
} else if (node instanceof AST_String && has_annotation(node, _KEY)) { |
|
add(node.value); |
|
} |
|
})); |
|
|
|
// step 2: transform the tree, renaming properties |
|
return ast.transform(new TreeTransformer(function(node) { |
|
if ( |
|
node instanceof AST_ClassPrivateProperty |
|
|| node instanceof AST_PrivateMethod |
|
|| node instanceof AST_PrivateGetter |
|
|| node instanceof AST_PrivateSetter |
|
|| node instanceof AST_DotHash |
|
) ; else if (node instanceof AST_ObjectKeyVal) { |
|
if (typeof node.key == "string" && (!keep_quoted || !node.quote)) { |
|
node.key = mangle(node.key); |
|
} |
|
} else if (node instanceof AST_ObjectProperty) { |
|
// setter, getter, method or class field |
|
if (!keep_quoted || !node.quote) { |
|
if (!node.computed_key()) { |
|
node.key.name = mangle(node.key.name); |
|
} |
|
} |
|
} else if (node instanceof AST_Dot) { |
|
if (!keep_quoted || !node.quote) { |
|
node.property = mangle(node.property); |
|
} |
|
} else if (!keep_quoted && node instanceof AST_Sub) { |
|
node.property = mangleStrings(node.property); |
|
} else if (node instanceof AST_Call |
|
&& node.expression.print_to_string() == "Object.defineProperty") { |
|
node.args[1] = mangleStrings(node.args[1]); |
|
} else if (node instanceof AST_Binary && node.operator === "in") { |
|
node.left = mangleStrings(node.left); |
|
} else if (node instanceof AST_String && has_annotation(node, _KEY)) { |
|
// Clear _KEY annotation to prevent double mangling |
|
clear_annotation(node, _KEY); |
|
node.value = mangle(node.value); |
|
} |
|
})); |
|
|
|
// only function declarations after this line |
|
|
|
function can_mangle(name) { |
|
if (unmangleable.has(name)) return false; |
|
if (reserved.has(name)) return false; |
|
if (options.only_cache) { |
|
return cache.has(name); |
|
} |
|
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false; |
|
return true; |
|
} |
|
|
|
function should_mangle(name) { |
|
if (only_annotated && !annotated_props.has(name)) return false; |
|
if (regex && !regex.test(name)) { |
|
return annotated_props.has(name); |
|
} |
|
if (reserved.has(name)) return false; |
|
return cache.has(name) |
|
|| names_to_mangle.has(name); |
|
} |
|
|
|
function add(name) { |
|
if (can_mangle(name)) { |
|
names_to_mangle.add(name); |
|
} |
|
|
|
if (!should_mangle(name)) { |
|
unmangleable.add(name); |
|
} |
|
} |
|
|
|
function mangle(name) { |
|
if (!should_mangle(name)) { |
|
return name; |
|
} |
|
|
|
var mangled = cache.get(name); |
|
if (!mangled) { |
|
if (debug) { |
|
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. |
|
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_"; |
|
|
|
if (can_mangle(debug_mangled)) { |
|
mangled = debug_mangled; |
|
} |
|
} |
|
|
|
// either debug mode is off, or it is on and we could not use the mangled name |
|
if (!mangled) { |
|
do { |
|
mangled = nth_identifier.get(++cname); |
|
} while (!can_mangle(mangled)); |
|
} |
|
|
|
cache.set(name, mangled); |
|
} |
|
return mangled; |
|
} |
|
|
|
function mangleStrings(node) { |
|
return node.transform(new TreeTransformer(function(node) { |
|
if (node instanceof AST_Sequence) { |
|
var last = node.expressions.length - 1; |
|
node.expressions[last] = mangleStrings(node.expressions[last]); |
|
} else if (node instanceof AST_String) { |
|
// Clear _KEY annotation to prevent double mangling |
|
clear_annotation(node, _KEY); |
|
node.value = mangle(node.value); |
|
} else if (node instanceof AST_Conditional) { |
|
node.consequent = mangleStrings(node.consequent); |
|
node.alternative = mangleStrings(node.alternative); |
|
} |
|
return node; |
|
})); |
|
} |
|
} |
|
|
|
// to/from base64 functions |
|
// Prefer built-in Buffer, if available, then use hack |
|
// https://developer.mozilla.org/en-US/docs/Glossary/Base64#The_Unicode_Problem |
|
var to_ascii = typeof Buffer !== "undefined" |
|
? (b64) => Buffer.from(b64, "base64").toString() |
|
: (b64) => decodeURIComponent(escape(atob(b64))); |
|
var to_base64 = typeof Buffer !== "undefined" |
|
? (str) => Buffer.from(str).toString("base64") |
|
: (str) => btoa(unescape(encodeURIComponent(str))); |
|
|
|
function read_source_map(code) { |
|
var match = /(?:^|[^.])\/\/# sourceMappingURL=data:application\/json(;[\w=-]*)?;base64,([+/0-9A-Za-z]*=*)\s*$/.exec(code); |
|
if (!match) { |
|
console.warn("inline source map not found"); |
|
return null; |
|
} |
|
return to_ascii(match[2]); |
|
} |
|
|
|
function set_shorthand(name, options, keys) { |
|
if (options[name]) { |
|
keys.forEach(function(key) { |
|
if (options[key]) { |
|
if (typeof options[key] != "object") options[key] = {}; |
|
if (!(name in options[key])) options[key][name] = options[name]; |
|
} |
|
}); |
|
} |
|
} |
|
|
|
function init_cache(cache) { |
|
if (!cache) return; |
|
if (!("props" in cache)) { |
|
cache.props = new Map(); |
|
} else if (!(cache.props instanceof Map)) { |
|
cache.props = map_from_object(cache.props); |
|
} |
|
} |
|
|
|
function cache_to_json(cache) { |
|
return { |
|
props: map_to_object(cache.props) |
|
}; |
|
} |
|
|
|
function log_input(files, options, fs, debug_folder) { |
|
if (!(fs && fs.writeFileSync && fs.mkdirSync)) { |
|
return; |
|
} |
|
|
|
try { |
|
fs.mkdirSync(debug_folder); |
|
} catch (e) { |
|
if (e.code !== "EEXIST") throw e; |
|
} |
|
|
|
const log_path = `${debug_folder}/terser-debug-${(Math.random() * 9999999) | 0}.log`; |
|
|
|
options = options || {}; |
|
|
|
const options_str = JSON.stringify(options, (_key, thing) => { |
|
if (typeof thing === "function") return "[Function " + thing.toString() + "]"; |
|
if (thing instanceof RegExp) return "[RegExp " + thing.toString() + "]"; |
|
return thing; |
|
}, 4); |
|
|
|
const files_str = (file) => { |
|
if (typeof file === "object" && options.parse && options.parse.spidermonkey) { |
|
return JSON.stringify(file, null, 2); |
|
} else if (typeof file === "object") { |
|
return Object.keys(file) |
|
.map((key) => key + ": " + files_str(file[key])) |
|
.join("\n\n"); |
|
} else if (typeof file === "string") { |
|
return "```\n" + file + "\n```"; |
|
} else { |
|
return file; // What do? |
|
} |
|
}; |
|
|
|
fs.writeFileSync(log_path, "Options: \n" + options_str + "\n\nInput files:\n\n" + files_str(files) + "\n"); |
|
} |
|
|
|
function* minify_sync_or_async(files, options, _fs_module) { |
|
if ( |
|
_fs_module |
|
&& typeof process === "object" |
|
&& process.env |
|
&& typeof process.env.TERSER_DEBUG_DIR === "string" |
|
) { |
|
log_input(files, options, _fs_module, process.env.TERSER_DEBUG_DIR); |
|
} |
|
|
|
options = defaults(options, { |
|
compress: {}, |
|
ecma: undefined, |
|
enclose: false, |
|
ie8: false, |
|
keep_classnames: undefined, |
|
keep_fnames: false, |
|
mangle: {}, |
|
module: false, |
|
nameCache: null, |
|
output: null, |
|
format: null, |
|
parse: {}, |
|
rename: undefined, |
|
safari10: false, |
|
sourceMap: false, |
|
spidermonkey: false, |
|
timings: false, |
|
toplevel: false, |
|
warnings: false, |
|
wrap: false, |
|
}, true); |
|
|
|
var timings = options.timings && { |
|
start: Date.now() |
|
}; |
|
if (options.keep_classnames === undefined) { |
|
options.keep_classnames = options.keep_fnames; |
|
} |
|
if (options.rename === undefined) { |
|
options.rename = options.compress && options.mangle; |
|
} |
|
if (options.output && options.format) { |
|
throw new Error("Please only specify either output or format option, preferrably format."); |
|
} |
|
options.format = options.format || options.output || {}; |
|
set_shorthand("ecma", options, [ "parse", "compress", "format" ]); |
|
set_shorthand("ie8", options, [ "compress", "mangle", "format" ]); |
|
set_shorthand("keep_classnames", options, [ "compress", "mangle" ]); |
|
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); |
|
set_shorthand("module", options, [ "parse", "compress", "mangle" ]); |
|
set_shorthand("safari10", options, [ "mangle", "format" ]); |
|
set_shorthand("toplevel", options, [ "compress", "mangle" ]); |
|
set_shorthand("warnings", options, [ "compress" ]); // legacy |
|
var quoted_props; |
|
if (options.mangle) { |
|
options.mangle = defaults(options.mangle, { |
|
cache: options.nameCache && (options.nameCache.vars || {}), |
|
eval: false, |
|
ie8: false, |
|
keep_classnames: false, |
|
keep_fnames: false, |
|
module: false, |
|
nth_identifier: base54, |
|
properties: false, |
|
reserved: [], |
|
safari10: false, |
|
toplevel: false, |
|
}, true); |
|
if (options.mangle.properties) { |
|
if (typeof options.mangle.properties != "object") { |
|
options.mangle.properties = {}; |
|
} |
|
if (options.mangle.properties.keep_quoted) { |
|
quoted_props = options.mangle.properties.reserved; |
|
if (!Array.isArray(quoted_props)) quoted_props = []; |
|
options.mangle.properties.reserved = quoted_props; |
|
} |
|
if (options.nameCache && !("cache" in options.mangle.properties)) { |
|
options.mangle.properties.cache = options.nameCache.props || {}; |
|
} |
|
} |
|
init_cache(options.mangle.cache); |
|
init_cache(options.mangle.properties.cache); |
|
} |
|
if (options.sourceMap) { |
|
options.sourceMap = defaults(options.sourceMap, { |
|
asObject: false, |
|
content: null, |
|
filename: null, |
|
includeSources: false, |
|
root: null, |
|
url: null, |
|
}, true); |
|
} |
|
|
|
// -- Parse phase -- |
|
if (timings) timings.parse = Date.now(); |
|
var toplevel; |
|
if (files instanceof AST_Toplevel) { |
|
toplevel = files; |
|
} else { |
|
if (typeof files == "string" || (options.parse.spidermonkey && !Array.isArray(files))) { |
|
files = [ files ]; |
|
} |
|
options.parse = options.parse || {}; |
|
options.parse.toplevel = null; |
|
|
|
if (options.parse.spidermonkey) { |
|
options.parse.toplevel = AST_Node.from_mozilla_ast(Object.keys(files).reduce(function(toplevel, name) { |
|
if (!toplevel) return files[name]; |
|
toplevel.body = toplevel.body.concat(files[name].body); |
|
return toplevel; |
|
}, null)); |
|
} else { |
|
delete options.parse.spidermonkey; |
|
|
|
for (var name in files) if (HOP(files, name)) { |
|
options.parse.filename = name; |
|
options.parse.toplevel = parse(files[name], options.parse); |
|
if (options.sourceMap && options.sourceMap.content == "inline") { |
|
if (Object.keys(files).length > 1) |
|
throw new Error("inline source map only works with singular input"); |
|
options.sourceMap.content = read_source_map(files[name]); |
|
} |
|
} |
|
} |
|
if (options.parse.toplevel === null) { |
|
throw new Error("no source file given"); |
|
} |
|
|
|
toplevel = options.parse.toplevel; |
|
} |
|
if (quoted_props && options.mangle.properties.keep_quoted !== "strict") { |
|
reserve_quoted_keys(toplevel, quoted_props); |
|
} |
|
var annotated_props; |
|
if (options.mangle && options.mangle.properties) { |
|
annotated_props = find_annotated_props(toplevel); |
|
} |
|
if (options.wrap) { |
|
toplevel = toplevel.wrap_commonjs(options.wrap); |
|
} |
|
if (options.enclose) { |
|
toplevel = toplevel.wrap_enclose(options.enclose); |
|
} |
|
if (timings) timings.rename = Date.now(); |
|
|
|
// -- Compress phase -- |
|
if (timings) timings.compress = Date.now(); |
|
if (options.compress) { |
|
toplevel = new Compressor(options.compress, { |
|
mangle_options: options.mangle |
|
}).compress(toplevel); |
|
} |
|
|
|
// -- Mangle phase -- |
|
if (timings) timings.scope = Date.now(); |
|
if (options.mangle) toplevel.figure_out_scope(options.mangle); |
|
if (timings) timings.mangle = Date.now(); |
|
if (options.mangle) { |
|
toplevel.compute_char_frequency(options.mangle); |
|
toplevel.mangle_names(options.mangle); |
|
toplevel = mangle_private_properties(toplevel, options.mangle); |
|
} |
|
if (timings) timings.properties = Date.now(); |
|
if (options.mangle && options.mangle.properties) { |
|
toplevel = mangle_properties(toplevel, options.mangle.properties, annotated_props); |
|
} |
|
|
|
// Format phase |
|
if (timings) timings.format = Date.now(); |
|
var result = {}; |
|
if (options.format.ast) { |
|
result.ast = toplevel; |
|
} |
|
if (options.format.spidermonkey) { |
|
result.ast = toplevel.to_mozilla_ast(); |
|
} |
|
let format_options; |
|
if (!HOP(options.format, "code") || options.format.code) { |
|
// Make a shallow copy so that we can modify without mutating the user's input. |
|
format_options = {...options.format}; |
|
if (!format_options.ast) { |
|
// Destroy stuff to save RAM. (unless the deprecated `ast` option is on) |
|
format_options._destroy_ast = true; |
|
|
|
walk(toplevel, node => { |
|
if (node instanceof AST_Scope) { |
|
node.variables = undefined; |
|
node.enclosed = undefined; |
|
node.parent_scope = undefined; |
|
} |
|
if (node.block_scope) { |
|
node.block_scope.variables = undefined; |
|
node.block_scope.enclosed = undefined; |
|
node.block_scope.parent_scope = undefined; |
|
} |
|
}); |
|
} |
|
|
|
if (options.sourceMap) { |
|
if (options.sourceMap.includeSources && files instanceof AST_Toplevel) { |
|
throw new Error("original source content unavailable"); |
|
} |
|
format_options.source_map = yield* SourceMap({ |
|
file: options.sourceMap.filename, |
|
orig: options.sourceMap.content, |
|
root: options.sourceMap.root, |
|
files: options.sourceMap.includeSources ? files : null, |
|
}); |
|
} |
|
delete format_options.ast; |
|
delete format_options.code; |
|
delete format_options.spidermonkey; |
|
var stream = OutputStream(format_options); |
|
toplevel.print(stream); |
|
result.code = stream.get(); |
|
if (options.sourceMap) { |
|
Object.defineProperty(result, "map", { |
|
configurable: true, |
|
enumerable: true, |
|
get() { |
|
const map = format_options.source_map.getEncoded(); |
|
return (result.map = options.sourceMap.asObject ? map : JSON.stringify(map)); |
|
}, |
|
set(value) { |
|
Object.defineProperty(result, "map", { |
|
value, |
|
writable: true, |
|
}); |
|
} |
|
}); |
|
result.decoded_map = format_options.source_map.getDecoded(); |
|
if (options.sourceMap.url == "inline") { |
|
var sourceMap = typeof result.map === "object" ? JSON.stringify(result.map) : result.map; |
|
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(sourceMap); |
|
} else if (options.sourceMap.url) { |
|
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url; |
|
} |
|
} |
|
} |
|
if (options.nameCache && options.mangle) { |
|
if (options.mangle.cache) options.nameCache.vars = cache_to_json(options.mangle.cache); |
|
if (options.mangle.properties && options.mangle.properties.cache) { |
|
options.nameCache.props = cache_to_json(options.mangle.properties.cache); |
|
} |
|
} |
|
if (format_options && format_options.source_map) { |
|
format_options.source_map.destroy(); |
|
} |
|
if (timings) { |
|
timings.end = Date.now(); |
|
result.timings = { |
|
parse: 1e-3 * (timings.rename - timings.parse), |
|
rename: 1e-3 * (timings.compress - timings.rename), |
|
compress: 1e-3 * (timings.scope - timings.compress), |
|
scope: 1e-3 * (timings.mangle - timings.scope), |
|
mangle: 1e-3 * (timings.properties - timings.mangle), |
|
properties: 1e-3 * (timings.format - timings.properties), |
|
format: 1e-3 * (timings.end - timings.format), |
|
total: 1e-3 * (timings.end - timings.start) |
|
}; |
|
} |
|
return result; |
|
} |
|
|
|
async function minify(files, options, _fs_module) { |
|
const gen = minify_sync_or_async(files, options, _fs_module); |
|
|
|
let yielded; |
|
let val; |
|
do { |
|
val = gen.next(await yielded); |
|
yielded = val.value; |
|
} while (!val.done); |
|
|
|
return val.value; |
|
} |
|
|
|
function minify_sync(files, options, _fs_module) { |
|
const gen = minify_sync_or_async(files, options, _fs_module); |
|
|
|
let yielded; |
|
let val; |
|
do { |
|
if (yielded && typeof yielded.then === "function") { |
|
throw new Error("minify_sync cannot be used with the legacy source-map module"); |
|
} |
|
val = gen.next(yielded); |
|
yielded = val.value; |
|
} while (!val.done); |
|
|
|
return val.value; |
|
} |
|
|
|
async function run_cli({ program, packageJson, fs, path }) { |
|
const skip_keys = new Set([ "cname", "parent_scope", "scope", "uses_eval", "uses_with" ]); |
|
var files = {}; |
|
var options = { |
|
compress: false, |
|
mangle: false |
|
}; |
|
const default_options = await _default_options(); |
|
program.version(packageJson.name + " " + packageJson.version); |
|
program.parseArgv = program.parse; |
|
program.parse = undefined; |
|
|
|
if (process.argv.includes("ast")) program.helpInformation = describe_ast; |
|
else if (process.argv.includes("options")) program.helpInformation = function() { |
|
var text = []; |
|
for (var option in default_options) { |
|
text.push("--" + (option === "sourceMap" ? "source-map" : option) + " options:"); |
|
text.push(format_object(default_options[option])); |
|
text.push(""); |
|
} |
|
return text.join("\n"); |
|
}; |
|
|
|
program.option("-p, --parse <options>", "Specify parser options.", parse_js()); |
|
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js()); |
|
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js()); |
|
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js()); |
|
program.option("-f, --format [options]", "Format options.", parse_js()); |
|
program.option("-b, --beautify [options]", "Alias for --format.", parse_js()); |
|
program.option("-o, --output <file>", "Output file (default STDOUT)."); |
|
program.option("--comments [filter]", "Preserve copyright comments in the output."); |
|
program.option("--config-file <file>", "Read minify() options from JSON file."); |
|
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define")); |
|
program.option("--ecma <version>", "Specify ECMAScript release: 5, 2015, 2016 or 2017..."); |
|
program.option("-e, --enclose [arg[,...][:value[,...]]]", "Embed output in a big function with configurable arguments and values."); |
|
program.option("--ie8", "Support non-standard Internet Explorer 8."); |
|
program.option("--keep-classnames", "Do not mangle/drop class names."); |
|
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name."); |
|
program.option("--module", "Input is an ES6 module"); |
|
program.option("--name-cache <file>", "File to hold mangled name mappings."); |
|
program.option("--rename", "Force symbol expansion."); |
|
program.option("--no-rename", "Disable symbol expansion."); |
|
program.option("--safari10", "Support non-standard Safari 10."); |
|
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_js()); |
|
program.option("--timings", "Display operations run time on STDERR."); |
|
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope."); |
|
program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally."); |
|
program.arguments("[files...]").parseArgv(process.argv); |
|
if (program.configFile) { |
|
options = JSON.parse(read_file(program.configFile)); |
|
} |
|
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") { |
|
fatal("ERROR: cannot write source map to STDOUT"); |
|
} |
|
|
|
[ |
|
"compress", |
|
"enclose", |
|
"ie8", |
|
"mangle", |
|
"module", |
|
"safari10", |
|
"sourceMap", |
|
"toplevel", |
|
"wrap" |
|
].forEach(function(name) { |
|
if (name in program) { |
|
options[name] = program[name]; |
|
} |
|
}); |
|
|
|
if ("ecma" in program) { |
|
if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer"); |
|
const ecma = program.ecma | 0; |
|
if (ecma > 5 && ecma < 2015) |
|
options.ecma = ecma + 2009; |
|
else |
|
options.ecma = ecma; |
|
} |
|
if (program.format || program.beautify) { |
|
const chosenOption = program.format || program.beautify; |
|
options.format = typeof chosenOption === "object" ? chosenOption : {}; |
|
} |
|
if (program.comments) { |
|
if (typeof options.format != "object") options.format = {}; |
|
options.format.comments = typeof program.comments == "string" ? (program.comments == "false" ? false : program.comments) : "some"; |
|
} |
|
if (program.define) { |
|
if (typeof options.compress != "object") options.compress = {}; |
|
if (typeof options.compress.global_defs != "object") options.compress.global_defs = {}; |
|
for (var expr in program.define) { |
|
options.compress.global_defs[expr] = program.define[expr]; |
|
} |
|
} |
|
if (program.keepClassnames) { |
|
options.keep_classnames = true; |
|
} |
|
if (program.keepFnames) { |
|
options.keep_fnames = true; |
|
} |
|
if (program.mangleProps) { |
|
if (program.mangleProps.domprops) { |
|
delete program.mangleProps.domprops; |
|
} else { |
|
if (typeof program.mangleProps != "object") program.mangleProps = {}; |
|
if (!Array.isArray(program.mangleProps.reserved)) program.mangleProps.reserved = []; |
|
} |
|
if (typeof options.mangle != "object") options.mangle = {}; |
|
options.mangle.properties = program.mangleProps; |
|
} |
|
if (program.nameCache) { |
|
options.nameCache = JSON.parse(read_file(program.nameCache, "{}")); |
|
} |
|
if (program.output == "ast") { |
|
options.format = { |
|
ast: true, |
|
code: false |
|
}; |
|
} |
|
if (program.parse) { |
|
if (!program.parse.acorn && !program.parse.spidermonkey) { |
|
options.parse = program.parse; |
|
} else if (program.sourceMap && program.sourceMap.content == "inline") { |
|
fatal("ERROR: inline source map only works with built-in parser"); |
|
} |
|
} |
|
if (~program.rawArgs.indexOf("--rename")) { |
|
options.rename = true; |
|
} else if (!program.rename) { |
|
options.rename = false; |
|
} |
|
|
|
let convert_path = name => name; |
|
if (typeof program.sourceMap == "object" && "base" in program.sourceMap) { |
|
convert_path = function() { |
|
var base = program.sourceMap.base; |
|
delete options.sourceMap.base; |
|
return function(name) { |
|
return path.relative(base, name); |
|
}; |
|
}(); |
|
} |
|
|
|
let filesList; |
|
if (options.files && options.files.length) { |
|
filesList = options.files; |
|
|
|
delete options.files; |
|
} else if (program.args.length) { |
|
filesList = program.args; |
|
} |
|
|
|
if (filesList) { |
|
simple_glob(filesList).forEach(function(name) { |
|
files[convert_path(name)] = read_file(name); |
|
}); |
|
} else { |
|
await new Promise((resolve) => { |
|
var chunks = []; |
|
process.stdin.setEncoding("utf8"); |
|
process.stdin.on("data", function(chunk) { |
|
chunks.push(chunk); |
|
}).on("end", function() { |
|
files = [ chunks.join("") ]; |
|
resolve(); |
|
}); |
|
process.stdin.resume(); |
|
}); |
|
} |
|
|
|
await run_cli(); |
|
|
|
function convert_ast(fn) { |
|
return AST_Node.from_mozilla_ast(Object.keys(files).reduce(fn, null)); |
|
} |
|
|
|
async function run_cli() { |
|
var content = program.sourceMap && program.sourceMap.content; |
|
if (content && content !== "inline") { |
|
options.sourceMap.content = read_file(content, content); |
|
} |
|
if (program.timings) options.timings = true; |
|
|
|
try { |
|
if (program.parse) { |
|
if (program.parse.acorn) { |
|
files = convert_ast(function(toplevel, name) { |
|
return require("acorn").parse(files[name], { |
|
ecmaVersion: 2024, |
|
locations: true, |
|
program: toplevel, |
|
sourceFile: name, |
|
sourceType: options.module || program.parse.module ? "module" : "script" |
|
}); |
|
}); |
|
} else if (program.parse.spidermonkey) { |
|
files = convert_ast(function(toplevel, name) { |
|
var obj = JSON.parse(files[name]); |
|
if (!toplevel) return obj; |
|
toplevel.body = toplevel.body.concat(obj.body); |
|
return toplevel; |
|
}); |
|
} |
|
} |
|
} catch (ex) { |
|
fatal(ex); |
|
} |
|
|
|
let result; |
|
try { |
|
result = await minify(files, options, fs); |
|
} catch (ex) { |
|
if (ex.name == "SyntaxError") { |
|
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col); |
|
var col = ex.col; |
|
var lines = files[ex.filename].split(/\r?\n/); |
|
var line = lines[ex.line - 1]; |
|
if (!line && !col) { |
|
line = lines[ex.line - 2]; |
|
col = line.length; |
|
} |
|
if (line) { |
|
var limit = 70; |
|
if (col > limit) { |
|
line = line.slice(col - limit); |
|
col = limit; |
|
} |
|
print_error(line.slice(0, 80)); |
|
print_error(line.slice(0, col).replace(/\S/g, " ") + "^"); |
|
} |
|
} |
|
if (ex.defs) { |
|
print_error("Supported options:"); |
|
print_error(format_object(ex.defs)); |
|
} |
|
fatal(ex); |
|
return; |
|
} |
|
|
|
if (program.output == "ast") { |
|
if (!options.compress && !options.mangle) { |
|
result.ast.figure_out_scope({}); |
|
} |
|
console.log(JSON.stringify(result.ast, function(key, value) { |
|
if (value) switch (key) { |
|
case "thedef": |
|
return symdef(value); |
|
case "enclosed": |
|
return value.length ? value.map(symdef) : undefined; |
|
case "variables": |
|
case "globals": |
|
return value.size ? collect_from_map(value, symdef) : undefined; |
|
} |
|
if (skip_keys.has(key)) return; |
|
if (value instanceof AST_Token) return; |
|
if (value instanceof Map) return; |
|
if (value instanceof AST_Node) { |
|
var result = { |
|
_class: "AST_" + value.TYPE |
|
}; |
|
if (value.block_scope) { |
|
result.variables = value.block_scope.variables; |
|
result.enclosed = value.block_scope.enclosed; |
|
} |
|
value.CTOR.PROPS.forEach(function(prop) { |
|
if (prop !== "block_scope") { |
|
result[prop] = value[prop]; |
|
} |
|
}); |
|
return result; |
|
} |
|
return value; |
|
}, 2)); |
|
} else if (program.output == "spidermonkey") { |
|
try { |
|
const minified = await minify( |
|
result.code, |
|
{ |
|
compress: false, |
|
mangle: false, |
|
format: { |
|
ast: true, |
|
code: false |
|
} |
|
}, |
|
fs |
|
); |
|
console.log(JSON.stringify(minified.ast.to_mozilla_ast(), null, 2)); |
|
} catch (ex) { |
|
fatal(ex); |
|
return; |
|
} |
|
} else if (program.output) { |
|
fs.mkdirSync(path.dirname(program.output), { recursive: true }); |
|
fs.writeFileSync(program.output, result.code); |
|
if (options.sourceMap && options.sourceMap.url !== "inline" && result.map) { |
|
fs.writeFileSync(program.output + ".map", result.map); |
|
} |
|
} else { |
|
console.log(result.code); |
|
} |
|
if (program.nameCache) { |
|
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache)); |
|
} |
|
if (result.timings) for (var phase in result.timings) { |
|
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s"); |
|
} |
|
} |
|
|
|
function fatal(message) { |
|
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:"); |
|
print_error(message); |
|
process.exit(1); |
|
} |
|
|
|
// A file glob function that only supports "*" and "?" wildcards in the basename. |
|
// Example: "foo/bar/*baz??.*.js" |
|
// Argument `glob` may be a string or an array of strings. |
|
// Returns an array of strings. Garbage in, garbage out. |
|
function simple_glob(glob) { |
|
if (Array.isArray(glob)) { |
|
return [].concat.apply([], glob.map(simple_glob)); |
|
} |
|
if (glob && glob.match(/[*?]/)) { |
|
var dir = path.dirname(glob); |
|
try { |
|
var entries = fs.readdirSync(dir); |
|
} catch (ex) {} |
|
if (entries) { |
|
var pattern = "^" + path.basename(glob) |
|
.replace(/[.+^$[\]\\(){}]/g, "\\$&") |
|
.replace(/\*/g, "[^/\\\\]*") |
|
.replace(/\?/g, "[^/\\\\]") + "$"; |
|
var mod = process.platform === "win32" ? "i" : ""; |
|
var rx = new RegExp(pattern, mod); |
|
var results = entries.filter(function(name) { |
|
return rx.test(name); |
|
}).map(function(name) { |
|
return path.join(dir, name); |
|
}); |
|
if (results.length) return results; |
|
} |
|
} |
|
return [ glob ]; |
|
} |
|
|
|
function read_file(path, default_value) { |
|
try { |
|
return fs.readFileSync(path, "utf8"); |
|
} catch (ex) { |
|
if ((ex.code == "ENOENT" || ex.code == "ENAMETOOLONG") && default_value != null) return default_value; |
|
fatal(ex); |
|
} |
|
} |
|
|
|
function parse_js(flag) { |
|
return function(value, options) { |
|
options = options || {}; |
|
try { |
|
walk(parse(value, { expression: true }), node => { |
|
if (node instanceof AST_Assign) { |
|
var name = node.left.print_to_string(); |
|
var value = node.right; |
|
if (flag) { |
|
options[name] = value; |
|
} else if (value instanceof AST_Array) { |
|
options[name] = value.elements.map(to_string); |
|
} else if (value instanceof AST_RegExp) { |
|
value = value.value; |
|
options[name] = new RegExp(value.source, value.flags); |
|
} else { |
|
options[name] = to_string(value); |
|
} |
|
return true; |
|
} |
|
if (node instanceof AST_Symbol || node instanceof AST_PropAccess) { |
|
var name = node.print_to_string(); |
|
options[name] = true; |
|
return true; |
|
} |
|
if (!(node instanceof AST_Sequence)) throw node; |
|
|
|
function to_string(value) { |
|
return value instanceof AST_Constant ? value.getValue() : value.print_to_string({ |
|
quote_keys: true |
|
}); |
|
} |
|
}); |
|
} catch(ex) { |
|
if (flag) { |
|
fatal("Error parsing arguments for '" + flag + "': " + value); |
|
} else { |
|
options[value] = null; |
|
} |
|
} |
|
return options; |
|
}; |
|
} |
|
|
|
function symdef(def) { |
|
var ret = (1e6 + def.id) + " " + def.name; |
|
if (def.mangled_name) ret += " " + def.mangled_name; |
|
return ret; |
|
} |
|
|
|
function collect_from_map(map, callback) { |
|
var result = []; |
|
map.forEach(function (def) { |
|
result.push(callback(def)); |
|
}); |
|
return result; |
|
} |
|
|
|
function format_object(obj) { |
|
var lines = []; |
|
var padding = ""; |
|
Object.keys(obj).map(function(name) { |
|
if (padding.length < name.length) padding = Array(name.length + 1).join(" "); |
|
return [ name, JSON.stringify(obj[name]) ]; |
|
}).forEach(function(tokens) { |
|
lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]); |
|
}); |
|
return lines.join("\n"); |
|
} |
|
|
|
function print_error(msg) { |
|
process.stderr.write(msg); |
|
process.stderr.write("\n"); |
|
} |
|
|
|
function describe_ast() { |
|
var out = OutputStream({ beautify: true }); |
|
function doitem(ctor) { |
|
out.print("AST_" + ctor.TYPE); |
|
const props = ctor.SELF_PROPS.filter(prop => !/^\$/.test(prop)); |
|
|
|
if (props.length > 0) { |
|
out.space(); |
|
out.with_parens(function() { |
|
props.forEach(function(prop, i) { |
|
if (i) out.space(); |
|
out.print(prop); |
|
}); |
|
}); |
|
} |
|
|
|
if (ctor.documentation) { |
|
out.space(); |
|
out.print_string(ctor.documentation); |
|
} |
|
|
|
if (ctor.SUBCLASSES.length > 0) { |
|
out.space(); |
|
out.with_block(function() { |
|
ctor.SUBCLASSES.forEach(function(ctor) { |
|
out.indent(); |
|
doitem(ctor); |
|
out.newline(); |
|
}); |
|
}); |
|
} |
|
} |
|
doitem(AST_Node); |
|
return out + "\n"; |
|
} |
|
} |
|
|
|
async function _default_options() { |
|
const defs = {}; |
|
|
|
Object.keys(infer_options({ 0: 0 })).forEach((component) => { |
|
const options = infer_options({ |
|
[component]: {0: 0} |
|
}); |
|
|
|
if (options) defs[component] = options; |
|
}); |
|
return defs; |
|
} |
|
|
|
async function infer_options(options) { |
|
try { |
|
await minify("", options); |
|
} catch (error) { |
|
return error.defs; |
|
} |
|
} |
|
|
|
exports._default_options = _default_options; |
|
exports._run_cli = run_cli; |
|
exports.minify = minify; |
|
exports.minify_sync = minify_sync; |
|
|
|
}));
|