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 } 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") {
|
---|
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 | function push_uniq(array, el) {
|
---|
134 | if (!array.includes(el))
|
---|
135 | array.push(el);
|
---|
136 | }
|
---|
137 |
|
---|
138 | function string_template(text, props) {
|
---|
139 | return text.replace(/{(.+?)}/g, function(str, p) {
|
---|
140 | return props && props[p];
|
---|
141 | });
|
---|
142 | }
|
---|
143 |
|
---|
144 | function remove(array, el) {
|
---|
145 | for (var i = array.length; --i >= 0;) {
|
---|
146 | if (array[i] === el) array.splice(i, 1);
|
---|
147 | }
|
---|
148 | }
|
---|
149 |
|
---|
150 | function mergeSort(array, cmp) {
|
---|
151 | if (array.length < 2) return array.slice();
|
---|
152 | function merge(a, b) {
|
---|
153 | var r = [], ai = 0, bi = 0, i = 0;
|
---|
154 | while (ai < a.length && bi < b.length) {
|
---|
155 | cmp(a[ai], b[bi]) <= 0
|
---|
156 | ? r[i++] = a[ai++]
|
---|
157 | : r[i++] = b[bi++];
|
---|
158 | }
|
---|
159 | if (ai < a.length) r.push.apply(r, a.slice(ai));
|
---|
160 | if (bi < b.length) r.push.apply(r, b.slice(bi));
|
---|
161 | return r;
|
---|
162 | }
|
---|
163 | function _ms(a) {
|
---|
164 | if (a.length <= 1)
|
---|
165 | return a;
|
---|
166 | var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
|
---|
167 | left = _ms(left);
|
---|
168 | right = _ms(right);
|
---|
169 | return merge(left, right);
|
---|
170 | }
|
---|
171 | return _ms(array);
|
---|
172 | }
|
---|
173 |
|
---|
174 | function makePredicate(words) {
|
---|
175 | if (!Array.isArray(words)) words = words.split(" ");
|
---|
176 |
|
---|
177 | return new Set(words.sort());
|
---|
178 | }
|
---|
179 |
|
---|
180 | function map_add(map, key, value) {
|
---|
181 | if (map.has(key)) {
|
---|
182 | map.get(key).push(value);
|
---|
183 | } else {
|
---|
184 | map.set(key, [ value ]);
|
---|
185 | }
|
---|
186 | }
|
---|
187 |
|
---|
188 | function map_from_object(obj) {
|
---|
189 | var map = new Map();
|
---|
190 | for (var key in obj) {
|
---|
191 | if (HOP(obj, key) && key.charAt(0) === "$") {
|
---|
192 | map.set(key.substr(1), obj[key]);
|
---|
193 | }
|
---|
194 | }
|
---|
195 | return map;
|
---|
196 | }
|
---|
197 |
|
---|
198 | function map_to_object(map) {
|
---|
199 | var obj = Object.create(null);
|
---|
200 | map.forEach(function (value, key) {
|
---|
201 | obj["$" + key] = value;
|
---|
202 | });
|
---|
203 | return obj;
|
---|
204 | }
|
---|
205 |
|
---|
206 | function HOP(obj, prop) {
|
---|
207 | return Object.prototype.hasOwnProperty.call(obj, prop);
|
---|
208 | }
|
---|
209 |
|
---|
210 | function keep_name(keep_setting, name) {
|
---|
211 | return keep_setting === true
|
---|
212 | || (keep_setting instanceof RegExp && keep_setting.test(name));
|
---|
213 | }
|
---|
214 |
|
---|
215 | var lineTerminatorEscape = {
|
---|
216 | "\0": "0",
|
---|
217 | "\n": "n",
|
---|
218 | "\r": "r",
|
---|
219 | "\u2028": "u2028",
|
---|
220 | "\u2029": "u2029",
|
---|
221 | };
|
---|
222 | function regexp_source_fix(source) {
|
---|
223 | // V8 does not escape line terminators in regexp patterns in node 12
|
---|
224 | // We'll also remove literal \0
|
---|
225 | return source.replace(/[\0\n\r\u2028\u2029]/g, function (match, offset) {
|
---|
226 | var escaped = source[offset - 1] == "\\"
|
---|
227 | && (source[offset - 2] != "\\"
|
---|
228 | || /(?:^|[^\\])(?:\\{2})*$/.test(source.slice(0, offset - 1)));
|
---|
229 | return (escaped ? "" : "\\") + lineTerminatorEscape[match];
|
---|
230 | });
|
---|
231 | }
|
---|
232 |
|
---|
233 | // Subset of regexps that is not going to cause regexp based DDOS
|
---|
234 | // https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
|
---|
235 | const re_safe_regexp = /^[\\/|\0\s\w^$.[\]()]*$/;
|
---|
236 |
|
---|
237 | /** Check if the regexp is safe for Terser to create without risking a RegExp DOS */
|
---|
238 | export const regexp_is_safe = (source) => re_safe_regexp.test(source);
|
---|
239 |
|
---|
240 | const all_flags = "dgimsuyv";
|
---|
241 | function sort_regexp_flags(flags) {
|
---|
242 | const existing_flags = new Set(flags.split(""));
|
---|
243 | let out = "";
|
---|
244 | for (const flag of all_flags) {
|
---|
245 | if (existing_flags.has(flag)) {
|
---|
246 | out += flag;
|
---|
247 | existing_flags.delete(flag);
|
---|
248 | }
|
---|
249 | }
|
---|
250 | if (existing_flags.size) {
|
---|
251 | // Flags Terser doesn't know about
|
---|
252 | existing_flags.forEach(flag => { out += flag; });
|
---|
253 | }
|
---|
254 | return out;
|
---|
255 | }
|
---|
256 |
|
---|
257 | function has_annotation(node, annotation) {
|
---|
258 | return node._annotations & annotation;
|
---|
259 | }
|
---|
260 |
|
---|
261 | function set_annotation(node, annotation) {
|
---|
262 | node._annotations |= annotation;
|
---|
263 | }
|
---|
264 |
|
---|
265 | function clear_annotation(node, annotation) {
|
---|
266 | node._annotations &= ~annotation;
|
---|
267 | }
|
---|
268 |
|
---|
269 | export {
|
---|
270 | characters,
|
---|
271 | defaults,
|
---|
272 | HOP,
|
---|
273 | keep_name,
|
---|
274 | make_node,
|
---|
275 | makePredicate,
|
---|
276 | map_add,
|
---|
277 | map_from_object,
|
---|
278 | map_to_object,
|
---|
279 | MAP,
|
---|
280 | member,
|
---|
281 | mergeSort,
|
---|
282 | noop,
|
---|
283 | push_uniq,
|
---|
284 | regexp_source_fix,
|
---|
285 | remove,
|
---|
286 | return_false,
|
---|
287 | return_null,
|
---|
288 | return_this,
|
---|
289 | return_true,
|
---|
290 | sort_regexp_flags,
|
---|
291 | string_template,
|
---|
292 | has_annotation,
|
---|
293 | set_annotation,
|
---|
294 | clear_annotation,
|
---|
295 | };
|
---|