UNPKG

9.17 kBJavaScriptView Raw
1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS
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
46var builtins = function() {
47 var names = new Dictionary();
48 // NaN will be included due to Number.NaN
49 [
50 "null",
51 "true",
52 "false",
53 "Infinity",
54 "-Infinity",
55 "undefined",
56 ].forEach(add);
57 [
58 Array,
59 Boolean,
60 Date,
61 Error,
62 Function,
63 Math,
64 Number,
65 Object,
66 RegExp,
67 String,
68 ].forEach(function(ctor) {
69 Object.getOwnPropertyNames(ctor).map(add);
70 if (ctor.prototype) {
71 Object.getOwnPropertyNames(new ctor()).map(add);
72 Object.getOwnPropertyNames(ctor.prototype).map(add);
73 }
74 });
75 return names;
76
77 function add(name) {
78 names.set(name, true);
79 }
80}();
81
82function reserve_quoted_keys(ast, reserved) {
83 ast.walk(new TreeWalker(function(node) {
84 if (node instanceof AST_ClassProperty) {
85 if (node.start && node.start.quote) add(node.key);
86 } else if (node instanceof AST_ObjectProperty) {
87 if (node.start && node.start.quote) add(node.key);
88 } else if (node instanceof AST_Sub) {
89 addStrings(node.property, add);
90 }
91 }));
92
93 function add(name) {
94 push_uniq(reserved, name);
95 }
96}
97
98function addStrings(node, add) {
99 if (node instanceof AST_Conditional) {
100 addStrings(node.consequent, add);
101 addStrings(node.alternative, add);
102 } else if (node instanceof AST_Sequence) {
103 addStrings(node.tail_node(), add);
104 } else if (node instanceof AST_String) {
105 add(node.value);
106 }
107}
108
109function mangle_properties(ast, options) {
110 options = defaults(options, {
111 builtins: false,
112 cache: null,
113 debug: false,
114 keep_quoted: false,
115 regex: null,
116 reserved: null,
117 }, true);
118
119 var reserved = options.builtins ? new Dictionary() : builtins.clone();
120 if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
121 reserved.set(name, true);
122 });
123
124 var cname = -1;
125 var cache;
126 if (options.cache) {
127 cache = options.cache.props;
128 cache.each(function(name) {
129 reserved.set(name, true);
130 });
131 } else {
132 cache = new Dictionary();
133 }
134
135 var regex = options.regex;
136
137 // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
138 // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
139 // the same as passing an empty string.
140 var debug = options.debug !== false;
141 var debug_suffix;
142 if (debug) debug_suffix = options.debug === true ? "" : options.debug;
143
144 var names_to_mangle = new Dictionary();
145 var unmangleable = reserved.clone();
146
147 // step 1: find candidates to mangle
148 ast.walk(new TreeWalker(function(node) {
149 if (node instanceof AST_Binary) {
150 if (node.operator == "in") addStrings(node.left, add);
151 } else if (node.TYPE == "Call") {
152 var exp = node.expression;
153 if (exp instanceof AST_Dot) switch (exp.property) {
154 case "defineProperty":
155 case "getOwnPropertyDescriptor":
156 if (node.args.length < 2) break;
157 exp = exp.expression;
158 if (!(exp instanceof AST_SymbolRef)) break;
159 if (exp.name != "Object") break;
160 if (!exp.definition().undeclared) break;
161 addStrings(node.args[1], add);
162 break;
163 case "hasOwnProperty":
164 if (node.args.length < 1) break;
165 addStrings(node.args[0], add);
166 break;
167 }
168 } else if (node instanceof AST_ClassProperty) {
169 if (typeof node.key == "string") add(node.key);
170 } else if (node instanceof AST_Dot) {
171 add(node.property);
172 } else if (node instanceof AST_ObjectProperty) {
173 if (typeof node.key == "string") add(node.key);
174 } else if (node instanceof AST_Sub) {
175 addStrings(node.property, add);
176 }
177 }));
178
179 // step 2: renaming properties
180 ast.walk(new TreeWalker(function(node) {
181 if (node instanceof AST_Binary) {
182 if (node.operator == "in") mangleStrings(node.left);
183 } else if (node.TYPE == "Call") {
184 var exp = node.expression;
185 if (exp instanceof AST_Dot) switch (exp.property) {
186 case "defineProperty":
187 case "getOwnPropertyDescriptor":
188 if (node.args.length < 2) break;
189 exp = exp.expression;
190 if (!(exp instanceof AST_SymbolRef)) break;
191 if (exp.name != "Object") break;
192 if (!exp.definition().undeclared) break;
193 mangleStrings(node.args[1]);
194 break;
195 case "hasOwnProperty":
196 if (node.args.length < 1) break;
197 mangleStrings(node.args[0]);
198 break;
199 }
200 } else if (node instanceof AST_ClassProperty) {
201 if (typeof node.key == "string") node.key = mangle(node.key);
202 } else if (node instanceof AST_Dot) {
203 node.property = mangle(node.property);
204 } else if (node instanceof AST_ObjectProperty) {
205 if (typeof node.key == "string") node.key = mangle(node.key);
206 } else if (node instanceof AST_Sub) {
207 if (!options.keep_quoted) mangleStrings(node.property);
208 }
209 }));
210
211 // only function declarations after this line
212
213 function can_mangle(name) {
214 if (unmangleable.has(name)) return false;
215 if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
216 return true;
217 }
218
219 function should_mangle(name) {
220 if (reserved.has(name)) return false;
221 if (regex && !regex.test(name)) return false;
222 return cache.has(name) || names_to_mangle.has(name);
223 }
224
225 function add(name) {
226 if (can_mangle(name)) names_to_mangle.set(name, true);
227 if (!should_mangle(name)) unmangleable.set(name, true);
228 }
229
230 function mangle(name) {
231 if (!should_mangle(name)) return name;
232 var mangled = cache.get(name);
233 if (!mangled) {
234 if (debug) {
235 // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo ---> o._$foo$NNN_.
236 var debug_mangled = "_$" + name + "$" + debug_suffix + "_";
237 if (can_mangle(debug_mangled)) mangled = debug_mangled;
238 }
239 // either debug mode is off, or it is on and we could not use the mangled name
240 if (!mangled) do {
241 mangled = base54(++cname);
242 } while (!can_mangle(mangled));
243 if (/^#/.test(name)) mangled = "#" + mangled;
244 cache.set(name, mangled);
245 }
246 return mangled;
247 }
248
249 function mangleStrings(node) {
250 if (node instanceof AST_Sequence) {
251 mangleStrings(node.expressions.tail_node());
252 } else if (node instanceof AST_String) {
253 node.value = mangle(node.value);
254 } else if (node instanceof AST_Conditional) {
255 mangleStrings(node.consequent);
256 mangleStrings(node.alternative);
257 }
258 }
259}