| 1 | /***********************************************************************
|
|---|
| 2 |
|
|---|
| 3 | A JavaScript tokenizer / parser / beautifier / compressor.
|
|---|
| 4 | https://github.com/mishoo/UglifyJS2
|
|---|
| 5 |
|
|---|
| 6 | -------------------------------- (C) ---------------------------------
|
|---|
| 7 |
|
|---|
| 8 | Author: Mihai Bazon
|
|---|
| 9 | <mihai.bazon@gmail.com>
|
|---|
| 10 | http://mihai.bazon.net/blog
|
|---|
| 11 |
|
|---|
| 12 | Distributed under the BSD license:
|
|---|
| 13 |
|
|---|
| 14 | Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
|---|
| 15 |
|
|---|
| 16 | Redistribution and use in source and binary forms, with or without
|
|---|
| 17 | modification, are permitted provided that the following conditions
|
|---|
| 18 | are met:
|
|---|
| 19 |
|
|---|
| 20 | * Redistributions of source code must retain the above
|
|---|
| 21 | copyright notice, this list of conditions and the following
|
|---|
| 22 | disclaimer.
|
|---|
| 23 |
|
|---|
| 24 | * Redistributions in binary form must reproduce the above
|
|---|
| 25 | copyright notice, this list of conditions and the following
|
|---|
| 26 | disclaimer in the documentation and/or other materials
|
|---|
| 27 | provided with the distribution.
|
|---|
| 28 |
|
|---|
| 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
|---|
| 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|---|
| 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|---|
| 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
|---|
| 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
|---|
| 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|---|
| 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|---|
| 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|---|
| 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|---|
| 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|---|
| 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|---|
| 40 | SUCH DAMAGE.
|
|---|
| 41 |
|
|---|
| 42 | ***********************************************************************/
|
|---|
| 43 |
|
|---|
| 44 | "use strict";
|
|---|
| 45 |
|
|---|
| 46 | import { AST_Node, AST_Number, AST_UnaryPrefix } from "../ast.js";
|
|---|
| 47 |
|
|---|
| 48 | function characters(str) {
|
|---|
| 49 | return str.split("");
|
|---|
| 50 | }
|
|---|
| 51 |
|
|---|
| 52 | function member(name, array) {
|
|---|
| 53 | return array.includes(name);
|
|---|
| 54 | }
|
|---|
| 55 |
|
|---|
| 56 | class DefaultsError extends Error {
|
|---|
| 57 | constructor(msg, defs) {
|
|---|
| 58 | super();
|
|---|
| 59 |
|
|---|
| 60 | this.name = "DefaultsError";
|
|---|
| 61 | this.message = msg;
|
|---|
| 62 | this.defs = defs;
|
|---|
| 63 | }
|
|---|
| 64 | }
|
|---|
| 65 |
|
|---|
| 66 | function defaults(args, defs, croak) {
|
|---|
| 67 | if (args === true) {
|
|---|
| 68 | args = {};
|
|---|
| 69 | } else if (args != null && typeof args === "object") {
|
|---|
| 70 | args = {...args};
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 | const ret = args || {};
|
|---|
| 74 |
|
|---|
| 75 | if (croak) for (const i in ret) if (HOP(ret, i) && !HOP(defs, i)) {
|
|---|
| 76 | throw new DefaultsError("`" + i + "` is not a supported option", defs);
|
|---|
| 77 | }
|
|---|
| 78 |
|
|---|
| 79 | for (const i in defs) if (HOP(defs, i)) {
|
|---|
| 80 | if (!args || !HOP(args, i)) {
|
|---|
| 81 | ret[i] = defs[i];
|
|---|
| 82 | } else if (i === "ecma" || i === "builtins_ecma") {
|
|---|
| 83 | let ecma = args[i] | 0;
|
|---|
| 84 | if (ecma > 5 && ecma < 2015) ecma += 2009;
|
|---|
| 85 | ret[i] = ecma;
|
|---|
| 86 | } else {
|
|---|
| 87 | ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
|
|---|
| 88 | }
|
|---|
| 89 | }
|
|---|
| 90 |
|
|---|
| 91 | return ret;
|
|---|
| 92 | }
|
|---|
| 93 |
|
|---|
| 94 | function noop() {}
|
|---|
| 95 | function return_false() { return false; }
|
|---|
| 96 | function return_true() { return true; }
|
|---|
| 97 | function return_this() { return this; }
|
|---|
| 98 | function return_null() { return null; }
|
|---|
| 99 |
|
|---|
| 100 | var MAP = (function() {
|
|---|
| 101 | function MAP(a, tw, allow_splicing = true) {
|
|---|
| 102 | const new_a = [];
|
|---|
| 103 |
|
|---|
| 104 | for (let i = 0; i < a.length; ++i) {
|
|---|
| 105 | let item = a[i];
|
|---|
| 106 | let ret = item.transform(tw, allow_splicing);
|
|---|
| 107 |
|
|---|
| 108 | if (ret instanceof AST_Node) {
|
|---|
| 109 | new_a.push(ret);
|
|---|
| 110 | } else if (ret instanceof Splice) {
|
|---|
| 111 | new_a.push(...ret.v);
|
|---|
| 112 | }
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | return new_a;
|
|---|
| 116 | }
|
|---|
| 117 |
|
|---|
| 118 | MAP.splice = function(val) { return new Splice(val); };
|
|---|
| 119 | MAP.skip = {};
|
|---|
| 120 | function Splice(val) { this.v = val; }
|
|---|
| 121 | return MAP;
|
|---|
| 122 | })();
|
|---|
| 123 |
|
|---|
| 124 | function make_node(ctor, orig, props) {
|
|---|
| 125 | if (!props) props = {};
|
|---|
| 126 | if (orig) {
|
|---|
| 127 | if (!props.start) props.start = orig.start;
|
|---|
| 128 | if (!props.end) props.end = orig.end;
|
|---|
| 129 | }
|
|---|
| 130 | return new ctor(props);
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | /** Makes a `void 0` expression. Use instead of AST_Undefined which may conflict
|
|---|
| 134 | * with an existing variable called `undefined` */
|
|---|
| 135 | function make_void_0(orig) {
|
|---|
| 136 | return make_node(AST_UnaryPrefix, orig, {
|
|---|
| 137 | operator: "void",
|
|---|
| 138 | expression: make_node(AST_Number, orig, { value: 0 })
|
|---|
| 139 | });
|
|---|
| 140 | }
|
|---|
| 141 |
|
|---|
| 142 | function push_uniq(array, el) {
|
|---|
| 143 | if (!array.includes(el))
|
|---|
| 144 | array.push(el);
|
|---|
| 145 | }
|
|---|
| 146 |
|
|---|
| 147 | function string_template(text, props) {
|
|---|
| 148 | return text.replace(/{(.+?)}/g, function(str, p) {
|
|---|
| 149 | return props && props[p];
|
|---|
| 150 | });
|
|---|
| 151 | }
|
|---|
| 152 |
|
|---|
| 153 | function remove(array, el) {
|
|---|
| 154 | for (var i = array.length; --i >= 0;) {
|
|---|
| 155 | if (array[i] === el) array.splice(i, 1);
|
|---|
| 156 | }
|
|---|
| 157 | }
|
|---|
| 158 |
|
|---|
| 159 | function mergeSort(array, cmp) {
|
|---|
| 160 | if (array.length < 2) return array.slice();
|
|---|
| 161 | function merge(a, b) {
|
|---|
| 162 | var r = [], ai = 0, bi = 0, i = 0;
|
|---|
| 163 | while (ai < a.length && bi < b.length) {
|
|---|
| 164 | cmp(a[ai], b[bi]) <= 0
|
|---|
| 165 | ? r[i++] = a[ai++]
|
|---|
| 166 | : r[i++] = b[bi++];
|
|---|
| 167 | }
|
|---|
| 168 | if (ai < a.length) r.push.apply(r, a.slice(ai));
|
|---|
| 169 | if (bi < b.length) r.push.apply(r, b.slice(bi));
|
|---|
| 170 | return r;
|
|---|
| 171 | }
|
|---|
| 172 | function _ms(a) {
|
|---|
| 173 | if (a.length <= 1)
|
|---|
| 174 | return a;
|
|---|
| 175 | var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
|
|---|
| 176 | left = _ms(left);
|
|---|
| 177 | right = _ms(right);
|
|---|
| 178 | return merge(left, right);
|
|---|
| 179 | }
|
|---|
| 180 | return _ms(array);
|
|---|
| 181 | }
|
|---|
| 182 |
|
|---|
| 183 | function makePredicate(words) {
|
|---|
| 184 | if (!Array.isArray(words)) words = words.split(" ");
|
|---|
| 185 |
|
|---|
| 186 | return new Set(words.sort());
|
|---|
| 187 | }
|
|---|
| 188 |
|
|---|
| 189 | function map_add(map, key, value) {
|
|---|
| 190 | if (map.has(key)) {
|
|---|
| 191 | map.get(key).push(value);
|
|---|
| 192 | } else {
|
|---|
| 193 | map.set(key, [ value ]);
|
|---|
| 194 | }
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | function map_from_object(obj) {
|
|---|
| 198 | var map = new Map();
|
|---|
| 199 | for (var key in obj) {
|
|---|
| 200 | if (HOP(obj, key) && key.charAt(0) === "$") {
|
|---|
| 201 | map.set(key.substr(1), obj[key]);
|
|---|
| 202 | }
|
|---|
| 203 | }
|
|---|
| 204 | return map;
|
|---|
| 205 | }
|
|---|
| 206 |
|
|---|
| 207 | function map_to_object(map) {
|
|---|
| 208 | var obj = Object.create(null);
|
|---|
| 209 | map.forEach(function (value, key) {
|
|---|
| 210 | obj["$" + key] = value;
|
|---|
| 211 | });
|
|---|
| 212 | return obj;
|
|---|
| 213 | }
|
|---|
| 214 |
|
|---|
| 215 | function HOP(obj, prop) {
|
|---|
| 216 | return Object.prototype.hasOwnProperty.call(obj, prop);
|
|---|
| 217 | }
|
|---|
| 218 |
|
|---|
| 219 | function keep_name(keep_setting, name) {
|
|---|
| 220 | return keep_setting === true
|
|---|
| 221 | || (keep_setting instanceof RegExp && keep_setting.test(name));
|
|---|
| 222 | }
|
|---|
| 223 |
|
|---|
| 224 | var lineTerminatorEscape = {
|
|---|
| 225 | "\0": "0",
|
|---|
| 226 | "\n": "n",
|
|---|
| 227 | "\r": "r",
|
|---|
| 228 | "\u2028": "u2028",
|
|---|
| 229 | "\u2029": "u2029",
|
|---|
| 230 | };
|
|---|
| 231 | function regexp_source_fix(source) {
|
|---|
| 232 | // V8 does not escape line terminators in regexp patterns in node 12
|
|---|
| 233 | // We'll also remove literal \0
|
|---|
| 234 | return source.replace(/[\0\n\r\u2028\u2029]/g, function (match, offset) {
|
|---|
| 235 | var escaped = source[offset - 1] == "\\"
|
|---|
| 236 | && (source[offset - 2] != "\\"
|
|---|
| 237 | || /(?:^|[^\\])(?:\\{2})*$/.test(source.slice(0, offset - 1)));
|
|---|
| 238 | return (escaped ? "" : "\\") + lineTerminatorEscape[match];
|
|---|
| 239 | });
|
|---|
| 240 | }
|
|---|
| 241 |
|
|---|
| 242 | // Subset of regexps that is not going to cause regexp based DDOS
|
|---|
| 243 | // https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
|
|---|
| 244 | const re_safe_regexp = /^[\\/|\0\s\w^$.[\]()]*$/;
|
|---|
| 245 |
|
|---|
| 246 | /** Check if the regexp is safe for Terser to create without risking a RegExp DOS */
|
|---|
| 247 | export const regexp_is_safe = (source) => re_safe_regexp.test(source);
|
|---|
| 248 |
|
|---|
| 249 | const all_flags = "dgimsuyv";
|
|---|
| 250 | function sort_regexp_flags(flags) {
|
|---|
| 251 | const existing_flags = new Set(flags.split(""));
|
|---|
| 252 | let out = "";
|
|---|
| 253 | for (const flag of all_flags) {
|
|---|
| 254 | if (existing_flags.has(flag)) {
|
|---|
| 255 | out += flag;
|
|---|
| 256 | existing_flags.delete(flag);
|
|---|
| 257 | }
|
|---|
| 258 | }
|
|---|
| 259 | if (existing_flags.size) {
|
|---|
| 260 | // Flags Terser doesn't know about
|
|---|
| 261 | existing_flags.forEach(flag => { out += flag; });
|
|---|
| 262 | }
|
|---|
| 263 | return out;
|
|---|
| 264 | }
|
|---|
| 265 |
|
|---|
| 266 | function has_annotation(node, annotation) {
|
|---|
| 267 | return node._annotations & annotation;
|
|---|
| 268 | }
|
|---|
| 269 |
|
|---|
| 270 | function set_annotation(node, annotation) {
|
|---|
| 271 | node._annotations |= annotation;
|
|---|
| 272 | }
|
|---|
| 273 |
|
|---|
| 274 | function clear_annotation(node, annotation) {
|
|---|
| 275 | node._annotations &= ~annotation;
|
|---|
| 276 | }
|
|---|
| 277 |
|
|---|
| 278 | export {
|
|---|
| 279 | characters,
|
|---|
| 280 | defaults,
|
|---|
| 281 | HOP,
|
|---|
| 282 | keep_name,
|
|---|
| 283 | make_node,
|
|---|
| 284 | make_void_0,
|
|---|
| 285 | makePredicate,
|
|---|
| 286 | map_add,
|
|---|
| 287 | map_from_object,
|
|---|
| 288 | map_to_object,
|
|---|
| 289 | MAP,
|
|---|
| 290 | member,
|
|---|
| 291 | mergeSort,
|
|---|
| 292 | noop,
|
|---|
| 293 | push_uniq,
|
|---|
| 294 | regexp_source_fix,
|
|---|
| 295 | remove,
|
|---|
| 296 | return_false,
|
|---|
| 297 | return_null,
|
|---|
| 298 | return_this,
|
|---|
| 299 | return_true,
|
|---|
| 300 | sort_regexp_flags,
|
|---|
| 301 | string_template,
|
|---|
| 302 | has_annotation,
|
|---|
| 303 | set_annotation,
|
|---|
| 304 | clear_annotation,
|
|---|
| 305 | };
|
|---|